@cloud-copilot/iam-lens 0.1.25 → 0.1.27

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 (166) hide show
  1. package/dist/cjs/canWhat/canWhat.d.ts +16 -0
  2. package/dist/cjs/canWhat/canWhat.d.ts.map +1 -1
  3. package/dist/cjs/canWhat/canWhat.js +7 -0
  4. package/dist/cjs/canWhat/canWhat.js.map +1 -1
  5. package/dist/cjs/cli.js +9 -3
  6. package/dist/cjs/cli.js.map +1 -1
  7. package/dist/cjs/collect/client.d.ts +81 -3
  8. package/dist/cjs/collect/client.d.ts.map +1 -1
  9. package/dist/cjs/collect/client.js +294 -152
  10. package/dist/cjs/collect/client.js.map +1 -1
  11. package/dist/cjs/collect/collect.d.ts +2 -2
  12. package/dist/cjs/collect/collect.d.ts.map +1 -1
  13. package/dist/cjs/collect/collect.js +2 -2
  14. package/dist/cjs/collect/collect.js.map +1 -1
  15. package/dist/cjs/index.d.ts +6 -0
  16. package/dist/cjs/index.d.ts.map +1 -1
  17. package/dist/cjs/index.js +15 -0
  18. package/dist/cjs/index.js.map +1 -1
  19. package/dist/cjs/principals.d.ts +10 -0
  20. package/dist/cjs/principals.d.ts.map +1 -1
  21. package/dist/cjs/principals.js +33 -1
  22. package/dist/cjs/principals.js.map +1 -1
  23. package/dist/cjs/simulate/contextKeys.d.ts +7 -0
  24. package/dist/cjs/simulate/contextKeys.d.ts.map +1 -1
  25. package/dist/cjs/simulate/contextKeys.js +4 -1
  26. package/dist/cjs/simulate/contextKeys.js.map +1 -1
  27. package/dist/cjs/simulate/simulate.d.ts +32 -0
  28. package/dist/cjs/simulate/simulate.d.ts.map +1 -1
  29. package/dist/cjs/simulate/simulate.js +11 -0
  30. package/dist/cjs/simulate/simulate.js.map +1 -1
  31. package/dist/cjs/test-datasets/testClient.d.ts +2 -0
  32. package/dist/cjs/test-datasets/testClient.d.ts.map +1 -1
  33. package/dist/cjs/test-datasets/testClient.js +12 -0
  34. package/dist/cjs/test-datasets/testClient.js.map +1 -1
  35. package/dist/cjs/utils/workerScript.d.ts +8 -0
  36. package/dist/cjs/utils/workerScript.d.ts.map +1 -0
  37. package/dist/cjs/utils/workerScript.js +18 -0
  38. package/dist/cjs/utils/workerScript.js.map +1 -0
  39. package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts +14 -0
  40. package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
  41. package/dist/cjs/whoCan/WhoCanMainThreadWorker.js +34 -0
  42. package/dist/cjs/whoCan/WhoCanMainThreadWorker.js.map +1 -0
  43. package/dist/cjs/whoCan/WhoCanWorker.d.ts +12 -0
  44. package/dist/cjs/whoCan/WhoCanWorker.d.ts.map +1 -0
  45. package/dist/cjs/whoCan/WhoCanWorker.js +71 -0
  46. package/dist/cjs/whoCan/WhoCanWorker.js.map +1 -0
  47. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
  48. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
  49. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js +46 -0
  50. package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
  51. package/dist/cjs/whoCan/whoCan.d.ts +2 -1
  52. package/dist/cjs/whoCan/whoCan.d.ts.map +1 -1
  53. package/dist/cjs/whoCan/whoCan.js +111 -77
  54. package/dist/cjs/whoCan/whoCan.js.map +1 -1
  55. package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts +15 -0
  56. package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
  57. package/dist/cjs/workers/ArrayStreamingWorkQueue.js +35 -0
  58. package/dist/cjs/workers/ArrayStreamingWorkQueue.js.map +1 -0
  59. package/dist/cjs/workers/JobRunner.d.ts +47 -0
  60. package/dist/cjs/workers/JobRunner.d.ts.map +1 -0
  61. package/dist/cjs/workers/JobRunner.js +101 -0
  62. package/dist/cjs/workers/JobRunner.js.map +1 -0
  63. package/dist/cjs/workers/RingQueue.d.ts +14 -0
  64. package/dist/cjs/workers/RingQueue.d.ts.map +1 -0
  65. package/dist/cjs/workers/RingQueue.js +43 -0
  66. package/dist/cjs/workers/RingQueue.js.map +1 -0
  67. package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts +10 -0
  68. package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
  69. package/dist/cjs/workers/SharedArrayBufferMainCache.js +37 -0
  70. package/dist/cjs/workers/SharedArrayBufferMainCache.js.map +1 -0
  71. package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
  72. package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
  73. package/dist/cjs/workers/SharedArrayBufferWorkerCache.js +49 -0
  74. package/dist/cjs/workers/SharedArrayBufferWorkerCache.js.map +1 -0
  75. package/dist/cjs/workers/StreamingWorkQueue.d.ts +18 -0
  76. package/dist/cjs/workers/StreamingWorkQueue.d.ts.map +1 -0
  77. package/dist/cjs/workers/StreamingWorkQueue.js +67 -0
  78. package/dist/cjs/workers/StreamingWorkQueue.js.map +1 -0
  79. package/dist/cjs/workers/buffers.d.ts +4 -0
  80. package/dist/cjs/workers/buffers.d.ts.map +1 -0
  81. package/dist/cjs/workers/buffers.js +26 -0
  82. package/dist/cjs/workers/buffers.js.map +1 -0
  83. package/dist/esm/canWhat/canWhat.d.ts +16 -0
  84. package/dist/esm/canWhat/canWhat.d.ts.map +1 -1
  85. package/dist/esm/canWhat/canWhat.js +7 -0
  86. package/dist/esm/canWhat/canWhat.js.map +1 -1
  87. package/dist/esm/cli.js +9 -3
  88. package/dist/esm/cli.js.map +1 -1
  89. package/dist/esm/collect/client.d.ts +81 -3
  90. package/dist/esm/collect/client.d.ts.map +1 -1
  91. package/dist/esm/collect/client.js +292 -150
  92. package/dist/esm/collect/client.js.map +1 -1
  93. package/dist/esm/collect/collect.d.ts +2 -2
  94. package/dist/esm/collect/collect.d.ts.map +1 -1
  95. package/dist/esm/collect/collect.js +2 -2
  96. package/dist/esm/collect/collect.js.map +1 -1
  97. package/dist/esm/index.d.ts +6 -0
  98. package/dist/esm/index.d.ts.map +1 -1
  99. package/dist/esm/index.js +5 -1
  100. package/dist/esm/index.js.map +1 -1
  101. package/dist/esm/principals.d.ts +10 -0
  102. package/dist/esm/principals.d.ts.map +1 -1
  103. package/dist/esm/principals.js +30 -1
  104. package/dist/esm/principals.js.map +1 -1
  105. package/dist/esm/simulate/contextKeys.d.ts +7 -0
  106. package/dist/esm/simulate/contextKeys.d.ts.map +1 -1
  107. package/dist/esm/simulate/contextKeys.js +4 -1
  108. package/dist/esm/simulate/contextKeys.js.map +1 -1
  109. package/dist/esm/simulate/simulate.d.ts +32 -0
  110. package/dist/esm/simulate/simulate.d.ts.map +1 -1
  111. package/dist/esm/simulate/simulate.js +12 -1
  112. package/dist/esm/simulate/simulate.js.map +1 -1
  113. package/dist/esm/test-datasets/testClient.d.ts +2 -0
  114. package/dist/esm/test-datasets/testClient.d.ts.map +1 -1
  115. package/dist/esm/test-datasets/testClient.js +11 -0
  116. package/dist/esm/test-datasets/testClient.js.map +1 -1
  117. package/dist/esm/utils/workerScript.d.ts +8 -0
  118. package/dist/esm/utils/workerScript.d.ts.map +1 -0
  119. package/dist/esm/utils/workerScript.js +13 -0
  120. package/dist/esm/utils/workerScript.js.map +1 -0
  121. package/dist/esm/utils/workerScriptEsm.d.ts.map +1 -0
  122. package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts +14 -0
  123. package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
  124. package/dist/esm/whoCan/WhoCanMainThreadWorker.js +28 -0
  125. package/dist/esm/whoCan/WhoCanMainThreadWorker.js.map +1 -0
  126. package/dist/esm/whoCan/WhoCanWorker.d.ts +12 -0
  127. package/dist/esm/whoCan/WhoCanWorker.d.ts.map +1 -0
  128. package/dist/esm/whoCan/WhoCanWorker.js +67 -0
  129. package/dist/esm/whoCan/WhoCanWorker.js.map +1 -0
  130. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
  131. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
  132. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js +44 -0
  133. package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
  134. package/dist/esm/whoCan/whoCan.d.ts +2 -1
  135. package/dist/esm/whoCan/whoCan.d.ts.map +1 -1
  136. package/dist/esm/whoCan/whoCan.js +111 -77
  137. package/dist/esm/whoCan/whoCan.js.map +1 -1
  138. package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts +15 -0
  139. package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
  140. package/dist/esm/workers/ArrayStreamingWorkQueue.js +31 -0
  141. package/dist/esm/workers/ArrayStreamingWorkQueue.js.map +1 -0
  142. package/dist/esm/workers/JobRunner.d.ts +47 -0
  143. package/dist/esm/workers/JobRunner.d.ts.map +1 -0
  144. package/dist/esm/workers/JobRunner.js +93 -0
  145. package/dist/esm/workers/JobRunner.js.map +1 -0
  146. package/dist/esm/workers/RingQueue.d.ts +14 -0
  147. package/dist/esm/workers/RingQueue.d.ts.map +1 -0
  148. package/dist/esm/workers/RingQueue.js +36 -0
  149. package/dist/esm/workers/RingQueue.js.map +1 -0
  150. package/dist/esm/workers/SharedArrayBufferMainCache.d.ts +10 -0
  151. package/dist/esm/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
  152. package/dist/esm/workers/SharedArrayBufferMainCache.js +33 -0
  153. package/dist/esm/workers/SharedArrayBufferMainCache.js.map +1 -0
  154. package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
  155. package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
  156. package/dist/esm/workers/SharedArrayBufferWorkerCache.js +44 -0
  157. package/dist/esm/workers/SharedArrayBufferWorkerCache.js.map +1 -0
  158. package/dist/esm/workers/StreamingWorkQueue.d.ts +18 -0
  159. package/dist/esm/workers/StreamingWorkQueue.d.ts.map +1 -0
  160. package/dist/esm/workers/StreamingWorkQueue.js +61 -0
  161. package/dist/esm/workers/StreamingWorkQueue.js.map +1 -0
  162. package/dist/esm/workers/buffers.d.ts +4 -0
  163. package/dist/esm/workers/buffers.d.ts.map +1 -0
  164. package/dist/esm/workers/buffers.js +21 -0
  165. package/dist/esm/workers/buffers.js.map +1 -0
  166. package/package.json +6 -4
@@ -1,29 +1,61 @@
1
1
  import { splitArnParts } from '@cloud-copilot/iam-utils';
2
- export class IamCollectClient {
3
- constructor(storageClient, clientOptions) {
4
- this.storageClient = storageClient;
5
- this._cache = {};
6
- this._enableCaching = clientOptions?.enableCaching !== false;
2
+ /**
3
+ * A cache provider that stores results in memory for a single worker.
4
+ */
5
+ export class InMemoryCacheProvider {
6
+ constructor() {
7
+ this.cache = {};
7
8
  }
8
- // Generic cache helper
9
9
  async withCache(cacheKey, fetcher) {
10
- if (this._enableCaching && cacheKey in this._cache) {
11
- return this._cache[cacheKey];
10
+ if (cacheKey in this.cache) {
11
+ return this.cache[cacheKey];
12
12
  }
13
13
  const value = await fetcher();
14
- if (this._enableCaching) {
15
- this._cache[cacheKey] = value;
16
- }
14
+ this.cache[cacheKey] = value;
17
15
  return value;
18
16
  }
17
+ }
18
+ /**
19
+ * A cache provider that does not cache results.
20
+ */
21
+ export class NoCacheProvider {
22
+ async withCache(cacheKey, fetcher) {
23
+ return fetcher();
24
+ }
25
+ }
26
+ /**
27
+ * A client for simplifying access to the IAM collect data store.
28
+ */
29
+ export class IamCollectClient {
30
+ /**
31
+ * Creates a new instance of the IamCollectClient.
32
+ *
33
+ * @param storageClient the iam-collect storage client to use for data access
34
+ * @param clientOptions optional configuration options for the client. By default, uses an in-memory cache provider.
35
+ */
36
+ constructor(storageClient, clientOptions) {
37
+ this.storageClient = storageClient;
38
+ if (clientOptions?.cacheProvider === undefined) {
39
+ this.cacheProvider = new InMemoryCacheProvider();
40
+ }
41
+ else {
42
+ this.cacheProvider = clientOptions.cacheProvider;
43
+ }
44
+ }
45
+ async withCache(cacheKey, fetcher) {
46
+ return this.cacheProvider.withCache(cacheKey, fetcher);
47
+ }
19
48
  /**
20
49
  * Checks if an account exists in the store.
21
50
  * @param accountId The ID of the account to check.
22
51
  * @returns True if the account exists, false otherwise.
23
52
  */
24
53
  async accountExists(accountId) {
25
- const accounts = await this.storageClient.listAccountIds();
26
- return accounts.includes(accountId);
54
+ const cacheKey = `accountExists:${accountId}`;
55
+ return this.withCache(cacheKey, async () => {
56
+ const accounts = await this.storageClient.listAccountIds();
57
+ return accounts.includes(accountId);
58
+ });
27
59
  }
28
60
  /**
29
61
  * Get all account IDs in the store.
@@ -31,7 +63,10 @@ export class IamCollectClient {
31
63
  * @returns all account IDs in the store
32
64
  */
33
65
  async allAccounts() {
34
- return this.storageClient.listAccountIds();
66
+ const cacheKey = `allAccounts`;
67
+ return this.withCache(cacheKey, async () => {
68
+ return this.storageClient.listAccountIds();
69
+ });
35
70
  }
36
71
  /**
37
72
  * Checks if a principal exists in the store.
@@ -39,9 +74,12 @@ export class IamCollectClient {
39
74
  * @returns True if the principal exists, false otherwise.
40
75
  */
41
76
  async principalExists(principalArn) {
42
- const accountId = splitArnParts(principalArn).accountId;
43
- const principalData = await this.storageClient.getResourceMetadata(accountId, principalArn, 'metadata');
44
- return !!principalData;
77
+ const cacheKey = `principalExists:${principalArn}`;
78
+ return this.withCache(cacheKey, async () => {
79
+ const accountId = splitArnParts(principalArn).accountId;
80
+ const principalData = await this.storageClient.getResourceMetadata(accountId, principalArn, 'metadata');
81
+ return !!principalData;
82
+ });
45
83
  }
46
84
  /**
47
85
  * Gets the SCP Hierarchy for an account. The first element is the root, the last element is the account itself.
@@ -99,21 +137,24 @@ export class IamCollectClient {
99
137
  * @returns The OUs for the account.
100
138
  */
101
139
  async getOrgUnitHierarchyForAccount(accountId) {
102
- const orgId = await this.getOrgIdForAccount(accountId);
103
- if (!orgId) {
104
- return [];
105
- }
106
- const ouIds = [];
107
- let ouId = await this.getOrgUnitIdForAccount(accountId);
108
- ouIds.push(ouId);
109
- while (ouId) {
110
- const parentOuId = await this.getParentOrgUnitIdForOrgUnit(orgId, ouId);
111
- if (parentOuId) {
112
- ouIds.unshift(parentOuId);
140
+ const cacheKey = `orgUnitHierarchy:${accountId}`;
141
+ return this.withCache(cacheKey, async () => {
142
+ const orgId = await this.getOrgIdForAccount(accountId);
143
+ if (!orgId) {
144
+ return [];
113
145
  }
114
- ouId = parentOuId;
115
- }
116
- return ouIds;
146
+ const ouIds = [];
147
+ let ouId = await this.getOrgUnitIdForAccount(accountId);
148
+ ouIds.push(ouId);
149
+ while (ouId) {
150
+ const parentOuId = await this.getParentOrgUnitIdForOrgUnit(orgId, ouId);
151
+ if (parentOuId) {
152
+ ouIds.unshift(parentOuId);
153
+ }
154
+ ouId = parentOuId;
155
+ }
156
+ return ouIds;
157
+ });
117
158
  }
118
159
  /**
119
160
  * Gets the org unit ID for an account.
@@ -121,12 +162,15 @@ export class IamCollectClient {
121
162
  * @returns The org unit ID for the account, or undefined if not found.
122
163
  */
123
164
  async getOrgUnitIdForAccount(accountId) {
124
- const orgId = await this.getOrgIdForAccount(accountId);
125
- if (!orgId) {
126
- return undefined;
127
- }
128
- const accounts = (await this.getAccountDataForOrg(orgId));
129
- return accounts[accountId].ou;
165
+ const cacheKey = `orgUnitId:${accountId}`;
166
+ return this.withCache(cacheKey, async () => {
167
+ const orgId = await this.getOrgIdForAccount(accountId);
168
+ if (!orgId) {
169
+ return undefined;
170
+ }
171
+ const accounts = (await this.getAccountDataForOrg(orgId));
172
+ return accounts[accountId].ou;
173
+ });
130
174
  }
131
175
  /**
132
176
  * Gets the parent org unit ID for a given org unit.
@@ -135,9 +179,12 @@ export class IamCollectClient {
135
179
  * @returns The parent org unit ID, or undefined if not found.
136
180
  */
137
181
  async getParentOrgUnitIdForOrgUnit(orgId, ouId) {
138
- const ouData = await this.getOrgUnitsDataForOrg(orgId);
139
- const ou = ouData[ouId];
140
- return ou.parent;
182
+ const cacheKey = `parentOrgUnit:${orgId}:${ouId}`;
183
+ return this.withCache(cacheKey, async () => {
184
+ const ouData = await this.getOrgUnitsDataForOrg(orgId);
185
+ const ou = ouData[ouId];
186
+ return ou.parent;
187
+ });
141
188
  }
142
189
  /**
143
190
  * Gets the SCPs for an account.
@@ -154,19 +201,22 @@ export class IamCollectClient {
154
201
  * @returns The org policies for the account.
155
202
  */
156
203
  async getOrgPoliciesForAccount(accountId, policyType) {
157
- const orgId = await this.getOrgIdForAccount(accountId);
158
- if (!orgId) {
159
- return [];
160
- }
161
- const accounts = (await this.getAccountDataForOrg(orgId));
162
- const orgInformation = accounts[accountId];
163
- const policyArns = orgInformation[policyType];
164
- const policies = [];
165
- for (const policyArn of policyArns) {
166
- const policyInfo = await this.getOrgPolicy(orgId, policyType, policyArn);
167
- policies.push(policyInfo);
168
- }
169
- return policies;
204
+ const cacheKey = `orgPoliciesForAccount:${accountId}:${policyType}`;
205
+ return this.withCache(cacheKey, async () => {
206
+ const orgId = await this.getOrgIdForAccount(accountId);
207
+ if (!orgId) {
208
+ return [];
209
+ }
210
+ const accounts = (await this.getAccountDataForOrg(orgId));
211
+ const orgInformation = accounts[accountId];
212
+ const policyArns = orgInformation[policyType];
213
+ const policies = [];
214
+ for (const policyArn of policyArns) {
215
+ const policyInfo = await this.getOrgPolicy(orgId, policyType, policyArn);
216
+ policies.push(policyInfo);
217
+ }
218
+ return policies;
219
+ });
170
220
  }
171
221
  /**
172
222
  * Gets the account data for an organization.
@@ -174,7 +224,10 @@ export class IamCollectClient {
174
224
  * @returns The account data for the organization.
175
225
  */
176
226
  async getAccountDataForOrg(orgId) {
177
- return this.storageClient.getOrganizationMetadata(orgId, 'accounts');
227
+ const cacheKey = `accountDataForOrg:${orgId}`;
228
+ return this.withCache(cacheKey, async () => {
229
+ return this.storageClient.getOrganizationMetadata(orgId, 'accounts');
230
+ });
178
231
  }
179
232
  /**
180
233
  * Gets the org units data for an organization.
@@ -182,7 +235,10 @@ export class IamCollectClient {
182
235
  * @returns The org units data for the organization.
183
236
  */
184
237
  async getOrgUnitsDataForOrg(orgId) {
185
- return this.storageClient.getOrganizationMetadata(orgId, 'ous');
238
+ const cacheKey = `orgUnitsDataForOrg:${orgId}`;
239
+ return this.withCache(cacheKey, async () => {
240
+ return this.storageClient.getOrganizationMetadata(orgId, 'ous');
241
+ });
186
242
  }
187
243
  /**
188
244
  * Gets a specific org policy.
@@ -192,17 +248,20 @@ export class IamCollectClient {
192
248
  * @returns The org policy.
193
249
  */
194
250
  async getOrgPolicy(orgId, policyType, policyArn) {
195
- const policyId = policyArn.split('/').at(-1);
196
- const policyData = await this.storageClient.getOrganizationPolicyMetadata(orgId, policyType, policyId, 'metadata');
197
- const policyDocument = await this.storageClient.getOrganizationPolicyMetadata(orgId, policyType, policyId, 'policy');
198
- if (!policyDocument) {
199
- console.error(`Policy document not found for ${policyArn} in org ${orgId}`);
200
- }
201
- return {
202
- arn: policyData.arn,
203
- name: policyData.name,
204
- policy: policyDocument
205
- };
251
+ const cacheKey = `orgPolicy:${orgId}:${policyType}:${policyArn}`;
252
+ return this.withCache(cacheKey, async () => {
253
+ const policyId = policyArn.split('/').at(-1);
254
+ const policyData = await this.storageClient.getOrganizationPolicyMetadata(orgId, policyType, policyId, 'metadata');
255
+ const policyDocument = await this.storageClient.getOrganizationPolicyMetadata(orgId, policyType, policyId, 'policy');
256
+ if (!policyDocument) {
257
+ console.error(`Policy document not found for ${policyArn} in org ${orgId}`);
258
+ }
259
+ return {
260
+ arn: policyData.arn,
261
+ name: policyData.name,
262
+ policy: policyDocument
263
+ };
264
+ });
206
265
  }
207
266
  /**
208
267
  * Gets the RCPs for an account.
@@ -237,15 +296,18 @@ export class IamCollectClient {
237
296
  * @returns The org policies for the org unit.
238
297
  */
239
298
  async getOrgPoliciesForOrgUnit(orgId, orgUnitId, policyType) {
240
- const orgUnitInformation = await this.getOrgUnitsDataForOrg(orgId);
241
- const orgUnit = orgUnitInformation[orgUnitId];
242
- const orgPolicies = orgUnit[policyType];
243
- const policies = [];
244
- for (const policyArn of orgPolicies) {
245
- const policyInfo = await this.getOrgPolicy(orgId, policyType, policyArn);
246
- policies.push(policyInfo);
247
- }
248
- return policies;
299
+ const cacheKey = `orgPoliciesForOrgUnit:${orgId}:${orgUnitId}:${policyType}`;
300
+ return this.withCache(cacheKey, async () => {
301
+ const orgUnitInformation = await this.getOrgUnitsDataForOrg(orgId);
302
+ const orgUnit = orgUnitInformation[orgUnitId];
303
+ const orgPolicies = orgUnit[policyType];
304
+ const policies = [];
305
+ for (const policyArn of orgPolicies) {
306
+ const policyInfo = await this.getOrgPolicy(orgId, policyType, policyArn);
307
+ policies.push(policyInfo);
308
+ }
309
+ return policies;
310
+ });
249
311
  }
250
312
  /**
251
313
  * Gets the RCPs for an org unit.
@@ -262,17 +324,23 @@ export class IamCollectClient {
262
324
  * @returns The org ID for the account, or undefined if not found.
263
325
  */
264
326
  async getOrgIdForAccount(accountId) {
265
- const index = await this.storageClient.getIndex('accounts-to-orgs', {});
327
+ const index = await this.getIndex('accounts-to-orgs', {});
266
328
  const accountToOrgMap = index.data;
267
329
  return accountToOrgMap[accountId];
268
330
  }
331
+ async getIndex(indexName, defaultValue) {
332
+ const cacheKey = `index:${indexName}`;
333
+ return this.withCache(cacheKey, async () => {
334
+ return this.storageClient.getIndex(indexName, defaultValue);
335
+ });
336
+ }
269
337
  /**
270
338
  * Gets the account ID for a given S3 bucket name.
271
339
  * @param bucketName The name of the bucket.
272
340
  * @returns The account ID for the bucket, or undefined if not found.
273
341
  */
274
342
  async getAccountIdForBucket(bucketName) {
275
- const index = await this.storageClient.getIndex('buckets-to-accounts', {});
343
+ const index = await this.getIndex('buckets-to-accounts', {});
276
344
  const bucketToAccountMap = index.data;
277
345
  return bucketToAccountMap[bucketName]?.accountId;
278
346
  }
@@ -282,7 +350,7 @@ export class IamCollectClient {
282
350
  * @returns The account ID for the API Gateway, or undefined if not found.
283
351
  */
284
352
  async getAccountIdForRestApi(apiArn) {
285
- const index = await this.storageClient.getIndex('apigateways-to-accounts', {});
353
+ const index = await this.getIndex('apigateways-to-accounts', {});
286
354
  const bucketToAccountMap = index.data;
287
355
  return bucketToAccountMap[apiArn];
288
356
  }
@@ -304,16 +372,19 @@ export class IamCollectClient {
304
372
  });
305
373
  }
306
374
  async getManagedPolicy(accountId, policyArn) {
307
- const policyMetadata = await this.storageClient.getResourceMetadata(accountId, policyArn, 'metadata');
308
- const policyDocument = await this.storageClient.getResourceMetadata(accountId, policyArn, 'current-policy');
309
- if (!policyDocument) {
310
- console.error(`Policy document not found for ${policyArn} in account ${accountId}`);
311
- }
312
- return {
313
- arn: policyMetadata.arn,
314
- name: policyMetadata.name,
315
- policy: policyDocument
316
- };
375
+ const cacheKey = `managedPolicy:${accountId}:${policyArn}`;
376
+ return this.withCache(cacheKey, async () => {
377
+ const policyMetadata = await this.storageClient.getResourceMetadata(accountId, policyArn, 'metadata');
378
+ const policyDocument = await this.storageClient.getResourceMetadata(accountId, policyArn, 'current-policy');
379
+ if (!policyDocument) {
380
+ console.error(`Policy document not found for ${policyArn} in account ${accountId}`);
381
+ }
382
+ return {
383
+ arn: policyMetadata.arn,
384
+ name: policyMetadata.name,
385
+ policy: policyDocument
386
+ };
387
+ });
317
388
  }
318
389
  /**
319
390
  * Gets the inline policies attached to a user.
@@ -331,10 +402,19 @@ export class IamCollectClient {
331
402
  }));
332
403
  });
333
404
  }
405
+ /**
406
+ * Gets metadata for an IAM user.
407
+ *
408
+ * @param userArn the ARN of the user.
409
+ * @returns the metadata for the user, or undefined if not found.
410
+ */
334
411
  async getIamUserMetadata(userArn) {
335
- const accountId = splitArnParts(userArn).accountId;
336
- // The permissions boundary is stored as a policy ARN on the user resource metadata
337
- return this.storageClient.getResourceMetadata(accountId, userArn, 'metadata');
412
+ const cacheKey = `iamUserMetadata:${userArn}`;
413
+ return this.withCache(cacheKey, async () => {
414
+ const accountId = splitArnParts(userArn).accountId;
415
+ // The permissions boundary is stored as a policy ARN on the user resource metadata
416
+ return this.storageClient.getResourceMetadata(accountId, userArn, 'metadata');
417
+ });
338
418
  }
339
419
  /**
340
420
  * Gets the permissions boundary policy attached to a user, if any.
@@ -389,6 +469,12 @@ export class IamCollectClient {
389
469
  return results;
390
470
  });
391
471
  }
472
+ /**
473
+ * Get the inline policies attached to a group.
474
+ *
475
+ * @param groupArn the ARN of the group.
476
+ * @returns the inline policies for the group.
477
+ */
392
478
  async getInlinePoliciesForGroup(groupArn) {
393
479
  const cacheKey = `groupInlinePolicies:${groupArn}`;
394
480
  return this.withCache(cacheKey, async () => {
@@ -400,6 +486,11 @@ export class IamCollectClient {
400
486
  }));
401
487
  });
402
488
  }
489
+ /**
490
+ * Gets the managed policies attached to a role.
491
+ * @param roleArn the ARN of the role.
492
+ * @returns the managed policies attached to the role.
493
+ */
403
494
  async getManagedPoliciesForRole(roleArn) {
404
495
  const cacheKey = `managedPoliciesForRole:${roleArn}`;
405
496
  return this.withCache(cacheKey, async () => {
@@ -412,6 +503,12 @@ export class IamCollectClient {
412
503
  return results;
413
504
  });
414
505
  }
506
+ /**
507
+ * Get the inline policies attached to a role.
508
+ *
509
+ * @param roleArn the ARN of the role.
510
+ * @returns the inline policies for the role.
511
+ */
415
512
  async getInlinePoliciesForRole(roleArn) {
416
513
  const cacheKey = `inlinePoliciesForRole:${roleArn}`;
417
514
  return this.withCache(cacheKey, async () => {
@@ -423,6 +520,11 @@ export class IamCollectClient {
423
520
  }));
424
521
  });
425
522
  }
523
+ /**
524
+ * Get the permissions boundary policy attached to a role, if any.
525
+ * @param roleArn the ARN of the role.
526
+ * @returns the permissions boundary policy as a ManagedPolicy, or undefined if none is set.
527
+ */
426
528
  async getPermissionsBoundaryForRole(roleArn) {
427
529
  const cacheKey = `permissionBoundaryForRole:${roleArn}`;
428
530
  return this.withCache(cacheKey, async () => {
@@ -446,7 +548,10 @@ export class IamCollectClient {
446
548
  * @returns the metadata for the organization
447
549
  */
448
550
  async getOrganizationMetadata(organizationId) {
449
- return this.storageClient.getOrganizationMetadata(organizationId, 'metadata');
551
+ const cacheKey = `organizationMetadata:${organizationId}`;
552
+ return this.withCache(cacheKey, async () => {
553
+ return this.storageClient.getOrganizationMetadata(organizationId, 'metadata');
554
+ });
450
555
  }
451
556
  /**
452
557
  * Gets the resource policy for a given resource ARN and account.
@@ -456,13 +561,16 @@ export class IamCollectClient {
456
561
  * @returns The resource policy, or undefined if not found.
457
562
  */
458
563
  async getResourcePolicyForArn(resourceArn, accountId) {
459
- const arnParts = splitArnParts(resourceArn);
460
- let metadataKey = 'policy';
461
- if (arnParts.service === 'iam' && arnParts.resourceType === 'role') {
462
- metadataKey = 'trust-policy';
463
- }
464
- const resourcePolicy = await this.storageClient.getResourceMetadata(accountId, resourceArn, metadataKey);
465
- return resourcePolicy;
564
+ const cacheKey = `resourcePolicy:${accountId}:${resourceArn}`;
565
+ return this.withCache(cacheKey, async () => {
566
+ const arnParts = splitArnParts(resourceArn);
567
+ let metadataKey = 'policy';
568
+ if (arnParts.service === 'iam' && arnParts.resourceType === 'role') {
569
+ metadataKey = 'trust-policy';
570
+ }
571
+ const resourcePolicy = await this.storageClient.getResourceMetadata(accountId, resourceArn, metadataKey);
572
+ return resourcePolicy;
573
+ });
466
574
  }
467
575
  /**
468
576
  * Gets the RAM share policy for a given resource ARN and account.
@@ -472,8 +580,11 @@ export class IamCollectClient {
472
580
  * @returns The RAM share policy, or undefined if not found.
473
581
  */
474
582
  async getRamSharePolicyForArn(resourceArn, accountId) {
475
- const armSharePolicy = await this.storageClient.getRamResource(accountId, resourceArn);
476
- return armSharePolicy?.policy;
583
+ const cacheKey = `ramSharePolicy:${accountId}:${resourceArn}`;
584
+ return this.withCache(cacheKey, async () => {
585
+ const armSharePolicy = await this.storageClient.getRamResource(accountId, resourceArn);
586
+ return armSharePolicy?.policy;
587
+ });
477
588
  }
478
589
  /**
479
590
  * Gets the tags for a given resource ARN and account.
@@ -483,8 +594,11 @@ export class IamCollectClient {
483
594
  * @returns The tags as a record, or undefined if not found.
484
595
  */
485
596
  async getTagsForResource(resourceArn, accountId) {
486
- const tags = await this.storageClient.getResourceMetadata(accountId, resourceArn, 'tags');
487
- return tags || {};
597
+ const cacheKey = `tagsForResource:${accountId}:${resourceArn}`;
598
+ return this.withCache(cacheKey, async () => {
599
+ const tags = await this.storageClient.getResourceMetadata(accountId, resourceArn, 'tags');
600
+ return tags || {};
601
+ });
488
602
  }
489
603
  /**
490
604
  * Gets a unique ID for an IAM resource based on its ARN and account ID.
@@ -495,9 +609,12 @@ export class IamCollectClient {
495
609
  * @returns a unique ID for the resource, or undefined if not found
496
610
  */
497
611
  async getUniqueIdForIamResource(resourceArn) {
498
- const accountId = splitArnParts(resourceArn).accountId;
499
- const resourceMetadata = await this.storageClient.getResourceMetadata(accountId, resourceArn, 'metadata');
500
- return resourceMetadata?.id;
612
+ const cacheKey = `uniqueIdForIamResource:${resourceArn}`;
613
+ return this.withCache(cacheKey, async () => {
614
+ const accountId = splitArnParts(resourceArn).accountId;
615
+ const resourceMetadata = await this.storageClient.getResourceMetadata(accountId, resourceArn, 'metadata');
616
+ return resourceMetadata?.id;
617
+ });
501
618
  }
502
619
  /**
503
620
  * Get the account IDs for an organization.
@@ -520,52 +637,74 @@ export class IamCollectClient {
520
637
  * @returns returns the organization structure or undefined if not found
521
638
  */
522
639
  async getOrganizationStructure(orgId) {
523
- return this.storageClient.getOrganizationMetadata(orgId, 'structure');
640
+ const cacheKey = `organizationStructure:${orgId}`;
641
+ return this.withCache(cacheKey, async () => {
642
+ return this.storageClient.getOrganizationMetadata(orgId, 'structure');
643
+ });
524
644
  }
645
+ /**
646
+ * Get the accounts for a given organization path.
647
+ *
648
+ * @param orgId the ID of the organization
649
+ * @param ouIds the ids of the organizational units in the path
650
+ * @returns a tuple containing a boolean indicating success and an array of account IDs
651
+ */
525
652
  async getAccountsForOrgPath(orgId, ouIds) {
526
- const orgUnits = await this.getOrganizationStructure(orgId);
527
- if (!orgUnits || ouIds.length === 0) {
528
- return [false, []];
529
- }
530
- const rootOu = orgUnits[ouIds[0]];
531
- // Now look through the structure to find the OU
532
- let currentStructure = rootOu;
533
- for (const ou of ouIds.slice(1)) {
534
- currentStructure = currentStructure.children?.[ou];
535
- if (!currentStructure) {
536
- return [false, []]; // OU not found in the structure
653
+ const cacheKey = `accountsForOrgPath:${orgId}:${ouIds.join('/')}`;
654
+ return this.withCache(cacheKey, async () => {
655
+ const orgUnits = await this.getOrganizationStructure(orgId);
656
+ if (!orgUnits || ouIds.length === 0) {
657
+ return [false, []];
537
658
  }
538
- }
539
- const getAccountId = (a) => a.split('/').at(-1);
540
- const accounts = [];
541
- if (currentStructure.accounts) {
542
- accounts.push(...currentStructure.accounts?.map(getAccountId));
543
- }
544
- const children = Object.values(currentStructure.children || {});
545
- // Traverse the children to collect all accounts
546
- while (children.length > 0) {
547
- const child = children.shift();
548
- if (child?.accounts) {
549
- accounts.push(...child.accounts.map(getAccountId));
659
+ const rootOu = orgUnits[ouIds[0]];
660
+ // Now look through the structure to find the OU
661
+ let currentStructure = rootOu;
662
+ for (const ou of ouIds.slice(1)) {
663
+ currentStructure = currentStructure.children?.[ou];
664
+ if (!currentStructure) {
665
+ return [false, []]; // OU not found in the structure
666
+ }
550
667
  }
551
- if (child?.children) {
552
- children.push(...Object.values(child.children));
668
+ const getAccountId = (a) => a.split('/').at(-1);
669
+ const accounts = [];
670
+ if (currentStructure.accounts) {
671
+ accounts.push(...currentStructure.accounts?.map(getAccountId));
553
672
  }
554
- }
555
- return [true, accounts];
673
+ const children = Object.values(currentStructure.children || {});
674
+ // Traverse the children to collect all accounts
675
+ while (children.length > 0) {
676
+ const child = children.shift();
677
+ if (child?.accounts) {
678
+ accounts.push(...child.accounts.map(getAccountId));
679
+ }
680
+ if (child?.children) {
681
+ children.push(...Object.values(child.children));
682
+ }
683
+ }
684
+ return [true, accounts];
685
+ });
556
686
  }
687
+ /**
688
+ * Get all the principals (users and roles) in a given account.
689
+ *
690
+ * @param accountId the ID of the account
691
+ * @returns a list of all principal ARNs in the account
692
+ */
557
693
  async getAllPrincipalsInAccount(accountId) {
558
- const iamUsers = await this.storageClient.findResourceMetadata(accountId, {
559
- service: 'iam',
560
- resourceType: 'user',
561
- account: accountId
562
- });
563
- const iamRoles = await this.storageClient.findResourceMetadata(accountId, {
564
- service: 'iam',
565
- resourceType: 'role',
566
- account: accountId
694
+ const cacheKey = `allPrincipalsInAccount:${accountId}`;
695
+ return this.withCache(cacheKey, async () => {
696
+ const iamUsers = await this.storageClient.findResourceMetadata(accountId, {
697
+ service: 'iam',
698
+ resourceType: 'user',
699
+ account: accountId
700
+ });
701
+ const iamRoles = await this.storageClient.findResourceMetadata(accountId, {
702
+ service: 'iam',
703
+ resourceType: 'role',
704
+ account: accountId
705
+ });
706
+ return [...iamUsers.map((user) => user.arn), ...iamRoles.map((role) => role.arn)];
567
707
  });
568
- return [...iamUsers.map((user) => user.arn), ...iamRoles.map((role) => role.arn)];
569
708
  }
570
709
  /**
571
710
  * Get the VPC endpoint policy for a given VPC endpoint ARN.
@@ -574,9 +713,12 @@ export class IamCollectClient {
574
713
  * @returns the VPC endpoint policy, or undefined if not found
575
714
  */
576
715
  async getVpcEndpointPolicyForArn(vpcEndpointArn) {
577
- const accountId = splitArnParts(vpcEndpointArn).accountId;
578
- const vpcEndpointPolicy = await this.storageClient.getResourceMetadata(accountId, vpcEndpointArn, 'endpoint-policy');
579
- return vpcEndpointPolicy;
716
+ const cacheKey = `vpcEndpointPolicy:${vpcEndpointArn}`;
717
+ return this.withCache(cacheKey, async () => {
718
+ const accountId = splitArnParts(vpcEndpointArn).accountId;
719
+ const vpcEndpointPolicy = await this.storageClient.getResourceMetadata(accountId, vpcEndpointArn, 'endpoint-policy');
720
+ return vpcEndpointPolicy;
721
+ });
580
722
  }
581
723
  /**
582
724
  * Get the ARN of a VPC endpoint given its ID.
@@ -584,7 +726,7 @@ export class IamCollectClient {
584
726
  * @returns the ARN of the VPC endpoint, or undefined if not found
585
727
  */
586
728
  async getVpcEndpointArnForVpcEndpointId(vpcEndpointId) {
587
- const index = await this.storageClient.getIndex('vpcs', {
729
+ const index = await this.getIndex('vpcs', {
588
730
  endpoints: {},
589
731
  vpcs: {}
590
732
  });
@@ -598,7 +740,7 @@ export class IamCollectClient {
598
740
  * @returns the VPC endpoint ID, or undefined if not found
599
741
  */
600
742
  async getVpcEndpointIdForVpcService(vpcId, service) {
601
- const index = await this.storageClient.getIndex('vpcs', {
743
+ const index = await this.getIndex('vpcs', {
602
744
  endpoints: {},
603
745
  vpcs: {}
604
746
  });
@@ -616,7 +758,7 @@ export class IamCollectClient {
616
758
  * @returns the VPC ID, or undefined if not found
617
759
  */
618
760
  async getVpcIdForVpcEndpointId(vpcEndpointId) {
619
- const index = await this.storageClient.getIndex('vpcs', {
761
+ const index = await this.getIndex('vpcs', {
620
762
  endpoints: {},
621
763
  vpcs: {}
622
764
  });