diff options
author | kungurtsev <kungasc@ydb.tech> | 2024-12-31 11:51:53 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-31 11:51:53 +0100 |
commit | bafd5812353f1c4f6ef5ab41b069d3eb765f1b31 (patch) | |
tree | 17f9982f3781c234fa659d968f9c76c79f200eef | |
parent | 03cfdfdd06771fa6b8e70a28e32b73317d3517f6 (diff) | |
download | ydb-bafd5812353f1c4f6ef5ab41b069d3eb765f1b31.tar.gz |
SystemView Auth Users, Groups, Members (#12521)
23 files changed, 792 insertions, 34 deletions
diff --git a/ydb/core/kqp/ut/common/kqp_ut_common.cpp b/ydb/core/kqp/ut/common/kqp_ut_common.cpp index 269570e7ca..ed075e40b1 100644 --- a/ydb/core/kqp/ut/common/kqp_ut_common.cpp +++ b/ydb/core/kqp/ut/common/kqp_ut_common.cpp @@ -553,7 +553,7 @@ TString ReformatYson(const TString& yson) { } void CompareYson(const TString& expected, const TString& actual) { - UNIT_ASSERT_NO_DIFF(ReformatYson(expected), ReformatYson(actual)); + UNIT_ASSERT_VALUES_EQUAL(ReformatYson(expected), ReformatYson(actual)); } void CompareYson(const TString& expected, const NKikimrMiniKQL::TResult& actual) { diff --git a/ydb/core/sys_view/auth/auth_scan_base.h b/ydb/core/sys_view/auth/auth_scan_base.h new file mode 100644 index 0000000000..4a1ef6b25b --- /dev/null +++ b/ydb/core/sys_view/auth/auth_scan_base.h @@ -0,0 +1,121 @@ +#pragma once + +#include <ydb/core/sys_view/common/events.h> +#include <ydb/core/sys_view/common/schema.h> +#include <ydb/core/sys_view/common/scan_actor_base_impl.h> +#include <ydb/core/base/tablet_pipecache.h> +#include <ydb/library/login/protos/login.pb.h> + +#include <ydb/library/actors/core/hfunc.h> + +namespace NKikimr::NSysView::NAuth { + +using namespace NSchemeShard; +using namespace NActors; +using namespace NSchemeCache; +using TNavigate = NSchemeCache::TSchemeCacheNavigate; + +template <typename TDerived> +class TAuthScanBase : public TScanActorBase<TDerived> { +public: + using TBase = TScanActorBase<TDerived>; + + static constexpr auto ActorActivityType() { + return NKikimrServices::TActivity::KQP_SYSTEM_VIEW_SCAN; + } + + TAuthScanBase(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns) + : TBase(ownerId, scanId, tableId, tableRange, columns) + { + } + + STFUNC(StateScan) { + switch (ev->GetTypeRewrite()) { + hFunc(NKqp::TEvKqpCompute::TEvScanDataAck, Handle); + hFunc(TEvPipeCache::TEvDeliveryProblem, Handle); + HFunc(TEvTxProxySchemeCache::TEvNavigateKeySetResult, Handle); + hFunc(NKqp::TEvKqp::TEvAbortExecution, TBase::HandleAbortExecution); + cFunc(TEvents::TEvWakeup::EventType, TBase::HandleTimeout); + cFunc(TEvents::TEvPoison::EventType, PassAway); + default: + LOG_CRIT(*TlsActivationContext, NKikimrServices::SYSTEM_VIEWS, + "NSysView::NAuth::TAuthScanBase: unexpected event 0x%08" PRIx32, ev->GetTypeRewrite()); + } + } + +protected: + void ProceedToScan() override { + TBase::Become(&TAuthScanBase::StateScan); + if (TBase::AckReceived) { + StartScan(); + } + } + + void Handle(NKqp::TEvKqpCompute::TEvScanDataAck::TPtr&) { + StartScan(); + } + + 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; + } + + NavigatePath(TBase::TenantName); + } + + void Handle(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev, const TActorContext& ctx) { + THolder<NSchemeCache::TSchemeCacheNavigate> request(ev->Get()->Request.Release()); + + for (const auto& entry : request->ResultSet) { + if (entry.Status != TNavigate::EStatus::Ok) { + TBase::ReplyErrorAndDie(Ydb::StatusIds::INTERNAL_ERROR, TStringBuilder() << + "Failed to navigate " << CanonizePath(entry.Path) << ": " << entry.Status); + return; + } + } + + LOG_TRACE_S(ctx, NKikimrServices::SYSTEM_VIEWS, + "Got navigate: " << request->ToString(*AppData()->TypeRegistry)); + + auto batch = MakeHolder<NKqp::TEvKqpCompute::TEvScanData>(TBase::ScanId); + + FillBatch(*batch, request->ResultSet); + + TBase::SendBatch(std::move(batch)); + } + + void Handle(TEvPipeCache::TEvDeliveryProblem::TPtr&) { + TBase::ReplyErrorAndDie(Ydb::StatusIds::UNAVAILABLE, "Failed to request path info"); + } + + void PassAway() override { + TBase::PassAway(); + } + + void NavigatePath(TString path) { + auto request = MakeHolder<NSchemeCache::TSchemeCacheNavigate>(); + + auto& entry = request->ResultSet.emplace_back(); + entry.RequestType = TSchemeCacheNavigate::TEntry::ERequestType::ByPath; + entry.Path = SplitPath(path); + entry.Operation = TSchemeCacheNavigate::OpPath; + entry.RedirectRequired = false; + + LOG_TRACE_S(TlsActivationContext->AsActorContext(), NKikimrServices::SYSTEM_VIEWS, + "Navigate " << path << ": " << request->ToString(*AppData()->TypeRegistry)); + + TBase::Send(MakeSchemeCacheID(), new TEvTxProxySchemeCache::TEvNavigateKeySet(request.Release())); + } + + virtual void FillBatch(NKqp::TEvKqpCompute::TEvScanData& batch, const TNavigate::TResultSet& resultSet) = 0; + +}; + +} diff --git a/ydb/core/sys_view/auth/group_members.cpp b/ydb/core/sys_view/auth/group_members.cpp new file mode 100644 index 0000000000..0d9a8cdf29 --- /dev/null +++ b/ydb/core/sys_view/auth/group_members.cpp @@ -0,0 +1,70 @@ +#include "auth_scan_base.h" +#include "group_members.h" + +#include <ydb/core/sys_view/common/events.h> +#include <ydb/core/sys_view/common/schema.h> +#include <ydb/core/sys_view/common/scan_actor_base_impl.h> +#include <ydb/core/base/tablet_pipecache.h> +#include <ydb/library/login/protos/login.pb.h> + +#include <ydb/library/actors/core/hfunc.h> + +namespace NKikimr::NSysView::NAuth { + +using namespace NSchemeShard; +using namespace NActors; + +class TGroupMembersScan : public TAuthScanBase<TGroupMembersScan> { +public: + using TScanBase = TScanActorBase<TGroupMembersScan>; + using TAuthBase = TAuthScanBase<TGroupMembersScan>; + + TGroupMembersScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns) + : TAuthBase(ownerId, scanId, tableId, tableRange, columns) + { + } + +protected: + void FillBatch(NKqp::TEvKqpCompute::TEvScanData& batch, const TNavigate::TResultSet& resultSet) override { + Y_ABORT_UNLESS(resultSet.size() == 1); + auto& entry = resultSet.back(); + Y_ABORT_UNLESS(entry.Status == TNavigate::EStatus::Ok); + Y_ABORT_UNLESS(CanonizePath(entry.Path) == TBase::TenantName); + + TVector<TCell> cells(::Reserve(Columns.size())); + + // TODO: add rows according to request's sender user rights + + for (const auto& group : entry.DomainInfo->Groups) { + for (const auto& member : group.Members) { + for (auto& column : Columns) { + switch (column.Tag) { + case Schema::AuthGroupMembers::GroupSid::ColumnId: + cells.push_back(TCell(group.Sid.data(), group.Sid.size())); + break; + case Schema::AuthGroupMembers::MemberSid::ColumnId: + cells.push_back(TCell(member.data(), member.size())); + break; + default: + cells.emplace_back(); + } + } + + TArrayRef<const TCell> ref(cells); + batch.Rows.emplace_back(TOwnedCellVec::Make(ref)); + cells.clear(); + } + } + + batch.Finished = true; + } +}; + +THolder<NActors::IActor> CreateGroupMembersScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns) +{ + return MakeHolder<TGroupMembersScan>(ownerId, scanId, tableId, tableRange, columns); +} + +} diff --git a/ydb/core/sys_view/auth/group_members.h b/ydb/core/sys_view/auth/group_members.h new file mode 100644 index 0000000000..183875871e --- /dev/null +++ b/ydb/core/sys_view/auth/group_members.h @@ -0,0 +1,13 @@ +#pragma once + +#include <ydb/core/kqp/runtime/kqp_compute.h> + +#include <ydb/library/actors/core/actor.h> +#include <ydb/library/actors/core/actorid.h> + +namespace NKikimr::NSysView::NAuth { + +THolder<NActors::IActor> CreateGroupMembersScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns); + +} diff --git a/ydb/core/sys_view/auth/groups.cpp b/ydb/core/sys_view/auth/groups.cpp new file mode 100644 index 0000000000..805318a41b --- /dev/null +++ b/ydb/core/sys_view/auth/groups.cpp @@ -0,0 +1,65 @@ +#include "auth_scan_base.h" +#include "groups.h" + +#include <ydb/core/sys_view/common/events.h> +#include <ydb/core/sys_view/common/schema.h> +#include <ydb/core/sys_view/common/scan_actor_base_impl.h> +#include <ydb/core/base/tablet_pipecache.h> +#include <ydb/library/login/protos/login.pb.h> + +#include <ydb/library/actors/core/hfunc.h> + +namespace NKikimr::NSysView::NAuth { + +using namespace NSchemeShard; +using namespace NActors; + +class TGroupsScan : public TAuthScanBase<TGroupsScan> { +public: + using TScanBase = TScanActorBase<TGroupsScan>; + using TAuthBase = TAuthScanBase<TGroupsScan>; + + TGroupsScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns) + : TAuthBase(ownerId, scanId, tableId, tableRange, columns) + { + } + +protected: + void FillBatch(NKqp::TEvKqpCompute::TEvScanData& batch, const TNavigate::TResultSet& resultSet) override { + Y_ABORT_UNLESS(resultSet.size() == 1); + auto& entry = resultSet.back(); + Y_ABORT_UNLESS(entry.Status == TNavigate::EStatus::Ok); + Y_ABORT_UNLESS(CanonizePath(entry.Path) == TBase::TenantName); + + TVector<TCell> cells(::Reserve(Columns.size())); + + // TODO: add rows according to request's sender user rights + + for (const auto& group : entry.DomainInfo->Groups) { + for (auto& column : Columns) { + switch (column.Tag) { + case Schema::AuthGroups::Sid::ColumnId: + cells.push_back(TCell(group.Sid.data(), group.Sid.size())); + break; + default: + cells.emplace_back(); + } + } + + TArrayRef<const TCell> ref(cells); + batch.Rows.emplace_back(TOwnedCellVec::Make(ref)); + cells.clear(); + } + + batch.Finished = true; + } +}; + +THolder<NActors::IActor> CreateGroupsScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns) +{ + return MakeHolder<TGroupsScan>(ownerId, scanId, tableId, tableRange, columns); +} + +} diff --git a/ydb/core/sys_view/auth/groups.h b/ydb/core/sys_view/auth/groups.h new file mode 100644 index 0000000000..512785d46a --- /dev/null +++ b/ydb/core/sys_view/auth/groups.h @@ -0,0 +1,13 @@ +#pragma once + +#include <ydb/core/kqp/runtime/kqp_compute.h> + +#include <ydb/library/actors/core/actor.h> +#include <ydb/library/actors/core/actorid.h> + +namespace NKikimr::NSysView::NAuth { + +THolder<NActors::IActor> CreateGroupsScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns); + +} diff --git a/ydb/core/sys_view/auth/users.cpp b/ydb/core/sys_view/auth/users.cpp new file mode 100644 index 0000000000..339b07da72 --- /dev/null +++ b/ydb/core/sys_view/auth/users.cpp @@ -0,0 +1,65 @@ +#include "auth_scan_base.h" +#include "users.h" + +#include <ydb/core/sys_view/common/events.h> +#include <ydb/core/sys_view/common/schema.h> +#include <ydb/core/sys_view/common/scan_actor_base_impl.h> +#include <ydb/core/base/tablet_pipecache.h> +#include <ydb/library/login/protos/login.pb.h> + +#include <ydb/library/actors/core/hfunc.h> + +namespace NKikimr::NSysView::NAuth { + +using namespace NSchemeShard; +using namespace NActors; + +class TUsersScan : public TAuthScanBase<TUsersScan> { +public: + using TScanBase = TScanActorBase<TUsersScan>; + using TAuthBase = TAuthScanBase<TUsersScan>; + + TUsersScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns) + : TAuthBase(ownerId, scanId, tableId, tableRange, columns) + { + } + +protected: + void FillBatch(NKqp::TEvKqpCompute::TEvScanData& batch, const TNavigate::TResultSet& resultSet) override { + Y_ABORT_UNLESS(resultSet.size() == 1); + auto& entry = resultSet.back(); + Y_ABORT_UNLESS(entry.Status == TNavigate::EStatus::Ok); + Y_ABORT_UNLESS(CanonizePath(entry.Path) == TBase::TenantName); + + TVector<TCell> cells(::Reserve(Columns.size())); + + // TODO: add rows according to request's sender user rights + + for (const auto& user : entry.DomainInfo->Users) { + for (auto& column : Columns) { + switch (column.Tag) { + case Schema::AuthUsers::Sid::ColumnId: + cells.push_back(TCell(user.Sid.data(), user.Sid.size())); + break; + default: + cells.emplace_back(); + } + } + + TArrayRef<const TCell> ref(cells); + batch.Rows.emplace_back(TOwnedCellVec::Make(ref)); + cells.clear(); + } + + batch.Finished = true; + } +}; + +THolder<NActors::IActor> CreateUsersScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns) +{ + return MakeHolder<TUsersScan>(ownerId, scanId, tableId, tableRange, columns); +} + +} diff --git a/ydb/core/sys_view/auth/users.h b/ydb/core/sys_view/auth/users.h new file mode 100644 index 0000000000..23ab3d77a6 --- /dev/null +++ b/ydb/core/sys_view/auth/users.h @@ -0,0 +1,13 @@ +#pragma once + +#include <ydb/core/kqp/runtime/kqp_compute.h> + +#include <ydb/library/actors/core/actor.h> +#include <ydb/library/actors/core/actorid.h> + +namespace NKikimr::NSysView::NAuth { + +THolder<NActors::IActor> CreateUsersScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef<NMiniKQL::TKqpComputeContextBase::TColumn>& columns); + +} diff --git a/ydb/core/sys_view/auth/ya.make b/ydb/core/sys_view/auth/ya.make new file mode 100644 index 0000000000..72ef7d11f2 --- /dev/null +++ b/ydb/core/sys_view/auth/ya.make @@ -0,0 +1,21 @@ +LIBRARY() + +SRCS( + group_members.h + group_members.cpp + users.h + users.cpp + groups.h + groups.cpp +) + +PEERDIR( + ydb/library/actors/core + ydb/core/base + ydb/core/kqp/runtime + ydb/core/sys_view/common +) + +YQL_LAST_ABI_VERSION() + +END() diff --git a/ydb/core/sys_view/common/processor_scan.h b/ydb/core/sys_view/common/processor_scan.h index 1d181f0ae1..1bf999029c 100644 --- a/ydb/core/sys_view/common/processor_scan.h +++ b/ydb/core/sys_view/common/processor_scan.h @@ -74,9 +74,7 @@ private: auto req = MakeHolder<TEvRequest>(); req->Record.CopyFrom(Request); - TBase::Send(MakePipePerNodeCacheID(false), - new TEvPipeCache::TEvForward(req.Release(), this->SysViewProcessorId, true), - IEventHandle::FlagTrackDelivery); + this->SendThroughPipeCache(req.Release(), this->SysViewProcessorId); this->BatchRequestInFlight = true; } @@ -110,7 +108,6 @@ private: } void PassAway() override { - TBase::Send(MakePipePerNodeCacheID(false), new TEvPipeCache::TEvUnlink(0)); TBase::PassAway(); } 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 966e20e039..c126bdbb8b 100644 --- a/ydb/core/sys_view/common/scan_actor_base_impl.h +++ b/ydb/core/sys_view/common/scan_actor_base_impl.h @@ -3,6 +3,7 @@ #include "utils.h" #include <ydb/core/kqp/compute_actor/kqp_compute_events.h> +#include <ydb/core/kqp/runtime/kqp_compute.h> #include <ydb/core/tx/scheme_cache/scheme_cache.h> #include <ydb/core/mind/tenant_node_enumeration.h> #include <ydb/core/sys_view/service/sysview_service.h> @@ -51,6 +52,12 @@ public: } protected: + void SendThroughPipeCache(IEventBase* ev, ui64 tabletId) { + DoPipeCacheUnlink = true; + TBase::Send(MakePipePerNodeCacheID(false), new TEvPipeCache::TEvForward(ev, tabletId, true), + IEventHandle::FlagTrackDelivery); + } + void SendBatch(THolder<NKqp::TEvKqpCompute::TEvScanData> batch) { LOG_DEBUG_S(TlsActivationContext->AsActorContext(), NKikimrServices::SYSTEM_VIEWS, "Sending scan batch, actor: " << TBase::SelfId() @@ -116,11 +123,11 @@ protected: ScanLimiter->Dec(); } - TBase::PassAway(); - } + if (std::exchange(DoPipeCacheUnlink, false)) { + TBase::Send(MakePipePerNodeCacheID(false), new TEvPipeCache::TEvUnlink(0)); + } - ui64 GetBSControllerId() { - return MakeBSControllerID(); + TBase::PassAway(); } template <typename TResponse, typename TEntry, typename TExtractorsMap, bool BatchSupport = false> @@ -321,6 +328,7 @@ protected: bool AckReceived = false; bool BatchRequestInFlight = false; + bool DoPipeCacheUnlink = false; private: enum EFailState { diff --git a/ydb/core/sys_view/common/schema.cpp b/ydb/core/sys_view/common/schema.cpp index 34703a2e64..6355ec528e 100644 --- a/ydb/core/sys_view/common/schema.cpp +++ b/ydb/core/sys_view/common/schema.cpp @@ -286,6 +286,13 @@ private: RegisterSystemView<Schema::TopPartitions>(TopPartitions1HourName); RegisterPgTablesSystemViews(); + + { + using namespace NAuth; + RegisterSystemView<Schema::AuthUsers>(UsersName); + RegisterSystemView<Schema::AuthGroups>(NAuth::GroupsName); + RegisterSystemView<Schema::AuthGroupMembers>(GroupMembersName); + } } private: diff --git a/ydb/core/sys_view/common/schema.h b/ydb/core/sys_view/common/schema.h index 4c134ad597..e6592f66be 100644 --- a/ydb/core/sys_view/common/schema.h +++ b/ydb/core/sys_view/common/schema.h @@ -49,6 +49,12 @@ constexpr TStringBuf PgTablesName = "pg_tables"; constexpr TStringBuf InformationSchemaTablesName = "tables"; constexpr TStringBuf PgClassName = "pg_class"; +namespace NAuth { + constexpr TStringBuf UsersName = "auth_users"; + constexpr TStringBuf GroupsName = "auth_groups"; + constexpr TStringBuf GroupMembersName = "auth_group_members"; +} + struct Schema : NIceDb::Schema { struct PartitionStats : Table<1> { @@ -611,6 +617,34 @@ struct Schema : NIceDb::Schema { >; }; + struct AuthUsers : Table<15> { + struct Sid: Column<1, NScheme::NTypeIds::Utf8> {}; + + using TKey = TableKey<Sid>; + using TColumns = TableColumns< + Sid + >; + }; + + struct AuthGroups : Table<16> { + struct Sid: Column<1, NScheme::NTypeIds::Utf8> {}; + + using TKey = TableKey<Sid>; + using TColumns = TableColumns< + Sid + >; + }; + + struct AuthGroupMembers : Table<17> { + struct GroupSid: Column<1, NScheme::NTypeIds::Utf8> {}; + struct MemberSid: Column<2, NScheme::NTypeIds::Utf8> {}; + + using TKey = TableKey<GroupSid, MemberSid>; + using TColumns = TableColumns< + GroupSid, + MemberSid + >; + }; struct PgColumn { NIceDb::TColumnId _ColumnId; diff --git a/ydb/core/sys_view/query_stats/query_stats.cpp b/ydb/core/sys_view/query_stats/query_stats.cpp index c2e26a2379..35a24b2d94 100644 --- a/ydb/core/sys_view/query_stats/query_stats.cpp +++ b/ydb/core/sys_view/query_stats/query_stats.cpp @@ -172,9 +172,7 @@ private: auto request = MakeHolder<TEvSysView::TEvGetQueryMetricsRequest>(); request->Record.CopyFrom(Request); - this->Send(MakePipePerNodeCacheID(false), - new TEvPipeCache::TEvForward(request.Release(), this->SysViewProcessorId, true), - IEventHandle::FlagTrackDelivery); + this->SendThroughPipeCache(request.Release(), this->SysViewProcessorId); this->BatchRequestInFlight = true; } @@ -468,9 +466,7 @@ private: } void PassAway() override { - if (UseProcessor) { - this->Send(MakePipePerNodeCacheID(false), new TEvPipeCache::TEvUnlink(0)); - } else { + if (!UseProcessor) { for (auto& [nodeId, _] : Nodes) { this->Send(TActorContext::InterconnectProxy(nodeId), new TEvents::TEvUnsubscribe); } diff --git a/ydb/core/sys_view/scan.cpp b/ydb/core/sys_view/scan.cpp index 87fb15cd96..763689b4c9 100644 --- a/ydb/core/sys_view/scan.cpp +++ b/ydb/core/sys_view/scan.cpp @@ -2,6 +2,9 @@ #include <ydb/core/kqp/compute_actor/kqp_compute_events.h> +#include <ydb/core/sys_view/auth/users.h> +#include <ydb/core/sys_view/auth/groups.h> +#include <ydb/core/sys_view/auth/group_members.h> #include <ydb/core/sys_view/common/schema.h> #include <ydb/core/sys_view/partition_stats/partition_stats.h> #include <ydb/core/sys_view/nodes/nodes.h> @@ -233,10 +236,24 @@ THolder<NActors::IActor> CreateSystemViewScan( if (tableId.SysViewInfo == InformationSchemaTablesName) { return CreateInformationSchemaTablesScan(ownerId, scanId, tableId, tablePath, tableRange, columns); } - if (tableId.SysViewInfo == PgClassName) { + + if (tableId.SysViewInfo == PgClassName) { return CreatePgClassScan(ownerId, scanId, tableId, tablePath, tableRange, columns); } + { + using namespace NAuth; + if (tableId.SysViewInfo == UsersName) { + return CreateUsersScan(ownerId, scanId, tableId, tableRange, columns); + } + if (tableId.SysViewInfo == NAuth::GroupsName) { + return NAuth::CreateGroupsScan(ownerId, scanId, tableId, tableRange, columns); + } + if (tableId.SysViewInfo == GroupMembersName) { + return NAuth::CreateGroupMembersScan(ownerId, scanId, tableId, tableRange, columns); + } + } + return {}; } diff --git a/ydb/core/sys_view/storage/base.h b/ydb/core/sys_view/storage/base.h index 65c8aa2910..709be1ae64 100644 --- a/ydb/core/sys_view/storage/base.h +++ b/ydb/core/sys_view/storage/base.h @@ -42,14 +42,9 @@ namespace NKikimr::NSysView { } void StartScan() { - ui64 bsControllerId = TBase::GetBSControllerId(); - if (!bsControllerId) { - return; - } - auto pipeCache = MakePipePerNodeCacheID(false); TBase::Send(pipeCache, new TEvPipeCache::TEvForward(static_cast<TDerived&>(*this).CreateQuery(), - bsControllerId, true), IEventHandle::FlagTrackDelivery); + MakeBSControllerID(), true), IEventHandle::FlagTrackDelivery); } void Handle(NKqp::TEvKqpCompute::TEvScanDataAck::TPtr&) { diff --git a/ydb/core/sys_view/tablets/tablets.cpp b/ydb/core/sys_view/tablets/tablets.cpp index ffecd239b1..b976c47399 100644 --- a/ydb/core/sys_view/tablets/tablets.cpp +++ b/ydb/core/sys_view/tablets/tablets.cpp @@ -180,9 +180,7 @@ private: record.SetFrom(FromTabletId); record.SetTo(ToTabletId); - auto pipeCache = MakePipePerNodeCacheID(false); - Send(pipeCache, new TEvPipeCache::TEvForward(request.Release(), HiveId, true), - IEventHandle::FlagTrackDelivery); + SendThroughPipeCache(request.Release(), HiveId); } void RequestBatch() { @@ -202,9 +200,7 @@ private: record.SetBatchSizeLimit(BatchSize); - auto pipeCache = MakePipePerNodeCacheID(false); - Send(pipeCache, new TEvPipeCache::TEvForward(request.Release(), HiveId, true), - IEventHandle::FlagTrackDelivery); + SendThroughPipeCache(request.Release(), HiveId); } void Handle(TEvPipeCache::TEvDeliveryProblem::TPtr&) { @@ -338,7 +334,6 @@ private: } void PassAway() override { - Send(MakePipePerNodeCacheID(false), new TEvPipeCache::TEvUnlink(0)); TBase::PassAway(); } diff --git a/ydb/core/sys_view/ut_kqp.cpp b/ydb/core/sys_view/ut_kqp.cpp index 004747b0fd..aa5ba9ff0e 100644 --- a/ydb/core/sys_view/ut_kqp.cpp +++ b/ydb/core/sys_view/ut_kqp.cpp @@ -1730,7 +1730,7 @@ Y_UNIT_TEST_SUITE(SystemView) { UNIT_ASSERT_VALUES_EQUAL(entry.Type, ESchemeEntryType::Directory); auto children = result.GetChildren(); - UNIT_ASSERT_VALUES_EQUAL(children.size(), 23); + UNIT_ASSERT_VALUES_EQUAL(children.size(), 26); THashSet<TString> names; for (const auto& child : children) { @@ -1748,7 +1748,7 @@ Y_UNIT_TEST_SUITE(SystemView) { UNIT_ASSERT_VALUES_EQUAL(entry.Type, ESchemeEntryType::Directory); auto children = result.GetChildren(); - UNIT_ASSERT_VALUES_EQUAL(children.size(), 17); + UNIT_ASSERT_VALUES_EQUAL(children.size(), 20); THashSet<TString> names; for (const auto& child : children) { @@ -2134,6 +2134,198 @@ Y_UNIT_TEST_SUITE(SystemView) { [[0u]]; ])", ysonString); } + + Y_UNIT_TEST(AuthUsers) { + TTestEnv env; + env.GetServer().GetRuntime()->SetLogPriority(NKikimrServices::FLAT_TX_SCHEMESHARD, NLog::PRI_DEBUG); + env.GetServer().GetRuntime()->SetLogPriority(NKikimrServices::SYSTEM_VIEWS, NLog::PRI_TRACE); + CreateTenantsAndTables(env, true); + 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", "group1"); + env.GetClient().CreateGroup("/Root/Tenant1", "group2"); + env.GetClient().CreateGroup("/Root/Tenant2", "group3"); + env.GetClient().CreateGroup("/Root/Tenant2", "group4"); + + // Cerr << env.GetClient().Describe(env.GetServer().GetRuntime(), "/Root").DebugString() << Endl; + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_users` + )").GetValueSync(); + + auto expected = R"([ + [["user1"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant1/.sys/auth_users` + )").GetValueSync(); + + auto expected = R"([ + [["user2"]] + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant2/.sys/auth_users` + )").GetValueSync(); + + auto expected = R"([ + [["user4"]]; + [["user3"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + } + + Y_UNIT_TEST(AuthGroups) { + TTestEnv env; + env.GetServer().GetRuntime()->SetLogPriority(NKikimrServices::FLAT_TX_SCHEMESHARD, NLog::PRI_DEBUG); + env.GetServer().GetRuntime()->SetLogPriority(NKikimrServices::SYSTEM_VIEWS, NLog::PRI_TRACE); + CreateTenantsAndTables(env, true); + 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", "group1"); + env.GetClient().CreateGroup("/Root/Tenant1", "group2"); + env.GetClient().CreateGroup("/Root/Tenant2", "group3"); + env.GetClient().CreateGroup("/Root/Tenant2", "group4"); + + // Cerr << env.GetClient().Describe(env.GetServer().GetRuntime(), "/Root").DebugString() << Endl; + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/.sys/auth_groups` + )").GetValueSync(); + + auto expected = R"([ + [["group1"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant1/.sys/auth_groups` + )").GetValueSync(); + + auto expected = R"([ + [["group2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant2/.sys/auth_groups` + )").GetValueSync(); + + auto expected = R"([ + [["group4"]]; + [["group3"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + } + + + + Y_UNIT_TEST(AuthGroupMembers) { + TTestEnv env; + env.GetServer().GetRuntime()->SetLogPriority(NKikimrServices::FLAT_TX_SCHEMESHARD, NLog::PRI_DEBUG); + env.GetServer().GetRuntime()->SetLogPriority(NKikimrServices::SYSTEM_VIEWS, NLog::PRI_TRACE); + CreateTenantsAndTables(env, true); + 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", "group1"); + env.GetClient().CreateGroup("/Root/Tenant1", "group2"); + env.GetClient().CreateGroup("/Root/Tenant2", "group3"); + env.GetClient().CreateGroup("/Root/Tenant2", "group4"); + env.GetClient().CreateGroup("/Root/Tenant2", "group5"); + + env.GetClient().AddGroupMembership("/Root", "group1", "user1"); + env.GetClient().AddGroupMembership("/Root/Tenant1", "group2", "user2"); + env.GetClient().AddGroupMembership("/Root/Tenant2", "group3", "user4"); + env.GetClient().AddGroupMembership("/Root/Tenant2", "group4", "user3"); + env.GetClient().AddGroupMembership("/Root/Tenant2", "group4", "user4"); + env.GetClient().AddGroupMembership("/Root/Tenant2", "group4", "group3"); + env.GetClient().AddGroupMembership("/Root/Tenant2", "group4", "group4"); + + // 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_group_members` + )").GetValueSync(); + + auto expected = R"([ + [["group1"];["user1"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant1/.sys/auth_group_members` + )").GetValueSync(); + + auto expected = R"([ + [["group2"];["user2"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + + { + auto it = client.StreamExecuteScanQuery(R"( + SELECT * + FROM `Root/Tenant2/.sys/auth_group_members` + )").GetValueSync(); + + auto expected = R"([ + [["group4"];["group4"]]; + [["group4"];["group3"]]; + [["group4"];["user4"]]; + [["group4"];["user3"]]; + [["group3"];["user4"]]; + ])"; + + NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); + } + } } } // NSysView diff --git a/ydb/core/sys_view/ya.make b/ydb/core/sys_view/ya.make index df596d8438..74bfd9e155 100644 --- a/ydb/core/sys_view/ya.make +++ b/ydb/core/sys_view/ya.make @@ -8,6 +8,7 @@ SRCS( PEERDIR( ydb/core/base ydb/core/kqp/runtime + ydb/core/sys_view/auth ydb/core/sys_view/common ydb/core/sys_view/nodes ydb/core/sys_view/sessions @@ -24,6 +25,7 @@ YQL_LAST_ABI_VERSION() END() RECURSE( + auth common nodes partition_stats diff --git a/ydb/core/testlib/test_client.cpp b/ydb/core/testlib/test_client.cpp index 2a22901ee5..ea5b47e416 100644 --- a/ydb/core/testlib/test_client.cpp +++ b/ydb/core/testlib/test_client.cpp @@ -1772,6 +1772,39 @@ namespace Tests { return event->Record; } + NMsgBusProxy::EResponseStatus TClient::CreateGroup(const TString& parent, const TString& group) { + TAutoPtr<NMsgBusProxy::TBusSchemeOperation> request(new NMsgBusProxy::TBusSchemeOperation()); + auto* op = request->Record.MutableTransaction()->MutableModifyScheme(); + op->SetOperationType(NKikimrSchemeOp::EOperationType::ESchemeOpAlterLogin); + op->SetWorkingDir(parent); + + auto* createUser = op->MutableAlterLogin()->MutableCreateGroup(); + createUser->SetGroup(group); + + TAutoPtr<NBus::TBusMessage> reply; + NBus::EMessageStatus status = SendAndWaitCompletion(request.Release(), reply); + UNIT_ASSERT_VALUES_EQUAL(status, NBus::MESSAGE_OK); + const NKikimrClient::TResponse &response = dynamic_cast<NMsgBusProxy::TBusResponse *>(reply.Get())->Record; + return (NMsgBusProxy::EResponseStatus)response.GetStatus(); + } + + NMsgBusProxy::EResponseStatus TClient::AddGroupMembership(const TString& parent, const TString& group, const TString& member) { + TAutoPtr<NMsgBusProxy::TBusSchemeOperation> request(new NMsgBusProxy::TBusSchemeOperation()); + auto* op = request->Record.MutableTransaction()->MutableModifyScheme(); + op->SetOperationType(NKikimrSchemeOp::EOperationType::ESchemeOpAlterLogin); + op->SetWorkingDir(parent); + + auto* createUser = op->MutableAlterLogin()->MutableAddGroupMembership(); + createUser->SetGroup(group); + createUser->SetMember(member); + + TAutoPtr<NBus::TBusMessage> reply; + NBus::EMessageStatus status = SendAndWaitCompletion(request.Release(), reply); + UNIT_ASSERT_VALUES_EQUAL(status, NBus::MESSAGE_OK); + const NKikimrClient::TResponse &response = dynamic_cast<NMsgBusProxy::TBusResponse *>(reply.Get())->Record; + return (NMsgBusProxy::EResponseStatus)response.GetStatus(); + } + NMsgBusProxy::EResponseStatus TClient::CreateTable(const TString& parent, const NKikimrSchemeOp::TTableDescription &table, TDuration timeout) { TAutoPtr<NMsgBusProxy::TBusSchemeOperation> request(new NMsgBusProxy::TBusSchemeOperation()); auto *op = request->Record.MutableTransaction()->MutableModifyScheme(); @@ -2221,6 +2254,24 @@ namespace Tests { return res; } + NKikimrScheme::TEvDescribeSchemeResult TClient::Describe(TTestActorRuntime* runtime, const TString& path, ui64 tabletId) { + TAutoPtr<NSchemeShard::TEvSchemeShard::TEvDescribeScheme> request(new NSchemeShard::TEvSchemeShard::TEvDescribeScheme()); + request->Record.SetPath(path); + const ui64 schemeRoot = GetPatchedSchemeRoot(tabletId, Domain, SupportsRedirect); + TActorId sender = runtime->AllocateEdgeActor(0); + ForwardToTablet(*runtime, schemeRoot, sender, request.Release(), 0); + + TAutoPtr<IEventHandle> handle; + runtime->GrabEdgeEvent<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult>(handle); + auto& record = handle->Get<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult>()->GetRecord(); + + if (auto schemeShardId = record.GetPathDescription().GetDomainDescription().GetProcessingParams().GetSchemeShard(); schemeShardId && schemeShardId != tabletId) { + return Describe(runtime, path, schemeShardId); + } else { + return record; + } + } + TString TClient::CreateStoragePool(const TString& poolKind, const TString& partOfName, ui32 groups) { Y_ABORT_UNLESS(StoragePoolTypes.contains(poolKind)); const TString poolName = Sprintf("name_%s_kind_%s", partOfName.c_str(), poolKind.c_str()); diff --git a/ydb/core/testlib/test_client.h b/ydb/core/testlib/test_client.h index c6292ed665..75e9486107 100644 --- a/ydb/core/testlib/test_client.h +++ b/ydb/core/testlib/test_client.h @@ -6,6 +6,7 @@ #include <ydb/core/base/tablet_types.h> #include <ydb/core/base/domain.h> #include <ydb/core/driver_lib/run/config.h> +#include <ydb/core/tx/schemeshard/schemeshard.h> #include <ydb/public/api/protos/ydb_cms.pb.h> #include <ydb/public/sdk/cpp/client/ydb_driver/driver.h> #include <ydb/public/lib/deprecated/client/msgbus_client.h> @@ -460,7 +461,9 @@ namespace Tests { NMsgBusProxy::EResponseStatus CreateUser(const TString& parent, const TString& user, const TString& password, const TString& userToken = ""); NMsgBusProxy::EResponseStatus ModifyUser(const TString& parent, const TString& user, const TString& password, const TString& userToken = ""); NKikimrScheme::TEvLoginResult Login(TTestActorRuntime& runtime, const TString& user, const TString& password); - + NMsgBusProxy::EResponseStatus CreateGroup(const TString& parent, const TString& group); + NMsgBusProxy::EResponseStatus AddGroupMembership(const TString& parent, const TString& group, const TString& member); + NMsgBusProxy::EResponseStatus CreateTable(const TString& parent, const TString& scheme, TDuration timeout = TDuration::Seconds(5000)); NMsgBusProxy::EResponseStatus CreateTable(const TString& parent, const NKikimrSchemeOp::TTableDescription &table, TDuration timeout = TDuration::Seconds(5000)); NMsgBusProxy::EResponseStatus CreateTableWithUniformShardedIndex(const TString& parent, @@ -513,6 +516,7 @@ namespace Tests { void SetSecurityToken(const TString& token) { SecurityToken = token; } void ModifyOwner(const TString& parent, const TString& name, const TString& owner); void ModifyACL(const TString& parent, const TString& name, const TString& acl); + NKikimrScheme::TEvDescribeSchemeResult Describe(TTestActorRuntime* runtime, const TString& path, ui64 tabletId = SchemeRoot); TString CreateStoragePool(const TString& poolKind, const TString& partOfName, ui32 groups = 1); NKikimrBlobStorage::TDefineStoragePool DescribeStoragePool(const TString& name); void RemoveStoragePool(const TString& name); diff --git a/ydb/core/tx/scheme_cache/scheme_cache.cpp b/ydb/core/tx/scheme_cache/scheme_cache.cpp index 67caca5044..8e5a3a367c 100644 --- a/ydb/core/tx/scheme_cache/scheme_cache.cpp +++ b/ydb/core/tx/scheme_cache/scheme_cache.cpp @@ -20,14 +20,60 @@ TSchemeCacheConfig::TSchemeCacheConfig(const TAppData* appData, ::NMonitoring::T } TString TDomainInfo::ToString() const { - return TStringBuilder() << "{" + auto result = TStringBuilder() << "{" << " DomainKey: " << DomainKey << " ResourcesDomainKey: " << ResourcesDomainKey << " Params { " << Params.ShortDebugString() << " }" - << " ServerlessComputeResourcesMode: " << ServerlessComputeResourcesMode + << " ServerlessComputeResourcesMode: " << ServerlessComputeResourcesMode; + + result << " Users: ["; + for (ui32 i = 0; i < Users.size(); ++i) { + if (i) { + result << ","; + } + + result << Users.at(i).ToString(); + } + result << "]"; + + result << " Groups: ["; + for (ui32 i = 0; i < Groups.size(); ++i) { + if (i) { + result << ","; + } + + result << Groups.at(i).ToString(); + } + result << "]"; + + result << " }"; + return result; +} + +TString TDomainInfo::TUser::ToString() const { + return TStringBuilder() << "{" + << " Sid: " << Sid << " }"; } +TString TDomainInfo::TGroup::ToString() const { + auto result = TStringBuilder() << "{" + << " Sid: " << Sid; + + result << " Members: ["; + for (ui32 i = 0; i < Members.size(); ++i) { + if (i) { + result << ","; + } + + result << Members.at(i); + } + result << "]"; + + result << " }"; + return result; +} + TString TSchemeCacheNavigate::TEntry::ToString() const { return TStringBuilder() << "{" << " Path: " << JoinPath(Path) diff --git a/ydb/core/tx/scheme_cache/scheme_cache.h b/ydb/core/tx/scheme_cache/scheme_cache.h index 6798bf4164..966a6da936 100644 --- a/ydb/core/tx/scheme_cache/scheme_cache.h +++ b/ydb/core/tx/scheme_cache/scheme_cache.h @@ -13,6 +13,7 @@ #include <ydb/core/scheme_types/scheme_type_registry.h> #include <ydb/core/tx/locks/sys_tables.h> #include <ydb/library/aclib/aclib.h> +#include <ydb/library/login/protos/login.pb.h> #include <util/datetime/base.h> #include <util/generic/hash.h> @@ -48,6 +49,19 @@ struct TSchemeCacheConfig : public TThrRefBase { struct TDomainInfo : public TAtomicRefCount<TDomainInfo> { using TPtr = TIntrusivePtr<TDomainInfo>; + struct TUser { + TString Sid; + + TString ToString() const; + }; + + struct TGroup { + TString Sid; + TVector<TString> Members; + + TString ToString() const; + }; + explicit TDomainInfo(const TPathId& domainKey, const TPathId& resourcesDomainKey) : DomainKey(domainKey) , ResourcesDomainKey(resourcesDomainKey) @@ -72,6 +86,23 @@ struct TDomainInfo : public TAtomicRefCount<TDomainInfo> { if (descr.HasSharedHive()) { SharedHiveId = descr.GetSharedHive(); } + + if (descr.HasSecurityState()) { + for (const auto& sid : descr.GetSecurityState().GetSids()) { + switch (sid.GetType()) { + case NLoginProto::ESidType_SidType_USER: + Users.emplace_back(sid.GetName()); + break; + case NLoginProto::ESidType_SidType_GROUP: { + TVector<TString> members(sid.GetMembers().begin(), sid.GetMembers().end()); + Groups.emplace_back(sid.GetName(), std::move(members)); + break; + } + default: + break; + } + } + } } inline ui64 GetVersion() const { @@ -104,6 +135,8 @@ struct TDomainInfo : public TAtomicRefCount<TDomainInfo> { TCoordinators Coordinators; TMaybeServerlessComputeResourcesMode ServerlessComputeResourcesMode; ui64 SharedHiveId = 0; + TVector<TUser> Users; + TVector<TGroup> Groups; TString ToString() const; |