-
Notifications
You must be signed in to change notification settings - Fork 4k
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
s3: bucketUrl and urlForObject(key) #370
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import { applyRemovalPolicy, Arn, Construct, FnConcat, Output, PolicyStatement, RemovalPolicy, Token } from '@aws-cdk/core'; | ||
import { IIdentityResource } from '@aws-cdk/iam'; | ||
import * as kms from '@aws-cdk/kms'; | ||
import cdk = require('@aws-cdk/core'); | ||
import iam = require('@aws-cdk/iam'); | ||
import kms = require('@aws-cdk/kms'); | ||
import { BucketPolicy } from './bucket-policy'; | ||
import * as perms from './perms'; | ||
import { LifecycleRule } from './rule'; | ||
|
@@ -45,7 +45,7 @@ export interface BucketRefProps { | |
* BucketRef.import(this, 'MyImportedBucket', ref); | ||
* | ||
*/ | ||
export abstract class BucketRef extends Construct { | ||
export abstract class BucketRef extends cdk.Construct { | ||
/** | ||
* Creates a Bucket construct that represents an external bucket. | ||
* | ||
|
@@ -54,7 +54,7 @@ export abstract class BucketRef extends Construct { | |
* @param ref A BucketRefProps object. Can be obtained from a call to | ||
* `bucket.export()`. | ||
*/ | ||
public static import(parent: Construct, name: string, props: BucketRefProps): BucketRef { | ||
public static import(parent: cdk.Construct, name: string, props: BucketRefProps): BucketRef { | ||
return new ImportedBucketRef(parent, name, props); | ||
} | ||
|
||
|
@@ -92,8 +92,8 @@ export abstract class BucketRef extends Construct { | |
*/ | ||
public export(): BucketRefProps { | ||
return { | ||
bucketArn: new Output(this, 'BucketArn', { value: this.bucketArn }).makeImportValue(), | ||
bucketName: new Output(this, 'BucketName', { value: this.bucketName }).makeImportValue(), | ||
bucketArn: new cdk.Output(this, 'BucketArn', { value: this.bucketArn }).makeImportValue(), | ||
bucketName: new cdk.Output(this, 'BucketName', { value: this.bucketName }).makeImportValue(), | ||
}; | ||
} | ||
|
||
|
@@ -103,7 +103,7 @@ export abstract class BucketRef extends Construct { | |
* contents. Use `bucketArn` and `arnForObjects(keys)` to obtain ARNs for | ||
* this bucket or objects. | ||
*/ | ||
public addToResourcePolicy(permission: PolicyStatement) { | ||
public addToResourcePolicy(permission: cdk.PolicyStatement) { | ||
if (!this.policy && this.autoCreatePolicy) { | ||
this.policy = new BucketPolicy(this, 'Policy', { bucket: this }); | ||
} | ||
|
@@ -113,6 +113,38 @@ export abstract class BucketRef extends Construct { | |
} | ||
} | ||
|
||
/** | ||
* The https:// URL of this bucket. | ||
* @example https://s3.us-west-1.amazonaws.com/onlybucket | ||
* Similar to calling `urlForObject` with no object key. | ||
*/ | ||
public get bucketUrl() { | ||
return this.urlForObject(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have expected the dependency to be in the reverse order of this... Also, any reason to not use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, in order for this to be available for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This will result in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only when using a |
||
} | ||
|
||
/** | ||
* The https URL of an S3 object. For example: | ||
* @example https://s3.us-west-1.amazonaws.com/onlybucket | ||
* @example https://s3.us-west-1.amazonaws.com/bucket/key | ||
* @example https://s3.cn-north-1.amazonaws.com.cn/china-bucket/mykey | ||
* @param key The S3 key of the object. If not specified, the URL of the | ||
* bucket is returned. | ||
* @returns an ObjectS3Url token | ||
*/ | ||
public urlForObject(key?: any): S3Url { | ||
const components = [ 'https://', 's3.', new cdk.AwsRegion(), '.', new cdk.AwsURLSuffix(), '/', this.bucketName ]; | ||
if (key) { | ||
// trim prepending '/' | ||
if (typeof key === 'string' && key.startsWith('/')) { | ||
key = key.substr(1); | ||
} | ||
components.push('/'); | ||
components.push(key); | ||
} | ||
|
||
return new cdk.FnConcat(...components); | ||
} | ||
|
||
/** | ||
* Returns an ARN that represents all objects within the bucket that match | ||
* the key pattern specified. To represent all keys, specify ``"*"``. | ||
|
@@ -122,8 +154,8 @@ export abstract class BucketRef extends Construct { | |
* arnForObjects('home/', team, '/', user, '/*') | ||
* | ||
*/ | ||
public arnForObjects(...keyPattern: any[]): Arn { | ||
return new FnConcat(this.bucketArn, '/', ...keyPattern); | ||
public arnForObjects(...keyPattern: any[]): cdk.Arn { | ||
return new cdk.FnConcat(this.bucketArn, '/', ...keyPattern); | ||
} | ||
|
||
/** | ||
|
@@ -133,7 +165,7 @@ export abstract class BucketRef extends Construct { | |
* If an encryption key is used, permission to ues the key to decrypt the | ||
* contents of the bucket will also be granted. | ||
*/ | ||
public grantRead(identity?: IIdentityResource, objectsKeyPattern = '*') { | ||
public grantRead(identity?: iam.IPrincipal, objectsKeyPattern: any = '*') { | ||
if (!identity) { | ||
return; | ||
} | ||
|
@@ -147,7 +179,7 @@ export abstract class BucketRef extends Construct { | |
* If an encryption key is used, permission to use the key for | ||
* encrypt/decrypt will also be granted. | ||
*/ | ||
public grantReadWrite(identity?: IIdentityResource, objectsKeyPattern = '*') { | ||
public grantReadWrite(identity?: iam.IPrincipal, objectsKeyPattern: any = '*') { | ||
if (!identity) { | ||
return; | ||
} | ||
|
@@ -156,24 +188,24 @@ export abstract class BucketRef extends Construct { | |
this.grant(identity, objectsKeyPattern, bucketActions, keyActions); | ||
} | ||
|
||
private grant(identity: IIdentityResource, objectsKeyPattern: string, bucketActions: string[], keyActions: string[]) { | ||
private grant(identity: iam.IPrincipal, objectsKeyPattern: any, bucketActions: string[], keyActions: string[]) { | ||
const resources = [ | ||
this.bucketArn, | ||
this.arnForObjects(objectsKeyPattern) | ||
]; | ||
|
||
identity.addToPolicy(new PolicyStatement() | ||
identity.addToPolicy(new cdk.PolicyStatement() | ||
.addResources(...resources) | ||
.addActions(...bucketActions)); | ||
|
||
// grant key permissions if there's an associated key. | ||
if (this.encryptionKey) { | ||
// KMS permissions need to be granted both directions | ||
identity.addToPolicy(new PolicyStatement() | ||
identity.addToPolicy(new cdk.PolicyStatement() | ||
.addResource(this.encryptionKey.keyArn) | ||
.addActions(...keyActions)); | ||
|
||
this.encryptionKey.addToResourcePolicy(new PolicyStatement() | ||
this.encryptionKey.addToResourcePolicy(new cdk.PolicyStatement() | ||
.addResource('*') | ||
.addPrincipal(identity.principal) | ||
.addActions(...keyActions)); | ||
|
@@ -216,7 +248,7 @@ export interface BucketProps { | |
* | ||
* @default By default, the bucket will be destroyed if it is removed from the stack. | ||
*/ | ||
removalPolicy?: RemovalPolicy; | ||
removalPolicy?: cdk.RemovalPolicy; | ||
|
||
/** | ||
* The bucket policy associated with this bucket. | ||
|
@@ -258,7 +290,7 @@ export class Bucket extends BucketRef { | |
private readonly lifecycleRules: LifecycleRule[] = []; | ||
private readonly versioned?: boolean; | ||
|
||
constructor(parent: Construct, name: string, props: BucketProps = {}) { | ||
constructor(parent: cdk.Construct, name: string, props: BucketProps = {}) { | ||
super(parent, name); | ||
|
||
validateBucketName(props && props.bucketName); | ||
|
@@ -269,10 +301,10 @@ export class Bucket extends BucketRef { | |
bucketName: props && props.bucketName, | ||
bucketEncryption, | ||
versioningConfiguration: props.versioned ? { status: 'Enabled' } : undefined, | ||
lifecycleConfiguration: new Token(() => this.parseLifecycleConfiguration()), | ||
lifecycleConfiguration: new cdk.Token(() => this.parseLifecycleConfiguration()), | ||
}); | ||
|
||
applyRemovalPolicy(resource, props.removalPolicy); | ||
cdk.applyRemovalPolicy(resource, props.removalPolicy); | ||
|
||
this.versioned = props.versioned; | ||
this.policy = props.policy; | ||
|
@@ -435,7 +467,21 @@ export enum BucketEncryption { | |
/** | ||
* The name of the bucket. | ||
*/ | ||
export class BucketName extends Token { | ||
export class BucketName extends cdk.Token { | ||
|
||
} | ||
|
||
/** | ||
* A key to an S3 object. | ||
*/ | ||
export class ObjectKey extends cdk.Token { | ||
|
||
} | ||
|
||
/** | ||
* The web URL (https://s3.us-west-1.amazonaws.com/bucket/key) of an S3 object. | ||
*/ | ||
export class S3Url extends cdk.Token { | ||
|
||
} | ||
|
||
|
@@ -447,7 +493,7 @@ class ImportedBucketRef extends BucketRef { | |
protected policy?: BucketPolicy; | ||
protected autoCreatePolicy: boolean; | ||
|
||
constructor(parent: Construct, name: string, props: BucketRefProps) { | ||
constructor(parent: cdk.Construct, name: string, props: BucketRefProps) { | ||
super(parent, name); | ||
|
||
this.bucketArn = parseBucketArn(props); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
{ | ||
"Resources": { | ||
"MyBucketF68F3FF0": { | ||
"Type": "AWS::S3::Bucket" | ||
} | ||
}, | ||
"Outputs": { | ||
"BucketURL": { | ||
"Value": { | ||
"Fn::Join": [ | ||
"", | ||
[ | ||
"https://", | ||
"s3.", | ||
{ | ||
"Ref": "AWS::Region" | ||
}, | ||
".", | ||
{ | ||
"Ref": "AWS::URLSuffix" | ||
}, | ||
"/", | ||
{ | ||
"Ref": "MyBucketF68F3FF0" | ||
} | ||
] | ||
] | ||
}, | ||
"Export": { | ||
"Name": "aws-cdk-s3-urls:BucketURL" | ||
} | ||
}, | ||
"ObjectURL": { | ||
"Value": { | ||
"Fn::Join": [ | ||
"", | ||
[ | ||
"https://", | ||
"s3.", | ||
{ | ||
"Ref": "AWS::Region" | ||
}, | ||
".", | ||
{ | ||
"Ref": "AWS::URLSuffix" | ||
}, | ||
"/", | ||
{ | ||
"Ref": "MyBucketF68F3FF0" | ||
}, | ||
"/", | ||
"myfolder/myfile.txt" | ||
] | ||
] | ||
}, | ||
"Export": { | ||
"Name": "aws-cdk-s3-urls:ObjectURL" | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import cdk = require('@aws-cdk/core'); | ||
import s3 = require('../lib'); | ||
|
||
class TestStack extends cdk.Stack { | ||
constructor(parent: cdk.App, id: string) { | ||
super(parent, id); | ||
|
||
/// !show | ||
const bucket = new s3.Bucket(this, 'MyBucket'); | ||
|
||
new cdk.Output(this, 'BucketURL', { value: bucket.bucketUrl }); | ||
new cdk.Output(this, 'ObjectURL', { value: bucket.urlForObject('myfolder/myfile.txt') }); | ||
/// !hide | ||
} | ||
} | ||
|
||
const app = new cdk.App(process.argv); | ||
new TestStack(app, 'aws-cdk-s3-urls'); | ||
process.stdout.write(app.run()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!