-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
alias.ts
261 lines (222 loc) · 8.67 KB
/
alias.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
import { Construct } from 'constructs';
import { IKey } from './key';
import { CfnAlias } from './kms.generated';
import * as iam from '../../aws-iam';
import { FeatureFlags, RemovalPolicy, Resource, Stack, Token, Tokenization } from '../../core';
import { KMS_ALIAS_NAME_REF } from '../../cx-api';
const REQUIRED_ALIAS_PREFIX = 'alias/';
const DISALLOWED_PREFIX = REQUIRED_ALIAS_PREFIX + 'aws/';
/**
* A KMS Key alias.
* An alias can be used in all places that expect a key.
*/
export interface IAlias extends IKey {
/**
* The name of the alias.
*
* @attribute
*/
readonly aliasName: string;
/**
* The Key to which the Alias refers.
*
* @attribute
*/
readonly aliasTargetKey: IKey;
}
/**
* Construction properties for a KMS Key Alias object.
*/
export interface AliasProps {
/**
* The name of the alias. The name must start with alias followed by a
* forward slash, such as alias/. You can't specify aliases that begin with
* alias/AWS. These aliases are reserved.
*/
readonly aliasName: string;
/**
* The ID of the key for which you are creating the alias. Specify the key's
* globally unique identifier or Amazon Resource Name (ARN). You can't
* specify another alias.
*/
readonly targetKey: IKey;
/**
* Policy to apply when the alias is removed from this stack.
*
* @default - The alias will be deleted
*/
readonly removalPolicy?: RemovalPolicy;
}
abstract class AliasBase extends Resource implements IAlias {
public abstract readonly aliasName: string;
public abstract readonly aliasTargetKey: IKey;
/**
* The ARN of the alias.
*
* @attribute
* @deprecated use `aliasArn` instead
*/
public get keyArn(): string {
return Stack.of(this).formatArn({
service: 'kms',
// aliasName already contains the '/'
resource: this.aliasName,
});
}
/**
* The ARN of the alias.
*
* @attribute
*/
public get aliasArn(): string {
return Stack.of(this).formatArn({
service: 'kms',
// aliasName already contains the '/'
resource: this.aliasName,
});
}
public get keyId(): string {
return this.aliasName;
}
public addAlias(alias: string): Alias {
return this.aliasTargetKey.addAlias(alias);
}
public addToResourcePolicy(statement: iam.PolicyStatement, allowNoOp?: boolean): iam.AddToResourcePolicyResult {
return this.aliasTargetKey.addToResourcePolicy(statement, allowNoOp);
}
public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant {
return this.aliasTargetKey.grant(grantee, ...actions);
}
public grantDecrypt(grantee: iam.IGrantable): iam.Grant {
return this.aliasTargetKey.grantDecrypt(grantee);
}
public grantEncrypt(grantee: iam.IGrantable): iam.Grant {
return this.aliasTargetKey.grantEncrypt(grantee);
}
public grantEncryptDecrypt(grantee: iam.IGrantable): iam.Grant {
return this.aliasTargetKey.grantEncryptDecrypt(grantee);
}
grantGenerateMac(grantee: iam.IGrantable): iam.Grant {
return this.aliasTargetKey.grantGenerateMac(grantee);
}
grantVerifyMac(grantee: iam.IGrantable): iam.Grant {
return this.aliasTargetKey.grantVerifyMac(grantee);
}
}
/**
* Properties of a reference to an existing KMS Alias
*/
export interface AliasAttributes {
/**
* Specifies the alias name. This value must begin with alias/ followed by a name (i.e. alias/ExampleAlias)
*/
readonly aliasName: string;
/**
* The customer master key (CMK) to which the Alias refers.
*/
readonly aliasTargetKey: IKey;
}
/**
* Defines a display name for a customer master key (CMK) in AWS Key Management
* Service (AWS KMS). Using an alias to refer to a key can help you simplify key
* management. For example, when rotating keys, you can just update the alias
* mapping instead of tracking and changing key IDs. For more information, see
* Working with Aliases in the AWS Key Management Service Developer Guide.
*
* You can also add an alias for a key by calling `key.addAlias(alias)`.
*
* @resource AWS::KMS::Alias
*/
export class Alias extends AliasBase {
/**
* Import an existing KMS Alias defined outside the CDK app.
*
* @param scope The parent creating construct (usually `this`).
* @param id The construct's name.
* @param attrs the properties of the referenced KMS Alias
*/
public static fromAliasAttributes(scope: Construct, id: string, attrs: AliasAttributes): IAlias {
class _Alias extends AliasBase {
public get aliasName() { return attrs.aliasName; }
public get aliasTargetKey() { return attrs.aliasTargetKey; }
}
return new _Alias(scope, id);
}
/**
* Import an existing KMS Alias defined outside the CDK app, by the alias name. This method should be used
* instead of 'fromAliasAttributes' when the underlying KMS Key ARN is not available.
* This Alias will not have a direct reference to the KMS Key, so addAlias and grant* methods are not supported.
*
* @param scope The parent creating construct (usually `this`).
* @param id The construct's name.
* @param aliasName The full name of the KMS Alias (e.g., 'alias/aws/s3', 'alias/myKeyAlias').
*/
public static fromAliasName(scope: Construct, id: string, aliasName: string): IAlias {
class Import extends Resource implements IAlias {
public readonly keyArn = Stack.of(this).formatArn({ service: 'kms', resource: aliasName });
public readonly keyId = aliasName;
public readonly aliasName = aliasName;
public get aliasTargetKey(): IKey { throw new Error('Cannot access aliasTargetKey on an Alias imported by Alias.fromAliasName().'); }
public addAlias(_alias: string): Alias { throw new Error('Cannot call addAlias on an Alias imported by Alias.fromAliasName().'); }
public addToResourcePolicy(_statement: iam.PolicyStatement, _allowNoOp?: boolean): iam.AddToResourcePolicyResult {
return { statementAdded: false };
}
public grant(grantee: iam.IGrantable, ..._actions: string[]): iam.Grant { return iam.Grant.drop(grantee, ''); }
public grantDecrypt(grantee: iam.IGrantable): iam.Grant { return iam.Grant.drop(grantee, ''); }
public grantEncrypt(grantee: iam.IGrantable): iam.Grant { return iam.Grant.drop(grantee, ''); }
public grantEncryptDecrypt(grantee: iam.IGrantable): iam.Grant { return iam.Grant.drop(grantee, ''); }
public grantGenerateMac(grantee: iam.IGrantable): iam.Grant { return iam.Grant.drop(grantee, ''); }
public grantVerifyMac(grantee: iam.IGrantable): iam.Grant { return iam.Grant.drop(grantee, ''); }
}
return new Import(scope, id);
}
public readonly aliasName: string;
public readonly aliasTargetKey: IKey;
constructor(scope: Construct, id: string, props: AliasProps) {
let aliasName = props.aliasName;
if (!Token.isUnresolved(aliasName)) {
if (!aliasName.startsWith(REQUIRED_ALIAS_PREFIX)) {
aliasName = REQUIRED_ALIAS_PREFIX + aliasName;
}
if (aliasName === REQUIRED_ALIAS_PREFIX) {
throw new Error(`Alias must include a value after "${REQUIRED_ALIAS_PREFIX}": ${aliasName}`);
}
if (aliasName.toLocaleLowerCase().startsWith(DISALLOWED_PREFIX)) {
throw new Error(`Alias cannot start with ${DISALLOWED_PREFIX}: ${aliasName}`);
}
if (!aliasName.match(/^[a-zA-Z0-9:/_-]{1,256}$/)) {
throw new Error('Alias name must be between 1 and 256 characters in a-zA-Z0-9:/_-');
}
} else if (Tokenization.reverseString(aliasName).firstValue && Tokenization.reverseString(aliasName).firstToken === undefined) {
const valueInToken = Tokenization.reverseString(aliasName).firstValue;
if (!valueInToken.startsWith(REQUIRED_ALIAS_PREFIX)) {
aliasName = REQUIRED_ALIAS_PREFIX + aliasName;
}
if (valueInToken.toLocaleLowerCase().startsWith(DISALLOWED_PREFIX)) {
throw new Error(`Alias cannot start with ${DISALLOWED_PREFIX}: ${aliasName}`);
}
if (!valueInToken.match(/^[a-zA-Z0-9:/_-]{1,256}$/)) {
throw new Error('Alias name must be between 1 and 256 characters in a-zA-Z0-9:/_-');
}
}
super(scope, id, {
physicalName: aliasName,
});
this.aliasTargetKey = props.targetKey;
const resource = new CfnAlias(this, 'Resource', {
aliasName: this.physicalName,
targetKeyId: this.aliasTargetKey.keyArn,
});
if (FeatureFlags.of(this).isEnabled(KMS_ALIAS_NAME_REF)) {
this.aliasName = this.getResourceNameAttribute(resource.ref);
} else {
this.aliasName = this.getResourceNameAttribute(resource.aliasName);
}
if (props.removalPolicy) {
resource.applyRemovalPolicy(props.removalPolicy);
}
}
protected generatePhysicalName(): string {
return REQUIRED_ALIAS_PREFIX + super.generatePhysicalName();
}
}