@guardian/cdk 62.5.3 → 62.6.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.
@@ -1,5 +1,4 @@
1
1
  export * from "./policies";
2
2
  export * from "./roles";
3
3
  export * from "./fastly-logs-iam";
4
- export * from "./janus-assumable-role";
5
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/constructs/iam/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/constructs/iam/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC"}
@@ -17,5 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./policies"), exports);
18
18
  __exportStar(require("./roles"), exports);
19
19
  __exportStar(require("./fastly-logs-iam"), exports);
20
- __exportStar(require("./janus-assumable-role"), exports);
21
20
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/constructs/iam/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,0CAAwB;AACxB,oDAAkC;AAClC,yDAAuC","sourcesContent":["export * from \"./policies\";\nexport * from \"./roles\";\nexport * from \"./fastly-logs-iam\";\nexport * from \"./janus-assumable-role\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/constructs/iam/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,0CAAwB;AACxB,oDAAkC","sourcesContent":["export * from \"./policies\";\nexport * from \"./roles\";\nexport * from \"./fastly-logs-iam\";\n"]}
@@ -1,15 +1,7 @@
1
- import { ManagedPolicy, PolicyStatement } from "aws-cdk-lib/aws-iam";
1
+ import { type PolicyStatement } from "aws-cdk-lib/aws-iam";
2
+ import { ManagedPolicy } from "aws-cdk-lib/aws-iam";
2
3
  import type { GuStack } from "../../../../constructs/core";
3
- import type { GuAllowPolicyProps, GuDenyPolicyProps } from "../../../../constructs/iam";
4
4
  export type GuWorkloadPolicyProps = {
5
- /**
6
- * List of explicitly allowed permissions given by this policy.
7
- */
8
- readonly allow?: GuAllowPolicyProps[];
9
- /**
10
- * List of explicitly denied permissions which can be used to fine tune this policy by pruning the allow permissions.
11
- */
12
- readonly deny?: GuDenyPolicyProps[];
13
5
  /**
14
6
  * Initial set of permissions to add to this policy document.
15
7
  * You can also use `addPermission(statement)` to add permissions later.
@@ -1 +1 @@
1
- {"version":3,"file":"developer-policy.d.ts","sourceRoot":"","sources":["../../../../../src/experimental/constructs/iam/policies/developer-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAU,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAExF,MAAM,MAAM,qBAAqB,GAAG;IAClC;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACtC;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IACxC;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,6BAA8B,SAAQ,aAAa;gBAClD,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB;CAsErE"}
1
+ {"version":3,"file":"developer-policy.d.ts","sourceRoot":"","sources":["../../../../../src/experimental/constructs/iam/policies/developer-policy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAoB,aAAa,EAAuB,MAAM,qBAAqB,CAAC;AAE3F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IACxC;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,6BAA8B,SAAQ,aAAa;gBAClD,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB;CAerE"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GuDeveloperPolicyExperimental = void 0;
4
4
  const aws_cdk_lib_1 = require("aws-cdk-lib");
5
5
  const aws_iam_1 = require("aws-cdk-lib/aws-iam");
6
+ const aws_iam_2 = require("aws-cdk-lib/aws-iam");
6
7
  /**
7
8
  * Creates a structured `AWS::IAM::ManagedPolicy` resource to manage arbitrary permissions on general account
8
9
  * resources which can then be used to create limited permission credentials for use with specific activities.
@@ -36,68 +37,93 @@ const aws_iam_1 = require("aws-cdk-lib/aws-iam");
36
37
  *
37
38
  * @experimental
38
39
  */
39
- class GuDeveloperPolicyExperimental extends aws_iam_1.ManagedPolicy {
40
+ class GuDeveloperPolicyExperimental extends aws_iam_2.ManagedPolicy {
40
41
  constructor(scope, id, props) {
41
42
  super(scope, id, {
42
43
  description: `${props.permission} developer policy`,
43
44
  ...props,
44
45
  path: `/developer-policy/${props.permission}/`,
45
46
  });
46
- let valid = true;
47
- const { statements = [] } = props;
48
- for (const statement of statements) {
49
- if (statement.effect === aws_iam_1.Effect.ALLOW) {
50
- for (const action of statement.actions) {
51
- if (action === "*") {
52
- const name = statement.actions.join(",");
53
- aws_cdk_lib_1.Annotations.of(this).addError(`Action of '*' found in ${name} ALLOW permission`);
54
- valid = false;
55
- }
56
- }
57
- for (const resource of statement.resources) {
58
- if (resource === "*") {
59
- const name = statement.actions.join(",");
60
- aws_cdk_lib_1.Annotations.of(this).addError(`Resource of '*' found in ${name} ALLOW permission`);
61
- valid = false;
62
- }
63
- }
64
- }
47
+ // Don't mind if it's missing, but if it's used it must not be empty
48
+ if (props.statements?.length == 0) {
49
+ throw new Error("Empty statements array passed to GuDeveloperPolicyExperimental");
65
50
  }
66
- const { allow = [] } = props;
67
- for (const allowed of allow) {
68
- // validity checks
69
- const name = allowed.policyName ?? allowed.actions.join(",") + " on " + allowed.resources.join(",");
70
- for (const resource of allowed.resources) {
71
- if (resource === "*") {
72
- aws_cdk_lib_1.Annotations.of(this).addError(`Resource of '*' found in ${name} ALLOW permission`);
73
- valid = false;
74
- }
51
+ // Later, apply to the stack and check for specific errors
52
+ aws_cdk_lib_1.Aspects.of(this).add(new GuDeveloperPolicyExperimentalChecker());
53
+ }
54
+ }
55
+ exports.GuDeveloperPolicyExperimental = GuDeveloperPolicyExperimental;
56
+ class GuDeveloperPolicyExperimentalChecker {
57
+ visit(node) {
58
+ if (node instanceof aws_iam_2.CfnManagedPolicy) {
59
+ const policyDocumentJson = node.policyDocument.toJSON();
60
+ // These conditions will result in failures from the AWS classes themselves, so we don't need to validate them.
61
+ if (policyDocumentJson === null || typeof policyDocumentJson !== "object") {
62
+ return;
63
+ }
64
+ if (!("Statement" in policyDocumentJson)) {
65
+ return;
75
66
  }
76
- for (const action of allowed.actions) {
77
- if (action === "*") {
78
- const name = allowed.policyName ?? allowed.actions.join(",");
79
- aws_cdk_lib_1.Annotations.of(this).addError(`Action of '*' found in ${name} ALLOW permission`);
80
- valid = false;
67
+ if (!Array.isArray(policyDocumentJson.Statement) || policyDocumentJson.Statement.length == 0) {
68
+ return;
69
+ }
70
+ // For the following conditions, we want to make a strong assertions: you cannot "Allow" with wildcards.
71
+ for (const statement of policyDocumentJson.Statement) {
72
+ if (!("Effect" in statement) || statement.Effect === aws_iam_1.Effect.ALLOW) {
73
+ if (!("Action" in statement)) {
74
+ aws_cdk_lib_1.Annotations.of(node).addError("Statement is missing an Action");
75
+ }
76
+ else {
77
+ const action = statement.Action;
78
+ if (typeof action === "string") {
79
+ this.checkAction(action, node);
80
+ }
81
+ else if (Array.isArray(action)) {
82
+ const actionArray = action;
83
+ actionArray.forEach((action) => this.checkAction(action, node));
84
+ }
85
+ }
86
+ if (!("Resource" in statement)) {
87
+ aws_cdk_lib_1.Annotations.of(node).addError("Statement is missing an Resource");
88
+ }
89
+ else {
90
+ const resource = statement.Resource;
91
+ if (typeof resource === "string") {
92
+ this.checkResource(resource, node);
93
+ }
94
+ else if (Array.isArray(resource)) {
95
+ const resourceArray = resource;
96
+ resourceArray.forEach((resource) => {
97
+ this.checkResource(resource, node);
98
+ });
99
+ }
100
+ }
81
101
  }
82
102
  }
83
- this.addStatements(new aws_iam_1.PolicyStatement({
84
- effect: aws_iam_1.Effect.ALLOW,
85
- resources: allowed.resources,
86
- actions: allowed.actions,
87
- }));
88
103
  }
89
- if (!valid) {
90
- throw new Error("Overly broad permission present, see annotations for details");
91
- }
92
- const { deny = [] } = props;
93
- for (const denied of deny) {
94
- this.addStatements(new aws_iam_1.PolicyStatement({
95
- effect: aws_iam_1.Effect.DENY,
96
- resources: denied.resources,
97
- actions: denied.actions,
98
- }));
104
+ }
105
+ /**
106
+ * Ensure that we don't have either actions or resources of the following forms:
107
+ *
108
+ * *
109
+ * s3:*
110
+ * arn:aws:dynamodb:us-east-2:account-ID-without-hyphens:table/*
111
+ * arn:aws:s3:::*
112
+ *
113
+ * @param checkString resource or action
114
+ * @param node
115
+ * @private
116
+ */
117
+ check(checkString, checkType, node) {
118
+ if (checkString === "*" || checkString.indexOf(":*") || checkString.indexOf("/*") > 0) {
119
+ aws_cdk_lib_1.Annotations.of(node).addError(`Statement ${checkType} is too broad: ${checkString}`);
99
120
  }
100
121
  }
122
+ checkAction(checkString, node) {
123
+ this.check(checkString, "Action", node);
124
+ }
125
+ checkResource(checkString, node) {
126
+ this.check(checkString, "Resource", node);
127
+ }
101
128
  }
102
- exports.GuDeveloperPolicyExperimental = GuDeveloperPolicyExperimental;
103
129
  //# sourceMappingURL=developer-policy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"developer-policy.js","sourceRoot":"","sources":["../../../../../src/experimental/constructs/iam/policies/developer-policy.ts"],"names":[],"mappings":"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","sourcesContent":["import { Annotations } from \"aws-cdk-lib\";\nimport { Effect, ManagedPolicy, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport type { GuStack } from \"../../../../constructs/core\";\nimport type { GuAllowPolicyProps, GuDenyPolicyProps } from \"../../../../constructs/iam\";\n\nexport type GuWorkloadPolicyProps = {\n /**\n * List of explicitly allowed permissions given by this policy.\n */\n readonly allow?: GuAllowPolicyProps[];\n /**\n * List of explicitly denied permissions which can be used to fine tune this policy by pruning the allow permissions.\n */\n readonly deny?: GuDenyPolicyProps[];\n /**\n * Initial set of permissions to add to this policy document.\n * You can also use `addPermission(statement)` to add permissions later.\n *\n * @default - No statements.\n */\n readonly statements?: PolicyStatement[];\n /**\n * The unique identifier of the policy, which will be displayed when creating credentials.\n */\n readonly permission: string;\n /**\n * An optional description of the policy which will be displayed if present.\n */\n readonly description?: string;\n};\n\n/**\n * Creates a structured `AWS::IAM::ManagedPolicy` resource to manage arbitrary permissions on general account\n * resources which can then be used to create limited permission credentials for use with specific activities.\n *\n * The permission scope is not controlled. This class should be used with care to create minimal permissions.\n * To that end, broad ALLOW permissions can pruned with narrower optional DENY permissions.\n *\n * `permission` is prefixed with `/developer-policy/` and postfixed with `/` to construct the path. This will\n * be used for discovery and display. It is restricted to the same character set as AWS `path`.\n *\n * `description` is optionally used to construct the AWS Managed Policy description and used for display.\n *\n * ```yaml\n * TestingECAE2E87:\n * Type: AWS::IAM::ManagedPolicy\n * Properties:\n * Description: This is testing stuff\n * Path: /developer-policy/read-from-mybucket-under-mypath/\n * PolicyDocument:\n * Statement:\n * - Action: s3:GetObject\n * Effect: Allow\n * Resource: arn:aws:s3:::mybucket/mypath\n * - Action: s3:GetObject\n * Effect: Deny\n * Resource: arn:aws:s3:::mybucket/mypath/butnotthispath\n * Version: \"2012-10-17\"\n * Metadata:\n * aws:cdk:path: janus-resources-for-testing-managed-policy-tagging/justin-testing/Resource* ```\n * ```\n *\n * @experimental\n */\nexport class GuDeveloperPolicyExperimental extends ManagedPolicy {\n constructor(scope: GuStack, id: string, props: GuWorkloadPolicyProps) {\n super(scope, id, {\n description: `${props.permission} developer policy`,\n ...props,\n path: `/developer-policy/${props.permission}/`,\n });\n\n let valid = true;\n\n const { statements = [] } = props;\n for (const statement of statements) {\n if (statement.effect === Effect.ALLOW) {\n for (const action of statement.actions) {\n if (action === \"*\") {\n const name = statement.actions.join(\",\");\n Annotations.of(this).addError(`Action of '*' found in ${name} ALLOW permission`);\n valid = false;\n }\n }\n for (const resource of statement.resources) {\n if (resource === \"*\") {\n const name = statement.actions.join(\",\");\n Annotations.of(this).addError(`Resource of '*' found in ${name} ALLOW permission`);\n valid = false;\n }\n }\n }\n }\n\n const { allow = [] } = props;\n for (const allowed of allow) {\n // validity checks\n const name = allowed.policyName ?? allowed.actions.join(\",\") + \" on \" + allowed.resources.join(\",\");\n for (const resource of allowed.resources) {\n if (resource === \"*\") {\n Annotations.of(this).addError(`Resource of '*' found in ${name} ALLOW permission`);\n valid = false;\n }\n }\n for (const action of allowed.actions) {\n if (action === \"*\") {\n const name = allowed.policyName ?? allowed.actions.join(\",\");\n Annotations.of(this).addError(`Action of '*' found in ${name} ALLOW permission`);\n valid = false;\n }\n }\n this.addStatements(\n new PolicyStatement({\n effect: Effect.ALLOW,\n resources: allowed.resources,\n actions: allowed.actions,\n }),\n );\n }\n\n if (!valid) {\n throw new Error(\"Overly broad permission present, see annotations for details\");\n }\n\n const { deny = [] } = props;\n for (const denied of deny) {\n this.addStatements(\n new PolicyStatement({\n effect: Effect.DENY,\n resources: denied.resources,\n actions: denied.actions,\n }),\n );\n }\n }\n}\n"]}
1
+ {"version":3,"file":"developer-policy.js","sourceRoot":"","sources":["../../../../../src/experimental/constructs/iam/policies/developer-policy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IAspect } from \"aws-cdk-lib\";\nimport { Annotations, Aspects } from \"aws-cdk-lib\";\nimport { Effect, type PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { CfnManagedPolicy, ManagedPolicy, type PolicyDocument } from \"aws-cdk-lib/aws-iam\";\nimport type { IConstruct } from \"constructs\";\nimport type { GuStack } from \"../../../../constructs/core\";\n\nexport type GuWorkloadPolicyProps = {\n /**\n * Initial set of permissions to add to this policy document.\n * You can also use `addPermission(statement)` to add permissions later.\n *\n * @default - No statements.\n */\n readonly statements?: PolicyStatement[];\n /**\n * The unique identifier of the policy, which will be displayed when creating credentials.\n */\n readonly permission: string;\n /**\n * An optional description of the policy which will be displayed if present.\n */\n readonly description?: string;\n};\n\n/**\n * Creates a structured `AWS::IAM::ManagedPolicy` resource to manage arbitrary permissions on general account\n * resources which can then be used to create limited permission credentials for use with specific activities.\n *\n * The permission scope is not controlled. This class should be used with care to create minimal permissions.\n * To that end, broad ALLOW permissions can pruned with narrower optional DENY permissions.\n *\n * `permission` is prefixed with `/developer-policy/` and postfixed with `/` to construct the path. This will\n * be used for discovery and display. It is restricted to the same character set as AWS `path`.\n *\n * `description` is optionally used to construct the AWS Managed Policy description and used for display.\n *\n * ```yaml\n * TestingECAE2E87:\n * Type: AWS::IAM::ManagedPolicy\n * Properties:\n * Description: This is testing stuff\n * Path: /developer-policy/read-from-mybucket-under-mypath/\n * PolicyDocument:\n * Statement:\n * - Action: s3:GetObject\n * Effect: Allow\n * Resource: arn:aws:s3:::mybucket/mypath\n * - Action: s3:GetObject\n * Effect: Deny\n * Resource: arn:aws:s3:::mybucket/mypath/butnotthispath\n * Version: \"2012-10-17\"\n * Metadata:\n * aws:cdk:path: janus-resources-for-testing-managed-policy-tagging/justin-testing/Resource* ```\n * ```\n *\n * @experimental\n */\nexport class GuDeveloperPolicyExperimental extends ManagedPolicy {\n constructor(scope: GuStack, id: string, props: GuWorkloadPolicyProps) {\n super(scope, id, {\n description: `${props.permission} developer policy`,\n ...props,\n path: `/developer-policy/${props.permission}/`,\n });\n\n // Don't mind if it's missing, but if it's used it must not be empty\n if (props.statements?.length == 0) {\n throw new Error(\"Empty statements array passed to GuDeveloperPolicyExperimental\");\n }\n\n // Later, apply to the stack and check for specific errors\n Aspects.of(this).add(new GuDeveloperPolicyExperimentalChecker());\n }\n}\n\nclass GuDeveloperPolicyExperimentalChecker implements IAspect {\n public visit(node: IConstruct): void {\n if (node instanceof CfnManagedPolicy) {\n const policyDocumentJson: unknown = (node.policyDocument as PolicyDocument).toJSON();\n\n // These conditions will result in failures from the AWS classes themselves, so we don't need to validate them.\n if (policyDocumentJson === null || typeof policyDocumentJson !== \"object\") {\n return;\n }\n if (!(\"Statement\" in policyDocumentJson)) {\n return;\n }\n if (!Array.isArray(policyDocumentJson.Statement) || policyDocumentJson.Statement.length == 0) {\n return;\n }\n\n // For the following conditions, we want to make a strong assertions: you cannot \"Allow\" with wildcards.\n for (const statement of policyDocumentJson.Statement) {\n if (!(\"Effect\" in statement) || (statement as { Effect: unknown }).Effect === Effect.ALLOW) {\n if (!(\"Action\" in statement)) {\n Annotations.of(node).addError(\"Statement is missing an Action\");\n } else {\n const action = (statement as { Action: unknown }).Action;\n\n if (typeof action === \"string\") {\n this.checkAction(action, node);\n } else if (Array.isArray(action)) {\n const actionArray = action as string[];\n actionArray.forEach((action) => this.checkAction(action, node));\n }\n }\n\n if (!(\"Resource\" in statement)) {\n Annotations.of(node).addError(\"Statement is missing an Resource\");\n } else {\n const resource = (statement as { Resource: unknown }).Resource;\n\n if (typeof resource === \"string\") {\n this.checkResource(resource, node);\n } else if (Array.isArray(resource)) {\n const resourceArray = resource as string[];\n resourceArray.forEach((resource) => {\n this.checkResource(resource, node);\n });\n }\n }\n }\n }\n }\n }\n\n /**\n * Ensure that we don't have either actions or resources of the following forms:\n *\n * *\n * s3:*\n * arn:aws:dynamodb:us-east-2:account-ID-without-hyphens:table/*\n * arn:aws:s3:::*\n *\n * @param checkString resource or action\n * @param node\n * @private\n */\n private check(checkString: string, checkType: string, node: CfnManagedPolicy) {\n if (checkString === \"*\" || checkString.indexOf(\":*\") || checkString.indexOf(\"/*\") > 0) {\n Annotations.of(node).addError(`Statement ${checkType} is too broad: ${checkString}`);\n }\n }\n private checkAction(checkString: string, node: CfnManagedPolicy) {\n this.check(checkString, \"Action\", node);\n }\n private checkResource(checkString: string, node: CfnManagedPolicy) {\n this.check(checkString, \"Resource\", node);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@guardian/cdk",
3
3
  "description": "Generic Guardian flavoured AWS CDK components",
4
- "version": "62.5.3",
4
+ "version": "62.6.0",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "files": [
@@ -1,34 +0,0 @@
1
- import type { GuStack } from "../core";
2
- import { GuRole } from "./roles";
3
- export interface GuJanusAssumableRoleProps {
4
- /**
5
- * Tells Janus which ProvisionedRole this IAM role is part of.
6
- */
7
- janusPermission: string;
8
- /**
9
- * A friendly name for this IAM role to be shown in Janus.
10
- * It should help Janus users to choose the right role.
11
- */
12
- janusName?: string;
13
- /**
14
- * A description of this IAM role to be shown in Janus.
15
- * It should help Janus users to choose the right role.
16
- */
17
- janusDescription?: string;
18
- }
19
- /**
20
- * Construct that creates an IAM role with the additional tags that Janus uses to find the role
21
- * and its metadata.
22
- *
23
- * ```typescript
24
- * new GuJanusAssumableRole(stack, {
25
- * janusPermission: "some-app-dev",
26
- * janusName: "Some App Developer",
27
- * janusDescription: "Description of role that will be shown in Janus.",
28
- * })
29
- * ```
30
- */
31
- export declare class GuJanusAssumableRole extends GuRole {
32
- constructor(scope: GuStack, props: GuJanusAssumableRoleProps);
33
- }
34
- //# sourceMappingURL=janus-assumable-role.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"janus-assumable-role.d.ts","sourceRoot":"","sources":["../../../src/constructs/iam/janus-assumable-role.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,SAAQ,MAAM;gBAClC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,yBAAyB;CAoB7D"}
@@ -1,43 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GuJanusAssumableRole = void 0;
4
- const aws_cdk_lib_1 = require("aws-cdk-lib");
5
- const aws_iam_1 = require("aws-cdk-lib/aws-iam");
6
- const aws_ssm_1 = require("aws-cdk-lib/aws-ssm");
7
- const roles_1 = require("./roles");
8
- /**
9
- * Construct that creates an IAM role with the additional tags that Janus uses to find the role
10
- * and its metadata.
11
- *
12
- * ```typescript
13
- * new GuJanusAssumableRole(stack, {
14
- * janusPermission: "some-app-dev",
15
- * janusName: "Some App Developer",
16
- * janusDescription: "Description of role that will be shown in Janus.",
17
- * })
18
- * ```
19
- */
20
- class GuJanusAssumableRole extends roles_1.GuRole {
21
- constructor(scope, props) {
22
- // Existing SSM param in stack account that holds Janus user ARN
23
- const janusArnParamValue = aws_ssm_1.StringParameter.valueForStringParameter(scope, "/security/janus/user-arn");
24
- super(scope, `${props.janusPermission}Role`, {
25
- // Will be assumed by Janus user
26
- assumedBy: new aws_iam_1.ArnPrincipal(janusArnParamValue),
27
- // IAM API only allows filtering by path prefix
28
- path: "/gu/janus/discoverable/",
29
- });
30
- const tags = aws_cdk_lib_1.Tags.of(this);
31
- // Discoverability marked by tag as well as path because tags are easier to find
32
- tags.add("gu:janus:discoverable", "true");
33
- tags.add("gu:janus:permission", props.janusPermission);
34
- if (props.janusName) {
35
- tags.add("gu:janus:name", props.janusName);
36
- }
37
- if (props.janusDescription) {
38
- tags.add("gu:janus:description", props.janusDescription);
39
- }
40
- }
41
- }
42
- exports.GuJanusAssumableRole = GuJanusAssumableRole;
43
- //# sourceMappingURL=janus-assumable-role.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"janus-assumable-role.js","sourceRoot":"","sources":["../../../src/constructs/iam/janus-assumable-role.ts"],"names":[],"mappings":";;;AAAA,6CAAmC;AACnC,iDAAmD;AACnD,iDAAsD;AAEtD,mCAAiC;AAqBjC;;;;;;;;;;;GAWG;AACH,MAAa,oBAAqB,SAAQ,cAAM;IAC9C,YAAY,KAAc,EAAE,KAAgC;QAC1D,gEAAgE;QAChE,MAAM,kBAAkB,GAAG,yBAAe,CAAC,uBAAuB,CAAC,KAAK,EAAE,0BAA0B,CAAC,CAAC;QACtG,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,eAAe,MAAM,EAAE;YAC3C,gCAAgC;YAChC,SAAS,EAAE,IAAI,sBAAY,CAAC,kBAAkB,CAAC;YAC/C,+CAA+C;YAC/C,IAAI,EAAE,yBAAyB;SAChC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,kBAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3B,gFAAgF;QAChF,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;QACvD,IAAI,KAAK,CAAC,SAAS,EAAE;YACnB,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;SAC5C;QACD,IAAI,KAAK,CAAC,gBAAgB,EAAE;YAC1B,IAAI,CAAC,GAAG,CAAC,sBAAsB,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;SAC1D;IACH,CAAC;CACF;AArBD,oDAqBC","sourcesContent":["import { Tags } from \"aws-cdk-lib\";\nimport { ArnPrincipal } from \"aws-cdk-lib/aws-iam\";\nimport { StringParameter } from \"aws-cdk-lib/aws-ssm\";\nimport type { GuStack } from \"../core\";\nimport { GuRole } from \"./roles\";\n\nexport interface GuJanusAssumableRoleProps {\n /**\n * Tells Janus which ProvisionedRole this IAM role is part of.\n */\n janusPermission: string;\n\n /**\n * A friendly name for this IAM role to be shown in Janus.\n * It should help Janus users to choose the right role.\n */\n janusName?: string;\n\n /**\n * A description of this IAM role to be shown in Janus.\n * It should help Janus users to choose the right role.\n */\n janusDescription?: string;\n}\n\n/**\n * Construct that creates an IAM role with the additional tags that Janus uses to find the role\n * and its metadata.\n *\n * ```typescript\n * new GuJanusAssumableRole(stack, {\n * janusPermission: \"some-app-dev\",\n * janusName: \"Some App Developer\",\n * janusDescription: \"Description of role that will be shown in Janus.\",\n * })\n * ```\n */\nexport class GuJanusAssumableRole extends GuRole {\n constructor(scope: GuStack, props: GuJanusAssumableRoleProps) {\n // Existing SSM param in stack account that holds Janus user ARN\n const janusArnParamValue = StringParameter.valueForStringParameter(scope, \"/security/janus/user-arn\");\n super(scope, `${props.janusPermission}Role`, {\n // Will be assumed by Janus user\n assumedBy: new ArnPrincipal(janusArnParamValue),\n // IAM API only allows filtering by path prefix\n path: \"/gu/janus/discoverable/\",\n });\n const tags = Tags.of(this);\n // Discoverability marked by tag as well as path because tags are easier to find\n tags.add(\"gu:janus:discoverable\", \"true\");\n tags.add(\"gu:janus:permission\", props.janusPermission);\n if (props.janusName) {\n tags.add(\"gu:janus:name\", props.janusName);\n }\n if (props.janusDescription) {\n tags.add(\"gu:janus:description\", props.janusDescription);\n }\n }\n}\n"]}