Skip to content

Commit

Permalink
Merge branch 'main' into feature/expose-apssync-custom-domain
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Aug 11, 2022
2 parents 74c045e + c81068b commit e91882e
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 16 deletions.
2 changes: 0 additions & 2 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ export interface UserPoolSESOptions {
* region in which the UserPool is deployed, you must specify that
* region here.
*
* Must be 'us-east-1', 'us-west-2', or 'eu-west-1'
*
* @default - The same region as the Cognito UserPool
*/
readonly sesRegion?: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-lambda-event-sources/lib/kafka.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export interface SelfManagedKafkaEventSourceProps extends KafkaEventSourceProps
*
* @default - none
*/
readonly rootCACertificate?: secretsmanager.Secret;
readonly rootCACertificate?: secretsmanager.ISecret;
}

/**
Expand Down
32 changes: 32 additions & 0 deletions packages/@aws-cdk/aws-lambda-event-sources/test/kafka.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,38 @@ describe('KafkaEventSource', () => {
});
});

test('rootCACertificate can be ISecret', () => {
// GIVEN
const stack = new cdk.Stack();
const fn = new TestFunction(stack, 'Fn');
const kafkaTopic = 'some-topic';
const mockRootCACertificateSecretArn = 'arn:aws:secretsmanager:us-east-1:012345678901:secret:mock';
const rootCACertificate = Secret.fromSecretPartialArn(stack, 'RootCASecret', mockRootCACertificateSecretArn);
const bootstrapServers = ['kafka-broker:9092'];
const sg = SecurityGroup.fromSecurityGroupId(stack, 'SecurityGroup', 'sg-0123456789');
const vpc = new Vpc(stack, 'Vpc');

// WHEN
fn.addEventSource(new sources.SelfManagedKafkaEventSource(
{
bootstrapServers: bootstrapServers,
topic: kafkaTopic,
startingPosition: lambda.StartingPosition.TRIM_HORIZON,
vpc: vpc,
vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_NAT },
securityGroup: sg,
rootCACertificate: rootCACertificate,
}));

Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', {
SourceAccessConfigurations: Match.arrayWith([
{
Type: 'SERVER_ROOT_CA_CERTIFICATE',
URI: mockRootCACertificateSecretArn,
},
]),
});
});

test('ManagedKafkaEventSource name conforms to construct id rules', () => {
// GIVEN
Expand Down
56 changes: 48 additions & 8 deletions packages/@aws-cdk/aws-s3/lib/bucket-policy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { PolicyDocument } from '@aws-cdk/aws-iam';
import { RemovalPolicy, Resource } from '@aws-cdk/core';
import { RemovalPolicy, Resource, Token, Tokenization } from '@aws-cdk/core';
import { CfnReference } from '@aws-cdk/core/lib/private/cfn-reference';
import { Construct } from 'constructs';
import { IBucket } from './bucket';
import { CfnBucketPolicy } from './s3.generated';
import { Bucket, IBucket } from './bucket';
import { CfnBucket, CfnBucketPolicy } from './s3.generated';

export interface BucketPolicyProps {
/**
Expand Down Expand Up @@ -33,6 +34,45 @@ export interface BucketPolicyProps {
* Prefer to use `addToResourcePolicy()` instead.
*/
export class BucketPolicy extends Resource {
/**
* Create a mutable {@link BucketPolicy} from a {@link CfnBucketPolicy}.
*/
public static fromCfnBucketPolicy(cfnBucketPolicy: CfnBucketPolicy): BucketPolicy {
// use a "weird" id that has a higher chance of being unique
const id = '@FromCfnBucketPolicy';

// if fromCfnBucketPolicy() was already called on this CfnBucketPolicy,
// return the same L2
// (as different L2s would conflict, because of the mutation of the document property of the L1 below)
const existing = cfnBucketPolicy.node.tryFindChild(id);
if (existing) {
return <BucketPolicy>existing;
}

// resolve the Bucket this Policy references
let bucket: IBucket | undefined;
if (Token.isUnresolved(cfnBucketPolicy.bucket)) {
const bucketIResolvable = Tokenization.reverse(cfnBucketPolicy.bucket);
if (bucketIResolvable instanceof CfnReference) {
const cfnElement = bucketIResolvable.target;
if (cfnElement instanceof CfnBucket) {
bucket = Bucket.fromCfnBucket(cfnElement);
}
}
}
if (!bucket) {
bucket = Bucket.fromBucketName(cfnBucketPolicy, '@FromCfnBucket', cfnBucketPolicy.bucket);
}

const ret = new class extends BucketPolicy {
public readonly document = PolicyDocument.fromJson(cfnBucketPolicy.policyDocument);
}(cfnBucketPolicy, id, {
bucket,
});
// mark the Bucket as having this Policy
bucket.policy = ret;
return ret;
}

/**
* A policy document containing permissions to add to the specified bucket.
Expand All @@ -41,17 +81,18 @@ export class BucketPolicy extends Resource {
*/
public readonly document = new PolicyDocument();

/** The Bucket this Policy applies to. */
public readonly bucket: IBucket;

private resource: CfnBucketPolicy;

constructor(scope: Construct, id: string, props: BucketPolicyProps) {
super(scope, id);

if (!props.bucket.bucketName) {
throw new Error('Bucket doesn\'t have a bucketName defined');
}
this.bucket = props.bucket;

this.resource = new CfnBucketPolicy(this, 'Resource', {
bucket: props.bucket.bucketName,
bucket: this.bucket.bucketName,
policyDocument: this.document,
});

Expand All @@ -67,5 +108,4 @@ export class BucketPolicy extends Resource {
public applyRemovalPolicy(removalPolicy: RemovalPolicy) {
this.resource.applyRemovalPolicy(removalPolicy);
}

}
61 changes: 60 additions & 1 deletion packages/@aws-cdk/aws-s3/lib/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import {
Stack,
Tags,
Token,
Tokenization,
} from '@aws-cdk/core';
import { CfnReference } from '@aws-cdk/core/lib/private/cfn-reference';
import * as cxapi from '@aws-cdk/cx-api';
import { Construct } from 'constructs';
import { BucketPolicy } from './bucket-policy';
Expand Down Expand Up @@ -1575,7 +1577,6 @@ export interface Tag {
*
*/
export class Bucket extends BucketBase {

public static fromBucketArn(scope: Construct, id: string, bucketArn: string): IBucket {
return Bucket.fromBucketAttributes(scope, id, { bucketArn });
}
Expand Down Expand Up @@ -1641,6 +1642,64 @@ export class Bucket extends BucketBase {
});
}

/**
* Create a mutable {@link IBucket} based on a low-level {@link CfnBucket}.
*/
public static fromCfnBucket(cfnBucket: CfnBucket): IBucket {
// use a "weird" id that has a higher chance of being unique
const id = '@FromCfnBucket';

// if fromCfnBucket() was already called on this cfnBucket,
// return the same L2
// (as different L2s would conflict, because of the mutation of the policy property of the L1 below)
const existing = cfnBucket.node.tryFindChild(id);
if (existing) {
return <IBucket>existing;
}

// handle the KMS Key if the Bucket references one
let encryptionKey: kms.IKey | undefined;
if (cfnBucket.bucketEncryption) {
const serverSideEncryptionConfiguration = (cfnBucket.bucketEncryption as any).serverSideEncryptionConfiguration;
if (Array.isArray(serverSideEncryptionConfiguration) && serverSideEncryptionConfiguration.length === 1) {
const serverSideEncryptionRuleProperty = serverSideEncryptionConfiguration[0];
const serverSideEncryptionByDefault = serverSideEncryptionRuleProperty.serverSideEncryptionByDefault;
if (serverSideEncryptionByDefault && Token.isUnresolved(serverSideEncryptionByDefault.kmsMasterKeyId)) {
const kmsIResolvable = Tokenization.reverse(serverSideEncryptionByDefault.kmsMasterKeyId);
if (kmsIResolvable instanceof CfnReference) {
const cfnElement = kmsIResolvable.target;
if (cfnElement instanceof kms.CfnKey) {
encryptionKey = kms.Key.fromCfnKey(cfnElement);
}
}
}
}
}

return new class extends BucketBase {
public readonly bucketArn = cfnBucket.attrArn;
public readonly bucketName = cfnBucket.ref;
public readonly bucketDomainName = cfnBucket.attrDomainName;
public readonly bucketDualStackDomainName = cfnBucket.attrDualStackDomainName;
public readonly bucketRegionalDomainName = cfnBucket.attrRegionalDomainName;
public readonly bucketWebsiteUrl = cfnBucket.attrWebsiteUrl;
public readonly bucketWebsiteDomainName = Fn.select(2, Fn.split('/', cfnBucket.attrWebsiteUrl));

public readonly encryptionKey = encryptionKey;
public readonly isWebsite = cfnBucket.websiteConfiguration !== undefined;
public policy = undefined;
protected autoCreatePolicy = true;
protected disallowPublicAccess = cfnBucket.publicAccessBlockConfiguration &&
(cfnBucket.publicAccessBlockConfiguration as any).blockPublicPolicy;

constructor() {
super(cfnBucket, id);

this.node.defaultChild = cfnBucket;
}
}();
}

/**
* Thrown an exception if the given bucket name is not valid.
*
Expand Down
47 changes: 46 additions & 1 deletion packages/@aws-cdk/aws-s3/test/bucket-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Template } from '@aws-cdk/assertions';
import { AnyPrincipal, PolicyStatement } from '@aws-cdk/aws-iam';
import { RemovalPolicy, Stack, App } from '@aws-cdk/core';
import * as s3 from '../lib';
import { CfnBucketPolicy } from '../lib';

// to make it easy to copy & paste from output:
/* eslint-disable quote-props */
Expand Down Expand Up @@ -150,4 +151,48 @@ describe('bucket policy', () => {

expect(() => app.synth()).toThrow(/A PolicyStatement used in a resource-based policy must specify at least one IAM principal/);
});
});

describe('fromCfnBucketPolicy()', () => {
const stack = new Stack();

test('correctly extracts the Document and Bucket from the L1', () => {
const cfnBucket = new s3.CfnBucket(stack, 'CfnBucket');
const cfnBucketPolicy = bucketPolicyForBucketNamed(cfnBucket.ref);
const bucketPolicy = s3.BucketPolicy.fromCfnBucketPolicy(cfnBucketPolicy);

expect(bucketPolicy.document).not.toBeUndefined();
expect(bucketPolicy.document.isEmpty).toBeFalsy();

expect(bucketPolicy.bucket).not.toBeUndefined();
expect(bucketPolicy.bucket.policy).not.toBeUndefined();
expect(bucketPolicy.bucket.policy?.document.isEmpty).toBeFalsy();
});

test('correctly references a bucket by name', () => {
const cfnBucketPolicy = bucketPolicyForBucketNamed('hardcoded-name');
const bucketPolicy = s3.BucketPolicy.fromCfnBucketPolicy(cfnBucketPolicy);

expect(bucketPolicy.bucket).not.toBeUndefined();
expect(bucketPolicy.bucket.bucketName).toBe('hardcoded-name');
});

function bucketPolicyForBucketNamed(name: string): CfnBucketPolicy {
return new s3.CfnBucketPolicy(stack, `CfnBucketPolicy-${name}`, {
policyDocument: {
'Statement': [
{
'Action': 's3:*',
'Effect': 'Deny',
'Principal': {
'AWS': '*',
},
'Resource': '*',
},
],
'Version': '2012-10-17',
},
bucket: name,
});
}
});
});
Loading

0 comments on commit e91882e

Please sign in to comment.