@liflig/cdk 3.0.21 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,500 @@
1
+ import * as constructs from "constructs";
2
+ import * as cdk from "aws-cdk-lib";
3
+ import type * as elb from "aws-cdk-lib/aws-elasticloadbalancingv2";
4
+ import type * as ec2 from "aws-cdk-lib/aws-ec2";
5
+ import * as lambda from "aws-cdk-lib/aws-lambda";
6
+ import type * as sqs from "aws-cdk-lib/aws-sqs";
7
+ import * as apigw from "aws-cdk-lib/aws-apigatewayv2";
8
+ import * as logs from "aws-cdk-lib/aws-logs";
9
+ import * as iam from "aws-cdk-lib/aws-iam";
10
+ import type { IUserPool } from "aws-cdk-lib/aws-cognito";
11
+ import * as route53 from "aws-cdk-lib/aws-route53";
12
+ /**
13
+ * Props for the {@link ApiGateway} construct.
14
+ *
15
+ * @author Kristian Rekstad <kre@capraconsulting.no>
16
+ * @author Hermann Mørkrid <hem@liflig.no>
17
+ */
18
+ export type ApiGatewayProps<AuthScopesT extends string = string> = {
19
+ /** Settings for the external-facing part of the API-GW. */
20
+ dns: ApiGatewayDnsProps;
21
+ /**
22
+ * If no integration is specified for a route, this integration is used.
23
+ *
24
+ * See {@link IntegrationProps} for the available options.
25
+ */
26
+ defaultIntegration?: IntegrationProps;
27
+ /**
28
+ * If no authorization is specified for a route, this authorization is used.
29
+ *
30
+ * See {@link AuthorizationProps} for the available options.
31
+ */
32
+ defaultAuthorization?: AuthorizationProps<AuthScopesT>;
33
+ routes: ApiGatewayRoute<AuthScopesT>[];
34
+ /**
35
+ * The API-GW access logs for the `$default` stage are kept.
36
+ * This has options for the logs.
37
+ */
38
+ accessLogs?: ApiGatewayAccessLogsProps;
39
+ /**
40
+ * Set to false to disable route-level metrics.
41
+ * This can increase CloudWatch costs when not disabled.
42
+ *
43
+ * See [AWS: Working with metrics for HTTP APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-metrics.html?icmpid=apigateway_console_help#:~:text=and%20stage%20ID.-,ApiId%2C%20Stage%2C%20Route,-Filters%20API%20Gateway)
44
+ * for more info.
45
+ *
46
+ * @default true
47
+ */
48
+ detailedMetrics?: boolean;
49
+ /**
50
+ * Throttling of requests.
51
+ * If not set, the AWS default of 5000 burst and 10000 rate is used.
52
+ *
53
+ * The default throttling is per-account, and counts all APIs in the account and region.
54
+ * If you have another API in this region getting 10_000 request rate, it may impact this API as well.
55
+ */
56
+ throttling?: {
57
+ /**
58
+ * Going over `5_000` may require you to contact AWS Support - they are account bound.
59
+ */
60
+ burst?: number;
61
+ /**
62
+ * Going over `10_000` may require you to contact AWS Support - they are account bound.
63
+ */
64
+ rate?: number;
65
+ };
66
+ /**
67
+ * Sets CORS headers on responses from the API Gateway to allow all origins, headers and methods.
68
+ * Useful if your API should be accessed by any browser.
69
+ */
70
+ corsAllowAll?: boolean;
71
+ /**
72
+ * If some settings in this construct do not work for you, this is an escape hatch mechanism to
73
+ * override anything.
74
+ */
75
+ propsOverride?: {
76
+ /**
77
+ * Override settings for the {@link apigw.HttpApi} (accessible in {@link ApiGateway.httpApi}).
78
+ *
79
+ * For example, if you have a frontend accessing this API, you might want to set
80
+ * [CORS preflight](https://docs.aws.amazon.com/cdk/api/v1/docs/aws-apigatewayv2-readme.html#cross-origin-resource-sharing-cors)
81
+ * settings.
82
+ */
83
+ httpApi?: Partial<apigw.HttpApiProps>;
84
+ };
85
+ };
86
+ export type ApiGatewayDnsProps = {
87
+ /**
88
+ * Only the subdomain prefix, which should be the name of the service.
89
+ * Example: Subdomain `product` would give the end result of an API-GW with
90
+ * `product.platform.example.no`.
91
+ */
92
+ subdomain: string;
93
+ /**
94
+ * Hosted Zone for the external facing domain.
95
+ * This is where routes will be created, to redirect consumers to the API-GW.
96
+ * For example a HZ for `platform.example.no`.
97
+ */
98
+ hostedZone: route53.IHostedZone;
99
+ /**
100
+ * The Time To Live (TTL) for the public DNS A record that will expose the API-GW.
101
+ * This is how long DNS servers will cache the record.
102
+ *
103
+ * A long TTL (hours) is beneficial to DNS servers, but makes developers (you) wait longer when
104
+ * doing changes.
105
+ *
106
+ * @default 5 minutes
107
+ */
108
+ ttl?: cdk.Duration;
109
+ };
110
+ export type ApiGatewayRoute<AuthScopesT extends string = string> = {
111
+ /** The path of the route to expose through the API Gateway. Use "/" for the root route. */
112
+ path: string;
113
+ /**
114
+ * By default, we only forward requests that match the route's path exactly. So for a route with
115
+ * path `/api/users`, a request to `/api/users` will be forwarded, but a request to
116
+ * `/api/users/admin` will not. If you want to forward requests to all sub-paths under the route's
117
+ * path, you can set this to true.
118
+ *
119
+ * @default false
120
+ */
121
+ includeSubpaths?: boolean;
122
+ /**
123
+ * The HTTP method to expose. `ANY` exposes all HTTP methods on the path.
124
+ *
125
+ * @default "ANY"
126
+ */
127
+ method?: HttpMethod;
128
+ /**
129
+ * The integration that the route will forward to. See {@link IntegrationProps} for the available
130
+ * options.
131
+ *
132
+ * If undefined, uses the {@link ApiGatewayProps.defaultIntegration}.
133
+ */
134
+ integration?: IntegrationProps;
135
+ /**
136
+ * How requests on the route are authenticated. See {@link AuthorizationProps} for the available
137
+ * options.
138
+ *
139
+ * If undefined, uses the {@link ApiGatewayProps.defaultAuthorization}.
140
+ */
141
+ authorization?: AuthorizationProps<AuthScopesT>;
142
+ };
143
+ export type HttpMethod = "ANY" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD";
144
+ export type IntegrationProps =
145
+ /** Use this when connecting the route to an ALB (Application Load Balancer). */
146
+ ({
147
+ type: "ALB";
148
+ } & AlbIntegrationProps)
149
+ /** Use this when connecting route to a Lambda. */
150
+ | ({
151
+ type: "Lambda";
152
+ } & LambdaIntegrationProps)
153
+ /** Use this when connecting a route to send to an SQS queue. */
154
+ | ({
155
+ type: "SQS";
156
+ } & SqsIntegrationProps);
157
+ /**
158
+ * Props for the API-GW -> ALB (Application Load Balancer) integration.
159
+ *
160
+ * See the note on {@link ApiGateway} about the load balancer security group.
161
+ */
162
+ export type AlbIntegrationProps = {
163
+ /**
164
+ * A listener on e.g. port 443 (HTTPS).
165
+ *
166
+ * See the note on {@link ApiGateway} about the load balancer security group.
167
+ */
168
+ loadBalancerListener: elb.IApplicationListener;
169
+ /**
170
+ * The host name (domain name) of the backend service that we want to reach through the ALB.
171
+ *
172
+ * This is used to:
173
+ * - Verify the HTTPS certificate of the backend service, so that the request forwarded from
174
+ * API-GW can use TLS
175
+ * - Set the `Host` header on the request when forwarding to the ALB, so that requests can be
176
+ * routed to the correct Target Group
177
+ *
178
+ * Example value: `<service>.staging.my-project.liflig.io` (not prefixed by `https://`).
179
+ */
180
+ hostName: string;
181
+ /**
182
+ * The VPC used by the ALB. The API-GW integration will connect to the ALB using a VPC Link for
183
+ * this VPC.
184
+ *
185
+ * See the note on {@link ApiGateway} about the load balancer security group.
186
+ */
187
+ vpc: ec2.IVpc;
188
+ /**
189
+ * A security group (SG) that allows incoming traffic to the ALB. Will be used by the VPC link, so
190
+ * the API-GW integration can connect.
191
+ *
192
+ * This is usually the same SG as the ALB uses, because they have a rule that allows traffic from
193
+ * others in the same SG.
194
+ *
195
+ * See the note on {@link ApiGateway} about the load balancer security group.
196
+ */
197
+ securityGroup: ec2.ISecurityGroup;
198
+ /**
199
+ * Map request parameters (add/overwrite path/headers/query params) before forwarding to the
200
+ * backend.
201
+ *
202
+ * See {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-parameter-mapping.html}
203
+ * for more on this. Read the 'Reserved headers' section for which headers cannot be overridden.
204
+ * In addition to the AWS-reserved headers, you should not override the 'Host' header either, as
205
+ * that's used for routing the request to the correct service behind the load balancer.
206
+ *
207
+ * ### Example:
208
+ *
209
+ * Adding a header:
210
+ * ```
211
+ * mapParameters: (parameters) => parameters.overwriteHeader(
212
+ * "X-My-Custom-Header",
213
+ * apigw.MappingValue.custom("my-custom-value"),
214
+ * )
215
+ * ```
216
+ *
217
+ * Overwriting the path (if, for example, you configure a `/users` route on the API Gateway that
218
+ * you want to forward to `/api/users` on the backend):
219
+ * ```
220
+ * mapParameters: (parameters) => parameters.overwritePath("/api/users")
221
+ * ```
222
+ */
223
+ mapParameters?: (parameters: apigw.ParameterMapping) => void;
224
+ };
225
+ export type LambdaIntegrationProps = {
226
+ /**
227
+ * The Lambda integration uses the V2 payload format:
228
+ * {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html}
229
+ *
230
+ * If writing the Lambda in TypeScript, this means you should use `APIGatewayProxyEventV2` as the
231
+ * request type, and `APIGatewayProxyResultV2` as the response type.
232
+ */
233
+ lambda: lambda.IFunction;
234
+ };
235
+ export type SqsIntegrationProps = {
236
+ queue: sqs.IQueue;
237
+ /**
238
+ * Message attributes to pass on to SQS. The keys in this object are the names of the attributes.
239
+ * Each attribute has a DataType field, and either a StringValue or BinaryValue field depending on
240
+ * its type. See AWS docs:
241
+ * https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_MessageAttributeValue.html
242
+ *
243
+ * In the StringValue field, you can do API Gateway parameter mapping:
244
+ * https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-parameter-mapping.html
245
+ *
246
+ * Example:
247
+ * ```
248
+ * messageAttributes: {
249
+ * clientId: {
250
+ * DataType: "String",
251
+ * StringValue: "${context.authorizer.clientId}",
252
+ * },
253
+ * },
254
+ * ```
255
+ */
256
+ messageAttributes?: {
257
+ [attributeName: string]: {
258
+ DataType: "String" | "Number";
259
+ StringValue: string;
260
+ } | {
261
+ DataType: "Binary";
262
+ /** Base64-encoded binary data object. */
263
+ BinaryValue: string;
264
+ };
265
+ };
266
+ };
267
+ export type AuthorizationProps<AuthScopesT extends string = string> =
268
+ /**
269
+ * No authentication, for when you want a fully public route (or handle authentication in the
270
+ * backend integration).
271
+ */
272
+ {
273
+ type: "NONE";
274
+ }
275
+ /**
276
+ * AWS IAM authorization.
277
+ * https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control-iam.html
278
+ */
279
+ | {
280
+ type: "IAM";
281
+ }
282
+ /**
283
+ * Creates a custom authorizer lambda which reads `Authorization: Bearer <token>` header and
284
+ * verifies the token against a Cognito user pool.
285
+ */
286
+ | ({
287
+ type: "COGNITO_USER_POOL";
288
+ } & CognitoUserPoolAuthorizerProps<AuthScopesT>)
289
+ /**
290
+ * Creates a custom authorizer lambda which reads `Authorization: Basic <base64-encoded credentials>`
291
+ * header and verifies the credentials against a given secret.
292
+ */
293
+ | ({
294
+ type: "BASIC_AUTH";
295
+ } & BasicAuthAuthorizerProps)
296
+ /**
297
+ * Creates a custom authorizer lambda which allows both:
298
+ * - `Authorization: Bearer <token>` header, for which the token is checked against the given
299
+ * Cognito user pool
300
+ * - `Authorization: Basic <base64-encoded credentials>` header, for which the credentials are
301
+ * checked against the credentials from the given basic auth secret name
302
+ *
303
+ * If either of these are given and valid, the request is authenticated.
304
+ */
305
+ | ({
306
+ type: "COGNITO_USER_POOL_OR_BASIC_AUTH";
307
+ } & CognitoUserPoolOrBasicAuthAuthorizerProps<AuthScopesT>);
308
+ export type CognitoUserPoolAuthorizerProps<AuthScopesT extends string = string> = {
309
+ userPool: IUserPool;
310
+ /**
311
+ * Verifies that token claims contain the given scope.
312
+ *
313
+ * When defined as part of a resource server, scopes are on the format:
314
+ * `{resource server identifier}/{scope name}`, e.g. `external/view_users`.
315
+ *
316
+ * To get more type safety on this parameter, see the docs for the `AuthScopesT` type parameter on
317
+ * {@link ApiGateway}.
318
+ */
319
+ requiredScope?: AuthScopesT;
320
+ /**
321
+ * Name of secret in AWS Secrets Manager that stores basic auth credentials for the backend
322
+ * service, to be forwarded to the backend if Cognito user pool authentication succeeded.
323
+ *
324
+ * The secret value must follow this format:
325
+ * ```json
326
+ * {"username":"<username>","password":"<password>"}
327
+ * ```
328
+ *
329
+ * This prop solves the following use-case:
330
+ * - You want to do Cognito user pool authentication in the API Gateway
331
+ * - You want an additional auth check in the backend, but you don't want to deal with Cognito
332
+ * there
333
+ * - The backend uses basic auth
334
+ *
335
+ * This prop solves this by letting you specify credentials to pass to the backend after API-GW
336
+ * authentication succeeds. You can pass the encoded credentials through
337
+ * {@link AlbIntegrationProps.mapParameters}, using the `authorizer.internalAuthorizationHeader`
338
+ * context variable, like so:
339
+ * ```
340
+ * mapParameters: (parameters) => parameters.overwriteHeader(
341
+ * // 'Authorization' header cannot be overridden, so we use a custom header
342
+ * "X-Internal-Authorization",
343
+ * apigw.MappingValue.contextVariable("authorizer.internalAuthorizationHeader"),
344
+ * )
345
+ * ```
346
+ * The backend can then check the `X-Internal-Authorization` header.
347
+ */
348
+ credentialsForInternalAuthorization?: string;
349
+ };
350
+ export type BasicAuthAuthorizerProps = {
351
+ /**
352
+ * Name of secret in AWS Secrets Manager that stores basic auth credentials. The secret value must
353
+ * follow this format:
354
+ * ```json
355
+ * {"username":"<username>","password":"<password>"}
356
+ * ```
357
+ */
358
+ credentialsSecretName: string;
359
+ };
360
+ export type CognitoUserPoolOrBasicAuthAuthorizerProps<AuthScopesT extends string = string> = {
361
+ userPool: IUserPool;
362
+ /**
363
+ * Name of secret in AWS Secrets Manager that stores basic auth credentials. The secret value must
364
+ * follow this format:
365
+ * ```json
366
+ * {"username":"<username>","password":"<password>"}
367
+ * ```
368
+ */
369
+ basicAuthCredentialsSecretName?: string;
370
+ /**
371
+ * Verifies that token claims contain the given scope. Only applicable for `Bearer` token requests
372
+ * checked against the Cognito User Pool (not applicable for basic auth).
373
+ *
374
+ * When defined as part of a resource server, scopes are on the format:
375
+ * `{resource server identifier}/{scope name}`, e.g. `external/view_users`.
376
+ *
377
+ * To get more type safety on this parameter, see the docs for the `AuthScopesT` type parameter on
378
+ * {@link ApiGateway}.
379
+ */
380
+ requiredScope?: AuthScopesT;
381
+ };
382
+ export type ApiGatewayAccessLogsProps = {
383
+ /**
384
+ * Delete the access logs if this construct is deleted?
385
+ *
386
+ * Maybe you want to DESTROY instead. Or for legal reasons, retain for audit.
387
+ *
388
+ * @default RemovalPolicy.RETAIN
389
+ */
390
+ removalPolicy?: cdk.RemovalPolicy;
391
+ /**
392
+ * How long to keep the logs. If undefined, uses the same default as new AWS log groups.
393
+ *
394
+ * @default RetentionDays.TWO_YEARS
395
+ */
396
+ retention?: logs.RetentionDays;
397
+ /**
398
+ * A custom JSON log format, which uses variables from `"$context"`.
399
+ *
400
+ * See [AWS: CloudWatch log formats for API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html#apigateway-cloudwatch-log-formats)
401
+ * for formats and rules. It is possible to use other formats like CLF and XML, but this construct
402
+ * only supports JSON for now.
403
+ *
404
+ * For a list of all possible variables to log, see
405
+ * [AWS: $context Variables for data models, authorizers, mapping templates, and CloudWatch access logging](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference)
406
+ * and
407
+ * [AWS: $context Variables for access logging only](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference-access-logging-only) .
408
+ *
409
+ * @default {@link defaultAccessLogFormat}
410
+ */
411
+ accessLogFormat?: Record<string, string>;
412
+ };
413
+ /**
414
+ * This construct tries to simplify the creation of an API Gateway for a service, by collecting most
415
+ * of the common setup here.
416
+ *
417
+ * The approach followed in this construct is:
418
+ * 1. One API-GW per service
419
+ * 3. One subdomain per API-GW / service
420
+ * 4. Use HTTP API, not REST
421
+ * 5. Use a $default stage with autodeploy
422
+ * 6. Support multiple routes (with possible `/{proxy+}` to let all sub-paths through)
423
+ * 7. Allow custom integration/authorizer for each route, or defaults for the whole gateway
424
+ *
425
+ * The route integration is one of these:
426
+ * - ALB private integration with VPC Link using HTTPS to the ALB
427
+ * - Lambda integration
428
+ * - SQS integration
429
+ *
430
+ * ### Load Balancer Security Group
431
+ *
432
+ * Note that the load balancer used in an {@link AlbIntegrationProps} must allow outbound HTTPS
433
+ * traffic to its SecurityGroup. Otherwise, the VPC Link used by the API-GW can't get traffic from
434
+ * the ALB.
435
+ *
436
+ * ```
437
+ * const loadBalancerSecurityGroup = new ec2.SecurityGroup(..., {
438
+ * allowAllOutbound: false,
439
+ * })
440
+ *
441
+ * loadBalancerSecurityGroup.addEgressRule(
442
+ * loadBalancerSecurityGroup,
443
+ * ec2.Port.tcp(443),
444
+ * "Outbound to self for ALB to API-GW VPC-Link",
445
+ * )
446
+ *
447
+ * const loadBalancer = new lifligLoadBalancer.LoadBalancer(...,
448
+ * {
449
+ * overrideLoadBalancerProps: {
450
+ * securityGroup: loadBalancerSecurityGroup,
451
+ * },
452
+ * },
453
+ * )
454
+ * ```
455
+ *
456
+ * @template AuthScopesT This type parameter allows you to improve type safety on the
457
+ * `requiredScope` field on {@link CognitoUserPoolOrBasicAuthAuthorizerProps}, by narrowing the type
458
+ * to specific strings. You can then extend the `ApiGateway` with this type to enforce those scopes
459
+ * across the application. Remember that auth scopes must be on the format
460
+ * `{resource server identifier}/{scope name}`.
461
+ *
462
+ * Example:
463
+ * ```
464
+ * type AuthScopes = "external/read_users" | "internal/create_users"
465
+ *
466
+ * export class MyProjectApiGateway extends ApiGateway<AuthScopes> {}
467
+ * ```
468
+ * TypeScript will then enforce that `requiredScope` is one of `AuthScopes`, and provide
469
+ * auto-complete.
470
+ *
471
+ * @author Kristian Rekstad <kre@capraconsulting.no>
472
+ * @author Hermann Mørkrid <hem@liflig.no>
473
+ */
474
+ export declare class ApiGateway<AuthScopesT extends string = string> extends constructs.Construct {
475
+ /** The API Gateway HTTP API. This is the main construct for API-GW. */
476
+ readonly httpApi: apigw.HttpApi;
477
+ /** The routes which connect the {@link httpApi} to the backend integration(s). */
478
+ readonly routes: apigw.HttpRoute[];
479
+ /** The domain which consumers must use.*/
480
+ readonly domain: string;
481
+ /** Access log group. */
482
+ readonly logGroup: logs.LogGroup;
483
+ private readonly props;
484
+ constructor(scope: constructs.Construct, id: string, props: ApiGatewayProps<AuthScopesT>);
485
+ /** @throws Error */
486
+ private static validateProps;
487
+ /**
488
+ * The authorizer only accepts requests from external users that are authorized.
489
+ * Unauthorized users are stopped in the API-GW, and not forwarded to the integration.
490
+ */
491
+ private createAuthorizer;
492
+ private createIntegration;
493
+ /**
494
+ * Allows a grantable target (role, user etc.) permission to invoke the API.
495
+ * Only works when using `IAM` as {@link ApiGatewayRoute.authorization}.
496
+ *
497
+ * @param target A grantable, like {@link iam.Role}
498
+ */
499
+ grantInvoke(target: iam.IGrantable): void;
500
+ }