@aws-mdaa/dataops-job-l3-construct 1.3.0 → 1.5.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.
Files changed (35) hide show
  1. package/.jsii +114 -389
  2. package/lib/dataops-job-l3-construct.d.ts +30 -240
  3. package/lib/dataops-job-l3-construct.js +25 -25
  4. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/.npmignore +34 -0
  5. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/jest.config.js +5 -0
  6. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/lib/index.js +1 -1
  7. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/lib/index.ts +241 -0
  8. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/package.json +16 -18
  9. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/test/bucketpolicy-helper.test.d.ts +5 -0
  10. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/test/bucketpolicy-helper.test.js +200 -0
  11. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/test/bucketpolicy-helper.test.ts +215 -0
  12. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/tsconfig.json +40 -0
  13. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/tsconfig.tsbuildinfo +1 -0
  14. package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/typedoc.json +7 -0
  15. package/node_modules/@aws-mdaa/s3-inventory-helper/.npmignore +34 -0
  16. package/node_modules/@aws-mdaa/s3-inventory-helper/jest.config.js +5 -0
  17. package/node_modules/@aws-mdaa/s3-inventory-helper/lib/index.d.ts +2 -20
  18. package/node_modules/@aws-mdaa/s3-inventory-helper/lib/index.js +2 -11
  19. package/node_modules/@aws-mdaa/s3-inventory-helper/lib/index.ts +241 -0
  20. package/node_modules/@aws-mdaa/s3-inventory-helper/package.json +15 -17
  21. package/node_modules/@aws-mdaa/s3-inventory-helper/test/TODO +0 -0
  22. package/node_modules/@aws-mdaa/s3-inventory-helper/tsconfig.json +40 -0
  23. package/node_modules/@aws-mdaa/s3-inventory-helper/tsconfig.tsbuildinfo +1 -0
  24. package/node_modules/@aws-mdaa/s3-inventory-helper/typedoc.json +7 -0
  25. package/node_modules/lodash/README.md +2 -2
  26. package/node_modules/lodash/_baseUnset.js +47 -2
  27. package/node_modules/lodash/core.js +1 -1
  28. package/node_modules/lodash/core.min.js +1 -1
  29. package/node_modules/lodash/lodash.js +43 -4
  30. package/node_modules/lodash/lodash.min.js +57 -57
  31. package/node_modules/lodash/package.json +1 -1
  32. package/package.json +33 -47
  33. package/node_modules/lodash/flake.lock +0 -40
  34. package/node_modules/lodash/flake.nix +0 -20
  35. package/node_modules/lodash/release.md +0 -48
@@ -0,0 +1,241 @@
1
+ /*!
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { IPrincipal, PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam';
7
+ import { IBucket } from 'aws-cdk-lib/aws-s3';
8
+
9
+ export interface IRestrictObjectPrefixToRoles {
10
+ readonly s3Bucket: IBucket;
11
+ readonly s3Prefix: string;
12
+ readonly readRoleIds?: string[];
13
+ readonly readWriteRoleIds?: string[];
14
+ readonly readWriteSuperRoleIds?: string[];
15
+ readonly readPrincipals?: IPrincipal[];
16
+ readonly readWritePrincipals?: IPrincipal[];
17
+ readonly readWriteSuperPrincipals?: IPrincipal[];
18
+ }
19
+
20
+ export interface IRestrictBucketToRoles {
21
+ readonly s3Bucket: IBucket;
22
+ readonly roleExcludeIds: string[];
23
+ readonly principalExcludes?: string[];
24
+ readonly prefixExcludes?: string[];
25
+ readonly prefixIncludes?: string[];
26
+ }
27
+
28
+ /** Helper class for generating S3 bucket policy statements which grant access to specific object prefixes */
29
+ export class RestrictObjectPrefixToRoles {
30
+ static readonly READ_ACTIONS = ['s3:GetObject*'];
31
+ static readonly READ_WRITE_ACTIONS = [
32
+ ...RestrictObjectPrefixToRoles.READ_ACTIONS,
33
+ 's3:PutObject',
34
+ 's3:PutObjectTagging',
35
+ 's3:DeleteObject',
36
+ ];
37
+ static readonly READ_WRITE_SUPER_ACTIONS = [
38
+ ...RestrictObjectPrefixToRoles.READ_WRITE_ACTIONS,
39
+ 's3:DeleteObjectVersion',
40
+ ];
41
+ static readonly BUCKET_ALLOW_ACTIONS = ['s3:List*', 's3:GetBucket*'];
42
+ static readonly BUCKET_DENY_ACTIONS = ['s3:PutObject*', 's3:GetObject*', 's3:DeleteObject*'];
43
+
44
+ private _readStatements: PolicyStatement[] = [];
45
+ private _readWriteStatements: PolicyStatement[] = [];
46
+ private _readWriteSuperStatements: PolicyStatement[] = [];
47
+ private _formattedPrefix: string;
48
+
49
+ constructor(props: IRestrictObjectPrefixToRoles) {
50
+ this._formattedPrefix = '/' + this.formatS3Prefix(props.s3Prefix) + '/*';
51
+ // Covers our case where two / get resolved because our prefix is actually /
52
+ this._formattedPrefix = this._formattedPrefix.replace(/\/\//, '/');
53
+
54
+ // FEDERATED / READ
55
+ if (props.readRoleIds != undefined && props.readRoleIds.length > 0) {
56
+ // Construct our User:Id roles for read
57
+ const statement = this._readStatementScaffold(props);
58
+ statement.addCondition('StringLike', { 'aws:userId': props.readRoleIds.map(x => `${x}:*`) });
59
+ statement.addAnyPrincipal();
60
+ this._readStatements.push(statement);
61
+ }
62
+ // FEDERATED / READWRITE
63
+ if (props.readWriteRoleIds != undefined && props.readWriteRoleIds.length > 0) {
64
+ const statement = this._readWriteStatementScaffold(props);
65
+ statement.addCondition('StringLike', { 'aws:userId': props.readWriteRoleIds.map(x => `${x}:*`) });
66
+ statement.addAnyPrincipal();
67
+ this._readWriteStatements.push(statement);
68
+ }
69
+
70
+ // FEDERATED / READWRITESUPER
71
+ if (props.readWriteSuperRoleIds != undefined && props.readWriteSuperRoleIds.length > 0) {
72
+ const statement = this._readWriteSuperStatementScaffold(props);
73
+ statement.addCondition('StringLike', { 'aws:userId': props.readWriteSuperRoleIds.map(x => `${x}:*`) });
74
+ statement.addAnyPrincipal();
75
+ this._readWriteSuperStatements.push(statement);
76
+ }
77
+
78
+ // NONFEDERATED / READ
79
+ if (props.readPrincipals != undefined && props.readPrincipals.length > 0) {
80
+ const statement = this._readStatementScaffold(props);
81
+ props.readPrincipals.forEach(principal => {
82
+ statement.addPrincipals(principal);
83
+ });
84
+ this._readStatements.push(statement);
85
+ }
86
+ // NONFEDERATED / READWRITE
87
+ if (props.readWritePrincipals != undefined && props.readWritePrincipals.length > 0) {
88
+ const statement = this._readWriteStatementScaffold(props);
89
+ props.readWritePrincipals.forEach(principal => {
90
+ statement.addPrincipals(principal);
91
+ });
92
+ this._readWriteStatements.push(statement);
93
+ }
94
+ // NONFEDERATED / READWRITESUPER
95
+ if (props.readWriteSuperPrincipals != undefined && props.readWriteSuperPrincipals.length > 0) {
96
+ const statement = this._readWriteSuperStatementScaffold(props);
97
+ props.readWriteSuperPrincipals.forEach(principal => {
98
+ statement.addPrincipals(principal);
99
+ });
100
+ this._readWriteSuperStatements.push(statement);
101
+ }
102
+ }
103
+
104
+ private _readStatementScaffold(props: IRestrictObjectPrefixToRoles): PolicyStatement {
105
+ return new PolicyStatement({
106
+ sid: `${props.s3Prefix.replace(/\\W/g, '')}_Read`,
107
+ effect: Effect.ALLOW,
108
+ resources: [props.s3Bucket.bucketArn + this._formattedPrefix],
109
+ actions: RestrictObjectPrefixToRoles.READ_ACTIONS,
110
+ });
111
+ }
112
+
113
+ private _readWriteStatementScaffold(props: IRestrictObjectPrefixToRoles): PolicyStatement {
114
+ return new PolicyStatement({
115
+ sid: `${props.s3Prefix.replace(/\\W/g, '')}_ReadWrite`,
116
+ effect: Effect.ALLOW,
117
+ resources: [props.s3Bucket.bucketArn + this._formattedPrefix],
118
+ actions: RestrictObjectPrefixToRoles.READ_WRITE_ACTIONS,
119
+ });
120
+ }
121
+
122
+ private _readWriteSuperStatementScaffold(props: IRestrictObjectPrefixToRoles): PolicyStatement {
123
+ return new PolicyStatement({
124
+ sid: `${props.s3Prefix.replace(/\\W/g, '')}_ReadWriteSuper`,
125
+ effect: Effect.ALLOW,
126
+ resources: [props.s3Bucket.bucketArn + this._formattedPrefix],
127
+ actions: RestrictObjectPrefixToRoles.READ_WRITE_SUPER_ACTIONS,
128
+ });
129
+ }
130
+
131
+ public readStatements(): PolicyStatement[] {
132
+ return this._readStatements;
133
+ }
134
+
135
+ public readWriteStatements(): PolicyStatement[] {
136
+ return this._readWriteStatements;
137
+ }
138
+
139
+ public readWriteSuperStatements(): PolicyStatement[] {
140
+ return this._readWriteSuperStatements;
141
+ }
142
+
143
+ public statements(): PolicyStatement[] {
144
+ return [...this._readStatements, ...this._readWriteStatements, ...this._readWriteSuperStatements];
145
+ }
146
+
147
+ public formatS3Prefix(prefix: string): string {
148
+ let rawPrefix = prefix;
149
+
150
+ // Removes trailing slashes
151
+ rawPrefix = rawPrefix.endsWith('/') ? rawPrefix.slice(0, -1) : rawPrefix;
152
+ // Removes leading slashes
153
+ rawPrefix = rawPrefix.startsWith('/') ? rawPrefix.substring(1) : rawPrefix;
154
+ return rawPrefix;
155
+ }
156
+ }
157
+
158
+ /** Helper class for generating bucket policy statements
159
+ * which allow or deny access to an entire bucket. Used to
160
+ * create bucket-level default deny statements to block accesses
161
+ * not granted in the bucket policy. */
162
+ export class RestrictBucketToRoles {
163
+ public readonly denyStatement: PolicyStatement;
164
+ public readonly allowStatement: PolicyStatement;
165
+ private resource: string[] = [];
166
+ private notResource: string[] = [];
167
+ private denyConditionalNotEquals: {
168
+ 'aws:userId'?: string[];
169
+ 'aws:PrincipalArn'?: string[];
170
+ } = {};
171
+
172
+ constructor(props: IRestrictBucketToRoles) {
173
+ // Statement allowing access to the bucket for the AROAs
174
+ this.allowStatement = new PolicyStatement({
175
+ sid: `BucketAllow`,
176
+ effect: Effect.ALLOW,
177
+ resources: [props.s3Bucket.bucketArn + '/*', props.s3Bucket.bucketArn],
178
+ actions: RestrictObjectPrefixToRoles.BUCKET_ALLOW_ACTIONS,
179
+ });
180
+ this.allowStatement.addAnyPrincipal();
181
+ this.allowStatement.addCondition('StringLike', { 'aws:userId': props.roleExcludeIds.map(x => `${x}:*`) });
182
+
183
+ // Constuct our deny statement.
184
+ // prefixIncludes denotes we want to include a prefix in our deny meaning Resource
185
+ if (props.prefixIncludes) {
186
+ this.resource = props.prefixIncludes.map(prefix => {
187
+ return `${props.s3Bucket.bucketArn}/${this.formatS3Prefix(prefix)}`;
188
+ });
189
+ } else {
190
+ this.resource = [props.s3Bucket.bucketArn + '/*'];
191
+ }
192
+ // prefixExcludes denote we want to exclude a prefix in our deny meaning notResource
193
+ if (props.prefixExcludes) {
194
+ this.notResource = props.prefixExcludes.map(prefix => {
195
+ return `${props.s3Bucket.bucketArn}/${this.formatS3Prefix(prefix)}`;
196
+ });
197
+ }
198
+
199
+ if (this.notResource.length > 0) {
200
+ this.denyStatement = new PolicyStatement({
201
+ sid: `BucketDeny`,
202
+ effect: Effect.DENY,
203
+ notResources: this.notResource,
204
+ actions: RestrictObjectPrefixToRoles.BUCKET_DENY_ACTIONS,
205
+ });
206
+ } else {
207
+ this.denyStatement = new PolicyStatement({
208
+ sid: `BucketDeny`,
209
+ effect: Effect.DENY,
210
+ resources: this.resource,
211
+ actions: RestrictObjectPrefixToRoles.BUCKET_DENY_ACTIONS,
212
+ });
213
+ }
214
+ this.denyStatement.addAnyPrincipal();
215
+
216
+ // Build our conditionals.
217
+ this.denyConditionalNotEquals['aws:userId'] = props.roleExcludeIds.map(x => `${x}:*`);
218
+ if (props.principalExcludes && props.principalExcludes.length > 0) {
219
+ this.denyConditionalNotEquals['aws:PrincipalArn'] = [...new Set(props.principalExcludes)].sort((a, b) =>
220
+ a.localeCompare(b),
221
+ );
222
+ }
223
+
224
+ // Construct our conditional for our deny
225
+ if (Object.keys(this.denyConditionalNotEquals).length == 1) {
226
+ this.denyStatement.addCondition('StringNotLike', this.denyConditionalNotEquals);
227
+ } else {
228
+ this.denyStatement.addCondition('ForAnyValue:StringNotLike', this.denyConditionalNotEquals);
229
+ }
230
+ }
231
+
232
+ private formatS3Prefix(prefix: string): string {
233
+ let rawPrefix = prefix;
234
+
235
+ // Removes trailing slashes
236
+ rawPrefix = rawPrefix.endsWith('/') ? rawPrefix.slice(0, -1) : rawPrefix;
237
+ // Removes leading slashes
238
+ rawPrefix = rawPrefix.startsWith('/') ? rawPrefix.substring(1) : rawPrefix;
239
+ return `${rawPrefix}/*`;
240
+ }
241
+ }
@@ -5,35 +5,33 @@
5
5
  "name": "Amazon Web Services",
6
6
  "url": "https://aws.amazon.com/solutions"
7
7
  },
8
- "version": "1.3.0",
8
+ "version": "1.5.0",
9
9
  "license": "Apache-2.0",
10
10
  "main": "lib/index.js",
11
11
  "types": "lib/index.d.ts",
12
12
  "scripts": {
13
13
  "build": "tsc",
14
14
  "watch": "tsc -w",
15
- "test": "jest --passWithNoTests --coverage",
16
- "lint": "eslint --max-warnings 0 -c ../../../.eslintrc.json '**/*.{ts,tsx}' --ignore-pattern 'dist/*' --ignore-pattern 'node_modules/*' --ignore-pattern \"*.d.ts\" ",
17
- "test-coverage": "jest --passWithNoTests --coverage"
15
+ "test": "jest --passWithNoTests --testPathIgnorePatterns='.*\\.snapshot\\.test\\.ts'",
16
+ "lint": "eslint --max-warnings 0 -c ../../../eslint.config.mjs",
17
+ "test:coverage": "jest --passWithNoTests --coverage --testPathIgnorePatterns='.*\\.snapshot\\.test\\.ts'",
18
+ "test:snapshots": "jest --passWithNoTests --testPathPattern='.*\\.snapshot\\.test\\.ts'",
19
+ "test:snapshots:update": "jest --passWithNoTests --testPathPattern='.*\\.snapshot\\.test\\.ts' --updateSnapshot"
18
20
  },
19
21
  "devDependencies": {
20
- "@aws-mdaa/testing": "1.3.0",
21
- "@types/jest": "29.5.0",
22
- "@types/node": "17.0.23",
22
+ "@aws-mdaa/testing": "1.5.0",
23
+ "@types/jest": "29.5.14",
24
+ "@types/node": "22.9.0",
23
25
  "@types/prettier": "2.6.0",
24
- "jest": "29.5.0",
25
- "ts-jest": "29.1.0",
26
- "ts-node": "10.9.1",
27
- "typescript": "4.6.3",
28
- "typescript-json-schema": "0.63.0"
29
- },
30
- "overrides": {
31
- "aws-cdk-lib": "2.220.0",
32
- "@types/babel__traverse": "7.18.2"
26
+ "jest": "29.7.0",
27
+ "ts-jest": "29.4.6",
28
+ "ts-node": "10.9.2",
29
+ "typescript": "5.9.3",
30
+ "typescript-json-schema": "0.67.1"
33
31
  },
34
32
  "dependencies": {
35
- "@aws-mdaa/iam-role-helper": "1.3.0",
36
- "@aws-mdaa/naming": "1.3.0",
33
+ "@aws-mdaa/iam-role-helper": "1.5.0",
34
+ "@aws-mdaa/naming": "1.5.0",
37
35
  "aws-cdk-lib": "2.220.0",
38
36
  "cdk-nag": "2.37.55",
39
37
  "constructs": "10.0.96"
@@ -0,0 +1,5 @@
1
+ /*!
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ export {};
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const testing_1 = require("@aws-mdaa/testing");
8
+ const lib_1 = require("../lib");
9
+ const aws_s3_1 = require("aws-cdk-lib/aws-s3");
10
+ const aws_iam_1 = require("aws-cdk-lib/aws-iam");
11
+ describe('Test BucketPolicy Helper', () => {
12
+ const testApp = new testing_1.MdaaTestApp();
13
+ const testBucket = aws_s3_1.Bucket.fromBucketName(testApp.testStack, 'test-bucket', 'test-bucket');
14
+ describe('RestrictPrefix', () => {
15
+ const baseTestProps = {
16
+ s3Bucket: testBucket,
17
+ s3Prefix: 'test-prefix',
18
+ };
19
+ test('Read Role Ids', () => {
20
+ const testProps = {
21
+ ...baseTestProps,
22
+ readRoleIds: ['test-role-id-1', 'test-role-id-2'],
23
+ };
24
+ const restriction = new lib_1.RestrictObjectPrefixToRoles(testProps);
25
+ // console.log( JSON.stringify( restriction.statements()[ 0 ], undefined, 2 ) )
26
+ expect(restriction.statements().length).toBe(1);
27
+ expect(restriction.readStatements().length).toBe(1);
28
+ expect(restriction.readWriteSuperStatements().length).toBe(0);
29
+ expect(restriction.readWriteStatements().length).toBe(0);
30
+ expect(restriction.readStatements()[0].actions).toStrictEqual(['s3:GetObject*']);
31
+ expect(restriction.readStatements()[0].conditions).toStrictEqual({
32
+ StringLike: {
33
+ 'aws:userId': ['test-role-id-1:*', 'test-role-id-2:*'],
34
+ },
35
+ });
36
+ expect(restriction.readStatements()[0].effect).toBe('Allow');
37
+ expect(restriction.readStatements()[0].resources).toStrictEqual([
38
+ 'arn:test-partition:s3:::test-bucket/test-prefix/*',
39
+ ]);
40
+ });
41
+ test('ReadWrite Role Ids', () => {
42
+ const testProps = {
43
+ ...baseTestProps,
44
+ readWriteRoleIds: ['test-role-id-1', 'test-role-id-2'],
45
+ };
46
+ const restriction = new lib_1.RestrictObjectPrefixToRoles(testProps);
47
+ // console.log( JSON.stringify( restriction.statements()[ 0 ], undefined, 2 ) )
48
+ expect(restriction.statements().length).toBe(1);
49
+ expect(restriction.readWriteStatements().length).toBe(1);
50
+ expect(restriction.readStatements().length).toBe(0);
51
+ expect(restriction.readWriteSuperStatements().length).toBe(0);
52
+ expect(restriction.readWriteStatements()[0].actions).toStrictEqual([
53
+ 's3:GetObject*',
54
+ 's3:PutObject',
55
+ 's3:PutObjectTagging',
56
+ 's3:DeleteObject',
57
+ ]);
58
+ expect(restriction.readWriteStatements()[0].conditions).toStrictEqual({
59
+ StringLike: {
60
+ 'aws:userId': ['test-role-id-1:*', 'test-role-id-2:*'],
61
+ },
62
+ });
63
+ });
64
+ test('ReadWriteSuper Role Ids', () => {
65
+ const testProps = {
66
+ ...baseTestProps,
67
+ readWriteSuperRoleIds: ['test-role-id-1', 'test-role-id-2'],
68
+ };
69
+ const restriction = new lib_1.RestrictObjectPrefixToRoles(testProps);
70
+ // console.log( JSON.stringify( restriction.statements()[ 0 ], undefined, 2 ) )
71
+ expect(restriction.statements().length).toBe(1);
72
+ expect(restriction.readWriteSuperStatements().length).toBe(1);
73
+ expect(restriction.readStatements().length).toBe(0);
74
+ expect(restriction.readWriteStatements().length).toBe(0);
75
+ expect(restriction.readWriteSuperStatements()[0].actions).toStrictEqual([
76
+ 's3:GetObject*',
77
+ 's3:PutObject',
78
+ 's3:PutObjectTagging',
79
+ 's3:DeleteObject',
80
+ 's3:DeleteObjectVersion',
81
+ ]);
82
+ expect(restriction.readWriteSuperStatements()[0].conditions).toStrictEqual({
83
+ StringLike: {
84
+ 'aws:userId': ['test-role-id-1:*', 'test-role-id-2:*'],
85
+ },
86
+ });
87
+ });
88
+ test('Read Principals', () => {
89
+ const testProps = {
90
+ ...baseTestProps,
91
+ readPrincipals: [new aws_iam_1.ArnPrincipal('test-role-arn-1')],
92
+ };
93
+ const restriction = new lib_1.RestrictObjectPrefixToRoles(testProps);
94
+ // console.log( JSON.stringify( restriction.statements()[ 0 ], undefined, 2 ) )
95
+ expect(restriction.statements().length).toBe(1);
96
+ expect(restriction.readStatements().length).toBe(1);
97
+ expect(restriction.readWriteSuperStatements().length).toBe(0);
98
+ expect(restriction.readWriteStatements().length).toBe(0);
99
+ expect(restriction.readStatements()[0].actions).toStrictEqual(['s3:GetObject*']);
100
+ expect(restriction.readStatements()[0].effect).toBe('Allow');
101
+ expect(restriction.readStatements()[0].resources).toStrictEqual([
102
+ 'arn:test-partition:s3:::test-bucket/test-prefix/*',
103
+ ]);
104
+ expect(restriction.readStatements()[0].principals.length).toBe(1);
105
+ expect(JSON.stringify(restriction.readStatements()[0].principals[0])).toStrictEqual(JSON.stringify({ AWS: ['test-role-arn-1'] }));
106
+ });
107
+ test('ReadWrite Principals', () => {
108
+ const testProps = {
109
+ ...baseTestProps,
110
+ readWritePrincipals: [new aws_iam_1.ArnPrincipal('test-role-arn-1')],
111
+ };
112
+ const restriction = new lib_1.RestrictObjectPrefixToRoles(testProps);
113
+ // console.log( JSON.stringify( restriction.statements()[ 0 ], undefined, 2 ) )
114
+ expect(restriction.statements().length).toBe(1);
115
+ expect(restriction.readWriteStatements().length).toBe(1);
116
+ expect(restriction.readStatements().length).toBe(0);
117
+ expect(restriction.readWriteSuperStatements().length).toBe(0);
118
+ expect(restriction.readWriteStatements()[0].actions).toStrictEqual([
119
+ 's3:GetObject*',
120
+ 's3:PutObject',
121
+ 's3:PutObjectTagging',
122
+ 's3:DeleteObject',
123
+ ]);
124
+ expect(restriction.readWriteStatements()[0].effect).toBe('Allow');
125
+ expect(restriction.readWriteStatements()[0].resources).toStrictEqual([
126
+ 'arn:test-partition:s3:::test-bucket/test-prefix/*',
127
+ ]);
128
+ expect(restriction.readWriteStatements()[0].principals.length).toBe(1);
129
+ expect(JSON.stringify(restriction.readWriteStatements()[0].principals[0])).toStrictEqual(JSON.stringify({ AWS: ['test-role-arn-1'] }));
130
+ });
131
+ test('ReadWriteSuper Principals', () => {
132
+ const testProps = {
133
+ ...baseTestProps,
134
+ readWriteSuperPrincipals: [new aws_iam_1.ArnPrincipal('test-role-arn-1')],
135
+ };
136
+ const restriction = new lib_1.RestrictObjectPrefixToRoles(testProps);
137
+ // console.log( JSON.stringify( restriction.statements()[ 0 ], undefined, 2 ) )
138
+ expect(restriction.statements().length).toBe(1);
139
+ expect(restriction.readStatements().length).toBe(0);
140
+ expect(restriction.readWriteStatements().length).toBe(0);
141
+ expect(restriction.readWriteSuperStatements().length).toBe(1);
142
+ expect(restriction.readWriteSuperStatements()[0].actions).toStrictEqual([
143
+ 's3:GetObject*',
144
+ 's3:PutObject',
145
+ 's3:PutObjectTagging',
146
+ 's3:DeleteObject',
147
+ 's3:DeleteObjectVersion',
148
+ ]);
149
+ expect(restriction.readWriteSuperStatements()[0].effect).toBe('Allow');
150
+ expect(restriction.readWriteSuperStatements()[0].resources).toStrictEqual([
151
+ 'arn:test-partition:s3:::test-bucket/test-prefix/*',
152
+ ]);
153
+ expect(restriction.readWriteSuperStatements()[0].principals.length).toBe(1);
154
+ expect(JSON.stringify(restriction.readWriteSuperStatements()[0].principals[0])).toStrictEqual(JSON.stringify({ AWS: ['test-role-arn-1'] }));
155
+ });
156
+ });
157
+ describe('RestrictBucket', () => {
158
+ const baseTestProps = {
159
+ s3Bucket: testBucket,
160
+ roleExcludeIds: ['test-role-id-1', 'test-role-id-2'],
161
+ principalExcludes: ['test-arn'],
162
+ prefixExcludes: ['exclude-prefix'],
163
+ prefixIncludes: ['exclude-prefix'],
164
+ };
165
+ test('Base Allow', () => {
166
+ const testProps = {
167
+ ...baseTestProps,
168
+ };
169
+ const restriction = new lib_1.RestrictBucketToRoles(testProps);
170
+ console.log(JSON.stringify(restriction.allowStatement, undefined, 2));
171
+ expect(restriction.allowStatement.actions).toStrictEqual(['s3:List*', 's3:GetBucket*']);
172
+ expect(restriction.allowStatement.effect).toBe('Allow');
173
+ expect(restriction.allowStatement.conditions).toStrictEqual({
174
+ StringLike: {
175
+ 'aws:userId': ['test-role-id-1:*', 'test-role-id-2:*'],
176
+ },
177
+ });
178
+ expect(restriction.allowStatement.resources).toStrictEqual([
179
+ 'arn:test-partition:s3:::test-bucket/*',
180
+ 'arn:test-partition:s3:::test-bucket',
181
+ ]);
182
+ });
183
+ test('Base Deny', () => {
184
+ const testProps = {
185
+ ...baseTestProps,
186
+ };
187
+ const restriction = new lib_1.RestrictBucketToRoles(testProps);
188
+ console.log(JSON.stringify(restriction.denyStatement, undefined, 2));
189
+ expect(restriction.denyStatement.actions).toStrictEqual(['s3:PutObject*', 's3:GetObject*', 's3:DeleteObject*']);
190
+ expect(restriction.denyStatement.effect).toBe('Deny');
191
+ expect(restriction.denyStatement.conditions).toStrictEqual({
192
+ 'ForAnyValue:StringNotLike': {
193
+ 'aws:userId': ['test-role-id-1:*', 'test-role-id-2:*'],
194
+ 'aws:PrincipalArn': ['test-arn'],
195
+ },
196
+ });
197
+ });
198
+ });
199
+ });
200
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVja2V0cG9saWN5LWhlbHBlci50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYnVja2V0cG9saWN5LWhlbHBlci50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O0dBR0c7O0FBRUgsK0NBQWdEO0FBQ2hELGdDQUtnQjtBQUNoQiwrQ0FBNEM7QUFDNUMsaURBQW1EO0FBRW5ELFFBQVEsQ0FBQywwQkFBMEIsRUFBRSxHQUFHLEVBQUU7SUFDeEMsTUFBTSxPQUFPLEdBQUcsSUFBSSxxQkFBVyxFQUFFLENBQUM7SUFDbEMsTUFBTSxVQUFVLEdBQUcsZUFBTSxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLGFBQWEsRUFBRSxhQUFhLENBQUMsQ0FBQztJQUMxRixRQUFRLENBQUMsZ0JBQWdCLEVBQUUsR0FBRyxFQUFFO1FBQzlCLE1BQU0sYUFBYSxHQUFpQztZQUNsRCxRQUFRLEVBQUUsVUFBVTtZQUNwQixRQUFRLEVBQUUsYUFBYTtTQUN4QixDQUFDO1FBQ0YsSUFBSSxDQUFDLGVBQWUsRUFBRSxHQUFHLEVBQUU7WUFDekIsTUFBTSxTQUFTLEdBQWlDO2dCQUM5QyxHQUFHLGFBQWE7Z0JBQ2hCLFdBQVcsRUFBRSxDQUFDLGdCQUFnQixFQUFFLGdCQUFnQixDQUFDO2FBQ2xELENBQUM7WUFDRixNQUFNLFdBQVcsR0FBRyxJQUFJLGlDQUEyQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQy9ELCtFQUErRTtZQUMvRSxNQUFNLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNoRCxNQUFNLENBQUMsV0FBVyxDQUFDLGNBQWMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNwRCxNQUFNLENBQUMsV0FBVyxDQUFDLHdCQUF3QixFQUFFLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFD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LEVBQUUsa0JBQWtCLENBQUMsQ0FBQyxDQUFDO1lBQ2hILE1BQU0sQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUN0RCxNQUFNLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxhQUFhLENBQUM7Z0JBQ3pELDJCQUEyQixFQUFFO29CQUMzQixZQUFZLEVBQUUsQ0FBQyxrQkFBa0IsRUFBRSxrQkFBa0IsQ0FBQztvQkFDdEQsa0JBQWtCLEVBQUUsQ0FBQyxVQUFVLENBQUM7aUJBQ2pDO2FBQ0YsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyohXG4gKiBDb3B5cmlnaHQgQW1hem9uLmNvbSwgSW5jLiBvciBpdHMgYWZmaWxpYXRlcy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG4gKi9cblxuaW1wb3J0IHsgTWRhYVRlc3RBcHAgfSBmcm9tICdAYXdzLW1kYWEvdGVzdGluZyc7XG5pbXBvcnQge1xuICBJUmVzdHJpY3RCdWNrZXRUb1JvbGVzLFxuICBJUmVzdHJpY3RPYmplY3RQcmVmaXhUb1JvbGVzLFxuICBSZXN0cmljdEJ1Y2tldFRvUm9sZXMsXG4gIFJlc3RyaWN0T2JqZWN0UHJlZml4VG9Sb2xlcyxcbn0gZnJvbSAnLi4vbGliJztcbmltcG9ydCB7IEJ1Y2tldCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMyc7XG5pbXBvcnQgeyBBcm5QcmluY2lwYWwgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcblxuZGVzY3JpYmUoJ1Rlc3QgQnVja2V0UG9saWN5IEhlbHBlcicsICgpID0+IHtcbiAgY29uc3QgdGVzdEFwcCA9IG5ldyBNZGFhVGVzdEFwcCgpO1xuICBjb25zdCB0ZXN0QnVja2V0ID0gQnVja2V0LmZyb21CdWNrZXROYW1lKHRlc3RBcHAudGVzdFN0YWNrLCAndGVzdC1idWNrZXQnLCAndGVzdC1idWNrZXQnKTtcbiAgZGVzY3JpYmUoJ1Jlc3RyaWN0UHJlZml4JywgKCkgPT4ge1xuICAgIGNvbnN0IGJhc2VUZXN0UHJvcHM6IElSZXN0cmljdE9iamVjdFByZWZpeFRvUm9sZXMgPSB7XG4gICAgICBzM0J1Y2tldDogdGVzdEJ1Y2tldCxcbiAgICAgIHMzUHJlZml4OiAndGVzdC1wcmVmaXgnLFxuICAgIH07XG4gICAgdGVzdCgnUmVhZCBSb2xlIElkcycsICgpID0+IHtcbiAgICAgIGNvbnN0IHRlc3RQcm9wczogSVJlc3RyaWN0T2JqZWN0UHJlZml4VG9Sb2xlcyA9IHtcbiAgICAgICAgLi4uYmFzZVRlc3RQcm9wcyxcbiAgICAgICAgcmVhZFJvbGVJZHM6IFsndGVzdC1yb2xlLWlkLTEnLCAndGVzdC1yb2xlLWlkLTInXSxcbiAgICAgIH07XG4gICAgICBjb25zdCByZXN0cmljdGlvbiA9IG5ldyBSZXN0cmljdE9iamVjdFByZWZpeFRvUm9sZXModGVzdFByb3BzKTtcbiAgICAgIC8vIGNvbnNvbGUubG9nKCBKU09OLnN0cmluZ2lmeSggcmVzdHJpY3Rpb24uc3RhdGVtZW50cygpWyAwIF0sIHVuZGVmaW5lZCwgMiApIClcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5zdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDEpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDEpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN1cGVyU3RhdGVtZW50cygpLmxlbmd0aCkudG9CZSgwKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkV3JpdGVTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDApO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRTdGF0ZW1lbnRzKClbMF0uYWN0aW9ucykudG9TdHJpY3RFcXVhbChbJ3MzOkdldE9iamVjdConXSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFN0YXRlbWVudHMoKVswXS5jb25kaXRpb25zKS50b1N0cmljdEVxdWFsKHtcbiAgICAgICAgU3RyaW5nTGlrZToge1xuICAgICAgICAgICdhd3M6dXNlcklkJzogWyd0ZXN0LXJvbGUtaWQtMToqJywgJ3Rlc3Qtcm9sZS1pZC0yOionXSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRTdGF0ZW1lbnRzKClbMF0uZWZmZWN0KS50b0JlKCdBbGxvdycpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRTdGF0ZW1lbnRzKClbMF0ucmVzb3VyY2VzKS50b1N0cmljdEVxdWFsKFtcbiAgICAgICAgJ2Fybjp0ZXN0LXBhcnRpdGlvbjpzMzo6OnRlc3QtYnVja2V0L3Rlc3QtcHJlZml4LyonLFxuICAgICAgXSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdSZWFkV3JpdGUgUm9sZSBJZHMnLCAoKSA9PiB7XG4gICAgICBjb25zdCB0ZXN0UHJvcHM6IElSZXN0cmljdE9iamVjdFByZWZpeFRvUm9sZXMgPSB7XG4gICAgICAgIC4uLmJhc2VUZXN0UHJvcHMsXG4gICAgICAgIHJlYWRXcml0ZVJvbGVJZHM6IFsndGVzdC1yb2xlLWlkLTEnLCAndGVzdC1yb2xlLWlkLTInXSxcbiAgICAgIH07XG4gICAgICBjb25zdCByZXN0cmljdGlvbiA9IG5ldyBSZXN0cmljdE9iamVjdFByZWZpeFRvUm9sZXModGVzdFByb3BzKTtcbiAgICAgIC8vIGNvbnNvbGUubG9nKCBKU09OLnN0cmluZ2lmeSggcmVzdHJpY3Rpb24uc3RhdGVtZW50cygpWyAwIF0sIHVuZGVmaW5lZCwgMiApIClcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5zdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDEpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN0YXRlbWVudHMoKS5sZW5ndGgpLnRvQmUoMSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFN0YXRlbWVudHMoKS5sZW5ndGgpLnRvQmUoMCk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFdyaXRlU3VwZXJTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDApO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN0YXRlbWVudHMoKVswXS5hY3Rpb25zKS50b1N0cmljdEVxdWFsKFtcbiAgICAgICAgJ3MzOkdldE9iamVjdConLFxuICAgICAgICAnczM6UHV0T2JqZWN0JyxcbiAgICAgICAgJ3MzOlB1dE9iamVjdFRhZ2dpbmcnLFxuICAgICAgICAnczM6RGVsZXRlT2JqZWN0JyxcbiAgICAgIF0pO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN0YXRlbWVudHMoKVswXS5jb25kaXRpb25zKS50b1N0cmljdEVxdWFsKHtcbiAgICAgICAgU3RyaW5nTGlrZToge1xuICAgICAgICAgICdhd3M6dXNlcklkJzogWyd0ZXN0LXJvbGUtaWQtMToqJywgJ3Rlc3Qtcm9sZS1pZC0yOionXSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnUmVhZFdyaXRlU3VwZXIgUm9sZSBJZHMnLCAoKSA9PiB7XG4gICAgICBjb25zdCB0ZXN0UHJvcHM6IElSZXN0cmljdE9iamVjdFByZWZpeFRvUm9sZXMgPSB7XG4gICAgICAgIC4uLmJhc2VUZXN0UHJvcHMsXG4gICAgICAgIHJlYWRXcml0ZVN1cGVyUm9sZUlkczogWyd0ZXN0LXJvbGUtaWQtMScsICd0ZXN0LXJvbGUtaWQtMiddLFxuICAgICAgfTtcbiAgICAgIGNvbnN0IHJlc3RyaWN0aW9uID0gbmV3IFJlc3RyaWN0T2JqZWN0UHJlZml4VG9Sb2xlcyh0ZXN0UHJvcHMpO1xuICAgICAgLy8gY29uc29sZS5sb2coIEpTT04uc3RyaW5naWZ5KCByZXN0cmljdGlvbi5zdGF0ZW1lbnRzKClbIDAgXSwgdW5kZWZpbmVkLCAyICkgKVxuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnN0YXRlbWVudHMoKS5sZW5ndGgpLnRvQmUoMSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFdyaXRlU3VwZXJTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDEpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDApO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN0YXRlbWVudHMoKS5sZW5ndGgpLnRvQmUoMCk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFdyaXRlU3VwZXJTdGF0ZW1lbnRzKClbMF0uYWN0aW9ucykudG9TdHJpY3RFcXVhbChbXG4gICAgICAgICdzMzpHZXRPYmplY3QqJyxcbiAgICAgICAgJ3MzOlB1dE9iamVjdCcsXG4gICAgICAgICdzMzpQdXRPYmplY3RUYWdnaW5nJyxcbiAgICAgICAgJ3MzOkRlbGV0ZU9iamVjdCcsXG4gICAgICAgICdzMzpEZWxldGVPYmplY3RWZXJzaW9uJyxcbiAgICAgIF0pO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN1cGVyU3RhdGVtZW50cygpWzBdLmNvbmRpdGlvbnMpLnRvU3RyaWN0RXF1YWwoe1xuICAgICAgICBTdHJpbmdMaWtlOiB7XG4gICAgICAgICAgJ2F3czp1c2VySWQnOiBbJ3Rlc3Qtcm9sZS1pZC0xOionLCAndGVzdC1yb2xlLWlkLTI6KiddLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdSZWFkIFByaW5jaXBhbHMnLCAoKSA9PiB7XG4gICAgICBjb25zdCB0ZXN0UHJvcHM6IElSZXN0cmljdE9iamVjdFByZWZpeFRvUm9sZXMgPSB7XG4gICAgICAgIC4uLmJhc2VUZXN0UHJvcHMsXG4gICAgICAgIHJlYWRQcmluY2lwYWxzOiBbbmV3IEFyblByaW5jaXBhbCgndGVzdC1yb2xlLWFybi0xJyldLFxuICAgICAgfTtcbiAgICAgIGNvbnN0IHJlc3RyaWN0aW9uID0gbmV3IFJlc3RyaWN0T2JqZWN0UHJlZml4VG9Sb2xlcyh0ZXN0UHJvcHMpO1xuICAgICAgLy8gY29uc29sZS5sb2coIEpTT04uc3RyaW5naWZ5KCByZXN0cmljdGlvbi5zdGF0ZW1lbnRzKClbIDAgXSwgdW5kZWZpbmVkLCAyICkgKVxuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnN0YXRlbWVudHMoKS5sZW5ndGgpLnRvQmUoMSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFN0YXRlbWVudHMoKS5sZW5ndGgpLnRvQmUoMSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFdyaXRlU3VwZXJTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDApO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN0YXRlbWVudHMoKS5sZW5ndGgpLnRvQmUoMCk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFN0YXRlbWVudHMoKVswXS5hY3Rpb25zKS50b1N0cmljdEVxdWFsKFsnczM6R2V0T2JqZWN0KiddKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkU3RhdGVtZW50cygpWzBdLmVmZmVjdCkudG9CZSgnQWxsb3cnKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkU3RhdGVtZW50cygpWzBdLnJlc291cmNlcykudG9TdHJpY3RFcXVhbChbXG4gICAgICAgICdhcm46dGVzdC1wYXJ0aXRpb246czM6Ojp0ZXN0LWJ1Y2tldC90ZXN0LXByZWZpeC8qJyxcbiAgICAgIF0pO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRTdGF0ZW1lbnRzKClbMF0ucHJpbmNpcGFscy5sZW5ndGgpLnRvQmUoMSk7XG4gICAgICBleHBlY3QoSlNPTi5zdHJpbmdpZnkocmVzdHJpY3Rpb24ucmVhZFN0YXRlbWVudHMoKVswXS5wcmluY2lwYWxzWzBdKSkudG9TdHJpY3RFcXVhbChcbiAgICAgICAgSlNPTi5zdHJpbmdpZnkoeyBBV1M6IFsndGVzdC1yb2xlLWFybi0xJ10gfSksXG4gICAgICApO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnUmVhZFdyaXRlIFByaW5jaXBhbHMnLCAoKSA9PiB7XG4gICAgICBjb25zdCB0ZXN0UHJvcHM6IElSZXN0cmljdE9iamVjdFByZWZpeFRvUm9sZXMgPSB7XG4gICAgICAgIC4uLmJhc2VUZXN0UHJvcHMsXG4gICAgICAgIHJlYWRXcml0ZVByaW5jaXBhbHM6IFtuZXcgQXJuUHJpbmNpcGFsKCd0ZXN0LXJvbGUtYXJuLTEnKV0sXG4gICAgICB9O1xuICAgICAgY29uc3QgcmVzdHJpY3Rpb24gPSBuZXcgUmVzdHJpY3RPYmplY3RQcmVmaXhUb1JvbGVzKHRlc3RQcm9wcyk7XG4gICAgICAvLyBjb25zb2xlLmxvZyggSlNPTi5zdHJpbmdpZnkoIHJlc3RyaWN0aW9uLnN0YXRlbWVudHMoKVsgMCBdLCB1bmRlZmluZWQsIDIgKSApXG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24uc3RhdGVtZW50cygpLmxlbmd0aCkudG9CZSgxKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkV3JpdGVTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDEpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDApO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN1cGVyU3RhdGVtZW50cygpLmxlbmd0aCkudG9CZSgwKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkV3JpdGVTdGF0ZW1lbnRzKClbMF0uYWN0aW9ucykudG9TdHJpY3RFcXVhbChbXG4gICAgICAgICdzMzpHZXRPYmplY3QqJyxcbiAgICAgICAgJ3MzOlB1dE9iamVjdCcsXG4gICAgICAgICdzMzpQdXRPYmplY3RUYWdnaW5nJyxcbiAgICAgICAgJ3MzOkRlbGV0ZU9iamVjdCcsXG4gICAgICBdKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkV3JpdGVTdGF0ZW1lbnRzKClbMF0uZWZmZWN0KS50b0JlKCdBbGxvdycpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN0YXRlbWVudHMoKVswXS5yZXNvdXJjZXMpLnRvU3RyaWN0RXF1YWwoW1xuICAgICAgICAnYXJuOnRlc3QtcGFydGl0aW9uOnMzOjo6dGVzdC1idWNrZXQvdGVzdC1wcmVmaXgvKicsXG4gICAgICBdKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkV3JpdGVTdGF0ZW1lbnRzKClbMF0ucHJpbmNpcGFscy5sZW5ndGgpLnRvQmUoMSk7XG4gICAgICBleHBlY3QoSlNPTi5zdHJpbmdpZnkocmVzdHJpY3Rpb24ucmVhZFdyaXRlU3RhdGVtZW50cygpWzBdLnByaW5jaXBhbHNbMF0pKS50b1N0cmljdEVxdWFsKFxuICAgICAgICBKU09OLnN0cmluZ2lmeSh7IEFXUzogWyd0ZXN0LXJvbGUtYXJuLTEnXSB9KSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdSZWFkV3JpdGVTdXBlciBQcmluY2lwYWxzJywgKCkgPT4ge1xuICAgICAgY29uc3QgdGVzdFByb3BzOiBJUmVzdHJpY3RPYmplY3RQcmVmaXhUb1JvbGVzID0ge1xuICAgICAgICAuLi5iYXNlVGVzdFByb3BzLFxuICAgICAgICByZWFkV3JpdGVTdXBlclByaW5jaXBhbHM6IFtuZXcgQXJuUHJpbmNpcGFsKCd0ZXN0LXJvbGUtYXJuLTEnKV0sXG4gICAgICB9O1xuICAgICAgY29uc3QgcmVzdHJpY3Rpb24gPSBuZXcgUmVzdHJpY3RPYmplY3RQcmVmaXhUb1JvbGVzKHRlc3RQcm9wcyk7XG4gICAgICAvLyBjb25zb2xlLmxvZyggSlNPTi5zdHJpbmdpZnkoIHJlc3RyaWN0aW9uLnN0YXRlbWVudHMoKVsgMCBdLCB1bmRlZmluZWQsIDIgKSApXG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24uc3RhdGVtZW50cygpLmxlbmd0aCkudG9CZSgxKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkU3RhdGVtZW50cygpLmxlbmd0aCkudG9CZSgwKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkV3JpdGVTdGF0ZW1lbnRzKCkubGVuZ3RoKS50b0JlKDApO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN1cGVyU3RhdGVtZW50cygpLmxlbmd0aCkudG9CZSgxKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5yZWFkV3JpdGVTdXBlclN0YXRlbWVudHMoKVswXS5hY3Rpb25zKS50b1N0cmljdEVxdWFsKFtcbiAgICAgICAgJ3MzOkdldE9iamVjdConLFxuICAgICAgICAnczM6UHV0T2JqZWN0JyxcbiAgICAgICAgJ3MzOlB1dE9iamVjdFRhZ2dpbmcnLFxuICAgICAgICAnczM6RGVsZXRlT2JqZWN0JyxcbiAgICAgICAgJ3MzOkRlbGV0ZU9iamVjdFZlcnNpb24nLFxuICAgICAgXSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24ucmVhZFdyaXRlU3VwZXJTdGF0ZW1lbnRzKClbMF0uZWZmZWN0KS50b0JlKCdBbGxvdycpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN1cGVyU3RhdGVtZW50cygpWzBdLnJlc291cmNlcykudG9TdHJpY3RFcXVhbChbXG4gICAgICAgICdhcm46dGVzdC1wYXJ0aXRpb246czM6Ojp0ZXN0LWJ1Y2tldC90ZXN0LXByZWZpeC8qJyxcbiAgICAgIF0pO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN1cGVyU3RhdGVtZW50cygpWzBdLnByaW5jaXBhbHMubGVuZ3RoKS50b0JlKDEpO1xuICAgICAgZXhwZWN0KEpTT04uc3RyaW5naWZ5KHJlc3RyaWN0aW9uLnJlYWRXcml0ZVN1cGVyU3RhdGVtZW50cygpWzBdLnByaW5jaXBhbHNbMF0pKS50b1N0cmljdEVxdWFsKFxuICAgICAgICBKU09OLnN0cmluZ2lmeSh7IEFXUzogWyd0ZXN0LXJvbGUtYXJuLTEnXSB9KSxcbiAgICAgICk7XG4gICAgfSk7XG4gIH0pO1xuICBkZXNjcmliZSgnUmVzdHJpY3RCdWNrZXQnLCAoKSA9PiB7XG4gICAgY29uc3QgYmFzZVRlc3RQcm9wczogSVJlc3RyaWN0QnVja2V0VG9Sb2xlcyA9IHtcbiAgICAgIHMzQnVja2V0OiB0ZXN0QnVja2V0LFxuICAgICAgcm9sZUV4Y2x1ZGVJZHM6IFsndGVzdC1yb2xlLWlkLTEnLCAndGVzdC1yb2xlLWlkLTInXSxcbiAgICAgIHByaW5jaXBhbEV4Y2x1ZGVzOiBbJ3Rlc3QtYXJuJ10sXG4gICAgICBwcmVmaXhFeGNsdWRlczogWydleGNsdWRlLXByZWZpeCddLFxuICAgICAgcHJlZml4SW5jbHVkZXM6IFsnZXhjbHVkZS1wcmVmaXgnXSxcbiAgICB9O1xuICAgIHRlc3QoJ0Jhc2UgQWxsb3cnLCAoKSA9PiB7XG4gICAgICBjb25zdCB0ZXN0UHJvcHM6IElSZXN0cmljdEJ1Y2tldFRvUm9sZXMgPSB7XG4gICAgICAgIC4uLmJhc2VUZXN0UHJvcHMsXG4gICAgICB9O1xuICAgICAgY29uc3QgcmVzdHJpY3Rpb24gPSBuZXcgUmVzdHJpY3RCdWNrZXRUb1JvbGVzKHRlc3RQcm9wcyk7XG4gICAgICBjb25zb2xlLmxvZyhKU09OLnN0cmluZ2lmeShyZXN0cmljdGlvbi5hbGxvd1N0YXRlbWVudCwgdW5kZWZpbmVkLCAyKSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24uYWxsb3dTdGF0ZW1lbnQuYWN0aW9ucykudG9TdHJpY3RFcXVhbChbJ3MzOkxpc3QqJywgJ3MzOkdldEJ1Y2tldConXSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24uYWxsb3dTdGF0ZW1lbnQuZWZmZWN0KS50b0JlKCdBbGxvdycpO1xuICAgICAgZXhwZWN0KHJlc3RyaWN0aW9uLmFsbG93U3RhdGVtZW50LmNvbmRpdGlvbnMpLnRvU3RyaWN0RXF1YWwoe1xuICAgICAgICBTdHJpbmdMaWtlOiB7XG4gICAgICAgICAgJ2F3czp1c2VySWQnOiBbJ3Rlc3Qtcm9sZS1pZC0xOionLCAndGVzdC1yb2xlLWlkLTI6KiddLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24uYWxsb3dTdGF0ZW1lbnQucmVzb3VyY2VzKS50b1N0cmljdEVxdWFsKFtcbiAgICAgICAgJ2Fybjp0ZXN0LXBhcnRpdGlvbjpzMzo6OnRlc3QtYnVja2V0LyonLFxuICAgICAgICAnYXJuOnRlc3QtcGFydGl0aW9uOnMzOjo6dGVzdC1idWNrZXQnLFxuICAgICAgXSk7XG4gICAgfSk7XG4gICAgdGVzdCgnQmFzZSBEZW55JywgKCkgPT4ge1xuICAgICAgY29uc3QgdGVzdFByb3BzOiBJUmVzdHJpY3RCdWNrZXRUb1JvbGVzID0ge1xuICAgICAgICAuLi5iYXNlVGVzdFByb3BzLFxuICAgICAgfTtcbiAgICAgIGNvbnN0IHJlc3RyaWN0aW9uID0gbmV3IFJlc3RyaWN0QnVja2V0VG9Sb2xlcyh0ZXN0UHJvcHMpO1xuICAgICAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkocmVzdHJpY3Rpb24uZGVueVN0YXRlbWVudCwgdW5kZWZpbmVkLCAyKSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24uZGVueVN0YXRlbWVudC5hY3Rpb25zKS50b1N0cmljdEVxdWFsKFsnczM6UHV0T2JqZWN0KicsICdzMzpHZXRPYmplY3QqJywgJ3MzOkRlbGV0ZU9iamVjdConXSk7XG4gICAgICBleHBlY3QocmVzdHJpY3Rpb24uZGVueVN0YXRlbWVudC5lZmZlY3QpLnRvQmUoJ0RlbnknKTtcbiAgICAgIGV4cGVjdChyZXN0cmljdGlvbi5kZW55U3RhdGVtZW50LmNvbmRpdGlvbnMpLnRvU3RyaWN0RXF1YWwoe1xuICAgICAgICAnRm9yQW55VmFsdWU6U3RyaW5nTm90TGlrZSc6IHtcbiAgICAgICAgICAnYXdzOnVzZXJJZCc6IFsndGVzdC1yb2xlLWlkLTE6KicsICd0ZXN0LXJvbGUtaWQtMjoqJ10sXG4gICAgICAgICAgJ2F3czpQcmluY2lwYWxBcm4nOiBbJ3Rlc3QtYXJuJ10sXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICB9KTtcbiAgfSk7XG59KTtcbiJdfQ==