Skip to content

Commit 3bcd672

Browse files
authored
Merge pull request #9263 from shirady/iam-user-policy-api
IAM | IAM User Policy API (Skeleton Only)
2 parents 26eeb9b + 3d25d48 commit 3bcd672

17 files changed

+704
-20
lines changed

src/endpoint/iam/iam_constants.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ const IAM_ACTIONS = Object.freeze({
1212
GET_ACCESS_KEY_LAST_USED: 'get_access_key_last_used',
1313
UPDATE_ACCESS_KEY: 'update_access_key',
1414
DELETE_ACCESS_KEY: 'delete_access_key',
15-
LIST_ACCESS_KEYS: 'list_access_keys'
15+
LIST_ACCESS_KEYS: 'list_access_keys',
16+
PUT_USER_POLICY: 'put_user_policy',
17+
GET_USER_POLICY: 'get_user_policy',
18+
DELETE_USER_POLICY: 'delete_user_policy',
19+
LIST_USER_POLICIES: 'list_user_policies',
1620
});
1721

1822
// key: action - the function name on accountspace_fs (snake case style)
@@ -29,6 +33,10 @@ const ACTION_MESSAGE_TITLE_MAP = Object.freeze({
2933
'update_access_key': 'UpdateAccessKey',
3034
'delete_access_key': 'DeleteAccessKey',
3135
'list_access_keys': 'ListAccessKeys',
36+
'put_user_policy': 'PutUserPolicy',
37+
'get_user_policy': 'GetUserPolicy',
38+
'delete_user_policy': 'DeleteUserPolicy',
39+
'list_user_policies': 'ListUserPolicies',
3240
});
3341

3442
const ACCESS_KEY_STATUS_ENUM = Object.freeze({
@@ -56,6 +64,8 @@ const IAM_PARAMETER_NAME = Object.freeze({
5664
IAM_PATH_PREFIX: 'PathPrefix',
5765
USERNAME: 'UserName',
5866
NEW_USERNAME: 'NewUserName',
67+
POLICY_NAME: 'PolicyName',
68+
POLICY_DOCUMENT: 'PolicyDocument',
5969
});
6070

6171
const IAM_SPLIT_CHARACTERS = ':';

src/endpoint/iam/iam_errors.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ IamError.NotImplemented = Object.freeze({
140140
// UpdateAccessKey errors https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateAccessKey.html
141141
// DeleteAccessKey errors https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteAccessKey.html
142142
// ListAccessKeys errors https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAccessKeys.html
143+
// user policy
144+
// PutUserPolicy errors https://docs.aws.amazon.com/IAM/latest/APIReference/API_PutUserPolicy.html
145+
// GetUserPolicy errors https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetUserPolicy.html
146+
// DeleteUserPolicy errors https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteUserPolicy.html
147+
// ListUserPolicies errors https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListUserPolicies.html
143148
IamError.ConcurrentModification = Object.freeze({
144149
code: 'ConcurrentModification',
145150
message: 'The request was rejected because multiple requests to change this object were submitted simultaneously. Wait a few minutes and submit your request again.',
@@ -188,6 +193,13 @@ IamError.EntityTemporarilyUnmodifiable = Object.freeze({
188193
http_code: 409,
189194
type: error_type_enum.SENDER,
190195
});
196+
IamError.MalformedPolicyDocument = Object.freeze({
197+
code: 'MalformedPolicyDocument',
198+
message: 'The request was rejected because the policy document was malformed',
199+
http_code: 400,
200+
type: error_type_enum.SENDER,
201+
});
202+
191203

192204
// These errors were copied from STS errors
193205
IamError.InvalidParameterValue = Object.freeze({

src/endpoint/iam/iam_rest.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const RPC_ERRORS_TO_IAM = Object.freeze({
2121
NO_SUCH_ACCOUNT: IamError.AccessDeniedException,
2222
NO_SUCH_ROLE: IamError.AccessDeniedException,
2323
VALIDATION_ERROR: IamError.ValidationError,
24+
MALFORMED_POLICY_DOCUMENT: IamError.MalformedPolicyDocument,
2425
});
2526

2627
const ACTIONS = Object.freeze({
@@ -34,6 +35,10 @@ const ACTIONS = Object.freeze({
3435
'UpdateAccessKey': 'update_access_key',
3536
'DeleteAccessKey': 'delete_access_key',
3637
'ListAccessKeys': 'list_access_keys',
38+
'PutUserPolicy': 'put_user_policy',
39+
'GetUserPolicy': 'get_user_policy',
40+
'DeleteUserPolicy': 'delete_user_policy',
41+
'ListUserPolicies': 'list_user_policies',
3742
'ListGroupsForUser': 'list_groups_for_user',
3843
'ListAccountAliases': 'list_account_aliases',
3944
'ListAttachedGroupPolicies': 'list_attached_group_policies',
@@ -60,7 +65,6 @@ const ACTIONS = Object.freeze({
6065
'ListServiceSpecificCredentials': 'list_service_specific_credentials',
6166
'ListSigningCertificates': 'list_signing_certificates',
6267
'ListSSHPublicKeys': 'list_ssh_public_keys',
63-
'ListUserPolicies': 'list_user_policies',
6468
'ListUserTags': 'list_user_tags',
6569
'ListVirtualMFADevices': 'list_virtual_mfa_devices',
6670
});
@@ -79,6 +83,11 @@ const IAM_OPS = js_utils.deep_freeze({
7983
post_update_access_key: require('./ops/iam_update_access_key'),
8084
post_delete_access_key: require('./ops/iam_delete_access_key'),
8185
post_list_access_keys: require('./ops/iam_list_access_keys'),
86+
// user policy
87+
post_put_user_policy: require('./ops/iam_put_user_policy'),
88+
post_get_user_policy: require('./ops/iam_get_user_policy'),
89+
post_delete_user_policy: require('./ops/iam_delete_user_policy'),
90+
post_list_user_policies: require('./ops/iam_list_user_policies'),
8291
// other (currently ops that return empty or NoSuchEntity error - just not to fail them)
8392
post_list_groups_for_user: require('./ops/iam_list_groups_for_user.js'),
8493
post_list_account_aliases: require('./ops/iam_list_account_aliases.js'),
@@ -106,7 +115,6 @@ const IAM_OPS = js_utils.deep_freeze({
106115
post_list_service_specific_credentials: require('./ops/iam_list_service_specific_credentials.js'),
107116
post_list_signing_certificates: require('./ops/iam_list_signing_certificates.js'),
108117
post_list_ssh_public_keys: require('./ops/iam_list_ssh_public_keys.js'),
109-
post_list_user_policies: require('./ops/iam_list_user_policies.js'),
110118
post_list_user_tags: require('./ops/iam_list_user_tags.js'),
111119
post_list_virtual_mfa_devices: require('./ops/iam_list_virtual_mfa_devices.js'),
112120
});

src/endpoint/iam/iam_utils.js

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ function parse_max_items(input_max_items) {
8686
* @param {object} params
8787
*/
8888
function validate_params(action, params) {
89-
if (action.includes('user')) {
89+
if (action.includes('policy') || action.includes('policies')) {
90+
validate_policy_params(action, params);
91+
} else if (action.includes('user')) {
9092
validate_user_params(action, params);
9193
} else if (action.includes('access_key')) {
9294
validate_access_keys_params(action, params);
@@ -149,6 +151,30 @@ function validate_access_keys_params(action, params) {
149151
}
150152
}
151153

154+
/**
155+
* validate_policy_params will call the aquivalent function for each action in user policy API
156+
* @param {string} action
157+
* @param {object} params
158+
*/
159+
function validate_policy_params(action, params) {
160+
switch (action) {
161+
case iam_constants.IAM_ACTIONS.PUT_USER_POLICY:
162+
validate_put_user_policy(params);
163+
break;
164+
case iam_constants.IAM_ACTIONS.GET_USER_POLICY:
165+
validate_get_user_policy(params);
166+
break;
167+
case iam_constants.IAM_ACTIONS.DELETE_USER_POLICY:
168+
validate_delete_user_policy(params);
169+
break;
170+
case iam_constants.IAM_ACTIONS.LIST_USER_POLICIES:
171+
validate_list_user_policies(params);
172+
break;
173+
default:
174+
throw new RpcError('INTERNAL_ERROR', `${action} is not supported`);
175+
}
176+
}
177+
152178
/**
153179
* check_required_username checks if the username was set
154180
* @param {object} params
@@ -173,6 +199,22 @@ function check_required_status(params) {
173199
check_required_key(params.status, 'status');
174200
}
175201

202+
/**
203+
* check_required_policy_name checks if the policy name was set
204+
* @param {object} params
205+
*/
206+
function check_required_policy_name(params) {
207+
check_required_key(params.policy_name, 'policy-name');
208+
}
209+
210+
/**
211+
* check_required_policy_document checks if the policy document was set
212+
* @param {object} params
213+
*/
214+
function check_required_policy_document(params) {
215+
check_required_key(params.policy_document, 'policy-document');
216+
}
217+
176218
/**
177219
* check_required_key checks if a required key was set
178220
* @param {any} value
@@ -324,6 +366,68 @@ function validate_list_access_keys(params) {
324366
}
325367
}
326368

369+
/**
370+
* validate_put_user_policy checks the params for put_user_policy action
371+
* @param {object} params
372+
*/
373+
function validate_put_user_policy(params) {
374+
try {
375+
check_required_username(params);
376+
validation_utils.validate_username(params.username, iam_constants.IAM_PARAMETER_NAME.USERNAME);
377+
check_required_policy_name(params);
378+
validation_utils.validate_policy_name(params.policy_name, iam_constants.IAM_PARAMETER_NAME.POLICY_NAME);
379+
check_required_policy_document(params);
380+
validation_utils.validate_policy_document(params.policy_document, iam_constants.IAM_PARAMETER_NAME.POLICY_DOCUMENT);
381+
} catch (err) {
382+
translate_rpc_error(err);
383+
}
384+
}
385+
386+
/**
387+
* validate_get_user_policy checks the params for get_user_policy action
388+
* @param {object} params
389+
*/
390+
function validate_get_user_policy(params) {
391+
try {
392+
check_required_username(params);
393+
validation_utils.validate_username(params.username, iam_constants.IAM_PARAMETER_NAME.USERNAME);
394+
check_required_policy_name(params);
395+
validation_utils.validate_policy_name(params.policy_name, iam_constants.IAM_PARAMETER_NAME.POLICY_NAME);
396+
} catch (err) {
397+
translate_rpc_error(err);
398+
}
399+
}
400+
401+
/**
402+
* validate_delete_user_policy checks the params for delete_user_policy action
403+
* @param {object} params
404+
*/
405+
function validate_delete_user_policy(params) {
406+
try {
407+
check_required_username(params);
408+
validation_utils.validate_username(params.username, iam_constants.IAM_PARAMETER_NAME.USERNAME);
409+
check_required_policy_name(params);
410+
validation_utils.validate_policy_name(params.policy_name, iam_constants.IAM_PARAMETER_NAME.POLICY_NAME);
411+
} catch (err) {
412+
translate_rpc_error(err);
413+
}
414+
}
415+
416+
/**
417+
* validate_list_user_policies checks the params for list_user_policies action
418+
* @param {object} params
419+
*/
420+
function validate_list_user_policies(params) {
421+
try {
422+
validate_marker(params.marker);
423+
validate_max_items(params.max_items);
424+
check_required_username(params);
425+
validation_utils.validate_username(params.username, iam_constants.IAM_PARAMETER_NAME.USERNAME);
426+
} catch (err) {
427+
translate_rpc_error(err);
428+
}
429+
}
430+
327431
/**
328432
* validate_iam_path will validate:
329433
* 1. type
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* Copyright (C) 2024 NooBaa */
2+
'use strict';
3+
4+
const dbg = require('../../../util/debug_module')(__filename);
5+
const iam_utils = require('../iam_utils');
6+
const iam_constants = require('../iam_constants');
7+
const { CONTENT_TYPE_APP_FORM_URLENCODED } = require('../../../util/http_utils');
8+
9+
/**
10+
* https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteUserPolicy.html
11+
*/
12+
async function delete_user_policy(req, res) {
13+
14+
const params = {
15+
username: req.body.user_name,
16+
policy_name: req.body.policy_name,
17+
};
18+
dbg.log1('IAM DELETE USER POLICY', params);
19+
iam_utils.validate_params(iam_constants.IAM_ACTIONS.DELETE_USER_POLICY, params);
20+
await req.account_sdk.delete_user_policy(params);
21+
22+
return {
23+
DeleteUserPolicyResponse: {
24+
ResponseMetadata: {
25+
RequestId: req.request_id,
26+
}
27+
}
28+
};
29+
}
30+
31+
module.exports = {
32+
handler: delete_user_policy,
33+
body: {
34+
type: CONTENT_TYPE_APP_FORM_URLENCODED,
35+
},
36+
reply: {
37+
type: 'xml',
38+
},
39+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* Copyright (C) 2024 NooBaa */
2+
'use strict';
3+
4+
const dbg = require('../../../util/debug_module')(__filename);
5+
const iam_utils = require('../iam_utils');
6+
const iam_constants = require('../iam_constants');
7+
const { CONTENT_TYPE_APP_FORM_URLENCODED } = require('../../../util/http_utils');
8+
9+
/**
10+
* https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetUserPolicy.html
11+
*/
12+
async function get_user_policy(req, res) {
13+
14+
const params = {
15+
username: req.body.user_name,
16+
policy_name: req.body.policy_name,
17+
};
18+
dbg.log1('IAM GET USER POLICY', params);
19+
iam_utils.validate_params(iam_constants.IAM_ACTIONS.GET_USER_POLICY, params);
20+
const reply = await req.account_sdk.get_user_policy(params);
21+
dbg.log2('get_user_policy reply', reply);
22+
23+
return {
24+
GetUserPolicyResponse: {
25+
GetUserPolicyResult: {
26+
UserName: reply.username,
27+
PolicyName: reply.policy_name,
28+
PolicyDocument: reply.policy_document,
29+
},
30+
ResponseMetadata: {
31+
RequestId: req.request_id,
32+
}
33+
},
34+
};
35+
}
36+
37+
module.exports = {
38+
handler: get_user_policy,
39+
body: {
40+
type: CONTENT_TYPE_APP_FORM_URLENCODED,
41+
},
42+
reply: {
43+
type: 'xml',
44+
},
45+
};

src/endpoint/iam/ops/iam_list_user_policies.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,16 @@ async function list_user_policies(req, res) {
1717
max_items: iam_utils.parse_max_items(req.body.max_items) ?? iam_constants.DEFAULT_MAX_ITEMS,
1818
};
1919

20-
dbg.log1('To check that we have the user we will run the IAM GET USER', params);
21-
iam_utils.validate_params(iam_constants.IAM_ACTIONS.GET_USER, params);
22-
await req.account_sdk.get_user(params);
23-
24-
dbg.log1('IAM LIST USER POLICIES (returns empty list on every request)', params);
20+
dbg.log1('IAM LIST USER POLICIES', params);
21+
iam_utils.validate_params(iam_constants.IAM_ACTIONS.LIST_USER_POLICIES, params);
22+
const reply = await req.account_sdk.list_user_policies(params);
23+
dbg.log2('list_user_policies reply', reply);
2524

2625
return {
2726
ListUserPoliciesResponse: {
2827
ListUserPoliciesResult: {
29-
PolicyNames: [],
30-
IsTruncated: false,
28+
PolicyNames: reply.members,
29+
IsTruncated: reply.is_truncated,
3130
},
3231
ResponseMetadata: {
3332
RequestId: req.request_id,

0 commit comments

Comments
 (0)