diff --git a/src/server/system_services/account_server.js b/src/server/system_services/account_server.js index ba4bb3a577..e497b073f1 100644 --- a/src/server/system_services/account_server.js +++ b/src/server/system_services/account_server.js @@ -1394,10 +1394,11 @@ 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 access_key_id = req.rpc_params.access_key; + const requested_account = account_util._check_if_iam_user_belongs_to_account_owner_by_access_key( + action, requesting_account, access_key_id); 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. return { region: dummy_region, // GAP 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 e767f5f891..e17880842f 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 @@ -217,8 +217,6 @@ mocha.describe('IAM integration tests', async function() { }); 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 }; @@ -1057,6 +1055,39 @@ mocha.describe('IAM integration tests', async function() { }); }); + + mocha.describe('IAM Access Keys API', async function() { + const username2 = 'Alejandro'; + let access_key_id; + + mocha.describe('IAM GetAccessKeyLastUsed API', async function() { + mocha.before(async () => { + await create_iam_user(username2); + const res = await create_access_key_iam_user(username2); + access_key_id = res.access_key_id; + }); + + mocha.after(async () => { + await delete_access_key_iam_user(access_key_id, username2); + await delete_iam_user(username2); + }); + + mocha.it('get access key last used with invalid access key ID should fail', async function() { + const access_key_id_invalid = access_key_id + '0'; + try { + const input = { + AccessKeyId: access_key_id_invalid + }; + const command = new GetAccessKeyLastUsedCommand(input); + await iam_account.send(command); + assert.fail('get access key last used with invalid access key ID - should throw an error'); + } catch (err) { + const err_code = err.Error.Code; + assert.equal(err_code, IamError.NoSuchEntity.code); + } + }); + }); + }); }); }); @@ -1095,3 +1126,36 @@ async function delete_iam_user(username_to_delete) { const response = await iam_account.send(command); _check_status_code_ok(response); } + + +/** + * Create an IAM user's access key with the given username. + * use this function for before/after hooks to avoid code duplication + * @param {string} username + */ +async function create_access_key_iam_user(username) { + const input = { + UserName: username + }; + const command = new CreateAccessKeyCommand(input); + const response = await iam_account.send(command); + _check_status_code_ok(response); + return { access_key_id: response.AccessKey.AccessKeyId, secret_access_key: response.AccessKey.SecretAccessKey }; +} + + +/** + * Delete an IAM user's access key with the given access key ID and username. + * use this function for before/after hooks to avoid code duplication + * @param {string} access_key_to_delete + * @param {string} username + */ +async function delete_access_key_iam_user(access_key_to_delete, username) { + const input = { + UserName: username, + AccessKeyId: access_key_to_delete + }; + const command = new DeleteAccessKeyCommand(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 aec0a0e05c..7422df0b7e 100644 --- a/src/util/account_util.js +++ b/src/util/account_util.js @@ -479,6 +479,29 @@ function _check_number_of_access_key_array(action, requested_account) { } } +function _check_if_iam_user_belongs_to_account_owner_by_access_key(action, requesting_account, access_key_id) { + const access_key_id_wrapped = new SensitiveString(access_key_id); + const requested_account = system_store.get_account_by_access_key(access_key_id_wrapped); + if (!requested_account) { + _throw_error_no_such_entity_access_key(action, access_key_id); + } + const is_account_on_himself = String(requesting_account._id) === String(requested_account._id); + if (is_account_on_himself) { + // didn't block an account from running action on itself since it not editing anything here + return requested_account; + } else { + const is_root_account = _check_root_account(requesting_account); + if (!is_root_account) { + _throw_access_denied_error(action, requesting_account, { access_key: access_key_id }, "ACCESS_KEY"); + } + const is_requested_account_owned_by_root_account = _check_root_account_owns_user(requesting_account, requested_account); + if (!is_requested_account_owned_by_root_account) { + _throw_error_no_such_entity_access_key(action, access_key_id); + } + return requested_account; + } +} + function _check_access_key_belongs_to_account(action, requested_account, access_key_id) { const is_access_key_belongs_to_account = _check_specific_access_key_exists(requested_account.access_keys, access_key_id); if (!is_access_key_belongs_to_account) { @@ -777,3 +800,4 @@ exports.validate_and_return_requested_account = validate_and_return_requested_ac 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; +exports._check_if_iam_user_belongs_to_account_owner_by_access_key = _check_if_iam_user_belongs_to_account_owner_by_access_key;