Browse Source

Scheme Shard List users request (#13344)

kungurtsev 1 month ago
parent
commit
fc214dfe8a

+ 2 - 0
ydb/core/protos/counters_schemeshard.proto

@@ -607,4 +607,6 @@ enum ETxTypes {
     TXTYPE_CREATE_BACKUP_COLLECTION_RESULT = 87                 [(TxTypeOpts) = {Name: "TxCreateBackupCollectionResult"}];
     TXTYPE_ALTER_BACKUP_COLLECTION_RESULT = 88                  [(TxTypeOpts) = {Name: "TxAlterBackupCollectionResult"}];
     TXTYPE_DROP_BACKUP_COLLECTION_RESULT = 89                   [(TxTypeOpts) = {Name: "TxDropBackupCollectionResult"}];
+
+    TXTYPE_LIST_USERS = 90                                       [(TxTypeOpts) = {Name: "TxListUsers"}];
 }

+ 19 - 0
ydb/core/protos/flat_tx_scheme.proto

@@ -157,6 +157,25 @@ message TEvLoginResult {
     optional bool IsAdmin = 4;
 }
 
+message TEvListUsers {
+
+}
+
+message TEvListUsersResult {
+    message TUser {
+        optional string Name = 1;
+        optional bool IsEnabled = 2;
+        optional bool IsLockedOut = 3;
+        optional uint64 CreatedAt = 4;
+        optional uint64 LastSuccessfulAttemptAt = 5;
+        optional uint64 LastFailedAttemptAt = 6;
+        optional uint32 FailedAttemptCount = 7;
+        optional string PasswordHash = 8; // JSON with type, salt and password hash 
+    }
+
+    repeated TUser Users = 1;
+}
+
 // Sending actor registers itself to be notified when tx completes
 message TEvNotifyTxCompletion {
     optional uint64 TxId = 1;

+ 108 - 10
ydb/core/sys_view/auth/users.cpp

@@ -14,31 +14,129 @@ namespace NKikimr::NSysView::NAuth {
 using namespace NSchemeShard;
 using namespace NActors;
 
-class TUsersScan : public TAuthScanBase<TUsersScan> {
+class TUsersScan : public TScanActorBase<TUsersScan> {
 public:
-    using TScanBase = TScanActorBase<TUsersScan>;
-    using TAuthBase = TAuthScanBase<TUsersScan>;
+    using TBase = TScanActorBase<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)
+        : TBase(ownerId, scanId, tableId, tableRange, columns)
     {
     }
 
+    STFUNC(StateScan) {
+        switch (ev->GetTypeRewrite()) {
+            HFunc(TEvSchemeShard::TEvListUsersResult, Handle);
+            hFunc(NKqp::TEvKqpCompute::TEvScanDataAck, Handle);
+            hFunc(TEvPipeCache::TEvDeliveryProblem, 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::TUsersScan: unexpected event 0x%08" PRIx32, ev->GetTypeRewrite());
+        }
+    }
+
 protected:
-    void FillBatch(NKqp::TEvKqpCompute::TEvScanData& batch, const TNavigate::TEntry& entry) override {
-        Y_ABORT_UNLESS(entry.Status == TNavigate::EStatus::Ok);
-        Y_ABORT_UNLESS(CanonizePath(entry.Path) == TBase::TenantName);
-        
+    void ProceedToScan() override {
+        TBase::Become(&TUsersScan::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;
+        }
+
+        auto request = MakeHolder<TEvSchemeShard::TEvListUsers>();
+
+        LOG_TRACE_S(TlsActivationContext->AsActorContext(), NKikimrServices::SYSTEM_VIEWS,
+            "Sending list users request " << request->Record.ShortUtf8DebugString());
+
+        TBase::SendThroughPipeCache(request.Release(), TBase::SchemeShardId);
+    }
+
+    void Handle(TEvSchemeShard::TEvListUsersResult::TPtr& ev, const TActorContext& ctx) {
+        const auto& record = ev->Get()->Record;
+
+        LOG_TRACE_S(ctx, NKikimrServices::SYSTEM_VIEWS,
+            "Got list users response " <<   record.ShortUtf8DebugString());
+
+        auto batch = MakeHolder<NKqp::TEvKqpCompute::TEvScanData>(TBase::ScanId);
+
+        FillBatch(*batch, record);
+
+        TBase::SendBatch(std::move(batch));
+    }
+
+    void Handle(TEvPipeCache::TEvDeliveryProblem::TPtr&) {
+        TBase::ReplyErrorAndDie(Ydb::StatusIds::UNAVAILABLE, "Failed to request domain info");
+    }
+
+    void PassAway() override {
+        TBase::PassAway();
+    }
+
+    void FillBatch(NKqp::TEvKqpCompute::TEvScanData& batch, const NKikimrScheme::TEvListUsersResult& result) {
         TVector<TCell> cells(::Reserve(Columns.size()));
 
         // TODO: add rows according to request's sender user rights
 
-        for (const auto& user : entry.DomainInfo->Users) {
+        for (const auto& user : result.GetUsers()) {
             for (auto& column : Columns) {
                 switch (column.Tag) {
                 case Schema::AuthUsers::Sid::ColumnId:
-                    cells.push_back(TCell(user.Sid.data(), user.Sid.size()));
+                    cells.push_back(user.HasName()
+                        ? TCell(user.GetName().data(), user.GetName().size())
+                        : TCell());
+                    break;
+                case Schema::AuthUsers::IsEnabled::ColumnId:
+                    cells.push_back(user.HasIsEnabled()
+                        ? TCell::Make(user.GetIsEnabled())
+                        : TCell());
+                    break;
+                case Schema::AuthUsers::IsLockedOut::ColumnId:
+                    cells.push_back(user.HasIsLockedOut()
+                        ? TCell::Make(user.GetIsLockedOut())
+                        : TCell());
+                    break;
+                case Schema::AuthUsers::CreatedAt::ColumnId:
+                    cells.push_back(user.HasCreatedAt()
+                        ? TCell::Make(user.GetCreatedAt())
+                        : TCell());
+                    break;
+                case Schema::AuthUsers::LastSuccessfulAttemptAt::ColumnId:
+                    cells.push_back(user.HasLastSuccessfulAttemptAt()
+                        ? TCell::Make(user.GetLastSuccessfulAttemptAt())
+                        : TCell());
+                    break;
+                case Schema::AuthUsers::LastFailedAttemptAt::ColumnId:
+                    cells.push_back(user.HasLastFailedAttemptAt()
+                        ? TCell::Make(user.GetLastFailedAttemptAt())
+                        : TCell());
+                    break;
+                case Schema::AuthUsers::FailedAttemptCount::ColumnId:
+                    cells.push_back(user.HasFailedAttemptCount()
+                        ? TCell::Make(user.GetFailedAttemptCount())
+                        : TCell());
+                    break;
+                case Schema::AuthUsers::PasswordHash::ColumnId:
+                    cells.push_back(user.HasPasswordHash()
+                        ? TCell(user.GetPasswordHash().data(), user.GetPasswordHash().size())
+                        : TCell());
                     break;
                 default:
                     cells.emplace_back();

+ 15 - 1
ydb/core/sys_view/common/schema.h

@@ -622,10 +622,24 @@ struct Schema : NIceDb::Schema {
 
     struct AuthUsers : Table<15> {
         struct Sid: Column<1, NScheme::NTypeIds::Utf8> {};
+        struct IsEnabled: Column<2, NScheme::NTypeIds::Bool> {};
+        struct IsLockedOut: Column<3, NScheme::NTypeIds::Bool> {};
+        struct CreatedAt: Column<4, NScheme::NTypeIds::Timestamp> {};
+        struct LastSuccessfulAttemptAt: Column<5, NScheme::NTypeIds::Timestamp> {};
+        struct LastFailedAttemptAt: Column<6, NScheme::NTypeIds::Timestamp> {};
+        struct FailedAttemptCount: Column<7, NScheme::NTypeIds::Uint32> {};
+        struct PasswordHash: Column<8, NScheme::NTypeIds::Utf8> {};
 
         using TKey = TableKey<Sid>;
         using TColumns = TableColumns<
-            Sid
+            Sid,
+            IsEnabled,
+            IsLockedOut,
+            CreatedAt,
+            LastSuccessfulAttemptAt,
+            LastFailedAttemptAt,
+            FailedAttemptCount,
+            PasswordHash
         >;
     };
 

+ 20 - 7
ydb/core/sys_view/ut_kqp.cpp

@@ -2155,12 +2155,12 @@ Y_UNIT_TEST_SUITE(SystemView) {
 
         {
             auto it = client.StreamExecuteScanQuery(R"(
-                SELECT *
+                SELECT Sid, IsEnabled, IsLockedOut, LastSuccessfulAttemptAt, LastFailedAttemptAt, FailedAttemptCount
                 FROM `Root/.sys/auth_users`
             )").GetValueSync();
 
             auto expected = R"([
-                [["user1"]];
+                [["user1"];#;#;#;#;#];
             ])";
 
             NKqp::CompareYson(expected, NKqp::StreamResultToYson(it));
@@ -2168,12 +2168,25 @@ Y_UNIT_TEST_SUITE(SystemView) {
 
         {
             auto it = client.StreamExecuteScanQuery(R"(
-                SELECT *
+                SELECT PasswordHash
+                FROM `Root/.sys/auth_users`
+            )").GetValueSync();
+
+            auto actual = NKqp::StreamResultToYson(it);
+            UNIT_ASSERT_STRING_CONTAINS(actual, "hash");
+            UNIT_ASSERT_STRING_CONTAINS(actual, "salt");
+            UNIT_ASSERT_STRING_CONTAINS(actual, "type");
+            UNIT_ASSERT_STRING_CONTAINS(actual, "argon2id");
+        }
+
+        {
+            auto it = client.StreamExecuteScanQuery(R"(
+                SELECT Sid, IsEnabled, IsLockedOut, LastSuccessfulAttemptAt, LastFailedAttemptAt, FailedAttemptCount
                 FROM `Root/Tenant1/.sys/auth_users`
             )").GetValueSync();
 
             auto expected = R"([
-                [["user2"]]
+                [["user2"];#;#;#;#;#];
             ])";
 
             NKqp::CompareYson(expected, NKqp::StreamResultToYson(it));
@@ -2181,13 +2194,13 @@ Y_UNIT_TEST_SUITE(SystemView) {
 
         {
             auto it = client.StreamExecuteScanQuery(R"(
-                SELECT *
+                SELECT Sid, IsEnabled, IsLockedOut, LastSuccessfulAttemptAt, LastFailedAttemptAt, FailedAttemptCount
                 FROM `Root/Tenant2/.sys/auth_users`
             )").GetValueSync();
 
             auto expected = R"([
-                [["user4"]];
-                [["user3"]];
+                [["user4"];#;#;#;#;#];
+                [["user3"];#;#;#;#;#];
             ])";
 
             NKqp::CompareYson(expected, NKqp::StreamResultToYson(it));

+ 11 - 0
ydb/core/tx/schemeshard/schemeshard.h

@@ -95,6 +95,9 @@ namespace TEvSchemeShard {
 
         EvOwnerActorAck,
 
+        EvListUsers,
+        EvListUsersResult,
+
         EvEnd
     };
 
@@ -661,6 +664,14 @@ namespace TEvSchemeShard {
     struct TEvOwnerActorAck : TEventPB<TEvOwnerActorAck, NKikimrScheme::TEvOwnerActorAck, EvOwnerActorAck> {
         TEvOwnerActorAck() = default;
     };
+
+    struct TEvListUsers : TEventPB<TEvListUsers, NKikimrScheme::TEvListUsers, EvListUsers> {
+        TEvListUsers() = default;
+    };
+
+    struct TEvListUsersResult : TEventPB<TEvListUsersResult, NKikimrScheme::TEvListUsersResult, EvListUsersResult> {
+        TEvListUsersResult() = default;
+    };
 };
 
 }

+ 1 - 1
ydb/core/tx/schemeshard/schemeshard__init_root.cpp

@@ -57,7 +57,7 @@ struct TSchemeShard::TTxInitRoot : public TSchemeShard::TRwTxBase {
                 auto& sid = Self->LoginProvider.Sids[defaultUser.GetName()];
                 db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType,
                                                                    Schema::LoginSids::SidHash,
-                                                                   Schema::LoginSids::CreatedAt>(sid.Type, sid.Hash, ToInstant(sid.CreatedAt).MilliSeconds());
+                                                                   Schema::LoginSids::CreatedAt>(sid.Type, sid.PasswordHash, ToInstant(sid.CreatedAt).MilliSeconds());
                 if (owner.empty()) {
                     owner = defaultUser.GetName();
                 }

+ 54 - 0
ydb/core/tx/schemeshard/schemeshard__list_users.cpp

@@ -0,0 +1,54 @@
+#include <ydb/library/security/util.h>
+#include <ydb/core/protos/auth.pb.h>
+
+#include "schemeshard_impl.h"
+
+namespace NKikimr {
+namespace NSchemeShard {
+
+using namespace NTabletFlatExecutor;
+
+struct TSchemeShard::TTxListUsers : TTransactionBase<TSchemeShard> {
+    TEvSchemeShard::TEvListUsers::TPtr Request;
+    THolder<TEvSchemeShard::TEvListUsersResult> Result = MakeHolder<TEvSchemeShard::TEvListUsersResult>();
+
+    TTxListUsers(TSelf *self, TEvSchemeShard::TEvListUsers::TPtr &ev)
+        : TTransactionBase<TSchemeShard>(self)
+        , Request(std::move(ev))
+    {}
+
+    TTxType GetTxType() const override { return TXTYPE_LIST_USERS; }
+
+    bool Execute(TTransactionContext&, const TActorContext& ctx) override {
+        LOG_DEBUG_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
+                    "TTxListUsers Execute"
+                    << " at schemeshard: " << Self->TabletID());
+
+        for (const auto& [_, sid] : Self->LoginProvider.Sids) {
+            if (sid.Type != NLoginProto::ESidType::USER) {
+                continue;
+            }
+            auto user = Result->Record.AddUsers();
+            user->SetName(sid.Name);
+            user->SetPasswordHash(sid.PasswordHash);
+            // TODO: fill the rest user fields
+        }
+
+        return true;
+    }
+
+    void Complete(const TActorContext &ctx) override {
+        LOG_DEBUG_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
+                    "TTxListUsers Complete"
+                    << ", result: " << Result->Record.ShortDebugString()
+                    << ", at schemeshard: " << Self->TabletID());
+
+        ctx.Send(Request->Sender, std::move(Result), 0, Request->Cookie);
+    }
+};
+
+NTabletFlatExecutor::ITransaction* TSchemeShard::CreateTxListUsers(TEvSchemeShard::TEvListUsers::TPtr &ev) {
+    return new TTxListUsers(this, ev);
+}
+
+}}

+ 2 - 2
ydb/core/tx/schemeshard/schemeshard__operation_alter_login.cpp

@@ -47,7 +47,7 @@ public:
                         db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType,
                                                                            Schema::LoginSids::SidHash,
                                                                            Schema::LoginSids::CreatedAt,
-                                                                           Schema::LoginSids::IsEnabled>(sid.Type, sid.Hash, ToInstant(sid.CreatedAt).MilliSeconds(), sid.IsEnabled);
+                                                                           Schema::LoginSids::IsEnabled>(sid.Type, sid.PasswordHash, ToInstant(sid.CreatedAt).MilliSeconds(), sid.IsEnabled);
 
                         if (securityConfig.HasAllUsersGroup()) {
                             auto response = context.SS->LoginProvider.AddGroupMembership({
@@ -86,7 +86,7 @@ public:
                         auto& sid = context.SS->LoginProvider.Sids[modifyUser.GetUser()];
                         db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType,
                                                                            Schema::LoginSids::SidHash,
-                                                                           Schema::LoginSids::IsEnabled>(sid.Type, sid.Hash, sid.IsEnabled);
+                                                                           Schema::LoginSids::IsEnabled>(sid.Type, sid.PasswordHash, sid.IsEnabled);
                         result->SetStatus(NKikimrScheme::StatusSuccess);
 
                         AddIsUserAdmin(modifyUser.GetUser(), context.SS->LoginProvider, additionalParts);

+ 5 - 0
ydb/core/tx/schemeshard/schemeshard_impl.cpp

@@ -4898,6 +4898,7 @@ void TSchemeShard::StateWork(STFUNC_SIG) {
         HFuncTraced(TEvPrivate::TEvPersistTopicStats, Handle);
 
         HFuncTraced(TEvSchemeShard::TEvLogin, Handle);
+        HFuncTraced(TEvSchemeShard::TEvListUsers, Handle);
 
         HFuncTraced(TEvDataShard::TEvProposeTransactionAttachResult, Handle);
 
@@ -7517,6 +7518,10 @@ void TSchemeShard::Handle(TEvSchemeShard::TEvLogin::TPtr &ev, const TActorContex
     Execute(CreateTxLogin(ev), ctx);
 }
 
+void TSchemeShard::Handle(TEvSchemeShard::TEvListUsers::TPtr &ev, const TActorContext &ctx) {
+    Execute(CreateTxListUsers(ev), ctx);
+}
+
 void TSchemeShard::Handle(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev, const TActorContext&) {
     LOG_DEBUG_S(TlsActivationContext->AsActorContext(), NKikimrServices::STATISTICS,
         "Handle TEvTxProxySchemeCache::TEvNavigateKeySetResult"

Some files were not shown because too many files changed in this diff