Skip to content

Commit d730ac6

Browse files
author
Elad Ben-Israel
authored
feat(aws-s3): support granting public access to objects (#886)
Adds `bucket.grantPublicAccess` with a bunch of useful capabilities. Fixes #877
1 parent c21ebb5 commit d730ac6

File tree

2 files changed

+143
-3
lines changed

2 files changed

+143
-3
lines changed

packages/@aws-cdk/aws-s3/lib/bucket.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,41 @@ export abstract class BucketRef extends cdk.Construct {
269269
this.arnForObjects(objectsKeyPattern));
270270
}
271271

272+
/**
273+
* Allows unrestricted access to objects from this bucket.
274+
*
275+
* IMPORTANT: This permission allows anyone to perform actions on S3 objects
276+
* in this bucket, which is useful for when you configure your bucket as a
277+
* website and want everyone to be able to read objects in the bucket without
278+
* needing to authenticate.
279+
*
280+
* Without arguments, this method will grant read ("s3:GetObject") access to
281+
* all objects ("*") in the bucket.
282+
*
283+
* The method returns the `iam.PolicyStatement` object, which can then be modified
284+
* as needed. For example, you can add a condition that will restrict access only
285+
* to an IPv4 range like this:
286+
*
287+
* const statement = bucket.grantPublicAccess();
288+
* statement.addCondition('IpAddress', { "aws:SourceIp": "54.240.143.0/24" });
289+
*
290+
*
291+
* @param keyPrefix the prefix of S3 object keys (e.g. `home/*`). Default is "*".
292+
* @param allowedActions the set of S3 actions to allow. Default is "s3:GetObject".
293+
* @returns The `iam.PolicyStatement` object, which can be used to apply e.g. conditions.
294+
*/
295+
public grantPublicAccess(keyPrefix = '*', ...allowedActions: string[]): iam.PolicyStatement {
296+
allowedActions = allowedActions.length > 0 ? allowedActions : [ 's3:GetObject' ];
297+
298+
const statement = new iam.PolicyStatement()
299+
.addActions(...allowedActions)
300+
.addResource(this.arnForObjects(keyPrefix))
301+
.addPrincipal(new iam.Anyone());
302+
303+
this.addToResourcePolicy(statement);
304+
return statement;
305+
}
306+
272307
private grant(identity: iam.IPrincipal | undefined,
273308
bucketActions: string[],
274309
keyActions: string[],

packages/@aws-cdk/aws-s3/test/test.bucket.ts

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { expect } from '@aws-cdk/assert';
1+
import { expect, haveResource } from '@aws-cdk/assert';
22
import iam = require('@aws-cdk/aws-iam');
33
import kms = require('@aws-cdk/aws-kms');
44
import cdk = require('@aws-cdk/cdk');
55
import { Test } from 'nodeunit';
66
import s3 = require('../lib');
7-
import { Bucket } from '../lib';
87

98
// to make it easy to copy & paste from output:
109
// tslint:disable:object-literal-key-quotes
@@ -963,7 +962,7 @@ export = {
963962

964963
'urlForObject returns a token with the S3 URL of the token'(test: Test) {
965964
const stack = new cdk.Stack();
966-
const bucket = new Bucket(stack, 'MyBucket');
965+
const bucket = new s3.Bucket(stack, 'MyBucket');
967966

968967
new cdk.Output(stack, 'BucketURL', { value: bucket.bucketUrl });
969968
new cdk.Output(stack, 'MyFileURL', { value: bucket.urlForObject('my/file.txt') });
@@ -1059,5 +1058,111 @@ export = {
10591058
});
10601059

10611060
test.done();
1061+
},
1062+
1063+
'grantPublicAccess': {
1064+
'by default, grants s3:GetObject to all objects'(test: Test) {
1065+
// GIVEN
1066+
const stack = new cdk.Stack();
1067+
const bucket = new s3.Bucket(stack, 'b');
1068+
1069+
// WHEN
1070+
bucket.grantPublicAccess();
1071+
1072+
// THEN
1073+
expect(stack).to(haveResource('AWS::S3::BucketPolicy', {
1074+
"PolicyDocument": {
1075+
"Statement": [
1076+
{
1077+
"Action": "s3:GetObject",
1078+
"Effect": "Allow",
1079+
"Principal": "*",
1080+
"Resource": { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "bC3BBCC65", "Arn" ] }, "/", "*" ] ] }
1081+
}
1082+
],
1083+
"Version": "2012-10-17"
1084+
}
1085+
}));
1086+
test.done();
1087+
},
1088+
1089+
'"keyPrefix" can be used to only grant access to certain objects'(test: Test) {
1090+
// GIVEN
1091+
const stack = new cdk.Stack();
1092+
const bucket = new s3.Bucket(stack, 'b');
1093+
1094+
// WHEN
1095+
bucket.grantPublicAccess('only/access/these/*');
1096+
1097+
// THEN
1098+
expect(stack).to(haveResource('AWS::S3::BucketPolicy', {
1099+
"PolicyDocument": {
1100+
"Statement": [
1101+
{
1102+
"Action": "s3:GetObject",
1103+
"Effect": "Allow",
1104+
"Principal": "*",
1105+
"Resource": { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "bC3BBCC65", "Arn" ] }, "/", "only/access/these/*" ] ] }
1106+
}
1107+
],
1108+
"Version": "2012-10-17"
1109+
}
1110+
}));
1111+
test.done();
1112+
},
1113+
1114+
'"allowedActions" can be used to specify actions explicitly'(test: Test) {
1115+
// GIVEN
1116+
const stack = new cdk.Stack();
1117+
const bucket = new s3.Bucket(stack, 'b');
1118+
1119+
// WHEN
1120+
bucket.grantPublicAccess('*', 's3:GetObject', 's3:PutObject');
1121+
1122+
// THEN
1123+
expect(stack).to(haveResource('AWS::S3::BucketPolicy', {
1124+
"PolicyDocument": {
1125+
"Statement": [
1126+
{
1127+
"Action": [ "s3:GetObject", "s3:PutObject" ],
1128+
"Effect": "Allow",
1129+
"Principal": "*",
1130+
"Resource": { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "bC3BBCC65", "Arn" ] }, "/", "*" ] ] }
1131+
}
1132+
],
1133+
"Version": "2012-10-17"
1134+
}
1135+
}));
1136+
test.done();
1137+
},
1138+
1139+
'returns the PolicyStatement which can be then customized'(test: Test) {
1140+
// GIVEN
1141+
const stack = new cdk.Stack();
1142+
const bucket = new s3.Bucket(stack, 'b');
1143+
1144+
// WHEN
1145+
const statement = bucket.grantPublicAccess();
1146+
statement.addCondition('IpAddress', { "aws:SourceIp": "54.240.143.0/24" });
1147+
1148+
// THEN
1149+
expect(stack).to(haveResource('AWS::S3::BucketPolicy', {
1150+
"PolicyDocument": {
1151+
"Statement": [
1152+
{
1153+
"Action": "s3:GetObject",
1154+
"Effect": "Allow",
1155+
"Principal": "*",
1156+
"Resource": { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "bC3BBCC65", "Arn" ] }, "/", "*" ] ] },
1157+
"Condition": {
1158+
"IpAddress": { "aws:SourceIp": "54.240.143.0/24" }
1159+
}
1160+
}
1161+
],
1162+
"Version": "2012-10-17"
1163+
}
1164+
}));
1165+
test.done();
1166+
}
10621167
}
10631168
};

0 commit comments

Comments
 (0)