@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,413 @@
1
+ /**
2
+ * IAM Integration Module
3
+ * Integra Organizations/SCP com IAM para avaliação completa de permissões
4
+ */
5
+
6
+ const logger = require('../../utils/logger');
7
+
8
+ class IAMIntegration {
9
+ constructor(organizationsSimulator, iamService) {
10
+ this.organizationsSimulator = organizationsSimulator;
11
+ this.iamService = iamService;
12
+ }
13
+
14
+ /**
15
+ * Avalia ação completa: SCP + IAM Policy
16
+ * Retorna: allowed/denied com motivo detalhado
17
+ */
18
+ evaluateAction(principalArn, action, resource = '*', accountId = null, context = {}) {
19
+ logger.debug(`Evaluating action: ${action} on ${resource} by ${principalArn}`);
20
+
21
+ // 1. Extrai account ID do principal ARN
22
+ if (!accountId) {
23
+ const match = principalArn.match(/:(\d+):/);
24
+ accountId = match ? match[1] : null;
25
+ }
26
+
27
+ if (!accountId) {
28
+ return {
29
+ allowed: false,
30
+ deniedBy: ['UnknownAccount'],
31
+ reason: 'Could not determine account ID from principal ARN',
32
+ principal: principalArn,
33
+ action,
34
+ resource
35
+ };
36
+ }
37
+
38
+ // 2. Avalia SCPs primeiro (mais restritivas)
39
+ const scpEvaluation = this.organizationsSimulator.evaluateSCPs(accountId, action, resource);
40
+
41
+ if (!scpEvaluation.allowed) {
42
+ return {
43
+ allowed: false,
44
+ deniedBy: scpEvaluation.deniedBy || ['SCP'],
45
+ reason: 'Denied by Service Control Policy',
46
+ principal: principalArn,
47
+ action,
48
+ resource,
49
+ policy: 'SCP'
50
+ };
51
+ }
52
+
53
+ // 3. Se houver IAM service, avalia IAM policies
54
+ if (this.iamService) {
55
+ const iamEvaluation = this.evaluateIAMPolicies(principalArn, action, resource, context);
56
+
57
+ if (!iamEvaluation.allowed) {
58
+ return {
59
+ allowed: false,
60
+ deniedBy: iamEvaluation.deniedBy || ['IAM Policy'],
61
+ reason: iamEvaluation.reason || 'Denied by IAM policy',
62
+ principal: principalArn,
63
+ action,
64
+ resource,
65
+ policy: 'IAM'
66
+ };
67
+ }
68
+ }
69
+
70
+ // 4. Tudo permitido
71
+ return {
72
+ allowed: true,
73
+ principal: principalArn,
74
+ action,
75
+ resource,
76
+ evaluatedBy: ['SCP', 'IAM']
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Avalia IAM policies associadas ao principal
82
+ */
83
+ evaluateIAMPolicies(principalArn, action, resource, context = {}) {
84
+ try {
85
+ // Extrai o tipo de principal (user, role, assumedRole)
86
+ const principalType = this.extractPrincipalType(principalArn);
87
+
88
+ if (principalType === 'unknown') {
89
+ return {
90
+ allowed: false,
91
+ deniedBy: ['UnknownPrincipalType'],
92
+ reason: 'Unknown principal type'
93
+ };
94
+ }
95
+
96
+ // Obtém policies do principal
97
+ const policies = this.getPoliciesForPrincipal(principalArn, principalType);
98
+
99
+ if (policies.length === 0) {
100
+ return {
101
+ allowed: false,
102
+ deniedBy: ['NoPolicies'],
103
+ reason: 'No policies attached to principal'
104
+ };
105
+ }
106
+
107
+ // Avalia cada policy
108
+ for (const policy of policies) {
109
+ if (policy.Statement) {
110
+ for (const statement of policy.Statement) {
111
+ if (this.statementMatches(statement, action, resource, context)) {
112
+ if (statement.Effect === 'Deny') {
113
+ return {
114
+ allowed: false,
115
+ deniedBy: [policy.name || 'InlinePolicy'],
116
+ reason: 'Explicitly denied by policy'
117
+ };
118
+ }
119
+
120
+ if (statement.Effect === 'Allow') {
121
+ return {
122
+ allowed: true,
123
+ allowedBy: [policy.name || 'InlinePolicy']
124
+ };
125
+ }
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ // Nenhuma policy permitiu explicitamente
132
+ return {
133
+ allowed: false,
134
+ deniedBy: ['ImplicitDeny'],
135
+ reason: 'No policy explicitly allows this action'
136
+ };
137
+
138
+ } catch (error) {
139
+ logger.error(`Error evaluating IAM policies: ${error.message}`);
140
+ return {
141
+ allowed: false,
142
+ deniedBy: ['EvaluationError'],
143
+ reason: error.message
144
+ };
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Extrai tipo de principal
150
+ */
151
+ extractPrincipalType(principalArn) {
152
+ if (principalArn.includes(':iam::') && principalArn.includes(':user/')) {
153
+ return 'user';
154
+ }
155
+ if (principalArn.includes(':iam::') && principalArn.includes(':role/')) {
156
+ return 'role';
157
+ }
158
+ if (principalArn.includes(':sts::') && principalArn.includes(':assumed-role/')) {
159
+ return 'assumed-role';
160
+ }
161
+ if (principalArn === '*') {
162
+ return 'everyone';
163
+ }
164
+ return 'unknown';
165
+ }
166
+
167
+ /**
168
+ * Obtém policies para um principal
169
+ */
170
+ getPoliciesForPrincipal(principalArn, principalType) {
171
+ if (!this.iamService) {
172
+ return [];
173
+ }
174
+
175
+ try {
176
+ const policies = [];
177
+
178
+ if (principalType === 'user' || principalType === 'assumed-role') {
179
+ // Extrai nome do usuário do ARN
180
+ const userMatch = principalArn.match(/user\/(.+?)(?::.*)?$/);
181
+ if (userMatch && this.iamService.simulator) {
182
+ const userName = userMatch[1];
183
+ const user = this.iamService.simulator.users?.get(userName);
184
+
185
+ if (user && user.policies) {
186
+ policies.push(...Array.from(user.policies.values()));
187
+ }
188
+ }
189
+ }
190
+
191
+ if (principalType === 'role' || principalType === 'assumed-role') {
192
+ // Extrai nome da role
193
+ const roleMatch = principalArn.match(/role\/(.+?)(?::.*)?$/);
194
+ if (roleMatch) {
195
+ const roleName = roleMatch[1];
196
+
197
+ // Procura em Organizations roles
198
+ for (const [, role] of this.organizationsSimulator.roles) {
199
+ if (role.name === roleName) {
200
+ // Retorna inline policies
201
+ for (const [, policy] of role.inlinePolicies) {
202
+ policies.push(policy);
203
+ }
204
+ break;
205
+ }
206
+ }
207
+
208
+ // Se IAM service disponível, procura lá também
209
+ if (this.iamService && this.iamService.simulator) {
210
+ const role = this.iamService.simulator.roles?.get(roleName);
211
+ if (role) {
212
+ // Retorna policies inline
213
+ if (role.inline_policies) {
214
+ for (const [, policy] of role.inline_policies) {
215
+ policies.push(policy);
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ return policies;
224
+ } catch (error) {
225
+ logger.warn(`Could not get policies for ${principalArn}: ${error.message}`);
226
+ return [];
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Verifica se um statement match com a ação/recurso
232
+ */
233
+ statementMatches(statement, action, resource, context = {}) {
234
+ if (!statement.Action || !statement.Resource) {
235
+ return false;
236
+ }
237
+
238
+ // Verifica Action
239
+ const actionMatches = this.patternMatches(statement.Action, action);
240
+ if (!actionMatches) {
241
+ return false;
242
+ }
243
+
244
+ // Verifica Resource
245
+ const resourceMatches = this.patternMatches(statement.Resource, resource);
246
+ if (!resourceMatches) {
247
+ return false;
248
+ }
249
+
250
+ // Verifica Condition se houver
251
+ if (statement.Condition) {
252
+ return this.evaluateCondition(statement.Condition, context);
253
+ }
254
+
255
+ return true;
256
+ }
257
+
258
+ /**
259
+ * Verifica se padrão (com wildcards) match
260
+ */
261
+ patternMatches(pattern, value) {
262
+ const patterns = Array.isArray(pattern) ? pattern : [pattern];
263
+
264
+ for (const p of patterns) {
265
+ if (p === '*') {
266
+ return true;
267
+ }
268
+
269
+ const regex = new RegExp('^' + p.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
270
+ if (regex.test(value)) {
271
+ return true;
272
+ }
273
+ }
274
+
275
+ return false;
276
+ }
277
+
278
+ /**
279
+ * Avalia conditions (versão simplificada)
280
+ */
281
+ evaluateCondition(condition, context = {}) {
282
+ // Implementação simplificada
283
+ // Em produção seria mais complexo com vários operadores
284
+ if (!condition) {
285
+ return true;
286
+ }
287
+
288
+ try {
289
+ // Suporta StringEquals, StringLike, etc
290
+ for (const [operator, conditions] of Object.entries(condition)) {
291
+ for (const [key, values] of Object.entries(conditions)) {
292
+ const contextValue = context[key];
293
+
294
+ if (!contextValue) {
295
+ return false;
296
+ }
297
+
298
+ const condValues = Array.isArray(values) ? values : [values];
299
+
300
+ switch (operator) {
301
+ case 'StringEquals':
302
+ if (!condValues.includes(contextValue)) {
303
+ return false;
304
+ }
305
+ break;
306
+
307
+ case 'StringLike':
308
+ let matched = false;
309
+ for (const condValue of condValues) {
310
+ const regex = new RegExp('^' + condValue.replace(/\*/g, '.*') + '$');
311
+ if (regex.test(contextValue)) {
312
+ matched = true;
313
+ break;
314
+ }
315
+ }
316
+ if (!matched) {
317
+ return false;
318
+ }
319
+ break;
320
+
321
+ default:
322
+ // Operadores desconhecidos são ignorados
323
+ break;
324
+ }
325
+ }
326
+ }
327
+
328
+ return true;
329
+ } catch (error) {
330
+ logger.warn(`Error evaluating condition: ${error.message}`);
331
+ return false;
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Avalia AssumeRole - pode o principal assumir a role?
337
+ */
338
+ canAssumeRole(principalArn, roleArn) {
339
+ try {
340
+ // Procura role pelo ARN
341
+ let targetRole = null;
342
+
343
+ for (const [, role] of this.organizationsSimulator.roles) {
344
+ if (role.arn === roleArn) {
345
+ targetRole = role;
346
+ break;
347
+ }
348
+ }
349
+
350
+ if (!targetRole) {
351
+ return {
352
+ allowed: false,
353
+ reason: 'Role not found'
354
+ };
355
+ }
356
+
357
+ // Verifica trust policy
358
+ if (!targetRole.trustPolicy || !targetRole.trustPolicy.Statement) {
359
+ return {
360
+ allowed: false,
361
+ reason: 'No trust policy defined'
362
+ };
363
+ }
364
+
365
+ // Valida trust policy
366
+ for (const statement of targetRole.trustPolicy.Statement) {
367
+ if (statement.Effect === 'Allow') {
368
+ if (statement.Principal?.AWS) {
369
+ if (this.patternMatches(statement.Principal.AWS, principalArn)) {
370
+ return {
371
+ allowed: true,
372
+ reason: 'Allowed by trust policy'
373
+ };
374
+ }
375
+ }
376
+
377
+ if (statement.Principal === '*') {
378
+ return {
379
+ allowed: true,
380
+ reason: 'Allowed by wildcard principal'
381
+ };
382
+ }
383
+ }
384
+ }
385
+
386
+ return {
387
+ allowed: false,
388
+ reason: 'Not allowed by trust policy'
389
+ };
390
+
391
+ } catch (error) {
392
+ logger.error(`Error checking AssumeRole: ${error.message}`);
393
+ return {
394
+ allowed: false,
395
+ reason: error.message
396
+ };
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Gera audit log de avaliação
402
+ */
403
+ auditEvaluation(evaluation) {
404
+ const level = evaluation.allowed ? 'INFO' : 'WARN';
405
+ const message = evaluation.allowed
406
+ ? `Action allowed: ${evaluation.action} on ${evaluation.resource}`
407
+ : `Action denied: ${evaluation.action} on ${evaluation.resource} - ${evaluation.reason}`;
408
+
409
+ logger.log(level, `[Policy Evaluation] ${message} - Principal: ${evaluation.principal}`);
410
+ }
411
+ }
412
+
413
+ module.exports = IAMIntegration;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * AWS Organizations & Control Tower Service - Ponto de entrada
3
+ */
4
+
5
+ const OrganizationsServer = require('./server');
6
+ const OrganizationsSimulator = require('./simulator');
7
+ const ControlTowerSimulator = require('./control-tower-simulator');
8
+
9
+ class OrganizationsService {
10
+ constructor(config) {
11
+ this.config = config;
12
+ this.name = 'organizations';
13
+ this.port = config.ports.organizations || 4580;
14
+ this.server = null;
15
+ this.organizationsSimulator = null;
16
+ this.controlTowerSimulator = null;
17
+ this.isRunning = false;
18
+ }
19
+
20
+ async initialize() {
21
+ const logger = require('../../utils/logger');
22
+ logger.debug(`Inicializando Organizations Service na porta ${this.port}...`);
23
+
24
+ // Inicializa simuladores
25
+ this.organizationsSimulator = new OrganizationsSimulator(this.config);
26
+ await this.organizationsSimulator.initialize();
27
+
28
+ this.controlTowerSimulator = new ControlTowerSimulator(this.config, this.organizationsSimulator);
29
+ await this.controlTowerSimulator.initialize();
30
+
31
+ // Inicializa server
32
+ this.server = new OrganizationsServer(this.port, this.config);
33
+ this.server.organizationsSimulator = this.organizationsSimulator;
34
+ this.server.controlTowerSimulator = this.controlTowerSimulator;
35
+
36
+ await this.server.initialize();
37
+
38
+ logger.debug('Organizations Service inicializado');
39
+ }
40
+
41
+ injectDependencies(server) {
42
+ // Injetar dependências de outros serviços (IAM, VPC, EC2, etc)
43
+ }
44
+
45
+ async start() {
46
+ const logger = require('../../utils/logger');
47
+
48
+ if (this.isRunning) {
49
+ logger.warn('Organizations Service já está em execução');
50
+ return;
51
+ }
52
+
53
+ try {
54
+ await this.server.start();
55
+ this.isRunning = true;
56
+ logger.info('Organizations Service iniciado com sucesso');
57
+ } catch (error) {
58
+ logger.error(`Erro ao iniciar Organizations Service: ${error.message}`);
59
+ throw error;
60
+ }
61
+ }
62
+
63
+ async stop() {
64
+ const logger = require('../../utils/logger');
65
+
66
+ if (!this.isRunning) {
67
+ logger.warn('Organizations Service não está em execução');
68
+ return;
69
+ }
70
+
71
+ try {
72
+ await this.server.stop();
73
+ this.isRunning = false;
74
+ logger.info('Organizations Service parado com sucesso');
75
+ } catch (error) {
76
+ logger.error(`Erro ao parar Organizations Service: ${error.message}`);
77
+ throw error;
78
+ }
79
+ }
80
+
81
+ async reset() {
82
+ const logger = require('../../utils/logger');
83
+ try {
84
+ await this.organizationsSimulator.reset();
85
+ await this.controlTowerSimulator.reset();
86
+ logger.info('Organizations Service resetado');
87
+ } catch (error) {
88
+ logger.error(`Erro ao resetar Organizations Service: ${error.message}`);
89
+ throw error;
90
+ }
91
+ }
92
+
93
+ getStatus() {
94
+ const orgStats = this.organizationsSimulator?.getStats() || {};
95
+ const ctStats = this.controlTowerSimulator?.getStats() || {};
96
+
97
+ return {
98
+ name: this.name,
99
+ running: this.isRunning,
100
+ port: this.port,
101
+ endpoint: `http://localhost:${this.port}`,
102
+ resources: {
103
+ organizationId: orgStats.organizationId,
104
+ accounts: orgStats.accountCount,
105
+ ous: orgStats.ouCount,
106
+ policies: orgStats.policyCount,
107
+ roles: orgStats.roleCount,
108
+ landingZones: ctStats.landingZonesCount,
109
+ guardrails: ctStats.guardrailsCount,
110
+ preventiveGuardrails: ctStats.preventiveGuardrailsCount,
111
+ detectiveGuardrails: ctStats.detectiveGuardrailsCount,
112
+ enabledGuardrails: ctStats.enabledGuardrailsCount
113
+ }
114
+ };
115
+ }
116
+
117
+ // Métodos auxiliares para integração com outros serviços
118
+
119
+ /**
120
+ * Integração com IAM - Valida se um role pode assumir outro
121
+ */
122
+ validateAssumeRole(assumingRoleId, targetRoleId) {
123
+ const assumingRole = this.organizationsSimulator.roles.get(assumingRoleId);
124
+ const targetRole = this.organizationsSimulator.roles.get(targetRoleId);
125
+
126
+ if (!assumingRole || !targetRole) {
127
+ return false;
128
+ }
129
+
130
+ return targetRole.canAssumeRole(assumingRole.arn);
131
+ }
132
+
133
+ /**
134
+ * Integração com IAM - Avalia se ação é permitida
135
+ */
136
+ evaluateIAMAction(roleId, action, resource = '*') {
137
+ const role = this.organizationsSimulator.roles.get(roleId);
138
+ if (!role) {
139
+ return false;
140
+ }
141
+
142
+ return role.canPerformAction(action, resource);
143
+ }
144
+
145
+ /**
146
+ * Integração com VPC - Valida restrições regionais por SCP
147
+ */
148
+ validateRegionalRestrictions(accountId, region, service) {
149
+ const evaluation = this.organizationsSimulator.evaluateSCPs(
150
+ accountId,
151
+ `${service}:*`,
152
+ `arn:aws:${service}:${region}:*:*`
153
+ );
154
+
155
+ return evaluation.allowed;
156
+ }
157
+
158
+ /**
159
+ * Integração com EC2 - Valida se instância pode ser criada
160
+ */
161
+ validateEC2Instance(accountId, instanceType, region) {
162
+ // Verifica SCP
163
+ const scpEvaluation = this.evaluateSCPs(accountId, 'ec2:RunInstances');
164
+
165
+ // Verifica restrições regionais
166
+ const regionalCheck = this.validateRegionalRestrictions(accountId, region, 'ec2');
167
+
168
+ return scpEvaluation.allowed && regionalCheck;
169
+ }
170
+ }
171
+
172
+ module.exports = OrganizationsService;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * AWS Organizations Models
3
+ */
4
+
5
+ const Organization = require('./organization');
6
+ const ServiceControlPolicy = require('./scp');
7
+ const OrganizationRole = require('./role');
8
+
9
+ module.exports = {
10
+ Organization,
11
+ ServiceControlPolicy,
12
+ OrganizationRole
13
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * AWS Organizations - Invitation (Handshake) Model
3
+ * Representa um convite para uma conta entrar na organização.
4
+ */
5
+
6
+ const { v4: uuidv4 } = require('uuid');
7
+
8
+ const EXPIRATION_DAYS = 15;
9
+
10
+ class Invitation {
11
+ constructor(config = {}) {
12
+ this.id = config.id || `h-${uuidv4().substring(0, 8)}`;
13
+ this.organizationId = config.organizationId;
14
+ this.email = config.email;
15
+ this.accountId = config.accountId || null;
16
+ this.message = config.message || '';
17
+ this.state = config.state || 'REQUESTED'; // REQUESTED | ACCEPTED | DECLINED | CANCELED | EXPIRED
18
+ this.action = config.action || 'INVITE';
19
+ this.requestedTimestamp = config.requestedTimestamp || Math.floor(Date.now() / 1000);
20
+ this.expirationTimestamp =
21
+ config.expirationTimestamp || this.requestedTimestamp + EXPIRATION_DAYS * 24 * 60 * 60;
22
+ this.completedTimestamp = config.completedTimestamp || null;
23
+ }
24
+
25
+ /**
26
+ * Formato Handshake do AWS Organizations.
27
+ */
28
+ toHandshake() {
29
+ const parties = [
30
+ { Id: this.organizationId, Type: 'ORGANIZATION' },
31
+ { Id: this.email, Type: 'EMAIL' },
32
+ ];
33
+
34
+ return {
35
+ Id: this.id,
36
+ Arn: `arn:aws:organizations::123456789012:handshake/o-${this.organizationId}/invite/${this.id}`,
37
+ Parties: parties,
38
+ State: this.state,
39
+ Action: this.action,
40
+ RequestedTimestamp: this.requestedTimestamp,
41
+ ExpirationTimestamp: this.expirationTimestamp,
42
+ CompletedTimestamp: this.completedTimestamp,
43
+ Resources: this.accountId ? [{ Type: 'ACCOUNT', Value: this.accountId }] : [],
44
+ };
45
+ }
46
+ }
47
+
48
+ module.exports = Invitation;