Skip to content

Commit 1187986

Browse files
Merge pull request #9038 from romayalon/romy-5.18-backports
IAM backports to 5.18.4
2 parents c8e8bd1 + ea4c49b commit 1187986

14 files changed

+165
-16
lines changed

docs/design/iam.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ Here attached a diagram with all the accounts that we have in our system:
127127
- IAM DeleteAccessKey: AccessKeyId, UserName
128128
- IAM ListAccessKeys: UserName (not supported: Marker, MaxItems)
129129

130+
### Other
131+
- IAM ListGroupsForUser - would always return empty list (to check that the user exists it runs GetUser).
132+
130133
### Configuration Directory Components With users
131134
If account creates a user its config file will be created under identities/<user-id>.identity.json and under the account will be created `users/` directory and inside it it will link to the config.
132135
Example:

src/endpoint/iam/iam_rest.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const ACTIONS = Object.freeze({
3434
'UpdateAccessKey': 'update_access_key',
3535
'DeleteAccessKey': 'delete_access_key',
3636
'ListAccessKeys': 'list_access_keys',
37+
'ListGroupsForUser': 'list_groups_for_user',
3738
});
3839

3940
// notice: shows all methods as method post
@@ -50,6 +51,8 @@ const IAM_OPS = js_utils.deep_freeze({
5051
post_update_access_key: require('./ops/iam_update_access_key'),
5152
post_delete_access_key: require('./ops/iam_delete_access_key'),
5253
post_list_access_keys: require('./ops/iam_list_access_keys'),
54+
// other (currently ops that return empty just not to fail them)
55+
post_list_groups_for_user: require('./ops/iam_list_groups_for_user.js'),
5356
});
5457

5558
async function iam_rest(req, res) {
@@ -145,6 +148,7 @@ function parse_op_name(req, action) {
145148
if (ACTIONS[action]) {
146149
return `${method}_${ACTIONS[action]}`;
147150
}
151+
dbg.error('IAM parse_op_name - NotImplemented', action, method, req.originalUrl);
148152
throw new IamError(IamError.NotImplemented);
149153
}
150154

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 { CONTENT_TYPE_APP_FORM_URLENCODED } = require('../../../util/http_utils');
6+
const iam_utils = require('../iam_utils');
7+
const iam_constants = require('../iam_constants');
8+
9+
/**
10+
* https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListGroupsForUser.html
11+
*/
12+
async function list_groups_for_user(req, res) {
13+
14+
const params = {
15+
username: req.body.user_name,
16+
};
17+
18+
dbg.log1('To check that we have the user we will run the IAM GET USER', params);
19+
iam_utils.validate_params(iam_constants.IAM_ACTIONS.GET_USER, params);
20+
await req.account_sdk.get_user(params);
21+
22+
dbg.log1('IAM LIST GROUPS FOR USER (returns empty list on every request)', params);
23+
24+
return {
25+
ListGroupsForUserResponse: {
26+
ListGroupsForUserResult: {
27+
Groups: [],
28+
IsTruncated: false,
29+
},
30+
ResponseMetadata: {
31+
RequestId: req.request_id,
32+
}
33+
},
34+
};
35+
}
36+
37+
module.exports = {
38+
handler: list_groups_for_user,
39+
body: {
40+
type: CONTENT_TYPE_APP_FORM_URLENCODED,
41+
},
42+
reply: {
43+
type: 'xml',
44+
},
45+
};

src/endpoint/s3/ops/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ exports.get_bucket_object_lock = require('./s3_get_bucket_object_lock');
3131
exports.get_bucket_policy = require('./s3_get_bucket_policy');
3232
exports.get_bucket_policy_status = require('./s3_get_bucket_policy_status');
3333
exports.get_bucket_replication = require('./s3_get_bucket_replication');
34-
exports.get_bucket_requestPayment = require('./s3_get_bucket_requestPayment');
34+
exports.get_bucket_request_payment = require('./s3_get_bucket_request_payment');
3535
exports.get_bucket_tagging = require('./s3_get_bucket_tagging');
3636
exports.get_bucket_uploads = require('./s3_get_bucket_uploads');
3737
exports.get_bucket_versioning = require('./s3_get_bucket_versioning');
@@ -67,7 +67,7 @@ exports.put_bucket_notification = require('./s3_put_bucket_notification');
6767
exports.put_bucket_object_lock = require('./s3_put_bucket_object_lock');
6868
exports.put_bucket_policy = require('./s3_put_bucket_policy');
6969
exports.put_bucket_replication = require('./s3_put_bucket_replication');
70-
exports.put_bucket_requestPayment = require('./s3_put_bucket_requestPayment');
70+
exports.put_bucket_request_payment = require('./s3_put_bucket_request_payment');
7171
exports.put_bucket_tagging = require('./s3_put_bucket_tagging');
7272
exports.put_bucket_versioning = require('./s3_put_bucket_versioning');
7373
exports.put_bucket_website = require('./s3_put_bucket_website');

src/endpoint/s3/ops/s3_get_bucket_requestPayment.js renamed to src/endpoint/s3/ops/s3_get_bucket_request_payment.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
/* Copyright (C) 2016 NooBaa */
22
'use strict';
33

4+
const dbg = require('../../../util/debug_module')(__filename);
5+
46
/**
57
* http://docs.aws.amazon.com/AmazonS3/latest/API/RESTrequestPaymentGET.html
68
*/
7-
async function get_bucket_requestPayment(req) {
9+
async function get_bucket_request_payment(req) {
810
await req.object_sdk.read_bucket({ name: req.params.bucket });
11+
const payer = 'BucketOwner';
12+
dbg.log1(`s3_get_bucket_request_payment (returns ${payer} on every request)`);
913
return {
1014
RequestPaymentConfiguration: {
11-
Payer: 'BucketOwner'
15+
Payer: payer
1216
}
1317
};
1418
}
1519

1620
module.exports = {
17-
handler: get_bucket_requestPayment,
21+
handler: get_bucket_request_payment,
1822
body: {
1923
type: 'empty',
2024
},

src/endpoint/s3/ops/s3_put_bucket_requestPayment.js renamed to src/endpoint/s3/ops/s3_put_bucket_request_payment.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ const S3Error = require('../s3_errors').S3Error;
66
/**
77
* http://docs.aws.amazon.com/AmazonS3/latest/API/RESTrequestPaymentPUT.html
88
*/
9-
async function put_bucket_requestPayment(req) {
9+
async function put_bucket_request_payment(req) {
1010
await req.object_sdk.read_bucket({ name: req.params.bucket });
1111
// TODO S3 put_bucket_requestPayment not implemented
1212
throw new S3Error(S3Error.NotImplemented);
1313
}
1414

1515
module.exports = {
16-
handler: put_bucket_requestPayment,
16+
handler: put_bucket_request_payment,
1717
body: {
1818
type: 'xml',
1919
},

src/endpoint/s3/s3_bucket_policy_utils.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const OP_NAME_TO_ACTION = Object.freeze({
3636
get_bucket_policy: { regular: "s3:GetBucketPolicy" },
3737
get_bucket_policy_status: { regular: "s3:GetBucketPolicyStatus" },
3838
get_bucket_replication: { regular: "s3:GetReplicationConfiguration" },
39-
get_bucket_requestpayment: { regular: "s3:GetBucketRequestPayment" },
39+
get_bucket_request_payment: { regular: "s3:GetBucketRequestPayment" },
4040
get_bucket_tagging: { regular: "s3:GetBucketTagging" },
4141
get_bucket_uploads: { regular: "s3:ListBucketMultipartUploads" },
4242
get_bucket_versioning: { regular: "s3:GetBucketVersioning" },
@@ -75,7 +75,7 @@ const OP_NAME_TO_ACTION = Object.freeze({
7575
put_bucket_notification: { regular: "s3:PutBucketNotification" },
7676
put_bucket_policy: { regular: "s3:PutBucketPolicy" },
7777
put_bucket_replication: { regular: "s3:PutReplicationConfiguration" },
78-
put_bucket_requestpayment: { regular: "s3:PutBucketRequestPayment" },
78+
put_bucket_request_payment: { regular: "s3:PutBucketRequestPayment" },
7979
put_bucket_tagging: { regular: "s3:PutBucketTagging" },
8080
put_bucket_versioning: { regular: "s3:PutBucketVersioning" },
8181
put_bucket_website: { regular: "s3:PutBucketWebsite" },

src/endpoint/s3/s3_rest.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const BUCKET_SUB_RESOURCES = Object.freeze({
3636
'policy': 'policy',
3737
'policyStatus': 'policy_status',
3838
'replication': 'replication',
39-
'requestPayment': 'requestPayment',
39+
'requestPayment': 'request_payment',
4040
'tagging': 'tagging',
4141
'uploads': 'uploads',
4242
'versioning': 'versioning',

src/endpoint/sts/sts_rest.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ function parse_op_name(req, action) {
163163
if (ACTIONS[action]) {
164164
return `${method}_${ACTIONS[action]}`;
165165
}
166+
dbg.error('STS parse_op_name - NotImplemented', action, method, req.originalUrl);
166167
throw new StsError(StsError.NotImplemented);
167168
}
168169

src/sdk/bucketspace_fs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ class BucketSpaceFS extends BucketSpaceSimpleFS {
683683
const { name } = params;
684684
dbg.log0('BucketSpaceFS.get_bucket_encryption: Bucket name', name);
685685
const bucket = await this.config_fs.get_bucket_by_name(name);
686-
return bucket.encryption;
686+
return { encryption: bucket.encryption };
687687
} catch (err) {
688688
throw translate_error_codes(err, entity_enum.BUCKET);
689689
}

src/test/unit_tests/test_bucketspace_fs.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,11 @@ mocha.describe('bucketspace_fs', function() {
717717
});
718718

719719
mocha.describe('bucket encryption operations', function() {
720+
mocha.it('get_bucket_encryption (return empty encryption)', async function() {
721+
const param = { name: test_bucket };
722+
const empty_encryption = await bucketspace_fs.get_bucket_encryption(param);
723+
assert.ok(empty_encryption.encryption === undefined);
724+
});
720725
mocha.it('put_bucket_encryption ', async function() {
721726
const encryption = {
722727
algorithm: 'AES256',
@@ -726,19 +731,19 @@ mocha.describe('bucketspace_fs', function() {
726731
await bucketspace_fs.put_bucket_encryption(param);
727732

728733
const output_encryption = await bucketspace_fs.get_bucket_encryption(param);
729-
assert.deepEqual(output_encryption, encryption);
734+
assert.deepEqual(output_encryption.encryption, encryption);
730735
});
731-
mocha.it('delete_bucket_encryption ', async function() {
736+
mocha.it('delete_bucket_encryption', async function() {
732737
const encryption = {
733738
algorithm: 'AES256',
734739
kms_key_id: 'kms-123'
735740
};
736741
const param = { name: test_bucket };
737742
const output_encryption = await bucketspace_fs.get_bucket_encryption(param);
738-
assert.deepEqual(output_encryption, encryption);
743+
assert.deepEqual(output_encryption.encryption, encryption);
739744
await bucketspace_fs.delete_bucket_encryption(param);
740745
const empty_encryption = await bucketspace_fs.get_bucket_encryption(param);
741-
assert.ok(empty_encryption === undefined);
746+
assert.ok(empty_encryption.encryption === undefined);
742747
});
743748
});
744749

src/test/unit_tests/test_nc_iam_basic_integration.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ const { TMP_PATH, generate_nsfs_account, get_new_buckets_path_by_test_env, gener
1111
get_coretest_path } = require('../system_tests/test_utils');
1212
const { ListUsersCommand, CreateUserCommand, GetUserCommand, UpdateUserCommand, DeleteUserCommand,
1313
ListAccessKeysCommand, CreateAccessKeyCommand, GetAccessKeyLastUsedCommand,
14-
UpdateAccessKeyCommand, DeleteAccessKeyCommand } = require('@aws-sdk/client-iam');
14+
UpdateAccessKeyCommand, DeleteAccessKeyCommand,
15+
ListGroupsForUserCommand } = require('@aws-sdk/client-iam');
1516
const { ACCESS_KEY_STATUS_ENUM } = require('../../endpoint/iam/iam_constants');
17+
const IamError = require('../../endpoint/iam/iam_errors').IamError;
18+
1619

1720
const coretest_path = get_coretest_path();
1821
const coretest = require(coretest_path);
@@ -229,6 +232,54 @@ mocha.describe('IAM basic integration tests - happy path', async function() {
229232
_check_status_code_ok(response);
230233
});
231234
});
235+
236+
mocha.describe('IAM other APIs (currently returns empty value)', async function() {
237+
const username3 = 'Emi';
238+
239+
mocha.before(async () => {
240+
// create a user
241+
const input = {
242+
UserName: username3
243+
};
244+
const command = new CreateUserCommand(input);
245+
const response = await iam_account.send(command);
246+
_check_status_code_ok(response);
247+
});
248+
249+
mocha.after(async () => {
250+
// delete a user
251+
const input = {
252+
UserName: username3
253+
};
254+
const command = new DeleteUserCommand(input);
255+
const response = await iam_account.send(command);
256+
_check_status_code_ok(response);
257+
});
258+
259+
mocha.it('list groups for non existing user - should throw an error', async function() {
260+
try {
261+
const input = {
262+
UserName: 'non-existing-user'
263+
};
264+
const command = new ListGroupsForUserCommand(input);
265+
await iam_account.send(command);
266+
assert.fail('list groups for non existing user - should throw an error');
267+
} catch (err) {
268+
const err_code = err.Error.Code;
269+
assert.equal(err_code, IamError.NoSuchEntity.code);
270+
}
271+
});
272+
273+
mocha.it('list groups for user - should be empty', async function() {
274+
const input = {
275+
UserName: username3
276+
};
277+
const command = new ListGroupsForUserCommand(input);
278+
const response = await iam_account.send(command);
279+
_check_status_code_ok(response);
280+
assert.equal(response.Groups.length, 0);
281+
});
282+
});
232283
});
233284

234285
/**

src/test/unit_tests/test_nsfs_integration.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,20 @@ mocha.describe('bucket operations - namespace_fs', function() {
789789
const res_without_metadata = _.omit(res, '$metadata');
790790
assert.deepEqual(res_without_metadata, {});
791791
});
792+
mocha.it('get bucket request payment (currently returns a mock) without failing', async function() {
793+
const res = await s3_correct_uid_default_nsr.getBucketRequestPayment({ Bucket: bucket_name});
794+
assert.equal(res.$metadata.httpStatusCode, 200);
795+
const expected_payer = 'BucketOwner'; // this is the mock that we use
796+
assert.equal(res.Payer, expected_payer);
797+
});
798+
mocha.it('get bucket encryption (before put bucket encryption) - throws an error', async function() {
799+
try {
800+
await s3_correct_uid_default_nsr.getBucketEncryption({ Bucket: bucket_name});
801+
assert.fail('get bucket encryption when encryption not set should fail');
802+
} catch (err) {
803+
assert.strictEqual(err.Code, 'ServerSideEncryptionConfigurationNotFoundError');
804+
}
805+
});
792806

793807
mocha.it('delete multiple non existing objects without failing', async function() {
794808
const keys_to_delete = [

src/test/unit_tests/test_s3_ops.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,28 @@ mocha.describe('s3_ops', function() {
110110
const res = await s3.listBuckets({});
111111
assert(res.Buckets.find(bucket => bucket.Name === BKT1));
112112
});
113+
mocha.it('should not fail request of get bucket request payment (currently returns a mock)', async function() {
114+
const res = await s3.getBucketRequestPayment({ Bucket: BKT1 });
115+
assert.equal(res.$metadata.httpStatusCode, 200);
116+
const expected_payer = 'BucketOwner'; // this is the mock that we use
117+
assert.equal(res.Payer, expected_payer);
118+
});
119+
mocha.it('should allow get_bucket_encryption (no put before the get)', async function() {
120+
const res = await s3.getBucketEncryption({ Bucket: BKT1 });
121+
const expected_response = {
122+
ServerSideEncryptionConfiguration: {
123+
Rules: [{
124+
ApplyServerSideEncryptionByDefault: {
125+
SSEAlgorithm: 'AES256'
126+
},
127+
BucketKeyEnabled: false
128+
}]
129+
}
130+
};
131+
assert.equal(res.$metadata.httpStatusCode, 200);
132+
const res_without_metadata = _.omit(res, '$metadata');
133+
assert.deepEqual(res_without_metadata, expected_response);
134+
});
113135
mocha.it('should enable bucket logging', async function() {
114136
await s3.createBucket({ Bucket: BKT2 });
115137
await s3.putBucketLogging({

0 commit comments

Comments
 (0)