From 59315fe895c3b36441fe3cdb2dea9585be2b99e0 Mon Sep 17 00:00:00 2001 From: kungurtsev Date: Fri, 31 Jan 2025 06:34:40 +0100 Subject: [PATCH] SystemView Auth TableRange filter (#14023) --- ydb/core/kqp/ut/common/kqp_ut_common.cpp | 8 +- ydb/core/kqp/ut/common/kqp_ut_common.h | 4 +- ydb/core/sys_view/auth/auth_scan_base.h | 70 +- ydb/core/sys_view/auth/group_members.cpp | 6 +- ydb/core/sys_view/auth/groups.cpp | 6 +- ydb/core/sys_view/auth/owners.cpp | 36 +- ydb/core/sys_view/auth/permissions.cpp | 13 +- ydb/core/sys_view/auth/users.cpp | 17 +- .../sys_view/common/scan_actor_base_impl.h | 49 + ydb/core/sys_view/ut_kqp.cpp | 1043 ++++++++++++++--- 10 files changed, 1010 insertions(+), 242 deletions(-) diff --git a/ydb/core/kqp/ut/common/kqp_ut_common.cpp b/ydb/core/kqp/ut/common/kqp_ut_common.cpp index 4313192b6b88..86ab759c81c0 100644 --- a/ydb/core/kqp/ut/common/kqp_ut_common.cpp +++ b/ydb/core/kqp/ut/common/kqp_ut_common.cpp @@ -552,11 +552,11 @@ TString ReformatYson(const TString& yson) { return output.Str(); } -void CompareYson(const TString& expected, const TString& actual) { - UNIT_ASSERT_VALUES_EQUAL(ReformatYson(expected), ReformatYson(actual)); +void CompareYson(const TString& expected, const TString& actual, const TString& message) { + UNIT_ASSERT_VALUES_EQUAL_C(ReformatYson(expected), ReformatYson(actual), message); } -void CompareYson(const TString& expected, const NKikimrMiniKQL::TResult& actual) { +void CompareYson(const TString& expected, const NKikimrMiniKQL::TResult& actual, const TString& message) { TStringStream ysonStream; NYson::TYsonWriter writer(&ysonStream, NYson::EYsonFormat::Text); NYql::IDataProvider::TFillSettings fillSettings; @@ -564,7 +564,7 @@ void CompareYson(const TString& expected, const NKikimrMiniKQL::TResult& actual) KikimrResultToYson(ysonStream, writer, actual, {}, fillSettings, truncated); UNIT_ASSERT(!truncated); - CompareYson(expected, ysonStream.Str()); + CompareYson(expected, ysonStream.Str(), message); } bool HasIssue(const NYql::TIssues& issues, ui32 code, diff --git a/ydb/core/kqp/ut/common/kqp_ut_common.h b/ydb/core/kqp/ut/common/kqp_ut_common.h index f9b5703fdeaf..00d32391c7ed 100644 --- a/ydb/core/kqp/ut/common/kqp_ut_common.h +++ b/ydb/core/kqp/ut/common/kqp_ut_common.h @@ -296,8 +296,8 @@ inline constexpr TStringBuf IndexWithSqlString(EIndexTypeSql type) { } TString ReformatYson(const TString& yson); -void CompareYson(const TString& expected, const TString& actual); -void CompareYson(const TString& expected, const NKikimrMiniKQL::TResult& actual); +void CompareYson(const TString& expected, const TString& actual, const TString& message = {}); +void CompareYson(const TString& expected, const NKikimrMiniKQL::TResult& actual, const TString& message = {}); void CreateLargeTable(TKikimrRunner& kikimr, ui32 rowsPerShard, ui32 keyTextSize, ui32 dataTextSize, ui32 batchSizeRows = 100, ui32 fillShardsCount = 8, ui32 largeTableKeysPerShard = 1000000); diff --git a/ydb/core/sys_view/auth/auth_scan_base.h b/ydb/core/sys_view/auth/auth_scan_base.h index a0363601360e..b6ce0a4a9852 100644 --- a/ydb/core/sys_view/auth/auth_scan_base.h +++ b/ydb/core/sys_view/auth/auth_scan_base.h @@ -51,11 +51,19 @@ class TAuthScanBase : public TScanActorBase { TAuthScanBase(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, const TTableRange& tableRange, const TArrayRef& columns, TIntrusiveConstPtr userToken, - bool requireUserAdministratorAccess) + bool requireUserAdministratorAccess, bool applyPathTableRange) : TBase(ownerId, scanId, tableId, tableRange, columns) , UserToken(std::move(userToken)) , RequireUserAdministratorAccess(requireUserAdministratorAccess) { + if (applyPathTableRange) { + if (auto cellsFrom = TBase::TableRange.From.GetCells(); cellsFrom.size() > 0 && !cellsFrom[0].IsNull()) { + PathFrom = cellsFrom[0].AsBuf(); + } + if (auto cellsTo = TBase::TableRange.To.GetCells(); cellsTo.size() > 0 && !cellsTo[0].IsNull()) { + PathTo = cellsTo[0].AsBuf(); + } + } } STFUNC(StateScan) { @@ -81,16 +89,6 @@ class TAuthScanBase : public TScanActorBase { return; } - // TODO: support TableRange filter - if (auto cellsFrom = TBase::TableRange.From.GetCells(); cellsFrom.size() > 0 && !cellsFrom[0].IsNull()) { - TBase::ReplyErrorAndDie(Ydb::StatusIds::INTERNAL_ERROR, TStringBuilder() << "TableRange.From filter is not supported"); - return; - } - if (auto cellsTo = TBase::TableRange.To.GetCells(); cellsTo.size() > 0 && !cellsTo[0].IsNull()) { - TBase::ReplyErrorAndDie(Ydb::StatusIds::INTERNAL_ERROR, TStringBuilder() << "TableRange.To filter is not supported"); - return; - } - auto& last = DeepFirstSearchStack.emplace_back(); last.Index = Max(); // tenant root @@ -108,6 +106,10 @@ class TAuthScanBase : public TScanActorBase { auto& last = DeepFirstSearchStack.back(); if (last.Index == Max()) { // tenant root + if ((PathFrom || PathTo) && ShouldSkipSubTree(TBase::TenantName)) { + DeepFirstSearchStack.pop_back(); + continue; + } NavigatePath(SplitPath(TBase::TenantName)); DeepFirstSearchStack.pop_back(); return; @@ -122,6 +124,11 @@ class TAuthScanBase : public TScanActorBase { } last.Entry.Path.push_back(child.Name); + if ((PathFrom || PathTo) && ShouldSkipSubTree(CanonizePath(last.Entry.Path))) { + last.Entry.Path.pop_back(); + continue; + } + NavigatePath(last.Entry.Path); last.Entry.Path.pop_back(); return; @@ -189,6 +196,46 @@ class TAuthScanBase : public TScanActorBase { TBase::Send(MakeSchemeCacheID(), new TEvTxProxySchemeCache::TEvNavigateKeySet(request.Release())); } + + // this method only skip foolproof useless paths + // ignores from/to inclusive flags for simplicity + // ignores some boundary cases for simplicity + // precise check will be performed later on batch rows filtering + bool ShouldSkipSubTree(const TString& path) { + Y_DEBUG_ABORT_UNLESS(PathFrom || PathTo); + + if (PathFrom) { + // example: + // PathFrom = "Dir2/SubDir2" + // skip: + // - "Dir1" + // - "Dir2/SubDir1" + // do not skip: + // - "Dir2" + // - "Dir3" + + if (PathFrom > path && !PathFrom->StartsWith(path)) { + return true; + } + } + + if (PathTo) { + // example: + // PathTo = "Dir2/SubDir2" + // skip: + // - "Dir3" + // - "Dir2/SubDir3" + // do not skip: + // - "Dir1" + // - "Dir2" + + if (PathTo < path) { + return true; + } + } + + return false; + } virtual void FillBatch(NKqp::TEvKqpCompute::TEvScanData& batch, const TNavigate::TEntry& entry) = 0; @@ -197,6 +244,7 @@ class TAuthScanBase : public TScanActorBase { private: bool RequireUserAdministratorAccess; + std::optional PathFrom, PathTo; TVector DeepFirstSearchStack; }; diff --git a/ydb/core/sys_view/auth/group_members.cpp b/ydb/core/sys_view/auth/group_members.cpp index da290a40ec7f..253b9bcb63cd 100644 --- a/ydb/core/sys_view/auth/group_members.cpp +++ b/ydb/core/sys_view/auth/group_members.cpp @@ -22,7 +22,7 @@ class TGroupMembersScan : public TAuthScanBase { TGroupMembersScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, const TTableRange& tableRange, const TArrayRef& columns, TIntrusiveConstPtr userToken) - : TAuthBase(ownerId, scanId, tableId, tableRange, columns, std::move(userToken), true) + : TAuthBase(ownerId, scanId, tableId, tableRange, columns, std::move(userToken), true, false) { } @@ -34,7 +34,9 @@ class TGroupMembersScan : public TAuthScanBase { TVector> memberships; for (const auto& group : entry.DomainInfo->Groups) { for (const auto& member : group.Members) { - memberships.emplace_back(&group, &member); + if (StringKeyIsInTableRange({group.Sid, member})) { + memberships.emplace_back(&group, &member); + } } } SortBatch(memberships, [](const auto& left, const auto& right) { diff --git a/ydb/core/sys_view/auth/groups.cpp b/ydb/core/sys_view/auth/groups.cpp index d5add729896f..ae1d2263627a 100644 --- a/ydb/core/sys_view/auth/groups.cpp +++ b/ydb/core/sys_view/auth/groups.cpp @@ -22,7 +22,7 @@ class TGroupsScan : public TAuthScanBase { TGroupsScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, const TTableRange& tableRange, const TArrayRef& columns, TIntrusiveConstPtr userToken) - : TAuthBase(ownerId, scanId, tableId, tableRange, columns, std::move(userToken), true) + : TAuthBase(ownerId, scanId, tableId, tableRange, columns, std::move(userToken), true, false) { } @@ -33,7 +33,9 @@ class TGroupsScan : public TAuthScanBase { TVector groups(::Reserve(entry.DomainInfo->Groups.size())); for (const auto& group : entry.DomainInfo->Groups) { - groups.push_back(&group); + if (StringKeyIsInTableRange({group.Sid})) { + groups.push_back(&group); + } } SortBatch(groups, [](const auto* left, const auto* right) { return left->Sid < right->Sid; diff --git a/ydb/core/sys_view/auth/owners.cpp b/ydb/core/sys_view/auth/owners.cpp index 7d9b1182fea2..02f325c1f5d8 100644 --- a/ydb/core/sys_view/auth/owners.cpp +++ b/ydb/core/sys_view/auth/owners.cpp @@ -22,7 +22,7 @@ class TOwnersScan : public TAuthScanBase { TOwnersScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, const TTableRange& tableRange, const TArrayRef& columns, TIntrusiveConstPtr userToken) - : TAuthBase(ownerId, scanId, tableId, tableRange, columns, std::move(userToken), false) + : TAuthBase(ownerId, scanId, tableId, tableRange, columns, std::move(userToken), false, true) { } @@ -34,26 +34,28 @@ class TOwnersScan : public TAuthScanBase { auto entryPath = CanonizePath(entry.Path); - for (auto& column : Columns) { - switch (column.Tag) { - case Schema::AuthOwners::Path::ColumnId: - cells.push_back(TCell(entryPath.data(), entryPath.size())); - break; - case Schema::AuthOwners::Sid::ColumnId: - if (entry.SecurityObject && entry.SecurityObject->HasOwnerSID()) { - cells.push_back(TCell(entry.SecurityObject->GetOwnerSID().data(), entry.SecurityObject->GetOwnerSID().size())); - } else { + if (StringKeyIsInTableRange({entryPath})) { + for (auto& column : Columns) { + switch (column.Tag) { + case Schema::AuthOwners::Path::ColumnId: + cells.push_back(TCell(entryPath.data(), entryPath.size())); + break; + case Schema::AuthOwners::Sid::ColumnId: + if (entry.SecurityObject && entry.SecurityObject->HasOwnerSID()) { + cells.push_back(TCell(entry.SecurityObject->GetOwnerSID().data(), entry.SecurityObject->GetOwnerSID().size())); + } else { + cells.emplace_back(); + } + break; + default: cells.emplace_back(); } - break; - default: - cells.emplace_back(); } - } - TArrayRef ref(cells); - batch.Rows.emplace_back(TOwnedCellVec::Make(ref)); - cells.clear(); + TArrayRef ref(cells); + batch.Rows.emplace_back(TOwnedCellVec::Make(ref)); + cells.clear(); + } batch.Finished = false; } diff --git a/ydb/core/sys_view/auth/permissions.cpp b/ydb/core/sys_view/auth/permissions.cpp index 30fb45653f67..c09def50ab0c 100644 --- a/ydb/core/sys_view/auth/permissions.cpp +++ b/ydb/core/sys_view/auth/permissions.cpp @@ -23,7 +23,7 @@ class TPermissionsScan : public TAuthScanBase { TPermissionsScan(bool effective, const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, const TTableRange& tableRange, const TArrayRef& columns, TIntrusiveConstPtr userToken) - : TAuthBase(ownerId, scanId, tableId, tableRange, columns, std::move(userToken), false) + : TAuthBase(ownerId, scanId, tableId, tableRange, columns, std::move(userToken), false, true) , Effective(effective) { } @@ -36,6 +36,8 @@ class TPermissionsScan : public TAuthScanBase { batch.Finished = false; return; } + + auto entryPath = CanonizePath(entry.Path); TVector> permissions; for (const NACLibProto::TACE& ace : entry.SecurityObject->GetACL().GetACE()) { @@ -45,10 +47,15 @@ class TPermissionsScan : public TAuthScanBase { if (!Effective && ace.GetInherited()) { continue; } + if (!ace.HasSID()) { + continue; + } auto acePermissions = ConvertACLMaskToYdbPermissionNames(ace.GetAccessRight()); for (const auto& permission : acePermissions) { - permissions.emplace_back(ace.HasSID() ? ace.GetSID() : TString{}, std::move(permission)); + if (StringKeyIsInTableRange({entryPath, ace.GetSID(), permission})) { + permissions.emplace_back(ace.GetSID(), std::move(permission)); + } } } // Note: due to rights inheritance permissions may be duplicated @@ -59,8 +66,6 @@ class TPermissionsScan : public TAuthScanBase { TVector cells(::Reserve(Columns.size())); - auto entryPath = CanonizePath(entry.Path); - for (const auto& [sid, permission] : permissions) { for (auto& column : Columns) { switch (column.Tag) { diff --git a/ydb/core/sys_view/auth/users.cpp b/ydb/core/sys_view/auth/users.cpp index 263ac0bd52e6..5aa8c3b337c9 100644 --- a/ydb/core/sys_view/auth/users.cpp +++ b/ydb/core/sys_view/auth/users.cpp @@ -54,16 +54,6 @@ class TUsersScan : public TScanActorBase { } void StartScan() { - // TODO: support TableRange filter - if (auto cellsFrom = TBase::TableRange.From.GetCells(); cellsFrom.size() > 0 && !cellsFrom[0].IsNull()) { - TBase::ReplyErrorAndDie(Ydb::StatusIds::INTERNAL_ERROR, TStringBuilder() << "TableRange.From filter is not supported"); - return; - } - if (auto cellsTo = TBase::TableRange.To.GetCells(); cellsTo.size() > 0 && !cellsTo[0].IsNull()) { - TBase::ReplyErrorAndDie(Ydb::StatusIds::INTERNAL_ERROR, TStringBuilder() << "TableRange.To filter is not supported"); - return; - } - auto request = MakeHolder(); LOG_TRACE_S(TlsActivationContext->AsActorContext(), NKikimrServices::SYSTEM_VIEWS, @@ -99,6 +89,9 @@ class TUsersScan : public TScanActorBase { if (!user.HasName() || !CanAccessUser(user.GetName())) { continue; } + if (!StringKeyIsInTableRange({user.GetName()})) { + continue; + } users.push_back(&user); } SortBatch(users, [](const auto* left, const auto* right) { @@ -111,9 +104,7 @@ class TUsersScan : public TScanActorBase { for (auto& column : Columns) { switch (column.Tag) { case Schema::AuthUsers::Sid::ColumnId: - cells.push_back(user->HasName() - ? TCell(user->GetName().data(), user->GetName().size()) - : TCell()); + cells.push_back(TCell(user->GetName().data(), user->GetName().size())); break; case Schema::AuthUsers::IsEnabled::ColumnId: cells.push_back(user->HasIsEnabled() diff --git a/ydb/core/sys_view/common/scan_actor_base_impl.h b/ydb/core/sys_view/common/scan_actor_base_impl.h index c126bdbb8bd9..c3a72b3ac84c 100644 --- a/ydb/core/sys_view/common/scan_actor_base_impl.h +++ b/ydb/core/sys_view/common/scan_actor_base_impl.h @@ -162,6 +162,55 @@ class TScanActorBase : public TActorBootstrapped { SendBatch(std::move(batch)); } + bool StringKeyIsInTableRange(const TVector& key) const { + { + bool equalPrefixes = true; + for (size_t index : xrange(Min(TableRange.From.GetCells().size(), key.size()))) { + if (auto cellFrom = TableRange.From.GetCells()[index]; !cellFrom.IsNull()) { + int cmp = cellFrom.AsBuf().compare(key[index]); + if (cmp < 0) { + equalPrefixes = false; + break; + } + if (cmp > 0) { + return false; + } + // cmp == 0, prefixes are equal, go further + } else { + equalPrefixes = false; + break; + } + } + if (equalPrefixes && !TableRange.FromInclusive) { + return false; + } + } + + if (TableRange.To.GetCells().size()) { + bool equalPrefixes = true; + for (size_t index : xrange(Min(TableRange.To.GetCells().size(), key.size()))) { + if (auto cellTo = TableRange.To.GetCells()[index]; !cellTo.IsNull()) { + int cmp = cellTo.AsBuf().compare(key[index]); + if (cmp > 0) { + equalPrefixes = false; + break; + } + if (cmp < 0) { + return false; + } + // cmp == 0, prefixes are equal, go further + } else { + break; + } + } + if (equalPrefixes && !TableRange.ToInclusive) { + return false; + } + } + + return true; + } + private: virtual void ProceedToScan() = 0; diff --git a/ydb/core/sys_view/ut_kqp.cpp b/ydb/core/sys_view/ut_kqp.cpp index 434adf075113..c35f501add9b 100644 --- a/ydb/core/sys_view/ut_kqp.cpp +++ b/ydb/core/sys_view/ut_kqp.cpp @@ -2376,6 +2376,128 @@ Y_UNIT_TEST_SUITE(SystemView) { NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } + Y_UNIT_TEST(AuthUsers_TableRange) { + TTestEnv env; + SetupAuthEnvironment(env); + TTableClient client(env.GetDriver()); + + for (auto user : { + "user1", + "user2", + "user3", + "user4" + }) { + env.GetClient().CreateUser("/Root", user, "password"); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid + FROM `Root/.sys/auth_users` + )").GetValueSync(); + + auto expected = R"([ + [["user1"]]; + [["user2"]]; + [["user3"]]; + [["user4"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid + FROM `Root/.sys/auth_users` + WHERE Sid >= "user2" + )").GetValueSync(); + + auto expected = R"([ + [["user2"]]; + [["user3"]]; + [["user4"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid + FROM `Root/.sys/auth_users` + WHERE Sid > "user2" + )").GetValueSync(); + + auto expected = R"([ + [["user3"]]; + [["user4"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid + FROM `Root/.sys/auth_users` + WHERE Sid <= "user3" + )").GetValueSync(); + + auto expected = R"([ + [["user1"]]; + [["user2"]]; + [["user3"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid + FROM `Root/.sys/auth_users` + WHERE Sid < "user3" + )").GetValueSync(); + + auto expected = R"([ + [["user1"]]; + [["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid + FROM `Root/.sys/auth_users` + WHERE Sid > "user1" AND Sid <= "user3" + )").GetValueSync(); + + auto expected = R"([ + [["user2"]]; + [["user3"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid + FROM `Root/.sys/auth_users` + WHERE Sid >= "user2" AND Sid < "user3" + )").GetValueSync(); + + auto expected = R"([ + [["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + } + Y_UNIT_TEST(AuthGroups) { TTestEnv env; SetupAuthEnvironment(env); @@ -2568,6 +2690,36 @@ Y_UNIT_TEST_SUITE(SystemView) { NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } + Y_UNIT_TEST(AuthGroups_TableRange) { + TTestEnv env; + SetupAuthEnvironment(env); + TTableClient client(env.GetDriver()); + + for (auto group : { + "group1", + "group2", + "group3", + "group4", + }) { + env.GetClient().CreateGroup("/Root", group); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid + FROM `Root/.sys/auth_groups` + WHERE Sid > "group1" AND Sid <= "group3" + )").GetValueSync(); + + auto expected = R"([ + [["group2"]]; + [["group3"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + } + Y_UNIT_TEST(AuthGroupMembers) { TTestEnv env; SetupAuthEnvironment(env); @@ -2787,52 +2939,613 @@ Y_UNIT_TEST_SUITE(SystemView) { NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } - Y_UNIT_TEST(AuthOwners) { + Y_UNIT_TEST(AuthGroupMembers_TableRange) { TTestEnv env; SetupAuthEnvironment(env); TTableClient client(env.GetDriver()); - env.GetClient().CreateUser("/Root", "user1", "password1"); - env.GetClient().CreateUser("/Root/Tenant1", "user2", "password2"); - env.GetClient().CreateUser("/Root/Tenant2", "user3", "password3"); - env.GetClient().CreateUser("/Root/Tenant2", "user4", "password4"); - env.GetClient().CreateGroup("/Root/Tenant2", "group1"); + for (auto group : { + "group1", + "group2", + "group3", + }) { + env.GetClient().CreateGroup("/Root", group); + } - env.GetClient().MkDir("/Root", "Dir1/SubDir1"); - env.GetClient().ModifyOwner("/Root", "Dir1", "user1"); - env.GetClient().ModifyOwner("/Root/Dir1", "SubDir1", "user1"); + for (auto user : { + "user1", + "user2", + "user3" + }) { + env.GetClient().CreateUser("/Root", user, "password"); + } - env.GetClient().MkDir("/Root/Tenant1", "Dir2/SubDir2"); - env.GetClient().ModifyOwner("/Root/Tenant1", "Dir2", "user2"); - env.GetClient().ModifyOwner("/Root/Tenant1/Dir2", "SubDir2", "user2"); + for (auto membership : TVector>{ + {"group1", "user1"}, + {"group1", "user2"}, + {"group2", "user1"}, + {"group2", "user2"}, + {"group2", "user3"}, + {"group3", "user1"}, + {"group3", "user2"}, + }) { + env.GetClient().AddGroupMembership("/Root", membership.first, membership.second); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + )").GetValueSync(); - env.GetClient().MkDir("/Root/Tenant2", "Dir3/SubDir33"); - env.GetClient().MkDir("/Root/Tenant2", "Dir3/SubDir34"); - env.GetClient().MkDir("/Root/Tenant2", "Dir4/SubDir45"); - env.GetClient().MkDir("/Root/Tenant2", "Dir4/SubDir46"); - env.GetClient().ModifyOwner("/Root/Tenant2", "Dir3", "user3"); - env.GetClient().ModifyOwner("/Root/Tenant2", "Dir4", "user4"); - env.GetClient().ModifyOwner("/Root/Tenant2/Dir3", "SubDir33", "group1"); - env.GetClient().ModifyOwner("/Root/Tenant2/Dir4", "SubDir46", "user4"); + auto expected = R"([ + [["group1"];["user1"]]; + [["group1"];["user2"]]; + [["group2"];["user1"]]; + [["group2"];["user2"]]; + [["group2"];["user3"]]; + [["group3"];["user1"]]; + [["group3"];["user2"]]; + ])"; - // Cerr << env.GetClient().Describe(env.GetServer().GetRuntime(), "/Root").DebugString() << Endl; - // Cerr << env.GetClient().Describe(env.GetServer().GetRuntime(), "/Root/Tenant2").DebugString() << Endl; + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid > "group1" AND GroupSid <= "group3" + )").GetValueSync(); + + auto expected = R"([ + [["group2"];["user1"]]; + [["group2"];["user2"]]; + [["group2"];["user3"]]; + [["group3"];["user1"]]; + [["group3"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid >= "group2" + )").GetValueSync(); + + auto expected = R"([ + [["group2"];["user1"]]; + [["group2"];["user2"]]; + [["group2"];["user3"]]; + [["group3"];["user1"]]; + [["group3"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid > "group2" + )").GetValueSync(); + + auto expected = R"([ + [["group3"];["user1"]]; + [["group3"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid <= "group2" + )").GetValueSync(); + + auto expected = R"([ + [["group1"];["user1"]]; + [["group1"];["user2"]]; + [["group2"];["user1"]]; + [["group2"];["user2"]]; + [["group2"];["user3"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid < "group2" + )").GetValueSync(); + + auto expected = R"([ + [["group1"];["user1"]]; + [["group1"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid = "group2" AND MemberSid >= "user2" + )").GetValueSync(); + + auto expected = R"([ + [["group2"];["user2"]]; + [["group2"];["user3"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid = "group2" AND MemberSid > "user2" + )").GetValueSync(); + + auto expected = R"([ + [["group2"];["user3"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid = "group2" AND MemberSid <= "user2" + )").GetValueSync(); + + auto expected = R"([ + [["group2"];["user1"]]; + [["group2"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_group_members` + WHERE GroupSid = "group2" AND MemberSid < "user2" + )").GetValueSync(); + + auto expected = R"([ + [["group2"];["user1"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + } + + Y_UNIT_TEST(AuthOwners) { + TTestEnv env; + SetupAuthEnvironment(env); + TTableClient client(env.GetDriver()); + + env.GetClient().CreateUser("/Root", "user1", "password1"); + env.GetClient().CreateUser("/Root/Tenant1", "user2", "password2"); + env.GetClient().CreateUser("/Root/Tenant2", "user3", "password3"); + env.GetClient().CreateUser("/Root/Tenant2", "user4", "password4"); + env.GetClient().CreateGroup("/Root/Tenant2", "group1"); + + env.GetClient().MkDir("/Root", "Dir1/SubDir1"); + env.GetClient().ModifyOwner("/Root", "Dir1", "user1"); + env.GetClient().ModifyOwner("/Root/Dir1", "SubDir1", "user1"); + + env.GetClient().MkDir("/Root/Tenant1", "Dir2/SubDir2"); + env.GetClient().ModifyOwner("/Root/Tenant1", "Dir2", "user2"); + env.GetClient().ModifyOwner("/Root/Tenant1/Dir2", "SubDir2", "user2"); + + env.GetClient().MkDir("/Root/Tenant2", "Dir3/SubDir33"); + env.GetClient().MkDir("/Root/Tenant2", "Dir3/SubDir34"); + env.GetClient().MkDir("/Root/Tenant2", "Dir4/SubDir45"); + env.GetClient().MkDir("/Root/Tenant2", "Dir4/SubDir46"); + env.GetClient().ModifyOwner("/Root/Tenant2", "Dir3", "user3"); + env.GetClient().ModifyOwner("/Root/Tenant2", "Dir4", "user4"); + env.GetClient().ModifyOwner("/Root/Tenant2/Dir3", "SubDir33", "group1"); + env.GetClient().ModifyOwner("/Root/Tenant2/Dir4", "SubDir46", "user4"); + + // Cerr << env.GetClient().Describe(env.GetServer().GetRuntime(), "/Root").DebugString() << Endl; + // Cerr << env.GetClient().Describe(env.GetServer().GetRuntime(), "/Root/Tenant2").DebugString() << Endl; + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root"];["root@builtin"]]; + [["/Root/.metadata"];["metadata@system"]]; + [["/Root/.metadata/workload_manager"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; + [["/Root/Dir1"];["user1"]]; + [["/Root/Dir1/SubDir1"];["user1"]]; + [["/Root/Table0"];["root@builtin"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant1/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root/Tenant1"];["root@builtin"]]; + [["/Root/Tenant1/Dir2"];["user2"]]; + [["/Root/Tenant1/Dir2/SubDir2"];["user2"]]; + [["/Root/Tenant1/Table1"];["root@builtin"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant2/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root/Tenant2"];["root@builtin"]]; + [["/Root/Tenant2/Dir3"];["user3"]]; + [["/Root/Tenant2/Dir3/SubDir33"];["group1"]]; + [["/Root/Tenant2/Dir3/SubDir34"];["root@builtin"]]; + [["/Root/Tenant2/Dir4"];["user4"]]; + [["/Root/Tenant2/Dir4/SubDir45"];["root@builtin"]]; + [["/Root/Tenant2/Dir4/SubDir46"];["user4"]]; + [["/Root/Tenant2/Table2"];["root@builtin"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + } + + Y_UNIT_TEST(AuthOwners_Access) { + TTestEnv env; + SetupAuthAccessEnvironment(env); + TTableClient client(env.GetDriver()); + + env.GetClient().MkDir("/Root", "Dir1"); + env.GetClient().MkDir("/Root", "Dir2"); + env.GetClient().MkDir("/Root/Tenant1", "Dir3"); + env.GetClient().MkDir("/Root/Tenant1", "Dir4"); + env.GetClient().ModifyOwner("/Root", "Dir1", "user1rootadmin"); + env.GetClient().ModifyOwner("/Root/Tenant1", "Dir3", "user3"); + + { // anonymous login gives `ydb.granular.describe_schema` access + auto driverConfig = TDriverConfig() + .SetEndpoint(env.GetEndpoint()); + auto driver = TDriver(driverConfig); + TTableClient client(driver); + + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root"];["root@builtin"]]; + [["/Root/.metadata"];["metadata@system"]]; + [["/Root/.metadata/workload_manager"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; + [["/Root/Dir1"];["user1rootadmin"]]; + [["/Root/Dir2"];["root@builtin"]]; + [["/Root/Table0"];["root@builtin"]] + ])"; + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { // user1rootadmin has /Root GenericUse access + auto driverConfig = TDriverConfig() + .SetEndpoint(env.GetEndpoint()) + .SetCredentialsProviderFactory(NYdb::CreateLoginCredentialsProviderFactory({ + .User = "user1rootadmin", + .Password = "password1", + })); + auto driver = TDriver(driverConfig); + TTableClient client(driver); + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root"];["root@builtin"]]; + [["/Root/.metadata"];["metadata@system"]]; + [["/Root/.metadata/workload_manager"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; + [["/Root/Dir1"];["user1rootadmin"]]; + [["/Root/Dir2"];["root@builtin"]]; + [["/Root/Table0"];["root@builtin"]] + ])"; + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant1/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root/Tenant1"];["root@builtin"]]; + [["/Root/Tenant1/Dir3"];["user3"]]; + [["/Root/Tenant1/Dir4"];["root@builtin"]]; + [["/Root/Tenant1/Table1"];["root@builtin"]] + ])"; + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + } + + { // revoke user1rootadmin /Root/Dir2 GenericUse access + NACLib::TDiffACL acl; + acl.AddAccess(NACLib::EAccessType::Deny, NACLib::GenericUse, "user1rootadmin"); + env.GetClient().ModifyACL("/Root", "Dir2", acl.SerializeAsString()); + + auto driverConfig = TDriverConfig() + .SetEndpoint(env.GetEndpoint()) + .SetCredentialsProviderFactory(NYdb::CreateLoginCredentialsProviderFactory({ + .User = "user1rootadmin", + .Password = "password1", + })); + auto driver = TDriver(driverConfig); + TTableClient client(driver); + + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root"];["root@builtin"]]; + [["/Root/.metadata"];["metadata@system"]]; + [["/Root/.metadata/workload_manager"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; + [["/Root/Dir1"];["user1rootadmin"]]; + [["/Root/Table0"];["root@builtin"]] + ])"; + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + // TODO: fix https://github.com/ydb-platform/ydb/issues/13730 + // and test tenant user and tenant admin + } + + Y_UNIT_TEST(AuthOwners_ResultOrder) { + TTestEnv env; + SetupAuthEnvironment(env); + TTableClient client(env.GetDriver()); + + for (auto path : { + "Dir2/SubDir2", + "Dir1/SubDir1", + "Dir2/SubDir1", + "Dir1/SubDir2", + "Dir2/SubDir3", + "Dir1/SubDir3", + "Dir11/SubDir", + "Dir/SubDir", + }) { + env.GetClient().MkDir("/Root", path); + } + + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root"];["root@builtin"]]; + [["/Root/.metadata"];["metadata@system"]]; + [["/Root/.metadata/workload_manager"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; + [["/Root/Dir"];["root@builtin"]]; + [["/Root/Dir/SubDir"];["root@builtin"]]; + [["/Root/Dir1"];["root@builtin"]]; + [["/Root/Dir1/SubDir1"];["root@builtin"]]; + [["/Root/Dir1/SubDir2"];["root@builtin"]]; + [["/Root/Dir1/SubDir3"];["root@builtin"]]; + [["/Root/Dir11"];["root@builtin"]]; + [["/Root/Dir11/SubDir"];["root@builtin"]]; + [["/Root/Dir2"];["root@builtin"]]; + [["/Root/Dir2/SubDir1"];["root@builtin"]]; + [["/Root/Dir2/SubDir2"];["root@builtin"]]; + [["/Root/Dir2/SubDir3"];["root@builtin"]]; + [["/Root/Table0"];["root@builtin"]] + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + Y_UNIT_TEST(AuthOwners_TableRange) { + TTestEnv env; + SetupAuthEnvironment(env); + TTableClient client(env.GetDriver()); + + for (auto path : { + "Dir0/SubDir0", + "Dir0/SubDir1", + "Dir0/SubDir2", + "Dir1/SubDir0", + "Dir1/SubDir1", + "Dir1/SubDir2", + "Dir2/SubDir0", + "Dir2/SubDir1", + "Dir2/SubDir2", + "Dir3/SubDir0", + "Dir3/SubDir1", + "Dir3/SubDir2", + }) { + env.GetClient().MkDir("/Root", path); + } + env.GetClient().CreateUser("/Root", "user0", "password0"); + env.GetClient().CreateUser("/Root", "user1", "password1"); + env.GetClient().CreateUser("/Root", "user2", "password2"); + env.GetClient().ModifyOwner("/Root/Dir1", "SubDir0", "user0"); + env.GetClient().ModifyOwner("/Root/Dir1", "SubDir1", "user1"); + env.GetClient().ModifyOwner("/Root/Dir1", "SubDir2", "user2"); + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + )").GetValueSync(); + + auto expected = R"([ + [["/Root"];["root@builtin"]]; + [["/Root/.metadata"];["metadata@system"]]; + [["/Root/.metadata/workload_manager"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; + [["/Root/Dir0"];["root@builtin"]]; + [["/Root/Dir0/SubDir0"];["root@builtin"]]; + [["/Root/Dir0/SubDir1"];["root@builtin"]]; + [["/Root/Dir0/SubDir2"];["root@builtin"]]; + [["/Root/Dir1"];["root@builtin"]]; + [["/Root/Dir1/SubDir0"];["user0"]]; + [["/Root/Dir1/SubDir1"];["user1"]]; + [["/Root/Dir1/SubDir2"];["user2"]]; + [["/Root/Dir2"];["root@builtin"]]; + [["/Root/Dir2/SubDir0"];["root@builtin"]]; + [["/Root/Dir2/SubDir1"];["root@builtin"]]; + [["/Root/Dir2/SubDir2"];["root@builtin"]]; + [["/Root/Dir3"];["root@builtin"]]; + [["/Root/Dir3/SubDir0"];["root@builtin"]]; + [["/Root/Dir3/SubDir1"];["root@builtin"]]; + [["/Root/Dir3/SubDir2"];["root@builtin"]]; + [["/Root/Table0"];["root@builtin"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + WHERE Path >= "/A" AND Path <= "/Z" + )").GetValueSync(); + + auto expected = R"([ + [["/Root"];["root@builtin"]]; + [["/Root/.metadata"];["metadata@system"]]; + [["/Root/.metadata/workload_manager"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; + [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; + [["/Root/Dir0"];["root@builtin"]]; + [["/Root/Dir0/SubDir0"];["root@builtin"]]; + [["/Root/Dir0/SubDir1"];["root@builtin"]]; + [["/Root/Dir0/SubDir2"];["root@builtin"]]; + [["/Root/Dir1"];["root@builtin"]]; + [["/Root/Dir1/SubDir0"];["user0"]]; + [["/Root/Dir1/SubDir1"];["user1"]]; + [["/Root/Dir1/SubDir2"];["user2"]]; + [["/Root/Dir2"];["root@builtin"]]; + [["/Root/Dir2/SubDir0"];["root@builtin"]]; + [["/Root/Dir2/SubDir1"];["root@builtin"]]; + [["/Root/Dir2/SubDir2"];["root@builtin"]]; + [["/Root/Dir3"];["root@builtin"]]; + [["/Root/Dir3/SubDir0"];["root@builtin"]]; + [["/Root/Dir3/SubDir1"];["root@builtin"]]; + [["/Root/Dir3/SubDir2"];["root@builtin"]]; + [["/Root/Table0"];["root@builtin"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + WHERE Path >= "/Root/Dir1" AND Path < "/Root/Dir3" + )").GetValueSync(); + + auto expected = R"([ + [["/Root/Dir1"];["root@builtin"]]; + [["/Root/Dir1/SubDir0"];["user0"]]; + [["/Root/Dir1/SubDir1"];["user1"]]; + [["/Root/Dir1/SubDir2"];["user2"]]; + [["/Root/Dir2"];["root@builtin"]]; + [["/Root/Dir2/SubDir0"];["root@builtin"]]; + [["/Root/Dir2/SubDir1"];["root@builtin"]]; + [["/Root/Dir2/SubDir2"];["root@builtin"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + WHERE Path >= "/Root/Dir1/SubDir1" AND Path <= "/Root/Dir2/SubDir1" + )").GetValueSync(); + + auto expected = R"([ + [["/Root/Dir1/SubDir1"];["user1"]]; + [["/Root/Dir1/SubDir2"];["user2"]]; + [["/Root/Dir2"];["root@builtin"]]; + [["/Root/Dir2/SubDir0"];["root@builtin"]]; + [["/Root/Dir2/SubDir1"];["root@builtin"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + WHERE Path > "/Root/Dir1/SubDir1" AND Path < "/Root/Dir2/SubDir1" + )").GetValueSync(); + + auto expected = R"([ + [["/Root/Dir1/SubDir2"];["user2"]]; + [["/Root/Dir2"];["root@builtin"]]; + [["/Root/Dir2/SubDir0"];["root@builtin"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } { auto it = client.StreamExecuteScanQuery(R"( SELECT * FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" )").GetValueSync(); auto expected = R"([ - [["/Root"];["root@builtin"]]; - [["/Root/.metadata"];["metadata@system"]]; - [["/Root/.metadata/workload_manager"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; - [["/Root/Dir1"];["user1"]]; [["/Root/Dir1/SubDir1"];["user1"]]; - [["/Root/Table0"];["root@builtin"]]; ])"; NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); @@ -2841,14 +3554,13 @@ Y_UNIT_TEST_SUITE(SystemView) { { auto it = client.StreamExecuteScanQuery(R"( SELECT * - FROM `Root/Tenant1/.sys/auth_owners` + FROM `Root/.sys/auth_owners` + WHERE Path >= "/Root/Dir1/SubDir0" AND Sid >= "user1" AND Path < "/Root/Dir2" )").GetValueSync(); auto expected = R"([ - [["/Root/Tenant1"];["root@builtin"]]; - [["/Root/Tenant1/Dir2"];["user2"]]; - [["/Root/Tenant1/Dir2/SubDir2"];["user2"]]; - [["/Root/Tenant1/Table1"];["root@builtin"]]; + [["/Root/Dir1/SubDir1"];["user1"]]; + [["/Root/Dir1/SubDir2"];["user2"]]; ])"; NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); @@ -2857,185 +3569,114 @@ Y_UNIT_TEST_SUITE(SystemView) { { auto it = client.StreamExecuteScanQuery(R"( SELECT * - FROM `Root/Tenant2/.sys/auth_owners` + FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid > "user0" )").GetValueSync(); auto expected = R"([ - [["/Root/Tenant2"];["root@builtin"]]; - [["/Root/Tenant2/Dir3"];["user3"]]; - [["/Root/Tenant2/Dir3/SubDir33"];["group1"]]; - [["/Root/Tenant2/Dir3/SubDir34"];["root@builtin"]]; - [["/Root/Tenant2/Dir4"];["user4"]]; - [["/Root/Tenant2/Dir4/SubDir45"];["root@builtin"]]; - [["/Root/Tenant2/Dir4/SubDir46"];["user4"]]; - [["/Root/Tenant2/Table2"];["root@builtin"]]; + [["/Root/Dir1/SubDir1"];["user1"]]; ])"; NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } - } - Y_UNIT_TEST(AuthOwners_Access) { - TTestEnv env; - SetupAuthAccessEnvironment(env); - TTableClient client(env.GetDriver()); + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid < "user2" + )").GetValueSync(); - env.GetClient().MkDir("/Root", "Dir1"); - env.GetClient().MkDir("/Root", "Dir2"); - env.GetClient().MkDir("/Root/Tenant1", "Dir3"); - env.GetClient().MkDir("/Root/Tenant1", "Dir4"); - env.GetClient().ModifyOwner("/Root", "Dir1", "user1rootadmin"); - env.GetClient().ModifyOwner("/Root/Tenant1", "Dir3", "user3"); + auto expected = R"([ + [["/Root/Dir1/SubDir1"];["user1"]]; + ])"; - { // anonymous login gives `ydb.granular.describe_schema` access - auto driverConfig = TDriverConfig() - .SetEndpoint(env.GetEndpoint()); - auto driver = TDriver(driverConfig); - TTableClient client(driver); + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + { auto it = client.StreamExecuteScanQuery(R"( SELECT * FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid >= "user1" )").GetValueSync(); auto expected = R"([ - [["/Root"];["root@builtin"]]; - [["/Root/.metadata"];["metadata@system"]]; - [["/Root/.metadata/workload_manager"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; - [["/Root/Dir1"];["user1rootadmin"]]; - [["/Root/Dir2"];["root@builtin"]]; - [["/Root/Table0"];["root@builtin"]] + [["/Root/Dir1/SubDir1"];["user1"]]; ])"; + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } - { // user1rootadmin has /Root GenericUse access - auto driverConfig = TDriverConfig() - .SetEndpoint(env.GetEndpoint()) - .SetCredentialsProviderFactory(NYdb::CreateLoginCredentialsProviderFactory({ - .User = "user1rootadmin", - .Password = "password1", - })); - auto driver = TDriver(driverConfig); - TTableClient client(driver); + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid <= "user1" + )").GetValueSync(); - { - auto it = client.StreamExecuteScanQuery(R"( - SELECT * - FROM `Root/.sys/auth_owners` - )").GetValueSync(); + auto expected = R"([ + [["/Root/Dir1/SubDir1"];["user1"]]; + ])"; - auto expected = R"([ - [["/Root"];["root@builtin"]]; - [["/Root/.metadata"];["metadata@system"]]; - [["/Root/.metadata/workload_manager"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; - [["/Root/Dir1"];["user1rootadmin"]]; - [["/Root/Dir2"];["root@builtin"]]; - [["/Root/Table0"];["root@builtin"]] - ])"; - NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); - } + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } - { - auto it = client.StreamExecuteScanQuery(R"( - SELECT * - FROM `Root/Tenant1/.sys/auth_owners` - )").GetValueSync(); + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid > "user1" + )").GetValueSync(); - auto expected = R"([ - [["/Root/Tenant1"];["root@builtin"]]; - [["/Root/Tenant1/Dir3"];["user3"]]; - [["/Root/Tenant1/Dir4"];["root@builtin"]]; - [["/Root/Tenant1/Table1"];["root@builtin"]] - ])"; - NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); - } - } + auto expected = R"([ - { // revoke user1rootadmin /Root/Dir2 GenericUse access - NACLib::TDiffACL acl; - acl.AddAccess(NACLib::EAccessType::Deny, NACLib::GenericUse, "user1rootadmin"); - env.GetClient().ModifyACL("/Root", "Dir2", acl.SerializeAsString()); + ])"; - auto driverConfig = TDriverConfig() - .SetEndpoint(env.GetEndpoint()) - .SetCredentialsProviderFactory(NYdb::CreateLoginCredentialsProviderFactory({ - .User = "user1rootadmin", - .Password = "password1", - })); - auto driver = TDriver(driverConfig); - TTableClient client(driver); + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + { auto it = client.StreamExecuteScanQuery(R"( SELECT * FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid < "user1" )").GetValueSync(); auto expected = R"([ - [["/Root"];["root@builtin"]]; - [["/Root/.metadata"];["metadata@system"]]; - [["/Root/.metadata/workload_manager"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; - [["/Root/Dir1"];["user1rootadmin"]]; - [["/Root/Table0"];["root@builtin"]] + ])"; + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } - // TODO: fix https://github.com/ydb-platform/ydb/issues/13730 - // and test tenant user and tenant admin - } + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid = "user1" + )").GetValueSync(); - Y_UNIT_TEST(AuthOwners_ResultOrder) { - TTestEnv env; - SetupAuthEnvironment(env); - TTableClient client(env.GetDriver()); + auto expected = R"([ + [["/Root/Dir1/SubDir1"];["user1"]]; + ])"; - for (auto path : { - "Dir2/SubDir2", - "Dir1/SubDir1", - "Dir2/SubDir1", - "Dir1/SubDir2", - "Dir2/SubDir3", - "Dir1/SubDir3", - "Dir11/SubDir", - "Dir/SubDir", - }) { - env.GetClient().MkDir("/Root", path); + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } - - auto it = client.StreamExecuteScanQuery(R"( - SELECT * - FROM `Root/.sys/auth_owners` - )").GetValueSync(); - auto expected = R"([ - [["/Root"];["root@builtin"]]; - [["/Root/.metadata"];["metadata@system"]]; - [["/Root/.metadata/workload_manager"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools"];["metadata@system"]]; - [["/Root/.metadata/workload_manager/pools/default"];["metadata@system"]]; - [["/Root/Dir"];["root@builtin"]]; - [["/Root/Dir/SubDir"];["root@builtin"]]; - [["/Root/Dir1"];["root@builtin"]]; - [["/Root/Dir1/SubDir1"];["root@builtin"]]; - [["/Root/Dir1/SubDir2"];["root@builtin"]]; - [["/Root/Dir1/SubDir3"];["root@builtin"]]; - [["/Root/Dir11"];["root@builtin"]]; - [["/Root/Dir11/SubDir"];["root@builtin"]]; - [["/Root/Dir2"];["root@builtin"]]; - [["/Root/Dir2/SubDir1"];["root@builtin"]]; - [["/Root/Dir2/SubDir2"];["root@builtin"]]; - [["/Root/Dir2/SubDir3"];["root@builtin"]]; - [["/Root/Table0"];["root@builtin"]] - ])"; + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT Sid, Path + FROM `Root/.sys/auth_owners` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid >= "user1" + )").GetValueSync(); - NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + auto expected = R"([ + [["user1"];["/Root/Dir1/SubDir1"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } } Y_UNIT_TEST(AuthPermissions) { @@ -3452,37 +4093,53 @@ Y_UNIT_TEST_SUITE(SystemView) { WHERE Path = "/Root/Dir1" )").GetValueSync(); - // TODO: - NKqp::StreamResultToYson(it, false, EStatus::INTERNAL_ERROR, "TableRange.From filter is not supported"); + auto expected = R"([ + [["/Root/Dir1"];["ydb.generic.use"];["user1"]]; + [["/Root/Dir1"];["ydb.granular.select_row"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_permissions` + WHERE Sid = "user2" + )").GetValueSync(); - // auto expected = R"([ - // [["/Root/Dir1"];["ydb.generic.use"];["user1"]]; - // [["/Root/Dir1"];["ydb.granular.select_row"];["user2"]]; - // ])"; + auto expected = R"([ + [["/Root/Dir1"];["ydb.granular.select_row"];["user2"]]; + [["/Root/Dir1/SubDir1"];["ydb.granular.erase_row"];["user2"]]; + [["/Root/Dir1/SubDir1"];["ydb.granular.select_row"];["user2"]]; + ])"; - // NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } { auto it = client.StreamExecuteScanQuery(R"( SELECT * - FROM `Root/.sys/auth_effective_permissions` - WHERE Path = "/Root/Dir1" + FROM `Root/.sys/auth_permissions` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid >= "user2" )").GetValueSync(); - // TODO: - NKqp::StreamResultToYson(it, false, EStatus::INTERNAL_ERROR, "TableRange.From filter is not supported"); + auto expected = R"([ + [["/Root/Dir1/SubDir1"];["ydb.granular.erase_row"];["user2"]]; + [["/Root/Dir1/SubDir1"];["ydb.granular.select_row"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } { auto it = client.StreamExecuteScanQuery(R"( SELECT * FROM `Root/.sys/auth_permissions` - WHERE Sid = "user2" + WHERE Path = "/Root/Dir1/SubDir1" AND Sid = "user2" )").GetValueSync(); auto expected = R"([ - [["/Root/Dir1"];["ydb.granular.select_row"];["user2"]]; [["/Root/Dir1/SubDir1"];["ydb.granular.erase_row"];["user2"]]; [["/Root/Dir1/SubDir1"];["ydb.granular.select_row"];["user2"]]; ])"; @@ -3493,15 +4150,27 @@ Y_UNIT_TEST_SUITE(SystemView) { { auto it = client.StreamExecuteScanQuery(R"( SELECT * - FROM `Root/.sys/auth_effective_permissions` - WHERE Sid = "user2" + FROM `Root/.sys/auth_permissions` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid = "user2" AND Permission >= "ydb.granular.erase_row" )").GetValueSync(); auto expected = R"([ - [["/Root/Dir1"];["ydb.granular.select_row"];["user2"]]; [["/Root/Dir1/SubDir1"];["ydb.granular.erase_row"];["user2"]]; [["/Root/Dir1/SubDir1"];["ydb.granular.select_row"];["user2"]]; - [["/Root/Dir1/SubDir2"];["ydb.granular.select_row"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_permissions` + WHERE Path = "/Root/Dir1/SubDir1" AND Sid = "user2" AND Permission > "ydb.granular.erase_row" + )").GetValueSync(); + + auto expected = R"([ + [["/Root/Dir1/SubDir1"];["ydb.granular.select_row"];["user2"]]; ])"; NKqp::CompareYson(expected, NKqp::StreamResultToYson(it));