@gugananuvem/aws-local-simulator 1.0.11 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +349 -72
- package/package.json +12 -2
- package/src/config/config-loader.js +2 -0
- package/src/config/default-config.js +3 -0
- package/src/index.js +18 -2
- package/src/server.js +37 -31
- package/src/services/apigateway/index.js +10 -3
- package/src/services/apigateway/server.js +73 -0
- package/src/services/apigateway/simulator.js +13 -3
- package/src/services/athena/index.js +75 -0
- package/src/services/athena/server.js +101 -0
- package/src/services/athena/simulador.js +998 -0
- package/src/services/athena/simulator.js +346 -0
- package/src/services/cloudformation/index.js +106 -0
- package/src/services/cloudformation/server.js +417 -0
- package/src/services/cloudformation/simulador.js +1045 -0
- package/src/services/cloudtrail/index.js +84 -0
- package/src/services/cloudtrail/server.js +235 -0
- package/src/services/cloudtrail/simulador.js +719 -0
- package/src/services/cloudwatch/index.js +84 -0
- package/src/services/cloudwatch/server.js +366 -0
- package/src/services/cloudwatch/simulador.js +1173 -0
- package/src/services/cognito/index.js +5 -0
- package/src/services/cognito/server.js +54 -3
- package/src/services/cognito/simulator.js +273 -2
- package/src/services/config/index.js +96 -0
- package/src/services/config/server.js +215 -0
- package/src/services/config/simulador.js +1260 -0
- package/src/services/dynamodb/index.js +7 -3
- package/src/services/dynamodb/server.js +4 -2
- package/src/services/dynamodb/simulator.js +39 -29
- package/src/services/eventbridge/index.js +55 -51
- package/src/services/eventbridge/server.js +209 -0
- package/src/services/eventbridge/simulator.js +684 -0
- package/src/services/index.js +30 -4
- package/src/services/kms/index.js +75 -0
- package/src/services/kms/server.js +67 -0
- package/src/services/kms/simulator.js +324 -0
- package/src/services/lambda/handler-loader.js +13 -2
- package/src/services/lambda/index.js +7 -1
- package/src/services/lambda/server.js +32 -39
- package/src/services/lambda/simulator.js +78 -181
- package/src/services/parameter-store/index.js +80 -0
- package/src/services/parameter-store/server.js +50 -0
- package/src/services/parameter-store/simulator.js +201 -0
- package/src/services/s3/index.js +7 -3
- package/src/services/s3/server.js +20 -13
- package/src/services/s3/simulator.js +163 -407
- package/src/services/secret-manager/index.js +80 -0
- package/src/services/secret-manager/server.js +50 -0
- package/src/services/secret-manager/simulator.js +171 -0
- package/src/services/sns/index.js +55 -42
- package/src/services/sns/server.js +580 -0
- package/src/services/sns/simulator.js +1482 -0
- package/src/services/sqs/index.js +2 -4
- package/src/services/sqs/server.js +92 -18
- package/src/services/sqs/simulator.js +79 -298
- package/src/services/sts/index.js +37 -0
- package/src/services/sts/server.js +142 -0
- package/src/services/sts/simulator.js +69 -0
- package/src/services/xray/index.js +83 -0
- package/src/services/xray/server.js +308 -0
- package/src/services/xray/simulador.js +994 -0
- package/src/utils/cloudtrail-audit.js +129 -0
- package/src/utils/local-store.js +18 -2
|
@@ -31,6 +31,11 @@ class CognitoService {
|
|
|
31
31
|
logger.debug('Cognito Service inicializado');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
injectDependencies(server) {
|
|
35
|
+
const ct = server.getService('cloudtrail');
|
|
36
|
+
if (ct?.simulator) this.simulator.audit.setTrail(ct.simulator);
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
async start() {
|
|
35
40
|
if (this.isRunning) return;
|
|
36
41
|
await this.server.start();
|
|
@@ -16,7 +16,19 @@ class CognitoServer {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
setupMiddlewares() {
|
|
19
|
-
this.app.use(express.
|
|
19
|
+
this.app.use(express.raw({ type: '*/*', limit: '10mb' }));
|
|
20
|
+
this.app.use((req, res, next) => {
|
|
21
|
+
if (req.body && Buffer.isBuffer(req.body)) {
|
|
22
|
+
try {
|
|
23
|
+
req.body = JSON.parse(req.body.toString('utf8'));
|
|
24
|
+
} catch (e) {
|
|
25
|
+
req.body = {};
|
|
26
|
+
}
|
|
27
|
+
} else if (!req.body) {
|
|
28
|
+
req.body = {};
|
|
29
|
+
}
|
|
30
|
+
next();
|
|
31
|
+
});
|
|
20
32
|
|
|
21
33
|
if (logger.currentLogLevel === 'verboso') {
|
|
22
34
|
this.app.use((req, res, next) => {
|
|
@@ -48,12 +60,13 @@ class CognitoServer {
|
|
|
48
60
|
// User Pool operations
|
|
49
61
|
this.app.post('/', async (req, res) => {
|
|
50
62
|
const target = req.headers['x-amz-target'];
|
|
63
|
+
logger.info(`Cognito incoming: target=${target} body=${JSON.stringify(req.body)}`);
|
|
51
64
|
if (!target) {
|
|
52
65
|
return res.status(400).json({ error: 'Missing X-Amz-Target header' });
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
try {
|
|
56
|
-
const result = await this.handleRequest(target, req.body);
|
|
69
|
+
const result = await this.handleRequest(target, req.body || {});
|
|
57
70
|
res.json(result);
|
|
58
71
|
} catch (error) {
|
|
59
72
|
logger.error('Cognito Error:', error);
|
|
@@ -84,20 +97,46 @@ class CognitoServer {
|
|
|
84
97
|
case 'DeleteUserPool':
|
|
85
98
|
return this.simulator.deleteUserPool(params);
|
|
86
99
|
|
|
100
|
+
case 'ListUsers':
|
|
101
|
+
return this.simulator.listUsers(params);
|
|
87
102
|
// User Pool Client Management
|
|
88
103
|
case 'CreateUserPoolClient':
|
|
89
104
|
return this.simulator.createUserPoolClient(params);
|
|
105
|
+
case 'ListUserPoolClients':
|
|
106
|
+
return this.simulator.listUserPoolClients(params);
|
|
107
|
+
case 'DescribeUserPoolClient':
|
|
108
|
+
return this.simulator.describeUserPoolClient(params);
|
|
109
|
+
case 'DeleteUserPoolClient':
|
|
110
|
+
return this.simulator.deleteUserPoolClient(params);
|
|
90
111
|
|
|
91
112
|
// User Operations
|
|
92
113
|
case 'SignUp':
|
|
93
114
|
return this.simulator.signUp(params);
|
|
94
115
|
case 'ConfirmSignUp':
|
|
95
116
|
return this.simulator.confirmSignUp(params);
|
|
117
|
+
case 'ForgotPassword':
|
|
118
|
+
return this.simulator.forgotPassword(params);
|
|
119
|
+
case 'ConfirmForgotPassword':
|
|
120
|
+
return this.simulator.confirmForgotPassword(params);
|
|
121
|
+
case 'ChangePassword':
|
|
122
|
+
return this.simulator.changePassword(params);
|
|
96
123
|
case 'InitiateAuth':
|
|
97
124
|
return this.simulator.initiateAuth(params);
|
|
125
|
+
case 'RespondToAuthChallenge':
|
|
126
|
+
return this.simulator.respondToAuthChallenge(params);
|
|
98
127
|
case 'GetToken':
|
|
99
128
|
return this.simulator.getToken(params);
|
|
100
|
-
|
|
129
|
+
case 'GlobalSignOut':
|
|
130
|
+
return this.simulator.globalSignOut(params);
|
|
131
|
+
case 'RevokeToken':
|
|
132
|
+
return this.simulator.revokeToken(params);
|
|
133
|
+
case 'GetUser':
|
|
134
|
+
return this.simulator.getUser(params);
|
|
135
|
+
case 'UpdateUserAttributes':
|
|
136
|
+
return this.simulator.updateUserAttributes(params);
|
|
137
|
+
case 'DeleteUser':
|
|
138
|
+
return this.simulator.deleteUser(params);
|
|
139
|
+
|
|
101
140
|
// Admin Operations
|
|
102
141
|
case 'AdminGetUser':
|
|
103
142
|
return this.simulator.adminGetUser(params);
|
|
@@ -107,6 +146,18 @@ class CognitoServer {
|
|
|
107
146
|
return this.simulator.adminSetUserPassword(params);
|
|
108
147
|
case 'AdminDeleteUser':
|
|
109
148
|
return this.simulator.adminDeleteUser(params);
|
|
149
|
+
case 'AdminDisableUser':
|
|
150
|
+
return this.simulator.adminDisableUser(params);
|
|
151
|
+
case 'AdminEnableUser':
|
|
152
|
+
return this.simulator.adminEnableUser(params);
|
|
153
|
+
case 'AdminResetUserPassword':
|
|
154
|
+
return this.simulator.adminResetUserPassword(params);
|
|
155
|
+
case 'AdminInitiateAuth':
|
|
156
|
+
return this.simulator.initiateAuth(params);
|
|
157
|
+
case 'AdminListGroupsForUser':
|
|
158
|
+
return this.simulator.adminListGroupsForUser(params);
|
|
159
|
+
case 'AdminUserGlobalSignOut':
|
|
160
|
+
return this.simulator.adminUserGlobalSignOut(params);
|
|
110
161
|
|
|
111
162
|
// Identity Pool Operations
|
|
112
163
|
case 'CreateIdentityPool':
|
|
@@ -9,6 +9,7 @@ const { v4: uuidv4 } = require('uuid');
|
|
|
9
9
|
const logger = require('../../utils/logger');
|
|
10
10
|
const LocalStore = require('../../utils/local-store');
|
|
11
11
|
const path = require('path');
|
|
12
|
+
const { CloudTrailAudit } = require('../../utils/cloudtrail-audit');
|
|
12
13
|
|
|
13
14
|
class CognitoSimulator {
|
|
14
15
|
constructor(config) {
|
|
@@ -22,6 +23,7 @@ class CognitoSimulator {
|
|
|
22
23
|
this.refreshTokens = new Map();
|
|
23
24
|
this.accessTokens = new Map();
|
|
24
25
|
this.jwtSecret = crypto.randomBytes(64).toString('hex');
|
|
26
|
+
this.audit = new CloudTrailAudit('cognito-idp.amazonaws.com');
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
async initialize() {
|
|
@@ -73,6 +75,7 @@ class CognitoSimulator {
|
|
|
73
75
|
this.persistUserPools();
|
|
74
76
|
|
|
75
77
|
logger.debug(`✅ User Pool criado: ${PoolName} (${poolId})`);
|
|
78
|
+
this.audit.record({ eventName: 'CreateUserPool', readOnly: false, resources: [{ ARN: userPool.Arn, type: 'AWS::Cognito::UserPool' }], requestParameters: { poolName: PoolName } });
|
|
76
79
|
|
|
77
80
|
return {
|
|
78
81
|
UserPool: {
|
|
@@ -140,6 +143,215 @@ class CognitoSimulator {
|
|
|
140
143
|
};
|
|
141
144
|
}
|
|
142
145
|
|
|
146
|
+
listUsers(params = {}) {
|
|
147
|
+
const { UserPoolId, Filter, Limit = 60, PaginationToken } = params;
|
|
148
|
+
const userPool = this.userPools.get(UserPoolId);
|
|
149
|
+
|
|
150
|
+
if (!userPool) {
|
|
151
|
+
throw new Error(`User pool ${UserPoolId} not found`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let users = Array.from(this.users.values()).filter(u => u.UserPoolId === UserPoolId);
|
|
155
|
+
|
|
156
|
+
if (PaginationToken) {
|
|
157
|
+
const startIndex = parseInt(PaginationToken);
|
|
158
|
+
users = users.slice(startIndex);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const results = users.slice(0, Limit);
|
|
162
|
+
const nextToken = results.length === Limit && users.length > Limit ? String(Limit) : null;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
Users: results.map(u => ({
|
|
166
|
+
Username: u.Username,
|
|
167
|
+
UserStatus: u.UserStatus,
|
|
168
|
+
Enabled: u.Enabled,
|
|
169
|
+
UserCreateDate: u.CreatedDate,
|
|
170
|
+
UserLastModifiedDate: u.LastModifiedDate,
|
|
171
|
+
Attributes: this.formatUserAttributes(u.Attributes)
|
|
172
|
+
})),
|
|
173
|
+
PaginationToken: nextToken
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
listUserPoolClients(params = {}) {
|
|
178
|
+
const { UserPoolId, MaxResults = 60, NextToken } = params;
|
|
179
|
+
const userPool = this.userPools.get(UserPoolId);
|
|
180
|
+
if (!userPool) throw new Error(`User pool ${UserPoolId} not found`);
|
|
181
|
+
|
|
182
|
+
let clients = Array.from(userPool.Clients.values());
|
|
183
|
+
if (NextToken) clients = clients.slice(parseInt(NextToken));
|
|
184
|
+
const results = clients.slice(0, MaxResults);
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
UserPoolClients: results.map(c => ({
|
|
188
|
+
ClientId: c.ClientId,
|
|
189
|
+
ClientName: c.ClientName,
|
|
190
|
+
UserPoolId: c.UserPoolId
|
|
191
|
+
})),
|
|
192
|
+
NextToken: results.length === MaxResults && clients.length > MaxResults ? String(MaxResults) : null
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
describeUserPoolClient(params = {}) {
|
|
197
|
+
const { UserPoolId, ClientId } = params;
|
|
198
|
+
const userPool = this.userPools.get(UserPoolId);
|
|
199
|
+
if (!userPool) throw new Error(`User pool ${UserPoolId} not found`);
|
|
200
|
+
const client = userPool.Clients.get(ClientId);
|
|
201
|
+
if (!client) throw new Error(`Client ${ClientId} not found`);
|
|
202
|
+
return { UserPoolClient: client };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
deleteUserPoolClient(params = {}) {
|
|
206
|
+
const { UserPoolId, ClientId } = params;
|
|
207
|
+
const userPool = this.userPools.get(UserPoolId);
|
|
208
|
+
if (!userPool) throw new Error(`User pool ${UserPoolId} not found`);
|
|
209
|
+
userPool.Clients.delete(ClientId);
|
|
210
|
+
this.persistUserPools();
|
|
211
|
+
return {};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
forgotPassword(params = {}) {
|
|
215
|
+
const { ClientId, Username } = params;
|
|
216
|
+
const userPool = this.findUserPoolByClientId(ClientId);
|
|
217
|
+
if (!userPool) throw new Error(`Client ${ClientId} not found`);
|
|
218
|
+
return { CodeDeliveryDetails: { Destination: 'test@example.com', DeliveryMedium: 'EMAIL', AttributeName: 'email' } };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
confirmForgotPassword(params = {}) {
|
|
222
|
+
const { ClientId, Username, ConfirmationCode, Password } = params;
|
|
223
|
+
const user = this.findUserByUsername(Username, ClientId);
|
|
224
|
+
if (!user) throw new Error(`User not found: ${Username}`);
|
|
225
|
+
user.Password = this.hashPassword(Password);
|
|
226
|
+
user.UserStatus = 'CONFIRMED';
|
|
227
|
+
this.persistUsers();
|
|
228
|
+
return {};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
changePassword(params = {}) {
|
|
232
|
+
const { AccessToken, PreviousPassword, ProposedPassword } = params;
|
|
233
|
+
const session = this.accessTokens.get(AccessToken);
|
|
234
|
+
if (!session) throw new Error('Invalid access token');
|
|
235
|
+
const user = this.users.get(session.UserId);
|
|
236
|
+
if (!user) throw new Error('User not found');
|
|
237
|
+
if (!this.verifyPassword(PreviousPassword, user.Password)) throw new Error('Incorrect previous password');
|
|
238
|
+
user.Password = this.hashPassword(ProposedPassword);
|
|
239
|
+
this.persistUsers();
|
|
240
|
+
return {};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
respondToAuthChallenge(params = {}) {
|
|
244
|
+
return { AuthenticationResult: null, ChallengeName: null };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
revokeToken(params = {}) {
|
|
248
|
+
const { Token, ClientId } = params;
|
|
249
|
+
// Remove refresh token session if it exists
|
|
250
|
+
const session = this.refreshTokens.get(Token);
|
|
251
|
+
if (session) {
|
|
252
|
+
this.sessions.delete(session.Id);
|
|
253
|
+
this.accessTokens.delete(session.AccessToken);
|
|
254
|
+
this.refreshTokens.delete(Token);
|
|
255
|
+
this.persistSessions();
|
|
256
|
+
}
|
|
257
|
+
return {};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
globalSignOut(params = {}) {
|
|
261
|
+
const { AccessToken } = params;
|
|
262
|
+
const session = this.accessTokens.get(AccessToken);
|
|
263
|
+
if (session) {
|
|
264
|
+
this.sessions.delete(session.Id);
|
|
265
|
+
this.accessTokens.delete(AccessToken);
|
|
266
|
+
this.refreshTokens.delete(session.RefreshToken);
|
|
267
|
+
this.persistSessions();
|
|
268
|
+
}
|
|
269
|
+
return {};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
getUser(params = {}) {
|
|
273
|
+
const { AccessToken } = params;
|
|
274
|
+
const session = this.accessTokens.get(AccessToken);
|
|
275
|
+
if (!session) throw new Error('Invalid access token');
|
|
276
|
+
// Check session is still active
|
|
277
|
+
if (!this.sessions.has(session.Id)) throw new Error('Token has been revoked');
|
|
278
|
+
const user = this.users.get(session.UserId);
|
|
279
|
+
if (!user) throw new Error('User not found');
|
|
280
|
+
return {
|
|
281
|
+
Username: user.Username,
|
|
282
|
+
UserAttributes: this.formatUserAttributes(user.Attributes),
|
|
283
|
+
UserStatus: user.UserStatus
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
updateUserAttributes(params = {}) {
|
|
288
|
+
const { AccessToken, UserAttributes } = params;
|
|
289
|
+
const session = this.accessTokens.get(AccessToken);
|
|
290
|
+
if (!session) throw new Error('Invalid access token');
|
|
291
|
+
const user = this.users.get(session.UserId);
|
|
292
|
+
if (!user) throw new Error('User not found');
|
|
293
|
+
const updates = this.normalizeUserAttributes(UserAttributes || []);
|
|
294
|
+
Object.assign(user.Attributes, updates);
|
|
295
|
+
this.persistUsers();
|
|
296
|
+
return { CodeDeliveryDetailsList: [] };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
deleteUser(params = {}) {
|
|
300
|
+
const { AccessToken } = params;
|
|
301
|
+
const session = this.accessTokens.get(AccessToken);
|
|
302
|
+
if (!session) throw new Error('Invalid access token');
|
|
303
|
+
this.users.delete(session.UserId);
|
|
304
|
+
this.persistUsers();
|
|
305
|
+
return {};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
adminDisableUser(params = {}) {
|
|
309
|
+
const { UserPoolId, Username } = params;
|
|
310
|
+
const user = this.findUserByUsername(Username, null, UserPoolId);
|
|
311
|
+
if (!user) throw new Error(`User not found: ${Username}`);
|
|
312
|
+
user.Enabled = false;
|
|
313
|
+
this.persistUsers();
|
|
314
|
+
return {};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
adminEnableUser(params = {}) {
|
|
318
|
+
const { UserPoolId, Username } = params;
|
|
319
|
+
const user = this.findUserByUsername(Username, null, UserPoolId);
|
|
320
|
+
if (!user) throw new Error(`User not found: ${Username}`);
|
|
321
|
+
user.Enabled = true;
|
|
322
|
+
this.persistUsers();
|
|
323
|
+
return {};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
adminResetUserPassword(params = {}) {
|
|
327
|
+
const { UserPoolId, Username } = params;
|
|
328
|
+
const user = this.findUserByUsername(Username, null, UserPoolId);
|
|
329
|
+
if (!user) throw new Error(`User not found: ${Username}`);
|
|
330
|
+
user.UserStatus = 'RESET_REQUIRED';
|
|
331
|
+
this.persistUsers();
|
|
332
|
+
return {};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
adminUserGlobalSignOut(params = {}) {
|
|
336
|
+
const { UserPoolId, Username } = params;
|
|
337
|
+
const user = this.findUserByUsername(Username, null, UserPoolId);
|
|
338
|
+
if (!user) throw new Error(`User not found: ${Username}`);
|
|
339
|
+
// Invalidate all sessions for this user
|
|
340
|
+
for (const [id, session] of this.sessions.entries()) {
|
|
341
|
+
if (session.UserId === user.UserId) {
|
|
342
|
+
this.accessTokens.delete(session.AccessToken);
|
|
343
|
+
this.refreshTokens.delete(session.RefreshToken);
|
|
344
|
+
this.sessions.delete(id);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
this.persistSessions();
|
|
348
|
+
return {};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
adminListGroupsForUser(params = {}) {
|
|
352
|
+
return { Groups: [], NextToken: null };
|
|
353
|
+
}
|
|
354
|
+
|
|
143
355
|
deleteUserPool(params) {
|
|
144
356
|
const { UserPoolId } = params;
|
|
145
357
|
|
|
@@ -207,7 +419,7 @@ class CognitoSimulator {
|
|
|
207
419
|
|
|
208
420
|
// ============ User Operations ============
|
|
209
421
|
|
|
210
|
-
signUp(params) {
|
|
422
|
+
signUp(params = {}) {
|
|
211
423
|
const { ClientId, Username, Password, UserAttributes, ValidationData } = params;
|
|
212
424
|
|
|
213
425
|
// Encontra o user pool pelo client id
|
|
@@ -319,6 +531,7 @@ class CognitoSimulator {
|
|
|
319
531
|
this.persistSessions();
|
|
320
532
|
|
|
321
533
|
logger.debug(`🔐 Usuário autenticado: ${username}`);
|
|
534
|
+
this.audit.record({ eventName: 'InitiateAuth', readOnly: false, resources: [{ ARN: userPool.Arn, type: 'AWS::Cognito::UserPool' }], requestParameters: { clientId: ClientId, authFlow: AuthFlow } });
|
|
322
535
|
|
|
323
536
|
return {
|
|
324
537
|
AuthenticationResult: {
|
|
@@ -726,10 +939,10 @@ class CognitoSimulator {
|
|
|
726
939
|
// ============ Persistence ============
|
|
727
940
|
|
|
728
941
|
loadUserPools() {
|
|
942
|
+
// Load persisted pools first
|
|
729
943
|
const saved = this.store.read('__userpools__');
|
|
730
944
|
if (saved) {
|
|
731
945
|
for (const [id, data] of Object.entries(saved)) {
|
|
732
|
-
// Reconstitui Maps
|
|
733
946
|
data.Clients = new Map(Object.entries(data.Clients || {}));
|
|
734
947
|
data.Groups = new Map(Object.entries(data.Groups || {}));
|
|
735
948
|
data.IdentityProviders = new Map(Object.entries(data.IdentityProviders || {}));
|
|
@@ -737,6 +950,64 @@ class CognitoSimulator {
|
|
|
737
950
|
this.userPools.set(id, data);
|
|
738
951
|
}
|
|
739
952
|
}
|
|
953
|
+
|
|
954
|
+
// Create user pools from config if not already persisted
|
|
955
|
+
if (this.config.cognito?.userPools) {
|
|
956
|
+
let configChanged = false;
|
|
957
|
+
const configPath = this.config._configPath;
|
|
958
|
+
|
|
959
|
+
for (let i = 0; i < this.config.cognito.userPools.length; i++) {
|
|
960
|
+
const poolConfig = this.config.cognito.userPools[i];
|
|
961
|
+
const existing = Array.from(this.userPools.values()).find(p => p.Name === poolConfig.PoolName);
|
|
962
|
+
|
|
963
|
+
if (!existing) {
|
|
964
|
+
const result = this.createUserPool(poolConfig);
|
|
965
|
+
const poolId = result.UserPool.Id;
|
|
966
|
+
logger.debug(`✅ User Pool criado a partir da config: ${poolConfig.PoolName} (${poolId})`);
|
|
967
|
+
|
|
968
|
+
// Auto-create a default client if not specified
|
|
969
|
+
if (!poolConfig.ClientId) {
|
|
970
|
+
const clientResult = this.createUserPoolClient({
|
|
971
|
+
UserPoolId: poolId,
|
|
972
|
+
ClientName: `${poolConfig.PoolName}-client`,
|
|
973
|
+
GenerateSecret: false
|
|
974
|
+
});
|
|
975
|
+
const clientId = clientResult.UserPoolClient.ClientId;
|
|
976
|
+
this.config.cognito.userPools[i].ClientId = clientId;
|
|
977
|
+
this.config.cognito.userPools[i].UserPoolId = poolId;
|
|
978
|
+
configChanged = true;
|
|
979
|
+
logger.debug(`✅ Client criado automaticamente: ${clientId}`);
|
|
980
|
+
}
|
|
981
|
+
} else if (!poolConfig.ClientId) {
|
|
982
|
+
// Pool exists but no clientId in config — write it back
|
|
983
|
+
const firstClient = existing.Clients.size > 0 ? existing.Clients.values().next().value : null;
|
|
984
|
+
if (firstClient) {
|
|
985
|
+
this.config.cognito.userPools[i].ClientId = firstClient.ClientId;
|
|
986
|
+
this.config.cognito.userPools[i].UserPoolId = existing.Id;
|
|
987
|
+
configChanged = true;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// Write clientId back to aws-local-simulator.json
|
|
993
|
+
if (configChanged && configPath) {
|
|
994
|
+
try {
|
|
995
|
+
const fs = require('fs');
|
|
996
|
+
const fileContent = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
997
|
+
fileContent.cognito = fileContent.cognito || {};
|
|
998
|
+
fileContent.cognito.userPools = this.config.cognito.userPools.map(p => ({
|
|
999
|
+
PoolName: p.PoolName,
|
|
1000
|
+
AutoVerifiedAttributes: p.AutoVerifiedAttributes,
|
|
1001
|
+
UserPoolId: p.UserPoolId,
|
|
1002
|
+
ClientId: p.ClientId
|
|
1003
|
+
}));
|
|
1004
|
+
fs.writeFileSync(configPath, JSON.stringify(fileContent, null, 2));
|
|
1005
|
+
logger.info(`✅ ClientId gravado em: ${configPath}`);
|
|
1006
|
+
} catch (err) {
|
|
1007
|
+
logger.warn(`⚠️ Não foi possível gravar clientId no config: ${err.message}`);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
740
1011
|
}
|
|
741
1012
|
|
|
742
1013
|
loadIdentityPools() {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview AWS Config Service
|
|
5
|
+
* Porta padrão: 4013
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { ConfigSimulator } = require('./simulador');
|
|
11
|
+
const { createConfigServer } = require('./server');
|
|
12
|
+
const LocalStore = require('../../utils/local-store');
|
|
13
|
+
|
|
14
|
+
class ConfigService {
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.logger = require('../../utils/logger');
|
|
18
|
+
this.name = 'config';
|
|
19
|
+
this.port = config?.ports?.config || config?.services?.config?.port || 4013;
|
|
20
|
+
this.store = null;
|
|
21
|
+
this.simulator = null;
|
|
22
|
+
this._server = null;
|
|
23
|
+
this.isRunning = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async initialize() {
|
|
27
|
+
this.logger.debug(`Inicializando AWS Config Service na porta ${this.port}...`);
|
|
28
|
+
const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
|
|
29
|
+
this.store = new LocalStore(path.join(dataDir, 'config'));
|
|
30
|
+
this.simulator = new ConfigSimulator(this.config, this.store, this.logger);
|
|
31
|
+
await this.simulator.load();
|
|
32
|
+
this.logger.debug('AWS Config Service inicializado');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
injectDependencies(server) {
|
|
36
|
+
if (!server) return;
|
|
37
|
+
const s3 = server.getService('s3');
|
|
38
|
+
if (s3?.simulator) this.simulator.s3Simulator = s3.simulator;
|
|
39
|
+
const sns = server.getService('sns');
|
|
40
|
+
if (sns?.simulator) this.simulator.snsSimulator = sns.simulator;
|
|
41
|
+
const lambda = server.getService('lambda');
|
|
42
|
+
if (lambda?.simulator) this.simulator.lambdaSimulator = lambda.simulator;
|
|
43
|
+
const dynamo = server.getService('dynamodb');
|
|
44
|
+
if (dynamo?.simulator) this.simulator.dynamoSimulator = dynamo.simulator;
|
|
45
|
+
const ct = server.getService('cloudtrail');
|
|
46
|
+
if (ct?.simulator) this.simulator.cloudtrailSimulator = ct.simulator;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async start() {
|
|
50
|
+
if (this.isRunning) return;
|
|
51
|
+
const { handler } = createConfigServer(this.simulator, this.logger);
|
|
52
|
+
this._server = http.createServer((req, res) => {
|
|
53
|
+
handler(req, res).catch(err => {
|
|
54
|
+
this.logger.error('[Config] Unhandled error:', err);
|
|
55
|
+
if (!res.headersSent) {
|
|
56
|
+
res.writeHead(500, { 'Content-Type': 'application/x-amz-json-1.1' });
|
|
57
|
+
res.end(JSON.stringify({ __type: 'InternalFailure', message: err.message }));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
this._server.listen(this.port, '0.0.0.0', (err) => {
|
|
63
|
+
if (err) return reject(err);
|
|
64
|
+
this.isRunning = true;
|
|
65
|
+
this.logger.debug(`AWS Config rodando na porta ${this.port}`);
|
|
66
|
+
resolve();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async stop() {
|
|
72
|
+
if (!this.isRunning || !this._server) return;
|
|
73
|
+
await new Promise((resolve) => this._server.close(resolve));
|
|
74
|
+
this.simulator._stopRecording?.();
|
|
75
|
+
this._server = null;
|
|
76
|
+
this.isRunning = false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async reset() {
|
|
80
|
+
this.simulator.reset();
|
|
81
|
+
await this.simulator.save();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getStatus() {
|
|
85
|
+
return {
|
|
86
|
+
running: this.isRunning,
|
|
87
|
+
port: this.port,
|
|
88
|
+
endpoint: `http://localhost:${this.port}`,
|
|
89
|
+
...this.simulator?.getStatus(),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getSimulator() { return this.simulator; }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = { ConfigService };
|