From 1e7df68bb05c02fac2c6baf27eafd1328a2fda2a Mon Sep 17 00:00:00 2001 From: shirady <57721533+shirady@users.noreply.github.com> Date: Sun, 30 Nov 2025 11:48:24 +0200 Subject: [PATCH 1/4] IAM | CreateUser, UpdateUser - UserName, NewUserName Check Signed-off-by: shirady <57721533+shirady@users.noreply.github.com> --- src/api/account_api.js | 3 + src/sdk/accountspace_fs.js | 33 +- src/sdk/accountspace_nb.js | 1 + src/server/system_services/account_server.js | 20 +- .../api/iam/test_iam_basic_integration.js | 1699 +++++++++-------- src/util/account_util.js | 11 +- 6 files changed, 977 insertions(+), 790 deletions(-) diff --git a/src/api/account_api.js b/src/api/account_api.js index 4e4b19baba..4ba1bc785f 100644 --- a/src/api/account_api.js +++ b/src/api/account_api.js @@ -676,6 +676,9 @@ module.exports = { iam_path: { type: 'string', }, + username: { + type: 'string', + }, } }, reply: { diff --git a/src/sdk/accountspace_fs.js b/src/sdk/accountspace_fs.js index 2f0be7a1d9..d8dee413ec 100644 --- a/src/sdk/accountspace_fs.js +++ b/src/sdk/accountspace_fs.js @@ -794,15 +794,34 @@ class AccountSpaceFS { } async _check_username_already_exists(action, params, requesting_account) { - const owner_account_id = this._get_owner_account_argument(requesting_account, params); + const owner_account_id = this._get_owner_account_argument(requesting_account); const username = params.username; - const name_exists = await this.config_fs.is_account_exists_by_name(username, owner_account_id); - if (name_exists) { - dbg.error(`AccountSpaceFS.${action} username already exists`, username); - const message_with_details = `User with name ${username} already exists.`; - const { code, http_code, type } = IamError.EntityAlreadyExists; - throw new IamError({ code, message: message_with_details, http_code, type }); + const file_name_exists = await this.config_fs.is_account_exists_by_name(username, owner_account_id); + if (file_name_exists) { + this._throw_error_if_account_already_exists(action, username); + } + const is_username_lowercase_exists_under_owner = await this._check_if_account_exists_under_the_owner( + requesting_account, username); + if (is_username_lowercase_exists_under_owner) { + this._throw_error_if_account_already_exists(action, username); + } + } + + _throw_error_if_account_already_exists(action, username) { + dbg.error(`AccountSpaceFS.${action} username already exists`, username); + const message_with_details = `User with name ${username} already exists.`; + const { code, http_code, type } = IamError.EntityAlreadyExists; + throw new IamError({ code, message: message_with_details, http_code, type }); + } + + async _check_if_account_exists_under_the_owner(requesting_account, username) { + const members = await this._list_config_files_for_users(requesting_account, undefined); + for (const member of members) { + if (member.username.toLowerCase() === username.toLowerCase()) { + return true; + } } + return false; } async _copy_data_from_requesting_account_to_account_config(action, requesting_account, params) { diff --git a/src/sdk/accountspace_nb.js b/src/sdk/accountspace_nb.js index acadf52ae8..20122ae7b4 100644 --- a/src/sdk/accountspace_nb.js +++ b/src/sdk/accountspace_nb.js @@ -45,6 +45,7 @@ class AccountSpaceNB { s3_access: true, allow_bucket_creation: true, owner: requesting_account._id.toString(), + username: params.username, iam_path: params.iam_path, roles: ['admin'], // TODO: default_resource remove diff --git a/src/server/system_services/account_server.js b/src/server/system_services/account_server.js index b77c5889f0..2dd4fa668a 100644 --- a/src/server/system_services/account_server.js +++ b/src/server/system_services/account_server.js @@ -37,14 +37,14 @@ const check_new_azure_connection_timeout = 20 * 1000; * */ async function create_account(req) { - const action = IAM_ACTIONS.CREATE_USER; let iam_arn; if (req.rpc_params.owner) { - const user_name = account_util.get_iam_username(req.rpc_params.email.unwrap()); + const action = IAM_ACTIONS.CREATE_USER; account_util._check_if_requesting_account_is_root_account(action, req.account, - { username: user_name, path: req.rpc_params.iam_path }); - account_util._check_username_already_exists(action, req.rpc_params.email, user_name); - iam_arn = iam_utils.create_arn_for_user(req.account._id.toString(), user_name, + { username: req.rpc_params.username, path: req.rpc_params.iam_path }); + account_util._check_username_already_exists(action, req.rpc_params.email, + req.rpc_params.username); + iam_arn = iam_utils.create_arn_for_user(req.account._id.toString(), req.rpc_params.username, req.rpc_params.iam_path || IAM_DEFAULT_PATH); } else { account_util.validate_create_account_permissions(req); @@ -1236,8 +1236,14 @@ async function update_user(req) { let iam_path = requested_account.iam_path; let user_name = account_util.get_iam_username(requested_account.name.unwrap()); // Change to complete user name - const new_username = account_util.get_account_name_from_username(req.rpc_params.new_username, requesting_account._id.toString()); - account_util._check_username_already_exists(action, new_username, req.rpc_params.new_username); + const is_username_update = req.rpc_params.new_username !== undefined && + req.rpc_params.new_username !== req.rpc_params.username; + if (is_username_update) { + const email_new_username_wrapped = account_util.get_account_name_from_username( + req.rpc_params.new_username, + requesting_account._id.toString()); + account_util._check_username_already_exists(action, email_new_username_wrapped, req.rpc_params.new_username); + } account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); if (req.rpc_params.new_iam_path) iam_path = req.rpc_params.new_iam_path; diff --git a/src/test/integration_tests/api/iam/test_iam_basic_integration.js b/src/test/integration_tests/api/iam/test_iam_basic_integration.js index f27cb65400..e767f5f891 100644 --- a/src/test/integration_tests/api/iam/test_iam_basic_integration.js +++ b/src/test/integration_tests/api/iam/test_iam_basic_integration.js @@ -42,7 +42,7 @@ let iam_account; let account_res; let config_root; -mocha.describe('IAM basic integration tests - happy path', async function() { +mocha.describe('IAM integration tests', async function() { this.timeout(50000); // eslint-disable-line no-invalid-this mocha.before(async () => { @@ -69,8 +69,12 @@ mocha.describe('IAM basic integration tests - happy path', async function() { // needed details for creating the account (and then the client) const coretest_endpoint_iam = coretest.get_https_address_iam(); - const access_key = account_res.access_key instanceof SensitiveString ? account_res.access_key.unwrap() : account_res.access_key; - const secret_key = account_res.secret_key instanceof SensitiveString ? account_res.secret_key.unwrap() : account_res.secret_key; + const access_key = account_res.access_key instanceof SensitiveString ? + account_res.access_key.unwrap() : + account_res.access_key; + const secret_key = account_res.secret_key instanceof SensitiveString ? + account_res.secret_key.unwrap() : + account_res.secret_key; iam_account = generate_iam_client(access_key, secret_key, coretest_endpoint_iam); }); @@ -80,349 +84,351 @@ mocha.describe('IAM basic integration tests - happy path', async function() { } }); - mocha.describe('IAM User API', async function() { - const username = 'Asahi'; - const new_username = 'Botan'; + mocha.describe('IAM basic integration tests - happy path', async function() { - mocha.it('list users - should be empty', async function() { - const input = {}; - const command = new ListUsersCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Users.length, 0); - }); - - mocha.it('create a user', async function() { - const input = { - UserName: username - }; - const command = new CreateUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.User.UserName, username); - - // verify it using list users - const input2 = {}; - const command2 = new ListUsersCommand(input2); - const response2 = await iam_account.send(command2); - assert.equal(response2.$metadata.httpStatusCode, 200); - assert.equal(response2.Users.length, 1); - assert.equal(response2.Users[0].UserName, username); - }); - - mocha.it('get a user', async function() { - const input = { - UserName: username - }; - const command = new GetUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.User.UserName, username); - }); - - mocha.it('update a user', async function() { - const input = { - NewUserName: new_username, - UserName: username - }; - const command = new UpdateUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - - // verify it using list users - const input2 = {}; - const command2 = new ListUsersCommand(input2); - const response2 = await iam_account.send(command2); - _check_status_code_ok(response2); - assert.equal(response2.Users.length, 1); - assert.equal(response2.Users[0].UserName, new_username); - }); - - mocha.it('delete a user', async function() { - const input = { - UserName: new_username // delete a user after its username was updated - }; - const command = new DeleteUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - }); - }); + mocha.describe('IAM User API', async function() { + const username = 'Asahi'; + const new_username = 'Botan'; - mocha.describe('IAM Access Key API', async function() { - const username2 = 'Fuji'; - let access_key_id; + mocha.it('list users - should be empty', async function() { + const input = {}; + const command = new ListUsersCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Users.length, 0); + }); - mocha.before(async () => { - // create a user - const input = { - UserName: username2 - }; - const command = new CreateUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - }); - - mocha.after(async () => { - // delete a user - const input = { - UserName: username2 - }; - const command = new DeleteUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - // note: if somehow the delete access key would fail, then deleting the user would also fail - // (as we can delete a user only after its access keys were deleted) - }); + mocha.it('create a user', async function() { + const input = { + UserName: username + }; + const command = new CreateUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.User.UserName, username); + + // verify it using list users + const input2 = {}; + const command2 = new ListUsersCommand(input2); + const response2 = await iam_account.send(command2); + assert.equal(response2.$metadata.httpStatusCode, 200); + assert.equal(response2.Users.length, 1); + assert.equal(response2.Users[0].UserName, username); + }); + + mocha.it('get a user', async function() { + const input = { + UserName: username + }; + const command = new GetUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.User.UserName, username); + }); - mocha.it('list access keys - should be empty', async function() { - const input = { - UserName: username2 - }; - const command = new ListAccessKeysCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.AccessKeyMetadata.length, 0); + mocha.it('update a user', async function() { + const input = { + NewUserName: new_username, + UserName: username + }; + const command = new UpdateUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + + // verify it using list users + const input2 = {}; + const command2 = new ListUsersCommand(input2); + const response2 = await iam_account.send(command2); + _check_status_code_ok(response2); + assert.equal(response2.Users.length, 1); + assert.equal(response2.Users[0].UserName, new_username); + }); + + mocha.it('delete a user', async function() { + const input = { + UserName: new_username // delete a user after its username was updated + }; + const command = new DeleteUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); }); - mocha.it('create access keys', async function() { - const input = { - UserName: username2 - }; - const command = new CreateAccessKeyCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.AccessKey.UserName, username2); - assert(response.AccessKey.AccessKeyId !== undefined); - access_key_id = response.AccessKey.AccessKeyId; - assert(response.AccessKey.SecretAccessKey !== undefined); - assert.equal(response.AccessKey.Status, ACCESS_KEY_STATUS_ENUM.ACTIVE); - - // verify it using list access keys - const input2 = { - UserName: username2 - }; - const command2 = new ListAccessKeysCommand(input2); - const response2 = await iam_account.send(command2); - _check_status_code_ok(response2); - assert.equal(response2.AccessKeyMetadata.length, 1); - assert.equal(response2.AccessKeyMetadata[0].UserName, username2); - assert.equal(response2.AccessKeyMetadata[0].AccessKeyId, access_key_id); - assert.equal(response2.AccessKeyMetadata[0].Status, ACCESS_KEY_STATUS_ENUM.ACTIVE); - }); + mocha.describe('IAM Access Key API', async function() { + const username2 = 'Fuji'; + let access_key_id; - mocha.it('get access key (last used)', async function() { - // Skipping for containerized noobaa - if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this - const input = { - AccessKeyId: access_key_id - }; - const command = new GetAccessKeyLastUsedCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.UserName, username2); - assert(response.AccessKeyLastUsed.LastUsedDate !== undefined); - assert(response.AccessKeyLastUsed.ServiceName !== undefined); - assert(response.AccessKeyLastUsed.Region !== undefined); - }); + mocha.before(async () => { + // create a user + const input = { + UserName: username2 + }; + const command = new CreateUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); - mocha.it('update access keys (active to inactive)', async function() { - const input = { - UserName: username2, - AccessKeyId: access_key_id, - Status: ACCESS_KEY_STATUS_ENUM.INACTIVE - }; - const command = new UpdateAccessKeyCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); + mocha.after(async () => { + // delete a user + const input = { + UserName: username2 + }; + const command = new DeleteUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + // note: if somehow the delete access key would fail, then deleting the user would also fail + // (as we can delete a user only after its access keys were deleted) + }); + + mocha.it('list access keys - should be empty', async function() { + const input = { + UserName: username2 + }; + const command = new ListAccessKeysCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.AccessKeyMetadata.length, 0); + }); - // verify it using list access keys - const input2 = { - UserName: username2 - }; - const command2 = new ListAccessKeysCommand(input2); - const response2 = await iam_account.send(command2); - _check_status_code_ok(response2); - assert.equal(response2.AccessKeyMetadata.length, 1); - assert.equal(response2.AccessKeyMetadata[0].UserName, username2); - assert.equal(response2.AccessKeyMetadata[0].AccessKeyId, access_key_id); - assert.equal(response2.AccessKeyMetadata[0].Status, ACCESS_KEY_STATUS_ENUM.INACTIVE); - }); + mocha.it('create access keys', async function() { + const input = { + UserName: username2 + }; + const command = new CreateAccessKeyCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.AccessKey.UserName, username2); + assert(response.AccessKey.AccessKeyId !== undefined); + access_key_id = response.AccessKey.AccessKeyId; + assert(response.AccessKey.SecretAccessKey !== undefined); + assert.equal(response.AccessKey.Status, ACCESS_KEY_STATUS_ENUM.ACTIVE); + + // verify it using list access keys + const input2 = { + UserName: username2 + }; + const command2 = new ListAccessKeysCommand(input2); + const response2 = await iam_account.send(command2); + _check_status_code_ok(response2); + assert.equal(response2.AccessKeyMetadata.length, 1); + assert.equal(response2.AccessKeyMetadata[0].UserName, username2); + assert.equal(response2.AccessKeyMetadata[0].AccessKeyId, access_key_id); + assert.equal(response2.AccessKeyMetadata[0].Status, ACCESS_KEY_STATUS_ENUM.ACTIVE); + }); + + mocha.it('get access key (last used)', async function() { + // Skipping for containerized noobaa + if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this + const input = { + AccessKeyId: access_key_id + }; + const command = new GetAccessKeyLastUsedCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.UserName, username2); + assert(response.AccessKeyLastUsed.LastUsedDate !== undefined); + assert(response.AccessKeyLastUsed.ServiceName !== undefined); + assert(response.AccessKeyLastUsed.Region !== undefined); + }); + + mocha.it('update access keys (active to inactive)', async function() { + const input = { + UserName: username2, + AccessKeyId: access_key_id, + Status: ACCESS_KEY_STATUS_ENUM.INACTIVE + }; + const command = new UpdateAccessKeyCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); - mocha.it('delete access keys', async function() { - const input = { - UserName: username2, - AccessKeyId: access_key_id - }; - const command = new DeleteAccessKeyCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); + // verify it using list access keys + const input2 = { + UserName: username2 + }; + const command2 = new ListAccessKeysCommand(input2); + const response2 = await iam_account.send(command2); + _check_status_code_ok(response2); + assert.equal(response2.AccessKeyMetadata.length, 1); + assert.equal(response2.AccessKeyMetadata[0].UserName, username2); + assert.equal(response2.AccessKeyMetadata[0].AccessKeyId, access_key_id); + assert.equal(response2.AccessKeyMetadata[0].Status, ACCESS_KEY_STATUS_ENUM.INACTIVE); + }); + + mocha.it('delete access keys', async function() { + const input = { + UserName: username2, + AccessKeyId: access_key_id + }; + const command = new DeleteAccessKeyCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); }); - }); - mocha.describe('IAM User Policy API', async function() { - if (is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this - const username3 = 'Kai'; - const policy_name = 'AllAccessPolicy'; - const iam_user_inline_policy_document = '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}'; + mocha.describe('IAM User Policy API', async function() { + if (is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this + const username3 = 'Kai'; + const policy_name = 'AllAccessPolicy'; + const iam_user_inline_policy_document = '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}'; - mocha.before(async () => { - // create a user - const input = { - UserName: username3 - }; - const command = new CreateUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - }); - - mocha.after(async () => { - // delete a user - const input = { - UserName: username3 - }; - const command = new DeleteUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - // note: if somehow the delete user policy would fail, then deleting the user would also fail - // (as we can delete a user only after its user policies were deleted) - }); + mocha.before(async () => { + // create a user + const input = { + UserName: username3 + }; + const command = new CreateUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); - mocha.it('list user policies for non existing user - should throw an error', async function() { - try { + mocha.after(async () => { + // delete a user const input = { - UserName: 'non-existing-user' + UserName: username3 + }; + const command = new DeleteUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + // note: if somehow the delete user policy would fail, then deleting the user would also fail + // (as we can delete a user only after its user policies were deleted) + }); + + mocha.it('list user policies for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListUserPoliciesCommand(input); + await iam_account.send(command); + assert.fail('list user policies for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list user policies for user - should be empty', async function() { + const input = { + UserName: username3 }; const command = new ListUserPoliciesCommand(input); - await iam_account.send(command); - assert.fail('list user policies for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.PolicyNames.length, 0); + }); - mocha.it('list user policies for user - should be empty', async function() { - const input = { - UserName: username3 - }; - const command = new ListUserPoliciesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.PolicyNames.length, 0); - }); - - mocha.it('put user policy', async function() { - const input = { - UserName: username3, - PolicyName: policy_name, - PolicyDocument: iam_user_inline_policy_document - }; - const command = new PutUserPolicyCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); + mocha.it('put user policy', async function() { + const input = { + UserName: username3, + PolicyName: policy_name, + PolicyDocument: iam_user_inline_policy_document + }; + const command = new PutUserPolicyCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); - // verify it using list user policies - const input2 = { - UserName: username3 + // verify it using list user policies + const input2 = { + UserName: username3 + }; + const command2 = new ListUserPoliciesCommand(input2); + const response2 = await iam_account.send(command2); + _check_status_code_ok(response2); + assert.equal(response2.PolicyNames.length, 1); + assert.equal(response2.PolicyNames[0], policy_name); + }); + + mocha.it('get user policy', async function() { + const input = { + UserName: username3, + PolicyName: policy_name + }; + const command = new GetUserPolicyCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.UserName, username3); + assert.equal(response.PolicyName, policy_name); + assert(response.PolicyDocument !== undefined); + const response_policy_document_json = JSON.parse(response.PolicyDocument); + assert.equal(response_policy_document_json.Version, '2012-10-17'); + assert(Array.isArray(response_policy_document_json.Statement)); + assert.deepEqual(response_policy_document_json.Statement[0], { "Effect": "Allow", "Action": "*", "Resource": "*" }); + }); + + mocha.it('delete user policy', async function() { + const input = { + UserName: username3, + PolicyName: policy_name + }; + const command = new DeleteUserPolicyCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); + }); + + mocha.describe('IAM User Tags API', async function() { + if (is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this + const username4 = 'Itsuki'; + const user_tag_1 = { + Key: "CostCenter", + Value: "12345" }; - const command2 = new ListUserPoliciesCommand(input2); - const response2 = await iam_account.send(command2); - _check_status_code_ok(response2); - assert.equal(response2.PolicyNames.length, 1); - assert.equal(response2.PolicyNames[0], policy_name); - }); - - mocha.it('get user policy', async function() { - const input = { - UserName: username3, - PolicyName: policy_name + const user_tag_2 = { + Key: "Department", + Value: "Accounting" }; - const command = new GetUserPolicyCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.UserName, username3); - assert.equal(response.PolicyName, policy_name); - assert(response.PolicyDocument !== undefined); - const response_policy_document_json = JSON.parse(response.PolicyDocument); - assert.equal(response_policy_document_json.Version, '2012-10-17'); - assert(Array.isArray(response_policy_document_json.Statement)); - assert.deepEqual(response_policy_document_json.Statement[0], {"Effect": "Allow", "Action": "*", "Resource": "*"}); - }); - mocha.it('delete user policy', async function() { - const input = { - UserName: username3, - PolicyName: policy_name - }; - const command = new DeleteUserPolicyCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - }); - }); + const user_tags = [user_tag_1, user_tag_2]; - mocha.describe('IAM User Tags API', async function() { - if (is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this - const username4 = 'Itsuki'; - const user_tag_1 = { - Key: "CostCenter", - Value: "12345" - }; - const user_tag_2 = { - Key: "Department", - Value: "Accounting" - }; - - const user_tags = [user_tag_1, user_tag_2]; - - mocha.before(async () => { - // create a user - const input = { - UserName: username4 - }; - const command = new CreateUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - }); + mocha.before(async () => { + // create a user + const input = { + UserName: username4 + }; + const command = new CreateUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); - mocha.after(async () => { - // delete a user - const input = { - UserName: username4 - }; - const command = new DeleteUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - }); + mocha.after(async () => { + // delete a user + const input = { + UserName: username4 + }; + const command = new DeleteUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); - mocha.it('list user tags - should be empty', async function() { - const input = { - UserName: username4, - }; - const command = new ListUserTagsCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Tags.length, 0); - }); + mocha.it('list user tags - should be empty', async function() { + const input = { + UserName: username4, + }; + const command = new ListUserTagsCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Tags.length, 0); + }); - mocha.it('tag user', async function() { - const input = { - UserName: username4, - Tags: user_tags - }; - const command = new TagUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); + mocha.it('tag user', async function() { + const input = { + UserName: username4, + Tags: user_tags + }; + const command = new TagUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); - // verify it using list user tags - const input2 = { - UserName: username4 - }; - const command2 = new ListUserTagsCommand(input2); - const response2 = await iam_account.send(command2); - _check_status_code_ok(response2); - assert.equal(response2.Tags.length, 2); - const sorted = arr => _.sortBy(arr, 'Key'); - assert.deepEqual(sorted(response2.Tags), sorted(user_tags)); + // verify it using list user tags + const input2 = { + UserName: username4 + }; + const command2 = new ListUserTagsCommand(input2); + const response2 = await iam_account.send(command2); + _check_status_code_ok(response2); + assert.equal(response2.Tags.length, 2); + const sorted = arr => _.sortBy(arr, 'Key'); + assert.deepEqual(sorted(response2.Tags), sorted(user_tags)); // verify it with get user (Tags are included in the User object) const input3 = { @@ -433,506 +439,623 @@ mocha.describe('IAM basic integration tests - happy path', async function() { _check_status_code_ok(response3); assert.equal(response3.User.Tags.length, 2); assert.deepEqual(sorted(response3.User.Tags), sorted(user_tags)); - }); - - mocha.it('untag user', async function() { - const input = { - UserName: username4, - TagKeys: [user_tag_2.Key] - }; - const command = new UntagUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - - // verify it using list user tags - const input2 = { - UserName: username4 - }; - const command2 = new ListUserTagsCommand(input2); - const response2 = await iam_account.send(command2); - _check_status_code_ok(response2); - assert.equal(response2.Tags.length, 1); - assert.deepEqual(response2.Tags, [user_tag_1]); - }); - }); - - mocha.describe('IAM other APIs (currently returns empty value)', async function() { - const username5 = 'Emi'; - const group_name = 'my_group'; - const role_name = 'my_role'; - const instance_profile_name = 'my_instance_profile_name'; - const policy_arn = 'arn:aws:iam::123456789012:policy/billing-access'; - - mocha.before(async () => { - // create a user - const input = { - UserName: username5 - }; - const command = new CreateUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - }); - - mocha.after(async () => { - // delete a user - const input = { - UserName: username5 - }; - const command = new DeleteUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - }); - - mocha.it('list groups for non existing user - should throw an error', async function() { - try { - const input = { - UserName: 'non-existing-user' - }; - const command = new ListGroupsForUserCommand(input); - await iam_account.send(command); - assert.fail('list groups for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list groups for user - should be empty', async function() { - const input = { - UserName: username5 - }; - const command = new ListGroupsForUserCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Groups.length, 0); - }); - - mocha.it('list account aliases - should be empty', async function() { - const input = {}; - const command = new ListAccountAliasesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.AccountAliases.length, 0); - }); - - mocha.it('list attached group policies - should be empty', async function() { - const input = { - GroupName: group_name - }; - const command = new ListAttachedGroupPoliciesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.AttachedPolicies.length, 0); - }); - - mocha.it('list attached role policies - should be empty', async function() { - const input = { - RoleName: role_name - }; - const command = new ListAttachedRolePoliciesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.AttachedPolicies.length, 0); - }); + }); - mocha.it('list attached user policies for non existing user - should throw an error', async function() { - try { + mocha.it('untag user', async function() { const input = { - UserName: 'non-existing-user' + UserName: username4, + TagKeys: [user_tag_2.Key] }; - const command = new ListAttachedUserPoliciesCommand(input); - await iam_account.send(command); - assert.fail('list attached user policies for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); + const command = new UntagUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); - mocha.it('list attached user policies for user - should be empty', async function() { - const input = { - UserName: username5 - }; - const command = new ListAttachedUserPoliciesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.AttachedPolicies.length, 0); - }); - - mocha.it('list entities for policy - should throw an error', async function() { - try { - const input = { - PolicyArn: 'arn:aws:iam::123456789012:policy/TestPolicy' + // verify it using list user tags + const input2 = { + UserName: username4 }; - const command = new ListEntitiesForPolicyCommand(input); - await iam_account.send(command); - assert.fail('list entities for policy - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list group policies - should be empty', async function() { - const input = { - GroupName: group_name - }; - const command = new ListGroupPoliciesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.PolicyNames.length, 0); - }); - - mocha.it('list groups - should be empty', async function() { - const input = {}; - const command = new ListGroupsCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Groups.length, 0); - }); - - mocha.it('list instance profiles - should be empty', async function() { - const input = {}; - const command = new ListInstanceProfilesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.InstanceProfiles.length, 0); - }); - - mocha.it('list instances profiles for role - should throw an error', async function() { - try { + const command2 = new ListUserTagsCommand(input2); + const response2 = await iam_account.send(command2); + _check_status_code_ok(response2); + assert.equal(response2.Tags.length, 1); + assert.deepEqual(response2.Tags, [user_tag_1]); + }); + }); + + mocha.describe('IAM other APIs (currently returns empty value)', async function() { + const username5 = 'Emi'; + const group_name = 'my_group'; + const role_name = 'my_role'; + const instance_profile_name = 'my_instance_profile_name'; + const policy_arn = 'arn:aws:iam::123456789012:policy/billing-access'; + + mocha.before(async () => { + // create a user const input = { - RoleName: role_name + UserName: username5 }; - const command = new ListInstanceProfilesForRoleCommand(input); - await iam_account.send(command); - assert.fail('list instances profiles for role - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list instances profiles tags - should throw an error', async function() { - try { - const input = { - InstanceProfileName: instance_profile_name - }; - const command = new ListInstanceProfileTagsCommand(input); - await iam_account.send(command); - assert.fail('list instances profiles tags - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); + const command = new CreateUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); - mocha.it('list MFA devices for non existing user - should throw an error', async function() { - try { + mocha.after(async () => { + // delete a user const input = { - UserName: 'non-existing-user' + UserName: username5 }; - const command = new ListMFADevicesCommand(input); - await iam_account.send(command); - assert.fail('list MFA devices for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list MFA devices for user - should be empty', async function() { - const input = { - UserName: username5 - }; - const command = new ListMFADevicesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.MFADevices.length, 0); - }); - - mocha.it('list MFA devices (no user parameter) - should be empty', async function() { - const input = {}; - const command = new ListMFADevicesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.MFADevices.length, 0); - }); - - mocha.it('list MFA device tags- should throw an error', async function() { - try { + const command = new DeleteUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); + + mocha.it('list groups for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListGroupsForUserCommand(input); + await iam_account.send(command); + assert.fail('list groups for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list groups for user - should be empty', async function() { const input = { - SerialNumber: 'arn:aws:iam::123456789012:mfa/alice' + UserName: username5 }; - const command = new ListMFADeviceTagsCommand(input); - await iam_account.send(command); - assert.fail('list MFA device tags - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list open ID connect providers - should be empty', async function() { - const input = {}; - const command = new ListOpenIDConnectProvidersCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.OpenIDConnectProviderList.length, 0); - }); - - mocha.it('list open ID connect tags- should throw an error', async function() { - try { + const command = new ListGroupsForUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Groups.length, 0); + }); + + mocha.it('list account aliases - should be empty', async function() { + const input = {}; + const command = new ListAccountAliasesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.AccountAliases.length, 0); + }); + + mocha.it('list attached group policies - should be empty', async function() { const input = { - OpenIDConnectProviderArn: 'arn:aws:iam::123456789012:mfa/alice' + GroupName: group_name }; - const command = new ListOpenIDConnectProviderTagsCommand(input); - await iam_account.send(command); - assert.fail('list open ID connect tags - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list policies - should be empty', async function() { - const input = {}; - const command = new ListPoliciesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Policies.length, 0); - }); + const command = new ListAttachedGroupPoliciesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.AttachedPolicies.length, 0); + }); - mocha.it('list policy tags - should throw an error', async function() { - try { + mocha.it('list attached role policies - should be empty', async function() { const input = { - PolicyArn: policy_arn + RoleName: role_name }; - const command = new ListPolicyTagsCommand(input); - await iam_account.send(command); - assert.fail('list policy tags - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list policy versions - should throw an error', async function() { - try { + const command = new ListAttachedRolePoliciesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.AttachedPolicies.length, 0); + }); + + mocha.it('list attached user policies for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListAttachedUserPoliciesCommand(input); + await iam_account.send(command); + assert.fail('list attached user policies for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list attached user policies for user - should be empty', async function() { const input = { - PolicyArn: policy_arn + UserName: username5 }; - const command = new ListPolicyVersionsCommand(input); - await iam_account.send(command); - assert.fail('list policy versions - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list roles - should be empty', async function() { - const input = {}; - const command = new ListRolesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Roles.length, 0); - }); - - mocha.it('list role tags - should throw an error', async function() { - try { + const command = new ListAttachedUserPoliciesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.AttachedPolicies.length, 0); + }); + + mocha.it('list entities for policy - should throw an error', async function() { + try { + const input = { + PolicyArn: 'arn:aws:iam::123456789012:policy/TestPolicy' + }; + const command = new ListEntitiesForPolicyCommand(input); + await iam_account.send(command); + assert.fail('list entities for policy - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list group policies - should be empty', async function() { const input = { - RoleName: role_name + GroupName: group_name }; - const command = new ListRoleTagsCommand(input); - await iam_account.send(command); - assert.fail('list role tags - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list SAML providers - should be empty', async function() { - const input = {}; - const command = new ListSAMLProvidersCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.SAMLProviderList.length, 0); - }); - - mocha.it('list server certificates - should be empty', async function() { - const input = {}; - const command = new ListServerCertificatesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.ServerCertificateMetadataList.length, 0); - }); - - mocha.it('list server certificate tags - should throw an error', async function() { - try { + const command = new ListGroupPoliciesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.PolicyNames.length, 0); + }); + + mocha.it('list groups - should be empty', async function() { + const input = {}; + const command = new ListGroupsCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Groups.length, 0); + }); + + mocha.it('list instance profiles - should be empty', async function() { + const input = {}; + const command = new ListInstanceProfilesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.InstanceProfiles.length, 0); + }); + + mocha.it('list instances profiles for role - should throw an error', async function() { + try { + const input = { + RoleName: role_name + }; + const command = new ListInstanceProfilesForRoleCommand(input); + await iam_account.send(command); + assert.fail('list instances profiles for role - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list instances profiles tags - should throw an error', async function() { + try { + const input = { + InstanceProfileName: instance_profile_name + }; + const command = new ListInstanceProfileTagsCommand(input); + await iam_account.send(command); + assert.fail('list instances profiles tags - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list MFA devices for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListMFADevicesCommand(input); + await iam_account.send(command); + assert.fail('list MFA devices for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list MFA devices for user - should be empty', async function() { const input = { - ServerCertificateName: 'ExampleCertificate' + UserName: username5 }; - const command = new ListServerCertificateTagsCommand(input); - await iam_account.send(command); - assert.fail('list server certificate tags - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); + const command = new ListMFADevicesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.MFADevices.length, 0); + }); - mocha.it('list service specific credentials for non existing user - should throw an error', async function() { - try { + mocha.it('list MFA devices (no user parameter) - should be empty', async function() { + const input = {}; + const command = new ListMFADevicesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.MFADevices.length, 0); + }); + + mocha.it('list MFA device tags- should throw an error', async function() { + try { + const input = { + SerialNumber: 'arn:aws:iam::123456789012:mfa/alice' + }; + const command = new ListMFADeviceTagsCommand(input); + await iam_account.send(command); + assert.fail('list MFA device tags - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list open ID connect providers - should be empty', async function() { + const input = {}; + const command = new ListOpenIDConnectProvidersCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.OpenIDConnectProviderList.length, 0); + }); + + mocha.it('list open ID connect tags- should throw an error', async function() { + try { + const input = { + OpenIDConnectProviderArn: 'arn:aws:iam::123456789012:mfa/alice' + }; + const command = new ListOpenIDConnectProviderTagsCommand(input); + await iam_account.send(command); + assert.fail('list open ID connect tags - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list policies - should be empty', async function() { + const input = {}; + const command = new ListPoliciesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Policies.length, 0); + }); + + mocha.it('list policy tags - should throw an error', async function() { + try { + const input = { + PolicyArn: policy_arn + }; + const command = new ListPolicyTagsCommand(input); + await iam_account.send(command); + assert.fail('list policy tags - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list policy versions - should throw an error', async function() { + try { + const input = { + PolicyArn: policy_arn + }; + const command = new ListPolicyVersionsCommand(input); + await iam_account.send(command); + assert.fail('list policy versions - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list roles - should be empty', async function() { + const input = {}; + const command = new ListRolesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Roles.length, 0); + }); + + mocha.it('list role tags - should throw an error', async function() { + try { + const input = { + RoleName: role_name + }; + const command = new ListRoleTagsCommand(input); + await iam_account.send(command); + assert.fail('list role tags - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list SAML providers - should be empty', async function() { + const input = {}; + const command = new ListSAMLProvidersCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.SAMLProviderList.length, 0); + }); + + mocha.it('list server certificates - should be empty', async function() { + const input = {}; + const command = new ListServerCertificatesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.ServerCertificateMetadataList.length, 0); + }); + + mocha.it('list server certificate tags - should throw an error', async function() { + try { + const input = { + ServerCertificateName: 'ExampleCertificate' + }; + const command = new ListServerCertificateTagsCommand(input); + await iam_account.send(command); + assert.fail('list server certificate tags - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list service specific credentials for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListServiceSpecificCredentialsCommand(input); + await iam_account.send(command); + assert.fail('list service specific credentials for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list service specific credentials for user - should be empty', async function() { const input = { - UserName: 'non-existing-user' + UserName: username5 }; const command = new ListServiceSpecificCredentialsCommand(input); - await iam_account.send(command); - assert.fail('list service specific credentials for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list service specific credentials for user - should be empty', async function() { - const input = { - UserName: username5 - }; - const command = new ListServiceSpecificCredentialsCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.ServiceSpecificCredentials.length, 0); - }); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.ServiceSpecificCredentials.length, 0); + }); - mocha.it('list service specific credentials (no user parameter) - should be empty', async function() { - const input = {}; - const command = new ListServiceSpecificCredentialsCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.ServiceSpecificCredentials.length, 0); - }); - - mocha.it('list signing certificates for non existing user - should throw an error', async function() { - try { + mocha.it('list service specific credentials (no user parameter) - should be empty', async function() { + const input = {}; + const command = new ListServiceSpecificCredentialsCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.ServiceSpecificCredentials.length, 0); + }); + + mocha.it('list signing certificates for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListSigningCertificatesCommand(input); + await iam_account.send(command); + assert.fail('list signing certificates for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list signing certificates for user - should be empty', async function() { const input = { - UserName: 'non-existing-user' + UserName: username5 }; const command = new ListSigningCertificatesCommand(input); - await iam_account.send(command); - assert.fail('list signing certificates for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list signing certificates for user - should be empty', async function() { - const input = { - UserName: username5 - }; - const command = new ListSigningCertificatesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Certificates.length, 0); - }); - - mocha.it('list signing certificates (no user parameter) - should be empty', async function() { - const input = {}; - const command = new ListSigningCertificatesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Certificates.length, 0); - }); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Certificates.length, 0); + }); - mocha.it('list SSH public keys for non existing user - should throw an error', async function() { - try { + mocha.it('list signing certificates (no user parameter) - should be empty', async function() { + const input = {}; + const command = new ListSigningCertificatesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Certificates.length, 0); + }); + + mocha.it('list SSH public keys for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListSSHPublicKeysCommand(input); + await iam_account.send(command); + assert.fail('list SSH public keys for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list SSH public keys for user - should be empty', async function() { const input = { - UserName: 'non-existing-user' + UserName: username5 }; const command = new ListSSHPublicKeysCommand(input); - await iam_account.send(command); - assert.fail('list SSH public keys for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.SSHPublicKeys.length, 0); + }); - mocha.it('list SSH public keys for user - should be empty', async function() { - const input = { - UserName: username5 - }; - const command = new ListSSHPublicKeysCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.SSHPublicKeys.length, 0); - }); - - mocha.it('list SSH public keys (no user parameter) - should be empty', async function() { - const input = {}; - const command = new ListSSHPublicKeysCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.SSHPublicKeys.length, 0); - }); - - mocha.it('list user policies for non existing user - should throw an error', async function() { - try { + mocha.it('list SSH public keys (no user parameter) - should be empty', async function() { + const input = {}; + const command = new ListSSHPublicKeysCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.SSHPublicKeys.length, 0); + }); + + mocha.it('list user policies for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListUserPoliciesCommand(input); + await iam_account.send(command); + assert.fail('list user policies for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list user policies for user - should be empty', async function() { const input = { - UserName: 'non-existing-user' + UserName: username5 }; const command = new ListUserPoliciesCommand(input); - await iam_account.send(command); - assert.fail('list user policies for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } - }); - - mocha.it('list user policies for user - should be empty', async function() { - const input = { - UserName: username5 - }; - const command = new ListUserPoliciesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.PolicyNames.length, 0); - }); - - mocha.it('list user tags for non existing user - should throw an error', async function() { - try { + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.PolicyNames.length, 0); + }); + + mocha.it('list user tags for non existing user - should throw an error', async function() { + try { + const input = { + UserName: 'non-existing-user' + }; + const command = new ListUserTagsCommand(input); + await iam_account.send(command); + assert.fail('list user tags for non existing user - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + + mocha.it('list user tags for user - should be empty', async function() { const input = { - UserName: 'non-existing-user' + UserName: username5 }; const command = new ListUserTagsCommand(input); - await iam_account.send(command); - assert.fail('list user tags for non existing user - should throw an error'); - } catch (err) { - const err_code = err.Error.Code; - assert.equal(err_code, IamError.NoSuchEntity.code); - } + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.Tags.length, 0); + }); + + mocha.it('list virtual MFA devices - should be empty', async function() { + const input = {}; + const command = new ListVirtualMFADevicesCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + assert.equal(response.VirtualMFADevices.length, 0); + }); }); + }); - mocha.it('list user tags for user - should be empty', async function() { - const input = { - UserName: username5 - }; - const command = new ListUserTagsCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.Tags.length, 0); - }); + mocha.describe('IAM advanced integration tests', async function() { + mocha.describe('IAM User API', async function() { + const username = 'Mateo'; + const username_lowercase = username.toLowerCase(); + const username_uppercase = username.toUpperCase(); + const username2 = 'Leonardo'; + + mocha.describe('IAM CreateUser API', async function() { + mocha.before(async () => { + await create_iam_user(username); + }); + + mocha.after(async () => { + await delete_iam_user(username); + }); + + mocha.it('create a user with username that already exists should fail', async function() { + try { + const input = { + UserName: username + }; + const command = new CreateUserCommand(input); + await iam_account.send(command); + assert.fail('create user with existing username - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.EntityAlreadyExists.code); + } + }); + + mocha.it('create a user with username that already exists (lower case) should fail', async function() { + try { + const input = { + UserName: username_lowercase + }; + const command = new CreateUserCommand(input); + await iam_account.send(command); + assert.fail('create user with existing username (lower case) - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.EntityAlreadyExists.code); + } + }); + + mocha.it('create a user with username that already exists (upper case) should fail', async function() { + try { + const input = { + UserName: username_uppercase + }; + const command = new CreateUserCommand(input); + await iam_account.send(command); + assert.fail('create user with existing username (upper case) - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.EntityAlreadyExists.code); + } + }); + }); + + mocha.describe('IAM UpdateUser API', async function() { + mocha.before(async () => { + // create 2 users + await create_iam_user(username); + await create_iam_user(username2); + }); + + mocha.after(async () => { + // delete 2 users + await delete_iam_user(username); + await delete_iam_user(username2); + }); + + mocha.it('update a user with same username', async function() { + const input = { + UserName: username2, + NewUserName: username2, + }; + const command = new UpdateUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + }); + + mocha.it('update a user with new username that already exists (lower case) should fail', async function() { + try { + const input = { + UserName: username2, + NewUserName: username_lowercase, + }; + const command = new UpdateUserCommand(input); + await iam_account.send(command); + assert.fail('update user with existing username (lower case) - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.EntityAlreadyExists.code); + } + }); + + mocha.it('update a user with new username that already exists (upper case) should fail', async function() { + try { + const input = { + UserName: username2, + NewUserName: username_uppercase, + }; + const command = new UpdateUserCommand(input); + await iam_account.send(command); + assert.fail('update user with existing username (upper case) - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.EntityAlreadyExists.code); + } + }); + }); - mocha.it('list virtual MFA devices - should be empty', async function() { - const input = {}; - const command = new ListVirtualMFADevicesCommand(input); - const response = await iam_account.send(command); - _check_status_code_ok(response); - assert.equal(response.VirtualMFADevices.length, 0); }); }); }); @@ -944,3 +1067,31 @@ mocha.describe('IAM basic integration tests - happy path', async function() { function _check_status_code_ok(response) { assert.equal(response.$metadata.httpStatusCode, 200); } + +/** + * Create an IAM user with the given username. + * use this function for before/after hooks to avoid code duplication + * @param {string} username_to_create + */ +async function create_iam_user(username_to_create) { + const input = { + UserName: username_to_create + }; + const command = new CreateUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); +} + +/** + * Delete an IAM user with the given username. + * use this function for before/after hooks to avoid code duplication + * @param {string} username_to_delete + */ +async function delete_iam_user(username_to_delete) { + const input = { + UserName: username_to_delete + }; + const command = new DeleteUserCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); +} diff --git a/src/util/account_util.js b/src/util/account_util.js index 3e8592fcc4..5efde1fc5d 100644 --- a/src/util/account_util.js +++ b/src/util/account_util.js @@ -352,8 +352,8 @@ function _check_if_requesting_account_is_root_account(action, requesting_account } } -function _check_username_already_exists(action, email, username) { - const account = system_store.get_account_by_email(email); +function _check_username_already_exists(action, email_wrapped, username) { + const account = _check_if_account_exists_by_email(email_wrapped); if (account) { dbg.error(`AccountSpaceNB.${action} username already exists`, username); const message_with_details = `User with name ${username} already exists.`; @@ -361,6 +361,13 @@ function _check_username_already_exists(action, email, username) { } } +function _check_if_account_exists_by_email(email_wrapped) { + const accounts = system_store.data.accounts || []; + return accounts.find(account => + account.email.unwrap().toLowerCase() === email_wrapped.unwrap().toLowerCase() + ); +} + function _check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account) { const is_requested_account_root_account = _check_root_account(requested_account); dbg.log1(`AccountSpaceNB.${action} requested_account ID: ${requested_account._id} name: ${requested_account.name}`, From a35ad0d452ae65490ed899f083d448c30b94a5ba Mon Sep 17 00:00:00 2001 From: shirady <57721533+shirady@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:19:09 +0200 Subject: [PATCH 2/4] CR changes Signed-off-by: shirady <57721533+shirady@users.noreply.github.com> --- src/server/system_services/account_server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/system_services/account_server.js b/src/server/system_services/account_server.js index 2dd4fa668a..d9b5cd08d7 100644 --- a/src/server/system_services/account_server.js +++ b/src/server/system_services/account_server.js @@ -1239,10 +1239,10 @@ async function update_user(req) { const is_username_update = req.rpc_params.new_username !== undefined && req.rpc_params.new_username !== req.rpc_params.username; if (is_username_update) { - const email_new_username_wrapped = account_util.get_account_name_from_username( + const new_email_wrapped = account_util.get_account_name_from_username( req.rpc_params.new_username, requesting_account._id.toString()); - account_util._check_username_already_exists(action, email_new_username_wrapped, req.rpc_params.new_username); + account_util._check_username_already_exists(action, new_email_wrapped, req.rpc_params.new_username); } account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); From c2075aad60831c7c0ded1cc5b157e5f12da1b23b Mon Sep 17 00:00:00 2001 From: shirady <57721533+shirady@users.noreply.github.com> Date: Tue, 2 Dec 2025 10:57:07 +0200 Subject: [PATCH 3/4] change the saved fields - email and accoount - email with username lowercase - name as username as is Signed-off-by: shirady <57721533+shirady@users.noreply.github.com> --- src/api/account_api.js | 3 -- src/sdk/accountspace_nb.js | 7 ++- src/server/system_services/account_server.js | 51 ++++++++++---------- src/server/system_services/bucket_server.js | 2 +- src/util/account_util.js | 22 +++------ 5 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/api/account_api.js b/src/api/account_api.js index 4ba1bc785f..4e4b19baba 100644 --- a/src/api/account_api.js +++ b/src/api/account_api.js @@ -676,9 +676,6 @@ module.exports = { iam_path: { type: 'string', }, - username: { - type: 'string', - }, } }, reply: { diff --git a/src/sdk/accountspace_nb.js b/src/sdk/accountspace_nb.js index 20122ae7b4..5a289cdcdc 100644 --- a/src/sdk/accountspace_nb.js +++ b/src/sdk/accountspace_nb.js @@ -37,15 +37,14 @@ class AccountSpaceNB { async create_user(params, account_sdk) { const requesting_account = system_store.get_account_by_email(account_sdk.requesting_account.email); - const account_name = account_util.get_account_name_from_username(params.username, requesting_account._id.toString()); + const account_email_wrapped = account_util.get_account_email_from_username(params.username, requesting_account._id.toString()); const req = { - name: account_name, - email: account_name, + name: params.username, // actual username saved + email: account_email_wrapped, // unique email generated from username lowercase and root account id has_login: false, s3_access: true, allow_bucket_creation: true, owner: requesting_account._id.toString(), - username: params.username, iam_path: params.iam_path, roles: ['admin'], // TODO: default_resource remove diff --git a/src/server/system_services/account_server.js b/src/server/system_services/account_server.js index d9b5cd08d7..f76ae1eb21 100644 --- a/src/server/system_services/account_server.js +++ b/src/server/system_services/account_server.js @@ -40,12 +40,12 @@ async function create_account(req) { let iam_arn; if (req.rpc_params.owner) { const action = IAM_ACTIONS.CREATE_USER; + const username = req.account.name.unwrap(); account_util._check_if_requesting_account_is_root_account(action, req.account, - { username: req.rpc_params.username, path: req.rpc_params.iam_path }); - account_util._check_username_already_exists(action, req.rpc_params.email, - req.rpc_params.username); - iam_arn = iam_utils.create_arn_for_user(req.account._id.toString(), req.rpc_params.username, - req.rpc_params.iam_path || IAM_DEFAULT_PATH); + { username: username, path: req.rpc_params.iam_path }); + account_util._check_username_already_exists(action, req.rpc_params.email, username); + iam_arn = iam_utils.create_arn_for_user(req.account._id.toString(), username, + req.rpc_params.iam_path || IAM_DEFAULT_PATH); } else { account_util.validate_create_account_permissions(req); account_util.validate_create_account_params(req); @@ -1207,7 +1207,7 @@ async function get_user(req) { const action = IAM_ACTIONS.GET_USER; const requesting_account = req.account; const requested_account = account_util.validate_and_return_requested_account(req.rpc_params, action, requesting_account); - const username = account_util.get_iam_username(req.rpc_params.username || requested_account.name.unwrap()); + const username = requested_account.name.unwrap(); const iam_arn = iam_utils.create_arn_for_user(requesting_account._id.toString(), username, requested_account.iam_path || IAM_DEFAULT_PATH); const tags = account_util.get_sorted_list_tags_for_user(requested_account.tagging); @@ -1228,31 +1228,32 @@ async function update_user(req) { const action = IAM_ACTIONS.UPDATE_USER; const requesting_account = system_store.get_account_by_email(req.account.email); - const old_username = account_util.get_account_name_from_username(req.rpc_params.username, requesting_account._id.toString()); + const old_account_email_wrapped = account_util.get_account_email_from_username( + req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username, iam_path: req.rpc_params.new_iam_path }); - account_util._check_if_account_exists(action, old_username); - const requested_account = system_store.get_account_by_email(old_username); + account_util._check_if_account_exists(action, old_account_email_wrapped); + const requested_account = system_store.get_account_by_email(old_account_email_wrapped); let iam_path = requested_account.iam_path; - let user_name = account_util.get_iam_username(requested_account.name.unwrap()); + let user_name = req.rpc_params.username; // Change to complete user name const is_username_update = req.rpc_params.new_username !== undefined && req.rpc_params.new_username !== req.rpc_params.username; if (is_username_update) { - const new_email_wrapped = account_util.get_account_name_from_username( + const new_account_email_wrapped = account_util.get_account_email_from_username( req.rpc_params.new_username, requesting_account._id.toString()); - account_util._check_username_already_exists(action, new_email_wrapped, req.rpc_params.new_username); + account_util._check_username_already_exists(action, new_account_email_wrapped, req.rpc_params.new_username); } account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); if (req.rpc_params.new_iam_path) iam_path = req.rpc_params.new_iam_path; if (req.rpc_params.new_username) user_name = req.rpc_params.new_username; const iam_arn = iam_utils.create_arn_for_user(requesting_account._id.toString(), user_name, iam_path); - const new_account_name = account_util.get_account_name_from_username(user_name, requesting_account._id.toString()); + const new_account_email_wrapped = account_util.get_account_email_from_username(user_name, requesting_account._id.toString()); const updates = { - name: new_account_name, - email: new_account_name, + name: user_name, + email: new_account_email_wrapped, iam_path: iam_path, }; await system_store.make_changes({ @@ -1276,10 +1277,10 @@ async function delete_user(req) { const action = IAM_ACTIONS.DELETE_USER; const requesting_account = req.account; - const username = account_util.get_account_name_from_username(req.rpc_params.username, requesting_account._id.toString()); + const account_email_wrapped = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username }); - account_util._check_if_account_exists(action, username); - const requested_account = system_store.get_account_by_email(username); + account_util._check_if_account_exists(action, account_email_wrapped); + const requested_account = system_store.get_account_by_email(account_email_wrapped); account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); account_util._check_if_user_does_not_have_resources_before_deletion(action, requested_account); @@ -1431,12 +1432,12 @@ async function delete_access_key(req) { async function tag_user(req) { const action = IAM_ACTIONS.TAG_USER; const requesting_account = req.account; - const username = account_util.get_account_name_from_username(req.rpc_params.username, requesting_account._id.toString()); + const account_email_wrapped = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username }); - account_util._check_if_account_exists(action, username); + account_util._check_if_account_exists(action, account_email_wrapped); - const requested_account = system_store.get_account_by_email(username); + const requested_account = system_store.get_account_by_email(account_email_wrapped); account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); @@ -1473,12 +1474,12 @@ async function tag_user(req) { async function untag_user(req) { const action = IAM_ACTIONS.UNTAG_USER; const requesting_account = req.account; - const username = account_util.get_account_name_from_username(req.rpc_params.username, requesting_account._id.toString()); + const account_email_wrapped = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username }); - account_util._check_if_account_exists(action, username); + account_util._check_if_account_exists(action, account_email_wrapped); - const requested_account = system_store.get_account_by_email(username); + const requested_account = system_store.get_account_by_email(account_email_wrapped); account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); @@ -1502,7 +1503,7 @@ async function untag_user(req) { async function list_user_tags(req) { const action = IAM_ACTIONS.LIST_USER_TAGS; const requesting_account = req.account; - const username = account_util.get_account_name_from_username(req.rpc_params.username, requesting_account._id.toString()); + const username = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username }); account_util._check_if_account_exists(action, username); diff --git a/src/server/system_services/bucket_server.js b/src/server/system_services/bucket_server.js index f5c4a6ff53..491592dc83 100644 --- a/src/server/system_services/bucket_server.js +++ b/src/server/system_services/bucket_server.js @@ -539,7 +539,7 @@ async function account_exists_by_principal_arn(principal_as_string) { } else if (arn_sufix && arn_sufix.startsWith(user_sufix)) { const arn_path_parts = principal_as_string.split('/'); const iam_user_name = arn_path_parts[arn_path_parts.length - 1].trim(); - return system_store.get_account_by_email(new SensitiveString(`${iam_user_name}:${account_id}`)); + return system_store.get_account_by_email(new SensitiveString(`${iam_user_name.toLowerCase()}:${account_id}`)); } //else { // wrong principal ARN should not return anything. //} diff --git a/src/util/account_util.js b/src/util/account_util.js index 5efde1fc5d..85f98217f8 100644 --- a/src/util/account_util.js +++ b/src/util/account_util.js @@ -315,10 +315,11 @@ function validate_assume_role_policy(policy) { } } -// User name is first part is user provided name, and second part -// is root account id, This will make the user name uniq accross system. -function get_account_name_from_username(username, requesting_account_id) { - return new SensitiveString(`${username}:${requesting_account_id}`); +// To make the user name unique across system: +// - first part is user name in lower case +// - second part is root account id +function get_account_email_from_username(username, requesting_account_id) { + return new SensitiveString(`${username.toLowerCase()}:${requesting_account_id}`); } function _check_if_account_exists(action, email_wrapped) { @@ -353,7 +354,7 @@ function _check_if_requesting_account_is_root_account(action, requesting_account } function _check_username_already_exists(action, email_wrapped, username) { - const account = _check_if_account_exists_by_email(email_wrapped); + const account = system_store.get_account_by_email(email_wrapped); if (account) { dbg.error(`AccountSpaceNB.${action} username already exists`, username); const message_with_details = `User with name ${username} already exists.`; @@ -361,13 +362,6 @@ function _check_username_already_exists(action, email_wrapped, username) { } } -function _check_if_account_exists_by_email(email_wrapped) { - const accounts = system_store.data.accounts || []; - return accounts.find(account => - account.email.unwrap().toLowerCase() === email_wrapped.unwrap().toLowerCase() - ); -} - function _check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account) { const is_requested_account_root_account = _check_root_account(requested_account); dbg.log1(`AccountSpaceNB.${action} requested_account ID: ${requested_account._id} name: ${requested_account.name}`, @@ -714,7 +708,7 @@ function validate_and_return_requested_account(params, action, requesting_accoun } } else { _check_if_requesting_account_is_root_account(action, requesting_account, { username: params.username }); - const account_email = get_account_name_from_username(params.username, requesting_account._id.toString()); + const account_email = get_account_email_from_username(params.username, requesting_account._id.toString()); _check_if_account_exists(action, account_email); requested_account = system_store.get_account_by_email(account_email); _check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); @@ -754,7 +748,7 @@ function get_sorted_list_tags_for_user(user_tagging) { exports.delete_account = delete_account; exports.create_account = create_account; exports.generate_account_keys = generate_account_keys; -exports.get_account_name_from_username = get_account_name_from_username; +exports.get_account_email_from_username = get_account_email_from_username; exports.get_non_updating_access_key = get_non_updating_access_key; exports._check_if_requesting_account_is_root_account = _check_if_requesting_account_is_root_account; exports._check_username_already_exists = _check_username_already_exists; From ce10f4a5c0c13f7844e76f202153f3a5a9939175 Mon Sep 17 00:00:00 2001 From: shirady <57721533+shirady@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:24:19 +0200 Subject: [PATCH 4/4] continue with the name and email change Signed-off-by: shirady <57721533+shirady@users.noreply.github.com> --- src/endpoint/iam/iam_utils.js | 7 +----- src/server/system_services/account_server.js | 26 ++++++++++---------- src/util/account_util.js | 17 ++++++------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/endpoint/iam/iam_utils.js b/src/endpoint/iam/iam_utils.js index 4596cceeaa..c7d5422b78 100644 --- a/src/endpoint/iam/iam_utils.js +++ b/src/endpoint/iam/iam_utils.js @@ -62,10 +62,6 @@ function check_iam_path_was_set(iam_path) { return iam_path && iam_path !== iam_constants.IAM_DEFAULT_PATH; } -function get_iam_username(requested_account_name) { - return requested_account_name.split(iam_constants.IAM_SPLIT_CHARACTERS)[0]; -} - /** * _create_detailed_message_for_iam_user_access_in_s3 returns a detailed message with details needed for user who * tried to perform S3 operation @@ -77,7 +73,7 @@ function get_iam_username(requested_account_name) { function _create_detailed_message_for_iam_user_access_in_s3(user_account, method, resource_arn) { const owner_account_id = get_owner_account_id(user_account); const arn_for_requesting_account = create_arn_for_user(owner_account_id, - get_iam_username(user_account.name.unwrap()), user_account.iam_path); + user_account.name.unwrap(), user_account.iam_path); const full_action_name = Array.isArray(method) && method.length > 1 ? method[1] : method; // special case for get_object_attributes const message_start = `User: ${arn_for_requesting_account} is not authorized to perform: ${full_action_name} `; @@ -863,7 +859,6 @@ exports.create_arn_for_user = create_arn_for_user; exports.create_arn_for_root = create_arn_for_root; exports.get_action_message_title = get_action_message_title; exports.check_iam_path_was_set = check_iam_path_was_set; -exports.get_iam_username = get_iam_username; exports.parse_max_items = parse_max_items; exports.validate_params = validate_params; exports.validate_iam_path = validate_iam_path; diff --git a/src/server/system_services/account_server.js b/src/server/system_services/account_server.js index f76ae1eb21..ba4bb3a577 100644 --- a/src/server/system_services/account_server.js +++ b/src/server/system_services/account_server.js @@ -40,7 +40,7 @@ async function create_account(req) { let iam_arn; if (req.rpc_params.owner) { const action = IAM_ACTIONS.CREATE_USER; - const username = req.account.name.unwrap(); + const username = req.rpc_params.name.unwrap(); account_util._check_if_requesting_account_is_root_account(action, req.account, { username: username, path: req.rpc_params.iam_path }); account_util._check_username_already_exists(action, req.rpc_params.email, username); @@ -1232,7 +1232,7 @@ async function update_user(req) { req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username, iam_path: req.rpc_params.new_iam_path }); - account_util._check_if_account_exists(action, old_account_email_wrapped); + account_util._check_if_account_exists(action, old_account_email_wrapped, req.rpc_params.username); const requested_account = system_store.get_account_by_email(old_account_email_wrapped); let iam_path = requested_account.iam_path; let user_name = req.rpc_params.username; @@ -1279,7 +1279,7 @@ async function delete_user(req) { const requesting_account = req.account; const account_email_wrapped = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username }); - account_util._check_if_account_exists(action, account_email_wrapped); + account_util._check_if_account_exists(action, account_email_wrapped, req.rpc_params.username); const requested_account = system_store.get_account_by_email(account_email_wrapped); account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); @@ -1302,7 +1302,7 @@ async function list_users(req) { return owner_account_id === requesting_account._id.toString(); }); let members = _.map(requesting_account_iam_users, function(iam_user) { - const iam_username = account_util.get_iam_username(iam_user.name.unwrap()); + const iam_username = iam_user.name.unwrap(); const iam_path = iam_user.iam_path || IAM_DEFAULT_PATH; let member; // Check the iam_path_prefix and add only those satify the iam_path if exists @@ -1337,12 +1337,12 @@ async function create_access_key(req) { iam_access_key = await account_util.generate_account_keys(account_req); } catch (err) { dbg.error(`AccountSpaceNB.${action} error: `, err); - const message_with_details = `Create accesskey failed for the user with name ${account_util.get_iam_username(requested_account.email.unwrap())}.`; + const message_with_details = `Create accesskey failed for the user with name ${requested_account.name.unwrap()}.`; throw new RpcError('INTERNAL_FAILURE', message_with_details, 500); } return { - username: account_util.get_iam_username(requested_account.name.unwrap()), + username: requested_account.name.unwrap(), access_key: iam_access_key.access_key.unwrap(), create_date: iam_access_key.creation_date, status: ACCESS_KEY_STATUS_ENUM.ACTIVE, @@ -1394,16 +1394,16 @@ async function update_access_key(req) { async function get_access_key_last_used(req) { const action = IAM_ACTIONS.GET_ACCESS_KEY_LAST_USED; const requesting_account = req.account; + const requested_account = account_util.validate_and_return_requested_account(req.rpc_params, action, requesting_account); const dummy_region = 'us-west-2'; const dummy_service_name = 's3'; account_util._check_access_key_belongs_to_account(action, requesting_account, req.rpc_params.access_key); // TODO: Need to return valid last_used_date date, Low priority. - const username = account_util._returned_username(requesting_account, requesting_account.name.unwrap(), false); return { region: dummy_region, // GAP last_used_date: Date.now(), // GAP service_name: dummy_service_name, // GAP - username: username ? account_util.get_iam_username(username) : undefined, + username: requested_account.name.unwrap(), }; } @@ -1435,7 +1435,7 @@ async function tag_user(req) { const account_email_wrapped = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username }); - account_util._check_if_account_exists(action, account_email_wrapped); + account_util._check_if_account_exists(action, account_email_wrapped, req.rpc_params.username); const requested_account = system_store.get_account_by_email(account_email_wrapped); account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); @@ -1477,7 +1477,7 @@ async function untag_user(req) { const account_email_wrapped = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username }); - account_util._check_if_account_exists(action, account_email_wrapped); + account_util._check_if_account_exists(action, account_email_wrapped, req.rpc_params.username); const requested_account = system_store.get_account_by_email(account_email_wrapped); account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); @@ -1503,12 +1503,12 @@ async function untag_user(req) { async function list_user_tags(req) { const action = IAM_ACTIONS.LIST_USER_TAGS; const requesting_account = req.account; - const username = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); + const account_email_wrapped = account_util.get_account_email_from_username(req.rpc_params.username, requesting_account._id.toString()); account_util._check_if_requesting_account_is_root_account(action, requesting_account, { username: req.rpc_params.username }); - account_util._check_if_account_exists(action, username); + account_util._check_if_account_exists(action, account_email_wrapped, req.rpc_params.username); - const requested_account = system_store.get_account_by_email(username); + const requested_account = system_store.get_account_by_email(account_email_wrapped); account_util._check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); account_util._check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); diff --git a/src/util/account_util.js b/src/util/account_util.js index 85f98217f8..705f6e86c9 100644 --- a/src/util/account_util.js +++ b/src/util/account_util.js @@ -12,7 +12,7 @@ const auth_server = require('..//server/common_services/auth_server'); const system_store = require('..//server/system_services/system_store').get_instance(); const pool_server = require('../server/system_services/pool_server'); const { OP_NAME_TO_ACTION } = require('../endpoint/sts/sts_rest'); -const { create_arn_for_user, get_action_message_title, get_iam_username, get_owner_account_id } = require('../endpoint/iam/iam_utils'); +const { create_arn_for_user, get_action_message_title, get_owner_account_id } = require('../endpoint/iam/iam_utils'); const { IAM_ACTIONS, MAX_NUMBER_OF_ACCESS_KEYS, IAM_DEFAULT_PATH, ACCESS_KEY_STATUS_ENUM, IAM_ACTIONS_USER_INLINE_POLICY, AWS_LIMIT_CHARS_USER_INlINE_POLICY } = require('../endpoint/iam/iam_constants'); @@ -322,11 +322,11 @@ function get_account_email_from_username(username, requesting_account_id) { return new SensitiveString(`${username.toLowerCase()}:${requesting_account_id}`); } -function _check_if_account_exists(action, email_wrapped) { +function _check_if_account_exists(action, email_wrapped, username) { const account = system_store.get_account_by_email(email_wrapped); if (!account) { dbg.error(`AccountSpaceNB.${action} username does not exist`, email_wrapped.unwrap()); - const message_with_details = `The user with name ${get_iam_username(email_wrapped.unwrap())} cannot be found.`; + const message_with_details = `The user with name ${username} cannot be found.`; throw new RpcError('NO_SUCH_ENTITY', message_with_details); } } @@ -385,7 +385,7 @@ function _check_if_requested_is_owned_by_root_account(action, requesting_account const username = requested_account.name instanceof SensitiveString ? requested_account.name.unwrap() : requested_account.name; dbg.error(`AccountSpaceNB.${action} requested account is not owned by root account`, username); - const message_with_details = `The user with name ${get_iam_username(username)} cannot be found.`; + const message_with_details = `The user with name ${username} cannot be found.`; throw new RpcError('NO_SUCH_ENTITY', message_with_details); } } @@ -421,7 +421,7 @@ function _throw_error_perform_action_on_another_root_account(action, requesting_ // (cannot perform action on users from another root accounts) dbg.error(`AccountSpaceNB.${action} root account of requested account is different than requesting root account`, requesting_account._id.toString(), username); - const message_with_details = `The user with name ${get_iam_username(username)} cannot be found.`; + const message_with_details = `The user with name ${username} cannot be found.`; throw new RpcError('NO_SUCH_ENTITY', message_with_details); } @@ -440,7 +440,7 @@ function _throw_error_no_such_entity_policy(action, policy_name) { function _throw_access_denied_error(action, requesting_account, details, entity) { const full_action_name = get_action_message_title(action); const account_id_for_arn = _get_account_owner_id_for_arn(requesting_account).toString(); - const arn_for_requesting_account = create_arn_for_user(account_id_for_arn, get_iam_username(requesting_account.name.unwrap()), + const arn_for_requesting_account = create_arn_for_user(account_id_for_arn, requesting_account.name.unwrap(), requesting_account.iam_path || IAM_DEFAULT_PATH); const basic_message = `User: ${arn_for_requesting_account} is not authorized to perform:` + `${full_action_name} on resource: `; @@ -551,7 +551,7 @@ function _list_access_keys_from_account(requesting_account, account, on_itself) } for (const access_key of account.access_keys) { const member = { - username: get_iam_username(_returned_username(requesting_account, account.name, on_itself)), + username: _returned_username(requesting_account, account.name.unwrap(), on_itself), access_key: access_key.access_key instanceof SensitiveString ? access_key.access_key.unwrap() : access_key.access_key, status: _get_access_key_status(access_key.deactivated), // Creation date only for IAM users and new account(4.20 and above), @@ -709,7 +709,7 @@ function validate_and_return_requested_account(params, action, requesting_accoun } else { _check_if_requesting_account_is_root_account(action, requesting_account, { username: params.username }); const account_email = get_account_email_from_username(params.username, requesting_account._id.toString()); - _check_if_account_exists(action, account_email); + _check_if_account_exists(action, account_email, params.username); requested_account = system_store.get_account_by_email(account_email); _check_if_requested_account_is_root_account_or_IAM_user(action, requesting_account, requested_account); _check_if_requested_is_owned_by_root_account(action, requesting_account, requested_account); @@ -768,7 +768,6 @@ exports._check_user_policy_exists = _check_user_policy_exists; exports._check_if_user_does_not_have_resources_before_deletion = _check_if_user_does_not_have_resources_before_deletion; exports._check_total_policy_size = _check_total_policy_size; exports.validate_and_return_requested_account = validate_and_return_requested_account; -exports.get_iam_username = get_iam_username; exports.return_list_member = return_list_member; exports.get_owner_account_id = get_owner_account_id; exports.get_sorted_list_tags_for_user = get_sorted_list_tags_for_user;