Skip to content

Commit e6c9d81

Browse files
committed
Extented permissions in whoami
1 parent b086d72 commit e6c9d81

File tree

7 files changed

+323
-0
lines changed

7 files changed

+323
-0
lines changed

ydb/core/grpc_services/rpc_whoami.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <ydb/core/grpc_services/base/base.h>
44
#include <ydb/library/actors/core/actor_bootstrapped.h>
55
#include <ydb/core/security/ticket_parser.h>
6+
#include <ydb/core/base/appdata.h>
7+
#include <ydb/core/base/auth.h>
68
#include <ydb/public/api/protos/ydb_discovery.pb.h>
79

810
namespace NKikimr {
@@ -39,6 +41,18 @@ class TWhoAmIRPC : public TActorBootstrapped<TWhoAmIRPC> {
3941
for (const auto& group : userToken.GetGroupSIDs()) {
4042
response->add_groups(group);
4143
}
44+
45+
// Add permission information
46+
const auto* appData = AppData();
47+
response->set_is_token_required(appData->EnforceUserTokenRequirement);
48+
bool isAdministrationAllowed = IsTokenAllowed(&userToken, appData->DomainsConfig.GetSecurityConfig().GetAdministrationAllowedSIDs());
49+
bool isMonitoringAllowed = isAdministrationAllowed || IsTokenAllowed(&userToken, appData->DomainsConfig.GetSecurityConfig().GetMonitoringAllowedSIDs());
50+
bool isViewerAllowed = isMonitoringAllowed || IsTokenAllowed(&userToken, appData->DomainsConfig.GetSecurityConfig().GetViewerAllowedSIDs());
51+
bool isDatabaseAllowed = isViewerAllowed || IsTokenAllowed(&userToken, appData->DomainsConfig.GetSecurityConfig().GetDatabaseAllowedSIDs());
52+
response->set_is_administration_allowed(isAdministrationAllowed);
53+
response->set_is_monitoring_allowed(isMonitoringAllowed);
54+
response->set_is_viewer_allowed(isViewerAllowed);
55+
response->set_is_database_allowed(isDatabaseAllowed);
4256
}
4357

4458
Request->SendResult(*response, Ydb::StatusIds::SUCCESS);

ydb/public/api/protos/ydb_discovery.proto

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ message WhoAmIResult {
6363
string user = 1;
6464
// List of group SIDs (Security IDs) for the user
6565
repeated string groups = 2;
66+
// Whether user token is required for authentication
67+
bool is_token_required = 3;
68+
// Whether user is allowed to perform administration operations
69+
bool is_administration_allowed = 4;
70+
// Whether user is allowed to perform monitoring operations
71+
bool is_monitoring_allowed = 5;
72+
// Whether user is allowed to view data
73+
bool is_viewer_allowed = 6;
74+
// Whether user is allowed to access database
75+
bool is_database_allowed = 7;
6676
}
6777

6878
message WhoAmIResponse {

ydb/public/lib/ydb_cli/commands/ydb_service_discovery.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ void TCommandWhoAmI::PrintResponse(NDiscovery::TWhoAmIResult& result) {
100100
} else {
101101
Cout << Endl << "User has no groups" << Endl;
102102
}
103+
104+
Cout << Endl << "Effective permissions:" << Endl;
105+
Cout << " Token required: " << (result.IsTokenRequired() ? "true" : "false") << Endl;
106+
Cout << " Database access allowed: " << (result.IsDatabaseAllowed() ? "true" : "false") << Endl;
107+
Cout << " Viewer access allowed: " << (result.IsViewerAllowed() ? "true" : "false") << Endl;
108+
Cout << " Monitoring access allowed: " << (result.IsMonitoringAllowed() ? "true" : "false") << Endl;
109+
Cout << " Administration access allowed: " << (result.IsAdministrationAllowed() ? "true" : "false") << Endl;
103110
}
104111
}
105112
}

ydb/public/sdk/cpp/include/ydb-cpp-sdk/client/discovery/discovery.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,19 @@ class TWhoAmIResult : public TStatus {
9090
TWhoAmIResult(TStatus&& status, const Ydb::Discovery::WhoAmIResult& proto);
9191
const std::string& GetUserName() const;
9292
const std::vector<std::string>& GetGroups() const;
93+
bool IsTokenRequired() const;
94+
bool IsAdministrationAllowed() const;
95+
bool IsMonitoringAllowed() const;
96+
bool IsViewerAllowed() const;
97+
bool IsDatabaseAllowed() const;
9398
private:
9499
std::string UserName_;
95100
std::vector<std::string> Groups_;
101+
bool IsTokenRequired_ = false;
102+
bool IsAdministrationAllowed_ = false;
103+
bool IsMonitoringAllowed_ = false;
104+
bool IsViewerAllowed_ = false;
105+
bool IsDatabaseAllowed_ = false;
96106
};
97107

98108
using TAsyncWhoAmIResult = NThreading::TFuture<TWhoAmIResult>;

ydb/public/sdk/cpp/src/client/discovery/discovery.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ TWhoAmIResult::TWhoAmIResult(TStatus&& status, const Ydb::Discovery::WhoAmIResul
6060
for (const auto& group : groups) {
6161
Groups_.emplace_back(group);
6262
}
63+
IsTokenRequired_ = proto.is_token_required();
64+
IsAdministrationAllowed_ = proto.is_administration_allowed();
65+
IsMonitoringAllowed_ = proto.is_monitoring_allowed();
66+
IsViewerAllowed_ = proto.is_viewer_allowed();
67+
IsDatabaseAllowed_ = proto.is_database_allowed();
6368
}
6469

6570
const std::string& TWhoAmIResult::GetUserName() const {
@@ -70,6 +75,26 @@ const std::vector<std::string>& TWhoAmIResult::GetGroups() const {
7075
return Groups_;
7176
}
7277

78+
bool TWhoAmIResult::IsTokenRequired() const {
79+
return IsTokenRequired_;
80+
}
81+
82+
bool TWhoAmIResult::IsAdministrationAllowed() const {
83+
return IsAdministrationAllowed_;
84+
}
85+
86+
bool TWhoAmIResult::IsMonitoringAllowed() const {
87+
return IsMonitoringAllowed_;
88+
}
89+
90+
bool TWhoAmIResult::IsViewerAllowed() const {
91+
return IsViewerAllowed_;
92+
}
93+
94+
bool TWhoAmIResult::IsDatabaseAllowed() const {
95+
return IsDatabaseAllowed_;
96+
}
97+
7398
TNodeLocation::TNodeLocation(const Ydb::Discovery::NodeLocation& location)
7499
: DataCenterNum(location.has_data_center_num() ? std::make_optional(location.data_center_num()) : std::nullopt)
75100
, RoomNum(location.has_room_num() ? std::make_optional(location.room_num()) : std::nullopt)

ydb/services/ydb/ut/ya.make

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ SRCS(
2929
ydb_ldap_login_ut.cpp
3030
ydb_login_ut.cpp
3131
ydb_object_storage_ut.cpp
32+
ydb_whoami_ut.cpp
3233
)
3334

3435
PEERDIR(
@@ -51,6 +52,7 @@ PEERDIR(
5152
ydb/public/lib/yson_value
5253
ydb/public/lib/ut_helpers
5354
ydb/public/lib/ydb_cli/commands
55+
ydb/public/sdk/cpp/src/client/discovery
5456
ydb/public/sdk/cpp/src/client/draft
5557
ydb/public/sdk/cpp/src/client/coordination
5658
ydb/public/sdk/cpp/src/client/export

ydb/services/ydb/ydb_whoami_ut.cpp

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
#include <library/cpp/testing/unittest/tests_data.h>
2+
#include <library/cpp/testing/unittest/registar.h>
3+
4+
#include <ydb/core/base/storage_pools.h>
5+
#include <ydb/core/testlib/test_client.h>
6+
7+
#include <ydb/public/sdk/cpp/include/ydb-cpp-sdk/client/discovery/discovery.h>
8+
9+
#include "ydb_common_ut.h"
10+
11+
namespace NKikimr {
12+
13+
using namespace Tests;
14+
using namespace NYdb;
15+
16+
namespace {
17+
18+
struct TServerInitialization {
19+
bool EnforceUserToken = true;
20+
std::vector<TString> AdministrationAllowedSids = {};
21+
std::vector<TString> MonitoringAllowedSids = {};
22+
std::vector<TString> ViewerAllowedSids = {};
23+
std::vector<TString> DatabaseAllowedSids = {};
24+
};
25+
26+
NKikimrConfig::TAppConfig GetWhoAmIAppConfig(const TServerInitialization& serverInitialization) {
27+
auto config = NKikimrConfig::TAppConfig();
28+
29+
auto& securityConfig = *config.MutableDomainsConfig()->MutableSecurityConfig();
30+
securityConfig.SetEnforceUserTokenRequirement(serverInitialization.EnforceUserToken);
31+
32+
for (const auto& sid : serverInitialization.AdministrationAllowedSids) {
33+
securityConfig.AddAdministrationAllowedSIDs(sid);
34+
}
35+
for (const auto& sid : serverInitialization.MonitoringAllowedSids) {
36+
securityConfig.AddMonitoringAllowedSIDs(sid);
37+
}
38+
for (const auto& sid : serverInitialization.ViewerAllowedSids) {
39+
securityConfig.AddViewerAllowedSIDs(sid);
40+
}
41+
for (const auto& sid : serverInitialization.DatabaseAllowedSids) {
42+
securityConfig.AddDatabaseAllowedSIDs(sid);
43+
}
44+
45+
return config;
46+
}
47+
48+
NDiscovery::TWhoAmIResult WhoAmI(ui16 grpc, const TString& token, bool withGroups) {
49+
TString location = TStringBuilder() << "localhost:" << grpc;
50+
TDriverConfig config;
51+
config.SetEndpoint(location);
52+
config.SetAuthToken(token);
53+
config.SetDatabase("/Root");
54+
auto connection = NYdb::TDriver(config);
55+
NYdb::NDiscovery::TDiscoveryClient discoveryClient = NYdb::NDiscovery::TDiscoveryClient(connection);
56+
auto settings = NDiscovery::TWhoAmISettings().WithGroups(withGroups);
57+
auto result = discoveryClient.WhoAmI(settings).GetValueSync();
58+
connection.Stop(true);
59+
return result;
60+
}
61+
62+
} // namespace
63+
64+
Y_UNIT_TEST_SUITE(TWhoAmI) {
65+
66+
Y_UNIT_TEST(WhoAmIBasic) {
67+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
68+
.EnforceUserToken = true
69+
}));
70+
ui16 grpc = server.GetPort();
71+
72+
auto result = WhoAmI(grpc, "user1@builtin", false);
73+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
74+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "user1@builtin");
75+
}
76+
77+
Y_UNIT_TEST(WhoAmIWithGroups) {
78+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
79+
.EnforceUserToken = true
80+
}));
81+
ui16 grpc = server.GetPort();
82+
83+
auto result = WhoAmI(grpc, "user1@builtin", true);
84+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
85+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "user1@builtin");
86+
// Check that we got groups (even if empty, the call should succeed)
87+
auto groups = result.GetGroups();
88+
// User might have groups or not, depending on setup
89+
}
90+
91+
Y_UNIT_TEST(WhoAmIWithPermissions_NoPermissions) {
92+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
93+
.EnforceUserToken = true,
94+
.AdministrationAllowedSids = {},
95+
.MonitoringAllowedSids = {},
96+
.ViewerAllowedSids = {},
97+
.DatabaseAllowedSids = {}
98+
}));
99+
ui16 grpc = server.GetPort();
100+
101+
auto result = WhoAmI(grpc, "user1@builtin", true);
102+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
103+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "user1@builtin");
104+
UNIT_ASSERT_VALUES_EQUAL(result.IsTokenRequired(), true);
105+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), false);
106+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), false);
107+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), false);
108+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), false);
109+
}
110+
111+
Y_UNIT_TEST(WhoAmIWithPermissions_DatabaseAllowed) {
112+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
113+
.EnforceUserToken = true,
114+
.DatabaseAllowedSids = {"user1@builtin"}
115+
}));
116+
ui16 grpc = server.GetPort();
117+
118+
auto result = WhoAmI(grpc, "user1@builtin", true);
119+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
120+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "user1@builtin");
121+
UNIT_ASSERT_VALUES_EQUAL(result.IsTokenRequired(), true);
122+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), false);
123+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), false);
124+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), false);
125+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), true);
126+
}
127+
128+
Y_UNIT_TEST(WhoAmIWithPermissions_ViewerAllowed) {
129+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
130+
.EnforceUserToken = true,
131+
.ViewerAllowedSids = {"user1@builtin"}
132+
}));
133+
ui16 grpc = server.GetPort();
134+
135+
auto result = WhoAmI(grpc, "user1@builtin", true);
136+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
137+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "user1@builtin");
138+
UNIT_ASSERT_VALUES_EQUAL(result.IsTokenRequired(), true);
139+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), false);
140+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), false);
141+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), true);
142+
// Viewer implies database access
143+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), true);
144+
}
145+
146+
Y_UNIT_TEST(WhoAmIWithPermissions_MonitoringAllowed) {
147+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
148+
.EnforceUserToken = true,
149+
.MonitoringAllowedSids = {"user1@builtin"}
150+
}));
151+
ui16 grpc = server.GetPort();
152+
153+
auto result = WhoAmI(grpc, "user1@builtin", true);
154+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
155+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "user1@builtin");
156+
UNIT_ASSERT_VALUES_EQUAL(result.IsTokenRequired(), true);
157+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), false);
158+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), true);
159+
// Monitoring implies viewer and database access
160+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), true);
161+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), true);
162+
}
163+
164+
Y_UNIT_TEST(WhoAmIWithPermissions_AdministrationAllowed) {
165+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
166+
.EnforceUserToken = true,
167+
.AdministrationAllowedSids = {"user1@builtin"}
168+
}));
169+
ui16 grpc = server.GetPort();
170+
171+
auto result = WhoAmI(grpc, "user1@builtin", true);
172+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
173+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "user1@builtin");
174+
UNIT_ASSERT_VALUES_EQUAL(result.IsTokenRequired(), true);
175+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), true);
176+
// Administration implies all other permissions
177+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), true);
178+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), true);
179+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), true);
180+
}
181+
182+
Y_UNIT_TEST(WhoAmIWithPermissions_TokenNotRequired) {
183+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
184+
.EnforceUserToken = false
185+
}));
186+
ui16 grpc = server.GetPort();
187+
188+
auto result = WhoAmI(grpc, "user1@builtin", true);
189+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
190+
UNIT_ASSERT_VALUES_EQUAL(result.IsTokenRequired(), false);
191+
}
192+
193+
Y_UNIT_TEST(WhoAmIWithoutGroups_NoPermissions) {
194+
// When withGroups is false, permission fields should not be populated
195+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
196+
.EnforceUserToken = true,
197+
.AdministrationAllowedSids = {"user1@builtin"}
198+
}));
199+
ui16 grpc = server.GetPort();
200+
201+
auto result = WhoAmI(grpc, "user1@builtin", false);
202+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
203+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "user1@builtin");
204+
// Without groups flag, permissions should not be populated (all false)
205+
UNIT_ASSERT_VALUES_EQUAL(result.IsTokenRequired(), false);
206+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), false);
207+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), false);
208+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), false);
209+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), false);
210+
}
211+
212+
Y_UNIT_TEST(WhoAmIWithPermissions_DifferentUser) {
213+
TBasicKikimrWithGrpcAndRootSchema<TKikimrTestWithAuth> server(GetWhoAmIAppConfig({
214+
.EnforceUserToken = true,
215+
.AdministrationAllowedSids = {"admin@builtin"},
216+
.ViewerAllowedSids = {"viewer@builtin"}
217+
}));
218+
ui16 grpc = server.GetPort();
219+
220+
// Test admin user
221+
{
222+
auto result = WhoAmI(grpc, "admin@builtin", true);
223+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
224+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "admin@builtin");
225+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), true);
226+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), true);
227+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), true);
228+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), true);
229+
}
230+
231+
// Test viewer user
232+
{
233+
auto result = WhoAmI(grpc, "viewer@builtin", true);
234+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
235+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "viewer@builtin");
236+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), false);
237+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), false);
238+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), true);
239+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), true);
240+
}
241+
242+
// Test regular user
243+
{
244+
auto result = WhoAmI(grpc, "regular@builtin", true);
245+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToOneLineString());
246+
UNIT_ASSERT_STRINGS_EQUAL(result.GetUserName(), "regular@builtin");
247+
UNIT_ASSERT_VALUES_EQUAL(result.IsAdministrationAllowed(), false);
248+
UNIT_ASSERT_VALUES_EQUAL(result.IsMonitoringAllowed(), false);
249+
UNIT_ASSERT_VALUES_EQUAL(result.IsViewerAllowed(), false);
250+
UNIT_ASSERT_VALUES_EQUAL(result.IsDatabaseAllowed(), false);
251+
}
252+
}
253+
}
254+
255+
} // namespace NKikimr

0 commit comments

Comments
 (0)