1
1
import * as cdk from '@aws-cdk/core' ;
2
2
import { Default , FactName , RegionInfo } from '@aws-cdk/region-info' ;
3
3
import { IOpenIdConnectProvider } from './oidc-provider' ;
4
+ import { PolicyDocument } from './policy-document' ;
4
5
import { Condition , Conditions , PolicyStatement } from './policy-statement' ;
6
+ import { defaultAddPrincipalToAssumeRole } from './private/assume-role-policy' ;
5
7
import { ISamlProvider } from './saml-provider' ;
6
8
import { LITERAL_STRING_KEY , mergePrincipal } from './util' ;
7
9
@@ -68,6 +70,25 @@ export interface IPrincipal extends IGrantable {
68
70
addToPrincipalPolicy ( statement : PolicyStatement ) : AddToPrincipalPolicyResult ;
69
71
}
70
72
73
+ /**
74
+ * A type of principal that has more control over its own representation in AssumeRolePolicyDocuments
75
+ *
76
+ * More complex types of identity providers need more control over Role's policy documents
77
+ * than simply `{ Effect: 'Allow', Action: 'AssumeRole', Principal: <Whatever> }`.
78
+ *
79
+ * If that control is necessary, they can implement `IAssumeRolePrincipal` to get full
80
+ * access to a Role's AssumeRolePolicyDocument.
81
+ */
82
+ export interface IAssumeRolePrincipal extends IPrincipal {
83
+ /**
84
+ * Add the princpial to the AssumeRolePolicyDocument
85
+ *
86
+ * Add the statements to the AssumeRolePolicyDocument necessary to give this principal
87
+ * permissions to assume the given role.
88
+ */
89
+ addToAssumeRolePolicy ( document : PolicyDocument ) : void ;
90
+ }
91
+
71
92
/**
72
93
* Result of calling `addToPrincipalPolicy`
73
94
*/
@@ -89,7 +110,7 @@ export interface AddToPrincipalPolicyResult {
89
110
/**
90
111
* Base class for policy principals
91
112
*/
92
- export abstract class PrincipalBase implements IPrincipal {
113
+ export abstract class PrincipalBase implements IAssumeRolePrincipal {
93
114
public readonly grantPrincipal : IPrincipal = this ;
94
115
public readonly principalAccount : string | undefined = undefined ;
95
116
@@ -113,6 +134,14 @@ export abstract class PrincipalBase implements IPrincipal {
113
134
return { statementAdded : false } ;
114
135
}
115
136
137
+ public addToAssumeRolePolicy ( document : PolicyDocument ) : void {
138
+ // Default implementation of this protocol, compatible with the legacy behavior
139
+ document . addStatements ( new PolicyStatement ( {
140
+ actions : [ this . assumeRoleAction ] ,
141
+ principals : [ this ] ,
142
+ } ) ) ;
143
+ }
144
+
116
145
public toString ( ) {
117
146
// This is a first pass to make the object readable. Descendant principals
118
147
// should return something nicer.
@@ -138,9 +167,39 @@ export abstract class PrincipalBase implements IPrincipal {
138
167
*
139
168
* @returns a new PrincipalWithConditions object.
140
169
*/
141
- public withConditions ( conditions : Conditions ) : IPrincipal {
170
+ public withConditions ( conditions : Conditions ) : PrincipalBase {
142
171
return new PrincipalWithConditions ( this , conditions ) ;
143
172
}
173
+
174
+ /**
175
+ * Returns a new principal using this principal as the base, with session tags enabled.
176
+ *
177
+ * @returns a new SessionTagsPrincipal object.
178
+ */
179
+ public withSessionTags ( ) : PrincipalBase {
180
+ return new SessionTagsPrincipal ( this ) ;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Base class for Principals that wrap other principals
186
+ */
187
+ class PrincipalAdapter extends PrincipalBase {
188
+ public readonly assumeRoleAction = this . wrapped . assumeRoleAction ;
189
+ public readonly principalAccount = this . wrapped . principalAccount ;
190
+
191
+ constructor ( protected readonly wrapped : IPrincipal ) {
192
+ super ( ) ;
193
+ }
194
+
195
+ public get policyFragment ( ) : PrincipalPolicyFragment { return this . wrapped . policyFragment ; }
196
+
197
+ addToPolicy ( statement : PolicyStatement ) : boolean {
198
+ return this . wrapped . addToPolicy ( statement ) ;
199
+ }
200
+ addToPrincipalPolicy ( statement : PolicyStatement ) : AddToPrincipalPolicyResult {
201
+ return this . wrapped . addToPrincipalPolicy ( statement ) ;
202
+ }
144
203
}
145
204
146
205
/**
@@ -149,15 +208,11 @@ export abstract class PrincipalBase implements IPrincipal {
149
208
* For more information about conditions, see:
150
209
* https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html
151
210
*/
152
- export class PrincipalWithConditions implements IPrincipal {
153
- public readonly grantPrincipal : IPrincipal = this ;
154
- public readonly assumeRoleAction : string = this . principal . assumeRoleAction ;
211
+ export class PrincipalWithConditions extends PrincipalAdapter {
155
212
private additionalConditions : Conditions ;
156
213
157
- constructor (
158
- private readonly principal : IPrincipal ,
159
- conditions : Conditions ,
160
- ) {
214
+ constructor ( principal : IPrincipal , conditions : Conditions ) {
215
+ super ( principal ) ;
161
216
this . additionalConditions = conditions ;
162
217
}
163
218
@@ -186,27 +241,15 @@ export class PrincipalWithConditions implements IPrincipal {
186
241
* See [the IAM documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html).
187
242
*/
188
243
public get conditions ( ) {
189
- return this . mergeConditions ( this . principal . policyFragment . conditions , this . additionalConditions ) ;
244
+ return this . mergeConditions ( this . wrapped . policyFragment . conditions , this . additionalConditions ) ;
190
245
}
191
246
192
247
public get policyFragment ( ) : PrincipalPolicyFragment {
193
- return new PrincipalPolicyFragment ( this . principal . policyFragment . principalJson , this . conditions ) ;
194
- }
195
-
196
- public get principalAccount ( ) : string | undefined {
197
- return this . principal . principalAccount ;
198
- }
199
-
200
- public addToPolicy ( statement : PolicyStatement ) : boolean {
201
- return this . addToPrincipalPolicy ( statement ) . statementAdded ;
202
- }
203
-
204
- public addToPrincipalPolicy ( statement : PolicyStatement ) : AddToPrincipalPolicyResult {
205
- return this . principal . addToPrincipalPolicy ( statement ) ;
248
+ return new PrincipalPolicyFragment ( this . wrapped . policyFragment . principalJson , this . conditions ) ;
206
249
}
207
250
208
251
public toString ( ) {
209
- return this . principal . toString ( ) ;
252
+ return this . wrapped . toString ( ) ;
210
253
}
211
254
212
255
/**
@@ -247,6 +290,30 @@ export class PrincipalWithConditions implements IPrincipal {
247
290
}
248
291
}
249
292
293
+ /**
294
+ * Enables session tags on role assumptions from a principal
295
+ *
296
+ * For more information on session tags, see:
297
+ * https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html
298
+ */
299
+ export class SessionTagsPrincipal extends PrincipalAdapter {
300
+ constructor ( principal : IPrincipal ) {
301
+ super ( principal ) ;
302
+ }
303
+
304
+ public addToAssumeRolePolicy ( doc : PolicyDocument ) {
305
+ // Lazy import to avoid circular import dependencies during startup
306
+
307
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
308
+ const adapter : typeof import ( './private/policydoc-adapter' ) = require ( './private/policydoc-adapter' ) ;
309
+
310
+ defaultAddPrincipalToAssumeRole ( this . wrapped , new adapter . MutatingPolicyDocumentAdapter ( doc , ( statement ) => {
311
+ statement . addActions ( 'sts:TagSession' ) ;
312
+ return statement ;
313
+ } ) ) ;
314
+ }
315
+ }
316
+
250
317
/**
251
318
* A collection of the fields in a PolicyStatement that can be used to identify a principal.
252
319
*
@@ -441,6 +508,7 @@ export class FederatedPrincipal extends PrincipalBase {
441
508
* @param federated federated identity provider (i.e. 'cognito-identity.amazonaws.com' for users authenticated through Cognito)
442
509
* @param conditions The conditions under which the policy is in effect.
443
510
* See [the IAM documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html).
511
+ * @param sessionTags Whether to enable session tagging (see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html)
444
512
*/
445
513
constructor (
446
514
public readonly federated : string ,
@@ -471,6 +539,7 @@ export class WebIdentityPrincipal extends FederatedPrincipal {
471
539
* @param identityProvider identity provider (i.e. 'cognito-identity.amazonaws.com' for users authenticated through Cognito)
472
540
* @param conditions The conditions under which the policy is in effect.
473
541
* See [the IAM documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html).
542
+ * @param sessionTags Whether to enable session tagging (see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html)
474
543
*/
475
544
constructor ( identityProvider : string , conditions : Conditions = { } ) {
476
545
super ( identityProvider , conditions ?? { } , 'sts:AssumeRoleWithWebIdentity' ) ;
@@ -606,9 +675,9 @@ export class StarPrincipal extends PrincipalBase {
606
675
*/
607
676
export class CompositePrincipal extends PrincipalBase {
608
677
public readonly assumeRoleAction : string ;
609
- private readonly principals = new Array < PrincipalBase > ( ) ;
678
+ private readonly principals = new Array < IPrincipal > ( ) ;
610
679
611
- constructor ( ...principals : PrincipalBase [ ] ) {
680
+ constructor ( ...principals : IPrincipal [ ] ) {
612
681
super ( ) ;
613
682
if ( principals . length === 0 ) {
614
683
throw new Error ( 'CompositePrincipals must be constructed with at least 1 Principal but none were passed.' ) ;
@@ -623,28 +692,29 @@ export class CompositePrincipal extends PrincipalBase {
623
692
*
624
693
* @param principals IAM principals that will be added to the composite principal
625
694
*/
626
- public addPrincipals ( ...principals : PrincipalBase [ ] ) : this {
627
- for ( const p of principals ) {
628
- if ( p . assumeRoleAction !== this . assumeRoleAction ) {
629
- throw new Error (
630
- 'Cannot add multiple principals with different "assumeRoleAction". ' +
631
- `Expecting "${ this . assumeRoleAction } ", got "${ p . assumeRoleAction } "` ) ;
632
- }
695
+ public addPrincipals ( ...principals : IPrincipal [ ] ) : this {
696
+ this . principals . push ( ...principals ) ;
697
+ return this ;
698
+ }
699
+
700
+ public addToAssumeRolePolicy ( doc : PolicyDocument ) {
701
+ for ( const p of this . principals ) {
702
+ defaultAddPrincipalToAssumeRole ( p , doc ) ;
703
+ }
704
+ }
633
705
706
+ public get policyFragment ( ) : PrincipalPolicyFragment {
707
+ // We only have a problem with conditions if we are trying to render composite
708
+ // princpals into a single statement (which is when `policyFragment` would get called)
709
+ for ( const p of this . principals ) {
634
710
const fragment = p . policyFragment ;
635
711
if ( fragment . conditions && Object . keys ( fragment . conditions ) . length > 0 ) {
636
712
throw new Error (
637
713
'Components of a CompositePrincipal must not have conditions. ' +
638
714
`Tried to add the following fragment: ${ JSON . stringify ( fragment ) } ` ) ;
639
715
}
640
-
641
- this . principals . push ( p ) ;
642
716
}
643
717
644
- return this ;
645
- }
646
-
647
- public get policyFragment ( ) : PrincipalPolicyFragment {
648
718
const principalJson : { [ key : string ] : string [ ] } = { } ;
649
719
650
720
for ( const p of this . principals ) {
0 commit comments