@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.
- package/dist/cjs/canWhat/canWhat.d.ts +16 -0
- package/dist/cjs/canWhat/canWhat.d.ts.map +1 -1
- package/dist/cjs/canWhat/canWhat.js +7 -0
- package/dist/cjs/canWhat/canWhat.js.map +1 -1
- package/dist/cjs/cli.js +9 -3
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/collect/client.d.ts +81 -3
- package/dist/cjs/collect/client.d.ts.map +1 -1
- package/dist/cjs/collect/client.js +294 -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/index.d.ts +6 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +15 -0
- package/dist/cjs/index.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 +7 -0
- 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 +32 -0
- package/dist/cjs/simulate/simulate.d.ts.map +1 -1
- package/dist/cjs/simulate/simulate.js +11 -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/canWhat/canWhat.d.ts +16 -0
- package/dist/esm/canWhat/canWhat.d.ts.map +1 -1
- package/dist/esm/canWhat/canWhat.js +7 -0
- package/dist/esm/canWhat/canWhat.js.map +1 -1
- package/dist/esm/cli.js +9 -3
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/collect/client.d.ts +81 -3
- package/dist/esm/collect/client.d.ts.map +1 -1
- package/dist/esm/collect/client.js +292 -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/index.d.ts +6 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -1
- package/dist/esm/index.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 +7 -0
- 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 +32 -0
- package/dist/esm/simulate/simulate.d.ts.map +1 -1
- package/dist/esm/simulate/simulate.js +12 -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,61 @@
|
|
|
1
1
|
import { splitArnParts } from '@cloud-copilot/iam-utils';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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 (
|
|
11
|
-
return this.
|
|
10
|
+
if (cacheKey in this.cache) {
|
|
11
|
+
return this.cache[cacheKey];
|
|
12
12
|
}
|
|
13
13
|
const value = await fetcher();
|
|
14
|
-
|
|
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
|
|
26
|
-
return
|
|
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
|
-
|
|
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
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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);
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
139
|
-
|
|
140
|
-
|
|
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
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
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
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
metadataKey = '
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
|
476
|
-
return
|
|
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
|
|
487
|
-
return
|
|
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
|
|
499
|
-
|
|
500
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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));
|
|
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
|
-
|
|
552
|
-
|
|
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
|
-
|
|
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
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
|
578
|
-
|
|
579
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
761
|
+
const index = await this.getIndex('vpcs', {
|
|
620
762
|
endpoints: {},
|
|
621
763
|
vpcs: {}
|
|
622
764
|
});
|