Skip to content

Commit f7d41a7

Browse files
committed
IAM | CreateUser, UpdateUser - UserName, NewUserName Check
Signed-off-by: shirady <57721533+shirady@users.noreply.github.com>
1 parent 7362470 commit f7d41a7

File tree

6 files changed

+187
-16
lines changed

6 files changed

+187
-16
lines changed

src/api/account_api.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,9 @@ module.exports = {
676676
iam_path: {
677677
type: 'string',
678678
},
679+
username: {
680+
type: 'string',
681+
},
679682
}
680683
},
681684
reply: {

src/sdk/accountspace_fs.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -794,15 +794,34 @@ class AccountSpaceFS {
794794
}
795795

796796
async _check_username_already_exists(action, params, requesting_account) {
797-
const owner_account_id = this._get_owner_account_argument(requesting_account, params);
797+
const owner_account_id = this._get_owner_account_argument(requesting_account);
798798
const username = params.username;
799-
const name_exists = await this.config_fs.is_account_exists_by_name(username, owner_account_id);
800-
if (name_exists) {
801-
dbg.error(`AccountSpaceFS.${action} username already exists`, username);
802-
const message_with_details = `User with name ${username} already exists.`;
803-
const { code, http_code, type } = IamError.EntityAlreadyExists;
804-
throw new IamError({ code, message: message_with_details, http_code, type });
799+
const file_name_exists = await this.config_fs.is_account_exists_by_name(username, owner_account_id);
800+
if (file_name_exists) {
801+
this._throw_error_if_account_already_exists(action, username);
802+
}
803+
const is_username_lowercase_exists_under_owner = await this._check_if_account_exists_under_the_owner(
804+
requesting_account, username);
805+
if (is_username_lowercase_exists_under_owner) {
806+
this._throw_error_if_account_already_exists(action, username);
807+
}
808+
}
809+
810+
_throw_error_if_account_already_exists(action, username) {
811+
dbg.error(`AccountSpaceFS.${action} username already exists`, username);
812+
const message_with_details = `User with name ${username} already exists.`;
813+
const { code, http_code, type } = IamError.EntityAlreadyExists;
814+
throw new IamError({ code, message: message_with_details, http_code, type });
815+
}
816+
817+
async _check_if_account_exists_under_the_owner(requesting_account, username) {
818+
const members = await this._list_config_files_for_users(requesting_account, undefined);
819+
for (const member of members) {
820+
if (member.username.toLowerCase() === username.toLowerCase()) {
821+
return true;
822+
}
805823
}
824+
return false;
806825
}
807826

808827
async _copy_data_from_requesting_account_to_account_config(action, requesting_account, params) {

src/sdk/accountspace_nb.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class AccountSpaceNB {
4545
s3_access: true,
4646
allow_bucket_creation: true,
4747
owner: requesting_account._id.toString(),
48+
username: params.username,
4849
iam_path: params.iam_path,
4950
roles: ['admin'],
5051
// TODO: default_resource remove

src/server/system_services/account_server.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ const check_new_azure_connection_timeout = 20 * 1000;
3737
*
3838
*/
3939
async function create_account(req) {
40-
const action = IAM_ACTIONS.CREATE_USER;
4140
let iam_arn;
4241
if (req.rpc_params.owner) {
43-
const user_name = account_util.get_iam_username(req.rpc_params.email.unwrap());
42+
const action = IAM_ACTIONS.CREATE_USER;
4443
account_util._check_if_requesting_account_is_root_account(action, req.account,
45-
{ username: user_name, path: req.rpc_params.iam_path });
46-
account_util._check_username_already_exists(action, req.rpc_params.email, user_name);
47-
iam_arn = iam_utils.create_arn_for_user(req.account._id.toString(), user_name,
44+
{ username: req.rpc_params.username, path: req.rpc_params.iam_path });
45+
account_util._check_username_already_exists(action, req.rpc_params.email,
46+
req.rpc_params.username);
47+
iam_arn = iam_utils.create_arn_for_user(req.account._id.toString(), req.rpc_params.username,
4848
req.rpc_params.iam_path || IAM_DEFAULT_PATH);
4949
} else {
5050
account_util.validate_create_account_permissions(req);
@@ -1234,8 +1234,14 @@ async function update_user(req) {
12341234
let iam_path = requested_account.iam_path;
12351235
let user_name = account_util.get_iam_username(requested_account.name.unwrap());
12361236
// Change to complete user name
1237-
const new_username = account_util.get_account_name_from_username(req.rpc_params.new_username, requesting_account._id.toString());
1238-
account_util._check_username_already_exists(action, new_username, req.rpc_params.new_username);
1237+
const is_username_update = req.rpc_params.new_username !== undefined &&
1238+
req.rpc_params.new_username !== req.rpc_params.username;
1239+
if (is_username_update) {
1240+
const email_new_username_wrapped = account_util.get_account_name_from_username(
1241+
req.rpc_params.new_username,
1242+
requesting_account._id.toString());
1243+
account_util._check_username_already_exists(action, email_new_username_wrapped, req.rpc_params.new_username);
1244+
}
12391245
account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account);
12401246
account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account);
12411247
if (req.rpc_params.new_iam_path) iam_path = req.rpc_params.new_iam_path;

src/test/integration_tests/api/iam/test_iam_basic_integration.js

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,141 @@ mocha.describe('IAM basic integration tests - happy path', async function() {
927927
});
928928
});
929929

930+
mocha.describe('IAM advanced integration tests', async function() {
931+
mocha.describe('IAM User API', async function() {
932+
const username = 'Mateo';
933+
const username_lowercase = username.toLowerCase();
934+
const username_uppercase = username.toUpperCase();
935+
936+
mocha.describe('IAM CreateUser API', async function() {
937+
mocha.before(async () => {
938+
// create a user
939+
const input = {
940+
UserName: username
941+
};
942+
const command = new CreateUserCommand(input);
943+
const response = await iam_account.send(command);
944+
_check_status_code_ok(response);
945+
});
946+
947+
mocha.after(async () => {
948+
// delete a user
949+
const input = {
950+
UserName: username
951+
};
952+
const command = new DeleteUserCommand(input);
953+
const response = await iam_account.send(command);
954+
_check_status_code_ok(response);
955+
});
956+
957+
mocha.it('create a user with username that already exists should fail', async function() {
958+
try {
959+
const input = {
960+
UserName: username
961+
};
962+
const command = new CreateUserCommand(input);
963+
await iam_account.send(command);
964+
assert.fail('create user with existing username - should throw an error');
965+
} catch (err) {
966+
const err_code = err.Error.Code;
967+
assert.equal(err_code, IamError.EntityAlreadyExists.code);
968+
}
969+
});
970+
971+
mocha.it('create a user with username that already exists (lower case) should fail', async function() {
972+
try {
973+
const input = {
974+
UserName: username_lowercase
975+
};
976+
const command = new CreateUserCommand(input);
977+
await iam_account.send(command);
978+
assert.fail('create user with existing username (lower case) - should throw an error');
979+
} catch (err) {
980+
const err_code = err.Error.Code;
981+
assert.equal(err_code, IamError.EntityAlreadyExists.code);
982+
}
983+
});
984+
985+
mocha.it('create a user with username that already exists (upper case) should fail', async function() {
986+
try {
987+
const input = {
988+
UserName: username_uppercase
989+
};
990+
const command = new CreateUserCommand(input);
991+
await iam_account.send(command);
992+
assert.fail('create user with existing username (upper case) - should throw an error');
993+
} catch (err) {
994+
const err_code = err.Error.Code;
995+
assert.equal(err_code, IamError.EntityAlreadyExists.code);
996+
}
997+
});
998+
});
999+
1000+
mocha.describe('IAM UpdateUser API', async function() {
1001+
mocha.before(async () => {
1002+
// create a user
1003+
const input = {
1004+
UserName: username
1005+
};
1006+
const command = new CreateUserCommand(input);
1007+
const response = await iam_account.send(command);
1008+
_check_status_code_ok(response);
1009+
});
1010+
1011+
mocha.after(async () => {
1012+
// delete a user
1013+
const input = {
1014+
UserName: username
1015+
};
1016+
const command = new DeleteUserCommand(input);
1017+
const response = await iam_account.send(command);
1018+
_check_status_code_ok(response);
1019+
});
1020+
1021+
mocha.it('update a user with same username', async function() {
1022+
const input = {
1023+
UserName: username,
1024+
NewUserName: username,
1025+
};
1026+
const command = new UpdateUserCommand(input);
1027+
const response = await iam_account.send(command);
1028+
_check_status_code_ok(response);
1029+
});
1030+
1031+
mocha.it('update a user with new username that already exists (lower case) should fail', async function() {
1032+
try {
1033+
const input = {
1034+
UserName: username,
1035+
NewUserName: username_lowercase,
1036+
};
1037+
const command = new UpdateUserCommand(input);
1038+
await iam_account.send(command);
1039+
assert.fail('update user with existing username (lower case) - should throw an error');
1040+
} catch (err) {
1041+
const err_code = err.Error.Code;
1042+
assert.equal(err_code, IamError.EntityAlreadyExists.code);
1043+
}
1044+
});
1045+
1046+
mocha.it('update a user with new username that already exists (upper case) should fail', async function() {
1047+
try {
1048+
const input = {
1049+
UserName: username,
1050+
NewUserName: username_uppercase,
1051+
};
1052+
const command = new UpdateUserCommand(input);
1053+
await iam_account.send(command);
1054+
assert.fail('update user with existing username (upper case) - should throw an error');
1055+
} catch (err) {
1056+
const err_code = err.Error.Code;
1057+
assert.equal(err_code, IamError.EntityAlreadyExists.code);
1058+
}
1059+
});
1060+
});
1061+
1062+
});
1063+
});
1064+
9301065
/**
9311066
* _check_status_code_ok is an helper function to check that we got an response from the server
9321067
* @param {{ $metadata: { httpStatusCode: number; }; }} response

src/util/account_util.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,15 +352,22 @@ function _check_if_requesting_account_is_root_account(action, requesting_account
352352
}
353353
}
354354

355-
function _check_username_already_exists(action, email, username) {
356-
const account = system_store.get_account_by_email(email);
355+
function _check_username_already_exists(action, email_wrapped, username) {
356+
const account = _check_if_account_exists_by_email(email_wrapped);
357357
if (account) {
358358
dbg.error(`AccountSpaceNB.${action} username already exists`, username);
359359
const message_with_details = `User with name ${username} already exists.`;
360360
throw new RpcError('ENTITY_ALREADY_EXISTS', message_with_details);
361361
}
362362
}
363363

364+
function _check_if_account_exists_by_email(email_wrapped) {
365+
const accounts = system_store.data.accounts || [];
366+
return accounts.find(account =>
367+
account.email.unwrap().toLowerCase() === email_wrapped.unwrap().toLowerCase()
368+
);
369+
}
370+
364371
function _check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account) {
365372
const is_requested_account_root_account = _check_root_account(requested_account);
366373
dbg.log1(`AccountSpaceNB.${action} requested_account ID: ${requested_account._id} name: ${requested_account.name}`,

0 commit comments

Comments
 (0)