Skip to content

Commit 19245b4

Browse files
committed
feat(elasticache): add user support
1 parent eef4b0d commit 19245b4

File tree

6 files changed

+597
-12
lines changed

6 files changed

+597
-12
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { Construct } from 'constructs';
2+
import { UserEngine } from './common';
3+
import { CfnUser } from './elasticache.generated';
4+
import { UserBase, UserBaseProps } from './user-base';
5+
import * as iam from '../../aws-iam';
6+
import { addConstructMetadata } from '../../core/lib/metadata-resource';
7+
import { propertyInjectable } from '../../core/lib/prop-injectable';
8+
9+
const ELASTICACHE_IAMUSER_SYMBOL = Symbol.for('@aws-cdk/aws-elasticache.IamUser');
10+
11+
/**
12+
* Properties for defining an ElastiCache user with IAM authentication.
13+
*/
14+
export interface IamUserProps extends UserBaseProps {
15+
/**
16+
* The name of the user.
17+
*/
18+
readonly userName: string;
19+
}
20+
21+
/**
22+
* Define an ElastiCache user with IAM authentication.
23+
*
24+
* @resource AWS::ElastiCache::User
25+
*/
26+
@propertyInjectable
27+
export class IamUser extends UserBase {
28+
/**
29+
* Uniquely identifies this class.
30+
*/
31+
public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-elasticache.IamUser';
32+
33+
/**
34+
* Return whether the given object is an `IamUser`.
35+
*/
36+
public static isIamUser(x: any) : x is IamUser {
37+
return x !== null && typeof(x) === 'object' && ELASTICACHE_IAMUSER_SYMBOL in x;
38+
}
39+
40+
/**
41+
* The engine for the user.
42+
*/
43+
public readonly engine?: UserEngine;
44+
/**
45+
* The user's ID.
46+
*
47+
* @attribute
48+
*/
49+
public readonly userId: string;
50+
/**
51+
* The user's name.
52+
* For IAM authentication userName must be equal to userId.
53+
*
54+
* @attribute
55+
*/
56+
public readonly userName?: string;
57+
/**
58+
* The access string that defines the user's permissions.
59+
*/
60+
public readonly accessString: string;
61+
/**
62+
* The user's ARN.
63+
*
64+
* @attribute
65+
*/
66+
public readonly userArn: string;
67+
/**
68+
* The user's status.
69+
* Can be 'active', 'modifying', 'deleting'.
70+
*
71+
* @attribute
72+
*/
73+
public readonly userStatus: string;
74+
75+
private readonly resource: CfnUser;
76+
77+
constructor(scope: Construct, id: string, props: IamUserProps) {
78+
super(scope, id);
79+
80+
// Enhanced CDK Analytics Telemetry
81+
addConstructMetadata(this, props);
82+
83+
this.engine = props.engine ?? UserEngine.VALKEY;
84+
this.userId = props.userId;
85+
this.userName = props.userName;
86+
this.accessString = props.accessControl.accessString;
87+
88+
this.resource = new CfnUser(this, 'Resource', {
89+
engine: this.engine,
90+
userId: props.userId,
91+
userName: this.userName,
92+
accessString: this.accessString,
93+
authenticationMode: {
94+
Type: 'iam',
95+
},
96+
noPasswordRequired: false,
97+
passwords: undefined,
98+
});
99+
100+
this.userArn = this.resource.attrArn;
101+
this.userStatus = this.resource.attrStatus;
102+
103+
Object.defineProperty(this, ELASTICACHE_IAMUSER_SYMBOL, { value: true });
104+
}
105+
106+
/**
107+
* Grant connect permissions to the given IAM identity.
108+
*
109+
* @param grantee The IAM identity to grant permissions to.
110+
*/
111+
public grantConnect(grantee: iam.IGrantable): iam.Grant {
112+
return this.grant(grantee, 'elasticache:Connect');
113+
}
114+
115+
/**
116+
* Grant the given identity custom permissions.
117+
*
118+
* @param grantee The IAM identity to grant permissions to.
119+
* @param actions The actions to grant.
120+
*/
121+
public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant {
122+
return iam.Grant.addToPrincipal({
123+
grantee,
124+
actions,
125+
resourceArns: [this.userArn],
126+
});
127+
}
128+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
11
export * from './elasticache.generated';
2+
export * from './common';
3+
export * from './user-group';
4+
export * from './user-base';
5+
export * from './iam-user';
6+
export * from './password-user';
7+
export * from './no-password-user';
8+
export * from './serverless-cache-base';
9+
export * from './serverless-cache';
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { Construct } from 'constructs';
2+
import { UserEngine } from './common';
3+
import { CfnUser } from './elasticache.generated';
4+
import { UserBase, UserBaseProps } from './user-base';
5+
import { ValidationError } from '../../core/lib/errors';
6+
import { addConstructMetadata } from '../../core/lib/metadata-resource';
7+
import { propertyInjectable } from '../../core/lib/prop-injectable';
8+
9+
const ELASTICACHE_NOPASSWORDUSER_SYMBOL = Symbol.for('@aws-cdk/aws-elasticache.NoPasswordUser');
10+
11+
/**
12+
* Properties for defining an ElastiCache user with no password authentication.
13+
*/
14+
export interface NoPasswordUserProps extends UserBaseProps {
15+
/**
16+
* The name of the user.
17+
*
18+
* @default - Same as userId.
19+
*/
20+
readonly userName?: string;
21+
}
22+
23+
/**
24+
* Define an ElastiCache user with no password authentication.
25+
*
26+
* @resource AWS::ElastiCache::User
27+
*/
28+
@propertyInjectable
29+
export class NoPasswordUser extends UserBase {
30+
/**
31+
* Uniquely identifies this class.
32+
*/
33+
public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-elasticache.NoPasswordUser';
34+
35+
/**
36+
* Return whether the given object is a `NoPasswordUser`.
37+
*/
38+
public static isNoPasswordUser(x: any) : x is NoPasswordUser {
39+
return x !== null && typeof(x) === 'object' && ELASTICACHE_NOPASSWORDUSER_SYMBOL in x;
40+
}
41+
42+
/**
43+
* The engine for the user.
44+
*/
45+
public readonly engine?: UserEngine;
46+
/**
47+
* The user's ID.
48+
*
49+
* @attribute
50+
*/
51+
public readonly userId: string;
52+
/**
53+
* The user's name.
54+
*
55+
* @attribute
56+
*/
57+
public readonly userName?: string;
58+
/**
59+
* The access string that defines the user's permissions.
60+
*/
61+
public readonly accessString: string;
62+
/**
63+
* The user's ARN.
64+
*
65+
* @attribute
66+
*/
67+
public readonly userArn: string;
68+
/**
69+
* The user's status.
70+
* Can be 'active', 'modifying', 'deleting'.
71+
*
72+
* @attribute
73+
*/
74+
public readonly userStatus: string;
75+
76+
private readonly resource: CfnUser;
77+
78+
constructor(scope: Construct, id: string, props: NoPasswordUserProps) {
79+
super(scope, id);
80+
81+
// Enhanced CDK Analytics Telemetry
82+
addConstructMetadata(this, props);
83+
84+
this.engine = props.engine ?? UserEngine.VALKEY;
85+
this.userId = props.userId;
86+
this.userName = props.userName ?? props.userId;
87+
this.accessString = props.accessControl.accessString;
88+
89+
if (this.engine === UserEngine.VALKEY ) {
90+
throw new ValidationError('Valkey engine does not support no-password authentication.', this);
91+
}
92+
93+
this.resource = new CfnUser(this, 'Resource', {
94+
engine: this.engine,
95+
userId: props.userId,
96+
userName: this.userName,
97+
accessString: this.accessString,
98+
authenticationMode: {
99+
Type: 'no-password-required',
100+
},
101+
noPasswordRequired: true,
102+
passwords: undefined,
103+
});
104+
105+
this.userArn = this.resource.attrArn;
106+
this.userStatus = this.resource.attrStatus;
107+
108+
Object.defineProperty(this, ELASTICACHE_NOPASSWORDUSER_SYMBOL, { value: true });
109+
}
110+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { Construct } from 'constructs';
2+
import { UserEngine } from './common';
3+
import { CfnUser } from './elasticache.generated';
4+
import { UserBase, UserBaseProps } from './user-base';
5+
import { SecretValue } from '../../core';
6+
import { ValidationError } from '../../core/lib/errors';
7+
import { addConstructMetadata } from '../../core/lib/metadata-resource';
8+
import { propertyInjectable } from '../../core/lib/prop-injectable';
9+
10+
const ELASTICACHE_PASSWORDUSER_SYMBOL = Symbol.for('@aws-cdk/aws-elasticache.PasswordUser');
11+
12+
/**
13+
* Properties for defining an ElastiCache user with password authentication.
14+
*/
15+
export interface PasswordUserProps extends UserBaseProps {
16+
/**
17+
* The name of the user.
18+
*
19+
* @default - Same as userId.
20+
*/
21+
readonly userName?: string;
22+
/**
23+
* The passwords for the user.
24+
* Password authentication requires using 1-2 passwords.
25+
*/
26+
readonly passwords: SecretValue[];
27+
}
28+
29+
/**
30+
* Define an ElastiCache user with password authentication.
31+
*
32+
* @resource AWS::ElastiCache::User
33+
*/
34+
@propertyInjectable
35+
export class PasswordUser extends UserBase {
36+
/**
37+
* Uniquely identifies this class.
38+
*/
39+
public static readonly PROPERTY_INJECTION_ID: string = 'aws-cdk-lib.aws-elasticache.PasswordUser';
40+
41+
/**
42+
* Return whether the given object is a `PasswordUser`.
43+
*/
44+
public static isPasswordUser(x: any) : x is PasswordUser {
45+
return x !== null && typeof(x) === 'object' && ELASTICACHE_PASSWORDUSER_SYMBOL in x;
46+
}
47+
48+
/**
49+
* The engine for the user.
50+
*/
51+
public readonly engine?: UserEngine;
52+
/**
53+
* The user's ID.
54+
*
55+
* @attribute
56+
*/
57+
public readonly userId: string;
58+
/**
59+
* The user's name.
60+
*
61+
* @attribute
62+
*/
63+
public readonly userName?: string;
64+
/**
65+
* The access string that defines the user's permissions.
66+
*/
67+
public readonly accessString: string;
68+
/**
69+
* The user's ARN.
70+
*
71+
* @attribute
72+
*/
73+
public readonly userArn: string;
74+
/**
75+
* The user's status.
76+
* Can be 'active', 'modifying', 'deleting'.
77+
*
78+
* @attribute
79+
*/
80+
public readonly userStatus: string;
81+
82+
private readonly resource: CfnUser;
83+
84+
constructor(scope: Construct, id: string, props: PasswordUserProps) {
85+
super(scope, id);
86+
87+
// Enhanced CDK Analytics Telemetry
88+
addConstructMetadata(this, props);
89+
90+
this.engine = props.engine ?? UserEngine.VALKEY;
91+
this.userId = props.userId;
92+
this.userName = props.userName ?? props.userId;
93+
this.accessString = props.accessControl.accessString;
94+
95+
if (props.passwords.length > 2) {
96+
throw new ValidationError('Password authentication requires 1-2 passwords.', this);
97+
}
98+
99+
this.resource = new CfnUser(this, 'Resource', {
100+
engine: this.engine,
101+
userId: props.userId,
102+
userName: this.userName,
103+
accessString: this.accessString,
104+
authenticationMode: {
105+
Type: 'password',
106+
Passwords: props.passwords.map(p => p.unsafeUnwrap()),
107+
},
108+
noPasswordRequired: false,
109+
passwords: undefined,
110+
});
111+
112+
this.userArn = this.resource.attrArn;
113+
this.userStatus = this.resource.attrStatus;
114+
115+
Object.defineProperty(this, ELASTICACHE_PASSWORDUSER_SYMBOL, { value: true });
116+
}
117+
}

0 commit comments

Comments
 (0)