@gugananuvem/aws-local-simulator 1.0.34 → 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 (150) hide show
  1. package/README.md +662 -658
  2. package/package.json +11 -4
  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/handler-loader.js +50 -5
  100. package/src/services/lambda/index.js +26 -0
  101. package/src/services/lambda/server.js +18 -12
  102. package/src/services/lambda/simulator.js +73 -17
  103. package/src/services/opensearch/index.js +105 -0
  104. package/src/services/opensearch/models/document.js +115 -0
  105. package/src/services/opensearch/models/index.js +73 -0
  106. package/src/services/opensearch/server.js +310 -0
  107. package/src/services/opensearch/simulator.js +605 -0
  108. package/src/services/opensearch/store.js +162 -0
  109. package/src/services/organizations/control-tower-simulator.js +321 -0
  110. package/src/services/organizations/control-tower-store.js +182 -0
  111. package/src/services/organizations/iam-integration.js +413 -0
  112. package/src/services/organizations/index.js +172 -0
  113. package/src/services/organizations/models/index.js +13 -0
  114. package/src/services/organizations/models/invitation.js +48 -0
  115. package/src/services/organizations/models/organization.js +566 -0
  116. package/src/services/organizations/models/role.js +244 -0
  117. package/src/services/organizations/models/scp.js +131 -0
  118. package/src/services/organizations/server.js +736 -0
  119. package/src/services/organizations/simulator.js +593 -0
  120. package/src/services/organizations/store.js +235 -0
  121. package/src/services/organizations/vpc-integration.js +453 -0
  122. package/src/services/route53/index.js +67 -0
  123. package/src/services/route53/models/healthCheck.js +101 -0
  124. package/src/services/route53/models/hostedZone.js +37 -0
  125. package/src/services/route53/models/resourceRecord.js +106 -0
  126. package/src/services/route53/server.js +841 -0
  127. package/src/services/route53/simulator.js +350 -0
  128. package/src/services/s3/index.js +62 -0
  129. package/src/services/s3/presigned-url-generator.js +337 -0
  130. package/src/services/s3/server.js +231 -0
  131. package/src/services/s3/simulator.js +551 -4
  132. package/src/services/sns/index.js +31 -0
  133. package/src/services/sns/server.js +9 -0
  134. package/src/services/sqs/index.js +38 -0
  135. package/src/services/sqs/server.js +57 -4
  136. package/src/services/sqs/simulator.js +423 -33
  137. package/src/services/sts/simulator.js +209 -17
  138. package/src/services/vpc/README.md +459 -0
  139. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  140. package/src/services/vpc/index.js +90 -0
  141. package/src/services/vpc/models/internetGateway.js +71 -0
  142. package/src/services/vpc/models/routeTable.js +160 -0
  143. package/src/services/vpc/models/subnet.js +48 -0
  144. package/src/services/vpc/models/vpc.js +37 -0
  145. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  146. package/src/services/vpc/models/vpcGateway.js +134 -0
  147. package/src/services/vpc/server.js +836 -0
  148. package/src/services/vpc/simulator.js +613 -0
  149. package/src/utils/policy-cache.js +215 -0
  150. package/aws-config +0 -154
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Policy Enforcement Middleware
3
+ *
4
+ * Aplica validação de SCP + IAM policies em requisições
5
+ * Ordem real da AWS:
6
+ * 1. Extract principal
7
+ * 2. Get all applicable policies (SCP + IAM)
8
+ * 3. Evaluate (Deny > Allow > Implicit Deny)
9
+ * 4. Check conditions
10
+ * 5. Allow/Deny ação
11
+ */
12
+
13
+ const logger = require('../utils/logger');
14
+ const PolicyCache = require('../utils/policy-cache');
15
+
16
+ class PolicyEnforcementMiddleware {
17
+ constructor(organizationsService, iamService) {
18
+ this.organizationsService = organizationsService;
19
+ this.iamService = iamService;
20
+ this.cache = new PolicyCache();
21
+ }
22
+
23
+ /**
24
+ * Middleware Express para enforçar políticas
25
+ */
26
+ middleware(service = 'unknown', allowByDefault = true) {
27
+ return async (req, res, next) => {
28
+ try {
29
+ const enforced = req.app.locals.organizationsPolicyEnforcement?.enabled;
30
+
31
+ if (!enforced) {
32
+ // Se enforcement desativado, permite tudo
33
+ req.policyDecision = { allowed: true, reason: 'enforcement_disabled' };
34
+ return next();
35
+ }
36
+
37
+ // Se não há principal, negar (já foi validado em authorization.js)
38
+ if (!req.principal && req.app.locals.organizationsPolicyEnforcement?.requireAccountContext) {
39
+ return this.accessDenied(res, {
40
+ principal: 'unknown',
41
+ action: `${service}:*`,
42
+ resource: '*',
43
+ reason: 'no_principal',
44
+ message: 'Missing principal context'
45
+ });
46
+ }
47
+
48
+ if (!req.principal) {
49
+ // Sem principal mas enforcement desativado por account context
50
+ req.policyDecision = { allowed: true, reason: 'no_principal_enforcement_disabled' };
51
+ return next();
52
+ }
53
+
54
+ // Extrair ação e recurso da requisição
55
+ const { action, resource } = this.extractActionAndResource(req, service);
56
+
57
+ // Verificar cache
58
+ const cacheKey = this.generateCacheKey(req.principal, action, resource);
59
+ const cached = this.cache.get(cacheKey);
60
+
61
+ if (cached) {
62
+ req.policyDecision = cached;
63
+ if (!cached.allowed) {
64
+ return this.accessDenied(res, cached);
65
+ }
66
+ return next();
67
+ }
68
+
69
+ // Avaliar políticas
70
+ const decision = await this.evaluatePolicies(
71
+ req.principal,
72
+ action,
73
+ resource,
74
+ service,
75
+ req
76
+ );
77
+
78
+ // Cache resultado
79
+ this.cache.set(cacheKey, decision);
80
+ req.policyDecision = decision;
81
+
82
+ if (!decision.allowed) {
83
+ return this.accessDenied(res, decision);
84
+ }
85
+
86
+ // Log de acesso permitido
87
+ if (req.app.locals.organizationsPolicyEnforcement?.scpEvaluation?.logAllowances) {
88
+ this.logAllowance(req.principal, action, resource, decision);
89
+ }
90
+
91
+ next();
92
+ } catch (error) {
93
+ logger.error('Policy enforcement error:', error.message);
94
+ res.status(500).json({
95
+ code: 'InternalError',
96
+ message: 'Internal error during policy evaluation',
97
+ requestId: req.requestId || 'unknown'
98
+ });
99
+ }
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Avalia todas as políticas aplicáveis
105
+ * Ordem: SCP → IAM
106
+ */
107
+ async evaluatePolicies(principal, action, resource, service, req) {
108
+ try {
109
+ const accountId = principal.accountId;
110
+ const roleArn = principal.roleArn || principal.assumedRole;
111
+
112
+ // 1. Avaliar SCPs (Service Control Policies)
113
+ const scpDecision = await this.evaluateSCPs(accountId, action, resource, service);
114
+
115
+ // Explicit DENY em SCP → nega imediatamente
116
+ if (!scpDecision.allowed && scpDecision.reason === 'explicit_deny') {
117
+ return {
118
+ allowed: false,
119
+ principal: roleArn,
120
+ action,
121
+ resource,
122
+ account: accountId,
123
+ deniedBy: [{
124
+ type: 'SCP',
125
+ ...scpDecision.deniedByPolicy
126
+ }],
127
+ allowedBy: [],
128
+ evaluationDetails: scpDecision
129
+ };
130
+ }
131
+
132
+ // 2. Avaliar IAM policies
133
+ const iamDecision = await this.evaluateIAM(roleArn, action, resource);
134
+
135
+ // Explicit DENY em IAM → nega imediatamente
136
+ if (!iamDecision.allowed && iamDecision.reason === 'explicit_deny') {
137
+ return {
138
+ allowed: false,
139
+ principal: roleArn,
140
+ action,
141
+ resource,
142
+ account: accountId,
143
+ deniedBy: [{
144
+ type: 'IAM',
145
+ ...iamDecision.deniedByPolicy
146
+ }],
147
+ allowedBy: [],
148
+ evaluationDetails: iamDecision
149
+ };
150
+ }
151
+
152
+ // 3. SCP permite? IAM permite? → Allowed
153
+ if (scpDecision.allowed && iamDecision.allowed) {
154
+ return {
155
+ allowed: true,
156
+ principal: roleArn,
157
+ action,
158
+ resource,
159
+ account: accountId,
160
+ deniedBy: [],
161
+ allowedBy: [
162
+ { type: 'SCP', details: scpDecision },
163
+ { type: 'IAM', details: iamDecision }
164
+ ],
165
+ evaluationDetails: { scpDecision, iamDecision }
166
+ };
167
+ }
168
+
169
+ // 4. Se SCP nega (implicit ou explicit) → Nega
170
+ if (!scpDecision.allowed) {
171
+ return {
172
+ allowed: false,
173
+ principal: roleArn,
174
+ action,
175
+ resource,
176
+ account: accountId,
177
+ deniedBy: [{
178
+ type: 'SCP',
179
+ ...scpDecision.deniedByPolicy,
180
+ reason: scpDecision.reason
181
+ }],
182
+ allowedBy: iamDecision.allowed ? [{ type: 'IAM' }] : [],
183
+ evaluationDetails: scpDecision
184
+ };
185
+ }
186
+
187
+ // 5. Se IAM não permite (implicit deny) → Nega
188
+ if (!iamDecision.allowed) {
189
+ return {
190
+ allowed: false,
191
+ principal: roleArn,
192
+ action,
193
+ resource,
194
+ account: accountId,
195
+ deniedBy: [{
196
+ type: 'IAM',
197
+ reason: 'implicit_deny',
198
+ message: 'No explicit allow in principal policy'
199
+ }],
200
+ allowedBy: scpDecision.allowed ? [{ type: 'SCP' }] : [],
201
+ evaluationDetails: iamDecision
202
+ };
203
+ }
204
+
205
+ return {
206
+ allowed: false,
207
+ principal: roleArn,
208
+ action,
209
+ resource,
210
+ account: accountId,
211
+ deniedBy: [{ type: 'Unknown', reason: 'implicit_deny' }],
212
+ allowedBy: []
213
+ };
214
+ } catch (error) {
215
+ logger.error('Policy evaluation error:', error.message);
216
+ throw error;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Avalia Service Control Policies
222
+ */
223
+ async evaluateSCPs(accountId, action, resource, service) {
224
+ try {
225
+ if (!this.organizationsService?.organizationsSimulator) {
226
+ return { allowed: true, reason: 'organizations_not_available' };
227
+ }
228
+
229
+ const result = this.organizationsService.organizationsSimulator.evaluateSCPs(
230
+ accountId,
231
+ action,
232
+ resource || `arn:aws:${service}:*:${accountId}:*`
233
+ );
234
+
235
+ return {
236
+ allowed: result.allowed,
237
+ reason: result.allowed ? 'explicit_allow' : 'implicit_deny',
238
+ evaluationResults: result.evaluationResults,
239
+ deniedByPolicy: result.denied ? {
240
+ policyId: result.denied.id,
241
+ name: result.denied.name,
242
+ effect: 'Deny'
243
+ } : null
244
+ };
245
+ } catch (error) {
246
+ logger.debug('SCP evaluation error:', error.message);
247
+ return { allowed: true, reason: 'evaluation_error' };
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Avalia IAM policies
253
+ */
254
+ async evaluateIAM(roleArn, action, resource) {
255
+ try {
256
+ if (!this.organizationsService?.organizationsSimulator?.iamIntegration) {
257
+ return { allowed: true, reason: 'iam_not_available' };
258
+ }
259
+
260
+ const result = this.organizationsService.organizationsSimulator.iamIntegration.evaluateAction(
261
+ roleArn,
262
+ action,
263
+ resource
264
+ );
265
+
266
+ return {
267
+ allowed: result.allowed,
268
+ reason: result.allowed ? 'explicit_allow' : 'implicit_deny',
269
+ evaluationResults: result.evaluationResults
270
+ };
271
+ } catch (error) {
272
+ logger.debug('IAM evaluation error:', error.message);
273
+ return { allowed: true, reason: 'evaluation_error' };
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Extrai action e resource da requisição
279
+ */
280
+ extractActionAndResource(req, service) {
281
+ // Action: service:operation (ex: s3:PutObject, sqs:SendMessage)
282
+ const action = this.mapHttpMethodToAction(req.method, req.path, service);
283
+
284
+ // Resource: construir ARN baseado no path/body
285
+ const resource = this.mapPathToResource(req, service);
286
+
287
+ return { action, resource };
288
+ }
289
+
290
+ /**
291
+ * Map HTTP method + path → AWS action
292
+ */
293
+ mapHttpMethodToAction(method, path, service) {
294
+ // Exemplos:
295
+ // POST /buckets → s3:CreateBucket
296
+ // PUT /object → s3:PutObject
297
+ // DELETE /queue → sqs:DeleteQueue
298
+
299
+ const pathParts = path.toLowerCase().split('/').filter(p => p);
300
+
301
+ const methodMap = {
302
+ POST: 'Create',
303
+ PUT: 'Put',
304
+ GET: 'Get',
305
+ DELETE: 'Delete',
306
+ PATCH: 'Update',
307
+ HEAD: 'Head'
308
+ };
309
+
310
+ const verb = methodMap[method] || 'Invoke';
311
+ const resource = pathParts[0] || 'Resource';
312
+ const resourceName = resource.charAt(0).toUpperCase() + resource.slice(1, -1); // Remove 's'
313
+
314
+ return `${service}:${verb}${resourceName}`;
315
+ }
316
+
317
+ /**
318
+ * Map path/params → AWS resource ARN
319
+ */
320
+ mapPathToResource(req, service) {
321
+ const account = req.accountId || '*';
322
+ const region = req.get('X-Amz-Target')?.split('.')[0] || '*';
323
+
324
+ switch (service) {
325
+ case 's3':
326
+ // arn:aws:s3:::bucket-name
327
+ return req.body?.Bucket
328
+ ? `arn:aws:s3:::${req.body.Bucket}`
329
+ : 'arn:aws:s3:::*';
330
+
331
+ case 'sqs':
332
+ // arn:aws:sqs:region:account:queue-name
333
+ return req.body?.QueueName
334
+ ? `arn:aws:sqs:${region}:${account}:${req.body.QueueName}`
335
+ : `arn:aws:sqs:${region}:${account}:*`;
336
+
337
+ case 'sns':
338
+ // arn:aws:sns:region:account:topic-name
339
+ return req.body?.Name
340
+ ? `arn:aws:sns:${region}:${account}:${req.body.Name}`
341
+ : `arn:aws:sns:${region}:${account}:*`;
342
+
343
+ case 'dynamodb':
344
+ // arn:aws:dynamodb:region:account:table/table-name
345
+ return req.body?.TableName
346
+ ? `arn:aws:dynamodb:${region}:${account}:table/${req.body.TableName}`
347
+ : `arn:aws:dynamodb:${region}:${account}:table/*`;
348
+
349
+ case 'kms':
350
+ // arn:aws:kms:region:account:key/key-id
351
+ return req.body?.KeyId
352
+ ? `arn:aws:kms:${region}:${account}:key/${req.body.KeyId}`
353
+ : `arn:aws:kms:${region}:${account}:key/*`;
354
+
355
+ case 'lambda':
356
+ // arn:aws:lambda:region:account:function:function-name
357
+ return req.body?.FunctionName
358
+ ? `arn:aws:lambda:${region}:${account}:function:${req.body.FunctionName}`
359
+ : `arn:aws:lambda:${region}:${account}:function:*`;
360
+
361
+ default:
362
+ return `arn:aws:${service}:${region}:${account}:*`;
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Gera cache key única
368
+ */
369
+ generateCacheKey(principal, action, resource) {
370
+ const principalId = principal.roleArn || principal.credentials?.accessKeyId || 'unknown';
371
+ return `${principalId}|${action}|${resource}`;
372
+ }
373
+
374
+ /**
375
+ * Retorna erro de acesso negado (formato AWS real)
376
+ */
377
+ accessDenied(res, details) {
378
+ const deniedBy = details.deniedBy?.[0] || details;
379
+
380
+ logger.warn(`Access Denied: ${details.principal} → ${details.action}`, deniedBy);
381
+
382
+ return res.status(403).json({
383
+ code: 'AccessDenied',
384
+ message: `User: ${details.principal} is not authorized to perform: ${details.action} because ${deniedBy.reason || 'implicit deny'}`,
385
+ requestId: details.requestId || 'unknown',
386
+ evaluationDetails: {
387
+ principal: details.principal,
388
+ action: details.action,
389
+ resource: details.resource,
390
+ account: details.account,
391
+ deniedBy: details.deniedBy || [deniedBy],
392
+ allowedBy: details.allowedBy || []
393
+ }
394
+ });
395
+ }
396
+
397
+ /**
398
+ * Log de acesso permitido (para auditoria)
399
+ */
400
+ logAllowance(principal, action, resource, decision) {
401
+ logger.info(`Access Allowed: ${principal.roleArn} → ${action} on ${resource}`, {
402
+ allowedBy: decision.allowedBy
403
+ });
404
+ }
405
+ }
406
+
407
+ module.exports = PolicyEnforcementMiddleware;
package/src/server.js CHANGED
@@ -29,6 +29,21 @@ const { SecretManagerService } = require("./services/secret-manager");
29
29
  const { ParameterStoreService } = require("./services/parameter-store");
30
30
  const { ConfigService } = require("./services/config");
31
31
  const { AthenaService } = require("./services/athena");
32
+ const { AuroraService } = require("./services/aurora");
33
+ const IAMService = require("./services/iam");
34
+ const VPCService = require("./services/vpc");
35
+ const EC2Service = require("./services/ec2");
36
+ const ALBService = require("./services/alb");
37
+ const ASGService = require("./services/asg");
38
+ const OpenSearchService = require("./services/opensearch");
39
+ const KinesisDataStreamsService = require("./services/kinesis-data-streams");
40
+ const KinesisFirehoseService = require("./services/kinesis-firehose");
41
+ const KinesisAnalyticsService = require("./services/kinesis-analytics");
42
+ const OrganizationsService = require("./services/organizations");
43
+ const CloudFrontService = require("./services/cloudfront");
44
+ const Route53Service = require("./services/route53");
45
+ const ACMService = require("./services/acm");
46
+ const { initializeCloudShell } = require("./services/cloudshell");
32
47
 
33
48
  class Server {
34
49
  constructor(config) {
@@ -98,25 +113,29 @@ class Server {
98
113
  other.depends.includes(def.name) &&
99
114
  this.servicesMap.has(other.name)
100
115
  );
116
+ const port = status.port || this.config.ports?.[def.name];
117
+ const endpoint = status.endpoint || (port ? `http://localhost:${port}` : null);
101
118
  return {
102
119
  name: def.name,
103
120
  running: true,
104
121
  enabled: true,
105
- port: status.port || this.config.ports?.[def.name],
106
- endpoint: status.endpoint || `http://localhost:${status.port || this.config.ports?.[def.name]}`,
122
+ port,
123
+ endpoint,
107
124
  dependencies: def.depends,
108
125
  category: def.category,
109
126
  canDisable,
110
127
  ...status,
111
128
  };
112
129
  }
130
+ const port = this.config.ports?.[def.name];
113
131
  return {
114
132
  name: def.name,
115
133
  running: false,
116
134
  enabled: false,
117
- port: this.config.ports?.[def.name],
118
- endpoint: `http://localhost:${this.config.ports?.[def.name]}`,
135
+ port,
136
+ endpoint: port ? `http://localhost:${port}` : null,
119
137
  dependencies: def.depends,
138
+ category: def.category,
120
139
  canDisable: true,
121
140
  };
122
141
  });
@@ -181,18 +200,42 @@ class Server {
181
200
  const port = this.config.adminPort || 9999;
182
201
  this.managementServer = this.managementApp.listen(port, () => {
183
202
  logger.info(`🔧 Management API rodando em http://localhost:${port}`);
203
+ this.cloudshell = initializeCloudShell(
204
+ this.managementApp,
205
+ this.managementServer,
206
+ () => this.getActiveServicesForCloudShell()
207
+ );
184
208
  resolve();
185
209
  });
186
210
  this.managementServer.on("error", reject);
187
211
  });
188
212
  }
189
213
 
214
+ getActiveServicesForCloudShell() {
215
+ const services = [];
216
+ try {
217
+ for (const [name, service] of this.servicesMap) {
218
+ try {
219
+ const status = typeof service.getStatus === 'function' ? service.getStatus() : {};
220
+ services.push({ name, port: status.port || this.config.ports?.[name] });
221
+ } catch (e) {
222
+ // Skip services that fail to report status
223
+ services.push({ name, port: this.config.ports?.[name] });
224
+ }
225
+ }
226
+ } catch (e) {
227
+ // Return empty if servicesMap iteration fails
228
+ }
229
+ return services;
230
+ }
231
+
190
232
  buildServiceRegistry() {
191
233
  return [
192
234
  //Armazenamento & BD
193
235
  { name: "dynamodb", class: DynamoDBService, depends: [], category: 'Armazenamento & Banco de Dados' },
194
236
  { name: "s3", class: S3Service, depends: [], category: 'Armazenamento & Banco de Dados' },
195
237
  { name: "athena", class: AthenaService, depends: [], category: 'Armazenamento & Banco de Dados' },
238
+ { name: "aurora", class: AuroraService, depends: ['kms', 'secret-manager'], category: 'Armazenamento & Banco de Dados' },
196
239
 
197
240
  //Computação
198
241
  { name: "lambda", class: LambdaService, depends: [], category: 'Computação' },
@@ -200,6 +243,7 @@ class Server {
200
243
 
201
244
  { name: "cognito", class: CognitoService, depends: ["lambda"], category: 'Segurança & Identidade' },
202
245
  { name: "sts", class: STSService, depends: [], category: 'Segurança & Identidade' },
246
+ { name: "iam", class: IAMService, depends: ["sts"], category: 'Segurança & Identidade' },
203
247
  { name: "kms", class: KMSService, depends: [], category: 'Segurança & Identidade' },
204
248
  { name: "secret-manager", class: SecretManagerService, depends: [], category: 'Segurança & Identidade' },
205
249
  { name: "parameter-store", class: ParameterStoreService, depends: [], category: 'Segurança & Identidade' },
@@ -210,7 +254,23 @@ class Server {
210
254
  { name: "eventbridge", class: EventBridgeService, depends: [], category: 'Mensageria & Integração' },
211
255
 
212
256
  //Networking
257
+ { name: "vpc", class: VPCService, depends: [], category: 'Networking' },
213
258
  { name: "apigateway", class: APIGatewayService, depends: ["lambda", "cognito"], category: 'Networking' },
259
+ { name: "route53", class: Route53Service, depends: [], category: 'Networking' },
260
+ { name: "acm", class: ACMService, depends: [], category: 'Networking' },
261
+
262
+ //Computação em Nuvem
263
+ { name: "ec2", class: EC2Service, depends: [], category: 'Computação em Nuvem' },
264
+ { name: "alb", class: ALBService, depends: [], category: 'Computação em Nuvem' },
265
+ { name: "asg", class: ASGService, depends: ["ec2", "alb"], category: 'Computação em Nuvem' },
266
+
267
+ //Busca e Análise
268
+ { name: "opensearch", class: OpenSearchService, depends: [], category: 'Busca e Análise' },
269
+
270
+ //Streaming & Analytics
271
+ { name: "kinesis-data-streams", class: KinesisDataStreamsService, depends: [], category: 'Streaming & Analytics' },
272
+ { name: "kinesis-firehose", class: KinesisFirehoseService, depends: [], category: 'Streaming & Analytics' },
273
+ { name: "kinesis-analytics", class: KinesisAnalyticsService, depends: [], category: 'Streaming & Analytics' },
214
274
 
215
275
  //Observabilidade & Conformidade
216
276
  { name: "cloudwatch", class: CloudWatchService, depends: [], category: 'Observabilidade & Conformidade' },
@@ -219,6 +279,12 @@ class Server {
219
279
  { name: "config", class: ConfigService, depends: [], category: 'Observabilidade & Conformidade' },
220
280
  { name: "cloudformation", class: CloudFormationService, depends: [], category: 'Observabilidade & Conformidade' },
221
281
 
282
+ //Governança & Conformidade
283
+ { name: "organizations", class: OrganizationsService, depends: ["iam"], category: 'Governança & Conformidade' },
284
+
285
+ //Content Delivery
286
+ { name: "cloudfront", class: CloudFrontService, depends: ["s3", "lambda"], category: 'Content Delivery' },
287
+
222
288
  ];
223
289
  }
224
290
 
@@ -270,6 +336,10 @@ class Server {
270
336
  logger.info("\n🛑 Parando AWS Local Simulator...\n");
271
337
 
272
338
  try {
339
+ if (this.cloudshell) {
340
+ this.cloudshell.shutdown();
341
+ }
342
+
273
343
  const stopPromises = [...this.services].reverse().map((service) => service.stop());
274
344
  await Promise.all(stopPromises);
275
345
 
@@ -0,0 +1,66 @@
1
+ /**
2
+ * ACM (AWS Certificate Manager) Service
3
+ */
4
+
5
+ const ACMSimulator = require('./simulator');
6
+ const ACMServer = require('./server');
7
+ const logger = require('../../utils/logger');
8
+
9
+ class ACMService {
10
+ constructor(config = {}) {
11
+ this.name = 'acm';
12
+ this.config = config;
13
+ this.simulator = null;
14
+ this.server = null;
15
+ }
16
+
17
+ async initialize() {
18
+ this.simulator = new ACMSimulator(this.config);
19
+ await this.simulator.initialize();
20
+ }
21
+
22
+ injectDependencies(server) {
23
+ // Para possíveis integrações futuras
24
+ }
25
+
26
+ async start() {
27
+ try {
28
+ const port = this.config.port || 4025;
29
+ this.server = new ACMServer(port, this.config);
30
+ await this.server.initialize(this.simulator);
31
+ await this.server.start();
32
+ return true;
33
+ } catch (error) {
34
+ logger.error(`ACM Service error: ${error.message}`);
35
+ throw error;
36
+ }
37
+ }
38
+
39
+ async stop() {
40
+ if (this.server) {
41
+ await this.server.stop();
42
+ }
43
+ if (this.simulator) {
44
+ this.simulator.stopRenewalCheckInterval();
45
+ }
46
+ }
47
+
48
+ getStatus() {
49
+ return {
50
+ name: 'acm',
51
+ port: this.config.port || 4025,
52
+ running: this.server ? true : false,
53
+ endpoint: `http://localhost:${this.config.port || 4025}`,
54
+ };
55
+ }
56
+
57
+ async reset() {
58
+ if (this.simulator) {
59
+ this.simulator.certificates.clear();
60
+ this.simulator.certificatesByDomain.clear();
61
+ this.simulator.persistData();
62
+ }
63
+ }
64
+ }
65
+
66
+ module.exports = ACMService;