@cloud-copilot/iam-lens 0.1.25 → 0.1.26
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.
- package/dist/cjs/cli.js +9 -3
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/collect/client.d.ts +12 -3
- package/dist/cjs/collect/client.d.ts.map +1 -1
- package/dist/cjs/collect/client.js +237 -152
- package/dist/cjs/collect/client.js.map +1 -1
- package/dist/cjs/collect/collect.d.ts +2 -2
- package/dist/cjs/collect/collect.d.ts.map +1 -1
- package/dist/cjs/collect/collect.js +2 -2
- package/dist/cjs/collect/collect.js.map +1 -1
- package/dist/cjs/principals.d.ts +10 -0
- package/dist/cjs/principals.d.ts.map +1 -1
- package/dist/cjs/principals.js +33 -1
- package/dist/cjs/principals.js.map +1 -1
- package/dist/cjs/simulate/contextKeys.d.ts.map +1 -1
- package/dist/cjs/simulate/contextKeys.js +4 -1
- package/dist/cjs/simulate/contextKeys.js.map +1 -1
- package/dist/cjs/simulate/simulate.d.ts +1 -0
- package/dist/cjs/simulate/simulate.d.ts.map +1 -1
- package/dist/cjs/simulate/simulate.js +4 -0
- package/dist/cjs/simulate/simulate.js.map +1 -1
- package/dist/cjs/test-datasets/testClient.d.ts +2 -0
- package/dist/cjs/test-datasets/testClient.d.ts.map +1 -1
- package/dist/cjs/test-datasets/testClient.js +12 -0
- package/dist/cjs/test-datasets/testClient.js.map +1 -1
- package/dist/cjs/utils/workerScript.d.ts +8 -0
- package/dist/cjs/utils/workerScript.d.ts.map +1 -0
- package/dist/cjs/utils/workerScript.js +18 -0
- package/dist/cjs/utils/workerScript.js.map +1 -0
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts +14 -0
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.js +34 -0
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.js.map +1 -0
- package/dist/cjs/whoCan/WhoCanWorker.d.ts +12 -0
- package/dist/cjs/whoCan/WhoCanWorker.d.ts.map +1 -0
- package/dist/cjs/whoCan/WhoCanWorker.js +71 -0
- package/dist/cjs/whoCan/WhoCanWorker.js.map +1 -0
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js +46 -0
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
- package/dist/cjs/whoCan/whoCan.d.ts +2 -1
- package/dist/cjs/whoCan/whoCan.d.ts.map +1 -1
- package/dist/cjs/whoCan/whoCan.js +111 -77
- package/dist/cjs/whoCan/whoCan.js.map +1 -1
- package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts +15 -0
- package/dist/cjs/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
- package/dist/cjs/workers/ArrayStreamingWorkQueue.js +35 -0
- package/dist/cjs/workers/ArrayStreamingWorkQueue.js.map +1 -0
- package/dist/cjs/workers/JobRunner.d.ts +47 -0
- package/dist/cjs/workers/JobRunner.d.ts.map +1 -0
- package/dist/cjs/workers/JobRunner.js +101 -0
- package/dist/cjs/workers/JobRunner.js.map +1 -0
- package/dist/cjs/workers/RingQueue.d.ts +14 -0
- package/dist/cjs/workers/RingQueue.d.ts.map +1 -0
- package/dist/cjs/workers/RingQueue.js +43 -0
- package/dist/cjs/workers/RingQueue.js.map +1 -0
- package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts +10 -0
- package/dist/cjs/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
- package/dist/cjs/workers/SharedArrayBufferMainCache.js +37 -0
- package/dist/cjs/workers/SharedArrayBufferMainCache.js.map +1 -0
- package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
- package/dist/cjs/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
- package/dist/cjs/workers/SharedArrayBufferWorkerCache.js +49 -0
- package/dist/cjs/workers/SharedArrayBufferWorkerCache.js.map +1 -0
- package/dist/cjs/workers/StreamingWorkQueue.d.ts +18 -0
- package/dist/cjs/workers/StreamingWorkQueue.d.ts.map +1 -0
- package/dist/cjs/workers/StreamingWorkQueue.js +67 -0
- package/dist/cjs/workers/StreamingWorkQueue.js.map +1 -0
- package/dist/cjs/workers/buffers.d.ts +4 -0
- package/dist/cjs/workers/buffers.d.ts.map +1 -0
- package/dist/cjs/workers/buffers.js +26 -0
- package/dist/cjs/workers/buffers.js.map +1 -0
- package/dist/esm/cli.js +9 -3
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/collect/client.d.ts +12 -3
- package/dist/esm/collect/client.d.ts.map +1 -1
- package/dist/esm/collect/client.js +236 -150
- package/dist/esm/collect/client.js.map +1 -1
- package/dist/esm/collect/collect.d.ts +2 -2
- package/dist/esm/collect/collect.d.ts.map +1 -1
- package/dist/esm/collect/collect.js +2 -2
- package/dist/esm/collect/collect.js.map +1 -1
- package/dist/esm/principals.d.ts +10 -0
- package/dist/esm/principals.d.ts.map +1 -1
- package/dist/esm/principals.js +30 -1
- package/dist/esm/principals.js.map +1 -1
- package/dist/esm/simulate/contextKeys.d.ts.map +1 -1
- package/dist/esm/simulate/contextKeys.js +4 -1
- package/dist/esm/simulate/contextKeys.js.map +1 -1
- package/dist/esm/simulate/simulate.d.ts +1 -0
- package/dist/esm/simulate/simulate.d.ts.map +1 -1
- package/dist/esm/simulate/simulate.js +5 -1
- package/dist/esm/simulate/simulate.js.map +1 -1
- package/dist/esm/test-datasets/testClient.d.ts +2 -0
- package/dist/esm/test-datasets/testClient.d.ts.map +1 -1
- package/dist/esm/test-datasets/testClient.js +11 -0
- package/dist/esm/test-datasets/testClient.js.map +1 -1
- package/dist/esm/utils/workerScript.d.ts +8 -0
- package/dist/esm/utils/workerScript.d.ts.map +1 -0
- package/dist/esm/utils/workerScript.js +13 -0
- package/dist/esm/utils/workerScript.js.map +1 -0
- package/dist/esm/utils/workerScriptEsm.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts +14 -0
- package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanMainThreadWorker.js +28 -0
- package/dist/esm/whoCan/WhoCanMainThreadWorker.js.map +1 -0
- package/dist/esm/whoCan/WhoCanWorker.d.ts +12 -0
- package/dist/esm/whoCan/WhoCanWorker.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanWorker.js +67 -0
- package/dist/esm/whoCan/WhoCanWorker.js.map +1 -0
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts +2 -0
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.d.ts.map +1 -0
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js +44 -0
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js.map +1 -0
- package/dist/esm/whoCan/whoCan.d.ts +2 -1
- package/dist/esm/whoCan/whoCan.d.ts.map +1 -1
- package/dist/esm/whoCan/whoCan.js +111 -77
- package/dist/esm/whoCan/whoCan.js.map +1 -1
- package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts +15 -0
- package/dist/esm/workers/ArrayStreamingWorkQueue.d.ts.map +1 -0
- package/dist/esm/workers/ArrayStreamingWorkQueue.js +31 -0
- package/dist/esm/workers/ArrayStreamingWorkQueue.js.map +1 -0
- package/dist/esm/workers/JobRunner.d.ts +47 -0
- package/dist/esm/workers/JobRunner.d.ts.map +1 -0
- package/dist/esm/workers/JobRunner.js +93 -0
- package/dist/esm/workers/JobRunner.js.map +1 -0
- package/dist/esm/workers/RingQueue.d.ts +14 -0
- package/dist/esm/workers/RingQueue.d.ts.map +1 -0
- package/dist/esm/workers/RingQueue.js +36 -0
- package/dist/esm/workers/RingQueue.js.map +1 -0
- package/dist/esm/workers/SharedArrayBufferMainCache.d.ts +10 -0
- package/dist/esm/workers/SharedArrayBufferMainCache.d.ts.map +1 -0
- package/dist/esm/workers/SharedArrayBufferMainCache.js +33 -0
- package/dist/esm/workers/SharedArrayBufferMainCache.js.map +1 -0
- package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts +10 -0
- package/dist/esm/workers/SharedArrayBufferWorkerCache.d.ts.map +1 -0
- package/dist/esm/workers/SharedArrayBufferWorkerCache.js +44 -0
- package/dist/esm/workers/SharedArrayBufferWorkerCache.js.map +1 -0
- package/dist/esm/workers/StreamingWorkQueue.d.ts +18 -0
- package/dist/esm/workers/StreamingWorkQueue.d.ts.map +1 -0
- package/dist/esm/workers/StreamingWorkQueue.js +61 -0
- package/dist/esm/workers/StreamingWorkQueue.js.map +1 -0
- package/dist/esm/workers/buffers.d.ts +4 -0
- package/dist/esm/workers/buffers.d.ts.map +1 -0
- package/dist/esm/workers/buffers.js +21 -0
- package/dist/esm/workers/buffers.js.map +1 -0
- package/package.json +6 -4
|
@@ -1,29 +1,46 @@
|
|
|
1
1
|
import { splitArnParts } from '@cloud-copilot/iam-utils';
|
|
2
|
-
|
|
3
|
-
constructor(
|
|
4
|
-
this.
|
|
5
|
-
this._cache = {};
|
|
6
|
-
this._enableCaching = clientOptions?.enableCaching !== false;
|
|
2
|
+
class InMemoryCacheProvider {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.cache = {};
|
|
7
5
|
}
|
|
8
|
-
// Generic cache helper
|
|
9
6
|
async withCache(cacheKey, fetcher) {
|
|
10
|
-
if (
|
|
11
|
-
return this.
|
|
7
|
+
if (cacheKey in this.cache) {
|
|
8
|
+
return this.cache[cacheKey];
|
|
12
9
|
}
|
|
13
10
|
const value = await fetcher();
|
|
14
|
-
|
|
15
|
-
this._cache[cacheKey] = value;
|
|
16
|
-
}
|
|
11
|
+
this.cache[cacheKey] = value;
|
|
17
12
|
return value;
|
|
18
13
|
}
|
|
14
|
+
}
|
|
15
|
+
export class NoCacheProvider {
|
|
16
|
+
async withCache(cacheKey, fetcher) {
|
|
17
|
+
return fetcher();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class IamCollectClient {
|
|
21
|
+
constructor(storageClient, clientOptions) {
|
|
22
|
+
this.storageClient = storageClient;
|
|
23
|
+
if (clientOptions?.cacheProvider === undefined) {
|
|
24
|
+
this.cacheProvider = new InMemoryCacheProvider();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.cacheProvider = clientOptions.cacheProvider;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async withCache(cacheKey, fetcher) {
|
|
31
|
+
return this.cacheProvider.withCache(cacheKey, fetcher);
|
|
32
|
+
}
|
|
19
33
|
/**
|
|
20
34
|
* Checks if an account exists in the store.
|
|
21
35
|
* @param accountId The ID of the account to check.
|
|
22
36
|
* @returns True if the account exists, false otherwise.
|
|
23
37
|
*/
|
|
24
38
|
async accountExists(accountId) {
|
|
25
|
-
const
|
|
26
|
-
return
|
|
39
|
+
const cacheKey = `accountExists:${accountId}`;
|
|
40
|
+
return this.withCache(cacheKey, async () => {
|
|
41
|
+
const accounts = await this.storageClient.listAccountIds();
|
|
42
|
+
return accounts.includes(accountId);
|
|
43
|
+
});
|
|
27
44
|
}
|
|
28
45
|
/**
|
|
29
46
|
* Get all account IDs in the store.
|
|
@@ -31,7 +48,10 @@ export class IamCollectClient {
|
|
|
31
48
|
* @returns all account IDs in the store
|
|
32
49
|
*/
|
|
33
50
|
async allAccounts() {
|
|
34
|
-
|
|
51
|
+
const cacheKey = `allAccounts`;
|
|
52
|
+
return this.withCache(cacheKey, async () => {
|
|
53
|
+
return this.storageClient.listAccountIds();
|
|
54
|
+
});
|
|
35
55
|
}
|
|
36
56
|
/**
|
|
37
57
|
* Checks if a principal exists in the store.
|
|
@@ -39,9 +59,12 @@ export class IamCollectClient {
|
|
|
39
59
|
* @returns True if the principal exists, false otherwise.
|
|
40
60
|
*/
|
|
41
61
|
async principalExists(principalArn) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
62
|
+
const cacheKey = `principalExists:${principalArn}`;
|
|
63
|
+
return this.withCache(cacheKey, async () => {
|
|
64
|
+
const accountId = splitArnParts(principalArn).accountId;
|
|
65
|
+
const principalData = await this.storageClient.getResourceMetadata(accountId, principalArn, 'metadata');
|
|
66
|
+
return !!principalData;
|
|
67
|
+
});
|
|
45
68
|
}
|
|
46
69
|
/**
|
|
47
70
|
* Gets the SCP Hierarchy for an account. The first element is the root, the last element is the account itself.
|
|
@@ -99,21 +122,24 @@ export class IamCollectClient {
|
|
|
99
122
|
* @returns The OUs for the account.
|
|
100
123
|
*/
|
|
101
124
|
async getOrgUnitHierarchyForAccount(accountId) {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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);
|
|
125
|
+
const cacheKey = `orgUnitHierarchy:${accountId}`;
|
|
126
|
+
return this.withCache(cacheKey, async () => {
|
|
127
|
+
const orgId = await this.getOrgIdForAccount(accountId);
|
|
128
|
+
if (!orgId) {
|
|
129
|
+
return [];
|
|
113
130
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
131
|
+
const ouIds = [];
|
|
132
|
+
let ouId = await this.getOrgUnitIdForAccount(accountId);
|
|
133
|
+
ouIds.push(ouId);
|
|
134
|
+
while (ouId) {
|
|
135
|
+
const parentOuId = await this.getParentOrgUnitIdForOrgUnit(orgId, ouId);
|
|
136
|
+
if (parentOuId) {
|
|
137
|
+
ouIds.unshift(parentOuId);
|
|
138
|
+
}
|
|
139
|
+
ouId = parentOuId;
|
|
140
|
+
}
|
|
141
|
+
return ouIds;
|
|
142
|
+
});
|
|
117
143
|
}
|
|
118
144
|
/**
|
|
119
145
|
* Gets the org unit ID for an account.
|
|
@@ -121,12 +147,15 @@ export class IamCollectClient {
|
|
|
121
147
|
* @returns The org unit ID for the account, or undefined if not found.
|
|
122
148
|
*/
|
|
123
149
|
async getOrgUnitIdForAccount(accountId) {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
150
|
+
const cacheKey = `orgUnitId:${accountId}`;
|
|
151
|
+
return this.withCache(cacheKey, async () => {
|
|
152
|
+
const orgId = await this.getOrgIdForAccount(accountId);
|
|
153
|
+
if (!orgId) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
const accounts = (await this.getAccountDataForOrg(orgId));
|
|
157
|
+
return accounts[accountId].ou;
|
|
158
|
+
});
|
|
130
159
|
}
|
|
131
160
|
/**
|
|
132
161
|
* Gets the parent org unit ID for a given org unit.
|
|
@@ -135,9 +164,12 @@ export class IamCollectClient {
|
|
|
135
164
|
* @returns The parent org unit ID, or undefined if not found.
|
|
136
165
|
*/
|
|
137
166
|
async getParentOrgUnitIdForOrgUnit(orgId, ouId) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
167
|
+
const cacheKey = `parentOrgUnit:${orgId}:${ouId}`;
|
|
168
|
+
return this.withCache(cacheKey, async () => {
|
|
169
|
+
const ouData = await this.getOrgUnitsDataForOrg(orgId);
|
|
170
|
+
const ou = ouData[ouId];
|
|
171
|
+
return ou.parent;
|
|
172
|
+
});
|
|
141
173
|
}
|
|
142
174
|
/**
|
|
143
175
|
* Gets the SCPs for an account.
|
|
@@ -154,19 +186,22 @@ export class IamCollectClient {
|
|
|
154
186
|
* @returns The org policies for the account.
|
|
155
187
|
*/
|
|
156
188
|
async getOrgPoliciesForAccount(accountId, policyType) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
189
|
+
const cacheKey = `orgPoliciesForAccount:${accountId}:${policyType}`;
|
|
190
|
+
return this.withCache(cacheKey, async () => {
|
|
191
|
+
const orgId = await this.getOrgIdForAccount(accountId);
|
|
192
|
+
if (!orgId) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
const accounts = (await this.getAccountDataForOrg(orgId));
|
|
196
|
+
const orgInformation = accounts[accountId];
|
|
197
|
+
const policyArns = orgInformation[policyType];
|
|
198
|
+
const policies = [];
|
|
199
|
+
for (const policyArn of policyArns) {
|
|
200
|
+
const policyInfo = await this.getOrgPolicy(orgId, policyType, policyArn);
|
|
201
|
+
policies.push(policyInfo);
|
|
202
|
+
}
|
|
203
|
+
return policies;
|
|
204
|
+
});
|
|
170
205
|
}
|
|
171
206
|
/**
|
|
172
207
|
* Gets the account data for an organization.
|
|
@@ -174,7 +209,10 @@ export class IamCollectClient {
|
|
|
174
209
|
* @returns The account data for the organization.
|
|
175
210
|
*/
|
|
176
211
|
async getAccountDataForOrg(orgId) {
|
|
177
|
-
|
|
212
|
+
const cacheKey = `accountDataForOrg:${orgId}`;
|
|
213
|
+
return this.withCache(cacheKey, async () => {
|
|
214
|
+
return this.storageClient.getOrganizationMetadata(orgId, 'accounts');
|
|
215
|
+
});
|
|
178
216
|
}
|
|
179
217
|
/**
|
|
180
218
|
* Gets the org units data for an organization.
|
|
@@ -182,7 +220,10 @@ export class IamCollectClient {
|
|
|
182
220
|
* @returns The org units data for the organization.
|
|
183
221
|
*/
|
|
184
222
|
async getOrgUnitsDataForOrg(orgId) {
|
|
185
|
-
|
|
223
|
+
const cacheKey = `orgUnitsDataForOrg:${orgId}`;
|
|
224
|
+
return this.withCache(cacheKey, async () => {
|
|
225
|
+
return this.storageClient.getOrganizationMetadata(orgId, 'ous');
|
|
226
|
+
});
|
|
186
227
|
}
|
|
187
228
|
/**
|
|
188
229
|
* Gets a specific org policy.
|
|
@@ -192,17 +233,20 @@ export class IamCollectClient {
|
|
|
192
233
|
* @returns The org policy.
|
|
193
234
|
*/
|
|
194
235
|
async getOrgPolicy(orgId, policyType, policyArn) {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
236
|
+
const cacheKey = `orgPolicy:${orgId}:${policyType}:${policyArn}`;
|
|
237
|
+
return this.withCache(cacheKey, async () => {
|
|
238
|
+
const policyId = policyArn.split('/').at(-1);
|
|
239
|
+
const policyData = await this.storageClient.getOrganizationPolicyMetadata(orgId, policyType, policyId, 'metadata');
|
|
240
|
+
const policyDocument = await this.storageClient.getOrganizationPolicyMetadata(orgId, policyType, policyId, 'policy');
|
|
241
|
+
if (!policyDocument) {
|
|
242
|
+
console.error(`Policy document not found for ${policyArn} in org ${orgId}`);
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
arn: policyData.arn,
|
|
246
|
+
name: policyData.name,
|
|
247
|
+
policy: policyDocument
|
|
248
|
+
};
|
|
249
|
+
});
|
|
206
250
|
}
|
|
207
251
|
/**
|
|
208
252
|
* Gets the RCPs for an account.
|
|
@@ -237,15 +281,18 @@ export class IamCollectClient {
|
|
|
237
281
|
* @returns The org policies for the org unit.
|
|
238
282
|
*/
|
|
239
283
|
async getOrgPoliciesForOrgUnit(orgId, orgUnitId, policyType) {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
284
|
+
const cacheKey = `orgPoliciesForOrgUnit:${orgId}:${orgUnitId}:${policyType}`;
|
|
285
|
+
return this.withCache(cacheKey, async () => {
|
|
286
|
+
const orgUnitInformation = await this.getOrgUnitsDataForOrg(orgId);
|
|
287
|
+
const orgUnit = orgUnitInformation[orgUnitId];
|
|
288
|
+
const orgPolicies = orgUnit[policyType];
|
|
289
|
+
const policies = [];
|
|
290
|
+
for (const policyArn of orgPolicies) {
|
|
291
|
+
const policyInfo = await this.getOrgPolicy(orgId, policyType, policyArn);
|
|
292
|
+
policies.push(policyInfo);
|
|
293
|
+
}
|
|
294
|
+
return policies;
|
|
295
|
+
});
|
|
249
296
|
}
|
|
250
297
|
/**
|
|
251
298
|
* Gets the RCPs for an org unit.
|
|
@@ -262,17 +309,23 @@ export class IamCollectClient {
|
|
|
262
309
|
* @returns The org ID for the account, or undefined if not found.
|
|
263
310
|
*/
|
|
264
311
|
async getOrgIdForAccount(accountId) {
|
|
265
|
-
const index = await this.
|
|
312
|
+
const index = await this.getIndex('accounts-to-orgs', {});
|
|
266
313
|
const accountToOrgMap = index.data;
|
|
267
314
|
return accountToOrgMap[accountId];
|
|
268
315
|
}
|
|
316
|
+
async getIndex(indexName, defaultValue) {
|
|
317
|
+
const cacheKey = `index:${indexName}`;
|
|
318
|
+
return this.withCache(cacheKey, async () => {
|
|
319
|
+
return this.storageClient.getIndex(indexName, defaultValue);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
269
322
|
/**
|
|
270
323
|
* Gets the account ID for a given S3 bucket name.
|
|
271
324
|
* @param bucketName The name of the bucket.
|
|
272
325
|
* @returns The account ID for the bucket, or undefined if not found.
|
|
273
326
|
*/
|
|
274
327
|
async getAccountIdForBucket(bucketName) {
|
|
275
|
-
const index = await this.
|
|
328
|
+
const index = await this.getIndex('buckets-to-accounts', {});
|
|
276
329
|
const bucketToAccountMap = index.data;
|
|
277
330
|
return bucketToAccountMap[bucketName]?.accountId;
|
|
278
331
|
}
|
|
@@ -282,7 +335,7 @@ export class IamCollectClient {
|
|
|
282
335
|
* @returns The account ID for the API Gateway, or undefined if not found.
|
|
283
336
|
*/
|
|
284
337
|
async getAccountIdForRestApi(apiArn) {
|
|
285
|
-
const index = await this.
|
|
338
|
+
const index = await this.getIndex('apigateways-to-accounts', {});
|
|
286
339
|
const bucketToAccountMap = index.data;
|
|
287
340
|
return bucketToAccountMap[apiArn];
|
|
288
341
|
}
|
|
@@ -304,16 +357,19 @@ export class IamCollectClient {
|
|
|
304
357
|
});
|
|
305
358
|
}
|
|
306
359
|
async getManagedPolicy(accountId, policyArn) {
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
360
|
+
const cacheKey = `managedPolicy:${accountId}:${policyArn}`;
|
|
361
|
+
return this.withCache(cacheKey, async () => {
|
|
362
|
+
const policyMetadata = await this.storageClient.getResourceMetadata(accountId, policyArn, 'metadata');
|
|
363
|
+
const policyDocument = await this.storageClient.getResourceMetadata(accountId, policyArn, 'current-policy');
|
|
364
|
+
if (!policyDocument) {
|
|
365
|
+
console.error(`Policy document not found for ${policyArn} in account ${accountId}`);
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
arn: policyMetadata.arn,
|
|
369
|
+
name: policyMetadata.name,
|
|
370
|
+
policy: policyDocument
|
|
371
|
+
};
|
|
372
|
+
});
|
|
317
373
|
}
|
|
318
374
|
/**
|
|
319
375
|
* Gets the inline policies attached to a user.
|
|
@@ -332,9 +388,12 @@ export class IamCollectClient {
|
|
|
332
388
|
});
|
|
333
389
|
}
|
|
334
390
|
async getIamUserMetadata(userArn) {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
391
|
+
const cacheKey = `iamUserMetadata:${userArn}`;
|
|
392
|
+
return this.withCache(cacheKey, async () => {
|
|
393
|
+
const accountId = splitArnParts(userArn).accountId;
|
|
394
|
+
// The permissions boundary is stored as a policy ARN on the user resource metadata
|
|
395
|
+
return this.storageClient.getResourceMetadata(accountId, userArn, 'metadata');
|
|
396
|
+
});
|
|
338
397
|
}
|
|
339
398
|
/**
|
|
340
399
|
* Gets the permissions boundary policy attached to a user, if any.
|
|
@@ -446,7 +505,10 @@ export class IamCollectClient {
|
|
|
446
505
|
* @returns the metadata for the organization
|
|
447
506
|
*/
|
|
448
507
|
async getOrganizationMetadata(organizationId) {
|
|
449
|
-
|
|
508
|
+
const cacheKey = `organizationMetadata:${organizationId}`;
|
|
509
|
+
return this.withCache(cacheKey, async () => {
|
|
510
|
+
return this.storageClient.getOrganizationMetadata(organizationId, 'metadata');
|
|
511
|
+
});
|
|
450
512
|
}
|
|
451
513
|
/**
|
|
452
514
|
* Gets the resource policy for a given resource ARN and account.
|
|
@@ -456,13 +518,16 @@ export class IamCollectClient {
|
|
|
456
518
|
* @returns The resource policy, or undefined if not found.
|
|
457
519
|
*/
|
|
458
520
|
async getResourcePolicyForArn(resourceArn, accountId) {
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
metadataKey = '
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
521
|
+
const cacheKey = `resourcePolicy:${accountId}:${resourceArn}`;
|
|
522
|
+
return this.withCache(cacheKey, async () => {
|
|
523
|
+
const arnParts = splitArnParts(resourceArn);
|
|
524
|
+
let metadataKey = 'policy';
|
|
525
|
+
if (arnParts.service === 'iam' && arnParts.resourceType === 'role') {
|
|
526
|
+
metadataKey = 'trust-policy';
|
|
527
|
+
}
|
|
528
|
+
const resourcePolicy = await this.storageClient.getResourceMetadata(accountId, resourceArn, metadataKey);
|
|
529
|
+
return resourcePolicy;
|
|
530
|
+
});
|
|
466
531
|
}
|
|
467
532
|
/**
|
|
468
533
|
* Gets the RAM share policy for a given resource ARN and account.
|
|
@@ -472,8 +537,11 @@ export class IamCollectClient {
|
|
|
472
537
|
* @returns The RAM share policy, or undefined if not found.
|
|
473
538
|
*/
|
|
474
539
|
async getRamSharePolicyForArn(resourceArn, accountId) {
|
|
475
|
-
const
|
|
476
|
-
return
|
|
540
|
+
const cacheKey = `ramSharePolicy:${accountId}:${resourceArn}`;
|
|
541
|
+
return this.withCache(cacheKey, async () => {
|
|
542
|
+
const armSharePolicy = await this.storageClient.getRamResource(accountId, resourceArn);
|
|
543
|
+
return armSharePolicy?.policy;
|
|
544
|
+
});
|
|
477
545
|
}
|
|
478
546
|
/**
|
|
479
547
|
* Gets the tags for a given resource ARN and account.
|
|
@@ -483,8 +551,11 @@ export class IamCollectClient {
|
|
|
483
551
|
* @returns The tags as a record, or undefined if not found.
|
|
484
552
|
*/
|
|
485
553
|
async getTagsForResource(resourceArn, accountId) {
|
|
486
|
-
const
|
|
487
|
-
return
|
|
554
|
+
const cacheKey = `tagsForResource:${accountId}:${resourceArn}`;
|
|
555
|
+
return this.withCache(cacheKey, async () => {
|
|
556
|
+
const tags = await this.storageClient.getResourceMetadata(accountId, resourceArn, 'tags');
|
|
557
|
+
return tags || {};
|
|
558
|
+
});
|
|
488
559
|
}
|
|
489
560
|
/**
|
|
490
561
|
* Gets a unique ID for an IAM resource based on its ARN and account ID.
|
|
@@ -495,9 +566,12 @@ export class IamCollectClient {
|
|
|
495
566
|
* @returns a unique ID for the resource, or undefined if not found
|
|
496
567
|
*/
|
|
497
568
|
async getUniqueIdForIamResource(resourceArn) {
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
569
|
+
const cacheKey = `uniqueIdForIamResource:${resourceArn}`;
|
|
570
|
+
return this.withCache(cacheKey, async () => {
|
|
571
|
+
const accountId = splitArnParts(resourceArn).accountId;
|
|
572
|
+
const resourceMetadata = await this.storageClient.getResourceMetadata(accountId, resourceArn, 'metadata');
|
|
573
|
+
return resourceMetadata?.id;
|
|
574
|
+
});
|
|
501
575
|
}
|
|
502
576
|
/**
|
|
503
577
|
* Get the account IDs for an organization.
|
|
@@ -520,52 +594,61 @@ export class IamCollectClient {
|
|
|
520
594
|
* @returns returns the organization structure or undefined if not found
|
|
521
595
|
*/
|
|
522
596
|
async getOrganizationStructure(orgId) {
|
|
523
|
-
|
|
597
|
+
const cacheKey = `organizationStructure:${orgId}`;
|
|
598
|
+
return this.withCache(cacheKey, async () => {
|
|
599
|
+
return this.storageClient.getOrganizationMetadata(orgId, 'structure');
|
|
600
|
+
});
|
|
524
601
|
}
|
|
525
602
|
async getAccountsForOrgPath(orgId, ouIds) {
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
|
603
|
+
const cacheKey = `accountsForOrgPath:${orgId}:${ouIds.join('/')}`;
|
|
604
|
+
return this.withCache(cacheKey, async () => {
|
|
605
|
+
const orgUnits = await this.getOrganizationStructure(orgId);
|
|
606
|
+
if (!orgUnits || ouIds.length === 0) {
|
|
607
|
+
return [false, []];
|
|
537
608
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
while (children.length > 0) {
|
|
547
|
-
const child = children.shift();
|
|
548
|
-
if (child?.accounts) {
|
|
549
|
-
accounts.push(...child.accounts.map(getAccountId));
|
|
609
|
+
const rootOu = orgUnits[ouIds[0]];
|
|
610
|
+
// Now look through the structure to find the OU
|
|
611
|
+
let currentStructure = rootOu;
|
|
612
|
+
for (const ou of ouIds.slice(1)) {
|
|
613
|
+
currentStructure = currentStructure.children?.[ou];
|
|
614
|
+
if (!currentStructure) {
|
|
615
|
+
return [false, []]; // OU not found in the structure
|
|
616
|
+
}
|
|
550
617
|
}
|
|
551
|
-
|
|
552
|
-
|
|
618
|
+
const getAccountId = (a) => a.split('/').at(-1);
|
|
619
|
+
const accounts = [];
|
|
620
|
+
if (currentStructure.accounts) {
|
|
621
|
+
accounts.push(...currentStructure.accounts?.map(getAccountId));
|
|
553
622
|
}
|
|
554
|
-
|
|
555
|
-
|
|
623
|
+
const children = Object.values(currentStructure.children || {});
|
|
624
|
+
// Traverse the children to collect all accounts
|
|
625
|
+
while (children.length > 0) {
|
|
626
|
+
const child = children.shift();
|
|
627
|
+
if (child?.accounts) {
|
|
628
|
+
accounts.push(...child.accounts.map(getAccountId));
|
|
629
|
+
}
|
|
630
|
+
if (child?.children) {
|
|
631
|
+
children.push(...Object.values(child.children));
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return [true, accounts];
|
|
635
|
+
});
|
|
556
636
|
}
|
|
557
637
|
async getAllPrincipalsInAccount(accountId) {
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
638
|
+
const cacheKey = `allPrincipalsInAccount:${accountId}`;
|
|
639
|
+
return this.withCache(cacheKey, async () => {
|
|
640
|
+
const iamUsers = await this.storageClient.findResourceMetadata(accountId, {
|
|
641
|
+
service: 'iam',
|
|
642
|
+
resourceType: 'user',
|
|
643
|
+
account: accountId
|
|
644
|
+
});
|
|
645
|
+
const iamRoles = await this.storageClient.findResourceMetadata(accountId, {
|
|
646
|
+
service: 'iam',
|
|
647
|
+
resourceType: 'role',
|
|
648
|
+
account: accountId
|
|
649
|
+
});
|
|
650
|
+
return [...iamUsers.map((user) => user.arn), ...iamRoles.map((role) => role.arn)];
|
|
567
651
|
});
|
|
568
|
-
return [...iamUsers.map((user) => user.arn), ...iamRoles.map((role) => role.arn)];
|
|
569
652
|
}
|
|
570
653
|
/**
|
|
571
654
|
* Get the VPC endpoint policy for a given VPC endpoint ARN.
|
|
@@ -574,9 +657,12 @@ export class IamCollectClient {
|
|
|
574
657
|
* @returns the VPC endpoint policy, or undefined if not found
|
|
575
658
|
*/
|
|
576
659
|
async getVpcEndpointPolicyForArn(vpcEndpointArn) {
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
660
|
+
const cacheKey = `vpcEndpointPolicy:${vpcEndpointArn}`;
|
|
661
|
+
return this.withCache(cacheKey, async () => {
|
|
662
|
+
const accountId = splitArnParts(vpcEndpointArn).accountId;
|
|
663
|
+
const vpcEndpointPolicy = await this.storageClient.getResourceMetadata(accountId, vpcEndpointArn, 'endpoint-policy');
|
|
664
|
+
return vpcEndpointPolicy;
|
|
665
|
+
});
|
|
580
666
|
}
|
|
581
667
|
/**
|
|
582
668
|
* Get the ARN of a VPC endpoint given its ID.
|
|
@@ -584,7 +670,7 @@ export class IamCollectClient {
|
|
|
584
670
|
* @returns the ARN of the VPC endpoint, or undefined if not found
|
|
585
671
|
*/
|
|
586
672
|
async getVpcEndpointArnForVpcEndpointId(vpcEndpointId) {
|
|
587
|
-
const index = await this.
|
|
673
|
+
const index = await this.getIndex('vpcs', {
|
|
588
674
|
endpoints: {},
|
|
589
675
|
vpcs: {}
|
|
590
676
|
});
|
|
@@ -598,7 +684,7 @@ export class IamCollectClient {
|
|
|
598
684
|
* @returns the VPC endpoint ID, or undefined if not found
|
|
599
685
|
*/
|
|
600
686
|
async getVpcEndpointIdForVpcService(vpcId, service) {
|
|
601
|
-
const index = await this.
|
|
687
|
+
const index = await this.getIndex('vpcs', {
|
|
602
688
|
endpoints: {},
|
|
603
689
|
vpcs: {}
|
|
604
690
|
});
|
|
@@ -616,7 +702,7 @@ export class IamCollectClient {
|
|
|
616
702
|
* @returns the VPC ID, or undefined if not found
|
|
617
703
|
*/
|
|
618
704
|
async getVpcIdForVpcEndpointId(vpcEndpointId) {
|
|
619
|
-
const index = await this.
|
|
705
|
+
const index = await this.getIndex('vpcs', {
|
|
620
706
|
endpoints: {},
|
|
621
707
|
vpcs: {}
|
|
622
708
|
});
|