Skip to content

Commit 0e5eec0

Browse files
authored
Merge 76b5e60 into 6bddb61
2 parents 6bddb61 + 76b5e60 commit 0e5eec0

14 files changed

+431
-103
lines changed

ydb/core/protos/counters_schemeshard.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,4 +645,6 @@ enum ETxTypes {
645645

646646
TXTYPE_ADD_SHARDS_DATA_ERASURE = 98 [(TxTypeOpts) = {Name: "TxAddShardsDataErasure"}];
647647
TXTYPE_CANCEL_SHARDS_DATA_ERASURE = 99 [(TxTypeOpts) = {Name: "TxCancelShardsDataErasure"}];
648+
649+
TXTYPE_LOGIN_FINALIZE = 100 [(TxTypeOpts) = {Name: "TxLoginFinalize"}];
648650
}

ydb/core/tx/schemeshard/schemeshard__login.cpp

Lines changed: 72 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1+
#include "schemeshard_impl.h"
2+
#include <ydb/library/login/login.h>
13
#include <ydb/library/security/util.h>
24
#include <ydb/core/protos/auth.pb.h>
35
#include <ydb/core/base/auth.h>
46
#include <ydb/core/base/local_user_token.h>
57

6-
#include "schemeshard_impl.h"
7-
88
namespace NKikimr {
99
namespace NSchemeShard {
1010

11-
using namespace NTabletFlatExecutor;
12-
1311
struct TSchemeShard::TTxLogin : TSchemeShard::TRwTxBase {
1412
TEvSchemeShard::TEvLogin::TPtr Request;
1513
TPathId SubDomainPathId;
1614
bool NeedPublishOnComplete = false;
17-
THolder<TEvSchemeShard::TEvLoginResult> Result = MakeHolder<TEvSchemeShard::TEvLoginResult>();
15+
TString ErrMessage;
1816

1917
TTxLogin(TSelf *self, TEvSchemeShard::TEvLogin::TPtr &ev)
2018
: TRwTxBase(self)
@@ -43,35 +41,41 @@ struct TSchemeShard::TTxLogin : TSchemeShard::TRwTxBase {
4341
<< " at schemeshard: " << Self->TabletID());
4442
NIceDb::TNiceDb db(txc.DB);
4543
if (Self->LoginProvider.IsItTimeToRotateKeys()) {
46-
LOG_DEBUG_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, "TTxLogin RotateKeys at schemeshard: " << Self->TabletID());
47-
std::vector<ui64> keysExpired;
48-
std::vector<ui64> keysAdded;
49-
Self->LoginProvider.RotateKeys(keysExpired, keysAdded);
50-
SubDomainPathId = Self->GetCurrentSubDomainPathId();
51-
TSubDomainInfo::TPtr domainPtr = Self->ResolveDomainInfo(SubDomainPathId);
52-
53-
// TODO(xenoxeno): optimize security state changes
54-
domainPtr->UpdateSecurityState(Self->LoginProvider.GetSecurityState());
55-
domainPtr->IncSecurityStateVersion();
56-
57-
58-
Self->PersistSubDomainSecurityStateVersion(db, SubDomainPathId, *domainPtr);
44+
RotateKeys(ctx, db);
45+
NeedPublishOnComplete = true;
46+
}
5947

60-
for (ui64 keyId : keysExpired) {
61-
db.Table<Schema::LoginKeys>().Key(keyId).Delete();
62-
}
63-
for (ui64 keyId : keysAdded) {
64-
const auto* key = Self->LoginProvider.FindKey(keyId);
65-
if (key) {
66-
db.Table<Schema::LoginKeys>().Key(keyId).Update<Schema::LoginKeys::KeyDataPEM, Schema::LoginKeys::ExpiresAt>(
67-
key->PublicKey, ToInstant(key->ExpiresAt).MilliSeconds());
68-
}
48+
const auto& loginRequest = GetLoginRequest();
49+
if (!loginRequest.ExternalAuth) {
50+
if (!AppData(ctx)->AuthConfig.GetEnableLoginAuthentication()) {
51+
ErrMessage = "Login authentication is disabled";
52+
} else {
53+
CheckLockOutUserAndSetErrorIfAny(loginRequest.User, db);
6954
}
55+
}
7056

71-
NeedPublishOnComplete = true;
57+
if (ErrMessage) {
58+
SendError();
59+
return;
7260
}
7361

74-
LoginAttempt(db, ctx);
62+
NLogin::TLoginProvider::TLoginUserResponse Response;
63+
TString passwordHash;
64+
if (Self->LoginProvider.NeedVerifyHash(loginRequest, &Response, &passwordHash)) {
65+
ctx.Send(
66+
Self->LoginHelper,
67+
MakeHolder<TEvPrivate::TEvVerifyPassword>(loginRequest, Response, Request->Sender, passwordHash),
68+
0,
69+
Request->Cookie
70+
);
71+
} else {
72+
ctx.Send(
73+
Self->SelfId(),
74+
MakeHolder<TEvPrivate::TEvLoginFinalize>(loginRequest, Response, Request->Sender, "", /*needUpdateCache*/ false),
75+
0,
76+
Request->Cookie
77+
);
78+
}
7579
}
7680

7781
void DoComplete(const TActorContext &ctx) override {
@@ -81,96 +85,67 @@ struct TSchemeShard::TTxLogin : TSchemeShard::TRwTxBase {
8185

8286
LOG_DEBUG_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
8387
"TTxLogin Complete"
84-
<< ", result: " << Result->Record.ShortDebugString()
88+
<< ", with " << (ErrMessage ? "error: " + ErrMessage : "no errors")
8589
<< ", at schemeshard: " << Self->TabletID());
86-
87-
ctx.Send(Request->Sender, std::move(Result), 0, Request->Cookie);
88-
}
90+
}
8991

9092
private:
91-
bool IsAdmin() const {
92-
const auto& user = Request->Get()->Record.GetUser();
93-
const auto userToken = NKikimr::BuildLocalUserToken(Self->LoginProvider, user);
94-
return IsAdministrator(AppData(), &userToken);
95-
}
96-
97-
void LoginAttempt(NIceDb::TNiceDb& db, const TActorContext& ctx) {
98-
const auto& loginRequest = GetLoginRequest();
99-
if (!loginRequest.ExternalAuth && !AppData(ctx)->AuthConfig.GetEnableLoginAuthentication()) {
100-
Result->Record.SetError("Login authentication is disabled");
101-
return;
102-
}
103-
if (loginRequest.ExternalAuth) {
104-
HandleExternalAuth(loginRequest);
105-
} else {
106-
HandleLoginAuth(loginRequest, db);
107-
}
108-
}
109-
110-
void HandleExternalAuth(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest) {
111-
const NLogin::TLoginProvider::TLoginUserResponse loginResponse = Self->LoginProvider.LoginUser(loginRequest);
112-
switch (loginResponse.Status) {
113-
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::SUCCESS: {
114-
Result->Record.SetToken(loginResponse.Token);
115-
Result->Record.SetSanitizedToken(loginResponse.SanitizedToken);
116-
Result->Record.SetIsAdmin(IsAdmin());
117-
break;
118-
}
119-
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::INVALID_PASSWORD:
120-
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::INVALID_USER:
121-
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::UNAVAILABLE_KEY:
122-
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::UNSPECIFIED: {
123-
Result->Record.SetError(loginResponse.Error);
124-
break;
93+
void RotateKeys(const TActorContext& ctx, NIceDb::TNiceDb& db) {
94+
LOG_DEBUG_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, "TTxLogin RotateKeys at schemeshard: " << Self->TabletID());
95+
std::vector<ui64> keysExpired;
96+
std::vector<ui64> keysAdded;
97+
Self->LoginProvider.RotateKeys(keysExpired, keysAdded);
98+
SubDomainPathId = Self->GetCurrentSubDomainPathId();
99+
TSubDomainInfo::TPtr domainPtr = Self->ResolveDomainInfo(SubDomainPathId);
100+
101+
// TODO(xenoxeno): optimize security state changes
102+
domainPtr->UpdateSecurityState(Self->LoginProvider.GetSecurityState());
103+
domainPtr->IncSecurityStateVersion();
104+
105+
Self->PersistSubDomainSecurityStateVersion(db, SubDomainPathId, *domainPtr);
106+
107+
for (ui64 keyId : keysExpired) {
108+
db.Table<Schema::LoginKeys>().Key(keyId).Delete();
125109
}
110+
for (ui64 keyId : keysAdded) {
111+
const auto* key = Self->LoginProvider.FindKey(keyId);
112+
if (key) {
113+
db.Table<Schema::LoginKeys>().Key(keyId).Update<Schema::LoginKeys::KeyDataPEM, Schema::LoginKeys::ExpiresAt>(
114+
key->PublicKey, ToInstant(key->ExpiresAt).MilliSeconds());
115+
}
126116
}
127117
}
128118

129-
void HandleLoginAuth(const NLogin::TLoginProvider::TLoginUserRequest& loginRequest, NIceDb::TNiceDb& db) {
119+
void CheckLockOutUserAndSetErrorIfAny(const TString& user, NIceDb::TNiceDb& db) {
130120
using namespace NLogin;
131-
const TLoginProvider::TCheckLockOutResponse checkLockOutResponse = Self->LoginProvider.CheckLockOutUser({.User = loginRequest.User});
121+
const TLoginProvider::TCheckLockOutResponse checkLockOutResponse = Self->LoginProvider.CheckLockOutUser({.User = user});
132122
switch (checkLockOutResponse.Status) {
133123
case TLoginProvider::TCheckLockOutResponse::EStatus::SUCCESS:
134124
case TLoginProvider::TCheckLockOutResponse::EStatus::INVALID_USER: {
135-
Result->Record.SetError(checkLockOutResponse.Error);
125+
ErrMessage = checkLockOutResponse.Error;
136126
return;
137127
}
138128
case TLoginProvider::TCheckLockOutResponse::EStatus::RESET: {
139-
const auto& sid = Self->LoginProvider.Sids[loginRequest.User];
140-
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::FailedAttemptCount>(sid.FailedLoginAttemptCount);
129+
const auto& sid = Self->LoginProvider.Sids[user];
130+
db.Table<Schema::LoginSids>().Key(user).Update<Schema::LoginSids::FailedAttemptCount>(sid.FailedLoginAttemptCount);
141131
break;
142132
}
143133
case TLoginProvider::TCheckLockOutResponse::EStatus::UNLOCKED:
144134
case TLoginProvider::TCheckLockOutResponse::EStatus::UNSPECIFIED: {
145135
break;
146136
}
147137
}
138+
}
148139

149-
const TLoginProvider::TLoginUserResponse loginResponse = Self->LoginProvider.LoginUser(loginRequest);
150-
switch (loginResponse.Status) {
151-
case TLoginProvider::TLoginUserResponse::EStatus::SUCCESS: {
152-
const auto& sid = Self->LoginProvider.Sids[loginRequest.User];
153-
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::LastSuccessfulAttempt,
154-
Schema::LoginSids::FailedAttemptCount>(ToMicroSeconds(sid.LastSuccessfulLogin), sid.FailedLoginAttemptCount);
155-
Result->Record.SetToken(loginResponse.Token);
156-
Result->Record.SetSanitizedToken(loginResponse.SanitizedToken);
157-
Result->Record.SetIsAdmin(IsAdmin());
158-
break;
159-
}
160-
case TLoginProvider::TLoginUserResponse::EStatus::INVALID_PASSWORD: {
161-
const auto& sid = Self->LoginProvider.Sids[loginRequest.User];
162-
db.Table<Schema::LoginSids>().Key(loginRequest.User).Update<Schema::LoginSids::LastFailedAttempt,
163-
Schema::LoginSids::FailedAttemptCount>(ToMicroSeconds(sid.LastFailedLogin), sid.FailedLoginAttemptCount);
164-
Result->Record.SetError(loginResponse.Error);
165-
break;
166-
}
167-
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::INVALID_USER:
168-
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::UNAVAILABLE_KEY:
169-
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::UNSPECIFIED: {
170-
Result->Record.SetError(loginResponse.Error);
171-
break;
172-
}
173-
}
140+
void SendError() {
141+
THolder<TEvSchemeShard::TEvLoginResult> result = MakeHolder<TEvSchemeShard::TEvLoginResult>();
142+
result->Record.SetError(ErrMessage);
143+
Self->Send(
144+
Request->Sender,
145+
std::move(result),
146+
0,
147+
Request->Cookie
148+
);
174149
}
175150
};
176151

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#include "schemeshard_impl.h"
2+
#include <ydb/library/security/util.h>
3+
#include <ydb/core/protos/auth.pb.h>
4+
#include <ydb/core/base/auth.h>
5+
#include <ydb/core/base/local_user_token.h>
6+
7+
namespace NKikimr {
8+
namespace NSchemeShard {
9+
10+
struct TSchemeShard::TTxLoginFinalize : TSchemeShard::TRwTxBase {
11+
private:
12+
TEvPrivate::TEvLoginFinalize::TPtr LoginFinalizeEventPtr;
13+
TString ErrMessage;
14+
15+
public:
16+
TTxLoginFinalize(TSelf *self, TEvPrivate::TEvLoginFinalize::TPtr &ev)
17+
: TRwTxBase(self)
18+
, LoginFinalizeEventPtr(std::move(ev))
19+
{}
20+
21+
TTxType GetTxType() const override {
22+
return TXTYPE_LOGIN_FINALIZE;
23+
}
24+
25+
void DoExecute(TTransactionContext& txc, const TActorContext& ctx) override {
26+
LOG_DEBUG_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
27+
"TTxLoginFinalize Execute"
28+
<< " at schemeshard: " << Self->TabletID());
29+
30+
const auto& event = *LoginFinalizeEventPtr->Get();
31+
if (event.NeedUpdateCache) {
32+
const auto isSuccessVerifying =
33+
event.CheckResult.Status == NLogin::TLoginProvider::TLoginUserResponse::EStatus::SUCCESS;
34+
Self->LoginProvider.UpdateCache(
35+
event.Request,
36+
event.PasswordHash,
37+
isSuccessVerifying
38+
);
39+
}
40+
const auto response = Self->LoginProvider.LoginUser(event.Request, event.CheckResult);
41+
42+
if (!LoginFinalizeEventPtr->Get()->Request.ExternalAuth) {
43+
UpdateLoginSidsStats(response, txc);
44+
}
45+
if (!response.Error.empty()) {
46+
ErrMessage = response.Error;
47+
SendError(response.Error);
48+
return;
49+
}
50+
FillResult(response);
51+
}
52+
53+
void DoComplete(const TActorContext &ctx) override {
54+
LOG_DEBUG_S(ctx, NKikimrServices::FLAT_TX_SCHEMESHARD,
55+
"TTxLoginFinalize Completed"
56+
<< ", with " << (ErrMessage ? "error: " + ErrMessage : "no errors")
57+
<< " at schemeshard: " << Self->TabletID());
58+
}
59+
60+
private:
61+
bool IsAdmin(const TString& user) const {
62+
const auto userToken = NKikimr::BuildLocalUserToken(Self->LoginProvider, user);
63+
return IsAdministrator(AppData(), &userToken);
64+
}
65+
66+
void FillResult(const NLogin::TLoginProvider::TLoginUserResponse& response) {
67+
THolder<TEvSchemeShard::TEvLoginResult> result = MakeHolder<TEvSchemeShard::TEvLoginResult>();
68+
switch (response.Status) {
69+
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::SUCCESS: {
70+
result->Record.SetToken(response.Token);
71+
result->Record.SetSanitizedToken(response.SanitizedToken);
72+
result->Record.SetIsAdmin(IsAdmin(LoginFinalizeEventPtr->Get()->Request.User));
73+
break;
74+
}
75+
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::INVALID_PASSWORD:
76+
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::INVALID_USER:
77+
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::UNAVAILABLE_KEY:
78+
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::UNSPECIFIED: {
79+
result->Record.SetError(response.Error);
80+
break;
81+
}
82+
}
83+
Self->Send(
84+
LoginFinalizeEventPtr->Get()->Source,
85+
std::move(result),
86+
0,
87+
LoginFinalizeEventPtr->Cookie
88+
);
89+
}
90+
91+
void SendError(const TString& error) {
92+
auto result = MakeHolder<TEvSchemeShard::TEvLoginResult>();
93+
result->Record.SetError(error);
94+
Self->Send(
95+
LoginFinalizeEventPtr->Get()->Source,
96+
std::move(result),
97+
0,
98+
LoginFinalizeEventPtr->Cookie
99+
);
100+
}
101+
102+
void UpdateLoginSidsStats(const NLogin::TLoginProvider::TLoginUserResponse& response, TTransactionContext& txc) {
103+
NIceDb::TNiceDb db(txc.DB);
104+
switch (response.Status) {
105+
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::SUCCESS: {
106+
const auto& sid = Self->LoginProvider.Sids[LoginFinalizeEventPtr->Get()->Request.User];
107+
db.Table<Schema::LoginSids>()
108+
.Key(LoginFinalizeEventPtr->Get()->Request.User)
109+
.Update<Schema::LoginSids::LastSuccessfulAttempt, Schema::LoginSids::FailedAttemptCount>(
110+
ToMicroSeconds(sid.LastSuccessfulLogin), sid.FailedLoginAttemptCount
111+
);
112+
break;
113+
}
114+
case NLogin::TLoginProvider::TLoginUserResponse::EStatus::INVALID_PASSWORD: {
115+
const auto& sid = Self->LoginProvider.Sids[LoginFinalizeEventPtr->Get()->Request.User];
116+
db.Table<Schema::LoginSids>()
117+
.Key(LoginFinalizeEventPtr->Get()->Request.User)
118+
.Update<Schema::LoginSids::LastFailedAttempt, Schema::LoginSids::FailedAttemptCount>(
119+
ToMicroSeconds(sid.LastFailedLogin), sid.FailedLoginAttemptCount
120+
);
121+
}
122+
default:
123+
break;
124+
}
125+
}
126+
};
127+
128+
NTabletFlatExecutor::ITransaction* TSchemeShard::CreateTxLoginFinalize(TEvPrivate::TEvLoginFinalize::TPtr &ev) {
129+
return new TTxLoginFinalize(this, ev);
130+
}
131+
132+
}}

0 commit comments

Comments
 (0)