Skip to content

Commit c1a440a

Browse files
committed
IAM | Principal validation and S3 permission updated with ID
1 parent d3c091e commit c1a440a

File tree

3 files changed

+56
-36
lines changed

3 files changed

+56
-36
lines changed

src/endpoint/s3/s3_rest.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ async function authorize_request_policy(req) {
256256
const account = req.object_sdk.requesting_account;
257257
const is_nc_deployment = req.object_sdk.nsfs_config_root;
258258
const account_identifier_name = is_nc_deployment ? account.name.unwrap() : account.email.unwrap();
259-
const account_identifier_id = is_nc_deployment ? account._id : undefined;
259+
// Both NSFS NC and containerized will validate bucket policy against acccount id.
260+
const account_identifier_id = account._id;
260261
const account_identifier_arn = s3_bucket_policy_utils.get_bucket_policy_principal_arn(account);
261262

262263
// deny delete_bucket permissions from bucket_claim_owner accounts (accounts that were created by OBC from openshift\k8s)
@@ -310,24 +311,25 @@ async function authorize_request_policy(req) {
310311
s3_policy, account_identifier_id, method, arn_path, req,
311312
{ disallow_public_access: public_access_block?.restrict_public_buckets }
312313
);
313-
dbg.log3('authorize_request_policy: permission_by_id', permission_by_id);
314+
dbg.log0('authorize_request_policy: permission_by_id', permission_by_id);
314315
}
315316
if (permission_by_id === "DENY") throw new S3Error(S3Error.AccessDenied);
316-
if ((!account_identifier_id || permission_by_id !== "DENY") && account.owner === undefined) {
317+
// Check bucket policy permission against account name only for NSFS NC
318+
if (is_nc_deployment && (!account_identifier_id || permission_by_id !== "DENY") && account.owner === undefined) {
317319
permission_by_name = await s3_bucket_policy_utils.has_bucket_policy_permission(
318320
s3_policy, account_identifier_name, method, arn_path, req,
319321
{ disallow_public_access: public_access_block?.restrict_public_buckets }
320322
);
321-
dbg.log3('authorize_request_policy: permission_by_name', permission_by_name);
323+
dbg.log0('authorize_request_policy: permission_by_name', permission_by_name);
322324
}
323325
if (permission_by_name === "DENY") throw new S3Error(S3Error.AccessDenied);
324-
// Containerized deployment always will have account_identifier_id undefined
326+
// Check bucket policy permission against ARN only for containerized deployments.
325327
// Policy permission is validated by account arn
326-
if (!account_identifier_id) {
328+
if (!is_nc_deployment && permission_by_id !== "DENY") {
327329
permission_by_arn = await s3_bucket_policy_utils.has_bucket_policy_permission(
328330
s3_policy, account_identifier_arn, method, arn_path, req, public_access_block?.restrict_public_buckets
329331
);
330-
dbg.log3('authorize_request_policy: permission_by_arn', permission_by_arn);
332+
dbg.log0('authorize_request_policy: permission_by_arn', permission_by_arn);
331333
}
332334
if (permission_by_arn === "DENY") throw new S3Error(S3Error.AccessDenied);
333335

src/server/system_services/bucket_server.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -523,10 +523,9 @@ async function get_bucket_policy(req) {
523523
eg: arn:aws:iam::${account_id}:user/${iam_path}/${user_name}
524524
account email = ${iam_user_name}:${account_id}
525525
526-
@param {SensitiveString | String} principal Bucket policy principal
526+
@param {String} principal_as_string Bucket policy principal string
527527
*/
528-
async function get_account_by_principal(principal) {
529-
const principal_as_string = principal instanceof SensitiveString ? principal.unwrap() : principal;
528+
async function account_exists_by_principal_arn(principal_as_string) {
530529
const root_sufix = 'root';
531530
const user_sufix = 'user';
532531
const arn_parts = principal_as_string.split(':');
@@ -546,6 +545,27 @@ async function get_account_by_principal(principal) {
546545
//}
547546
}
548547

548+
/**
549+
Validate and return account by principal ARN and account id.
550+
551+
@param {SensitiveString | String} principal Bucket policy principal
552+
*/
553+
async function get_account_by_principal(principal) {
554+
const principal_as_string = principal instanceof SensitiveString ? principal.unwrap() : principal;
555+
const is_principal_arn = principal_as_string.startsWith('arn:aws:iam::');
556+
if (is_principal_arn) {
557+
const principal_by_arn = await account_exists_by_principal_arn(principal_as_string);
558+
dbg.log3('get_account_by_principal: principal_by_arn', principal_by_arn);
559+
if (principal_by_arn) return true;
560+
} else {
561+
const account = system_store.data.accounts.find(acc => acc._id.toString() === principal_as_string);
562+
const principal_by_id = account !== undefined;
563+
dbg.log3('get_account_by_principal: principal_by_id', principal_by_id);
564+
if (principal_by_id) return true;
565+
}
566+
return false;
567+
}
568+
549569
async function put_bucket_policy(req) {
550570
dbg.log0('put_bucket_policy:', req.rpc_params);
551571
const bucket = find_bucket(req, req.rpc_params.name);

src/test/integration_tests/api/s3/test_s3_bucket_policy.js

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ const cross_test_store = {};
6565
let user_a_account_details;
6666
let user_b_account_details;
6767

68+
let user_a_account_id;
69+
let user_b_account_id;
70+
6871
let admin_info;
69-
let account_info_a;
70-
let account_info_b;
7172

7273
let a_principal;
7374
let b_principal;
@@ -123,14 +124,14 @@ async function setup() {
123124
account.name = user_a;
124125
account.email = user_a;
125126
user_a_account_details = await rpc_client.account.create_account(account);
126-
account_info_a = user_a_account_details._id ? user_a_account_details : await rpc_client.account.read_account({ email: user_a });
127-
console.log('user_a_account_details', account_info_a);
127+
console.log('user_a_account_details', user_a_account_details);
128128
const user_a_keys = user_a_account_details.access_keys;
129+
user_a_account_id = is_nc_coretest ? user_a_account_details._id : user_a_account_details.id;
129130
account.name = user_b;
130131
account.email = user_b;
131132
user_b_account_details = await rpc_client.account.create_account(account);
132-
account_info_b = user_b_account_details._id ? user_b_account_details : await rpc_client.account.read_account({ email: user_b });
133-
console.log('user_b_account_details', account_info_b);
133+
console.log('user_b_account_details', user_b_account_details);
134+
user_b_account_id = is_nc_coretest ? user_b_account_details._id : user_b_account_details.id;
134135
const user_b_keys = user_b_account_details.access_keys;
135136
s3_creds.credentials = {
136137
accessKeyId: user_a_keys[0].access_key.unwrap(),
@@ -149,11 +150,11 @@ async function setup() {
149150
};
150151
/*
151152
For coretest nc, principal will have account name and
152-
+ for containerized deployment principal is ARN
153+
for containerized deployment principal is ARN
153154
*/
154155
admin_principal = is_nc_coretest ? EMAIL : s3_bucket_policy_utils.create_arn_for_root(admin_info._id.toString());
155-
a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(account_info_a._id.toString());
156-
b_principal = is_nc_coretest ? user_b : s3_bucket_policy_utils.create_arn_for_root(account_info_b._id.toString());
156+
a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(user_a_account_details.id.toString());
157+
b_principal = is_nc_coretest ? user_b : s3_bucket_policy_utils.create_arn_for_root(user_b_account_details.id.toString());
157158

158159
s3_owner = new S3(s3_creds);
159160
await s3_owner.createBucket({ Bucket: BKT });
@@ -312,12 +313,11 @@ mocha.describe('s3_bucket_policy', function() {
312313
});
313314

314315
mocha.it('should put/get bucket policy - principal by account ID', async function() {
315-
if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
316316
const policy = {
317317
Statement: [{
318-
Sid: `Allow all s3 actions on bucket ${BKT} to principal (by ID) ${user_a_account_details._id}`,
318+
Sid: `Allow all s3 actions on bucket ${BKT} to principal (by ID) ${user_a_account_id}`,
319319
Effect: 'Allow',
320-
Principal: { AWS: user_a_account_details._id },
320+
Principal: { AWS: user_a_account_id },
321321
Action: ['s3:*'],
322322
Resource: [`arn:aws:s3:::${BKT}`, `arn:aws:s3:::${BKT}/*`]
323323
}]
@@ -335,12 +335,11 @@ mocha.describe('s3_bucket_policy', function() {
335335
});
336336

337337
mocha.it('should put object to permitted account (bucket policy - principal by account ID', async function() {
338-
if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
339338
const policy = {
340339
Statement: [{
341-
Sid: `Allow all s3 actions on bucket ${BKT} to principal (by ID) ${user_a_account_details._id}`,
340+
Sid: `Allow all s3 actions on bucket ${BKT} to principal (by ID) ${user_a_account_id}`,
342341
Effect: 'Allow',
343-
Principal: { AWS: user_a_account_details._id },
342+
Principal: { AWS: user_a_account_id },
344343
Action: ['s3:*'],
345344
Resource: [`arn:aws:s3:::${BKT}`, `arn:aws:s3:::${BKT}/*`]
346345
}]
@@ -395,7 +394,7 @@ mocha.describe('s3_bucket_policy', function() {
395394
};
396395
}
397396
// Losing this value in-between, assigning it again
398-
a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(account_info_a._id.toString());
397+
a_principal = is_nc_coretest ? user_a : s3_bucket_policy_utils.create_arn_for_root(user_b_account_id.toString());
399398
const deny_account_by_name_all_s3_actions_statement = {
400399
Sid: `Do not allow user ${user_a} any s3 action`,
401400
Effect: 'Deny',
@@ -407,9 +406,8 @@ mocha.describe('s3_bucket_policy', function() {
407406
mocha.it('should not allow principal get object bucket policy with 2 statements: ' +
408407
'(1) DENY principal by account ID (2) ALLOW all principals as *', async function() {
409408
// in NC we allow principal to be also IDs
410-
if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
411409
const deny_account_by_id_all_s3_actions_statement =
412-
get_deny_account_by_id_all_s3_actions_statement(user_a_account_details._id);
410+
get_deny_account_by_id_all_s3_actions_statement(user_a_account_id);
413411
const policy = {
414412
Statement: [
415413
allow_all_principals_all_s3_actions_statement,
@@ -481,9 +479,9 @@ mocha.describe('s3_bucket_policy', function() {
481479
mocha.it('should not allow principal get object bucket policy with 2 statements: ' +
482480
'(1) DENY principal by account ID (2) ALLOW by account name', async function() {
483481
// in NC we allow principal to be also IDs
484-
if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
482+
// if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
485483
const deny_account_by_id_all_s3_actions_statement =
486-
get_deny_account_by_id_all_s3_actions_statement(user_a_account_details._id);
484+
get_deny_account_by_id_all_s3_actions_statement(user_a_account_id);
487485
const allow_account_by_name_all_s3_actions_statement = _.cloneDeep(deny_account_by_name_all_s3_actions_statement);
488486
allow_account_by_name_all_s3_actions_statement.Effect = 'Allow';
489487
allow_account_by_name_all_s3_actions_statement.Sid = `Allow user ${user_a} any s3 action`;
@@ -516,12 +514,12 @@ mocha.describe('s3_bucket_policy', function() {
516514
mocha.it('should not allow principal get object bucket policy with 2 statements: ' +
517515
'(1) DENY principal by account name (2) ALLOW by account ID', async function() {
518516
// in NC we allow principal to be also IDs
519-
if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
517+
// if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
520518
const deny_account_by_id_all_s3_actions_statement =
521-
get_deny_account_by_id_all_s3_actions_statement(user_a_account_details._id);
519+
get_deny_account_by_id_all_s3_actions_statement(user_a_account_id);
522520
const allow_account_by_id_all_s3_actions_statement = _.cloneDeep(deny_account_by_id_all_s3_actions_statement);
523521
allow_account_by_id_all_s3_actions_statement.Effect = 'Allow';
524-
allow_account_by_id_all_s3_actions_statement.Sid = `Allow user ${user_a_account_details._id} any s3 action`;
522+
allow_account_by_id_all_s3_actions_statement.Sid = `Allow user ${user_a_account_id} any s3 action`;
525523
const policy = {
526524
Statement: [
527525
deny_account_by_name_all_s3_actions_statement,
@@ -582,12 +580,12 @@ mocha.describe('s3_bucket_policy', function() {
582580
mocha.it('should not allow principal get object bucket policy with 2 statements: ' +
583581
'(1) ALLOW principal by account ID (2) DENY all principals as * (specific action only)', async function() {
584582
// in NC we allow principal to be also IDs
585-
if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
583+
// if (!is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
586584
const deny_account_by_id_all_s3_actions_statement =
587-
get_deny_account_by_id_all_s3_actions_statement(user_a_account_details._id);
585+
get_deny_account_by_id_all_s3_actions_statement(user_a_account_id);
588586
const allow_account_by_id_all_s3_actions_statement = _.cloneDeep(deny_account_by_id_all_s3_actions_statement);
589587
allow_account_by_id_all_s3_actions_statement.Effect = 'Allow';
590-
allow_account_by_id_all_s3_actions_statement.Sid = `Allow user ${user_a_account_details._id} any s3 action`;
588+
allow_account_by_id_all_s3_actions_statement.Sid = `Allow user ${user_a_account_id} any s3 action`;
591589
const policy = {
592590
Statement: [
593591
allow_account_by_id_all_s3_actions_statement,

0 commit comments

Comments
 (0)