-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
Copy pathpolicy-statement.ts
762 lines (675 loc) · 24.1 KB
/
policy-statement.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
import * as cdk from '@aws-cdk/core';
import { IConstruct } from 'constructs';
import { Group } from './group';
import {
AccountPrincipal, AccountRootPrincipal, AnyPrincipal, ArnPrincipal, CanonicalUserPrincipal,
FederatedPrincipal, IPrincipal, PrincipalBase, PrincipalPolicyFragment, ServicePrincipal, ServicePrincipalOpts,
} from './principals';
import { normalizeStatement } from './private/postprocess-policy-document';
import { LITERAL_STRING_KEY, mergePrincipal, sum } from './util';
const ensureArrayOrUndefined = (field: any) => {
if (field === undefined) {
return undefined;
}
if (typeof (field) !== 'string' && !Array.isArray(field)) {
throw new Error('Fields must be either a string or an array of strings');
}
if (Array.isArray(field) && !!field.find((f: any) => typeof (f) !== 'string')) {
throw new Error('Fields must be either a string or an array of strings');
}
return Array.isArray(field) ? field : [field];
};
/**
* An estimate on how long ARNs typically are
*
* This is used to decide when to start splitting statements into new Managed Policies.
* Because we often can't know the length of an ARN (it may be a token and only
* available at deployment time) we'll have to estimate it.
*
* The estimate can be overridden by setting the `@aws-cdk/aws-iam.arnSizeEstimate` context key.
*/
const DEFAULT_ARN_SIZE_ESTIMATE = 150;
/**
* Context key which can be used to override the estimated length of unresolved ARNs.
*/
const ARN_SIZE_ESTIMATE_CONTEXT_KEY = '@aws-cdk/aws-iam.arnSizeEstimate';
/**
* Represents a statement in an IAM policy document.
*/
export class PolicyStatement {
/**
* Creates a new PolicyStatement based on the object provided.
* This will accept an object created from the `.toJSON()` call
* @param obj the PolicyStatement in object form.
*/
public static fromJson(obj: any) {
const ret = new PolicyStatement({
sid: obj.Sid,
actions: ensureArrayOrUndefined(obj.Action),
resources: ensureArrayOrUndefined(obj.Resource),
conditions: obj.Condition,
effect: obj.Effect,
notActions: ensureArrayOrUndefined(obj.NotAction),
notResources: ensureArrayOrUndefined(obj.NotResource),
principals: obj.Principal ? [new JsonPrincipal(obj.Principal)] : undefined,
notPrincipals: obj.NotPrincipal ? [new JsonPrincipal(obj.NotPrincipal)] : undefined,
});
// validate that the PolicyStatement has the correct shape
const errors = ret.validateForAnyPolicy();
if (errors.length > 0) {
throw new Error('Incorrect Policy Statement: ' + errors.join('\n'));
}
return ret;
}
/**
* Statement ID for this statement
*/
public sid?: string;
/**
* Whether to allow or deny the actions in this statement
*/
public effect: Effect;
private readonly _action = new Array<string>();
private readonly _notAction = new Array<string>();
private readonly _principal: { [key: string]: any[] } = {};
private readonly _notPrincipal: { [key: string]: any[] } = {};
private readonly _resource = new Array<string>();
private readonly _notResource = new Array<string>();
private readonly _condition: { [key: string]: any } = { };
private principalConditionsJson?: string;
// Hold on to those principals
private readonly _principals = new Array<IPrincipal>();
private readonly _notPrincipals = new Array<IPrincipal>();
constructor(props: PolicyStatementProps = {}) {
// Validate actions
for (const action of [...props.actions || [], ...props.notActions || []]) {
if (!/^(\*|[a-zA-Z0-9-]+:[a-zA-Z0-9*]+)$/.test(action) && !cdk.Token.isUnresolved(action)) {
throw new Error(`Action '${action}' is invalid. An action string consists of a service namespace, a colon, and the name of an action. Action names can include wildcards.`);
}
}
this.sid = props.sid;
this.effect = props.effect || Effect.ALLOW;
this.addActions(...props.actions || []);
this.addNotActions(...props.notActions || []);
this.addPrincipals(...props.principals || []);
this.addNotPrincipals(...props.notPrincipals || []);
this.addResources(...props.resources || []);
this.addNotResources(...props.notResources || []);
if (props.conditions !== undefined) {
this.addConditions(props.conditions);
}
}
//
// Actions
//
/**
* Specify allowed actions into the "Action" section of the policy statement.
*
* @see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html
*
* @param actions actions that will be allowed.
*/
public addActions(...actions: string[]) {
if (actions.length > 0 && this._notAction.length > 0) {
throw new Error('Cannot add \'Actions\' to policy statement if \'NotActions\' have been added');
}
this._action.push(...actions);
}
/**
* Explicitly allow all actions except the specified list of actions into the "NotAction" section
* of the policy document.
*
* @see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notaction.html
*
* @param notActions actions that will be denied. All other actions will be permitted.
*/
public addNotActions(...notActions: string[]) {
if (notActions.length > 0 && this._action.length > 0) {
throw new Error('Cannot add \'NotActions\' to policy statement if \'Actions\' have been added');
}
this._notAction.push(...notActions);
}
//
// Principal
//
/**
* Indicates if this permission has a "Principal" section.
*/
public get hasPrincipal() {
return this._principals.length + this._notPrincipals.length > 0;
}
/**
* Adds principals to the "Principal" section of a policy statement.
*
* @see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html
*
* @param principals IAM principals that will be added
*/
public addPrincipals(...principals: IPrincipal[]) {
this._principals.push(...principals);
if (Object.keys(principals).length > 0 && Object.keys(this._notPrincipal).length > 0) {
throw new Error('Cannot add \'Principals\' to policy statement if \'NotPrincipals\' have been added');
}
for (const principal of principals) {
this.validatePolicyPrincipal(principal);
const fragment = principal.policyFragment;
mergePrincipal(this._principal, fragment.principalJson);
this.addPrincipalConditions(fragment.conditions);
}
}
/**
* Specify principals that is not allowed or denied access to the "NotPrincipal" section of
* a policy statement.
*
* @see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notprincipal.html
*
* @param notPrincipals IAM principals that will be denied access
*/
public addNotPrincipals(...notPrincipals: IPrincipal[]) {
this._notPrincipals.push(...notPrincipals);
if (Object.keys(notPrincipals).length > 0 && Object.keys(this._principal).length > 0) {
throw new Error('Cannot add \'NotPrincipals\' to policy statement if \'Principals\' have been added');
}
for (const notPrincipal of notPrincipals) {
this.validatePolicyPrincipal(notPrincipal);
const fragment = notPrincipal.policyFragment;
mergePrincipal(this._notPrincipal, fragment.principalJson);
this.addPrincipalConditions(fragment.conditions);
}
}
private validatePolicyPrincipal(principal: IPrincipal) {
if (principal instanceof Group) {
throw new Error('Cannot use an IAM Group as the \'Principal\' or \'NotPrincipal\' in an IAM Policy');
}
}
/**
* Specify AWS account ID as the principal entity to the "Principal" section of a policy statement.
*/
public addAwsAccountPrincipal(accountId: string) {
this.addPrincipals(new AccountPrincipal(accountId));
}
/**
* Specify a principal using the ARN identifier of the principal.
* You cannot specify IAM groups and instance profiles as principals.
*
* @param arn ARN identifier of AWS account, IAM user, or IAM role (i.e. arn:aws:iam::123456789012:user/user-name)
*/
public addArnPrincipal(arn: string) {
this.addPrincipals(new ArnPrincipal(arn));
}
/**
* Adds a service principal to this policy statement.
*
* @param service the service name for which a service principal is requested (e.g: `s3.amazonaws.com`).
* @param opts options for adding the service principal (such as specifying a principal in a different region)
*/
public addServicePrincipal(service: string, opts?: ServicePrincipalOpts) {
this.addPrincipals(new ServicePrincipal(service, opts));
}
/**
* Adds a federated identity provider such as Amazon Cognito to this policy statement.
*
* @param federated federated identity provider (i.e. 'cognito-identity.amazonaws.com')
* @param conditions The conditions under which the policy is in effect.
* See [the IAM documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html).
*/
public addFederatedPrincipal(federated: any, conditions: Conditions) {
this.addPrincipals(new FederatedPrincipal(federated, conditions));
}
/**
* Adds an AWS account root user principal to this policy statement
*/
public addAccountRootPrincipal() {
this.addPrincipals(new AccountRootPrincipal());
}
/**
* Adds a canonical user ID principal to this policy document
*
* @param canonicalUserId unique identifier assigned by AWS for every account
*/
public addCanonicalUserPrincipal(canonicalUserId: string) {
this.addPrincipals(new CanonicalUserPrincipal(canonicalUserId));
}
/**
* Adds all identities in all accounts ("*") to this policy statement
*/
public addAnyPrincipal() {
this.addPrincipals(new AnyPrincipal());
}
//
// Resources
//
/**
* Specify resources that this policy statement applies into the "Resource" section of
* this policy statement.
*
* @see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_resource.html
*
* @param arns Amazon Resource Names (ARNs) of the resources that this policy statement applies to
*/
public addResources(...arns: string[]) {
if (arns.length > 0 && this._notResource.length > 0) {
throw new Error('Cannot add \'Resources\' to policy statement if \'NotResources\' have been added');
}
this._resource.push(...arns);
}
/**
* Specify resources that this policy statement will not apply to in the "NotResource" section
* of this policy statement. All resources except the specified list will be matched.
*
* @see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notresource.html
*
* @param arns Amazon Resource Names (ARNs) of the resources that this policy statement does not apply to
*/
public addNotResources(...arns: string[]) {
if (arns.length > 0 && this._resource.length > 0) {
throw new Error('Cannot add \'NotResources\' to policy statement if \'Resources\' have been added');
}
this._notResource.push(...arns);
}
/**
* Adds a ``"*"`` resource to this statement.
*/
public addAllResources() {
this.addResources('*');
}
/**
* Indicates if this permission has at least one resource associated with it.
*/
public get hasResource() {
return this._resource && this._resource.length > 0;
}
//
// Condition
//
/**
* Add a condition to the Policy
*
* If multiple calls are made to add a condition with the same operator and field, only
* the last one wins. For example:
*
* ```ts
* declare const stmt: iam.PolicyStatement;
*
* stmt.addCondition('StringEquals', { 'aws:SomeField': '1' });
* stmt.addCondition('StringEquals', { 'aws:SomeField': '2' });
* ```
*
* Will end up with the single condition `StringEquals: { 'aws:SomeField': '2' }`.
*
* If you meant to add a condition to say that the field can be *either* `1` or `2`, write
* this:
*
* ```ts
* declare const stmt: iam.PolicyStatement;
*
* stmt.addCondition('StringEquals', { 'aws:SomeField': ['1', '2'] });
* ```
*/
public addCondition(key: string, value: Condition) {
const existingValue = this._condition[key];
this._condition[key] = existingValue ? { ...existingValue, ...value } : value;
}
/**
* Add multiple conditions to the Policy
*
* See the `addCondition` function for a caveat on calling this method multiple times.
*/
public addConditions(conditions: Conditions) {
Object.keys(conditions).map(key => {
this.addCondition(key, conditions[key]);
});
}
/**
* Add a condition that limits to a given account
*
* This method can only be called once: subsequent calls will overwrite earlier calls.
*/
public addAccountCondition(accountId: string) {
this.addCondition('StringEquals', { 'sts:ExternalId': accountId });
}
/**
* Create a new `PolicyStatement` with the same exact properties
* as this one, except for the overrides
*/
public copy(overrides: PolicyStatementProps = {}) {
return new PolicyStatement({
sid: overrides.sid ?? this.sid,
effect: overrides.effect ?? this.effect,
actions: overrides.actions ?? this.actions,
notActions: overrides.notActions ?? this.notActions,
principals: overrides.principals ?? this.principals,
notPrincipals: overrides.notPrincipals ?? this.notPrincipals,
resources: overrides.resources ?? this.resources,
notResources: overrides.notResources ?? this.notResources,
conditions: overrides.conditions ?? this.conditions,
});
}
/**
* JSON-ify the policy statement
*
* Used when JSON.stringify() is called
*/
public toStatementJson(): any {
return normalizeStatement({
Action: this._action,
NotAction: this._notAction,
Condition: this._condition,
Effect: this.effect,
Principal: this._principal,
NotPrincipal: this._notPrincipal,
Resource: this._resource,
NotResource: this._notResource,
Sid: this.sid,
});
}
/**
* String representation of this policy statement
*/
public toString() {
return cdk.Token.asString(this, {
displayHint: 'PolicyStatement',
});
}
/**
* JSON-ify the statement
*
* Used when JSON.stringify() is called
*/
public toJSON() {
return this.toStatementJson();
}
/**
* Add a principal's conditions
*
* For convenience, principals have been modeled as both a principal
* and a set of conditions. This makes it possible to have a single
* object represent e.g. an "SNS Topic" (SNS service principal + aws:SourcArn
* condition) or an Organization member (* + aws:OrgId condition).
*
* However, when using multiple principals in the same policy statement,
* they must all have the same conditions or the OR samentics
* implied by a list of principals cannot be guaranteed (user needs to
* add multiple statements in that case).
*/
private addPrincipalConditions(conditions: Conditions) {
// Stringifying the conditions is an easy way to do deep equality
const theseConditions = JSON.stringify(conditions);
if (this.principalConditionsJson === undefined) {
// First principal, anything goes
this.principalConditionsJson = theseConditions;
} else {
if (this.principalConditionsJson !== theseConditions) {
throw new Error(`All principals in a PolicyStatement must have the same Conditions (got '${this.principalConditionsJson}' and '${theseConditions}'). Use multiple statements instead.`);
}
}
this.addConditions(conditions);
}
/**
* Validate that the policy statement satisfies base requirements for a policy.
*
* @returns An array of validation error messages, or an empty array if the statement is valid.
*/
public validateForAnyPolicy(): string[] {
const errors = new Array<string>();
if (this._action.length === 0 && this._notAction.length === 0) {
errors.push('A PolicyStatement must specify at least one \'action\' or \'notAction\'.');
}
return errors;
}
/**
* Validate that the policy statement satisfies all requirements for a resource-based policy.
*
* @returns An array of validation error messages, or an empty array if the statement is valid.
*/
public validateForResourcePolicy(): string[] {
const errors = this.validateForAnyPolicy();
if (Object.keys(this._principal).length === 0 && Object.keys(this._notPrincipal).length === 0) {
errors.push('A PolicyStatement used in a resource-based policy must specify at least one IAM principal.');
}
return errors;
}
/**
* Validate that the policy statement satisfies all requirements for an identity-based policy.
*
* @returns An array of validation error messages, or an empty array if the statement is valid.
*/
public validateForIdentityPolicy(): string[] {
const errors = this.validateForAnyPolicy();
if (Object.keys(this._principal).length > 0 || Object.keys(this._notPrincipal).length > 0) {
errors.push('A PolicyStatement used in an identity-based policy cannot specify any IAM principals.');
}
if (Object.keys(this._resource).length === 0 && Object.keys(this._notResource).length === 0) {
errors.push('A PolicyStatement used in an identity-based policy must specify at least one resource.');
}
return errors;
}
/**
* The Actions added to this statement
*/
public get actions() {
return [...this._action];
}
/**
* The NotActions added to this statement
*/
public get notActions() {
return [...this._notAction];
}
/**
* The Principals added to this statement
*/
public get principals(): IPrincipal[] {
return [...this._principals];
}
/**
* The NotPrincipals added to this statement
*/
public get notPrincipals(): IPrincipal[] {
return [...this._notPrincipals];
}
/**
* The Resources added to this statement
*/
public get resources() {
return [...this._resource];
}
/**
* The NotResources added to this statement
*/
public get notResources() {
return [...this._notResource];
}
/**
* The conditions added to this statement
*/
public get conditions(): any {
return { ...this._condition };
}
/**
* Estimate the size of this policy statement
*
* By necessity, this will not be accurate. We'll do our best to overestimate
* so we won't have nasty surprises.
*
* @internal
*/
public _estimateSize(options: EstimateSizeOptions): number {
let ret = 0;
const { actionEstimate, arnEstimate } = options;
ret += `"Effect": "${this.effect}",`.length;
count('Action', this.actions, actionEstimate);
count('NotAction', this.notActions, actionEstimate);
count('Resource', this.resources, arnEstimate);
count('NotResource', this.notResources, arnEstimate);
ret += this.principals.length * arnEstimate;
ret += this.notPrincipals.length * arnEstimate;
ret += JSON.stringify(this.conditions).length;
return ret;
function count(key: string, values: string[], tokenSize: number) {
if (values.length > 0) {
ret += key.length + 5 /* quotes, colon, brackets */ +
sum(values.map(v => (cdk.Token.isUnresolved(v) ? tokenSize : v.length) + 3 /* quotes, separator */));
}
}
}
}
/**
* The Effect element of an IAM policy
*
* @see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_effect.html
*/
export enum Effect {
/**
* Allows access to a resource in an IAM policy statement. By default, access to resources are denied.
*/
ALLOW = 'Allow',
/**
* Explicitly deny access to a resource. By default, all requests are denied implicitly.
*
* @see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html
*/
DENY = 'Deny',
}
/**
* Condition for when an IAM policy is in effect. Maps from the keys in a request's context to
* a string value or array of string values. See the Conditions interface for more details.
*/
export type Condition = any;
// NOTE! We'd ideally like to type this as `Record<string, any>`, because the
// API expects a map which can take either strings or lists of strings.
//
// However, if we were to change this right now, the Java bindings for CDK would
// emit a type of `Map<String, Object>`, but the most common types people would
// instantiate would be an `ImmutableMap<String, String>` which would not be
// assignable to `Map<String, Object>`. The types don't have a built-in notion
// of co-contravariance, you have to indicate that on the type. So jsii would first
// need to emit the type as `Map<String, ? extends Object>`.
//
// Feature request in https://github.com/aws/jsii/issues/1517
/**
* Conditions for when an IAM Policy is in effect, specified in the following structure:
*
* `{ "Operator": { "keyInRequestContext": "value" } }`
*
* The value can be either a single string value or an array of string values.
*
* For more information, including which operators are supported, see [the IAM
* documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html).
*/
export type Conditions = Record<string, Condition>;
/**
* Interface for creating a policy statement
*/
export interface PolicyStatementProps {
/**
* The Sid (statement ID) is an optional identifier that you provide for the
* policy statement. You can assign a Sid value to each statement in a
* statement array. In services that let you specify an ID element, such as
* SQS and SNS, the Sid value is just a sub-ID of the policy document's ID. In
* IAM, the Sid value must be unique within a JSON policy.
*
* @default - no sid
*/
readonly sid?: string;
/**
* List of actions to add to the statement
*
* @default - no actions
*/
readonly actions?: string[];
/**
* List of not actions to add to the statement
*
* @default - no not-actions
*/
readonly notActions?: string[];
/**
* List of principals to add to the statement
*
* @default - no principals
*/
readonly principals?: IPrincipal[];
/**
* List of not principals to add to the statement
*
* @default - no not principals
*/
readonly notPrincipals?: IPrincipal[];
/**
* Resource ARNs to add to the statement
*
* @default - no resources
*/
readonly resources?: string[];
/**
* NotResource ARNs to add to the statement
*
* @default - no not-resources
*/
readonly notResources?: string[];
/**
* Conditions to add to the statement
*
* @default - no condition
*/
readonly conditions?: {[key: string]: any};
/**
* Whether to allow or deny the actions in this statement
*
* @default Effect.ALLOW
*/
readonly effect?: Effect;
}
class JsonPrincipal extends PrincipalBase {
public readonly policyFragment: PrincipalPolicyFragment;
constructor(json: any = { }) {
super();
// special case: if principal is a string, turn it into a "LiteralString" principal,
// so we render the exact same string back out.
if (typeof(json) === 'string') {
json = { [LITERAL_STRING_KEY]: [json] };
}
if (typeof(json) !== 'object') {
throw new Error(`JSON IAM principal should be an object, got ${JSON.stringify(json)}`);
}
this.policyFragment = {
principalJson: json,
conditions: {},
};
}
public dedupeString(): string | undefined {
return JSON.stringify(this.policyFragment);
}
}
/**
* Options for _estimateSize
*
* These can optionally come from context, but it's too expensive to look
* them up every time so we bundle them into a struct first.
*
* @internal
*/
export interface EstimateSizeOptions {
/**
* Estimated size of an unresolved ARN
*/
readonly arnEstimate: number;
/**
* Estimated size of an unresolved action
*/
readonly actionEstimate: number;
}
/**
* Derive the size estimation options from context
*
* @internal
*/
export function deriveEstimateSizeOptions(scope: IConstruct): EstimateSizeOptions {
const actionEstimate = 20;
const arnEstimate = scope.node.tryGetContext(ARN_SIZE_ESTIMATE_CONTEXT_KEY) ?? DEFAULT_ARN_SIZE_ESTIMATE;
if (typeof arnEstimate !== 'number') {
throw new Error(`Context value ${ARN_SIZE_ESTIMATE_CONTEXT_KEY} should be a number, got ${JSON.stringify(arnEstimate)}`);
}
return { actionEstimate, arnEstimate };
}