Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws-s3): support granting public access to objects #886

Merged
merged 1 commit into from
Oct 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/@aws-cdk/aws-s3/lib/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,41 @@ export abstract class BucketRef extends cdk.Construct {
this.arnForObjects(objectsKeyPattern));
}

/**
* Allows unrestricted access to objects from this bucket.
*
* IMPORTANT: This permission allows anyone to perform actions on S3 objects
* in this bucket, which is useful for when you configure your bucket as a
* website and want everyone to be able to read objects in the bucket without
* needing to authenticate.
*
* Without arguments, this method will grant read ("s3:GetObject") access to
* all objects ("*") in the bucket.
*
* The method returns the `iam.PolicyStatement` object, which can then be modified
* as needed. For example, you can add a condition that will restrict access only
* to an IPv4 range like this:
*
* const statement = bucket.grantPublicAccess();
* statement.addCondition('IpAddress', { "aws:SourceIp": "54.240.143.0/24" });
*
*
* @param keyPrefix the prefix of S3 object keys (e.g. `home/*`). Default is "*".
* @param allowedActions the set of S3 actions to allow. Default is "s3:GetObject".
* @returns The `iam.PolicyStatement` object, which can be used to apply e.g. conditions.
*/
public grantPublicAccess(keyPrefix = '*', ...allowedActions: string[]): iam.PolicyStatement {
allowedActions = allowedActions.length > 0 ? allowedActions : [ 's3:GetObject' ];

const statement = new iam.PolicyStatement()
.addActions(...allowedActions)
.addResource(this.arnForObjects(keyPrefix))
.addPrincipal(new iam.Anyone());

this.addToResourcePolicy(statement);
return statement;
}

private grant(identity: iam.IPrincipal | undefined,
bucketActions: string[],
keyActions: string[],
Expand Down
111 changes: 108 additions & 3 deletions packages/@aws-cdk/aws-s3/test/test.bucket.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { expect } from '@aws-cdk/assert';
import { expect, haveResource } from '@aws-cdk/assert';
import iam = require('@aws-cdk/aws-iam');
import kms = require('@aws-cdk/aws-kms');
import cdk = require('@aws-cdk/cdk');
import { Test } from 'nodeunit';
import s3 = require('../lib');
import { Bucket } from '../lib';

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

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

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

test.done();
},

'grantPublicAccess': {
'by default, grants s3:GetObject to all objects'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const bucket = new s3.Bucket(stack, 'b');

// WHEN
bucket.grantPublicAccess();

// THEN
expect(stack).to(haveResource('AWS::S3::BucketPolicy', {
"PolicyDocument": {
"Statement": [
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Principal": "*",
"Resource": { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "bC3BBCC65", "Arn" ] }, "/", "*" ] ] }
}
],
"Version": "2012-10-17"
}
}));
test.done();
},

'"keyPrefix" can be used to only grant access to certain objects'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const bucket = new s3.Bucket(stack, 'b');

// WHEN
bucket.grantPublicAccess('only/access/these/*');

// THEN
expect(stack).to(haveResource('AWS::S3::BucketPolicy', {
"PolicyDocument": {
"Statement": [
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Principal": "*",
"Resource": { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "bC3BBCC65", "Arn" ] }, "/", "only/access/these/*" ] ] }
}
],
"Version": "2012-10-17"
}
}));
test.done();
},

'"allowedActions" can be used to specify actions explicitly'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const bucket = new s3.Bucket(stack, 'b');

// WHEN
bucket.grantPublicAccess('*', 's3:GetObject', 's3:PutObject');

// THEN
expect(stack).to(haveResource('AWS::S3::BucketPolicy', {
"PolicyDocument": {
"Statement": [
{
"Action": [ "s3:GetObject", "s3:PutObject" ],
"Effect": "Allow",
"Principal": "*",
"Resource": { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "bC3BBCC65", "Arn" ] }, "/", "*" ] ] }
}
],
"Version": "2012-10-17"
}
}));
test.done();
},

'returns the PolicyStatement which can be then customized'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const bucket = new s3.Bucket(stack, 'b');

// WHEN
const statement = bucket.grantPublicAccess();
statement.addCondition('IpAddress', { "aws:SourceIp": "54.240.143.0/24" });

// THEN
expect(stack).to(haveResource('AWS::S3::BucketPolicy', {
"PolicyDocument": {
"Statement": [
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Principal": "*",
"Resource": { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "bC3BBCC65", "Arn" ] }, "/", "*" ] ] },
"Condition": {
"IpAddress": { "aws:SourceIp": "54.240.143.0/24" }
}
}
],
"Version": "2012-10-17"
}
}));
test.done();
}
}
};