Skip to content

Scheme Shard List users request #13344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ydb/core/protos/counters_schemeshard.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 19 additions & 0 deletions ydb/core/protos/flat_tx_scheme.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
118 changes: 108 additions & 10 deletions ydb/core/sys_view/auth/users.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
16 changes: 15 additions & 1 deletion ydb/core/sys_view/common/schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
>;
};

Expand Down
27 changes: 20 additions & 7 deletions ydb/core/sys_view/ut_kqp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2155,39 +2155,52 @@ 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));
}

{
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));
}

{
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));
Expand Down
11 changes: 11 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ namespace TEvSchemeShard {

EvOwnerActorAck,

EvListUsers,
EvListUsersResult,

EvEnd
};

Expand Down Expand Up @@ -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;
};
};

}
Expand Down
2 changes: 1 addition & 1 deletion ydb/core/tx/schemeshard/schemeshard__init_root.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
54 changes: 54 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard__list_users.cpp
Original file line number Diff line number Diff line change
@@ -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);
}

}}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class TAlterLogin: public TSubOperationBase {
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({
Expand Down Expand Up @@ -86,7 +86,7 @@ class TAlterLogin: public TSubOperationBase {
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);
Expand Down
5 changes: 5 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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"
Expand Down
Loading
Loading