@gugananuvem/aws-local-simulator 1.0.35 → 1.0.39

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 (149) hide show
  1. package/README.md +662 -658
  2. package/package.json +10 -3
  3. package/src/config/default-config.js +71 -0
  4. package/src/config/env-loader.js +4 -2
  5. package/src/middleware/authorization.js +271 -0
  6. package/src/middleware/cloudtrail-logger.js +333 -0
  7. package/src/middleware/error-handler.js +138 -0
  8. package/src/middleware/index.js +79 -0
  9. package/src/middleware/policy-enforcement.js +407 -0
  10. package/src/server.js +74 -4
  11. package/src/services/acm/index.js +66 -0
  12. package/src/services/acm/models/certificate.js +202 -0
  13. package/src/services/acm/server.js +224 -0
  14. package/src/services/acm/simulator.js +293 -0
  15. package/src/services/alb/index.js +104 -0
  16. package/src/services/alb/models/loadBalancer.js +88 -0
  17. package/src/services/alb/models/targetGroup.js +128 -0
  18. package/src/services/alb/server.js +160 -0
  19. package/src/services/alb/simulator.js +262 -0
  20. package/src/services/apigateway/index.js +22 -0
  21. package/src/services/apigateway/server.js +247 -35
  22. package/src/services/apigateway/simulator.js +780 -26
  23. package/src/services/asg/index.js +100 -0
  24. package/src/services/asg/models/autoScalingGroup.js +110 -0
  25. package/src/services/asg/models/launchTemplate.js +41 -0
  26. package/src/services/asg/models/scalingPolicy.js +51 -0
  27. package/src/services/asg/server.js +236 -0
  28. package/src/services/asg/simulator.js +410 -0
  29. package/src/services/aurora/cluster-manager.js +731 -0
  30. package/src/services/aurora/data-api.js +514 -0
  31. package/src/services/aurora/db-engine.js +270 -0
  32. package/src/services/aurora/index.js +159 -0
  33. package/src/services/aurora/rds-api.js +189 -0
  34. package/src/services/aurora/server.js +213 -0
  35. package/src/services/aurora/sql-translator.js +334 -0
  36. package/src/services/cloudformation/index.js +2 -2
  37. package/src/services/cloudformation/server.js +1 -0
  38. package/src/services/cloudfront/cache-engine.js +225 -0
  39. package/src/services/cloudfront/geo-restrictions.js +377 -0
  40. package/src/services/cloudfront/index.js +220 -0
  41. package/src/services/cloudfront/models/behavior.js +158 -0
  42. package/src/services/cloudfront/models/distribution.js +284 -0
  43. package/src/services/cloudfront/models/index.js +99 -0
  44. package/src/services/cloudfront/models/invalidation.js +109 -0
  45. package/src/services/cloudfront/models/origin.js +155 -0
  46. package/src/services/cloudfront/origin-router.js +552 -0
  47. package/src/services/cloudfront/origin-validator.js +398 -0
  48. package/src/services/cloudfront/server.js +512 -0
  49. package/src/services/cloudfront/signed-url-generator.js +377 -0
  50. package/src/services/cloudfront/simulator.js +425 -0
  51. package/src/services/cloudfront/store.js +326 -0
  52. package/src/services/cloudshell/env-builder.js +27 -0
  53. package/src/services/cloudshell/index.js +48 -0
  54. package/src/services/cloudshell/routes.js +60 -0
  55. package/src/services/cloudshell/session-manager.js +226 -0
  56. package/src/services/cloudshell/static/.gitkeep +0 -0
  57. package/src/services/cloudshell/static/index.html +406 -0
  58. package/src/services/cloudshell/websocket-handler.js +107 -0
  59. package/src/services/cloudtrail/index.js +2 -3
  60. package/src/services/cloudtrail/server.js +68 -0
  61. package/src/services/cognito/server.js +4 -1
  62. package/src/services/dynamodb/server.js +4 -1
  63. package/src/services/dynamodb/simulator.js +2 -1
  64. package/src/services/ec2/index.js +101 -0
  65. package/src/services/ec2/instanceServer.js +311 -0
  66. package/src/services/ec2/models/ami.js +39 -0
  67. package/src/services/ec2/models/instance.js +98 -0
  68. package/src/services/ec2/models/networkInterface.js +74 -0
  69. package/src/services/ec2/models/securityGroup.js +71 -0
  70. package/src/services/ec2/server.js +350 -0
  71. package/src/services/ec2/simulator.js +436 -0
  72. package/src/services/eventbridge/server.js +36 -0
  73. package/src/services/eventbridge/simulator.js +230 -45
  74. package/src/services/iam/index.js +65 -0
  75. package/src/services/iam/middleware.js +99 -0
  76. package/src/services/iam/server.js +331 -0
  77. package/src/services/iam/simulator.js +1432 -0
  78. package/src/services/index.js +28 -0
  79. package/src/services/kinesis-analytics/index.js +105 -0
  80. package/src/services/kinesis-analytics/models/application.js +325 -0
  81. package/src/services/kinesis-analytics/models/index.js +12 -0
  82. package/src/services/kinesis-analytics/server.js +250 -0
  83. package/src/services/kinesis-analytics/simulator.js +407 -0
  84. package/src/services/kinesis-analytics/store.js +176 -0
  85. package/src/services/kinesis-data-streams/index.js +106 -0
  86. package/src/services/kinesis-data-streams/models/index.js +12 -0
  87. package/src/services/kinesis-data-streams/models/record.js +70 -0
  88. package/src/services/kinesis-data-streams/models/stream.js +280 -0
  89. package/src/services/kinesis-data-streams/server.js +255 -0
  90. package/src/services/kinesis-data-streams/simulator.js +514 -0
  91. package/src/services/kinesis-data-streams/store.js +163 -0
  92. package/src/services/kinesis-firehose/index.js +106 -0
  93. package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
  94. package/src/services/kinesis-firehose/models/index.js +10 -0
  95. package/src/services/kinesis-firehose/server.js +215 -0
  96. package/src/services/kinesis-firehose/simulator.js +388 -0
  97. package/src/services/kinesis-firehose/store.js +163 -0
  98. package/src/services/kms/server.js +4 -1
  99. package/src/services/lambda/index.js +26 -0
  100. package/src/services/lambda/server.js +12 -1
  101. package/src/services/lambda/simulator.js +4 -0
  102. package/src/services/opensearch/index.js +105 -0
  103. package/src/services/opensearch/models/document.js +115 -0
  104. package/src/services/opensearch/models/index.js +73 -0
  105. package/src/services/opensearch/server.js +310 -0
  106. package/src/services/opensearch/simulator.js +605 -0
  107. package/src/services/opensearch/store.js +162 -0
  108. package/src/services/organizations/control-tower-simulator.js +321 -0
  109. package/src/services/organizations/control-tower-store.js +182 -0
  110. package/src/services/organizations/iam-integration.js +413 -0
  111. package/src/services/organizations/index.js +172 -0
  112. package/src/services/organizations/models/index.js +13 -0
  113. package/src/services/organizations/models/invitation.js +48 -0
  114. package/src/services/organizations/models/organization.js +566 -0
  115. package/src/services/organizations/models/role.js +244 -0
  116. package/src/services/organizations/models/scp.js +131 -0
  117. package/src/services/organizations/server.js +736 -0
  118. package/src/services/organizations/simulator.js +593 -0
  119. package/src/services/organizations/store.js +235 -0
  120. package/src/services/organizations/vpc-integration.js +453 -0
  121. package/src/services/route53/index.js +67 -0
  122. package/src/services/route53/models/healthCheck.js +101 -0
  123. package/src/services/route53/models/hostedZone.js +37 -0
  124. package/src/services/route53/models/resourceRecord.js +106 -0
  125. package/src/services/route53/server.js +841 -0
  126. package/src/services/route53/simulator.js +350 -0
  127. package/src/services/s3/index.js +62 -0
  128. package/src/services/s3/presigned-url-generator.js +337 -0
  129. package/src/services/s3/server.js +231 -0
  130. package/src/services/s3/simulator.js +551 -4
  131. package/src/services/sns/index.js +31 -0
  132. package/src/services/sns/server.js +9 -0
  133. package/src/services/sqs/index.js +38 -0
  134. package/src/services/sqs/server.js +57 -4
  135. package/src/services/sqs/simulator.js +423 -33
  136. package/src/services/sts/simulator.js +209 -17
  137. package/src/services/vpc/README.md +459 -0
  138. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  139. package/src/services/vpc/index.js +90 -0
  140. package/src/services/vpc/models/internetGateway.js +71 -0
  141. package/src/services/vpc/models/routeTable.js +160 -0
  142. package/src/services/vpc/models/subnet.js +48 -0
  143. package/src/services/vpc/models/vpc.js +37 -0
  144. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  145. package/src/services/vpc/models/vpcGateway.js +134 -0
  146. package/src/services/vpc/server.js +836 -0
  147. package/src/services/vpc/simulator.js +613 -0
  148. package/src/utils/policy-cache.js +215 -0
  149. package/aws-config +0 -154
@@ -0,0 +1,566 @@
1
+ /**
2
+ * AWS Organizations - Organization Model
3
+ * Representa a raiz da organização e gerencia estrutura hierárquica
4
+ */
5
+
6
+ const { v4: uuidv4 } = require('uuid');
7
+ const ServiceControlPolicy = require('./scp');
8
+
9
+ class Organization {
10
+ constructor(config = {}) {
11
+ this.id = config.id || uuidv4().substring(0, 12);
12
+ this.arn = config.arn || `arn:aws:organizations::123456789012:organization/o-${this.id}`;
13
+ this.rootId = config.rootId || `r-${uuidv4().substring(0, 4)}`;
14
+ this.masterAccountId = config.masterAccountId || '123456789012';
15
+ this.masterAccountArn = config.masterAccountArn ||
16
+ `arn:aws:organizations::123456789012:account/o-${this.id}/${this.masterAccountId}`;
17
+ this.masterAccountEmail = config.masterAccountEmail || 'master@example.com';
18
+ this.masterAccountName = config.masterAccountName || 'Master Account';
19
+
20
+ this.createdAt = config.createdAt || new Date().toISOString();
21
+ this.status = config.status || 'ACTIVE';
22
+ this.featureSet = config.featureSet || 'ALL'; // ALL ou CONSOLIDATED_BILLING
23
+
24
+ // Contas da organização
25
+ this.accounts = new Map();
26
+ this.accountCount = 1; // Conta master
27
+
28
+ // OUs (Organizational Units)
29
+ this.ous = new Map();
30
+ this.ous.set(this.rootId, {
31
+ id: this.rootId,
32
+ name: 'Root',
33
+ arn: `arn:aws:organizations::123456789012:ou/o-${this.id}/${this.rootId}`,
34
+ parentId: null,
35
+ children: []
36
+ });
37
+
38
+ // Policies (SCPs, Tag Policies, etc)
39
+ this.policies = new Map();
40
+ this.policyTargets = new Map(); // Mapear policy -> targets
41
+
42
+ // Policy types habilitados na organização (mapa Type -> Status).
43
+ // SERVICE_CONTROL_POLICY (SCP) já vem habilitado ao criar a organização.
44
+ this.policyTypes = new Map();
45
+ const enabledTypes = Array.isArray(config.policyTypes) && config.policyTypes.length > 0
46
+ ? config.policyTypes
47
+ : ['SERVICE_CONTROL_POLICY'];
48
+ for (const entry of enabledTypes) {
49
+ const type = typeof entry === 'string' ? entry : entry?.Type;
50
+ const status = typeof entry === 'string' ? 'ENABLED' : (entry?.Status || 'ENABLED');
51
+ if (type) this.policyTypes.set(type, status);
52
+ }
53
+
54
+ // Trusted access: serviços integrados habilitados (mapa Principal -> DateEnabled ISO).
55
+ this.serviceAccess = new Map();
56
+ const enabledServices = Array.isArray(config.serviceAccess) ? config.serviceAccess : [];
57
+ for (const entry of enabledServices) {
58
+ const principal = typeof entry === 'string' ? entry : entry?.ServicePrincipal;
59
+ const dateEnabled = typeof entry === 'string'
60
+ ? new Date().toISOString()
61
+ : (entry?.DateEnabled || new Date().toISOString());
62
+ if (principal) this.serviceAccess.set(principal, dateEnabled);
63
+ }
64
+
65
+ // Cria conta master
66
+ this.createMasterAccount();
67
+ }
68
+
69
+ /**
70
+ * Cria a conta master
71
+ */
72
+ createMasterAccount() {
73
+ const masterAccount = {
74
+ id: this.masterAccountId,
75
+ arn: this.masterAccountArn,
76
+ email: this.masterAccountEmail,
77
+ name: this.masterAccountName,
78
+ status: 'ACTIVE',
79
+ type: 'MASTER',
80
+ parentId: this.rootId,
81
+ createdAt: this.createdAt,
82
+ joinedMethod: 'CREATED'
83
+ };
84
+
85
+ this.accounts.set(this.masterAccountId, masterAccount);
86
+ }
87
+
88
+ /**
89
+ * Cria uma nova OU
90
+ */
91
+ createOrganizationalUnit(parentId, name, config = {}) {
92
+ if (!this.ous.has(parentId)) {
93
+ throw new Error(`Parent OU '${parentId}' not found`);
94
+ }
95
+
96
+ const ouId = `ou-${uuidv4().substring(0, 8)}`;
97
+ const ou = {
98
+ id: ouId,
99
+ name: name,
100
+ arn: `arn:aws:organizations::123456789012:ou/o-${this.id}/${ouId}`,
101
+ parentId: parentId,
102
+ children: [],
103
+ createdAt: new Date().toISOString()
104
+ };
105
+
106
+ this.ous.set(ouId, ou);
107
+
108
+ const parent = this.ous.get(parentId);
109
+ if (parent) parent.children.push(ouId);
110
+
111
+ return ou;
112
+ }
113
+
114
+ /**
115
+ * Obtém OU por ID
116
+ */
117
+ getOrganizationalUnit(ouId) {
118
+ return this.ous.get(ouId);
119
+ }
120
+
121
+ /**
122
+ * Lista OUs filhas de um parent
123
+ */
124
+ listOrganizationalUnitsForParent(parentId) {
125
+ return Array.from(this.ous.values()).filter(ou => ou.parentId === parentId);
126
+ }
127
+
128
+ /**
129
+ * Cria uma nova conta
130
+ */
131
+ createAccount(email, accountName, config = {}) {
132
+ for (const acc of this.accounts.values()) {
133
+ if (acc.email === email) {
134
+ throw new Error(`Account with email '${email}' already exists`);
135
+ }
136
+ }
137
+
138
+ const accountId = config.accountId || Math.floor(100000000000 + Math.random() * 899999999999).toString();
139
+ const parentOUId = config.parentOUId || this.rootId;
140
+
141
+ if (!this.ous.has(parentOUId)) {
142
+ throw new Error(`Parent OU '${parentOUId}' not found`);
143
+ }
144
+
145
+ const account = {
146
+ id: accountId,
147
+ arn: `arn:aws:organizations::123456789012:account/o-${this.id}/${accountId}`,
148
+ email: email,
149
+ name: accountName,
150
+ status: config.status || 'ACTIVE',
151
+ type: 'MEMBER',
152
+ parentId: parentOUId,
153
+ createdAt: new Date().toISOString(),
154
+ joinedMethod: config.joinedMethod || 'CREATED',
155
+ roleName: config.roleName || 'OrganizationAccountAccessRole',
156
+ tags: config.tags || []
157
+ };
158
+
159
+ this.accounts.set(accountId, account);
160
+ this.accountCount++;
161
+
162
+ const parent = this.ous.get(parentOUId);
163
+ if (parent && !parent.children.includes(accountId)) parent.children.push(accountId);
164
+
165
+ return account;
166
+ }
167
+
168
+ /**
169
+ * Move uma conta para outra OU
170
+ */
171
+ moveAccount(accountId, newParentId) {
172
+ const account = this.accounts.get(accountId);
173
+ if (!account) {
174
+ throw new Error(`Account '${accountId}' not found`);
175
+ }
176
+
177
+ const newParent = this.ous.get(newParentId);
178
+ if (!newParent) {
179
+ throw new Error(`Target OU '${newParentId}' not found`);
180
+ }
181
+
182
+ const oldParent = this.ous.get(account.parentId);
183
+ if (oldParent) {
184
+ oldParent.children = oldParent.children.filter(id => id !== accountId);
185
+ }
186
+
187
+ account.parentId = newParentId;
188
+ if (!newParent.children.includes(accountId)) newParent.children.push(accountId);
189
+
190
+ return account;
191
+ }
192
+
193
+ /**
194
+ * Remove uma conta
195
+ */
196
+ removeAccount(accountId) {
197
+ const account = this.accounts.get(accountId);
198
+ if (!account) {
199
+ throw new Error(`Account '${accountId}' not found`);
200
+ }
201
+ if (account.type === 'MASTER') {
202
+ throw new Error('The management account cannot be removed from the organization');
203
+ }
204
+
205
+ const parent = this.ous.get(account.parentId);
206
+ if (parent) parent.children = parent.children.filter(id => id !== accountId);
207
+
208
+ this.accounts.delete(accountId);
209
+ this.accountCount--;
210
+
211
+ return account;
212
+ }
213
+
214
+ /**
215
+ * Obtém conta por ID
216
+ */
217
+ getAccount(accountId) {
218
+ return this.accounts.get(accountId);
219
+ }
220
+
221
+ /**
222
+ * Lista todas as contas
223
+ */
224
+ listAccounts() {
225
+ return Array.from(this.accounts.values());
226
+ }
227
+
228
+ /**
229
+ * Lista contas filhas de um parent
230
+ */
231
+ listAccountsForParent(parentId) {
232
+ return Array.from(this.accounts.values()).filter(acc => acc.parentId === parentId);
233
+ }
234
+
235
+ /**
236
+ * Remove uma OU
237
+ */
238
+ deleteOrganizationalUnit(ouId) {
239
+ const ou = this.ous.get(ouId);
240
+ if (!ou) {
241
+ throw new Error(`OU '${ouId}' not found`);
242
+ }
243
+ if (ou.parentId === null) {
244
+ throw new Error('The root cannot be deleted');
245
+ }
246
+
247
+ const childOus = this.listOrganizationalUnitsForParent(ouId);
248
+ if (childOus.length > 0) {
249
+ throw new Error(`OU '${ouId}' has child OUs. Remove them first.`);
250
+ }
251
+
252
+ const accountsInOu = this.listAccountsForParent(ouId);
253
+ if (accountsInOu.length > 0) {
254
+ throw new Error(`OU '${ouId}' has accounts. Move them first.`);
255
+ }
256
+
257
+ this.ous.delete(ouId);
258
+
259
+ if (ou.parentId) {
260
+ const parent = this.ous.get(ou.parentId);
261
+ if (parent) parent.children = parent.children.filter(id => id !== ouId);
262
+ }
263
+
264
+ return true;
265
+ }
266
+
267
+ /**
268
+ * Cria uma SCP (Service Control Policy)
269
+ */
270
+ createPolicy(name, document, type = 'SERVICE_CONTROL_POLICY', config = {}) {
271
+ const policyId = `p-${uuidv4().substring(0, 8)}`;
272
+
273
+ // Usa o modelo SCP para ter evaluateAction/matchesPattern disponíveis.
274
+ const policy = new ServiceControlPolicy(name, document, config);
275
+ policy.id = policyId;
276
+ policy.arn = `arn:aws:organizations::123456789012:policy/o-${this.id}/service_control_policy/${policyId}`;
277
+ policy.type = type;
278
+ policy.targets = [];
279
+
280
+ this.policies.set(policyId, policy);
281
+ this.policyTargets.set(policyId, []);
282
+
283
+ return policy;
284
+ }
285
+
286
+ /**
287
+ * Obtém policy por ID
288
+ */
289
+ getPolicy(policyId) {
290
+ return this.policies.get(policyId);
291
+ }
292
+
293
+ /**
294
+ * Lista policies
295
+ */
296
+ listPolicies(type) {
297
+ return Array.from(this.policies.values())
298
+ .filter(p => !type || p.type === type);
299
+ }
300
+
301
+ /**
302
+ * Lista os policy types habilitados na raiz (formato da API ListRoots).
303
+ */
304
+ listPolicyTypes() {
305
+ // A API ListRoots devolve apenas os tipos habilitados.
306
+ return Array.from(this.policyTypes.entries())
307
+ .filter(([, Status]) => Status === 'ENABLED')
308
+ .map(([Type, Status]) => ({ Type, Status }));
309
+ }
310
+
311
+ /**
312
+ * Verifica se um policy type está habilitado.
313
+ */
314
+ isPolicyTypeEnabled(type) {
315
+ return this.policyTypes.get(type) === 'ENABLED';
316
+ }
317
+
318
+ /**
319
+ * Habilita um policy type (EnablePolicyType).
320
+ */
321
+ enablePolicyType(type) {
322
+ if (!type) throw new Error('PolicyType is required');
323
+ this.policyTypes.set(type, 'ENABLED');
324
+ return { Type: type, Status: 'ENABLED' };
325
+ }
326
+
327
+ /**
328
+ * Desabilita um policy type (DisablePolicyType).
329
+ */
330
+ disablePolicyType(type) {
331
+ if (!type) throw new Error('PolicyType is required');
332
+ this.policyTypes.delete(type);
333
+ return { Type: type, Status: 'DISABLED' };
334
+ }
335
+
336
+ /**
337
+ * Lista os serviços com trusted access habilitado (EnableAWSServiceAccess).
338
+ */
339
+ listServiceAccess() {
340
+ return Array.from(this.serviceAccess.entries()).map(([ServicePrincipal, DateEnabled]) => ({
341
+ ServicePrincipal,
342
+ DateEnabled
343
+ }));
344
+ }
345
+
346
+ /**
347
+ * Verifica se um serviço está com trusted access habilitado.
348
+ */
349
+ isServiceAccessEnabled(principal) {
350
+ return this.serviceAccess.has(principal);
351
+ }
352
+
353
+ /**
354
+ * Habilita trusted access de um serviço (EnableAWSServiceAccess).
355
+ */
356
+ enableServiceAccess(principal) {
357
+ if (!principal) throw new Error('ServicePrincipal is required');
358
+ const dateEnabled = this.serviceAccess.get(principal) || new Date().toISOString();
359
+ this.serviceAccess.set(principal, dateEnabled);
360
+ return { ServicePrincipal: principal, DateEnabled: dateEnabled };
361
+ }
362
+
363
+ /**
364
+ * Desabilita trusted access de um serviço (DisableAWSServiceAccess).
365
+ */
366
+ disableServiceAccess(principal) {
367
+ if (!principal) throw new Error('ServicePrincipal is required');
368
+ this.serviceAccess.delete(principal);
369
+ return { ServicePrincipal: principal };
370
+ }
371
+
372
+ /**
373
+ * Anexa policy a um target (OU ou Account)
374
+ */
375
+ attachPolicy(policyId, targetId) {
376
+ const policy = this.policies.get(policyId);
377
+ if (!policy) {
378
+ throw new Error(`Policy '${policyId}' not found`);
379
+ }
380
+
381
+ const ou = this.ous.get(targetId);
382
+ const account = this.accounts.get(targetId);
383
+
384
+ if (!ou && !account) {
385
+ throw new Error(`Target '${targetId}' not found`);
386
+ }
387
+
388
+ const targets = this.policyTargets.get(policyId) || [];
389
+ if (!targets.includes(targetId)) {
390
+ targets.push(targetId);
391
+ this.policyTargets.set(policyId, targets);
392
+ policy.targets = targets;
393
+ }
394
+
395
+ return policy;
396
+ }
397
+
398
+ /**
399
+ * Desanexa policy de um target
400
+ */
401
+ detachPolicy(policyId, targetId) {
402
+ const policy = this.policies.get(policyId);
403
+ if (!policy) {
404
+ throw new Error(`Policy '${policyId}' not found`);
405
+ }
406
+
407
+ const targets = this.policyTargets.get(policyId) || [];
408
+ const index = targets.indexOf(targetId);
409
+
410
+ if (index > -1) {
411
+ targets.splice(index, 1);
412
+ policy.targets = targets;
413
+ }
414
+
415
+ return policy;
416
+ }
417
+
418
+ /**
419
+ * Lista policies anexadas a um target
420
+ */
421
+ listPoliciesForTarget(targetId, type = 'SERVICE_CONTROL_POLICY') {
422
+ const policies = [];
423
+
424
+ for (const [policyId, policy] of this.policies) {
425
+ if (policy.type === type) {
426
+ const targets = this.policyTargets.get(policyId) || policy.targets || [];
427
+ if (targets.includes(targetId)) {
428
+ policies.push(policy);
429
+ }
430
+ }
431
+ }
432
+
433
+ return policies;
434
+ }
435
+
436
+ /**
437
+ * Descreve a organização
438
+ */
439
+ describe() {
440
+ return {
441
+ Organization: {
442
+ Id: this.id,
443
+ Arn: this.arn,
444
+ RootId: this.rootId,
445
+ MasterAccountId: this.masterAccountId,
446
+ MasterAccountArn: this.masterAccountArn,
447
+ MasterAccountEmail: this.masterAccountEmail,
448
+ CreatedAt: this.createdAt,
449
+ Status: this.status,
450
+ FeatureSet: this.featureSet
451
+ }
452
+ };
453
+ }
454
+
455
+ /**
456
+ * Snapshot serializável para persistência (mesmo padrão JSON do IAM).
457
+ */
458
+ toSnapshot() {
459
+ return {
460
+ id: this.id,
461
+ arn: this.arn,
462
+ rootId: this.rootId,
463
+ masterAccountId: this.masterAccountId,
464
+ masterAccountArn: this.masterAccountArn,
465
+ masterAccountEmail: this.masterAccountEmail,
466
+ masterAccountName: this.masterAccountName,
467
+ createdAt: this.createdAt,
468
+ status: this.status,
469
+ featureSet: this.featureSet,
470
+ accounts: Array.from(this.accounts.values()),
471
+ ous: Array.from(this.ous.values()),
472
+ policies: Array.from(this.policies.values()),
473
+ policyTypes: this.listPolicyTypes(),
474
+ serviceAccess: this.listServiceAccess()
475
+ };
476
+ }
477
+
478
+ /**
479
+ * Reconstrói a organização a partir de um snapshot persistido.
480
+ */
481
+ static fromSnapshot(snapshot) {
482
+ if (!snapshot || !snapshot.id) return new Organization();
483
+
484
+ const organization = new Organization({
485
+ id: snapshot.id,
486
+ arn: snapshot.arn,
487
+ rootId: snapshot.rootId,
488
+ masterAccountId: snapshot.masterAccountId,
489
+ masterAccountArn: snapshot.masterAccountArn,
490
+ masterAccountEmail: snapshot.masterAccountEmail,
491
+ masterAccountName: snapshot.masterAccountName,
492
+ createdAt: snapshot.createdAt,
493
+ status: snapshot.status,
494
+ featureSet: snapshot.featureSet,
495
+ policyTypes: snapshot.policyTypes,
496
+ serviceAccess: snapshot.serviceAccess
497
+ });
498
+
499
+ if (Array.isArray(snapshot.ous) && snapshot.ous.length > 0) {
500
+ organization.ous = new Map(snapshot.ous.map(ou => [ou.id, ou]));
501
+ }
502
+ if (Array.isArray(snapshot.accounts) && snapshot.accounts.length > 0) {
503
+ organization.accounts = new Map(snapshot.accounts.map(a => [a.id, a]));
504
+ }
505
+ if (Array.isArray(snapshot.policies)) {
506
+ organization.policies = new Map();
507
+ organization.policyTargets = new Map();
508
+ for (const stored of snapshot.policies) {
509
+ // Reidrata como instância de SCP para preservar evaluateAction().
510
+ const policy = new ServiceControlPolicy(stored.name, stored.document, {
511
+ awsManaged: stored.awsManaged
512
+ });
513
+ policy.id = stored.id;
514
+ policy.arn = stored.arn;
515
+ policy.type = stored.type || 'SERVICE_CONTROL_POLICY';
516
+ policy.status = stored.status || 'ACTIVE';
517
+ policy.createdAt = stored.createdAt || policy.createdAt;
518
+ policy.lastModifiedAt = stored.lastModifiedAt || policy.createdAt;
519
+ policy.targets = stored.targets || [];
520
+
521
+ organization.policies.set(policy.id, policy);
522
+ organization.policyTargets.set(policy.id, policy.targets);
523
+ }
524
+ }
525
+ organization.accountCount = organization.accounts.size;
526
+
527
+ // Reparações defensivas: garante root existente e sem duplicidade nos children.
528
+ if (!organization.ous.has(organization.rootId)) {
529
+ organization.ous.set(organization.rootId, {
530
+ id: organization.rootId,
531
+ name: 'Root',
532
+ arn: `arn:aws:organizations::123456789012:ou/o-${organization.id}/${organization.rootId}`,
533
+ parentId: null,
534
+ children: []
535
+ });
536
+ }
537
+ for (const ou of organization.ous.values()) {
538
+ const childOus = organization.listOrganizationalUnitsForParent(ou.id).map(o => o.id);
539
+ const childAccounts = organization.listAccountsForParent(ou.id).map(a => a.id);
540
+ ou.children = [...childOus, ...childAccounts];
541
+ }
542
+
543
+ return organization;
544
+ }
545
+
546
+ toJSON() {
547
+ return {
548
+ id: this.id,
549
+ arn: this.arn,
550
+ rootId: this.rootId,
551
+ masterAccountId: this.masterAccountId,
552
+ masterAccountEmail: this.masterAccountEmail,
553
+ masterAccountName: this.masterAccountName,
554
+ createdAt: this.createdAt,
555
+ status: this.status,
556
+ featureSet: this.featureSet,
557
+ accountCount: this.accountCount,
558
+ ouCount: this.ous.size,
559
+ policyCount: this.policies.size,
560
+ policyTypes: this.listPolicyTypes(),
561
+ serviceAccess: this.listServiceAccess()
562
+ };
563
+ }
564
+ }
565
+
566
+ module.exports = Organization;