Skip to content

Commit fb639aa

Browse files
authored
Merge branch 'master' into guchangh/eks-call
2 parents d6d55e5 + ff1e5b3 commit fb639aa

File tree

76 files changed

+2320
-358
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2320
-358
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
## [1.87.1](https://github.com/aws/aws-cdk/compare/v1.87.0...v1.87.1) (2021-01-28)
6+
7+
8+
### Bug Fixes
9+
10+
* **apigateway:** stack update fails to replace api key ([38cbe62](https://github.com/aws/aws-cdk/commit/38cbe620859d6efabda95dbdd3185a480ab43894)), closes [#12698](https://github.com/aws/aws-cdk/issues/12698)
11+
512
## [1.87.0](https://github.com/aws/aws-cdk/compare/v1.86.0...v1.87.0) (2021-01-27)
613

714

packages/@aws-cdk/assert/lib/assertions/have-resource.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export class HaveResourceAssertion extends JestFriendlyAssertion<StackInspector>
6666
for (const logicalId of Object.keys(inspector.value.Resources || {})) {
6767
const resource = inspector.value.Resources[logicalId];
6868
if (resource.Type === this.resourceType) {
69-
const propsToCheck = this.part === ResourcePart.Properties ? resource.Properties : resource;
69+
const propsToCheck = this.part === ResourcePart.Properties ? (resource.Properties ?? {}) : resource;
7070

7171
// Pass inspection object as 2nd argument, initialize failure with default string,
7272
// to maintain backwards compatibility with old predicate API.

packages/@aws-cdk/aws-apigateway/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ running on AWS Lambda, or any web application.
3232
- [IAM-based authorizer](#iam-based-authorizer)
3333
- [Lambda-based token authorizer](#lambda-based-token-authorizer)
3434
- [Lambda-based request authorizer](#lambda-based-request-authorizer)
35+
- [Cognito User Pools authorizer](#cognito-user-pools-authorizer)
3536
- [Mutual TLS](#mutal-tls-mtls)
3637
- [Deployments](#deployments)
3738
- [Deep dive: Invalidation of deployments](#deep-dive-invalidation-of-deployments)
@@ -580,6 +581,25 @@ Authorizers can also be passed via the `defaultMethodOptions` property within th
580581
explicitly overridden, the specified defaults will be applied across all `Method`s across the `RestApi` or across all `Resource`s,
581582
depending on where the defaults were specified.
582583

584+
### Cognito User Pools authorizer
585+
586+
API Gateway also allows [Amazon Cognito user pools as authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html)
587+
588+
The following snippet configures a Cognito user pool as an authorizer:
589+
590+
```ts
591+
const userPool = new cognito.UserPool(stack, 'UserPool');
592+
593+
const auth = new apigateway.CognitoUserPoolsAuthorizer(this, 'booksAuthorizer', {
594+
cognitoUserPools: [userPool]
595+
});
596+
597+
books.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com'), {
598+
authorizer: auth,
599+
authorizationType: apigateway.AuthorizationType.COGNITO,
600+
});
601+
```
602+
583603
## Mutual TLS (mTLS)
584604

585605
Mutual TLS can be configured to limit access to your API based by using client certificates instead of (or as an extension of) using authorization headers.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import * as cognito from '@aws-cdk/aws-cognito';
2+
import { Duration, Lazy, Names, Stack } from '@aws-cdk/core';
3+
import { Construct } from 'constructs';
4+
import { CfnAuthorizer } from '../apigateway.generated';
5+
import { Authorizer, IAuthorizer } from '../authorizer';
6+
import { AuthorizationType } from '../method';
7+
import { IRestApi } from '../restapi';
8+
9+
/**
10+
* Properties for CognitoUserPoolsAuthorizer
11+
*/
12+
export interface CognitoUserPoolsAuthorizerProps {
13+
/**
14+
* An optional human friendly name for the authorizer. Note that, this is not the primary identifier of the authorizer.
15+
*
16+
* @default - the unique construct ID
17+
*/
18+
readonly authorizerName?: string;
19+
20+
/**
21+
* The user pools to associate with this authorizer.
22+
*/
23+
readonly cognitoUserPools: cognito.IUserPool[];
24+
25+
/**
26+
* How long APIGateway should cache the results. Max 1 hour.
27+
* Disable caching by setting this to 0.
28+
*
29+
* @default Duration.minutes(5)
30+
*/
31+
readonly resultsCacheTtl?: Duration;
32+
33+
/**
34+
* The request header mapping expression for the bearer token. This is typically passed as part of the header, in which case
35+
* this should be `method.request.header.Authorizer` where Authorizer is the header containing the bearer token.
36+
* @see https://docs.aws.amazon.com/apigateway/api-reference/link-relation/authorizer-create/#identitySource
37+
* @default `IdentitySource.header('Authorization')`
38+
*/
39+
readonly identitySource?: string;
40+
}
41+
42+
/**
43+
* Cognito user pools based custom authorizer
44+
*
45+
* @resource AWS::ApiGateway::Authorizer
46+
*/
47+
export class CognitoUserPoolsAuthorizer extends Authorizer implements IAuthorizer {
48+
/**
49+
* The id of the authorizer.
50+
* @attribute
51+
*/
52+
public readonly authorizerId: string;
53+
54+
/**
55+
* The ARN of the authorizer to be used in permission policies, such as IAM and resource-based grants.
56+
* @attribute
57+
*/
58+
public readonly authorizerArn: string;
59+
60+
/**
61+
* The authorization type of this authorizer.
62+
*/
63+
public readonly authorizationType?: AuthorizationType;
64+
65+
private restApiId?: string;
66+
67+
constructor(scope: Construct, id: string, props: CognitoUserPoolsAuthorizerProps) {
68+
super(scope, id);
69+
70+
const restApiId = this.lazyRestApiId();
71+
const resource = new CfnAuthorizer(this, 'Resource', {
72+
name: props.authorizerName ?? Names.uniqueId(this),
73+
restApiId,
74+
type: 'COGNITO_USER_POOLS',
75+
providerArns: props.cognitoUserPools.map(userPool => userPool.userPoolArn),
76+
authorizerResultTtlInSeconds: props.resultsCacheTtl?.toSeconds(),
77+
identitySource: props.identitySource || 'method.request.header.Authorization',
78+
});
79+
80+
this.authorizerId = resource.ref;
81+
this.authorizerArn = Stack.of(this).formatArn({
82+
service: 'execute-api',
83+
resource: restApiId,
84+
resourceName: `authorizers/${this.authorizerId}`,
85+
});
86+
this.authorizationType = AuthorizationType.COGNITO;
87+
}
88+
89+
/**
90+
* Attaches this authorizer to a specific REST API.
91+
* @internal
92+
*/
93+
public _attachToApi(restApi: IRestApi): void {
94+
if (this.restApiId && this.restApiId !== restApi.restApiId) {
95+
throw new Error('Cannot attach authorizer to two different rest APIs');
96+
}
97+
98+
this.restApiId = restApi.restApiId;
99+
}
100+
101+
/**
102+
* Returns a token that resolves to the Rest Api Id at the time of synthesis.
103+
* Throws an error, during token resolution, if no RestApi is attached to this authorizer.
104+
*/
105+
private lazyRestApiId() {
106+
return Lazy.string({
107+
produce: () => {
108+
if (!this.restApiId) {
109+
throw new Error(`Authorizer (${this.node.path}) must be attached to a RestApi`);
110+
}
111+
return this.restApiId;
112+
},
113+
});
114+
}
115+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './lambda';
22
export * from './identity-source';
3+
export * from './cognito';

packages/@aws-cdk/aws-apigateway/lib/resource.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,51 @@ export abstract class ResourceBase extends ResourceConstruct implements IResourc
373373
}
374374
}
375375

376+
/**
377+
* Attributes that can be specified when importing a Resource
378+
*/
379+
export interface ResourceAttributes {
380+
/**
381+
* The ID of the resource.
382+
*/
383+
readonly resourceId: string;
384+
385+
/**
386+
* The rest API that this resource is part of.
387+
*/
388+
readonly restApi: IRestApi;
389+
390+
/**
391+
* The full path of this resource.
392+
*/
393+
readonly path: string;
394+
}
395+
376396
export class Resource extends ResourceBase {
397+
/**
398+
* Import an existing resource
399+
*/
400+
public static fromResourceAttributes(scope: Construct, id: string, attrs: ResourceAttributes): IResource {
401+
class Import extends ResourceBase {
402+
public readonly api = attrs.restApi;
403+
public readonly resourceId = attrs.resourceId;
404+
public readonly path = attrs.path;
405+
public readonly defaultIntegration?: Integration = undefined;
406+
public readonly defaultMethodOptions?: MethodOptions = undefined;
407+
public readonly defaultCorsPreflightOptions?: CorsOptions = undefined;
408+
409+
public get parentResource(): IResource {
410+
throw new Error('parentResource is not configured for imported resource.');
411+
}
412+
413+
public get restApi(): RestApi {
414+
throw new Error('restApi is not configured for imported resource.');
415+
}
416+
}
417+
418+
return new Import(scope, id);
419+
}
420+
377421
public readonly parentResource?: IResource;
378422
public readonly api: IRestApi;
379423
public readonly resourceId: string;

packages/@aws-cdk/aws-apigateway/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"dependencies": {
8181
"@aws-cdk/aws-certificatemanager": "0.0.0",
8282
"@aws-cdk/aws-cloudwatch": "0.0.0",
83+
"@aws-cdk/aws-cognito": "0.0.0",
8384
"@aws-cdk/aws-ec2": "0.0.0",
8485
"@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
8586
"@aws-cdk/aws-iam": "0.0.0",
@@ -95,6 +96,7 @@
9596
"peerDependencies": {
9697
"@aws-cdk/aws-certificatemanager": "0.0.0",
9798
"@aws-cdk/aws-cloudwatch": "0.0.0",
99+
"@aws-cdk/aws-cognito": "0.0.0",
98100
"@aws-cdk/aws-ec2": "0.0.0",
99101
"@aws-cdk/aws-elasticloadbalancingv2": "0.0.0",
100102
"@aws-cdk/aws-iam": "0.0.0",
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import '@aws-cdk/assert/jest';
2+
import * as cognito from '@aws-cdk/aws-cognito';
3+
import { Duration, Stack } from '@aws-cdk/core';
4+
import { AuthorizationType, CognitoUserPoolsAuthorizer, RestApi } from '../../lib';
5+
6+
describe('Cognito Authorizer', () => {
7+
test('default cognito authorizer', () => {
8+
// GIVEN
9+
const stack = new Stack();
10+
const userPool = new cognito.UserPool(stack, 'UserPool');
11+
12+
// WHEN
13+
const authorizer = new CognitoUserPoolsAuthorizer(stack, 'myauthorizer', {
14+
cognitoUserPools: [userPool],
15+
});
16+
17+
const restApi = new RestApi(stack, 'myrestapi');
18+
restApi.root.addMethod('ANY', undefined, {
19+
authorizer,
20+
authorizationType: AuthorizationType.COGNITO,
21+
});
22+
23+
// THEN
24+
expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', {
25+
Type: 'COGNITO_USER_POOLS',
26+
RestApiId: stack.resolve(restApi.restApiId),
27+
IdentitySource: 'method.request.header.Authorization',
28+
ProviderARNs: [stack.resolve(userPool.userPoolArn)],
29+
});
30+
31+
expect(authorizer.authorizerArn.endsWith(`/authorizers/${authorizer.authorizerId}`)).toBeTruthy();
32+
});
33+
34+
test('cognito authorizer with all parameters specified', () => {
35+
// GIVEN
36+
const stack = new Stack();
37+
const userPool1 = new cognito.UserPool(stack, 'UserPool1');
38+
const userPool2 = new cognito.UserPool(stack, 'UserPool2');
39+
40+
// WHEN
41+
const authorizer = new CognitoUserPoolsAuthorizer(stack, 'myauthorizer', {
42+
cognitoUserPools: [userPool1, userPool2],
43+
identitySource: 'method.request.header.whoami',
44+
authorizerName: 'myauthorizer',
45+
resultsCacheTtl: Duration.minutes(1),
46+
});
47+
48+
const restApi = new RestApi(stack, 'myrestapi');
49+
restApi.root.addMethod('ANY', undefined, {
50+
authorizer,
51+
authorizationType: AuthorizationType.COGNITO,
52+
});
53+
54+
// THEN
55+
expect(stack).toHaveResource('AWS::ApiGateway::Authorizer', {
56+
Type: 'COGNITO_USER_POOLS',
57+
Name: 'myauthorizer',
58+
RestApiId: stack.resolve(restApi.restApiId),
59+
IdentitySource: 'method.request.header.whoami',
60+
AuthorizerResultTtlInSeconds: 60,
61+
ProviderARNs: [stack.resolve(userPool1.userPoolArn), stack.resolve(userPool2.userPoolArn)],
62+
});
63+
64+
expect(authorizer.authorizerArn.endsWith(`/authorizers/${authorizer.authorizerId}`)).toBeTruthy();
65+
});
66+
});

0 commit comments

Comments
 (0)