aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormolotkov-and <molotkov-and@ydb.tech>2023-06-09 10:10:11 +0300
committermolotkov-and <molotkov-and@ydb.tech>2023-06-09 10:10:11 +0300
commitd81b7265e37dd8b5b5100509cd456d449cea58ae (patch)
tree702b847a5122cd406a68aba2a0494b776c40f641
parent317dbd1daf2f287bf3dbb79648de10e2bf963b78 (diff)
downloadydb-d81b7265e37dd8b5b5100509cd456d449cea58ae.tar.gz
Update groups for static credentials tickets
-rw-r--r--ydb/core/security/ticket_parser_impl.h39
-rw-r--r--ydb/core/security/ticket_parser_ut.cpp150
-rw-r--r--ydb/library/login/login.cpp9
-rw-r--r--ydb/library/login/login.h2
4 files changed, 199 insertions, 1 deletions
diff --git a/ydb/core/security/ticket_parser_impl.h b/ydb/core/security/ticket_parser_impl.h
index 3ebf4ed8385..550deb5d15c 100644
--- a/ydb/core/security/ticket_parser_impl.h
+++ b/ydb/core/security/ticket_parser_impl.h
@@ -964,8 +964,9 @@ protected:
bool NeedsRefresh() const {
switch (TokenType) {
case TDerived::ETokenType::Builtin:
- case TDerived::ETokenType::Login:
return false;
+ case TDerived::ETokenType::Login:
+ return true;
default:
return Signature.AccessKeyId.empty();
}
@@ -1178,7 +1179,43 @@ protected:
}
template <typename TTokenRecord>
+ bool CanRefreshLoginTicket(const TTokenRecord& record) {
+ return record.TokenType == TDerived::ETokenType::Login && record.Error.empty();
+ }
+
+ template <typename TTokenRecord>
+ bool RefreshLoginTicket(const TString& key, TTokenRecord& record) {
+ GetDerived()->ResetTokenRecord(record);
+ const TString& database = Config.GetDomainLoginOnly() ? DomainName : record.Database;
+ auto itLoginProvider = LoginProviders.find(database);
+ if (itLoginProvider == LoginProviders.end()) {
+ return false;
+ }
+ NLogin::TLoginProvider& loginProvider(itLoginProvider->second);
+ const TString userSID = record.GetToken()->GetUserSID();
+ if (loginProvider.CheckUserExists(userSID)) {
+ const std::vector<TString> providerGroups = loginProvider.GetGroupsMembership(userSID);
+ const TVector<NACLib::TSID> groups(providerGroups.begin(), providerGroups.end());
+ SetToken(key, record, new NACLib::TUserToken({
+ .OriginalUserToken = record.Ticket,
+ .UserSID = userSID,
+ .GroupSIDs = groups,
+ .AuthType = record.GetAuthType()
+ }));
+ } else {
+ TEvTicketParser::TError error;
+ error.Message = "User not found";
+ error.Retryable = false;
+ SetError(key, record, error);
+ }
+ return true;
+ }
+
+ template <typename TTokenRecord>
bool CanRefreshTicket(const TString& key, TTokenRecord& record) {
+ if (CanRefreshLoginTicket(record)) {
+ return RefreshLoginTicket(key, record);
+ }
if (CanRefreshAccessServiceTicket(record)) {
GetDerived()->ResetTokenRecord(record);
if (record.Permissions) {
diff --git a/ydb/core/security/ticket_parser_ut.cpp b/ydb/core/security/ticket_parser_ut.cpp
index b3c5f6be396..8eb89e07010 100644
--- a/ydb/core/security/ticket_parser_ut.cpp
+++ b/ydb/core/security/ticket_parser_ut.cpp
@@ -143,6 +143,156 @@ Y_UNIT_TEST_SUITE(TTicketParserTest) {
UNIT_ASSERT_VALUES_EQUAL(result->Error.Message, "Token is not in correct format");
}
+ Y_UNIT_TEST(LoginRefreshGroupsGood) {
+ using namespace Tests;
+ TPortManager tp;
+ ui16 kikimrPort = tp.GetPort(2134);
+ ui16 grpcPort = tp.GetPort(2135);
+ NKikimrProto::TAuthConfig authConfig;
+ authConfig.SetUseBlackBox(false);
+ authConfig.SetUseLoginProvider(true);
+ authConfig.SetRefreshTime("5s");
+ auto settings = TServerSettings(kikimrPort, authConfig);
+ settings.SetDomainName("Root");
+ settings.CreateTicketParser = NKikimr::CreateTicketParser;
+ TServer server(settings);
+ server.EnableGRpc(grpcPort);
+ server.GetRuntime()->SetLogPriority(NKikimrServices::TICKET_PARSER, NLog::PRI_TRACE);
+ server.GetRuntime()->SetLogPriority(NKikimrServices::GRPC_CLIENT, NLog::PRI_TRACE);
+ TClient client(settings);
+ NClient::TKikimr kikimr(client.GetClientConfig());
+ client.InitRootScheme();
+ TTestActorRuntime* runtime = server.GetRuntime();
+
+ NLogin::TLoginProvider provider;
+
+ provider.Audience = "/Root";
+ provider.RotateKeys();
+
+ TActorId sender = runtime->AllocateEdgeActor();
+
+ provider.CreateGroup({.Group = "group1"});
+ provider.CreateUser({.User = "user1", .Password = "password1"});
+ provider.AddGroupMembership({.Group = "group1", .Member = "user1"});
+
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvUpdateLoginSecurityState(provider.GetSecurityState())), 0);
+
+ auto loginResponse = provider.LoginUser({.User = "user1", .Password = "password1"});
+
+ UNIT_ASSERT_VALUES_EQUAL(loginResponse.Error, "");
+
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvAuthorizeTicket(loginResponse.Token)), 0);
+
+ TAutoPtr<IEventHandle> handle;
+
+ TEvTicketParser::TEvAuthorizeTicketResult* result = runtime->GrabEdgeEvent<TEvTicketParser::TEvAuthorizeTicketResult>(handle);
+ UNIT_ASSERT_C(result->Error.empty(), result->Error);
+ UNIT_ASSERT(result->Token != nullptr);
+ UNIT_ASSERT_VALUES_EQUAL(result->Token->GetUserSID(), "user1");
+ UNIT_ASSERT(result->Token->IsExist("group1"));
+ UNIT_ASSERT_VALUES_EQUAL(result->Token->GetGroupSIDs().size(), 2);
+
+ provider.CreateGroup({.Group = "group2"});
+ provider.AddGroupMembership({.Group = "group2", .Member = "group1"});
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvUpdateLoginSecurityState(provider.GetSecurityState())), 0);
+
+ Sleep(TDuration::Seconds(10));
+
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvAuthorizeTicket(loginResponse.Token)), 0);
+
+ result = runtime->GrabEdgeEvent<TEvTicketParser::TEvAuthorizeTicketResult>(handle);
+
+ UNIT_ASSERT_C(result->Error.empty(), result->Error);
+ UNIT_ASSERT(result->Token != nullptr);
+ UNIT_ASSERT_VALUES_EQUAL(result->Token->GetUserSID(), "user1");
+ UNIT_ASSERT(result->Token->IsExist("group1"));
+ UNIT_ASSERT(result->Token->IsExist("group2"));
+ UNIT_ASSERT_VALUES_EQUAL(result->Token->GetGroupSIDs().size(), 3);
+
+ provider.RemoveGroup({.Group = "group2"});
+ provider.CreateGroup({.Group = "group3"});
+ provider.AddGroupMembership({.Group = "group3", .Member = "user1"});
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvUpdateLoginSecurityState(provider.GetSecurityState())), 0);
+
+ Sleep(TDuration::Seconds(10));
+
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvAuthorizeTicket(loginResponse.Token)), 0);
+
+ result = runtime->GrabEdgeEvent<TEvTicketParser::TEvAuthorizeTicketResult>(handle);
+
+ UNIT_ASSERT_C(result->Error.empty(), result->Error);
+ UNIT_ASSERT(result->Token != nullptr);
+ UNIT_ASSERT_VALUES_EQUAL(result->Token->GetUserSID(), "user1");
+ UNIT_ASSERT(result->Token->IsExist("group1"));
+ UNIT_ASSERT(result->Token->IsExist("group3"));
+ UNIT_ASSERT(!result->Token->IsExist("group2"));
+ UNIT_ASSERT_VALUES_EQUAL(result->Token->GetGroupSIDs().size(), 3);
+ }
+
+ Y_UNIT_TEST(LoginCheckRemovedUser) {
+ using namespace Tests;
+ TPortManager tp;
+ ui16 kikimrPort = tp.GetPort(2134);
+ ui16 grpcPort = tp.GetPort(2135);
+ NKikimrProto::TAuthConfig authConfig;
+ authConfig.SetUseBlackBox(false);
+ authConfig.SetUseLoginProvider(true);
+ authConfig.SetRefreshTime("5s");
+ auto settings = TServerSettings(kikimrPort, authConfig);
+ settings.SetDomainName("Root");
+ settings.CreateTicketParser = NKikimr::CreateTicketParser;
+ TServer server(settings);
+ server.EnableGRpc(grpcPort);
+ server.GetRuntime()->SetLogPriority(NKikimrServices::TICKET_PARSER, NLog::PRI_TRACE);
+ server.GetRuntime()->SetLogPriority(NKikimrServices::GRPC_CLIENT, NLog::PRI_TRACE);
+ TClient client(settings);
+ NClient::TKikimr kikimr(client.GetClientConfig());
+ client.InitRootScheme();
+ TTestActorRuntime* runtime = server.GetRuntime();
+
+ NLogin::TLoginProvider provider;
+
+ provider.Audience = "/Root";
+ provider.RotateKeys();
+
+ TActorId sender = runtime->AllocateEdgeActor();
+
+ provider.CreateGroup({.Group = "group1"});
+ provider.CreateUser({.User = "user1", .Password = "password1"});
+ provider.AddGroupMembership({.Group = "group1", .Member = "user1"});
+
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvUpdateLoginSecurityState(provider.GetSecurityState())), 0);
+
+ auto loginResponse = provider.LoginUser({.User = "user1", .Password = "password1"});
+
+ UNIT_ASSERT_VALUES_EQUAL(loginResponse.Error, "");
+
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvAuthorizeTicket(loginResponse.Token)), 0);
+
+ TAutoPtr<IEventHandle> handle;
+
+ TEvTicketParser::TEvAuthorizeTicketResult* result = runtime->GrabEdgeEvent<TEvTicketParser::TEvAuthorizeTicketResult>(handle);
+ UNIT_ASSERT(result->Error.empty());
+ UNIT_ASSERT(result->Token != nullptr);
+ UNIT_ASSERT_VALUES_EQUAL(result->Token->GetUserSID(), "user1");
+ UNIT_ASSERT(result->Token->IsExist("group1"));
+ UNIT_ASSERT_VALUES_EQUAL(result->Token->GetGroupSIDs().size(), 2);
+
+ provider.RemoveUser({.User = "user1"});
+
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvUpdateLoginSecurityState(provider.GetSecurityState())), 0);
+
+ Sleep(TDuration::Seconds(10));
+
+ runtime->Send(new IEventHandle(MakeTicketParserID(), sender, new TEvTicketParser::TEvAuthorizeTicket(loginResponse.Token)), 0);
+
+ result = runtime->GrabEdgeEvent<TEvTicketParser::TEvAuthorizeTicketResult>(handle);
+
+ UNIT_ASSERT(!result->Error.empty());
+ UNIT_ASSERT_EQUAL(result->Error.Message, "User not found");
+ UNIT_ASSERT(result->Token == nullptr);
+ }
+
Y_UNIT_TEST(LoginEmptyTicketBad) {
using namespace Tests;
TPortManager tp;
diff --git a/ydb/library/login/login.cpp b/ydb/library/login/login.cpp
index 65cd98440fa..136f153d7be 100644
--- a/ydb/library/login/login.cpp
+++ b/ydb/library/login/login.cpp
@@ -70,6 +70,15 @@ TLoginProvider::TBasicResponse TLoginProvider::CreateUser(const TCreateUserReque
return response;
}
+bool TLoginProvider::CheckSubjectExists(const TString& name, const ESidType::SidType& type) {
+ auto itSidModify = Sids.find(name);
+ return itSidModify != Sids.end() && itSidModify->second.Type == type;
+}
+
+bool TLoginProvider::CheckUserExists(const TString& name) {
+ return CheckSubjectExists(name, ESidType::USER);
+}
+
TLoginProvider::TBasicResponse TLoginProvider::ModifyUser(const TModifyUserRequest& request) {
TBasicResponse response;
diff --git a/ydb/library/login/login.h b/ydb/library/login/login.h
index 96ef5d83183..035fdaebd81 100644
--- a/ydb/library/login/login.h
+++ b/ydb/library/login/login.h
@@ -143,6 +143,7 @@ public:
TBasicResponse CreateUser(const TCreateUserRequest& request);
TBasicResponse ModifyUser(const TModifyUserRequest& request);
TRemoveUserResponse RemoveUser(const TRemoveUserRequest& request);
+ bool CheckUserExists(const TString& name);
TBasicResponse CreateGroup(const TCreateGroupRequest& request);
TBasicResponse AddGroupMembership(const TAddGroupMembershipRequest& request);
@@ -158,6 +159,7 @@ public:
private:
std::deque<TKeyRecord>::iterator FindKeyIterator(ui64 keyId);
+ bool CheckSubjectExists(const TString& name, const ESidType::SidType& type);
static bool CheckAllowedName(const TString& name);
struct TImpl;