diff options
author | molotkov-and <molotkov-and@ydb.tech> | 2023-06-09 10:10:11 +0300 |
---|---|---|
committer | molotkov-and <molotkov-and@ydb.tech> | 2023-06-09 10:10:11 +0300 |
commit | d81b7265e37dd8b5b5100509cd456d449cea58ae (patch) | |
tree | 702b847a5122cd406a68aba2a0494b776c40f641 | |
parent | 317dbd1daf2f287bf3dbb79648de10e2bf963b78 (diff) | |
download | ydb-d81b7265e37dd8b5b5100509cd456d449cea58ae.tar.gz |
Update groups for static credentials tickets
-rw-r--r-- | ydb/core/security/ticket_parser_impl.h | 39 | ||||
-rw-r--r-- | ydb/core/security/ticket_parser_ut.cpp | 150 | ||||
-rw-r--r-- | ydb/library/login/login.cpp | 9 | ||||
-rw-r--r-- | ydb/library/login/login.h | 2 |
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; |