Skip to content

Commit f9f367e

Browse files
committed
almost there
Signed-off-by: Utkarsh Srivastava <[email protected]>
1 parent 064fc25 commit f9f367e

13 files changed

+257
-1
lines changed

Diff for: src/api/common_api.js

+9
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,15 @@ module.exports = {
14201420
}
14211421
}
14221422
}
1423+
},
1424+
public_access_block: {
1425+
type: 'object',
1426+
properties: {
1427+
block_public_acls: { type: 'boolean' },
1428+
ignore_public_acls: { type: 'boolean' },
1429+
block_public_policy: { type: 'boolean' },
1430+
restrict_public_buckets: { type: 'boolean' },
1431+
},
14231432
}
14241433
}
14251434
};

Diff for: src/endpoint/s3/ops/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ exports.delete_bucket_replication = require('./s3_delete_bucket_replication');
1313
exports.delete_bucket_tagging = require('./s3_delete_bucket_tagging');
1414
exports.delete_bucket_website = require('./s3_delete_bucket_website');
1515
exports.delete_object = require('./s3_delete_object');
16+
exports.delete_public_access_block = require('./s3_put_public_access_block');
1617
exports.delete_object_tagging = require('./s3_delete_object_tagging');
1718
exports.delete_object_uploadId = require('./s3_delete_object_uploadId');
1819
exports.get_bucket = require('./s3_get_bucket');
@@ -44,6 +45,7 @@ exports.get_object_legal_hold = require('./s3_get_object_legal_hold');
4445
exports.get_object_retention = require('./s3_get_object_retention');
4546
exports.get_object_tagging = require('./s3_get_object_tagging');
4647
exports.get_object_uploadId = require('./s3_get_object_uploadId');
48+
exports.get_public_access_block = require('./s3_get_public_access_block');
4749
exports.get_service = require('./s3_get_service');
4850
exports.head_bucket = require('./s3_head_bucket');
4951
exports.head_object = require('./s3_head_object');

Diff for: src/endpoint/s3/ops/s3_delete_public_access_block.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* Copyright (C) 2024 NooBaa */
2+
'use strict';
3+
4+
/**
5+
* https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeletePublicAccessBlock.html
6+
*/
7+
async function delete_public_access_block(req) {
8+
await req.object_sdk.delete_public_access_block({ name: req.params.bucket });
9+
}
10+
11+
module.exports = {
12+
handler: delete_public_access_block,
13+
body: {
14+
type: 'empty',
15+
},
16+
reply: {
17+
type: 'empty',
18+
},
19+
};
20+

Diff for: src/endpoint/s3/ops/s3_get_public_access_block.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* Copyright (C) 2024 NooBaa */
2+
'use strict';
3+
4+
/**
5+
* https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetPublicAccessBlock.html
6+
*/
7+
async function get_public_access_block(req) {
8+
const reply = await req.object_sdk.get_public_access_block({ name: req.params.bucket });
9+
if (!reply.public_access_block) {
10+
return {
11+
block_public_acls: false,
12+
ignore_public_acls: false,
13+
block_public_policy: false,
14+
restrict_public_buckets: false,
15+
};
16+
}
17+
18+
return {
19+
block_public_acls: Boolean(reply.public_access_block.block_public_acls),
20+
ignore_public_acls: Boolean(reply.public_access_block.ignore_public_acls),
21+
block_public_policy: Boolean(reply.public_access_block.block_public_policy),
22+
restrict_public_buckets: Boolean(reply.public_access_block.restrict_public_buckets),
23+
};
24+
}
25+
26+
module.exports = {
27+
handler: get_public_access_block,
28+
body: {
29+
type: 'empty'
30+
},
31+
reply: {
32+
type: 'json',
33+
},
34+
};
35+

Diff for: src/endpoint/s3/ops/s3_put_public_access_block.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
/* Copyright (C) 2024 NooBaa */
22
'use strict';
33

4+
const s3_utils = require('../s3_utils');
5+
46
/**
57
* https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutPublicAccessBlock.html
68
* @param {*} req
79
* @param {*} res
810
*/
911
async function put_public_access_block(req, res) {
10-
// Do something in this function
12+
const public_access_block = s3_utils.parse_body_public_access_block(req);
13+
await req.object_sdk.put_public_access_block({ name: req.params.bucket, public_access_block });
1114
}
1215

1316
module.exports = {

Diff for: src/endpoint/s3/s3_bucket_policy_utils.js

+28
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,34 @@ async function validate_s3_policy(policy, bucket_name, get_account_handler) {
295295
}
296296
}
297297

298+
/**
299+
* allows_public_access returns true if a policy will allow public access
300+
* to a resource
301+
*
302+
* NOTE: It assumes that the given policy has already been validated
303+
* @param {*} policy
304+
* @returns {boolean}
305+
*/
306+
function allows_public_access(policy) {
307+
for (const statement of policy.statement) {
308+
if (statement.Effect === 'Deny') continue;
309+
310+
const statement_principal = statement.principal;
311+
if (statement_principal.AWS) {
312+
for (const principal of _.flatten([statement_principal.AWS])) {
313+
if (typeof principal === 'string' ? principal === '*' : principal.unwrap() === '*') {
314+
return true;
315+
}
316+
}
317+
} else if (typeof statement_principal === 'string' ? statement_principal === '*' : statement_principal.unwrap() === '*') {
318+
return true;
319+
}
320+
}
321+
322+
return false;
323+
}
324+
298325
exports.OP_NAME_TO_ACTION = OP_NAME_TO_ACTION;
299326
exports.has_bucket_policy_permission = has_bucket_policy_permission;
300327
exports.validate_s3_policy = validate_s3_policy;
328+
exports.allows_public_access = allows_public_access;

Diff for: src/endpoint/s3/s3_utils.js

+20
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,25 @@ function key_marker_to_cont_tok(key_marker, objects_arr, is_truncated) {
785785
return Buffer.from(j).toString('base64');
786786
}
787787

788+
function parse_body_public_access_block(req) {
789+
const parsed = {};
790+
791+
const access_cfg = req.body.PublicAccessBlockConfiguration;
792+
if (!access_cfg) throw new S3Error(S3Error.MalformedXML);
793+
794+
if (access_cfg.BlockPublicAcls || access_cfg.IgnorePublicAcls) {
795+
throw new S3Error(S3Error.AccessControlListNotSupported);
796+
}
797+
if (access_cfg.BlockPublicPolicy) {
798+
parsed.block_public_policy = access_cfg.BlockPublicPolicy.toLowerCase?.() === 'true';
799+
}
800+
if (access_cfg.RestrictPublicBuckets) {
801+
parsed.restrict_public_buckets = access_cfg.RestrictPublicBuckets.toLowerCase?.() === 'true';
802+
}
803+
804+
return parsed;
805+
}
806+
788807
exports.STORAGE_CLASS_STANDARD = STORAGE_CLASS_STANDARD;
789808
exports.STORAGE_CLASS_GLACIER = STORAGE_CLASS_GLACIER;
790809
exports.STORAGE_CLASS_GLACIER_IR = STORAGE_CLASS_GLACIER_IR;
@@ -828,5 +847,6 @@ exports.set_response_supported_storage_classes = set_response_supported_storage_
828847
exports.cont_tok_to_key_marker = cont_tok_to_key_marker;
829848
exports.key_marker_to_cont_tok = key_marker_to_cont_tok;
830849
exports.parse_sse_c = parse_sse_c;
850+
exports.parse_body_public_access_block = parse_body_public_access_block;
831851
exports.OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES;
832852
exports.OBJECT_ATTRIBUTES_UNSUPPORTED = OBJECT_ATTRIBUTES_UNSUPPORTED;

Diff for: src/sdk/bucketspace_fs.js

+49
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,14 @@ class BucketSpaceFS extends BucketSpaceSimpleFS {
635635
const { name, policy } = params;
636636
dbg.log0('BucketSpaceFS.put_bucket_policy: Bucket name, policy', name, policy);
637637
const bucket = await this.config_fs.get_bucket_by_name(name);
638+
639+
if (
640+
bucket.public_access_block?.block_public_policy &&
641+
bucket_policy_utils.allows_public_access(policy)
642+
) {
643+
throw new S3Error(S3Error.AccessDenied);
644+
}
645+
638646
bucket.s3_policy = policy;
639647
// We need to validate bucket schema here as well for checking the policy schema
640648
nsfs_schema_utils.validate_bucket_schema(_.omitBy(bucket, _.isUndefined));
@@ -697,6 +705,47 @@ class BucketSpaceFS extends BucketSpaceSimpleFS {
697705
}
698706
}
699707

708+
/////////////////////////
709+
// PUBLIC ACCESS BLOCK //
710+
/////////////////////////
711+
712+
async get_public_access_block(params, object_sdk) {
713+
try {
714+
const { bucket_name } = params;
715+
dbg.log0('BucketSpaceFS.get_public_access_block: Bucket name', bucket_name);
716+
const bucket = await this.config_fs.get_bucket_by_name(bucket_name);
717+
return {
718+
public_access_block: bucket.public_access_block,
719+
};
720+
} catch (error) {
721+
throw translate_error_codes(error, entity_enum.BUCKET);
722+
}
723+
}
724+
725+
async put_public_access_block(params, object_sdk) {
726+
try {
727+
const { bucket_name, public_access_block } = params;
728+
dbg.log0('BucketSpaceFS.put_public_access_block: Bucket name', bucket_name, ", public_access_block ", public_access_block);
729+
const bucket = await this.config_fs.get_bucket_by_name(bucket_name);
730+
bucket.public_access_block = public_access_block;
731+
await this.config_fs.update_bucket_config_file(bucket);
732+
} catch (error) {
733+
throw translate_error_codes(error, entity_enum.BUCKET);
734+
}
735+
}
736+
737+
async delete_public_access_block(params, object_sdk) {
738+
try {
739+
const { bucket_name } = params;
740+
dbg.log0('BucketSpaceFS.delete_public_access_block: Bucket name', bucket_name);
741+
const bucket = await this.config_fs.get_bucket_by_name(bucket_name);
742+
delete bucket.public_access_block;
743+
await this.config_fs.update_bucket_config_file(bucket);
744+
} catch (error) {
745+
throw translate_error_codes(error, entity_enum.BUCKET);
746+
}
747+
}
748+
700749
/////////////////////////
701750
// DEFAULT OBJECT LOCK //
702751
/////////////////////////

Diff for: src/sdk/bucketspace_nb.js

+16
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,22 @@ class BucketSpaceNB {
259259
return this.rpc_client.bucket.put_object_lock_configuration(params);
260260
}
261261

262+
/////////////////////////
263+
// PUBLIC ACCESS BLOCK //
264+
/////////////////////////
265+
266+
async get_public_access_block(params, object_sdk) {
267+
return this.rpc_client.bucket.get_public_access_block(params);
268+
}
269+
270+
async put_public_access_block(params, object_sdk) {
271+
return this.rpc_client.bucket.put_public_access_block(params);
272+
}
273+
274+
async delete_public_access_block(params, object_sdk) {
275+
return this.rpc_client.bucket.delete_public_access_block(params);
276+
}
277+
262278
// nsfs
263279

264280

Diff for: src/sdk/nb.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,10 @@ interface Namespace {
818818

819819
restore_object(params: object, object_sdk: ObjectSDK): Promise<any>;
820820
get_object_attributes(params: object, object_sdk: ObjectSDK): Promise<any>;
821+
822+
get_public_access_block(params, object_sdk: ObjectSDK): Promise<any>;
823+
put_public_access_block(params, object_sdk: ObjectSDK): Promise<any>;
824+
delete_public_access_block(params, object_sdk: ObjectSDK): Promise<any>;
821825
}
822826

823827
interface BucketSpace {

Diff for: src/sdk/object_sdk.js

+18
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,24 @@ class ObjectSDK {
11541154
}
11551155
}
11561156

1157+
//////////////////////////
1158+
// PUBLIC ACCESS BLOCK //
1159+
//////////////////////////
1160+
1161+
async get_public_access_block(params) {
1162+
const ns = await this._get_bucket_namespace(params.bucket);
1163+
return ns.get_public_access_block?.(params, this);
1164+
}
1165+
1166+
async put_public_access_block(params) {
1167+
const ns = await this._get_bucket_namespace(params.bucket);
1168+
return ns.put_public_access_block?.(params, this);
1169+
}
1170+
1171+
async delete_public_access_block(params) {
1172+
const ns = await this._get_bucket_namespace(params.bucket);
1173+
return ns.delete_public_access_block?.(params, this);
1174+
}
11571175
}
11581176

11591177
// EXPORT

Diff for: src/server/system_services/bucket_server.js

+49
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,15 @@ async function put_bucket_policy(req) {
490490
const bucket = find_bucket(req, req.rpc_params.name);
491491
await bucket_policy_utils.validate_s3_policy(req.rpc_params.policy, bucket.name,
492492
principal => system_store.get_account_by_email(principal));
493+
494+
if (
495+
bucket.public_access_block?.block_public_policy &&
496+
bucket_policy_utils.allows_public_access(req.rpc_params.policy)
497+
) {
498+
// Should result in AccessDenied error
499+
throw new RpcError('UNAUTHORIZED');
500+
}
501+
493502
await system_store.make_changes({
494503
update: {
495504
buckets: [{
@@ -1402,6 +1411,46 @@ async function update_all_buckets_default_pool(req) {
14021411
});
14031412
}
14041413

1414+
/**
1415+
*
1416+
* PUBLIC_ACCESS_BLOCK
1417+
*
1418+
*/
1419+
1420+
async function get_public_access_block(req) {
1421+
dbg.log0('get_public_access_block:', req.rpc_params);
1422+
const bucket = find_bucket(req, req.rpc_params.name);
1423+
return {
1424+
public_access_block: bucket.public_access_block,
1425+
};
1426+
}
1427+
1428+
async function put_public_access_block(req) {
1429+
dbg.log0('put_public_access_block:', req.rpc_params);
1430+
const bucket = find_bucket(req, req.rpc_params.name);
1431+
await system_store.make_changes({
1432+
update: {
1433+
buckets: [{
1434+
_id: bucket._id,
1435+
public_access_block: req.rpc_params.public_access_block,
1436+
}]
1437+
}
1438+
});
1439+
}
1440+
1441+
async function delete_public_access_block(req) {
1442+
dbg.log0('delete_public_access_block:', req.rpc_params);
1443+
const bucket = find_bucket(req, req.rpc_params.name);
1444+
await system_store.make_changes({
1445+
update: {
1446+
buckets: [{
1447+
_id: bucket._id,
1448+
$unset: { public_access_block: 1 }
1449+
}]
1450+
}
1451+
});
1452+
}
1453+
14051454
// UTILS //////////////////////////////////////////////////////////
14061455

14071456
function validate_bucket_creation(req) {

Diff for: src/server/system_services/schemas/nsfs_bucket_schema.js

+3
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,8 @@ module.exports = {
8484
$ref: 'common_api#/definitions/bucket_notification'
8585
}
8686
},
87+
public_access_block: {
88+
$ref: 'common_api#/definitions/public_access_block',
89+
}
8790
}
8891
};

0 commit comments

Comments
 (0)