@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,388 @@
1
+ /**
2
+ * Kinesis Data Firehose Simulator
3
+ * Simulador de Kinesis Data Firehose com suporte a delivery streams e buffering
4
+ */
5
+
6
+ const { DeliveryStream } = require('./models/delivery-stream');
7
+ const LocalStore = require('../../utils/local-store');
8
+ const logger = require('../../utils/logger');
9
+ const path = require('path');
10
+ const { CloudTrailAudit } = require('../../utils/cloudtrail-audit');
11
+ const { v4: uuidv4 } = require('uuid');
12
+
13
+ class KinesisFirehoseSimulator {
14
+ constructor(config) {
15
+ this.config = config;
16
+ const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR || config.dataDir || './.aws-local-simulator-data';
17
+ this.dataDir = path.join(dataDir, 'kinesis-firehose');
18
+
19
+ this.store = new LocalStore(this.dataDir);
20
+ this.deliveryStreams = new Map();
21
+ this.audit = new CloudTrailAudit('firehose.amazonaws.com');
22
+
23
+ // Mutexes por stream para operações thread-safe
24
+ this._streamLocks = new Map();
25
+ }
26
+
27
+ /**
28
+ * Executa operação com lock exclusivo por stream
29
+ */
30
+ _withStreamLock(streamName, fn) {
31
+ const prev = this._streamLocks.get(streamName) || Promise.resolve();
32
+ const next = prev.then(() => fn());
33
+ this._streamLocks.set(streamName, next.catch(() => {}));
34
+ return next;
35
+ }
36
+
37
+ async initialize() {
38
+ logger.debug('Inicializando Kinesis Data Firehose Simulator...');
39
+ this.loadDeliveryStreams();
40
+
41
+ // Cria delivery streams da configuração
42
+ if (this.config['kinesis-firehose']?.deliveryStreams) {
43
+ for (const streamConfig of this.config['kinesis-firehose'].deliveryStreams) {
44
+ if (!this.deliveryStreams.has(streamConfig.name)) {
45
+ this.createDeliveryStream(streamConfig.name, streamConfig);
46
+ }
47
+ }
48
+ }
49
+
50
+ logger.debug(`✅ Kinesis Data Firehose Simulator inicializado com ${this.deliveryStreams.size} delivery streams`);
51
+ }
52
+
53
+ /**
54
+ * Carrega delivery streams persistidos
55
+ */
56
+ loadDeliveryStreams() {
57
+ const metadata = this.store.read('__delivery_streams__');
58
+ if (!metadata) return;
59
+
60
+ for (const [streamName, streamData] of Object.entries(metadata)) {
61
+ const stream = new DeliveryStream(streamName, streamData);
62
+ this.deliveryStreams.set(streamName, stream);
63
+ }
64
+
65
+ logger.debug(`Carregados ${this.deliveryStreams.size} delivery streams`);
66
+ }
67
+
68
+ /**
69
+ * Persiste metadados de delivery streams
70
+ */
71
+ persistDeliveryStreams() {
72
+ const metadata = {};
73
+
74
+ for (const [streamName, stream] of this.deliveryStreams) {
75
+ metadata[streamName] = stream.toJSON();
76
+ }
77
+
78
+ this.store.write('__delivery_streams__', metadata);
79
+ }
80
+
81
+ /**
82
+ * Cria um novo delivery stream
83
+ */
84
+ createDeliveryStream(deliveryStreamName, config = {}) {
85
+ if (this.deliveryStreams.has(deliveryStreamName)) {
86
+ throw new Error(`DeliveryStream '${deliveryStreamName}' already exists`);
87
+ }
88
+
89
+ const stream = new DeliveryStream(deliveryStreamName, config);
90
+
91
+ // Adiciona destinos (S3, HTTP, Redshift)
92
+ if (config.s3DestinationConfiguration) {
93
+ stream.addDestination({
94
+ type: 's3',
95
+ destinationId: uuidv4(),
96
+ arn: config.s3DestinationConfiguration.RoleArn,
97
+ bucketArn: config.s3DestinationConfiguration.BucketARN,
98
+ prefix: config.s3DestinationConfiguration.Prefix || '',
99
+ errorOutputPrefix: config.s3DestinationConfiguration.ErrorOutputPrefix || '',
100
+ bufferingHints: {
101
+ sizeInMBs: config.s3DestinationConfiguration.BufferingHints?.SizeInMBs || 128,
102
+ intervalInSeconds: config.s3DestinationConfiguration.BufferingHints?.IntervalInSeconds || 60
103
+ },
104
+ compressionFormat: config.s3DestinationConfiguration.CompressionFormat || 'UNCOMPRESSED',
105
+ destinationStatus: 'ACTIVE'
106
+ });
107
+ }
108
+
109
+ if (config.httpEndpointDestinationConfiguration) {
110
+ stream.addDestination({
111
+ type: 'http',
112
+ destinationId: uuidv4(),
113
+ url: config.httpEndpointDestinationConfiguration.EndpointConfiguration?.Url,
114
+ name: config.httpEndpointDestinationConfiguration.EndpointConfiguration?.Name,
115
+ roleArn: config.httpEndpointDestinationConfiguration.RoleARN,
116
+ bufferingHints: {
117
+ sizeInMBs: config.httpEndpointDestinationConfiguration.BufferingHints?.SizeInMBs || 128,
118
+ intervalInSeconds: config.httpEndpointDestinationConfiguration.BufferingHints?.IntervalInSeconds || 60
119
+ },
120
+ processingConfiguration: config.httpEndpointDestinationConfiguration.ProcessingConfiguration || null,
121
+ destinationStatus: 'ACTIVE'
122
+ });
123
+ }
124
+
125
+ if (config.redshiftDestinationConfiguration) {
126
+ stream.addDestination({
127
+ type: 'redshift',
128
+ destinationId: uuidv4(),
129
+ clusterArn: config.redshiftDestinationConfiguration.ClusterARN,
130
+ roleArn: config.redshiftDestinationConfiguration.RoleARN,
131
+ username: config.redshiftDestinationConfiguration.Username,
132
+ databaseName: config.redshiftDestinationConfiguration.DatabaseName,
133
+ tableName: config.redshiftDestinationConfiguration.TableName,
134
+ s3DestinationDescription: config.redshiftDestinationConfiguration.S3DestinationConfiguration,
135
+ destinationStatus: 'ACTIVE'
136
+ });
137
+ }
138
+
139
+ this.deliveryStreams.set(deliveryStreamName, stream);
140
+ this.persistDeliveryStreams();
141
+
142
+ this.audit.record({
143
+ eventName: 'CreateDeliveryStream',
144
+ readOnly: false,
145
+ resources: [{
146
+ ARN: stream.deliveryStreamArn,
147
+ type: 'AWS::Firehose::DeliveryStream'
148
+ }],
149
+ requestParameters: { deliveryStreamName }
150
+ });
151
+
152
+ logger.info(`✅ Delivery Stream criado: ${deliveryStreamName}`);
153
+
154
+ return {
155
+ DeliveryStreamArn: stream.deliveryStreamArn
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Descreve um delivery stream
161
+ */
162
+ describeDeliveryStream(deliveryStreamName) {
163
+ const stream = this.deliveryStreams.get(deliveryStreamName);
164
+ if (!stream) {
165
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
166
+ }
167
+
168
+ return stream.describe();
169
+ }
170
+
171
+ /**
172
+ * Lista delivery streams
173
+ */
174
+ listDeliveryStreams(params = {}) {
175
+ const { Limit = 100, ExclusiveStartDeliveryStreamName } = params;
176
+ const streamNames = Array.from(this.deliveryStreams.keys());
177
+
178
+ let startIndex = 0;
179
+ if (ExclusiveStartDeliveryStreamName) {
180
+ const index = streamNames.indexOf(ExclusiveStartDeliveryStreamName);
181
+ if (index !== -1) startIndex = index + 1;
182
+ }
183
+
184
+ const result = streamNames.slice(startIndex, startIndex + Limit);
185
+
186
+ return {
187
+ DeliveryStreamNames: result,
188
+ HasMoreDeliveryStreams: result.length === Limit
189
+ };
190
+ }
191
+
192
+ /**
193
+ * Deleta um delivery stream
194
+ */
195
+ deleteDeliveryStream(deliveryStreamName) {
196
+ const stream = this.deliveryStreams.get(deliveryStreamName);
197
+ if (!stream) {
198
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
199
+ }
200
+
201
+ this.deliveryStreams.delete(deliveryStreamName);
202
+ this.persistDeliveryStreams();
203
+
204
+ this.audit.record({
205
+ eventName: 'DeleteDeliveryStream',
206
+ readOnly: false,
207
+ resources: [{
208
+ ARN: stream.deliveryStreamArn,
209
+ type: 'AWS::Firehose::DeliveryStream'
210
+ }],
211
+ requestParameters: { deliveryStreamName }
212
+ });
213
+
214
+ logger.info(`✅ Delivery Stream deletado: ${deliveryStreamName}`);
215
+
216
+ return {};
217
+ }
218
+
219
+ /**
220
+ * Coloca um record no delivery stream (PutRecord)
221
+ */
222
+ async putRecord(deliveryStreamName, record) {
223
+ const stream = this.deliveryStreams.get(deliveryStreamName);
224
+ if (!stream) {
225
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
226
+ }
227
+
228
+ return this._withStreamLock(deliveryStreamName, () => {
229
+ const addedRecord = stream.addRecordToBuffer(record.Data);
230
+
231
+ this.persistDeliveryStreams();
232
+
233
+ this.audit.record({
234
+ eventName: 'PutRecord',
235
+ readOnly: false,
236
+ isDataEvent: true,
237
+ resources: [{
238
+ ARN: stream.deliveryStreamArn,
239
+ type: 'AWS::Firehose::DeliveryStream'
240
+ }],
241
+ requestParameters: { deliveryStreamName }
242
+ });
243
+
244
+ return {
245
+ RecordId: addedRecord.recordId,
246
+ Encrypted: false
247
+ };
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Coloca múltiplos records no delivery stream (PutRecordBatch)
253
+ */
254
+ async putRecordBatch(deliveryStreamName, records) {
255
+ const stream = this.deliveryStreams.get(deliveryStreamName);
256
+ if (!stream) {
257
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
258
+ }
259
+
260
+ return this._withStreamLock(deliveryStreamName, () => {
261
+ const results = [];
262
+ let failedCount = 0;
263
+
264
+ for (const record of records) {
265
+ try {
266
+ const addedRecord = stream.addRecordToBuffer(record.Data);
267
+ results.push({
268
+ RecordId: addedRecord.recordId,
269
+ ErrorCode: null,
270
+ ErrorMessage: null
271
+ });
272
+ } catch (error) {
273
+ failedCount++;
274
+ results.push({
275
+ RecordId: null,
276
+ ErrorCode: 'InternalFailure',
277
+ ErrorMessage: error.message
278
+ });
279
+ }
280
+ }
281
+
282
+ this.persistDeliveryStreams();
283
+
284
+ this.audit.record({
285
+ eventName: 'PutRecordBatch',
286
+ readOnly: false,
287
+ isDataEvent: true,
288
+ resources: [{
289
+ ARN: stream.deliveryStreamArn,
290
+ type: 'AWS::Firehose::DeliveryStream'
291
+ }],
292
+ requestParameters: { deliveryStreamName, recordCount: records.length }
293
+ });
294
+
295
+ return {
296
+ FailedPutCount: failedCount,
297
+ Encrypted: false,
298
+ Records: results
299
+ };
300
+ });
301
+ }
302
+
303
+ /**
304
+ * Obtém informações de destino
305
+ */
306
+ describeDestination(deliveryStreamName, destinationId) {
307
+ const stream = this.deliveryStreams.get(deliveryStreamName);
308
+ if (!stream) {
309
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
310
+ }
311
+
312
+ const destination = stream.destinations.find(d => d.destinationId === destinationId);
313
+ if (!destination) {
314
+ throw new Error(`Destination '${destinationId}' not found`);
315
+ }
316
+
317
+ return {
318
+ DestinationDescription: destination
319
+ };
320
+ }
321
+
322
+ /**
323
+ * Atualiza delivery stream
324
+ */
325
+ updateDeliveryStream(deliveryStreamName, config) {
326
+ const stream = this.deliveryStreams.get(deliveryStreamName);
327
+ if (!stream) {
328
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
329
+ }
330
+
331
+ // Atualiza configurações
332
+ if (config.deliveryStreamStatus) {
333
+ stream.deliveryStreamStatus = config.deliveryStreamStatus;
334
+ }
335
+
336
+ if (config.s3DestinationUpdate) {
337
+ const dest = stream.destinations[0];
338
+ if (dest && dest.type === 's3') {
339
+ Object.assign(dest, {
340
+ bufferingHints: config.s3DestinationUpdate.BufferingHints || dest.bufferingHints,
341
+ prefix: config.s3DestinationUpdate.Prefix || dest.prefix
342
+ });
343
+ }
344
+ }
345
+
346
+ stream.lastUpdateTimestamp = new Date().toISOString();
347
+ this.persistDeliveryStreams();
348
+
349
+ logger.info(`✅ Delivery Stream atualizado: ${deliveryStreamName}`);
350
+
351
+ return {};
352
+ }
353
+
354
+ /**
355
+ * Obtém estatísticas do simulator
356
+ */
357
+ getStats() {
358
+ const stats = {
359
+ deliveryStreamsCount: this.deliveryStreams.size,
360
+ totalRecords: 0,
361
+ totalDeliveredRecords: 0,
362
+ totalFailedRecords: 0,
363
+ deliveryStreams: []
364
+ };
365
+
366
+ for (const [streamName, stream] of this.deliveryStreams) {
367
+ const streamStats = stream.toJSON();
368
+ stats.totalRecords += streamStats.recordCount;
369
+ stats.totalDeliveredRecords += streamStats.deliveredRecordCount;
370
+ stats.totalFailedRecords += streamStats.failedDeliveryRecordCount;
371
+ stats.deliveryStreams.push(streamStats);
372
+ }
373
+
374
+ return stats;
375
+ }
376
+
377
+ /**
378
+ * Reset - limpa todos os dados
379
+ */
380
+ async reset() {
381
+ this.deliveryStreams.clear();
382
+ this.store.write('__delivery_streams__', {});
383
+
384
+ logger.info('✅ Kinesis Data Firehose Simulator resetado');
385
+ }
386
+ }
387
+
388
+ module.exports = KinesisFirehoseSimulator;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Kinesis Data Firehose Store - Persistência de delivery streams em JSON
3
+ */
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const logger = require('../../utils/logger');
8
+
9
+ class KinesisFirehoseStore {
10
+ constructor(dataDir) {
11
+ this.dataDir = dataDir;
12
+ this.streamsDir = path.join(dataDir, 'kinesis-firehose');
13
+ this.metadataFile = path.join(this.streamsDir, '__delivery_streams__.json');
14
+
15
+ if (!fs.existsSync(this.streamsDir)) {
16
+ fs.mkdirSync(this.streamsDir, { recursive: true });
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Carrega metadados de todos os delivery streams
22
+ */
23
+ loadMetadata() {
24
+ if (fs.existsSync(this.metadataFile)) {
25
+ try {
26
+ const data = fs.readFileSync(this.metadataFile, 'utf8');
27
+ return JSON.parse(data);
28
+ } catch (error) {
29
+ logger.warn(`Erro ao ler ${this.metadataFile}: ${error.message}`);
30
+ return {};
31
+ }
32
+ }
33
+ return {};
34
+ }
35
+
36
+ /**
37
+ * Salva metadados de todos os delivery streams
38
+ */
39
+ saveMetadata(metadata) {
40
+ try {
41
+ fs.writeFileSync(this.metadataFile, JSON.stringify(metadata, null, 2), 'utf8');
42
+ logger.debug(`Metadados de delivery streams salvos em ${this.metadataFile}`);
43
+ } catch (error) {
44
+ logger.error(`Erro ao salvar ${this.metadataFile}: ${error.message}`);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Obtém caminho do arquivo de delivery stream
50
+ */
51
+ getDeliveryStreamPath(deliveryStreamName) {
52
+ return path.join(this.streamsDir, `${deliveryStreamName}.json`);
53
+ }
54
+
55
+ /**
56
+ * Carrega records de um delivery stream
57
+ */
58
+ loadDeliveryStreamRecords(deliveryStreamName) {
59
+ const streamPath = this.getDeliveryStreamPath(deliveryStreamName);
60
+
61
+ if (!fs.existsSync(streamPath)) {
62
+ return [];
63
+ }
64
+
65
+ try {
66
+ const data = fs.readFileSync(streamPath, 'utf8');
67
+ return JSON.parse(data);
68
+ } catch (error) {
69
+ logger.warn(`Erro ao ler delivery stream ${deliveryStreamName}: ${error.message}`);
70
+ return [];
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Salva records de um delivery stream
76
+ */
77
+ saveDeliveryStreamRecords(deliveryStreamName, records) {
78
+ try {
79
+ const streamPath = this.getDeliveryStreamPath(deliveryStreamName);
80
+ fs.writeFileSync(streamPath, JSON.stringify(records, null, 2), 'utf8');
81
+ logger.debug(`${records.length} records salvos no delivery stream ${deliveryStreamName}`);
82
+ } catch (error) {
83
+ logger.error(`Erro ao salvar delivery stream ${deliveryStreamName}: ${error.message}`);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Adiciona record ao delivery stream
89
+ */
90
+ addRecord(deliveryStreamName, record) {
91
+ const records = this.loadDeliveryStreamRecords(deliveryStreamName);
92
+ records.push(record);
93
+ this.saveDeliveryStreamRecords(deliveryStreamName, records);
94
+ }
95
+
96
+ /**
97
+ * Adiciona múltiplos records
98
+ */
99
+ addRecords(deliveryStreamName, newRecords) {
100
+ const records = this.loadDeliveryStreamRecords(deliveryStreamName);
101
+ records.push(...newRecords);
102
+ this.saveDeliveryStreamRecords(deliveryStreamName, records);
103
+ }
104
+
105
+ /**
106
+ * Remove record do delivery stream
107
+ */
108
+ removeRecord(deliveryStreamName, recordId) {
109
+ const records = this.loadDeliveryStreamRecords(deliveryStreamName);
110
+ const filtered = records.filter(r => r.recordId !== recordId);
111
+
112
+ if (filtered.length < records.length) {
113
+ this.saveDeliveryStreamRecords(deliveryStreamName, filtered);
114
+ return true;
115
+ }
116
+
117
+ return false;
118
+ }
119
+
120
+ /**
121
+ * Deleta delivery stream completamente
122
+ */
123
+ deleteDeliveryStream(deliveryStreamName) {
124
+ try {
125
+ const streamPath = this.getDeliveryStreamPath(deliveryStreamName);
126
+ if (fs.existsSync(streamPath)) {
127
+ fs.unlinkSync(streamPath);
128
+ logger.debug(`Delivery Stream ${deliveryStreamName} deletado`);
129
+ }
130
+ } catch (error) {
131
+ logger.error(`Erro ao deletar delivery stream ${deliveryStreamName}: ${error.message}`);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Calcula tamanho total do delivery stream em bytes
137
+ */
138
+ getDeliveryStreamSize(deliveryStreamName) {
139
+ const records = this.loadDeliveryStreamRecords(deliveryStreamName);
140
+ return records.reduce((sum, record) => sum + Buffer.byteLength(JSON.stringify(record.data)), 0);
141
+ }
142
+
143
+ /**
144
+ * Limpa todos os delivery streams
145
+ */
146
+ clear() {
147
+ try {
148
+ if (fs.existsSync(this.streamsDir)) {
149
+ const files = fs.readdirSync(this.streamsDir);
150
+ files.forEach(file => {
151
+ if (file.endsWith('.json')) {
152
+ fs.unlinkSync(path.join(this.streamsDir, file));
153
+ }
154
+ });
155
+ logger.debug('Todos os delivery streams foram limpos');
156
+ }
157
+ } catch (error) {
158
+ logger.error(`Erro ao limpar delivery streams: ${error.message}`);
159
+ }
160
+ }
161
+ }
162
+
163
+ module.exports = KinesisFirehoseStore;
@@ -1,10 +1,13 @@
1
+ const { setupServiceMiddleware } = require('../../middleware');
1
2
  'use strict';
2
3
 
3
4
  const express = require('express');
4
5
  const cors = require('cors');
5
6
 
6
7
  class KMSServer {
7
- constructor(simulator, logger, config) {
8
+ constructor(simulator, logger, config) {
9
+ this.organizationsService = null;
10
+ this.stsService = null;
8
11
  this.simulator = simulator;
9
12
  this.logger = logger;
10
13
  this.config = config;
@@ -39,6 +39,32 @@ class LambdaService {
39
39
 
40
40
  const cw = server.getService('cloudwatch');
41
41
  if (cw?.simulator) this.simulator.cloudwatchSimulator = cw.simulator;
42
+
43
+ // IAM enforcement middleware (only when IAM service is active)
44
+ const iamService = server.getService('iam');
45
+ if (iamService && iamService.getSimulator()) {
46
+ const { createIAMEnforcementMiddleware } = require('../iam/middleware');
47
+ const iamSimulator = iamService.getSimulator();
48
+ this.server.iamMiddleware = createIAMEnforcementMiddleware({
49
+ iamSimulator,
50
+ servicePrefix: 'lambda',
51
+ extractAction: (req) => {
52
+ if (req.path.includes('/invocations')) return 'InvokeFunction';
53
+ if (req.method === 'POST') return 'CreateFunction';
54
+ if (req.method === 'GET') return 'ListFunctions';
55
+ if (req.method === 'DELETE') return 'DeleteFunction';
56
+ if (req.method === 'PUT') return 'UpdateFunctionConfiguration';
57
+ return 'Unknown';
58
+ },
59
+ extractResource: (req) => {
60
+ const functionName = req.params?.functionName || req.path.split('/functions/')[1]?.split('/')[0] || 'unknown';
61
+ return `arn:aws:lambda:us-east-1:123456789012:function:${functionName}`;
62
+ },
63
+ formatError: (res, code, message) => {
64
+ res.status(403).json({ Message: message, Type: code });
65
+ }
66
+ });
67
+ }
42
68
  }
43
69
 
44
70
  async start() {
@@ -1,3 +1,4 @@
1
+ const { setupServiceMiddleware } = require('../../middleware');
1
2
  /**
2
3
  * Lambda Server - Servidor HTTP para Lambda (API Gateway style)
3
4
  */
@@ -8,7 +9,9 @@ const LambdaSimulator = require('./simulator');
8
9
  const logger = require('../../utils/logger');
9
10
 
10
11
  class LambdaServer {
11
- constructor(port, config) {
12
+ constructor(port, config) {
13
+ this.organizationsService = null;
14
+ this.stsService = null;
12
15
  this.port = port;
13
16
  this.config = config;
14
17
  this.app = express();
@@ -42,6 +45,14 @@ class LambdaServer {
42
45
  }
43
46
 
44
47
  setupRoutes() {
48
+ // IAM enforcement (conditionally applied)
49
+ this.app.use((req, res, next) => {
50
+ if (this.iamMiddleware && !req.path.startsWith('/__admin') && !req.path.startsWith('/health')) {
51
+ return this.iamMiddleware(req, res, next);
52
+ }
53
+ next();
54
+ });
55
+
45
56
  // Health check
46
57
  this.app.get('/health', (req, res) => {
47
58
  res.json({ status: 'healthy', lambdas: this.simulator.getLambdasCount() });
@@ -141,6 +141,10 @@ class LambdaSimulator {
141
141
  return { StatusCode: result.statusCode || 200, Payload: result };
142
142
  }
143
143
 
144
+ async invokeFunction(functionName, event) {
145
+ return this.invoke(functionName, event, "Event");
146
+ }
147
+
144
148
  async executeHandler(lambda, functionName, event) {
145
149
  const requestId = Math.random().toString(36).substring(2, 10) + Math.random().toString(36).substring(2, 10);
146
150
  const capturedLogs = [];