@gugananuvem/aws-local-simulator 1.0.2 → 1.0.5
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/bin/aws-local-simulator.js +1 -62
- package/package.json +8 -10
- package/src/index.js +1 -131
- package/src/config/config-loader.js +0 -113
- package/src/config/default-config.js +0 -65
- package/src/config/env-loader.js +0 -67
- package/src/index.mjs +0 -124
- package/src/server.js +0 -219
- package/src/services/apigateway/index.js +0 -67
- package/src/services/apigateway/server.js +0 -435
- package/src/services/apigateway/simulator.js +0 -1252
- package/src/services/cognito/index.js +0 -66
- package/src/services/cognito/server.js +0 -229
- package/src/services/cognito/simulator.js +0 -848
- package/src/services/dynamodb/index.js +0 -71
- package/src/services/dynamodb/server.js +0 -122
- package/src/services/dynamodb/simulator.js +0 -614
- package/src/services/eventbridge/index.js +0 -85
- package/src/services/index.js +0 -19
- package/src/services/lambda/handler-loader.js +0 -173
- package/src/services/lambda/index.js +0 -73
- package/src/services/lambda/route-registry.js +0 -275
- package/src/services/lambda/server.js +0 -153
- package/src/services/lambda/simulator.js +0 -278
- package/src/services/s3/index.js +0 -70
- package/src/services/s3/server.js +0 -239
- package/src/services/s3/simulator.js +0 -740
- package/src/services/sns/index.js +0 -76
- package/src/services/sqs/index.js +0 -96
- package/src/services/sqs/server.js +0 -274
- package/src/services/sqs/simulator.js +0 -660
- package/src/template/aws-config-template.js +0 -88
- package/src/template/aws-config-template.mjs +0 -91
- package/src/template/config-template.json +0 -165
- package/src/utils/aws-config.js +0 -92
- package/src/utils/local-store.js +0 -68
- package/src/utils/logger.js +0 -60
|
@@ -1,848 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cognito Simulator Core
|
|
3
|
-
* Simula User Pools, Identity Pools, Autenticação, Tokens JWT
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const crypto = require('crypto');
|
|
7
|
-
const jwt = require('jsonwebtoken');
|
|
8
|
-
const { v4: uuidv4 } = require('uuid');
|
|
9
|
-
const logger = require('../../utils/logger');
|
|
10
|
-
const LocalStore = require('../../utils/local-store');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
|
|
13
|
-
class CognitoSimulator {
|
|
14
|
-
constructor(config) {
|
|
15
|
-
this.config = config;
|
|
16
|
-
this.dataDir = path.join(process.env.AWS_LOCAL_SIMULATOR_DATA_DIR, 'cognito');
|
|
17
|
-
this.store = new LocalStore(this.dataDir);
|
|
18
|
-
this.userPools = new Map();
|
|
19
|
-
this.identityPools = new Map();
|
|
20
|
-
this.users = new Map();
|
|
21
|
-
this.sessions = new Map();
|
|
22
|
-
this.refreshTokens = new Map();
|
|
23
|
-
this.accessTokens = new Map();
|
|
24
|
-
this.jwtSecret = crypto.randomBytes(64).toString('hex');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async initialize() {
|
|
28
|
-
logger.debug('Inicializando Cognito Simulator...');
|
|
29
|
-
this.loadUserPools();
|
|
30
|
-
this.loadIdentityPools();
|
|
31
|
-
this.loadUsers();
|
|
32
|
-
this.loadSessions();
|
|
33
|
-
|
|
34
|
-
logger.debug(`✅ Cognito Simulator inicializado com ${this.userPools.size} user pools, ${this.identityPools.size} identity pools, ${this.users.size} usuários`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// ============ User Pool Operations ============
|
|
38
|
-
|
|
39
|
-
createUserPool(params) {
|
|
40
|
-
const { PoolName, Policies, LambdaConfig, AutoVerifiedAttributes, AliasAttributes, UsernameAttributes, MfaConfiguration } = params;
|
|
41
|
-
|
|
42
|
-
const poolId = `local_${PoolName}_${Date.now()}`;
|
|
43
|
-
const userPool = {
|
|
44
|
-
Id: poolId,
|
|
45
|
-
Name: PoolName,
|
|
46
|
-
Arn: `arn:aws:cognito:local:000000000000:userpool/${poolId}`,
|
|
47
|
-
Status: 'ACTIVE',
|
|
48
|
-
CreationDate: new Date().toISOString(),
|
|
49
|
-
LastModifiedDate: new Date().toISOString(),
|
|
50
|
-
Policies: Policies || {
|
|
51
|
-
PasswordPolicy: {
|
|
52
|
-
MinimumLength: 8,
|
|
53
|
-
RequireUppercase: true,
|
|
54
|
-
RequireLowercase: true,
|
|
55
|
-
RequireNumbers: true,
|
|
56
|
-
RequireSymbols: false
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
LambdaConfig: LambdaConfig || {},
|
|
60
|
-
AutoVerifiedAttributes: AutoVerifiedAttributes || ['email'],
|
|
61
|
-
AliasAttributes: AliasAttributes || [],
|
|
62
|
-
UsernameAttributes: UsernameAttributes || ['email'],
|
|
63
|
-
MfaConfiguration: MfaConfiguration || 'OFF',
|
|
64
|
-
EstimatedNumberOfUsers: 0,
|
|
65
|
-
Users: [],
|
|
66
|
-
Clients: new Map(),
|
|
67
|
-
Groups: new Map(),
|
|
68
|
-
IdentityProviders: new Map(),
|
|
69
|
-
ResourceServers: new Map()
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
this.userPools.set(poolId, userPool);
|
|
73
|
-
this.persistUserPools();
|
|
74
|
-
|
|
75
|
-
logger.debug(`✅ User Pool criado: ${PoolName} (${poolId})`);
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
UserPool: {
|
|
79
|
-
Id: userPool.Id,
|
|
80
|
-
Name: userPool.Name,
|
|
81
|
-
Arn: userPool.Arn,
|
|
82
|
-
Status: userPool.Status,
|
|
83
|
-
CreationDate: userPool.CreationDate,
|
|
84
|
-
LastModifiedDate: userPool.LastModifiedDate,
|
|
85
|
-
MfaConfiguration: userPool.MfaConfiguration,
|
|
86
|
-
EstimatedNumberOfUsers: 0
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
listUserPools(params = {}) {
|
|
92
|
-
const { MaxResults = 60, NextToken } = params;
|
|
93
|
-
let userPools = Array.from(this.userPools.values());
|
|
94
|
-
|
|
95
|
-
if (NextToken) {
|
|
96
|
-
const startIndex = parseInt(NextToken);
|
|
97
|
-
userPools = userPools.slice(startIndex);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const results = userPools.slice(0, MaxResults);
|
|
101
|
-
const nextToken = results.length === MaxResults ? String(MaxResults) : null;
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
UserPools: results.map(pool => ({
|
|
105
|
-
Id: pool.Id,
|
|
106
|
-
Name: pool.Name,
|
|
107
|
-
Arn: pool.Arn,
|
|
108
|
-
Status: pool.Status,
|
|
109
|
-
CreationDate: pool.CreationDate,
|
|
110
|
-
LastModifiedDate: pool.LastModifiedDate
|
|
111
|
-
})),
|
|
112
|
-
NextToken: nextToken
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
describeUserPool(params) {
|
|
117
|
-
const { UserPoolId } = params;
|
|
118
|
-
const userPool = this.userPools.get(UserPoolId);
|
|
119
|
-
|
|
120
|
-
if (!userPool) {
|
|
121
|
-
throw new Error(`User pool ${UserPoolId} not found`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
UserPool: {
|
|
126
|
-
Id: userPool.Id,
|
|
127
|
-
Name: userPool.Name,
|
|
128
|
-
Arn: userPool.Arn,
|
|
129
|
-
Status: userPool.Status,
|
|
130
|
-
CreationDate: userPool.CreationDate,
|
|
131
|
-
LastModifiedDate: userPool.LastModifiedDate,
|
|
132
|
-
Policies: userPool.Policies,
|
|
133
|
-
LambdaConfig: userPool.LambdaConfig,
|
|
134
|
-
AutoVerifiedAttributes: userPool.AutoVerifiedAttributes,
|
|
135
|
-
AliasAttributes: userPool.AliasAttributes,
|
|
136
|
-
UsernameAttributes: userPool.UsernameAttributes,
|
|
137
|
-
MfaConfiguration: userPool.MfaConfiguration,
|
|
138
|
-
EstimatedNumberOfUsers: userPool.Users.length
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
deleteUserPool(params) {
|
|
144
|
-
const { UserPoolId } = params;
|
|
145
|
-
|
|
146
|
-
if (!this.userPools.has(UserPoolId)) {
|
|
147
|
-
throw new Error(`User pool ${UserPoolId} not found`);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
this.userPools.delete(UserPoolId);
|
|
151
|
-
this.persistUserPools();
|
|
152
|
-
|
|
153
|
-
return {};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// ============ User Pool Client Operations ============
|
|
157
|
-
|
|
158
|
-
createUserPoolClient(params) {
|
|
159
|
-
const { UserPoolId, ClientName, GenerateSecret, RefreshTokenValidity, AccessTokenValidity, IdTokenValidity, AllowedOAuthFlows, AllowedOAuthScopes, CallbackURLs, LogoutURLs } = params;
|
|
160
|
-
|
|
161
|
-
const userPool = this.userPools.get(UserPoolId);
|
|
162
|
-
if (!userPool) {
|
|
163
|
-
throw new Error(`User pool ${UserPoolId} not found`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const clientId = crypto.randomBytes(20).toString('hex');
|
|
167
|
-
const clientSecret = GenerateSecret ? crypto.randomBytes(32).toString('hex') : null;
|
|
168
|
-
|
|
169
|
-
const client = {
|
|
170
|
-
ClientId: clientId,
|
|
171
|
-
ClientName: ClientName,
|
|
172
|
-
ClientSecret: clientSecret,
|
|
173
|
-
UserPoolId: UserPoolId,
|
|
174
|
-
RefreshTokenValidity: RefreshTokenValidity || 30,
|
|
175
|
-
AccessTokenValidity: AccessTokenValidity || 1,
|
|
176
|
-
IdTokenValidity: IdTokenValidity || 1,
|
|
177
|
-
AllowedOAuthFlows: AllowedOAuthFlows || ['code'],
|
|
178
|
-
AllowedOAuthScopes: AllowedOAuthScopes || ['openid', 'email', 'profile'],
|
|
179
|
-
CallbackURLs: CallbackURLs || [],
|
|
180
|
-
LogoutURLs: LogoutURLs || [],
|
|
181
|
-
CreatedDate: new Date().toISOString(),
|
|
182
|
-
LastModifiedDate: new Date().toISOString()
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
userPool.Clients.set(clientId, client);
|
|
186
|
-
this.persistUserPools();
|
|
187
|
-
|
|
188
|
-
logger.debug(`✅ User Pool Client criado: ${ClientName} (${clientId})`);
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
UserPoolClient: {
|
|
192
|
-
ClientId: client.ClientId,
|
|
193
|
-
ClientName: client.ClientName,
|
|
194
|
-
ClientSecret: client.ClientSecret,
|
|
195
|
-
UserPoolId: client.UserPoolId,
|
|
196
|
-
RefreshTokenValidity: client.RefreshTokenValidity,
|
|
197
|
-
AccessTokenValidity: client.AccessTokenValidity,
|
|
198
|
-
IdTokenValidity: client.IdTokenValidity,
|
|
199
|
-
AllowedOAuthFlows: client.AllowedOAuthFlows,
|
|
200
|
-
AllowedOAuthScopes: client.AllowedOAuthScopes,
|
|
201
|
-
CallbackURLs: client.CallbackURLs,
|
|
202
|
-
LogoutURLs: client.LogoutURLs,
|
|
203
|
-
CreationDate: client.CreatedDate
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// ============ User Operations ============
|
|
209
|
-
|
|
210
|
-
signUp(params) {
|
|
211
|
-
const { ClientId, Username, Password, UserAttributes, ValidationData } = params;
|
|
212
|
-
|
|
213
|
-
// Encontra o user pool pelo client id
|
|
214
|
-
const userPool = this.findUserPoolByClientId(ClientId);
|
|
215
|
-
if (!userPool) {
|
|
216
|
-
throw new Error(`Client ${ClientId} not found`);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Verifica se usuário já existe
|
|
220
|
-
const existingUser = Array.from(this.users.values()).find(
|
|
221
|
-
u => u.Username === Username && u.UserPoolId === userPool.Id
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
if (existingUser) {
|
|
225
|
-
throw new Error(`User already exists: ${Username}`);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const userId = uuidv4();
|
|
229
|
-
const user = {
|
|
230
|
-
Username: Username,
|
|
231
|
-
UserPoolId: userPool.Id,
|
|
232
|
-
UserId: userId,
|
|
233
|
-
Attributes: this.normalizeUserAttributes(UserAttributes || []),
|
|
234
|
-
Enabled: true,
|
|
235
|
-
UserStatus: 'CONFIRMED', // Por padrão, confirma imediatamente (para teste)
|
|
236
|
-
CreatedDate: new Date().toISOString(),
|
|
237
|
-
LastModifiedDate: new Date().toISOString(),
|
|
238
|
-
Password: this.hashPassword(Password),
|
|
239
|
-
MfaOptions: [],
|
|
240
|
-
PreferredMfaSetting: null,
|
|
241
|
-
UserMFASettingList: []
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
this.users.set(userId, user);
|
|
245
|
-
userPool.Users.push(userId);
|
|
246
|
-
userPool.EstimatedNumberOfUsers++;
|
|
247
|
-
this.persistUsers();
|
|
248
|
-
this.persistUserPools();
|
|
249
|
-
|
|
250
|
-
logger.debug(`✅ Usuário criado: ${Username} (${userId})`);
|
|
251
|
-
|
|
252
|
-
return {
|
|
253
|
-
UserConfirmed: true,
|
|
254
|
-
UserSub: userId,
|
|
255
|
-
CodeDeliveryDetails: null
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
confirmSignUp(params) {
|
|
260
|
-
const { ClientId, Username, ConfirmationCode } = params;
|
|
261
|
-
|
|
262
|
-
const user = this.findUserByUsername(Username, ClientId);
|
|
263
|
-
if (!user) {
|
|
264
|
-
throw new Error(`User not found: ${Username}`);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
user.UserStatus = 'CONFIRMED';
|
|
268
|
-
user.LastModifiedDate = new Date().toISOString();
|
|
269
|
-
this.persistUsers();
|
|
270
|
-
|
|
271
|
-
return {};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
initiateAuth(params) {
|
|
275
|
-
const { AuthFlow, ClientId, AuthParameters } = params;
|
|
276
|
-
const userPool = this.findUserPoolByClientId(ClientId);
|
|
277
|
-
|
|
278
|
-
if (!userPool) {
|
|
279
|
-
throw new Error(`Client ${ClientId} not found`);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const username = AuthParameters.USERNAME;
|
|
283
|
-
const password = AuthParameters.PASSWORD;
|
|
284
|
-
|
|
285
|
-
const user = this.findUserByUsername(username, ClientId);
|
|
286
|
-
if (!user) {
|
|
287
|
-
throw new Error(`User not found: ${username}`);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (user.UserStatus !== 'CONFIRMED') {
|
|
291
|
-
throw new Error(`User not confirmed: ${username}`);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if (!this.verifyPassword(password, user.Password)) {
|
|
295
|
-
throw new Error('Incorrect username or password');
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Gera tokens JWT
|
|
299
|
-
const accessToken = this.generateAccessToken(user, userPool, ClientId);
|
|
300
|
-
const idToken = this.generateIdToken(user, userPool, ClientId);
|
|
301
|
-
const refreshToken = this.generateRefreshToken(user, userPool, ClientId);
|
|
302
|
-
|
|
303
|
-
const sessionId = uuidv4();
|
|
304
|
-
const session = {
|
|
305
|
-
Id: sessionId,
|
|
306
|
-
UserId: user.UserId,
|
|
307
|
-
UserPoolId: userPool.Id,
|
|
308
|
-
ClientId: ClientId,
|
|
309
|
-
AccessToken: accessToken,
|
|
310
|
-
IdToken: idToken,
|
|
311
|
-
RefreshToken: refreshToken,
|
|
312
|
-
CreatedAt: new Date().toISOString(),
|
|
313
|
-
ExpiresAt: new Date(Date.now() + 3600000).toISOString() // 1 hora
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
this.sessions.set(sessionId, session);
|
|
317
|
-
this.accessTokens.set(accessToken, session);
|
|
318
|
-
this.refreshTokens.set(refreshToken, session);
|
|
319
|
-
this.persistSessions();
|
|
320
|
-
|
|
321
|
-
logger.debug(`🔐 Usuário autenticado: ${username}`);
|
|
322
|
-
|
|
323
|
-
return {
|
|
324
|
-
AuthenticationResult: {
|
|
325
|
-
AccessToken: accessToken,
|
|
326
|
-
IdToken: idToken,
|
|
327
|
-
RefreshToken: refreshToken,
|
|
328
|
-
TokenType: 'Bearer',
|
|
329
|
-
ExpiresIn: 3600
|
|
330
|
-
},
|
|
331
|
-
ChallengeName: null,
|
|
332
|
-
Session: null
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
getToken(params) {
|
|
337
|
-
const { AuthFlow, ClientId, AuthParameters } = params;
|
|
338
|
-
|
|
339
|
-
if (AuthFlow === 'REFRESH_TOKEN_AUTH') {
|
|
340
|
-
const refreshToken = AuthParameters.REFRESH_TOKEN;
|
|
341
|
-
const session = this.refreshTokens.get(refreshToken);
|
|
342
|
-
|
|
343
|
-
if (!session) {
|
|
344
|
-
throw new Error('Invalid refresh token');
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const user = this.users.get(session.UserId);
|
|
348
|
-
const userPool = this.userPools.get(session.UserPoolId);
|
|
349
|
-
|
|
350
|
-
if (!user || !userPool) {
|
|
351
|
-
throw new Error('Invalid session');
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Gera novos tokens
|
|
355
|
-
const newAccessToken = this.generateAccessToken(user, userPool, session.ClientId);
|
|
356
|
-
const newIdToken = this.generateIdToken(user, userPool, session.ClientId);
|
|
357
|
-
|
|
358
|
-
session.AccessToken = newAccessToken;
|
|
359
|
-
session.IdToken = newIdToken;
|
|
360
|
-
session.ExpiresAt = new Date(Date.now() + 3600000).toISOString();
|
|
361
|
-
|
|
362
|
-
this.accessTokens.set(newAccessToken, session);
|
|
363
|
-
this.persistSessions();
|
|
364
|
-
|
|
365
|
-
return {
|
|
366
|
-
AuthenticationResult: {
|
|
367
|
-
AccessToken: newAccessToken,
|
|
368
|
-
IdToken: newIdToken,
|
|
369
|
-
TokenType: 'Bearer',
|
|
370
|
-
ExpiresIn: 3600
|
|
371
|
-
}
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
throw new Error(`Unsupported AuthFlow: ${AuthFlow}`);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// ============ Token Management ============
|
|
379
|
-
|
|
380
|
-
generateAccessToken(user, userPool, clientId) {
|
|
381
|
-
const payload = {
|
|
382
|
-
sub: user.UserId,
|
|
383
|
-
token_use: 'access',
|
|
384
|
-
client_id: clientId,
|
|
385
|
-
username: user.Username,
|
|
386
|
-
scope: 'aws.cognito.signin.user.admin',
|
|
387
|
-
iss: `https://cognito-idp.local/${userPool.Id}`,
|
|
388
|
-
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
389
|
-
iat: Math.floor(Date.now() / 1000)
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
return jwt.sign(payload, this.jwtSecret, { algorithm: 'HS256' });
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
generateIdToken(user, userPool, clientId) {
|
|
396
|
-
const payload = {
|
|
397
|
-
sub: user.UserId,
|
|
398
|
-
token_use: 'id',
|
|
399
|
-
client_id: clientId,
|
|
400
|
-
email: user.Attributes.email,
|
|
401
|
-
email_verified: user.Attributes.email_verified || true,
|
|
402
|
-
username: user.Username,
|
|
403
|
-
iss: `https://cognito-idp.local/${userPool.Id}`,
|
|
404
|
-
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
405
|
-
iat: Math.floor(Date.now() / 1000)
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
// Adiciona outros atributos do usuário
|
|
409
|
-
for (const [key, value] of Object.entries(user.Attributes)) {
|
|
410
|
-
if (key !== 'email' && key !== 'email_verified') {
|
|
411
|
-
payload[key] = value;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return jwt.sign(payload, this.jwtSecret, { algorithm: 'HS256' });
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
generateRefreshToken(user, userPool, clientId) {
|
|
419
|
-
const payload = {
|
|
420
|
-
sub: user.UserId,
|
|
421
|
-
token_use: 'refresh',
|
|
422
|
-
client_id: clientId,
|
|
423
|
-
username: user.Username,
|
|
424
|
-
iss: `https://cognito-idp.local/${userPool.Id}`,
|
|
425
|
-
exp: Math.floor(Date.now() / 1000) + 2592000, // 30 dias
|
|
426
|
-
iat: Math.floor(Date.now() / 1000)
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
return jwt.sign(payload, this.jwtSecret, { algorithm: 'HS256' });
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
verifyAccessToken(token) {
|
|
433
|
-
try {
|
|
434
|
-
const decoded = jwt.verify(token, this.jwtSecret);
|
|
435
|
-
const session = this.accessTokens.get(token);
|
|
436
|
-
|
|
437
|
-
if (!session || session.ExpiresAt < new Date().toISOString()) {
|
|
438
|
-
return null;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return decoded;
|
|
442
|
-
} catch (error) {
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// ============ Admin Operations ============
|
|
448
|
-
|
|
449
|
-
adminGetUser(params) {
|
|
450
|
-
const { UserPoolId, Username } = params;
|
|
451
|
-
const userPool = this.userPools.get(UserPoolId);
|
|
452
|
-
|
|
453
|
-
if (!userPool) {
|
|
454
|
-
throw new Error(`User pool ${UserPoolId} not found`);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const user = this.findUserByUsername(Username, null, UserPoolId);
|
|
458
|
-
if (!user) {
|
|
459
|
-
throw new Error(`User not found: ${Username}`);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
return {
|
|
463
|
-
Username: user.Username,
|
|
464
|
-
UserAttributes: this.formatUserAttributes(user.Attributes),
|
|
465
|
-
UserCreateDate: user.CreatedDate,
|
|
466
|
-
UserLastModifiedDate: user.LastModifiedDate,
|
|
467
|
-
Enabled: user.Enabled,
|
|
468
|
-
UserStatus: user.UserStatus,
|
|
469
|
-
MFAOptions: user.MfaOptions,
|
|
470
|
-
PreferredMfaSetting: user.PreferredMfaSetting,
|
|
471
|
-
UserMFASettingList: user.UserMFASettingList
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
adminCreateUser(params) {
|
|
476
|
-
const { UserPoolId, Username, UserAttributes, TemporaryPassword, DesiredDeliveryMediums } = params;
|
|
477
|
-
const userPool = this.userPools.get(UserPoolId);
|
|
478
|
-
|
|
479
|
-
if (!userPool) {
|
|
480
|
-
throw new Error(`User pool ${UserPoolId} not found`);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const userId = uuidv4();
|
|
484
|
-
const user = {
|
|
485
|
-
Username: Username,
|
|
486
|
-
UserPoolId: UserPoolId,
|
|
487
|
-
UserId: userId,
|
|
488
|
-
Attributes: this.normalizeUserAttributes(UserAttributes || []),
|
|
489
|
-
Enabled: true,
|
|
490
|
-
UserStatus: 'FORCE_CHANGE_PASSWORD',
|
|
491
|
-
CreatedDate: new Date().toISOString(),
|
|
492
|
-
LastModifiedDate: new Date().toISOString(),
|
|
493
|
-
Password: this.hashPassword(TemporaryPassword || 'Temp123!'),
|
|
494
|
-
MfaOptions: [],
|
|
495
|
-
PreferredMfaSetting: null,
|
|
496
|
-
UserMFASettingList: []
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
this.users.set(userId, user);
|
|
500
|
-
userPool.Users.push(userId);
|
|
501
|
-
userPool.EstimatedNumberOfUsers++;
|
|
502
|
-
this.persistUsers();
|
|
503
|
-
this.persistUserPools();
|
|
504
|
-
|
|
505
|
-
logger.debug(`👤 Usuário administrador criado: ${Username}`);
|
|
506
|
-
|
|
507
|
-
return {
|
|
508
|
-
User: {
|
|
509
|
-
Username: user.Username,
|
|
510
|
-
UserAttributes: this.formatUserAttributes(user.Attributes),
|
|
511
|
-
UserCreateDate: user.CreatedDate,
|
|
512
|
-
UserLastModifiedDate: user.LastModifiedDate,
|
|
513
|
-
Enabled: user.Enabled,
|
|
514
|
-
UserStatus: user.UserStatus
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
adminSetUserPassword(params) {
|
|
520
|
-
const { UserPoolId, Username, Password, Permanent } = params;
|
|
521
|
-
const user = this.findUserByUsername(Username, null, UserPoolId);
|
|
522
|
-
|
|
523
|
-
if (!user) {
|
|
524
|
-
throw new Error(`User not found: ${Username}`);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
user.Password = this.hashPassword(Password);
|
|
528
|
-
if (Permanent) {
|
|
529
|
-
user.UserStatus = 'CONFIRMED';
|
|
530
|
-
}
|
|
531
|
-
user.LastModifiedDate = new Date().toISOString();
|
|
532
|
-
this.persistUsers();
|
|
533
|
-
|
|
534
|
-
return {};
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
adminDeleteUser(params) {
|
|
538
|
-
const { UserPoolId, Username } = params;
|
|
539
|
-
const user = this.findUserByUsername(Username, null, UserPoolId);
|
|
540
|
-
|
|
541
|
-
if (!user) {
|
|
542
|
-
throw new Error(`User not found: ${Username}`);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
const userPool = this.userPools.get(UserPoolId);
|
|
546
|
-
if (userPool) {
|
|
547
|
-
const index = userPool.Users.indexOf(user.UserId);
|
|
548
|
-
if (index !== -1) {
|
|
549
|
-
userPool.Users.splice(index, 1);
|
|
550
|
-
userPool.EstimatedNumberOfUsers--;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
this.users.delete(user.UserId);
|
|
555
|
-
this.persistUsers();
|
|
556
|
-
this.persistUserPools();
|
|
557
|
-
|
|
558
|
-
return {};
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// ============ Helper Methods ============
|
|
562
|
-
|
|
563
|
-
findUserPoolByClientId(clientId) {
|
|
564
|
-
for (const userPool of this.userPools.values()) {
|
|
565
|
-
if (userPool.Clients.has(clientId)) {
|
|
566
|
-
return userPool;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
return null;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
findUserByUsername(username, clientId = null, userPoolId = null) {
|
|
573
|
-
let targetUserPoolId = userPoolId;
|
|
574
|
-
|
|
575
|
-
if (clientId && !targetUserPoolId) {
|
|
576
|
-
const userPool = this.findUserPoolByClientId(clientId);
|
|
577
|
-
if (userPool) {
|
|
578
|
-
targetUserPoolId = userPool.Id;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
for (const user of this.users.values()) {
|
|
583
|
-
if (user.Username === username && user.UserPoolId === targetUserPoolId) {
|
|
584
|
-
return user;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return null;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
normalizeUserAttributes(attributes) {
|
|
592
|
-
const normalized = {};
|
|
593
|
-
for (const attr of attributes) {
|
|
594
|
-
normalized[attr.Name] = attr.Value;
|
|
595
|
-
}
|
|
596
|
-
return normalized;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
formatUserAttributes(attributes) {
|
|
600
|
-
return Object.entries(attributes).map(([Name, Value]) => ({ Name, Value }));
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
hashPassword(password) {
|
|
604
|
-
// Simulação de hash (não usar em produção real)
|
|
605
|
-
return crypto.createHash('sha256').update(password).digest('hex');
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
verifyPassword(password, hash) {
|
|
609
|
-
return this.hashPassword(password) === hash;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// ============ Identity Pool Operations ============
|
|
613
|
-
|
|
614
|
-
createIdentityPool(params) {
|
|
615
|
-
const { IdentityPoolName, AllowUnauthenticatedIdentities, SupportedLoginProviders, CognitoIdentityProviders } = params;
|
|
616
|
-
|
|
617
|
-
const identityPoolId = `local:${IdentityPoolName}_${Date.now()}`;
|
|
618
|
-
const identityPool = {
|
|
619
|
-
IdentityPoolId: identityPoolId,
|
|
620
|
-
IdentityPoolName: IdentityPoolName,
|
|
621
|
-
AllowUnauthenticatedIdentities: AllowUnauthenticatedIdentities || false,
|
|
622
|
-
SupportedLoginProviders: SupportedLoginProviders || {},
|
|
623
|
-
CognitoIdentityProviders: CognitoIdentityProviders || [],
|
|
624
|
-
Identities: new Map()
|
|
625
|
-
};
|
|
626
|
-
|
|
627
|
-
this.identityPools.set(identityPoolId, identityPool);
|
|
628
|
-
this.persistIdentityPools();
|
|
629
|
-
|
|
630
|
-
logger.debug(`✅ Identity Pool criado: ${IdentityPoolName} (${identityPoolId})`);
|
|
631
|
-
|
|
632
|
-
return {
|
|
633
|
-
IdentityPoolId: identityPoolId,
|
|
634
|
-
IdentityPoolName: identityPoolName,
|
|
635
|
-
AllowUnauthenticatedIdentities: identityPool.AllowUnauthenticatedIdentities
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
getId(params) {
|
|
640
|
-
const { IdentityPoolId, Logins } = params;
|
|
641
|
-
const identityPool = this.identityPools.get(IdentityPoolId);
|
|
642
|
-
|
|
643
|
-
if (!identityPool) {
|
|
644
|
-
throw new Error(`Identity pool ${IdentityPoolId} not found`);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
let identityId = null;
|
|
648
|
-
|
|
649
|
-
if (Logins) {
|
|
650
|
-
// Procura identidade existente com os logins fornecidos
|
|
651
|
-
for (const [id, identity] of identityPool.Identities) {
|
|
652
|
-
if (identity.Logins && this.matchesLogins(identity.Logins, Logins)) {
|
|
653
|
-
identityId = id;
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
if (!identityId) {
|
|
660
|
-
identityId = uuidv4();
|
|
661
|
-
identityPool.Identities.set(identityId, {
|
|
662
|
-
IdentityId: identityId,
|
|
663
|
-
Logins: Logins || {},
|
|
664
|
-
CreationDate: new Date().toISOString(),
|
|
665
|
-
LastModifiedDate: new Date().toISOString()
|
|
666
|
-
});
|
|
667
|
-
this.persistIdentityPools();
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
return {
|
|
671
|
-
IdentityId: identityId
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
getCredentialsForIdentity(params) {
|
|
676
|
-
const { IdentityId, Logins } = params;
|
|
677
|
-
const identityPool = this.findIdentityPoolByIdentityId(IdentityId);
|
|
678
|
-
|
|
679
|
-
if (!identityPool) {
|
|
680
|
-
throw new Error(`Identity ${IdentityId} not found`);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
const identity = identityPool.Identities.get(IdentityId);
|
|
684
|
-
if (!identity) {
|
|
685
|
-
throw new Error(`Identity ${IdentityId} not found in pool`);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// Gera credenciais temporárias (simuladas)
|
|
689
|
-
const credentials = {
|
|
690
|
-
AccessKeyId: `AKIA${crypto.randomBytes(16).toString('hex').toUpperCase()}`,
|
|
691
|
-
SecretKey: crypto.randomBytes(32).toString('hex'),
|
|
692
|
-
SessionToken: crypto.randomBytes(64).toString('base64'),
|
|
693
|
-
Expiration: new Date(Date.now() + 3600000).toISOString()
|
|
694
|
-
};
|
|
695
|
-
|
|
696
|
-
return {
|
|
697
|
-
Credentials: credentials,
|
|
698
|
-
IdentityId: IdentityId
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
findIdentityPoolByIdentityId(identityId) {
|
|
703
|
-
for (const pool of this.identityPools.values()) {
|
|
704
|
-
if (pool.Identities.has(identityId)) {
|
|
705
|
-
return pool;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
return null;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
matchesLogins(existingLogins, newLogins) {
|
|
712
|
-
const existingKeys = Object.keys(existingLogins);
|
|
713
|
-
const newKeys = Object.keys(newLogins);
|
|
714
|
-
|
|
715
|
-
if (existingKeys.length !== newKeys.length) return false;
|
|
716
|
-
|
|
717
|
-
for (const key of existingKeys) {
|
|
718
|
-
if (existingLogins[key] !== newLogins[key]) {
|
|
719
|
-
return false;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
return true;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
// ============ Persistence ============
|
|
727
|
-
|
|
728
|
-
loadUserPools() {
|
|
729
|
-
const saved = this.store.read('__userpools__');
|
|
730
|
-
if (saved) {
|
|
731
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
732
|
-
// Reconstitui Maps
|
|
733
|
-
data.Clients = new Map(Object.entries(data.Clients || {}));
|
|
734
|
-
data.Groups = new Map(Object.entries(data.Groups || {}));
|
|
735
|
-
data.IdentityProviders = new Map(Object.entries(data.IdentityProviders || {}));
|
|
736
|
-
data.ResourceServers = new Map(Object.entries(data.ResourceServers || {}));
|
|
737
|
-
this.userPools.set(id, data);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
loadIdentityPools() {
|
|
743
|
-
const saved = this.store.read('__identitypools__');
|
|
744
|
-
if (saved) {
|
|
745
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
746
|
-
data.Identities = new Map(Object.entries(data.Identities || {}));
|
|
747
|
-
this.identityPools.set(id, data);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
loadUsers() {
|
|
753
|
-
const saved = this.store.read('__users__');
|
|
754
|
-
if (saved) {
|
|
755
|
-
for (const [id, user] of Object.entries(saved)) {
|
|
756
|
-
this.users.set(id, user);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
loadSessions() {
|
|
762
|
-
const saved = this.store.read('__sessions__');
|
|
763
|
-
if (saved) {
|
|
764
|
-
for (const [id, session] of Object.entries(saved)) {
|
|
765
|
-
this.sessions.set(id, session);
|
|
766
|
-
this.accessTokens.set(session.AccessToken, session);
|
|
767
|
-
this.refreshTokens.set(session.RefreshToken, session);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
persistUserPools() {
|
|
773
|
-
const poolsObj = {};
|
|
774
|
-
for (const [id, pool] of this.userPools.entries()) {
|
|
775
|
-
poolsObj[id] = {
|
|
776
|
-
...pool,
|
|
777
|
-
Clients: Object.fromEntries(pool.Clients),
|
|
778
|
-
Groups: Object.fromEntries(pool.Groups),
|
|
779
|
-
IdentityProviders: Object.fromEntries(pool.IdentityProviders),
|
|
780
|
-
ResourceServers: Object.fromEntries(pool.ResourceServers)
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
this.store.write('__userpools__', poolsObj);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
persistIdentityPools() {
|
|
787
|
-
const poolsObj = {};
|
|
788
|
-
for (const [id, pool] of this.identityPools.entries()) {
|
|
789
|
-
poolsObj[id] = {
|
|
790
|
-
...pool,
|
|
791
|
-
Identities: Object.fromEntries(pool.Identities)
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
this.store.write('__identitypools__', poolsObj);
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
persistUsers() {
|
|
798
|
-
const usersObj = {};
|
|
799
|
-
for (const [id, user] of this.users.entries()) {
|
|
800
|
-
usersObj[id] = user;
|
|
801
|
-
}
|
|
802
|
-
this.store.write('__users__', usersObj);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
persistSessions() {
|
|
806
|
-
const sessionsObj = {};
|
|
807
|
-
for (const [id, session] of this.sessions.entries()) {
|
|
808
|
-
sessionsObj[id] = session;
|
|
809
|
-
}
|
|
810
|
-
this.store.write('__sessions__', sessionsObj);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
async reset() {
|
|
814
|
-
this.userPools.clear();
|
|
815
|
-
this.identityPools.clear();
|
|
816
|
-
this.users.clear();
|
|
817
|
-
this.sessions.clear();
|
|
818
|
-
this.accessTokens.clear();
|
|
819
|
-
this.refreshTokens.clear();
|
|
820
|
-
|
|
821
|
-
this.persistUserPools();
|
|
822
|
-
this.persistIdentityPools();
|
|
823
|
-
this.persistUsers();
|
|
824
|
-
this.persistSessions();
|
|
825
|
-
|
|
826
|
-
logger.debug('Cognito: Todos os dados resetados');
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
// ============ Stats ============
|
|
830
|
-
|
|
831
|
-
getUserPoolsCount() {
|
|
832
|
-
return this.userPools.size;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
getTotalUsersCount() {
|
|
836
|
-
return this.users.size;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
getIdentityPoolsCount() {
|
|
840
|
-
return this.identityPools.size;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
getActiveSessionsCount() {
|
|
844
|
-
return this.sessions.size;
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
module.exports = CognitoSimulator;
|