@friggframework/core 2.0.0-next.54 → 2.0.0-next.56
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/application/commands/credential-commands.js +1 -1
- package/core/create-handler.js +12 -0
- package/credential/repositories/credential-repository-documentdb.js +81 -77
- package/credential/repositories/credential-repository-mongo.js +16 -54
- package/credential/repositories/credential-repository-postgres.js +14 -41
- package/credential/use-cases/get-credential-for-user.js +7 -3
- package/database/encryption/README.md +126 -21
- package/database/encryption/encryption-schema-registry.js +83 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +2 -1
- package/errors/index.js +2 -0
- package/integrations/integration-router.js +6 -6
- package/integrations/repositories/integration-mapping-repository-documentdb.js +178 -33
- package/integrations/repositories/integration-repository-documentdb.js +21 -0
- package/integrations/repositories/process-repository-documentdb.js +143 -41
- package/modules/requester/api-key.js +24 -8
- package/package.json +5 -5
- package/token/repositories/token-repository-documentdb.js +20 -8
- package/token/repositories/token-repository-mongo.js +10 -3
- package/token/repositories/token-repository-postgres.js +10 -3
- package/user/repositories/user-repository-documentdb.js +177 -37
- package/user/repositories/user-repository-mongo.js +3 -2
- package/user/repositories/user-repository-postgres.js +3 -2
- package/user/use-cases/login-user.js +1 -1
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
deleteMany,
|
|
11
11
|
} = require('../../database/documentdb-utils');
|
|
12
12
|
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
13
|
+
const { ClientSafeError } = require('../../errors');
|
|
13
14
|
|
|
14
15
|
const BCRYPT_ROUNDS = 10;
|
|
15
16
|
|
|
@@ -30,25 +31,36 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
|
|
|
30
31
|
created: now,
|
|
31
32
|
};
|
|
32
33
|
const insertedId = await insertOne(this.prisma, 'Token', document);
|
|
33
|
-
const created = await findOne(this.prisma, 'Token', {
|
|
34
|
+
const created = await findOne(this.prisma, 'Token', {
|
|
35
|
+
_id: insertedId,
|
|
36
|
+
});
|
|
34
37
|
return this._mapToken(created);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
async validateAndGetToken(tokenObj) {
|
|
38
41
|
const objectId = toObjectId(tokenObj.id);
|
|
39
42
|
if (!objectId) {
|
|
40
|
-
throw new
|
|
43
|
+
throw new ClientSafeError(
|
|
44
|
+
'Invalid Token: Token does not exist',
|
|
45
|
+
401
|
|
46
|
+
);
|
|
41
47
|
}
|
|
42
48
|
const record = await findOne(this.prisma, 'Token', { _id: objectId });
|
|
43
49
|
if (!record) {
|
|
44
|
-
throw new
|
|
50
|
+
throw new ClientSafeError(
|
|
51
|
+
'Invalid Token: Token does not exist',
|
|
52
|
+
401
|
|
53
|
+
);
|
|
45
54
|
}
|
|
46
55
|
const isValid = await bcrypt.compare(tokenObj.token, record.token);
|
|
47
56
|
if (!isValid) {
|
|
48
|
-
throw new
|
|
57
|
+
throw new ClientSafeError(
|
|
58
|
+
'Invalid Token: Token does not match',
|
|
59
|
+
401
|
|
60
|
+
);
|
|
49
61
|
}
|
|
50
62
|
if (record.expires && new Date(record.expires) < new Date()) {
|
|
51
|
-
throw new
|
|
63
|
+
throw new ClientSafeError('Invalid Token: Token is expired', 401);
|
|
52
64
|
}
|
|
53
65
|
return this._mapToken(record);
|
|
54
66
|
}
|
|
@@ -86,7 +98,9 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
|
|
|
86
98
|
async deleteTokensByUserId(userId) {
|
|
87
99
|
const objectId = toObjectId(userId);
|
|
88
100
|
if (!objectId) return { acknowledged: true, deletedCount: 0 };
|
|
89
|
-
const result = await deleteMany(this.prisma, 'Token', {
|
|
101
|
+
const result = await deleteMany(this.prisma, 'Token', {
|
|
102
|
+
userId: objectId,
|
|
103
|
+
});
|
|
90
104
|
const deleted = result?.n ?? 0;
|
|
91
105
|
return { acknowledged: true, deletedCount: deleted };
|
|
92
106
|
}
|
|
@@ -121,5 +135,3 @@ class TokenRepositoryDocumentDB extends TokenRepositoryInterface {
|
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
module.exports = { TokenRepositoryDocumentDB };
|
|
124
|
-
|
|
125
|
-
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
2
|
const bcrypt = require('bcryptjs');
|
|
3
3
|
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
4
|
+
const { ClientSafeError } = require('../../errors');
|
|
4
5
|
|
|
5
6
|
const BCRYPT_ROUNDS = 10;
|
|
6
7
|
|
|
@@ -58,7 +59,10 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
|
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
if (!sessionToken) {
|
|
61
|
-
throw new
|
|
62
|
+
throw new ClientSafeError(
|
|
63
|
+
'Invalid Token: Token does not exist',
|
|
64
|
+
401
|
|
65
|
+
);
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
// Verify token hash matches
|
|
@@ -67,7 +71,10 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
|
|
|
67
71
|
sessionToken.token
|
|
68
72
|
);
|
|
69
73
|
if (!isValid) {
|
|
70
|
-
throw new
|
|
74
|
+
throw new ClientSafeError(
|
|
75
|
+
'Invalid Token: Token does not match',
|
|
76
|
+
401
|
|
77
|
+
);
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
// Check if token is expired
|
|
@@ -75,7 +82,7 @@ class TokenRepositoryMongo extends TokenRepositoryInterface {
|
|
|
75
82
|
sessionToken.expires &&
|
|
76
83
|
new Date(sessionToken.expires) < new Date()
|
|
77
84
|
) {
|
|
78
|
-
throw new
|
|
85
|
+
throw new ClientSafeError('Invalid Token: Token is expired', 401);
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
return sessionToken;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { prisma } = require('../../database/prisma');
|
|
2
2
|
const bcrypt = require('bcryptjs');
|
|
3
3
|
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
4
|
+
const { ClientSafeError } = require('../../errors');
|
|
4
5
|
|
|
5
6
|
const BCRYPT_ROUNDS = 10;
|
|
6
7
|
|
|
@@ -92,7 +93,10 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
|
|
|
92
93
|
});
|
|
93
94
|
|
|
94
95
|
if (!sessionToken) {
|
|
95
|
-
throw new
|
|
96
|
+
throw new ClientSafeError(
|
|
97
|
+
'Invalid Token: Token does not exist',
|
|
98
|
+
401
|
|
99
|
+
);
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
// Verify token hash matches
|
|
@@ -101,7 +105,10 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
|
|
|
101
105
|
sessionToken.token
|
|
102
106
|
);
|
|
103
107
|
if (!isValid) {
|
|
104
|
-
throw new
|
|
108
|
+
throw new ClientSafeError(
|
|
109
|
+
'Invalid Token: Token does not match',
|
|
110
|
+
401
|
|
111
|
+
);
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
// Check if token is expired
|
|
@@ -109,7 +116,7 @@ class TokenRepositoryPostgres extends TokenRepositoryInterface {
|
|
|
109
116
|
sessionToken.expires &&
|
|
110
117
|
new Date(sessionToken.expires) < new Date()
|
|
111
118
|
) {
|
|
112
|
-
throw new
|
|
119
|
+
throw new ClientSafeError('Invalid Token: Token is expired', 401);
|
|
113
120
|
}
|
|
114
121
|
|
|
115
122
|
return this._convertTokenIds(sessionToken);
|
|
@@ -8,9 +8,14 @@ const {
|
|
|
8
8
|
updateOne,
|
|
9
9
|
deleteOne,
|
|
10
10
|
} = require('../../database/documentdb-utils');
|
|
11
|
-
const {
|
|
11
|
+
const {
|
|
12
|
+
createTokenRepository,
|
|
13
|
+
} = require('../../token/repositories/token-repository-factory');
|
|
12
14
|
const { UserRepositoryInterface } = require('./user-repository-interface');
|
|
13
|
-
const {
|
|
15
|
+
const { ClientSafeError } = require('../../errors');
|
|
16
|
+
const {
|
|
17
|
+
DocumentDBEncryptionService,
|
|
18
|
+
} = require('../../database/documentdb-encryption-service');
|
|
14
19
|
|
|
15
20
|
/**
|
|
16
21
|
* User repository for DocumentDB.
|
|
@@ -30,8 +35,11 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
async getSessionToken(token) {
|
|
33
|
-
const jsonToken =
|
|
34
|
-
|
|
38
|
+
const jsonToken =
|
|
39
|
+
this.tokenRepository.getJSONTokenFromBase64BufferToken(token);
|
|
40
|
+
const sessionToken = await this.tokenRepository.validateAndGetToken(
|
|
41
|
+
jsonToken
|
|
42
|
+
);
|
|
35
43
|
return sessionToken;
|
|
36
44
|
}
|
|
37
45
|
|
|
@@ -40,7 +48,10 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
40
48
|
_id: toObjectId(userId),
|
|
41
49
|
type: 'ORGANIZATION',
|
|
42
50
|
});
|
|
43
|
-
const decrypted = await this.encryptionService.decryptFields(
|
|
51
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
52
|
+
'User',
|
|
53
|
+
doc
|
|
54
|
+
);
|
|
44
55
|
return this._mapUser(decrypted);
|
|
45
56
|
}
|
|
46
57
|
|
|
@@ -49,7 +60,10 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
49
60
|
_id: toObjectId(userId),
|
|
50
61
|
type: 'INDIVIDUAL',
|
|
51
62
|
});
|
|
52
|
-
const decrypted = await this.encryptionService.decryptFields(
|
|
63
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
64
|
+
'User',
|
|
65
|
+
doc
|
|
66
|
+
);
|
|
53
67
|
return this._mapUser(decrypted);
|
|
54
68
|
}
|
|
55
69
|
|
|
@@ -59,7 +73,10 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
59
73
|
rawToken,
|
|
60
74
|
minutes
|
|
61
75
|
);
|
|
62
|
-
return this.tokenRepository.createBase64BufferToken(
|
|
76
|
+
return this.tokenRepository.createBase64BufferToken(
|
|
77
|
+
createdToken,
|
|
78
|
+
rawToken
|
|
79
|
+
);
|
|
63
80
|
}
|
|
64
81
|
|
|
65
82
|
async createIndividualUser(params) {
|
|
@@ -84,7 +101,7 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
84
101
|
params.hashword !== ''
|
|
85
102
|
) {
|
|
86
103
|
if (typeof params.hashword !== 'string') {
|
|
87
|
-
throw new
|
|
104
|
+
throw new ClientSafeError('Password must be a string', 400);
|
|
88
105
|
}
|
|
89
106
|
|
|
90
107
|
if (params.hashword.startsWith('$2')) {
|
|
@@ -98,28 +115,41 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
98
115
|
}
|
|
99
116
|
|
|
100
117
|
// Encrypt sensitive fields before insert
|
|
101
|
-
const encryptedDocument = await this.encryptionService.encryptFields(
|
|
102
|
-
|
|
118
|
+
const encryptedDocument = await this.encryptionService.encryptFields(
|
|
119
|
+
'User',
|
|
120
|
+
document
|
|
121
|
+
);
|
|
122
|
+
const insertedId = await insertOne(
|
|
123
|
+
this.prisma,
|
|
124
|
+
'User',
|
|
125
|
+
encryptedDocument
|
|
126
|
+
);
|
|
103
127
|
const created = await findOne(this.prisma, 'User', { _id: insertedId });
|
|
104
128
|
|
|
105
129
|
// Defensive check: verify document was found after insert
|
|
106
130
|
if (!created) {
|
|
107
|
-
console.error(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
131
|
+
console.error(
|
|
132
|
+
'[UserRepositoryDocumentDB] User not found after insert',
|
|
133
|
+
{
|
|
134
|
+
insertedId: fromObjectId(insertedId),
|
|
135
|
+
params: {
|
|
136
|
+
username: params.username,
|
|
137
|
+
appUserId: params.appUserId,
|
|
138
|
+
email: params.email,
|
|
139
|
+
},
|
|
113
140
|
}
|
|
114
|
-
|
|
141
|
+
);
|
|
115
142
|
throw new Error(
|
|
116
143
|
'Failed to create individual user: Document not found after insert. ' +
|
|
117
|
-
|
|
144
|
+
'This indicates a database consistency issue.'
|
|
118
145
|
);
|
|
119
146
|
}
|
|
120
147
|
|
|
121
148
|
// Decrypt sensitive fields after read
|
|
122
|
-
const decrypted = await this.encryptionService.decryptFields(
|
|
149
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
150
|
+
'User',
|
|
151
|
+
created
|
|
152
|
+
);
|
|
123
153
|
|
|
124
154
|
return this._mapUser(decrypted);
|
|
125
155
|
}
|
|
@@ -134,9 +164,42 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
134
164
|
updatedAt: now,
|
|
135
165
|
};
|
|
136
166
|
|
|
137
|
-
|
|
167
|
+
// Encrypt sensitive fields before insert (consistency with individual user)
|
|
168
|
+
const encryptedDocument = await this.encryptionService.encryptFields(
|
|
169
|
+
'User',
|
|
170
|
+
document
|
|
171
|
+
);
|
|
172
|
+
const insertedId = await insertOne(
|
|
173
|
+
this.prisma,
|
|
174
|
+
'User',
|
|
175
|
+
encryptedDocument
|
|
176
|
+
);
|
|
138
177
|
const created = await findOne(this.prisma, 'User', { _id: insertedId });
|
|
139
|
-
|
|
178
|
+
|
|
179
|
+
// Defensive check: verify document was found after insert
|
|
180
|
+
if (!created) {
|
|
181
|
+
console.error(
|
|
182
|
+
'[UserRepositoryDocumentDB] Organization user not found after insert',
|
|
183
|
+
{
|
|
184
|
+
insertedId: fromObjectId(insertedId),
|
|
185
|
+
params: {
|
|
186
|
+
appOrgId: params.appOrgId,
|
|
187
|
+
name: params.name,
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
throw new Error(
|
|
192
|
+
'Failed to create organization user: Document not found after insert. ' +
|
|
193
|
+
'This indicates a database consistency issue.'
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Decrypt sensitive fields after read
|
|
198
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
199
|
+
'User',
|
|
200
|
+
created
|
|
201
|
+
);
|
|
202
|
+
return this._mapUser(decrypted);
|
|
140
203
|
}
|
|
141
204
|
|
|
142
205
|
async findIndividualUserByUsername(username) {
|
|
@@ -144,7 +207,10 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
144
207
|
type: 'INDIVIDUAL',
|
|
145
208
|
username,
|
|
146
209
|
});
|
|
147
|
-
const decrypted = await this.encryptionService.decryptFields(
|
|
210
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
211
|
+
'User',
|
|
212
|
+
doc
|
|
213
|
+
);
|
|
148
214
|
return this._mapUser(decrypted);
|
|
149
215
|
}
|
|
150
216
|
|
|
@@ -153,7 +219,10 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
153
219
|
type: 'INDIVIDUAL',
|
|
154
220
|
appUserId,
|
|
155
221
|
});
|
|
156
|
-
const decrypted = await this.encryptionService.decryptFields(
|
|
222
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
223
|
+
'User',
|
|
224
|
+
doc
|
|
225
|
+
);
|
|
157
226
|
return this._mapUser(decrypted);
|
|
158
227
|
}
|
|
159
228
|
|
|
@@ -162,13 +231,21 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
162
231
|
type: 'ORGANIZATION',
|
|
163
232
|
appOrgId,
|
|
164
233
|
});
|
|
165
|
-
const decrypted = await this.encryptionService.decryptFields(
|
|
234
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
235
|
+
'User',
|
|
236
|
+
doc
|
|
237
|
+
);
|
|
166
238
|
return this._mapUser(decrypted);
|
|
167
239
|
}
|
|
168
240
|
|
|
169
241
|
async findUserById(userId) {
|
|
170
|
-
const doc = await findOne(this.prisma, 'User', {
|
|
171
|
-
|
|
242
|
+
const doc = await findOne(this.prisma, 'User', {
|
|
243
|
+
_id: toObjectId(userId),
|
|
244
|
+
});
|
|
245
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
246
|
+
'User',
|
|
247
|
+
doc
|
|
248
|
+
);
|
|
172
249
|
return this._mapUser(decrypted);
|
|
173
250
|
}
|
|
174
251
|
|
|
@@ -177,7 +254,10 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
177
254
|
type: 'INDIVIDUAL',
|
|
178
255
|
email,
|
|
179
256
|
});
|
|
180
|
-
const decrypted = await this.encryptionService.decryptFields(
|
|
257
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
258
|
+
'User',
|
|
259
|
+
doc
|
|
260
|
+
);
|
|
181
261
|
return this._mapUser(decrypted);
|
|
182
262
|
}
|
|
183
263
|
|
|
@@ -189,7 +269,10 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
189
269
|
payload.updatedAt = new Date();
|
|
190
270
|
|
|
191
271
|
// Encrypt sensitive fields before update
|
|
192
|
-
const encryptedPayload = await this.encryptionService.encryptFields(
|
|
272
|
+
const encryptedPayload = await this.encryptionService.encryptFields(
|
|
273
|
+
'User',
|
|
274
|
+
payload
|
|
275
|
+
);
|
|
193
276
|
|
|
194
277
|
await updateOne(
|
|
195
278
|
this.prisma,
|
|
@@ -199,7 +282,26 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
199
282
|
);
|
|
200
283
|
|
|
201
284
|
const updated = await findOne(this.prisma, 'User', { _id: objectId });
|
|
202
|
-
|
|
285
|
+
|
|
286
|
+
// Defensive check: verify document was found after update
|
|
287
|
+
if (!updated) {
|
|
288
|
+
console.error(
|
|
289
|
+
'[UserRepositoryDocumentDB] Individual user not found after update',
|
|
290
|
+
{
|
|
291
|
+
userId: fromObjectId(objectId),
|
|
292
|
+
updates,
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
throw new Error(
|
|
296
|
+
'Failed to update individual user: Document not found after update. ' +
|
|
297
|
+
'This indicates a database consistency issue.'
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
302
|
+
'User',
|
|
303
|
+
updated
|
|
304
|
+
);
|
|
203
305
|
return this._mapUser(decrypted);
|
|
204
306
|
}
|
|
205
307
|
|
|
@@ -209,15 +311,38 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
209
311
|
|
|
210
312
|
const payload = { ...updates, updatedAt: new Date() };
|
|
211
313
|
|
|
314
|
+
const encryptedPayload = await this.encryptionService.encryptFields(
|
|
315
|
+
'User',
|
|
316
|
+
payload
|
|
317
|
+
);
|
|
318
|
+
|
|
212
319
|
await updateOne(
|
|
213
320
|
this.prisma,
|
|
214
321
|
'User',
|
|
215
322
|
{ _id: objectId, type: 'ORGANIZATION' },
|
|
216
|
-
{ $set:
|
|
323
|
+
{ $set: encryptedPayload }
|
|
217
324
|
);
|
|
218
325
|
|
|
219
326
|
const updated = await findOne(this.prisma, 'User', { _id: objectId });
|
|
220
|
-
|
|
327
|
+
|
|
328
|
+
if (!updated) {
|
|
329
|
+
console.error(
|
|
330
|
+
'[UserRepositoryDocumentDB] Organization user not found after update',
|
|
331
|
+
{
|
|
332
|
+
userId: fromObjectId(objectId),
|
|
333
|
+
updates,
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
throw new Error(
|
|
337
|
+
'Failed to update organization user: Document not found after update. ' +
|
|
338
|
+
'This indicates a database consistency issue.'
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const decrypted = await this.encryptionService.decryptFields(
|
|
343
|
+
'User',
|
|
344
|
+
updated
|
|
345
|
+
);
|
|
221
346
|
return this._mapUser(decrypted);
|
|
222
347
|
}
|
|
223
348
|
|
|
@@ -232,7 +357,9 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
232
357
|
|
|
233
358
|
_mapUser(doc) {
|
|
234
359
|
if (!doc) {
|
|
235
|
-
console.warn(
|
|
360
|
+
console.warn(
|
|
361
|
+
'[UserRepositoryDocumentDB] _mapUser received null/undefined document'
|
|
362
|
+
);
|
|
236
363
|
return null;
|
|
237
364
|
}
|
|
238
365
|
|
|
@@ -244,11 +371,13 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
244
371
|
username: doc?.username ?? null,
|
|
245
372
|
hashword: doc?.hashword ?? null,
|
|
246
373
|
appUserId: doc?.appUserId ?? null,
|
|
247
|
-
organizationId: doc?.organizationId
|
|
374
|
+
organizationId: doc?.organizationId
|
|
375
|
+
? fromObjectId(doc.organizationId)
|
|
376
|
+
: null,
|
|
248
377
|
appOrgId: doc?.appOrgId ?? null,
|
|
249
378
|
name: doc?.name ?? null,
|
|
250
|
-
createdAt: doc?.createdAt
|
|
251
|
-
updatedAt: doc?.updatedAt
|
|
379
|
+
createdAt: this._parseDate(doc?.createdAt),
|
|
380
|
+
updatedAt: this._parseDate(doc?.updatedAt),
|
|
252
381
|
};
|
|
253
382
|
}
|
|
254
383
|
|
|
@@ -261,7 +390,7 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
261
390
|
payload.hashword !== ''
|
|
262
391
|
) {
|
|
263
392
|
if (typeof payload.hashword !== 'string') {
|
|
264
|
-
throw new
|
|
393
|
+
throw new ClientSafeError('Password must be a string', 400);
|
|
265
394
|
}
|
|
266
395
|
|
|
267
396
|
if (payload.hashword.startsWith('$2')) {
|
|
@@ -286,7 +415,18 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
|
|
|
286
415
|
|
|
287
416
|
return payload;
|
|
288
417
|
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Parse date value safely, returning undefined for invalid dates
|
|
421
|
+
* @private
|
|
422
|
+
* @param {*} value - Date value from database
|
|
423
|
+
* @returns {Date|undefined} Valid Date object or undefined
|
|
424
|
+
*/
|
|
425
|
+
_parseDate(value) {
|
|
426
|
+
if (!value) return undefined;
|
|
427
|
+
const date = new Date(value);
|
|
428
|
+
return isNaN(date.getTime()) ? undefined : date;
|
|
429
|
+
}
|
|
289
430
|
}
|
|
290
431
|
|
|
291
432
|
module.exports = { UserRepositoryDocumentDB };
|
|
292
|
-
|
|
@@ -4,6 +4,7 @@ const {
|
|
|
4
4
|
createTokenRepository,
|
|
5
5
|
} = require('../../token/repositories/token-repository-factory');
|
|
6
6
|
const { UserRepositoryInterface } = require('./user-repository-interface');
|
|
7
|
+
const { ClientSafeError } = require('../../errors');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* MongoDB User Repository Adapter
|
|
@@ -113,7 +114,7 @@ class UserRepositoryMongo extends UserRepositoryInterface {
|
|
|
113
114
|
params.hashword !== ''
|
|
114
115
|
) {
|
|
115
116
|
if (typeof params.hashword !== 'string') {
|
|
116
|
-
throw new
|
|
117
|
+
throw new ClientSafeError('Password must be a string', 400);
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
// Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
|
|
@@ -235,7 +236,7 @@ class UserRepositoryMongo extends UserRepositoryInterface {
|
|
|
235
236
|
data.hashword !== ''
|
|
236
237
|
) {
|
|
237
238
|
if (typeof data.hashword !== 'string') {
|
|
238
|
-
throw new
|
|
239
|
+
throw new ClientSafeError('Password must be a string', 400);
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
// Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
|
|
@@ -4,6 +4,7 @@ const {
|
|
|
4
4
|
createTokenRepository,
|
|
5
5
|
} = require('../../token/repositories/token-repository-factory');
|
|
6
6
|
const { UserRepositoryInterface } = require('./user-repository-interface');
|
|
7
|
+
const { ClientSafeError } = require('../../errors');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* PostgreSQL User Repository Adapter
|
|
@@ -150,7 +151,7 @@ class UserRepositoryPostgres extends UserRepositoryInterface {
|
|
|
150
151
|
params.hashword !== ''
|
|
151
152
|
) {
|
|
152
153
|
if (typeof params.hashword !== 'string') {
|
|
153
|
-
throw new
|
|
154
|
+
throw new ClientSafeError('Password must be a string', 400);
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
// Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
|
|
@@ -290,7 +291,7 @@ class UserRepositoryPostgres extends UserRepositoryInterface {
|
|
|
290
291
|
data.hashword !== ''
|
|
291
292
|
) {
|
|
292
293
|
if (typeof data.hashword !== 'string') {
|
|
293
|
-
throw new
|
|
294
|
+
throw new ClientSafeError('Password must be a string', 400);
|
|
294
295
|
}
|
|
295
296
|
|
|
296
297
|
// Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
|