Skip to content

Commit

Permalink
Merge 561e2bd into 22e791d
Browse files Browse the repository at this point in the history
  • Loading branch information
molotkov-and authored Dec 28, 2024
2 parents 22e791d + 561e2bd commit 574763c
Show file tree
Hide file tree
Showing 18 changed files with 531 additions and 155 deletions.
4 changes: 4 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard__init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3883,6 +3883,10 @@ struct TSchemeShard::TTxInit : public TTransactionBase<TSchemeShard> {
sid.SetName(rowset.GetValue<Schema::LoginSids::SidName>());
sid.SetType(rowset.GetValue<Schema::LoginSids::SidType>());
sid.SetHash(rowset.GetValue<Schema::LoginSids::SidHash>());
sid.SetCreatedAt(rowset.GetValueOrDefault<Schema::LoginSids::CreatedAt>());
sid.SetCurrentFailedLoginAttemptCount(rowset.GetValueOrDefault<Schema::LoginSids::FailedAttemptCount>());
sid.SetLastFailedLoginAttempt(rowset.GetValueOrDefault<Schema::LoginSids::LastFailedAttempt>());
sid.SetLastSuccessfulLoginAttempt(rowset.GetValueOrDefault<Schema::LoginSids::LastSuccessfulAttempt>());
sidIndex[sid.name()] = securityState.SidsSize() - 1;
if (!rowset.Next()) {
return false;
Expand Down
8 changes: 6 additions & 2 deletions ydb/core/tx/schemeshard/schemeshard__init_root.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <ydb/core/tablet/tablet_exception.h>
#include <ydb/core/tablet_flat/flat_cxx_database.h>
#include <ydb/library/aclib/aclib.h>
#include <ydb/library/security/util.h>

namespace NKikimr {
namespace NSchemeShard {
Expand Down Expand Up @@ -55,7 +56,9 @@ struct TSchemeShard::TTxInitRoot : public TSchemeShard::TRwTxBase {
<< ", error: " << response.Error);
} else {
auto& sid = Self->LoginProvider.Sids[defaultUser.GetName()];
db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType, Schema::LoginSids::SidHash>(sid.Type, sid.Hash);
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());
if (owner.empty()) {
owner = defaultUser.GetName();
}
Expand All @@ -77,7 +80,8 @@ struct TSchemeShard::TTxInitRoot : public TSchemeShard::TRwTxBase {
<< ", error: " << response.Error);
} else {
auto& sid = Self->LoginProvider.Sids[defaultGroup.GetName()];
db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType>(sid.Type);
db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType,
Schema::LoginSids::CreatedAt>(sid.Type, ToInstant(sid.CreatedAt).MilliSeconds());
for (const auto& member : defaultGroup.GetMembers()) {
auto response = Self->LoginProvider.AddGroupMembership({
.Group = defaultGroup.GetName(),
Expand Down
112 changes: 41 additions & 71 deletions ydb/core/tx/schemeshard/schemeshard__login.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ namespace NSchemeShard {

using namespace NTabletFlatExecutor;

struct TSchemeShard::TTxLogin : TTransactionBase<TSchemeShard> {
struct TSchemeShard::TTxLogin : TSchemeShard::TRwTxBase {
TEvSchemeShard::TEvLogin::TPtr Request;
TPathId SubDomainPathId;
bool NeedPublishOnComplete = false;
THolder<TEvSchemeShard::TEvLoginResult> Result = MakeHolder<TEvSchemeShard::TEvLoginResult>();
size_t CurrentFailedAttemptCount = 0;

TTxLogin(TSelf *self, TEvSchemeShard::TEvLogin::TPtr &ev)
: TTransactionBase<TSchemeShard>(self)
: TRwTxBase(self)
, Request(std::move(ev))
{}

Expand All @@ -36,7 +36,7 @@ struct TSchemeShard::TTxLogin : TTransactionBase<TSchemeShard> {
};
}

bool Execute(TTransactionContext& txc, const TActorContext& ctx) override {
void DoExecute(TTransactionContext& txc, const TActorContext& ctx) override {
LOG_DEBUG_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
"TTxLogin Execute"
<< " at schemeshard: " << Self->TabletID());
Expand Down Expand Up @@ -70,10 +70,10 @@ struct TSchemeShard::TTxLogin : TTransactionBase<TSchemeShard> {
NeedPublishOnComplete = true;
}

return LoginAttempt(db, ctx);
LoginAttempt(db, ctx);
}

void Complete(const TActorContext &ctx) override {
void DoComplete(const TActorContext &ctx) override {
if (NeedPublishOnComplete) {
Self->PublishToSchemeBoard(TTxId(), {SubDomainPathId}, ctx);
}
Expand All @@ -87,19 +87,20 @@ struct TSchemeShard::TTxLogin : TTransactionBase<TSchemeShard> {
}

private:
bool LoginAttempt(NIceDb::TNiceDb& db, const TActorContext& ctx) {
void LoginAttempt(NIceDb::TNiceDb& db, const TActorContext& ctx) {
const auto& loginRequest = GetLoginRequest();
if (!loginRequest.ExternalAuth && !AppData(ctx)->AuthConfig.GetEnableLoginAuthentication()) {
Result->Record.SetError("Login authentication is disabled");
return true;
return;
}
if (loginRequest.ExternalAuth) {
return HandleExternalAuth(loginRequest);
HandleExternalAuth(loginRequest);
} else {
HandleLoginAuth(loginRequest, db, ctx);
}
return HandleLoginAuth(loginRequest, db, ctx);
}

bool HandleExternalAuth(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest) {
void HandleExternalAuth(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest) {
const NLogin::TLoginProvider::TLoginUserResponse loginResponse = Self->LoginProvider.LoginUser(loginRequest);
switch (loginResponse.Status) {
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::SUCCESS: {
Expand All @@ -115,83 +116,52 @@ struct TSchemeShard::TTxLogin : TTransactionBase<TSchemeShard> {
break;
}
}
return true;
}

bool HandleLoginAuth(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest, NIceDb::TNiceDb& db, const TActorContext& ctx) {
auto row = db.Table<Schema::LoginSids>().Key(loginRequest.User).Select();
if (!row.IsReady()) {
return false;
}
if (!row.IsValid()) {
Result->Record.SetError(TStringBuilder() << "Cannot find user: " << loginRequest.User);
return true;
}
CurrentFailedAttemptCount = row.GetValueOrDefault<Schema::LoginSids::FailedAttemptCount>();
TInstant lastFailedAttempt = TInstant::FromValue(row.GetValue<Schema::LoginSids::LastFailedAttempt>());
if (CheckAccountLockout()) {
if (ShouldUnlockAccount(lastFailedAttempt)) {
UnlockAccount(loginRequest, db);
} else {
Result->Record.SetError(TStringBuilder() << "User " << loginRequest.User << " is locked out");
return true;
void HandleLoginAuth(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest, NIceDb::TNiceDb& db, const TActorContext& ctx) {
using namespace NLogin;
const TLoginProvider::TCheckLockOutResponse checkLockOutResponse = Self->LoginProvider.CheckLockOutUser({.User = loginRequest.User});
switch (checkLockOutResponse.Status) {
case TLoginProvider::TCheckLockOutResponse::EStatus::SUCCESS:
case TLoginProvider::TCheckLockOutResponse::EStatus::INVALID_USER: {
Result->Record.SetError(checkLockOutResponse.Error);
return;
}
case TLoginProvider::TCheckLockOutResponse::EStatus::RESET: {
const auto& sid = Self->LoginProvider.Sids[loginRequest.User];
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::FailedAttemptCount>(sid.CurrentFailedLoginAttemptCount);
break;
}
case TLoginProvider::TCheckLockOutResponse::EStatus::UNLOCKED:
case TLoginProvider::TCheckLockOutResponse::EStatus::UNSPECIFIED: {
break;
}
} else if (ShouldResetFailedAttemptCount(lastFailedAttempt)) {
ResetFailedAttemptCount(loginRequest, db);
}
const NLogin::TLoginProvider::TLoginUserResponse loginResponse = Self->LoginProvider.LoginUser(loginRequest);

const TLoginProvider::TLoginUserResponse loginResponse = Self->LoginProvider.LoginUser(loginRequest);
switch (loginResponse.Status) {
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::SUCCESS: {
HandleLoginAuthSuccess(loginRequest, loginResponse, db);
case TLoginProvider::TLoginUserResponse::EStatus::SUCCESS: {
const auto& sid = Self->LoginProvider.Sids[loginRequest.User];
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::LastSuccessfulAttempt,
Schema::LoginSids::FailedAttemptCount>(ToInstant(sid.LastSuccessfulLoginAttempt).MilliSeconds(), sid.CurrentFailedLoginAttemptCount);
Result->Record.SetToken(loginResponse.Token);
Result->Record.SetSanitizedToken(loginResponse.SanitizedToken);
break;
}
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::INVALID_PASSWORD: {
HandleLoginAuthInvalidPassword(loginRequest, loginResponse, db);
case TLoginProvider::TLoginUserResponse::EStatus::INVALID_PASSWORD: {
const auto& sid = Self->LoginProvider.Sids[loginRequest.User];
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::LastFailedAttempt,
Schema::LoginSids::FailedAttemptCount>(ToInstant(sid.LastFailedLoginAttempt).MilliSeconds(), sid.CurrentFailedLoginAttemptCount);
Result->Record.SetError(loginResponse.Error);
break;
}
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::INVALID_USER:
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::UNAVAILABLE_KEY:
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::UNSPECIFIED: {
case TLoginProvider::TLoginUserResponse::EStatus::INVALID_USER:
case TLoginProvider::TLoginUserResponse::EStatus::UNAVAILABLE_KEY:
case TLoginProvider::TLoginUserResponse::EStatus::UNSPECIFIED: {
Result->Record.SetError(loginResponse.Error);
break;
}
}
return true;
}

bool CheckAccountLockout() const {
return (Self->AccountLockout.AttemptThreshold != 0 && CurrentFailedAttemptCount >= Self->AccountLockout.AttemptThreshold);
}

bool ShouldResetFailedAttemptCount(const TInstant& lastFailedAttempt) {
if (Self->AccountLockout.AttemptResetDuration == TDuration::Zero()) {
return false;
}
return lastFailedAttempt + Self->AccountLockout.AttemptResetDuration < TAppData::TimeProvider->Now();
}

bool ShouldUnlockAccount(const TInstant& lastFailedAttempt) {
return ShouldResetFailedAttemptCount(lastFailedAttempt);
}

void ResetFailedAttemptCount(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest, NIceDb::TNiceDb& db) {
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::FailedAttemptCount>(Schema::LoginSids::FailedAttemptCount::Default);
CurrentFailedAttemptCount = Schema::LoginSids::FailedAttemptCount::Default;
}

void UnlockAccount(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest, NIceDb::TNiceDb& db) {
ResetFailedAttemptCount(loginRequest, db);
}

void HandleLoginAuthSuccess(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest, const NLogin::TLoginProvider::TLoginUserResponse& loginResponse, NIceDb::TNiceDb& db) {
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::LastSuccessfulAttempt, Schema::LoginSids::FailedAttemptCount>(TAppData::TimeProvider->Now().MicroSeconds(), Schema::LoginSids::FailedAttemptCount::Default);
}

void HandleLoginAuthInvalidPassword(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest, const NLogin::TLoginProvider::TLoginUserResponse& loginResponse, NIceDb::TNiceDb& db) {
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::LastFailedAttempt, Schema::LoginSids::FailedAttemptCount>(TAppData::TimeProvider->Now().MicroSeconds(), CurrentFailedAttemptCount + 1);
}
};

Expand Down
12 changes: 8 additions & 4 deletions ydb/core/tx/schemeshard/schemeshard__operation_alter_login.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "schemeshard__operation_part.h"
#include "schemeshard__operation_common.h"
#include "schemeshard_impl.h"
#include <ydb/library/security/util.h>
#include <ydb/core/protos/auth.pb.h>

namespace {
Expand Down Expand Up @@ -32,7 +33,9 @@ class TAlterLogin: public TSubOperationBase {
result->SetStatus(NKikimrScheme::StatusPreconditionFailed, response.Error);
} else {
auto& sid = context.SS->LoginProvider.Sids[createUser.GetUser()];
db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType, Schema::LoginSids::SidHash>(sid.Type, sid.Hash);
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());
if (securityConfig.HasAllUsersGroup()) {
auto response = context.SS->LoginProvider.AddGroupMembership({
.Group = securityConfig.GetAllUsersGroup(),
Expand Down Expand Up @@ -76,7 +79,8 @@ class TAlterLogin: public TSubOperationBase {
result->SetStatus(NKikimrScheme::StatusPreconditionFailed, response.Error);
} else {
auto& sid = context.SS->LoginProvider.Sids[group];
db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType>(sid.Type);
db.Table<Schema::LoginSids>().Key(sid.Name).Update<Schema::LoginSids::SidType,
Schema::LoginSids::CreatedAt>(sid.Type, ToInstant(sid.CreatedAt).MilliSeconds());
result->SetStatus(NKikimrScheme::StatusSuccess);
}
break;
Expand Down Expand Up @@ -200,7 +204,7 @@ class TAlterLogin: public TSubOperationBase {
TPathElement::TPtr path = context.SS->PathsById.at(pathId);
if (path->Owner == user) {
auto pathStr = TPath::Init(pathId, context.SS).PathString();
return {.Error = TStringBuilder() <<
return {.Error = TStringBuilder() <<
"User " << user << " owns " << pathStr << " and can't be removed"};
}
}
Expand Down Expand Up @@ -239,7 +243,7 @@ class TAlterLogin: public TSubOperationBase {
for (const TString& group : removeUserResponse.TouchedGroups) {
db.Table<Schema::LoginSidMembers>().Key(group, user).Delete();
}

return {}; // success
}
};
Expand Down
31 changes: 12 additions & 19 deletions ydb/core/tx/schemeshard/schemeshard_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <ydb/core/tx/columnshard/bg_tasks/events/events.h>
#include <ydb/core/tx/scheme_board/events_schemeshard.h>
#include <ydb/library/login/password_checker/password_checker.h>
#include <ydb/library/login/account_lockout/account_lockout.h>
#include <yql/essentials/minikql/mkql_type_ops.h>
#include <yql/essentials/providers/common/proto/gateways_config.pb.h>
#include <util/random/random.h>
Expand Down Expand Up @@ -4438,20 +4439,6 @@ TActorId TSchemeShard::TPipeClientFactory::CreateClient(const TActorContext& ctx
return clientId;
}

TSchemeShard::TAccountLockout::TAccountLockout(const ::NKikimrProto::TAccountLockout& accountLockout)
: AttemptThreshold(accountLockout.GetAttemptThreshold())
{
AttemptResetDuration = TDuration::Zero();
if (accountLockout.GetAttemptResetDuration().empty()) {
return;
}
if (TDuration::TryParse(accountLockout.GetAttemptResetDuration(), AttemptResetDuration)) {
if (AttemptResetDuration.Seconds() == 0) {
AttemptResetDuration = TDuration::Zero();
}
}
}

TSchemeShard::TSchemeShard(const TActorId &tablet, TTabletStorageInfo *info)
: TActor(&TThis::StateInit)
, TTabletExecutedFlat(info, tablet, new NMiniKQL::TMiniKQLFactory)
Expand Down Expand Up @@ -4489,8 +4476,9 @@ TSchemeShard::TSchemeShard(const TActorId &tablet, TTabletStorageInfo *info)
.MinSpecialCharsCount = AppData()->AuthConfig.GetPasswordComplexity().GetMinSpecialCharsCount(),
.SpecialChars = AppData()->AuthConfig.GetPasswordComplexity().GetSpecialChars(),
.CanContainUsername = AppData()->AuthConfig.GetPasswordComplexity().GetCanContainUsername()
}))
, AccountLockout(AppData()->AuthConfig.GetAccountLockout())
}), {.AttemptThreshold = AppData()->AuthConfig.GetAccountLockout().GetAttemptThreshold(),
.AttemptResetDuration = AppData()->AuthConfig.GetAccountLockout().GetAttemptResetDuration()
})
{
TabletCountersPtr.Reset(new TProtobufTabletCounters<
ESimpleCounters_descriptor,
Expand Down Expand Up @@ -7403,11 +7391,16 @@ void TSchemeShard::ConfigureAccountLockout(
const ::NKikimrProto::TAuthConfig& config,
const TActorContext &ctx)
{
AccountLockout = TAccountLockout(config.GetAccountLockout());
NLogin::TAccountLockout::TInitializer accountLockoutInitializer {
.AttemptThreshold = config.GetAccountLockout().GetAttemptThreshold(),
.AttemptResetDuration = config.GetAccountLockout().GetAttemptResetDuration()
};

LoginProvider.UpdateAccountLockout(accountLockoutInitializer);

LOG_NOTICE_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
"AccountLockout configured: AttemptThreshold# " << AccountLockout.AttemptThreshold
<< ", AttemptResetDuration# " << AccountLockout.AttemptResetDuration.ToString());
"AccountLockout configured: AttemptThreshold# " << accountLockoutInitializer.AttemptThreshold
<< ", AttemptResetDuration# " << accountLockoutInitializer.AttemptResetDuration);
}

void TSchemeShard::StartStopCompactionQueues() {
Expand Down
9 changes: 0 additions & 9 deletions ydb/core/tx/schemeshard/schemeshard_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1468,15 +1468,6 @@ class TSchemeShard

NLogin::TLoginProvider LoginProvider;

struct TAccountLockout {
size_t AttemptThreshold = 4;
TDuration AttemptResetDuration = TDuration::Hours(1);

TAccountLockout(const ::NKikimrProto::TAccountLockout& accountLockout);
};

TAccountLockout AccountLockout;

private:
void OnDetach(const TActorContext &ctx) override;
void OnTabletDead(TEvTablet::TEvTabletDead::TPtr &ev, const TActorContext &ctx) override;
Expand Down
4 changes: 3 additions & 1 deletion ydb/core/tx/schemeshard/schemeshard_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,7 @@ struct Schema : NIceDb::Schema {
struct LastSuccessfulAttempt : Column<4, NScheme::NTypeIds::Timestamp> {};
struct LastFailedAttempt : Column<5, NScheme::NTypeIds::Timestamp> {};
struct FailedAttemptCount : Column<6, NScheme::NTypeIds::Uint32> {using Type = ui32; static constexpr Type Default = 0;};
struct CreatedAt : Column<7, NScheme::NTypeIds::Timestamp> {};

using TKey = TableKey<SidName>;
using TColumns = TableColumns<
Expand All @@ -1642,7 +1643,8 @@ struct Schema : NIceDb::Schema {
SidHash,
LastSuccessfulAttempt,
LastFailedAttempt,
FailedAttemptCount
FailedAttemptCount,
CreatedAt
>;
};

Expand Down
Loading

0 comments on commit 574763c

Please sign in to comment.