Skip to content

Commit 3d165ab

Browse files
committed
IAM_NOOBAA | IAM Create account
Signed-off-by: Naveen Paul <[email protected]>
1 parent 06ca51e commit 3d165ab

File tree

9 files changed

+881
-283
lines changed

9 files changed

+881
-283
lines changed

src/endpoint/endpoint.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const endpoint_utils = require('./endpoint_utils');
2424
const StsSDK = require('../sdk/sts_sdk');
2525
const ObjectIO = require('../sdk/object_io');
2626
const ObjectSDK = require('../sdk/object_sdk');
27+
const NBAccountSDK = require('../sdk/nb_account_sdk');
2728
const xml_utils = require('../util/xml_utils');
2829
const http_utils = require('../util/http_utils');
2930
const net_utils = require('../util/net_utils');
@@ -44,6 +45,7 @@ const { get_notification_logger } = require('../util/notifications_util');
4445
const ldap_client = require('../util/ldap_client');
4546
const { is_nc_environment } = require('../nc/nc_utils');
4647
const NoobaaEvent = require('../manage_nsfs/manage_nsfs_events_utils').NoobaaEvent;
48+
4749
const cluster = /** @type {import('node:cluster').Cluster} */ (
4850
/** @type {unknown} */
4951
(require('node:cluster'))
@@ -431,6 +433,11 @@ function create_init_request_sdk(rpc, internal_rpc_client, object_io) {
431433
object_io,
432434
stats: endpoint_stats_collector.instance(),
433435
});
436+
req.account_sdk = new NBAccountSDK({
437+
rpc_client,
438+
internal_rpc_client,
439+
//stats: endpoint_stats_collector.instance(),
440+
});
434441
};
435442
return init_request_sdk;
436443
}

src/endpoint/iam/iam_constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const IAM_PARAMETER_NAME = Object.freeze({
5858
NEW_USERNAME: 'NewUserName',
5959
});
6060

61+
const IAM_SPLIT_CHARACTERS = ':';
6162

6263
// EXPORTS
6364
exports.IAM_ACTIONS = IAM_ACTIONS;
@@ -70,3 +71,4 @@ exports.IAM_DEFAULT_PATH = IAM_DEFAULT_PATH;
7071
exports.AWS_NOT_USED = AWS_NOT_USED;
7172
exports.IAM_SERVICE_SMALL_LETTERS = IAM_SERVICE_SMALL_LETTERS;
7273
exports.IAM_PARAMETER_NAME = IAM_PARAMETER_NAME;
74+
exports.IAM_SPLIT_CHARACTERS = IAM_SPLIT_CHARACTERS;

src/sdk/accountspace_nb.js

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/* Copyright (C) 2024 NooBaa */
2+
'use strict';
3+
4+
const _ = require('lodash');
5+
const SensitiveString = require('../util/sensitive_string');
6+
const account_util = require('../util/account_util');
7+
const iam_utils = require('../endpoint/iam/iam_utils');
8+
const dbg = require('../util/debug_module')(__filename);
9+
const system_store = require('..//server/system_services/system_store').get_instance();
10+
// const { account_cache } = require('./object_sdk');
11+
const IamError = require('../endpoint/iam/iam_errors').IamError;
12+
const { IAM_ACTIONS, IAM_DEFAULT_PATH, IAM_SPLIT_CHARACTERS } = require('../endpoint/iam/iam_constants');
13+
14+
15+
/*
16+
TODO: DISCUSS:
17+
1. IAM API only for account created using IAM API and OBC accounts not from admin, support,
18+
operator and account created using noobaa.
19+
2. Do we need to have two access keys
20+
3. get_access_key_last_used() API call could return dummy values?
21+
*/
22+
23+
/**
24+
* @implements {nb.AccountSpace}
25+
*/
26+
class AccountSpaceNB {
27+
/**
28+
* @param {{
29+
* rpc_client: nb.APIClient;
30+
* internal_rpc_client: nb.APIClient;
31+
* stats?: import('./endpoint_stats_collector').EndpointStatsCollector;
32+
* }} params
33+
*/
34+
constructor({ rpc_client, internal_rpc_client, stats }) {
35+
this.rpc_client = rpc_client;
36+
this.internal_rpc_client = internal_rpc_client;
37+
this.stats = stats;
38+
}
39+
40+
//////////////////////
41+
// ACCOUNT METHODS //
42+
//////////////////////
43+
44+
async create_user(params, account_sdk) {
45+
46+
const action = IAM_ACTIONS.CREATE_USER;
47+
const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email);
48+
account_util._check_if_requesting_account_is_root_account(action, requesting_account,
49+
{ username: params.username, path: params.iam_path });
50+
account_util._check_username_already_exists(action, params, requesting_account);
51+
const iam_arn = iam_utils.create_arn_for_user(requesting_account._id.toString(), params.username, params.iam_path);
52+
const account_name = account_util.populate_username(params.username, requesting_account.name.unwrap());
53+
const req = {
54+
rpc_params: {
55+
name: account_name,
56+
email: account_name,
57+
has_login: false,
58+
s3_access: true,
59+
allow_bucket_creation: true,
60+
owner: requesting_account._id.toString(),
61+
is_iam: true,
62+
iam_arn: iam_arn,
63+
iam_path: params.iam_path,
64+
role: 'iam_user',
65+
66+
// TODO: default_resource remove
67+
default_resource: 'noobaa-default-backing-store',
68+
},
69+
account: requesting_account,
70+
};
71+
// CORE CHANGES PENDING - START
72+
const iam_account = await account_util.create_account(req);
73+
// CORE CHANGES PENDING - END
74+
75+
// TODO : Clean account cache
76+
// TODO : Send Event
77+
const requested_account = system_store.get_account_by_email(account_name);
78+
return {
79+
iam_path: requested_account.iam_path || IAM_DEFAULT_PATH,
80+
username: params.username,
81+
user_id: iam_account.id,
82+
arn: iam_arn,
83+
create_date: iam_account.create_date,
84+
};
85+
86+
}
87+
88+
async get_user(params, account_sdk) {
89+
const action = IAM_ACTIONS.GET_USER;
90+
const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email);
91+
const account_name = account_util.populate_username(params.username, requesting_account.name.unwrap());
92+
const requested_account = system_store.get_account_by_email(account_name);
93+
account_util._check_if_requesting_account_is_root_account(action, requesting_account,
94+
{ username: params.username, iam_path: params.iam_path });
95+
account_util._check_if_account_exists(action, account_name);
96+
account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account);
97+
account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account);
98+
const reply = {
99+
user_id: requested_account._id.toString(),
100+
// TODO : IAM PATH
101+
iam_path: requested_account.iam_path || IAM_DEFAULT_PATH,
102+
username: account_util.get_iam_username(requested_account.name.unwrap()),
103+
arn: requested_account.iam_arn,
104+
// TODO: GAP Need to save created date
105+
create_date: new Date(),
106+
// TODO: Dates missing : GAP
107+
password_last_used: new Date(),
108+
};
109+
return reply;
110+
}
111+
112+
async update_user(params, account_sdk) {
113+
const action = IAM_ACTIONS.UPDATE_USER;
114+
const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email);
115+
const username = account_util.populate_username(params.username, requesting_account.name.unwrap());
116+
account_util._check_if_requesting_account_is_root_account(action, requesting_account,
117+
{ username: params.username, iam_path: params.iam_path });
118+
account_util._check_if_account_exists(action, username);
119+
const requested_account = system_store.get_account_by_email(username);
120+
let iam_path = requested_account.iam_path;
121+
let user_name = requested_account.name.unwrap();
122+
account_util._check_username_already_exists(action, { username: params.new_username }, requesting_account);
123+
account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account);
124+
account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account);
125+
if (params.new_iam_path !== undefined) iam_path = params.new_iam_path;
126+
if (params.new_username !== undefined) user_name = params.new_username;
127+
const iam_arn = iam_utils.create_arn_for_user(requested_account._id.toString(), user_name, iam_path);
128+
const new_account_name = new SensitiveString(`${params.new_username}:${requesting_account.name.unwrap()}`);
129+
const updates = {
130+
name: new_account_name,
131+
email: new_account_name,
132+
iam_arn: iam_arn,
133+
iam_path: iam_path,
134+
};
135+
// CORE CHANGES PENDING - START
136+
await system_store.make_changes({
137+
update: {
138+
accounts: [{
139+
_id: requested_account._id,
140+
$set: _.omitBy(updates, _.isUndefined),
141+
}]
142+
}
143+
});
144+
// CORE CHANGES PENDING - END
145+
// TODO : Clean account cache
146+
// TODO : Send Event
147+
return {
148+
// TODO: IAM path needs to be saved
149+
iam_path: iam_path || IAM_DEFAULT_PATH,
150+
username: user_name,
151+
user_id: requested_account._id.toString(),
152+
arn: iam_arn
153+
};
154+
155+
}
156+
157+
async delete_user(params, account_sdk) {
158+
const action = IAM_ACTIONS.DELETE_USER;
159+
// GAP - we do not have the user iam_path at this point (error message)
160+
//const requesting_account = account_sdk.requesting_account;
161+
const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email);
162+
const username = account_util.populate_username(params.username, requesting_account.name.unwrap());
163+
account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: params.username });
164+
account_util._check_if_account_exists(action, username);
165+
const requested_account = system_store.get_account_by_email(username);
166+
account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account);
167+
//const root_account = system_store.get_account_by_email(requesting_account.email);
168+
account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account);
169+
// TODO: DELETE INLINE POLICY : Manually
170+
// TODO: DELETE ACCESS KEY : manually
171+
const req = {
172+
system: system_store.data.systems[0],
173+
account: requested_account,
174+
};
175+
// CORE CHANGES PENDING - START
176+
return account_util.delete_account(req, requested_account);
177+
// CORE CHANGES PENDING - END
178+
// TODO : clean account cache
179+
180+
}
181+
182+
async list_users(params, account_sdk) {
183+
const action = IAM_ACTIONS.LIST_USERS;
184+
//const requesting_account = account_sdk.requesting_account;
185+
const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email);
186+
account_util._check_if_requesting_account_is_root_account(action, requesting_account, { });
187+
const is_truncated = false; // GAP - no pagination at this point
188+
189+
const root_name = requesting_account.name.unwrap();
190+
// CORE CHANGES PENDING - START
191+
const requesting_account_iam_users = _.filter(system_store.data.accounts, function(acc) {
192+
if (!acc.name.unwrap().includes(IAM_SPLIT_CHARACTERS)) {
193+
return false;
194+
}
195+
return acc.name.unwrap().split(IAM_SPLIT_CHARACTERS)[1] === root_name;
196+
});
197+
let members = _.map(requesting_account_iam_users, function(iam_user) {
198+
const member = {
199+
user_id: iam_user._id.toString(),
200+
iam_path: iam_user.iam_path || IAM_DEFAULT_PATH,
201+
username: iam_user.name.unwrap().split(IAM_SPLIT_CHARACTERS)[0],
202+
arn: iam_user.iam_arn,
203+
// TODO: GAP Need to save created date
204+
create_date: new Date(),
205+
// TODO: GAP Miising password_last_used
206+
password_last_used: Date.now(), // GAP
207+
};
208+
return member;
209+
});
210+
// CORE CHANGES PENDING - END
211+
members = members.sort((a, b) => a.username.localeCompare(b.username));
212+
return { members, is_truncated };
213+
}
214+
215+
/////////////////////////////////
216+
// ACCOUNT ACCESS KEY METHODS //
217+
/////////////////////////////////
218+
219+
async create_access_key(params, account_sdk) {
220+
// TODO
221+
dbg.log0('AccountSpaceNB.create_access_key:', params);
222+
const { code, http_code, type } = IamError.NotImplemented;
223+
throw new IamError({ code, message: 'NotImplemented', http_code, type });
224+
}
225+
226+
async get_access_key_last_used(params, account_sdk) {
227+
dbg.log0('AccountSpaceNB.get_access_key_last_used:', params);
228+
const { code, http_code, type } = IamError.NotImplemented;
229+
throw new IamError({ code, message: 'NotImplemented', http_code, type });
230+
}
231+
232+
async update_access_key(params, account_sdk) {
233+
dbg.log0('AccountSpaceNB.update_access_key:', params);
234+
const { code, http_code, type } = IamError.NotImplemented;
235+
throw new IamError({ code, message: 'NotImplemented', http_code, type });
236+
}
237+
238+
async delete_access_key(params, account_sdk) {
239+
dbg.log0('AccountSpaceNB.delete_access_key:', params);
240+
const { code, http_code, type } = IamError.NotImplemented;
241+
throw new IamError({ code, message: 'NotImplemented', http_code, type });
242+
}
243+
244+
async list_access_keys(params, account_sdk) {
245+
dbg.log0('AccountSpaceNB.list_access_keys:', params);
246+
const { code, http_code, type } = IamError.NotImplemented;
247+
throw new IamError({ code, message: 'NotImplemented', http_code, type });
248+
}
249+
250+
////////////////////
251+
// POLICY METHODS //
252+
////////////////////
253+
}
254+
255+
// EXPORTS
256+
module.exports = AccountSpaceNB;

src/sdk/nb_account_sdk.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* Copyright (C) 2016 NooBaa */
2+
'use strict';
3+
4+
const AccountSDK = require('../sdk/account_sdk');
5+
const BucketSpaceNB = require('./bucketspace_nb');
6+
const AccountSpaceNB = require('../sdk/accountspace_nb');
7+
8+
// NBAccountSDK was based on AccountSDK
9+
class NBAccountSDK extends AccountSDK {
10+
/**
11+
* @param {{
12+
* rpc_client: nb.APIClient;
13+
* internal_rpc_client: nb.APIClient;
14+
* }} args
15+
*/
16+
constructor({rpc_client, internal_rpc_client}) {
17+
const bucketspace = new BucketSpaceNB({ rpc_client, internal_rpc_client });
18+
const accountspace = new AccountSpaceNB({ rpc_client, internal_rpc_client });
19+
20+
super({
21+
rpc_client: rpc_client,
22+
internal_rpc_client: internal_rpc_client,
23+
bucketspace: bucketspace,
24+
accountspace: accountspace,
25+
});
26+
}
27+
}
28+
29+
module.exports = NBAccountSDK;

0 commit comments

Comments
 (0)