@flusys/nestjs-auth 9.1.1 → 9.2.0
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 +41 -9
- package/controllers/branch.controller.d.ts +1 -0
- package/controllers/company.controller.d.ts +1 -0
- package/controllers/social-auth-config.controller.d.ts +1 -0
- package/controllers/user.controller.d.ts +1 -0
- package/fesm/118.js +1 -1
- package/fesm/120.js +1 -1
- package/fesm/24.js +438 -264
- package/fesm/360.js +8 -2
- package/fesm/370.js +1 -0
- package/fesm/helpers/index.js +61 -20
- package/fesm/index.js +7 -3
- package/fesm/interceptors/index.js +1 -0
- package/helpers/cache-invalidation.helper.d.ts +3 -0
- package/helpers/token.helper.d.ts +4 -2
- package/interfaces/authentication.interface.d.ts +1 -0
- package/package.json +3 -3
- package/services/auth-email.service.d.ts +7 -2
- package/services/authentication.service.d.ts +3 -1
- package/services/branch.service.d.ts +1 -0
- package/services/company-selection-session.service.d.ts +7 -6
- package/services/company.service.d.ts +1 -0
- package/services/social-auth-config.service.d.ts +1 -0
- package/services/social-auth.service.d.ts +4 -1
- package/services/user-permission.service.d.ts +4 -3
- package/services/user.service.d.ts +3 -0
package/fesm/24.js
CHANGED
|
@@ -254,16 +254,39 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
254
254
|
|
|
255
255
|
|
|
256
256
|
},
|
|
257
|
-
|
|
257
|
+
37(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
258
|
+
/* import */ var _flusys_nestjs_shared_modules__rspack_import_0 = __webpack_require__(1236);
|
|
259
|
+
/* import */ var _flusys_nestjs_shared_utils__rspack_import_1 = __webpack_require__(9216);
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
const utils = new _flusys_nestjs_shared_modules__rspack_import_0.UtilsService();
|
|
263
|
+
/**
|
|
264
|
+
* Bumps the cache stamp of every entity a service wrote outside its own ApiService (repository,
|
|
265
|
+
* query runner, cascade), so every service caching those entities re-reads them. Call after commit.
|
|
266
|
+
*/ async function invalidateEntityCaches(cache, dataSourceProvider, entityNames) {
|
|
267
|
+
if (!cache) return;
|
|
268
|
+
const tenantId = (0,_flusys_nestjs_shared_utils__rspack_import_1.cacheTenantId)(dataSourceProvider);
|
|
269
|
+
await Promise.all(entityNames.map((name)=>utils.clearCache(name, cache, tenantId)));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
273
|
+
i: () => (invalidateEntityCaches)
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
},
|
|
278
|
+
5634(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
258
279
|
|
|
259
280
|
// EXPORTS
|
|
260
281
|
__webpack_require__.d(__webpack_exports__, {
|
|
261
|
-
Vg: () => (/* reexport */ generateAccessToken),
|
|
262
|
-
e: () => (/* reexport */ generateTokens),
|
|
282
|
+
Vg: () => (/* reexport */ token_helper/* .generateAccessToken */.Vg),
|
|
283
|
+
e: () => (/* reexport */ token_helper/* .generateTokens */.e),
|
|
284
|
+
PV: () => (/* reexport */ token_helper/* .getSessionsRevokedAtMs */.PV),
|
|
285
|
+
lL: () => (/* reexport */ token_helper/* .isIssuedBeforeRevocation */.lL),
|
|
263
286
|
vR: () => (/* reexport */ branch_ancestry_helper/* .loadCompanyBranchNodes */.v),
|
|
264
287
|
xu: () => (/* reexport */ branch_ancestry_helper/* .loadDisplayParentIds */.x),
|
|
265
|
-
om: () => (/* reexport */ parseExpiryToMs),
|
|
266
|
-
XP: () => (/* reexport */ revokeUserSessions),
|
|
288
|
+
om: () => (/* reexport */ token_helper/* .parseExpiryToMs */.om),
|
|
289
|
+
XP: () => (/* reexport */ token_helper/* .revokeUserSessions */.XP),
|
|
267
290
|
QH: () => (/* reexport */ toBranchInfo),
|
|
268
291
|
$C: () => (/* reexport */ toCompanyInfo),
|
|
269
292
|
FV: () => (/* reexport */ toUserInfo)
|
|
@@ -298,14 +321,23 @@ function toBranchInfo(branch) {
|
|
|
298
321
|
|
|
299
322
|
// EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/branch-ancestry.helper.ts
|
|
300
323
|
var branch_ancestry_helper = __webpack_require__(7526);
|
|
301
|
-
// EXTERNAL MODULE: ./projects/nestjs-auth/src/
|
|
302
|
-
var
|
|
303
|
-
;// CONCATENATED MODULE: ./projects/nestjs-auth/src/helpers/
|
|
324
|
+
// EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/token.helper.ts
|
|
325
|
+
var token_helper = __webpack_require__(1379);
|
|
326
|
+
;// CONCATENATED MODULE: ./projects/nestjs-auth/src/helpers/index.ts
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
|
|
304
331
|
|
|
305
|
-
|
|
332
|
+
},
|
|
333
|
+
1379(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
334
|
+
/* import */ var _config_auth_constants_js__rspack_import_0 = __webpack_require__(5588);
|
|
335
|
+
|
|
336
|
+
function generateAccessToken(jwtService, authConfig, payload, issuedAtMs = Date.now()) {
|
|
306
337
|
return jwtService.sign({
|
|
307
338
|
...payload,
|
|
308
|
-
type: 'access'
|
|
339
|
+
type: 'access',
|
|
340
|
+
iatMs: issuedAtMs
|
|
309
341
|
}, {
|
|
310
342
|
secret: authConfig.getJwtSecret(),
|
|
311
343
|
expiresIn: authConfig.getJwtExpiration(),
|
|
@@ -313,10 +345,12 @@ function generateAccessToken(jwtService, authConfig, payload) {
|
|
|
313
345
|
});
|
|
314
346
|
}
|
|
315
347
|
function generateTokens(jwtService, authConfig, payload) {
|
|
316
|
-
const
|
|
348
|
+
const issuedAtMs = Date.now();
|
|
349
|
+
const accessToken = generateAccessToken(jwtService, authConfig, payload, issuedAtMs);
|
|
317
350
|
const refreshToken = jwtService.sign({
|
|
318
351
|
...payload,
|
|
319
|
-
type: 'refresh'
|
|
352
|
+
type: 'refresh',
|
|
353
|
+
iatMs: issuedAtMs
|
|
320
354
|
}, {
|
|
321
355
|
secret: authConfig.getRefreshTokenSecret(),
|
|
322
356
|
expiresIn: authConfig.getRefreshTokenExpiration(),
|
|
@@ -345,15 +379,41 @@ function parseExpiryToMs(expiry) {
|
|
|
345
379
|
return 7 * 24 * 60 * 60 * 1000;
|
|
346
380
|
}
|
|
347
381
|
}
|
|
382
|
+
// Markers written before millisecond precision hold epoch seconds
|
|
383
|
+
const LEGACY_SECONDS_LIMIT = 1e11;
|
|
384
|
+
function revocationKey(userId) {
|
|
385
|
+
return `${_config_auth_constants_js__rspack_import_0/* .TOKEN_REVOKED_PREFIX */.EV}:${userId}`;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* When the user's sessions were last revoked, in epoch ms. Read past the in-process cache layer:
|
|
389
|
+
* the marker changes in place, and another instance's L1 copy would hold an older value.
|
|
390
|
+
*/ async function getSessionsRevokedAtMs(cache, userId) {
|
|
391
|
+
const revokedAt = await cache.get(revocationKey(userId), {
|
|
392
|
+
skipMemory: true
|
|
393
|
+
});
|
|
394
|
+
if (!revokedAt) return undefined;
|
|
395
|
+
return revokedAt < LEGACY_SECONDS_LIMIT ? revokedAt * 1000 : revokedAt;
|
|
396
|
+
}
|
|
397
|
+
/** True when a token or session issued at `issuedAtMs` predates the user's last revocation */ async function isIssuedBeforeRevocation(cache, userId, issuedAtMs) {
|
|
398
|
+
const revokedAtMs = await getSessionsRevokedAtMs(cache, userId);
|
|
399
|
+
return revokedAtMs !== undefined && issuedAtMs < revokedAtMs;
|
|
400
|
+
}
|
|
348
401
|
/** Rejects every token the user holds that was issued before now: checked on refresh and, by JwtStrategy, on every request. */ async function revokeUserSessions(cache, authConfig, userId) {
|
|
349
|
-
|
|
350
|
-
|
|
402
|
+
// Never move the marker back (an instance whose clock lags would otherwise un-revoke tokens)
|
|
403
|
+
const revokedAtMs = Math.max(Date.now(), await getSessionsRevokedAtMs(cache, userId) ?? 0);
|
|
404
|
+
await cache.set(revocationKey(userId), revokedAtMs, parseExpiryToMs(authConfig.getRefreshTokenExpiration()), {
|
|
405
|
+
skipMemory: true
|
|
406
|
+
});
|
|
351
407
|
}
|
|
352
408
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
409
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
410
|
+
PV: () => (getSessionsRevokedAtMs),
|
|
411
|
+
Vg: () => (generateAccessToken),
|
|
412
|
+
XP: () => (revokeUserSessions),
|
|
413
|
+
e: () => (generateTokens),
|
|
414
|
+
lL: () => (isIssuedBeforeRevocation),
|
|
415
|
+
om: () => (parseExpiryToMs)
|
|
416
|
+
});
|
|
357
417
|
|
|
358
418
|
|
|
359
419
|
},
|
|
@@ -639,6 +699,10 @@ const linkedInSocialProvider = new linkedin_social_provider_LinkedInSocialProvid
|
|
|
639
699
|
|
|
640
700
|
const GRAPH_ME_URL = 'https://graph.microsoft.com/v1.0/me';
|
|
641
701
|
const DEFAULT_TENANT = 'common';
|
|
702
|
+
const MULTI_TENANT_AUTHORITIES = new Set([
|
|
703
|
+
'common',
|
|
704
|
+
'organizations'
|
|
705
|
+
]);
|
|
642
706
|
function resolveTenant(credentials) {
|
|
643
707
|
const tenantId = credentials.extraConfig?.['tenantId'];
|
|
644
708
|
return typeof tenantId === 'string' && tenantId ? tenantId : DEFAULT_TENANT;
|
|
@@ -675,13 +739,14 @@ let microsoft_social_provider_MicrosoftSocialProvider = class MicrosoftSocialPro
|
|
|
675
739
|
scope: resolveScopes(credentials, this.defaultScopes)
|
|
676
740
|
});
|
|
677
741
|
const profile = await getJson(this.provider, GRAPH_ME_URL, readAccessToken(this.provider, token));
|
|
678
|
-
// `mail`
|
|
679
|
-
//
|
|
680
|
-
const
|
|
742
|
+
// Any Entra tenant admin can set `mail` to an address they do not own, so it only counts as
|
|
743
|
+
// verified when sign-in is pinned to one tenant (or to personal accounts), never common/organizations.
|
|
744
|
+
const mail = readString(profile, 'mail');
|
|
745
|
+
const trustsMail = !MULTI_TENANT_AUTHORITIES.has(resolveTenant(credentials).toLowerCase());
|
|
681
746
|
return {
|
|
682
747
|
providerUserId: readString(profile, 'id') ?? '',
|
|
683
|
-
email:
|
|
684
|
-
emailVerified: !!
|
|
748
|
+
email: mail ?? readString(profile, 'userPrincipalName'),
|
|
749
|
+
emailVerified: !!mail && trustsMail,
|
|
685
750
|
name: readString(profile, 'displayName'),
|
|
686
751
|
avatarUrl: null
|
|
687
752
|
};
|
|
@@ -925,13 +990,17 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
925
990
|
/* import */ var _flusys_nestjs_shared_utils__rspack_import_3 = __webpack_require__(9216);
|
|
926
991
|
/* import */ var _nestjs_common__rspack_import_4 = __webpack_require__(6157);
|
|
927
992
|
/* import */ var bcrypt__rspack_import_5 = __webpack_require__(7176);
|
|
928
|
-
/* import */ var
|
|
929
|
-
/* import */ var
|
|
930
|
-
/* import */ var
|
|
931
|
-
/* import */ var
|
|
932
|
-
/* import */ var
|
|
933
|
-
/* import */ var
|
|
934
|
-
/* import */ var
|
|
993
|
+
/* import */ var typeorm__rspack_import_6 = __webpack_require__(7158);
|
|
994
|
+
/* import */ var _config_index_js__rspack_import_13 = __webpack_require__(5363);
|
|
995
|
+
/* import */ var _config_index_js__rspack_import_15 = __webpack_require__(7987);
|
|
996
|
+
/* import */ var _entities_app_user_entity_js__rspack_import_7 = __webpack_require__(4758);
|
|
997
|
+
/* import */ var _entities_pending_registration_entity_js__rspack_import_8 = __webpack_require__(9388);
|
|
998
|
+
/* import */ var _entities_user_token_entity_js__rspack_import_9 = __webpack_require__(9696);
|
|
999
|
+
/* import */ var _helpers_cache_invalidation_helper_js__rspack_import_10 = __webpack_require__(37);
|
|
1000
|
+
/* import */ var _helpers_token_helper_js__rspack_import_16 = __webpack_require__(1379);
|
|
1001
|
+
/* import */ var _interfaces_index_js__rspack_import_17 = __webpack_require__(9125);
|
|
1002
|
+
/* import */ var _auth_config_service_js__rspack_import_11 = __webpack_require__(6514);
|
|
1003
|
+
/* import */ var _auth_datasource_provider_js__rspack_import_12 = __webpack_require__(2732);
|
|
935
1004
|
function _ts_decorate(decorators, target, key, desc) {
|
|
936
1005
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
937
1006
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -967,14 +1036,23 @@ function _ts_param(paramIndex, decorator) {
|
|
|
967
1036
|
|
|
968
1037
|
|
|
969
1038
|
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
|
|
970
1043
|
class AuthEmailService {
|
|
971
1044
|
dataSourceProvider;
|
|
972
1045
|
emailProvider;
|
|
1046
|
+
cacheManager;
|
|
1047
|
+
authConfig;
|
|
973
1048
|
PASSWORD_RESET_EXPIRY = 60 * 60 * 1000;
|
|
974
1049
|
EMAIL_VERIFICATION_EXPIRY = 24 * 60 * 60 * 1000;
|
|
975
|
-
|
|
1050
|
+
logger = (0,_flusys_nestjs_shared_classes__rspack_import_1.createModuleLogger)('auth');
|
|
1051
|
+
constructor(dataSourceProvider, emailProvider, cacheManager, authConfig){
|
|
976
1052
|
this.dataSourceProvider = dataSourceProvider;
|
|
977
1053
|
this.emailProvider = emailProvider;
|
|
1054
|
+
this.cacheManager = cacheManager;
|
|
1055
|
+
this.authConfig = authConfig;
|
|
978
1056
|
}
|
|
979
1057
|
isEmailEnabled() {
|
|
980
1058
|
return !!this.emailProvider;
|
|
@@ -1001,7 +1079,7 @@ class AuthEmailService {
|
|
|
1001
1079
|
if (!this.emailProvider) {
|
|
1002
1080
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1003
1081
|
message: 'Email service is not configured',
|
|
1004
|
-
messageKey:
|
|
1082
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.EMAIL_NOT_CONFIGURED */.mK.EMAIL_NOT_CONFIGURED
|
|
1005
1083
|
});
|
|
1006
1084
|
}
|
|
1007
1085
|
}
|
|
@@ -1011,7 +1089,7 @@ class AuthEmailService {
|
|
|
1011
1089
|
} catch {
|
|
1012
1090
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1013
1091
|
message: 'Failed to send verification email. Please try again.',
|
|
1014
|
-
messageKey:
|
|
1092
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.EMAIL_SEND_FAILED */.mK.EMAIL_SEND_FAILED
|
|
1015
1093
|
});
|
|
1016
1094
|
}
|
|
1017
1095
|
}
|
|
@@ -1029,20 +1107,21 @@ class AuthEmailService {
|
|
|
1029
1107
|
});
|
|
1030
1108
|
}
|
|
1031
1109
|
}
|
|
1032
|
-
async hasPendingRegistration(email) {
|
|
1110
|
+
async hasPendingRegistration(email, password) {
|
|
1033
1111
|
if (!this.isEmailEnabled()) return false;
|
|
1034
|
-
const pendingRepository = await this.getRepository(
|
|
1112
|
+
const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_8/* .PendingRegistration */.N);
|
|
1035
1113
|
const pending = await pendingRepository.findOne({
|
|
1036
1114
|
where: {
|
|
1037
1115
|
email
|
|
1038
1116
|
}
|
|
1039
1117
|
});
|
|
1040
|
-
|
|
1118
|
+
if (!pending || (0,_flusys_nestjs_shared_utils__rspack_import_3.isExpired)(pending.expiresAt)) return false;
|
|
1119
|
+
return password === undefined || await bcrypt__rspack_import_5.compare(password, pending.passwordHash);
|
|
1041
1120
|
}
|
|
1042
1121
|
async createPendingRegistration(data) {
|
|
1043
1122
|
this.assertEmailEnabled();
|
|
1044
|
-
const userRepository = await this.getRepository(
|
|
1045
|
-
const pendingRepository = await this.getRepository(
|
|
1123
|
+
const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
|
|
1124
|
+
const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_8/* .PendingRegistration */.N);
|
|
1046
1125
|
// Check if user already exists
|
|
1047
1126
|
const existingUser = await userRepository.findOne({
|
|
1048
1127
|
where: {
|
|
@@ -1052,7 +1131,7 @@ class AuthEmailService {
|
|
|
1052
1131
|
if (existingUser) {
|
|
1053
1132
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1054
1133
|
message: 'Registration failed. Please try again or contact support.',
|
|
1055
|
-
messageKey:
|
|
1134
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.REGISTER_FAILED */.mK.REGISTER_FAILED
|
|
1056
1135
|
});
|
|
1057
1136
|
}
|
|
1058
1137
|
// Delete any existing pending registration for this email
|
|
@@ -1081,13 +1160,13 @@ class AuthEmailService {
|
|
|
1081
1160
|
return {
|
|
1082
1161
|
success: true,
|
|
1083
1162
|
message: 'Please check your email to verify your registration.',
|
|
1084
|
-
messageKey:
|
|
1163
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SENT */.sv.VERIFICATION_SENT
|
|
1085
1164
|
};
|
|
1086
1165
|
}
|
|
1087
1166
|
async forgotPassword(email, resetUrlBase) {
|
|
1088
1167
|
this.assertEmailEnabled();
|
|
1089
|
-
const userRepository = await this.getRepository(
|
|
1090
|
-
const tokenRepository = await this.getRepository(
|
|
1168
|
+
const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
|
|
1169
|
+
const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_9/* .UserToken */.L);
|
|
1091
1170
|
const user = await userRepository.findOne({
|
|
1092
1171
|
where: {
|
|
1093
1172
|
email
|
|
@@ -1098,20 +1177,20 @@ class AuthEmailService {
|
|
|
1098
1177
|
return {
|
|
1099
1178
|
success: true,
|
|
1100
1179
|
message: 'If the email exists, a password reset link has been sent.',
|
|
1101
|
-
messageKey:
|
|
1180
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_EMAIL_SENT */.mK.PASSWORD_RESET_EMAIL_SENT
|
|
1102
1181
|
};
|
|
1103
1182
|
}
|
|
1104
1183
|
// Invalidate existing password reset tokens for this user
|
|
1105
1184
|
await tokenRepository.delete({
|
|
1106
1185
|
userId: user.id,
|
|
1107
|
-
type:
|
|
1186
|
+
type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET
|
|
1108
1187
|
});
|
|
1109
1188
|
// Create new token with hashed storage
|
|
1110
1189
|
const token = this.generateToken();
|
|
1111
1190
|
const tokenHash = this.hashToken(token);
|
|
1112
1191
|
const userToken = tokenRepository.create({
|
|
1113
1192
|
userId: user.id,
|
|
1114
|
-
type:
|
|
1193
|
+
type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET,
|
|
1115
1194
|
token: tokenHash,
|
|
1116
1195
|
expiresAt: new Date(Date.now() + this.PASSWORD_RESET_EXPIRY)
|
|
1117
1196
|
});
|
|
@@ -1120,43 +1199,36 @@ class AuthEmailService {
|
|
|
1120
1199
|
const resetUrl = `${resetUrlBase}?token=${token}`;
|
|
1121
1200
|
try {
|
|
1122
1201
|
await this.emailProvider.sendPasswordResetEmail(email, token, resetUrl);
|
|
1123
|
-
} catch
|
|
1124
|
-
|
|
1125
|
-
message: 'Failed to process request. Please try again later.',
|
|
1126
|
-
messageKey: _flusys_nestjs_shared_constants__rspack_import_2.SYSTEM_MESSAGES.INTERNAL_ERROR,
|
|
1127
|
-
messageVariables: {
|
|
1128
|
-
provider: 'EmailProvider',
|
|
1129
|
-
error: 'Send email failed'
|
|
1130
|
-
}
|
|
1131
|
-
});
|
|
1202
|
+
} catch (error) {
|
|
1203
|
+
this.logger.error(`Password reset email could not be sent: ${error instanceof Error ? error.message : String(error)}`);
|
|
1132
1204
|
}
|
|
1133
|
-
await this.publishUserEvent(
|
|
1205
|
+
await this.publishUserEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.PASSWORD_RESET_REQUESTED */.o.PASSWORD_RESET_REQUESTED, user.id, {
|
|
1134
1206
|
email: user.email
|
|
1135
1207
|
});
|
|
1136
1208
|
return {
|
|
1137
1209
|
success: true,
|
|
1138
1210
|
message: 'If the email exists, a password reset link has been sent.',
|
|
1139
|
-
messageKey:
|
|
1211
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_EMAIL_SENT */.mK.PASSWORD_RESET_EMAIL_SENT
|
|
1140
1212
|
};
|
|
1141
1213
|
}
|
|
1142
1214
|
async resetPassword(token, newPassword) {
|
|
1143
|
-
const userRepository = await this.getRepository(
|
|
1144
|
-
const tokenRepository = await this.getRepository(
|
|
1215
|
+
const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
|
|
1216
|
+
const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_9/* .UserToken */.L);
|
|
1145
1217
|
// Hash the incoming token to compare with stored hash
|
|
1146
1218
|
const tokenHash = this.hashToken(token);
|
|
1147
1219
|
const userToken = await tokenRepository.findOne({
|
|
1148
1220
|
where: {
|
|
1149
1221
|
token: tokenHash,
|
|
1150
|
-
type:
|
|
1222
|
+
type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.PASSWORD_RESET */.k.PASSWORD_RESET
|
|
1151
1223
|
}
|
|
1152
1224
|
});
|
|
1153
1225
|
if (!userToken) {
|
|
1154
1226
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1155
1227
|
message: 'Invalid or expired reset token',
|
|
1156
|
-
messageKey:
|
|
1228
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_INVALID */.sv.VERIFICATION_TOKEN_INVALID
|
|
1157
1229
|
});
|
|
1158
1230
|
}
|
|
1159
|
-
this.validateTokenNotExpiredOrUsed(userToken, 'Reset token has expired. Please request a new one.',
|
|
1231
|
+
this.validateTokenNotExpiredOrUsed(userToken, 'Reset token has expired. Please request a new one.', _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_TOKEN_EXPIRED */.mK.PASSWORD_RESET_TOKEN_EXPIRED, 'This reset token has already been used.', _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_TOKEN_USED */.mK.PASSWORD_RESET_TOKEN_USED);
|
|
1160
1232
|
const user = await userRepository.findOne({
|
|
1161
1233
|
where: {
|
|
1162
1234
|
id: userToken.userId
|
|
@@ -1165,28 +1237,42 @@ class AuthEmailService {
|
|
|
1165
1237
|
if (!user) {
|
|
1166
1238
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1167
1239
|
message: 'User not found',
|
|
1168
|
-
messageKey:
|
|
1240
|
+
messageKey: _config_index_js__rspack_import_13/* .USER_MESSAGES.NOT_FOUND */.ny.NOT_FOUND
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
const claimed = await tokenRepository.update({
|
|
1244
|
+
id: userToken.id,
|
|
1245
|
+
usedAt: (0,typeorm__rspack_import_6.IsNull)()
|
|
1246
|
+
}, {
|
|
1247
|
+
usedAt: new Date()
|
|
1248
|
+
});
|
|
1249
|
+
if (claimed?.affected === 0) {
|
|
1250
|
+
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1251
|
+
message: 'This reset token has already been used.',
|
|
1252
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_TOKEN_USED */.mK.PASSWORD_RESET_TOKEN_USED
|
|
1169
1253
|
});
|
|
1170
1254
|
}
|
|
1171
|
-
// Update password
|
|
1172
1255
|
user.password = await bcrypt__rspack_import_5.hash(newPassword, (/* inlined export .BCRYPT_SALT_ROUNDS */12));
|
|
1173
1256
|
await userRepository.save(user);
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1257
|
+
await (0,_helpers_cache_invalidation_helper_js__rspack_import_10/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
|
|
1258
|
+
_config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER
|
|
1259
|
+
]);
|
|
1260
|
+
if (this.cacheManager && this.authConfig) {
|
|
1261
|
+
await (0,_helpers_token_helper_js__rspack_import_16/* .revokeUserSessions */.XP)(this.cacheManager, this.authConfig, user.id);
|
|
1262
|
+
}
|
|
1263
|
+
await this.publishUserEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.PASSWORD_RESET */.o.PASSWORD_RESET, user.id, {
|
|
1178
1264
|
email: user.email
|
|
1179
1265
|
});
|
|
1180
1266
|
return {
|
|
1181
1267
|
success: true,
|
|
1182
1268
|
message: 'Password has been reset successfully.',
|
|
1183
|
-
messageKey:
|
|
1269
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_RESET_SUCCESS */.mK.PASSWORD_RESET_SUCCESS
|
|
1184
1270
|
};
|
|
1185
1271
|
}
|
|
1186
1272
|
async verifyEmail(token) {
|
|
1187
|
-
const pendingRepository = await this.getRepository(
|
|
1188
|
-
const userRepository = await this.getRepository(
|
|
1189
|
-
const tokenRepository = await this.getRepository(
|
|
1273
|
+
const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_8/* .PendingRegistration */.N);
|
|
1274
|
+
const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
|
|
1275
|
+
const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_9/* .UserToken */.L);
|
|
1190
1276
|
// Hash the incoming token to compare with stored hash
|
|
1191
1277
|
const tokenHash = this.hashToken(token);
|
|
1192
1278
|
// Check for pending registration first
|
|
@@ -1202,13 +1288,18 @@ class AuthEmailService {
|
|
|
1202
1288
|
});
|
|
1203
1289
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1204
1290
|
message: 'Verification token has expired. Please register again.',
|
|
1205
|
-
messageKey:
|
|
1291
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_EXPIRED */.sv.VERIFICATION_TOKEN_EXPIRED
|
|
1206
1292
|
});
|
|
1207
1293
|
}
|
|
1208
|
-
|
|
1209
|
-
await pendingRepository.delete({
|
|
1294
|
+
const consumed = await pendingRepository.delete({
|
|
1210
1295
|
id: pending.id
|
|
1211
1296
|
});
|
|
1297
|
+
if (consumed?.affected === 0) {
|
|
1298
|
+
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1299
|
+
message: 'Invalid or expired verification token',
|
|
1300
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_INVALID */.sv.VERIFICATION_TOKEN_INVALID
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1212
1303
|
return {
|
|
1213
1304
|
type: 'pending',
|
|
1214
1305
|
pending
|
|
@@ -1218,13 +1309,13 @@ class AuthEmailService {
|
|
|
1218
1309
|
const userToken = await tokenRepository.findOne({
|
|
1219
1310
|
where: {
|
|
1220
1311
|
token: tokenHash,
|
|
1221
|
-
type:
|
|
1312
|
+
type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION
|
|
1222
1313
|
}
|
|
1223
1314
|
});
|
|
1224
1315
|
if (!userToken) {
|
|
1225
1316
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1226
1317
|
message: 'Invalid or expired verification token',
|
|
1227
|
-
messageKey:
|
|
1318
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_INVALID */.sv.VERIFICATION_TOKEN_INVALID
|
|
1228
1319
|
});
|
|
1229
1320
|
}
|
|
1230
1321
|
if ((0,_flusys_nestjs_shared_utils__rspack_import_3.isExpired)(userToken.expiresAt)) {
|
|
@@ -1233,14 +1324,14 @@ class AuthEmailService {
|
|
|
1233
1324
|
});
|
|
1234
1325
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1235
1326
|
message: 'Verification token has expired. Please request a new one.',
|
|
1236
|
-
messageKey:
|
|
1327
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_TOKEN_EXPIRED */.sv.VERIFICATION_TOKEN_EXPIRED
|
|
1237
1328
|
});
|
|
1238
1329
|
}
|
|
1239
1330
|
if (userToken.usedAt) {
|
|
1240
1331
|
return {
|
|
1241
1332
|
type: 'verified',
|
|
1242
1333
|
message: 'Email has already been verified.',
|
|
1243
|
-
messageKey:
|
|
1334
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SUCCESS */.sv.VERIFICATION_SUCCESS
|
|
1244
1335
|
};
|
|
1245
1336
|
}
|
|
1246
1337
|
const user = await userRepository.findOne({
|
|
@@ -1251,28 +1342,31 @@ class AuthEmailService {
|
|
|
1251
1342
|
if (!user) {
|
|
1252
1343
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
1253
1344
|
message: 'User not found',
|
|
1254
|
-
messageKey:
|
|
1345
|
+
messageKey: _config_index_js__rspack_import_13/* .USER_MESSAGES.NOT_FOUND */.ny.NOT_FOUND
|
|
1255
1346
|
});
|
|
1256
1347
|
}
|
|
1257
1348
|
// Mark email as verified
|
|
1258
1349
|
user.emailVerified = true;
|
|
1259
1350
|
await userRepository.save(user);
|
|
1351
|
+
await (0,_helpers_cache_invalidation_helper_js__rspack_import_10/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
|
|
1352
|
+
_config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER
|
|
1353
|
+
]);
|
|
1260
1354
|
// Mark token as used
|
|
1261
1355
|
userToken.usedAt = new Date();
|
|
1262
1356
|
await tokenRepository.save(userToken);
|
|
1263
|
-
await this.publishUserEvent(
|
|
1357
|
+
await this.publishUserEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.EMAIL_VERIFIED */.o.EMAIL_VERIFIED, user.id, {
|
|
1264
1358
|
email: user.email
|
|
1265
1359
|
});
|
|
1266
1360
|
return {
|
|
1267
1361
|
type: 'verified',
|
|
1268
1362
|
message: 'Email has been verified successfully.',
|
|
1269
|
-
messageKey:
|
|
1363
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SUCCESS */.sv.VERIFICATION_SUCCESS
|
|
1270
1364
|
};
|
|
1271
1365
|
}
|
|
1272
1366
|
async publishUserEvent(action, userId, metadata) {
|
|
1273
1367
|
await (0,_flusys_nestjs_shared_classes__rspack_import_1.publishDomainEvent)({
|
|
1274
1368
|
module: (/* inlined export .AUTH_EVENT_MODULE */"auth"),
|
|
1275
|
-
entity:
|
|
1369
|
+
entity: _config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER,
|
|
1276
1370
|
action,
|
|
1277
1371
|
ids: [
|
|
1278
1372
|
userId
|
|
@@ -1282,9 +1376,9 @@ class AuthEmailService {
|
|
|
1282
1376
|
}
|
|
1283
1377
|
async resendVerificationEmail(email, verifyUrlBase) {
|
|
1284
1378
|
this.assertEmailEnabled();
|
|
1285
|
-
const pendingRepository = await this.getRepository(
|
|
1286
|
-
const userRepository = await this.getRepository(
|
|
1287
|
-
const tokenRepository = await this.getRepository(
|
|
1379
|
+
const pendingRepository = await this.getRepository(_entities_pending_registration_entity_js__rspack_import_8/* .PendingRegistration */.N);
|
|
1380
|
+
const userRepository = await this.getRepository(_entities_app_user_entity_js__rspack_import_7/* .AppUser */.$);
|
|
1381
|
+
const tokenRepository = await this.getRepository(_entities_user_token_entity_js__rspack_import_9/* .UserToken */.L);
|
|
1288
1382
|
// Check pending registration first
|
|
1289
1383
|
const pending = await pendingRepository.findOne({
|
|
1290
1384
|
where: {
|
|
@@ -1301,7 +1395,7 @@ class AuthEmailService {
|
|
|
1301
1395
|
return {
|
|
1302
1396
|
success: true,
|
|
1303
1397
|
message: 'If the email requires verification, a link has been sent.',
|
|
1304
|
-
messageKey:
|
|
1398
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
|
|
1305
1399
|
};
|
|
1306
1400
|
}
|
|
1307
1401
|
// Check existing user
|
|
@@ -1314,18 +1408,18 @@ class AuthEmailService {
|
|
|
1314
1408
|
return {
|
|
1315
1409
|
success: true,
|
|
1316
1410
|
message: 'If the email requires verification, a link has been sent.',
|
|
1317
|
-
messageKey:
|
|
1411
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
|
|
1318
1412
|
};
|
|
1319
1413
|
}
|
|
1320
1414
|
// Invalidate existing tokens and create new one
|
|
1321
1415
|
await tokenRepository.delete({
|
|
1322
1416
|
userId: user.id,
|
|
1323
|
-
type:
|
|
1417
|
+
type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION
|
|
1324
1418
|
});
|
|
1325
1419
|
const token = this.generateToken();
|
|
1326
1420
|
const userToken = tokenRepository.create({
|
|
1327
1421
|
userId: user.id,
|
|
1328
|
-
type:
|
|
1422
|
+
type: _entities_user_token_entity_js__rspack_import_9/* .TokenType.EMAIL_VERIFICATION */.k.EMAIL_VERIFICATION,
|
|
1329
1423
|
token: this.hashToken(token),
|
|
1330
1424
|
expiresAt: new Date(Date.now() + this.EMAIL_VERIFICATION_EXPIRY)
|
|
1331
1425
|
});
|
|
@@ -1335,7 +1429,7 @@ class AuthEmailService {
|
|
|
1335
1429
|
return {
|
|
1336
1430
|
success: true,
|
|
1337
1431
|
message: 'If the email requires verification, a link has been sent.',
|
|
1338
|
-
messageKey:
|
|
1432
|
+
messageKey: _config_index_js__rspack_import_13/* .EMAIL_VERIFICATION_MESSAGES.RESEND_SUCCESS */.sv.RESEND_SUCCESS
|
|
1339
1433
|
};
|
|
1340
1434
|
}
|
|
1341
1435
|
}
|
|
@@ -1405,13 +1499,19 @@ AuthEmailService = _ts_decorate([
|
|
|
1405
1499
|
scope: _nestjs_common__rspack_import_4.Scope.REQUEST
|
|
1406
1500
|
}),
|
|
1407
1501
|
_ts_param(0, (0,_nestjs_common__rspack_import_4.Optional)()),
|
|
1408
|
-
_ts_param(0, (0,_nestjs_common__rspack_import_4.Inject)(
|
|
1502
|
+
_ts_param(0, (0,_nestjs_common__rspack_import_4.Inject)(_auth_datasource_provider_js__rspack_import_12/* .AuthDataSourceProvider */.v)),
|
|
1409
1503
|
_ts_param(1, (0,_nestjs_common__rspack_import_4.Optional)()),
|
|
1410
|
-
_ts_param(1, (0,_nestjs_common__rspack_import_4.Inject)(
|
|
1504
|
+
_ts_param(1, (0,_nestjs_common__rspack_import_4.Inject)(_interfaces_index_js__rspack_import_17/* .AUTH_EMAIL_PROVIDER */.C)),
|
|
1505
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_4.Optional)()),
|
|
1506
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_4.Inject)('CACHE_INSTANCE')),
|
|
1507
|
+
_ts_param(3, (0,_nestjs_common__rspack_import_4.Optional)()),
|
|
1508
|
+
_ts_param(3, (0,_nestjs_common__rspack_import_4.Inject)(_auth_config_service_js__rspack_import_11/* .AuthConfigService */.W)),
|
|
1411
1509
|
_ts_metadata("design:type", Function),
|
|
1412
1510
|
_ts_metadata("design:paramtypes", [
|
|
1413
|
-
typeof
|
|
1414
|
-
typeof IAuthEmailProvider === "undefined" ? Object : IAuthEmailProvider
|
|
1511
|
+
typeof _auth_datasource_provider_js__rspack_import_12/* .AuthDataSourceProvider */.v === "undefined" ? Object : _auth_datasource_provider_js__rspack_import_12/* .AuthDataSourceProvider */.v,
|
|
1512
|
+
typeof IAuthEmailProvider === "undefined" ? Object : IAuthEmailProvider,
|
|
1513
|
+
typeof _flusys_nestjs_shared_classes__rspack_import_1.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_1.HybridCache,
|
|
1514
|
+
typeof _auth_config_service_js__rspack_import_11/* .AuthConfigService */.W === "undefined" ? Object : _auth_config_service_js__rspack_import_11/* .AuthConfigService */.W
|
|
1415
1515
|
])
|
|
1416
1516
|
], AuthEmailService);
|
|
1417
1517
|
|
|
@@ -1466,8 +1566,10 @@ var pending_registration_entity = __webpack_require__(9388);
|
|
|
1466
1566
|
var user_company_permission_entity = __webpack_require__(2377);
|
|
1467
1567
|
// EXTERNAL MODULE: ./projects/nestjs-auth/src/enums/user-org-permission-type.enum.ts
|
|
1468
1568
|
var user_org_permission_type_enum = __webpack_require__(5081);
|
|
1469
|
-
// EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/index.ts +
|
|
1470
|
-
var helpers = __webpack_require__(
|
|
1569
|
+
// EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/index.ts + 1 modules
|
|
1570
|
+
var helpers = __webpack_require__(5634);
|
|
1571
|
+
// EXTERNAL MODULE: ./projects/nestjs-auth/src/helpers/cache-invalidation.helper.ts
|
|
1572
|
+
var cache_invalidation_helper = __webpack_require__(37);
|
|
1471
1573
|
// EXTERNAL MODULE: ./projects/nestjs-auth/src/services/auth-config.service.ts + 1 modules
|
|
1472
1574
|
var auth_config_service = __webpack_require__(6514);
|
|
1473
1575
|
// EXTERNAL MODULE: ./projects/nestjs-auth/src/services/auth-datasource.provider.ts
|
|
@@ -1522,6 +1624,8 @@ function _ts_param(paramIndex, decorator) {
|
|
|
1522
1624
|
|
|
1523
1625
|
|
|
1524
1626
|
|
|
1627
|
+
|
|
1628
|
+
const TIMING_SAFE_DUMMY_HASH = '$2b$12$1l4HGUUGpuL.DdwxrAHDmeUINEEDQVBT4lPvdSYEQDNm5BFNskVum';
|
|
1525
1629
|
class AuthenticationService {
|
|
1526
1630
|
jwtService;
|
|
1527
1631
|
authConfig;
|
|
@@ -1569,8 +1673,11 @@ class AuthenticationService {
|
|
|
1569
1673
|
if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
|
|
1570
1674
|
return this.dataSourceProvider.getRepository(entity);
|
|
1571
1675
|
}
|
|
1676
|
+
/** This service writes users, companies, branches and grants directly; their cached reads go stale */ async invalidateCaches(...entityNames) {
|
|
1677
|
+
await (0,cache_invalidation_helper/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, entityNames);
|
|
1678
|
+
}
|
|
1572
1679
|
async login(email, password) {
|
|
1573
|
-
if (this.isEmailVerificationEnabled() && await this.authEmailService.hasPendingRegistration(email)) {
|
|
1680
|
+
if (this.isEmailVerificationEnabled() && await this.authEmailService.hasPendingRegistration(email, password)) {
|
|
1574
1681
|
return {
|
|
1575
1682
|
requiresEmailVerification: true,
|
|
1576
1683
|
email,
|
|
@@ -1719,12 +1826,20 @@ class AuthenticationService {
|
|
|
1719
1826
|
return await this.createUserDirectly(dto);
|
|
1720
1827
|
}
|
|
1721
1828
|
async handleExistingUserCompanyRegistration(existingUser, dto) {
|
|
1722
|
-
|
|
1829
|
+
const ownsAccount = await external_bcrypt_.compare(dto.password, existingUser.password || TIMING_SAFE_DUMMY_HASH);
|
|
1830
|
+
if (!dto.companySlug && !dto.newCompanyName || !ownsAccount || !existingUser.password) {
|
|
1723
1831
|
throw new common_.BadRequestException({
|
|
1724
1832
|
message: 'Email already registered. Please login or provide a company to join/create.',
|
|
1725
1833
|
messageKey: message_keys/* .AUTH_MESSAGES.REGISTER_EMAIL_EXISTS */.mK.REGISTER_EMAIL_EXISTS
|
|
1726
1834
|
});
|
|
1727
1835
|
}
|
|
1836
|
+
this.ensureUserActive(existingUser);
|
|
1837
|
+
if (this.isEmailVerificationEnabled() && !existingUser.emailVerified) {
|
|
1838
|
+
throw new common_.BadRequestException({
|
|
1839
|
+
message: 'Please verify your email address before joining a company.',
|
|
1840
|
+
messageKey: message_keys/* .AUTH_MESSAGES.LOGIN_EMAIL_NOT_VERIFIED */.mK.LOGIN_EMAIL_NOT_VERIFIED
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1728
1843
|
const permissionRepository = await this.getCompanyScopedRepository(user_company_permission_entity/* .UserCompanyPermission */.I);
|
|
1729
1844
|
const companyRepository = await this.getCompanyScopedRepository(company_entity/* .Company */.C);
|
|
1730
1845
|
if (!permissionRepository || !companyRepository) {
|
|
@@ -1788,6 +1903,7 @@ class AuthenticationService {
|
|
|
1788
1903
|
await this.userEnricher.onUserCreated(user.id, dto.additionalFields ?? null, queryRunner);
|
|
1789
1904
|
}
|
|
1790
1905
|
await queryRunner.commitTransaction();
|
|
1906
|
+
await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER);
|
|
1791
1907
|
if (!this.isCompanyFeatureEnabled()) {
|
|
1792
1908
|
return this.generateRegistrationResponse(user);
|
|
1793
1909
|
}
|
|
@@ -1849,6 +1965,7 @@ class AuthenticationService {
|
|
|
1849
1965
|
await this.userEnricher.onUserCreated(user.id, pending.additionalFields ?? null, queryRunner);
|
|
1850
1966
|
}
|
|
1851
1967
|
await queryRunner.commitTransaction();
|
|
1968
|
+
await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER);
|
|
1852
1969
|
if (this.isCompanyFeatureEnabled() && pending.companyData) {
|
|
1853
1970
|
return await this.processCompanyRegistration(user, pending.companyData);
|
|
1854
1971
|
}
|
|
@@ -1881,8 +1998,12 @@ class AuthenticationService {
|
|
|
1881
1998
|
return this.generateRegistrationResponse(user);
|
|
1882
1999
|
}
|
|
1883
2000
|
const { company, branch } = await this.findOrCreateCompanyAndBranch(data, companyRepository, branchRepository);
|
|
2001
|
+
if (data.newCompanyName && company) {
|
|
2002
|
+
await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.COMPANY */.g.COMPANY, event_actions/* .AUTH_EVENT_ENTITIES.COMPANY_BRANCH */.g.COMPANY_BRANCH);
|
|
2003
|
+
}
|
|
1884
2004
|
if (company) {
|
|
1885
2005
|
await this.assignUserPermissions(user.id, company.id, branch?.id, permissionRepository);
|
|
2006
|
+
await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION);
|
|
1886
2007
|
return this.generateRegistrationResponseWithCompany(user, company, branch ?? undefined);
|
|
1887
2008
|
}
|
|
1888
2009
|
return this.generateRegistrationResponse(user);
|
|
@@ -1979,8 +2100,9 @@ class AuthenticationService {
|
|
|
1979
2100
|
messageKey: message_keys/* .AUTH_MESSAGES.SESSION_NOT_AVAILABLE */.mK.SESSION_NOT_AVAILABLE
|
|
1980
2101
|
});
|
|
1981
2102
|
}
|
|
1982
|
-
const session = await this.sessionService.consumeSession(sessionId);
|
|
1983
|
-
|
|
2103
|
+
const session = await this.sessionService.consumeSession(sessionId, (0,utils_.cacheTenantId)(this.dataSourceProvider));
|
|
2104
|
+
// A session started before a logout / password reset must not mint tokens after it
|
|
2105
|
+
if (!session || await this.isRevokedSince(session.userId, new Date(session.createdAt).getTime())) {
|
|
1984
2106
|
throw new common_.UnauthorizedException({
|
|
1985
2107
|
message: 'Invalid or expired session',
|
|
1986
2108
|
messageKey: message_keys/* .AUTH_MESSAGES.SESSION_INVALID */.mK.SESSION_INVALID
|
|
@@ -2005,6 +2127,7 @@ class AuthenticationService {
|
|
|
2005
2127
|
messageKey: message_keys/* .USER_MESSAGES.NOT_FOUND */.ny.NOT_FOUND
|
|
2006
2128
|
});
|
|
2007
2129
|
}
|
|
2130
|
+
this.ensureUserActive(user);
|
|
2008
2131
|
const permissionRepository = await this.getCompanyScopedRepository(user_company_permission_entity/* .UserCompanyPermission */.I);
|
|
2009
2132
|
const companyRepository = await this.getCompanyScopedRepository(company_entity/* .Company */.C);
|
|
2010
2133
|
const branchRepository = await this.getCompanyScopedRepository(company_branch_entity/* .CompanyBranch */.A);
|
|
@@ -2084,7 +2207,13 @@ class AuthenticationService {
|
|
|
2084
2207
|
'HS256'
|
|
2085
2208
|
]
|
|
2086
2209
|
});
|
|
2087
|
-
if (
|
|
2210
|
+
if (payload.type !== 'refresh') {
|
|
2211
|
+
throw new common_.UnauthorizedException({
|
|
2212
|
+
message: 'Invalid refresh token',
|
|
2213
|
+
messageKey: message_keys/* .AUTH_MESSAGES.TOKEN_INVALID */.mK.TOKEN_INVALID
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
if (await this.isTokenRevoked(payload.id, payload.iat, payload.iatMs)) {
|
|
2088
2217
|
throw new common_.UnauthorizedException({
|
|
2089
2218
|
message: 'Token has been revoked',
|
|
2090
2219
|
messageKey: message_keys/* .AUTH_MESSAGES.TOKEN_REVOKED */.mK.TOKEN_REVOKED
|
|
@@ -2229,6 +2358,7 @@ class AuthenticationService {
|
|
|
2229
2358
|
}
|
|
2230
2359
|
user.password = await external_bcrypt_.hash(newPassword, (/* inlined export .BCRYPT_SALT_ROUNDS */12));
|
|
2231
2360
|
await userRepository.save(user);
|
|
2361
|
+
await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER);
|
|
2232
2362
|
this.publishAuthEvent(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER, event_actions/* .AUTH_EVENT_ACTIONS.PASSWORD_CHANGED */.o.PASSWORD_CHANGED, user);
|
|
2233
2363
|
}
|
|
2234
2364
|
async logout(userId) {
|
|
@@ -2251,16 +2381,14 @@ class AuthenticationService {
|
|
|
2251
2381
|
}
|
|
2252
2382
|
});
|
|
2253
2383
|
}
|
|
2254
|
-
async isTokenRevoked(userId, tokenIssuedAt) {
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
if (!
|
|
2261
|
-
|
|
2262
|
-
}
|
|
2263
|
-
return tokenIssuedAt < revokedAt;
|
|
2384
|
+
/** `tokenIssuedAtMs` is the token's `iatMs`; a token without it is judged by `iat` (seconds), erring towards revoked */ async isTokenRevoked(userId, tokenIssuedAt, tokenIssuedAtMs) {
|
|
2385
|
+
const issuedAtMs = typeof tokenIssuedAtMs === 'number' ? tokenIssuedAtMs : tokenIssuedAt ? tokenIssuedAt * 1000 : undefined;
|
|
2386
|
+
if (issuedAtMs === undefined) return false;
|
|
2387
|
+
return this.isRevokedSince(userId, issuedAtMs);
|
|
2388
|
+
}
|
|
2389
|
+
async isRevokedSince(userId, issuedAtMs) {
|
|
2390
|
+
if (!this.cacheManager) return false;
|
|
2391
|
+
return (0,helpers/* .isIssuedBeforeRevocation */.lL)(this.cacheManager, userId, issuedAtMs);
|
|
2264
2392
|
}
|
|
2265
2393
|
async getUserCompanies(userId) {
|
|
2266
2394
|
this.ensureCompanyFeatureEnabled();
|
|
@@ -2415,7 +2543,8 @@ class AuthenticationService {
|
|
|
2415
2543
|
email
|
|
2416
2544
|
}
|
|
2417
2545
|
});
|
|
2418
|
-
|
|
2546
|
+
const passwordMatches = await external_bcrypt_.compare(password, user?.password || TIMING_SAFE_DUMMY_HASH);
|
|
2547
|
+
if (!user?.password || !passwordMatches) {
|
|
2419
2548
|
throw new common_.UnauthorizedException({
|
|
2420
2549
|
message: 'Invalid credentials',
|
|
2421
2550
|
messageKey: message_keys/* .AUTH_MESSAGES.LOGIN_INVALID_CREDENTIALS */.mK.LOGIN_INVALID_CREDENTIALS
|
|
@@ -2423,6 +2552,7 @@ class AuthenticationService {
|
|
|
2423
2552
|
}
|
|
2424
2553
|
user.lastLoginAt = new Date();
|
|
2425
2554
|
await userRepository.save(user);
|
|
2555
|
+
await this.invalidateCaches(event_actions/* .AUTH_EVENT_ENTITIES.USER */.g.USER);
|
|
2426
2556
|
return user;
|
|
2427
2557
|
}
|
|
2428
2558
|
generateLoginResponse(user) {
|
|
@@ -2480,7 +2610,7 @@ class AuthenticationService {
|
|
|
2480
2610
|
messageKey: message_keys/* .AUTH_MESSAGES.SESSION_NOT_AVAILABLE */.mK.SESSION_NOT_AVAILABLE
|
|
2481
2611
|
});
|
|
2482
2612
|
}
|
|
2483
|
-
const sessionId = await this.sessionService.createSession(user.id);
|
|
2613
|
+
const sessionId = await this.sessionService.createSession(user.id, (0,utils_.cacheTenantId)(this.dataSourceProvider));
|
|
2484
2614
|
const companiesWithBranches = companies.map((company)=>({
|
|
2485
2615
|
...(0,helpers/* .toCompanyInfo */.$C)(company),
|
|
2486
2616
|
isActive: company.isActive,
|
|
@@ -2843,6 +2973,12 @@ class BranchService extends _flusys_nestjs_shared_classes__rspack_import_0.ApiSe
|
|
|
2843
2973
|
});
|
|
2844
2974
|
await Promise.all(rows.map((row)=>this.permissionSyncAdapter.onBranchDeleted(row.id, row.companyId)));
|
|
2845
2975
|
}
|
|
2976
|
+
// beforeDeleteOperation drops the branches' grants; bump after commit
|
|
2977
|
+
async delete(option, user) {
|
|
2978
|
+
const result = await super.delete(option, user);
|
|
2979
|
+
await this.utilsService.clearCache(_config_index_js__rspack_import_12/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION, this.cacheManager, this.cacheTenant());
|
|
2980
|
+
return result;
|
|
2981
|
+
}
|
|
2846
2982
|
// Hierarchy - ids that are empty or not UUIDs match no branch (and never reach a uuid column)
|
|
2847
2983
|
async getDescendantIds(branchIds, options = {}) {
|
|
2848
2984
|
const ids = (0,_flusys_nestjs_shared_utils__rspack_import_3.normalizeBranchIds)(branchIds).filter((id)=>(0,class_validator__rspack_import_5.isUUID)(id));
|
|
@@ -2978,47 +3114,39 @@ class CompanySelectionSessionService {
|
|
|
2978
3114
|
constructor(cacheManager){
|
|
2979
3115
|
this.cacheManager = cacheManager;
|
|
2980
3116
|
}
|
|
2981
|
-
async createSession(userId) {
|
|
3117
|
+
async createSession(userId, tenantId) {
|
|
2982
3118
|
const sessionId = (0,crypto__rspack_import_3.randomUUID)();
|
|
2983
|
-
const now =
|
|
2984
|
-
const expiresAt = new Date(now.getTime() + SESSION_TTL_MS);
|
|
3119
|
+
const now = Date.now();
|
|
2985
3120
|
const session = {
|
|
2986
3121
|
sessionId,
|
|
2987
3122
|
userId,
|
|
2988
3123
|
createdAt: now,
|
|
2989
|
-
expiresAt
|
|
3124
|
+
expiresAt: now + SESSION_TTL_MS
|
|
2990
3125
|
};
|
|
2991
|
-
await this.cacheManager.set(this.getKey(sessionId), session, SESSION_TTL_MS
|
|
3126
|
+
await this.cacheManager.set(this.getKey(sessionId, tenantId), session, SESSION_TTL_MS, {
|
|
3127
|
+
skipMemory: true
|
|
3128
|
+
});
|
|
2992
3129
|
return sessionId;
|
|
2993
3130
|
}
|
|
2994
|
-
/**
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
// Check if expired (extra safety, cache should handle TTL)
|
|
3000
|
-
if (new Date() > new Date(session.expiresAt)) {
|
|
3001
|
-
await this.deleteSession(sessionId);
|
|
3002
|
-
return null;
|
|
3003
|
-
}
|
|
3004
|
-
return session;
|
|
3131
|
+
/** Reads a session without consuming it */ async getSession(sessionId, tenantId) {
|
|
3132
|
+
const session = await this.cacheManager.get(this.getKey(sessionId, tenantId), {
|
|
3133
|
+
skipMemory: true
|
|
3134
|
+
});
|
|
3135
|
+
return this.unexpired(session);
|
|
3005
3136
|
}
|
|
3006
|
-
async consumeSession(sessionId) {
|
|
3007
|
-
const session = await this.
|
|
3008
|
-
|
|
3009
|
-
await this.deleteSession(sessionId);
|
|
3010
|
-
}
|
|
3011
|
-
return session;
|
|
3137
|
+
/** Atomic across instances: of two concurrent calls for one session, only one receives it */ async consumeSession(sessionId, tenantId) {
|
|
3138
|
+
const session = await this.cacheManager.take(this.getKey(sessionId, tenantId), SESSION_TTL_MS);
|
|
3139
|
+
return this.unexpired(session);
|
|
3012
3140
|
}
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
*/ async deleteSession(sessionId) {
|
|
3016
|
-
await this.cacheManager.del(this.getKey(sessionId));
|
|
3141
|
+
async deleteSession(sessionId, tenantId) {
|
|
3142
|
+
await this.cacheManager.del(this.getKey(sessionId, tenantId));
|
|
3017
3143
|
}
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3144
|
+
unexpired(session) {
|
|
3145
|
+
if (!session || !(new Date(session.expiresAt).getTime() >= Date.now())) return null;
|
|
3146
|
+
return session;
|
|
3147
|
+
}
|
|
3148
|
+
getKey(sessionId, tenantId) {
|
|
3149
|
+
return tenantId ? `${_config_auth_constants_js__rspack_import_4/* .SESSION_CACHE_PREFIX */.Vl}:${tenantId}:${sessionId}` : `${_config_auth_constants_js__rspack_import_4/* .SESSION_CACHE_PREFIX */.Vl}:${sessionId}`;
|
|
3022
3150
|
}
|
|
3023
3151
|
}
|
|
3024
3152
|
_ts_decorate([
|
|
@@ -3028,6 +3156,7 @@ _ts_decorate([
|
|
|
3028
3156
|
}),
|
|
3029
3157
|
_ts_metadata("design:type", Function),
|
|
3030
3158
|
_ts_metadata("design:paramtypes", [
|
|
3159
|
+
String,
|
|
3031
3160
|
String
|
|
3032
3161
|
]),
|
|
3033
3162
|
_ts_metadata("design:returntype", Promise)
|
|
@@ -3039,6 +3168,7 @@ _ts_decorate([
|
|
|
3039
3168
|
}),
|
|
3040
3169
|
_ts_metadata("design:type", Function),
|
|
3041
3170
|
_ts_metadata("design:paramtypes", [
|
|
3171
|
+
String,
|
|
3042
3172
|
String
|
|
3043
3173
|
]),
|
|
3044
3174
|
_ts_metadata("design:returntype", Promise)
|
|
@@ -3152,6 +3282,15 @@ class CompanyService extends _flusys_nestjs_shared_classes__rspack_import_0.ApiS
|
|
|
3152
3282
|
}
|
|
3153
3283
|
await Promise.all(companyIds.map((companyId)=>this.permissionSyncAdapter.onCompanyDeleted(companyId)));
|
|
3154
3284
|
}
|
|
3285
|
+
// beforeDeleteOperation drops the company's grants (and a purge cascades its branches); bump after commit
|
|
3286
|
+
async delete(option, user) {
|
|
3287
|
+
const result = await super.delete(option, user);
|
|
3288
|
+
await Promise.all([
|
|
3289
|
+
_config_index_js__rspack_import_9/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION,
|
|
3290
|
+
_config_index_js__rspack_import_9/* .AUTH_EVENT_ENTITIES.COMPANY_BRANCH */.g.COMPANY_BRANCH
|
|
3291
|
+
].map((name)=>this.utilsService.clearCache(name, this.cacheManager, this.cacheTenant())));
|
|
3292
|
+
return result;
|
|
3293
|
+
}
|
|
3155
3294
|
// Query Customization
|
|
3156
3295
|
async getSelectQuery(query, _user, select) {
|
|
3157
3296
|
if (!select?.length) {
|
|
@@ -3286,6 +3425,14 @@ class SocialAuthConfigService extends _flusys_nestjs_shared_classes__rspack_impo
|
|
|
3286
3425
|
});
|
|
3287
3426
|
}
|
|
3288
3427
|
}
|
|
3428
|
+
// The secret never leaves the service: not to clients, not into the shared cache
|
|
3429
|
+
convertEntityToResponseDto(entity, _isRaw) {
|
|
3430
|
+
const { clientSecret, ...rest } = entity;
|
|
3431
|
+
return {
|
|
3432
|
+
...rest,
|
|
3433
|
+
hasClientSecret: !!clientSecret
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3289
3436
|
async getExtraManipulateQuery(query, filterDto, user) {
|
|
3290
3437
|
const result = await super.getExtraManipulateQuery(query, filterDto, user);
|
|
3291
3438
|
query.orderBy(`${this.entityName}.provider`, 'ASC');
|
|
@@ -3320,15 +3467,18 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
3320
3467
|
/* import */ var _nestjs_common__rspack_import_3 = __webpack_require__(6157);
|
|
3321
3468
|
/* import */ var crypto__rspack_import_4 = __webpack_require__(7823);
|
|
3322
3469
|
/* import */ var typeorm__rspack_import_5 = __webpack_require__(7158);
|
|
3323
|
-
/* import */ var
|
|
3324
|
-
/* import */ var
|
|
3325
|
-
/* import */ var
|
|
3470
|
+
/* import */ var _config_index_js__rspack_import_14 = __webpack_require__(5363);
|
|
3471
|
+
/* import */ var _config_index_js__rspack_import_15 = __webpack_require__(7987);
|
|
3472
|
+
/* import */ var _config_index_js__rspack_import_13 = __webpack_require__(5588);
|
|
3326
3473
|
/* import */ var _entities_app_user_entity_js__rspack_import_6 = __webpack_require__(4758);
|
|
3327
3474
|
/* import */ var _entities_social_auth_config_entity_js__rspack_import_7 = __webpack_require__(2020);
|
|
3328
|
-
/* import */ var
|
|
3329
|
-
/* import */ var
|
|
3330
|
-
/* import */ var
|
|
3331
|
-
/* import */ var
|
|
3475
|
+
/* import */ var _enums_social_provider_type_enum_js__rspack_import_17 = __webpack_require__(5142);
|
|
3476
|
+
/* import */ var _helpers_cache_invalidation_helper_js__rspack_import_8 = __webpack_require__(37);
|
|
3477
|
+
/* import */ var _helpers_token_helper_js__rspack_import_16 = __webpack_require__(1379);
|
|
3478
|
+
/* import */ var _providers_social_provider_registry_js__rspack_import_9 = __webpack_require__(6928);
|
|
3479
|
+
/* import */ var _auth_config_service_js__rspack_import_10 = __webpack_require__(6514);
|
|
3480
|
+
/* import */ var _auth_datasource_provider_js__rspack_import_11 = __webpack_require__(2732);
|
|
3481
|
+
/* import */ var _authentication_service_js__rspack_import_12 = __webpack_require__(4722);
|
|
3332
3482
|
function _ts_decorate(decorators, target, key, desc) {
|
|
3333
3483
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3334
3484
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -3365,14 +3515,19 @@ function _ts_param(paramIndex, decorator) {
|
|
|
3365
3515
|
|
|
3366
3516
|
|
|
3367
3517
|
|
|
3518
|
+
|
|
3519
|
+
|
|
3520
|
+
|
|
3368
3521
|
class SocialAuthService {
|
|
3369
3522
|
authService;
|
|
3370
3523
|
dataSourceProvider;
|
|
3371
3524
|
cacheManager;
|
|
3372
|
-
|
|
3525
|
+
authConfig;
|
|
3526
|
+
constructor(authService, dataSourceProvider, cacheManager, authConfig){
|
|
3373
3527
|
this.authService = authService;
|
|
3374
3528
|
this.dataSourceProvider = dataSourceProvider;
|
|
3375
3529
|
this.cacheManager = cacheManager;
|
|
3530
|
+
this.authConfig = authConfig;
|
|
3376
3531
|
}
|
|
3377
3532
|
async listEnabledProviders(redirectUri) {
|
|
3378
3533
|
const repository = await this.dataSourceProvider.getRepository(_entities_social_auth_config_entity_js__rspack_import_7/* .SocialAuthConfig */.G);
|
|
@@ -3393,21 +3548,23 @@ class SocialAuthService {
|
|
|
3393
3548
|
await this.getCache().set(this.stateKey(state), {
|
|
3394
3549
|
provider,
|
|
3395
3550
|
redirectUri
|
|
3396
|
-
}, (/* inlined export .SOCIAL_STATE_TTL_MS */600000)
|
|
3551
|
+
}, (/* inlined export .SOCIAL_STATE_TTL_MS */600000), {
|
|
3552
|
+
skipMemory: true
|
|
3553
|
+
});
|
|
3397
3554
|
return {
|
|
3398
3555
|
provider,
|
|
3399
|
-
authorizationUrl: (0,
|
|
3556
|
+
authorizationUrl: (0,_providers_social_provider_registry_js__rspack_import_9/* .getSocialProvider */.V)(provider).buildAuthorizationUrl(this.toCredentials(config, redirectUri), state),
|
|
3400
3557
|
state
|
|
3401
3558
|
};
|
|
3402
3559
|
}
|
|
3403
3560
|
async completeLogin(code, state, redirectUri) {
|
|
3404
3561
|
const provider = await this.consumeState(state, redirectUri);
|
|
3405
3562
|
const config = await this.getUsableConfig(provider);
|
|
3406
|
-
const profile = await (0,
|
|
3563
|
+
const profile = await (0,_providers_social_provider_registry_js__rspack_import_9/* .getSocialProvider */.V)(provider).fetchProfile(this.toCredentials(config, redirectUri), code);
|
|
3407
3564
|
if (!profile.providerUserId) {
|
|
3408
3565
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
3409
3566
|
message: `${provider} did not return an account identifier`,
|
|
3410
|
-
messageKey:
|
|
3567
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.PROFILE_INCOMPLETE */.OF.PROFILE_INCOMPLETE,
|
|
3411
3568
|
messageVariables: {
|
|
3412
3569
|
provider
|
|
3413
3570
|
}
|
|
@@ -3415,7 +3572,7 @@ class SocialAuthService {
|
|
|
3415
3572
|
}
|
|
3416
3573
|
const user = await this.resolveUser(provider, profile);
|
|
3417
3574
|
const response = await this.authService.issueLoginForUser(user);
|
|
3418
|
-
this.publishSocialEvent(
|
|
3575
|
+
this.publishSocialEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.SESSION */.g.SESSION, _config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.SOCIAL_LOGGED_IN */.o.SOCIAL_LOGGED_IN, user, provider);
|
|
3419
3576
|
return response;
|
|
3420
3577
|
}
|
|
3421
3578
|
async resolveUser(provider, profile) {
|
|
@@ -3430,27 +3587,33 @@ class SocialAuthService {
|
|
|
3430
3587
|
if (existing?.deletedAt) {
|
|
3431
3588
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
3432
3589
|
message: 'This account has been permanently deleted. Please contact your system administrator.',
|
|
3433
|
-
messageKey:
|
|
3590
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.ACCOUNT_DELETED */.OF.ACCOUNT_DELETED
|
|
3434
3591
|
});
|
|
3435
3592
|
}
|
|
3436
3593
|
if (existing) {
|
|
3437
|
-
|
|
3438
|
-
// so a social login carries at least as much proof as clicking a verification link.
|
|
3439
|
-
if (!existing.emailVerified) {
|
|
3440
|
-
existing.emailVerified = true;
|
|
3441
|
-
await userRepository.save(existing);
|
|
3442
|
-
}
|
|
3594
|
+
if (!existing.emailVerified) await this.claimUnverifiedAccount(userRepository, existing);
|
|
3443
3595
|
return existing;
|
|
3444
3596
|
}
|
|
3445
3597
|
const user = await this.registerUser(provider, userRepository, profile);
|
|
3446
|
-
this.publishSocialEvent(
|
|
3598
|
+
this.publishSocialEvent(_config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER, _config_index_js__rspack_import_15/* .AUTH_EVENT_ACTIONS.REGISTERED */.o.REGISTERED, user, provider);
|
|
3447
3599
|
return user;
|
|
3448
3600
|
}
|
|
3601
|
+
async claimUnverifiedAccount(userRepository, user) {
|
|
3602
|
+
user.emailVerified = true;
|
|
3603
|
+
user.password = null;
|
|
3604
|
+
await userRepository.save(user);
|
|
3605
|
+
await (0,_helpers_cache_invalidation_helper_js__rspack_import_8/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
|
|
3606
|
+
_config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER
|
|
3607
|
+
]);
|
|
3608
|
+
if (this.cacheManager && this.authConfig) {
|
|
3609
|
+
await (0,_helpers_token_helper_js__rspack_import_16/* .revokeUserSessions */.XP)(this.cacheManager, this.authConfig, user.id);
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3449
3612
|
ensureVerifiedEmail(provider, profile) {
|
|
3450
3613
|
if (!profile.email) {
|
|
3451
3614
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
3452
3615
|
message: `${provider} did not share an email address`,
|
|
3453
|
-
messageKey:
|
|
3616
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.EMAIL_REQUIRED */.OF.EMAIL_REQUIRED,
|
|
3454
3617
|
messageVariables: {
|
|
3455
3618
|
provider
|
|
3456
3619
|
}
|
|
@@ -3459,7 +3622,7 @@ class SocialAuthService {
|
|
|
3459
3622
|
if (!profile.emailVerified) {
|
|
3460
3623
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
3461
3624
|
message: `${provider} has not verified this email address`,
|
|
3462
|
-
messageKey:
|
|
3625
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.EMAIL_NOT_VERIFIED */.OF.EMAIL_NOT_VERIFIED,
|
|
3463
3626
|
messageVariables: {
|
|
3464
3627
|
provider
|
|
3465
3628
|
}
|
|
@@ -3470,7 +3633,7 @@ class SocialAuthService {
|
|
|
3470
3633
|
if (!this.dataSourceProvider.isSignUpEnabled()) {
|
|
3471
3634
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
3472
3635
|
message: `No account is linked to this ${provider} profile`,
|
|
3473
|
-
messageKey:
|
|
3636
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.ACCOUNT_NOT_LINKED */.OF.ACCOUNT_NOT_LINKED,
|
|
3474
3637
|
messageVariables: {
|
|
3475
3638
|
provider
|
|
3476
3639
|
}
|
|
@@ -3479,19 +3642,23 @@ class SocialAuthService {
|
|
|
3479
3642
|
if (this.dataSourceProvider.isCompanyFeatureEnabled()) {
|
|
3480
3643
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
3481
3644
|
message: `No account is linked to this ${provider} profile. Please sign up manually and join or create a company first, then social sign-in will work.`,
|
|
3482
|
-
messageKey:
|
|
3645
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.COMPANY_REQUIRED */.OF.COMPANY_REQUIRED,
|
|
3483
3646
|
messageVariables: {
|
|
3484
3647
|
provider
|
|
3485
3648
|
}
|
|
3486
3649
|
});
|
|
3487
3650
|
}
|
|
3488
|
-
|
|
3651
|
+
const user = await userRepository.save(userRepository.create({
|
|
3489
3652
|
email: profile.email,
|
|
3490
3653
|
name: profile.name,
|
|
3491
3654
|
password: null,
|
|
3492
3655
|
isActive: true,
|
|
3493
3656
|
emailVerified: true
|
|
3494
3657
|
}));
|
|
3658
|
+
await (0,_helpers_cache_invalidation_helper_js__rspack_import_8/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
|
|
3659
|
+
_config_index_js__rspack_import_15/* .AUTH_EVENT_ENTITIES.USER */.g.USER
|
|
3660
|
+
]);
|
|
3661
|
+
return user;
|
|
3495
3662
|
}
|
|
3496
3663
|
async getUsableConfig(provider) {
|
|
3497
3664
|
const repository = await this.dataSourceProvider.getRepository(_entities_social_auth_config_entity_js__rspack_import_7/* .SocialAuthConfig */.G);
|
|
@@ -3504,7 +3671,7 @@ class SocialAuthService {
|
|
|
3504
3671
|
if (!config?.isEnabled) {
|
|
3505
3672
|
throw new _nestjs_common__rspack_import_3.BadRequestException({
|
|
3506
3673
|
message: `Social sign-in with ${provider} is not enabled`,
|
|
3507
|
-
messageKey:
|
|
3674
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.PROVIDER_DISABLED */.OF.PROVIDER_DISABLED,
|
|
3508
3675
|
messageVariables: {
|
|
3509
3676
|
provider
|
|
3510
3677
|
}
|
|
@@ -3513,7 +3680,7 @@ class SocialAuthService {
|
|
|
3513
3680
|
if (!config.clientId || !config.clientSecret) {
|
|
3514
3681
|
throw new _nestjs_common__rspack_import_3.BadRequestException({
|
|
3515
3682
|
message: `Social sign-in with ${provider} is missing its client credentials`,
|
|
3516
|
-
messageKey:
|
|
3683
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.PROVIDER_NOT_CONFIGURED */.OF.PROVIDER_NOT_CONFIGURED,
|
|
3517
3684
|
messageVariables: {
|
|
3518
3685
|
provider
|
|
3519
3686
|
}
|
|
@@ -3531,25 +3698,24 @@ class SocialAuthService {
|
|
|
3531
3698
|
};
|
|
3532
3699
|
}
|
|
3533
3700
|
async consumeState(state, redirectUri) {
|
|
3534
|
-
|
|
3535
|
-
const stored = await this.getCache().
|
|
3536
|
-
await this.getCache().del(key);
|
|
3701
|
+
// Atomic across instances: a state (and the code that comes with it) is replayable nowhere
|
|
3702
|
+
const stored = await this.getCache().take(this.stateKey(state), (/* inlined export .SOCIAL_STATE_TTL_MS */600000));
|
|
3537
3703
|
if (!stored) {
|
|
3538
3704
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
3539
3705
|
message: 'Social sign-in session is invalid or has expired',
|
|
3540
|
-
messageKey:
|
|
3706
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.STATE_INVALID */.OF.STATE_INVALID
|
|
3541
3707
|
});
|
|
3542
3708
|
}
|
|
3543
3709
|
if (stored.redirectUri !== redirectUri) {
|
|
3544
3710
|
throw new _nestjs_common__rspack_import_3.UnauthorizedException({
|
|
3545
3711
|
message: 'Redirect URI does not match the one the sign-in started with',
|
|
3546
|
-
messageKey:
|
|
3712
|
+
messageKey: _config_index_js__rspack_import_14/* .SOCIAL_AUTH_MESSAGES.REDIRECT_URI_MISMATCH */.OF.REDIRECT_URI_MISMATCH
|
|
3547
3713
|
});
|
|
3548
3714
|
}
|
|
3549
3715
|
return stored.provider;
|
|
3550
3716
|
}
|
|
3551
3717
|
stateKey(state) {
|
|
3552
|
-
return `${
|
|
3718
|
+
return `${_config_index_js__rspack_import_13/* .SOCIAL_STATE_PREFIX */.PR}:${state}`;
|
|
3553
3719
|
}
|
|
3554
3720
|
getCache() {
|
|
3555
3721
|
if (!this.cacheManager) {
|
|
@@ -3591,7 +3757,7 @@ _ts_decorate([
|
|
|
3591
3757
|
}),
|
|
3592
3758
|
_ts_metadata("design:type", Function),
|
|
3593
3759
|
_ts_metadata("design:paramtypes", [
|
|
3594
|
-
typeof
|
|
3760
|
+
typeof _enums_social_provider_type_enum_js__rspack_import_17/* .SocialProviderTypeEnum */.u === "undefined" ? Object : _enums_social_provider_type_enum_js__rspack_import_17/* .SocialProviderTypeEnum */.u,
|
|
3595
3761
|
String
|
|
3596
3762
|
]),
|
|
3597
3763
|
_ts_metadata("design:returntype", Promise)
|
|
@@ -3614,15 +3780,18 @@ SocialAuthService = _ts_decorate([
|
|
|
3614
3780
|
(0,_nestjs_common__rspack_import_3.Injectable)({
|
|
3615
3781
|
scope: _nestjs_common__rspack_import_3.Scope.REQUEST
|
|
3616
3782
|
}),
|
|
3617
|
-
_ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)(
|
|
3618
|
-
_ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(
|
|
3783
|
+
_ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)(_authentication_service_js__rspack_import_12/* .AuthenticationService */.k)),
|
|
3784
|
+
_ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_auth_datasource_provider_js__rspack_import_11/* .AuthDataSourceProvider */.v)),
|
|
3619
3785
|
_ts_param(2, (0,_nestjs_common__rspack_import_3.Optional)()),
|
|
3620
3786
|
_ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)('CACHE_INSTANCE')),
|
|
3787
|
+
_ts_param(3, (0,_nestjs_common__rspack_import_3.Optional)()),
|
|
3788
|
+
_ts_param(3, (0,_nestjs_common__rspack_import_3.Inject)(_auth_config_service_js__rspack_import_10/* .AuthConfigService */.W)),
|
|
3621
3789
|
_ts_metadata("design:type", Function),
|
|
3622
3790
|
_ts_metadata("design:paramtypes", [
|
|
3623
|
-
typeof
|
|
3624
|
-
typeof
|
|
3625
|
-
typeof _flusys_nestjs_shared_classes__rspack_import_1.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_1.HybridCache
|
|
3791
|
+
typeof _authentication_service_js__rspack_import_12/* .AuthenticationService */.k === "undefined" ? Object : _authentication_service_js__rspack_import_12/* .AuthenticationService */.k,
|
|
3792
|
+
typeof _auth_datasource_provider_js__rspack_import_11/* .AuthDataSourceProvider */.v === "undefined" ? Object : _auth_datasource_provider_js__rspack_import_11/* .AuthDataSourceProvider */.v,
|
|
3793
|
+
typeof _flusys_nestjs_shared_classes__rspack_import_1.HybridCache === "undefined" ? Object : _flusys_nestjs_shared_classes__rspack_import_1.HybridCache,
|
|
3794
|
+
typeof _auth_config_service_js__rspack_import_10/* .AuthConfigService */.W === "undefined" ? Object : _auth_config_service_js__rspack_import_10/* .AuthConfigService */.W
|
|
3626
3795
|
])
|
|
3627
3796
|
], SocialAuthService);
|
|
3628
3797
|
|
|
@@ -3637,12 +3806,12 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
3637
3806
|
/* import */ var _flusys_nestjs_shared_classes__rspack_import_1 = __webpack_require__(6937);
|
|
3638
3807
|
/* import */ var _flusys_nestjs_shared_interfaces__rspack_import_2 = __webpack_require__(5103);
|
|
3639
3808
|
/* import */ var _nestjs_common__rspack_import_3 = __webpack_require__(6157);
|
|
3640
|
-
/* import */ var _config_index_js__rspack_import_9 = __webpack_require__(
|
|
3641
|
-
/* import */ var _config_index_js__rspack_import_10 = __webpack_require__(
|
|
3809
|
+
/* import */ var _config_index_js__rspack_import_9 = __webpack_require__(7987);
|
|
3810
|
+
/* import */ var _config_index_js__rspack_import_10 = __webpack_require__(5363);
|
|
3642
3811
|
/* import */ var _entities_company_branch_entity_js__rspack_import_4 = __webpack_require__(1923);
|
|
3643
3812
|
/* import */ var _entities_user_company_permission_entity_js__rspack_import_5 = __webpack_require__(2377);
|
|
3644
3813
|
/* import */ var _helpers_branch_ancestry_helper_js__rspack_import_6 = __webpack_require__(7526);
|
|
3645
|
-
/* import */ var
|
|
3814
|
+
/* import */ var _helpers_cache_invalidation_helper_js__rspack_import_7 = __webpack_require__(37);
|
|
3646
3815
|
/* import */ var _auth_datasource_provider_js__rspack_import_8 = __webpack_require__(2732);
|
|
3647
3816
|
function _ts_decorate(decorators, target, key, desc) {
|
|
3648
3817
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -3677,20 +3846,28 @@ function _ts_param(paramIndex, decorator) {
|
|
|
3677
3846
|
|
|
3678
3847
|
|
|
3679
3848
|
|
|
3849
|
+
const UNIQUE_VIOLATION_CODES = new Set([
|
|
3850
|
+
'ER_DUP_ENTRY',
|
|
3851
|
+
'23505',
|
|
3852
|
+
'SQLITE_CONSTRAINT'
|
|
3853
|
+
]);
|
|
3680
3854
|
class UserPermissionService {
|
|
3681
3855
|
dataSourceProvider;
|
|
3682
|
-
authConfig;
|
|
3683
3856
|
permissionSyncAdapter;
|
|
3857
|
+
cacheManager;
|
|
3684
3858
|
_repository = null;
|
|
3685
|
-
constructor(dataSourceProvider,
|
|
3859
|
+
constructor(dataSourceProvider, permissionSyncAdapter, cacheManager){
|
|
3686
3860
|
this.dataSourceProvider = dataSourceProvider;
|
|
3687
|
-
this.authConfig = authConfig;
|
|
3688
3861
|
this.permissionSyncAdapter = permissionSyncAdapter;
|
|
3862
|
+
this.cacheManager = cacheManager;
|
|
3689
3863
|
}
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3864
|
+
// The user list caches each user's grant; it depends on this stamp
|
|
3865
|
+
async invalidateGrantCache() {
|
|
3866
|
+
await (0,_helpers_cache_invalidation_helper_js__rspack_import_7/* .invalidateEntityCaches */.i)(this.cacheManager, this.dataSourceProvider, [
|
|
3867
|
+
_config_index_js__rspack_import_9/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION
|
|
3868
|
+
]);
|
|
3869
|
+
}
|
|
3870
|
+
async getRepository() {
|
|
3694
3871
|
if (!this._repository) {
|
|
3695
3872
|
this._repository = await this.dataSourceProvider.getRepository(_entities_user_company_permission_entity_js__rspack_import_5/* .UserCompanyPermission */.I);
|
|
3696
3873
|
}
|
|
@@ -3725,7 +3902,7 @@ class UserPermissionService {
|
|
|
3725
3902
|
if (existing) {
|
|
3726
3903
|
throw new _nestjs_common__rspack_import_3.ConflictException({
|
|
3727
3904
|
message: `User already assigned to this ${typeName}`,
|
|
3728
|
-
messageKey:
|
|
3905
|
+
messageKey: _config_index_js__rspack_import_10/* .USER_PERMISSION_MESSAGES.ALREADY_ASSIGNED */.jz.ALREADY_ASSIGNED,
|
|
3729
3906
|
messageVariables: {
|
|
3730
3907
|
type: typeName
|
|
3731
3908
|
}
|
|
@@ -3739,10 +3916,10 @@ class UserPermissionService {
|
|
|
3739
3916
|
}));
|
|
3740
3917
|
} catch (error) {
|
|
3741
3918
|
const err = error;
|
|
3742
|
-
if (err?.code
|
|
3919
|
+
if (UNIQUE_VIOLATION_CODES.has(String(err?.code)) || String(err?.message).includes('Duplicate entry')) {
|
|
3743
3920
|
throw new _nestjs_common__rspack_import_3.ConflictException({
|
|
3744
3921
|
message: `User already assigned to this ${typeName}. Please refresh and try again.`,
|
|
3745
|
-
messageKey:
|
|
3922
|
+
messageKey: _config_index_js__rspack_import_10/* .USER_PERMISSION_MESSAGES.ALREADY_ASSIGNED */.jz.ALREADY_ASSIGNED,
|
|
3746
3923
|
messageVariables: {
|
|
3747
3924
|
type: typeName
|
|
3748
3925
|
}
|
|
@@ -3750,7 +3927,8 @@ class UserPermissionService {
|
|
|
3750
3927
|
}
|
|
3751
3928
|
throw error;
|
|
3752
3929
|
}
|
|
3753
|
-
await this.
|
|
3930
|
+
await this.invalidateGrantCache();
|
|
3931
|
+
await this.publishPermissionEvent(_config_index_js__rspack_import_9/* .AUTH_EVENT_ACTIONS.PERMISSION_GRANTED */.o.PERMISSION_GRANTED, userId, targetId, type);
|
|
3754
3932
|
}
|
|
3755
3933
|
async revokePermission(userId, targetId, type) {
|
|
3756
3934
|
const repository = await this.getRepository();
|
|
@@ -3768,7 +3946,7 @@ class UserPermissionService {
|
|
|
3768
3946
|
if (!permission) {
|
|
3769
3947
|
throw new _nestjs_common__rspack_import_3.NotFoundException({
|
|
3770
3948
|
message: `${typeName.charAt(0).toUpperCase() + typeName.slice(1)} permission not found`,
|
|
3771
|
-
messageKey:
|
|
3949
|
+
messageKey: _config_index_js__rspack_import_10/* .USER_PERMISSION_MESSAGES.PERMISSION_NOT_FOUND */.jz.PERMISSION_NOT_FOUND,
|
|
3772
3950
|
messageVariables: {
|
|
3773
3951
|
type: typeName.charAt(0).toUpperCase() + typeName.slice(1)
|
|
3774
3952
|
}
|
|
@@ -3783,12 +3961,13 @@ class UserPermissionService {
|
|
|
3783
3961
|
} else {
|
|
3784
3962
|
if (companyId) await this.permissionSyncAdapter?.onUserBranchAccessRevoked?.(userId, targetId, companyId);
|
|
3785
3963
|
}
|
|
3786
|
-
await this.
|
|
3964
|
+
await this.invalidateGrantCache();
|
|
3965
|
+
await this.publishPermissionEvent(_config_index_js__rspack_import_9/* .AUTH_EVENT_ACTIONS.PERMISSION_REVOKED */.o.PERMISSION_REVOKED, userId, targetId, type, companyId);
|
|
3787
3966
|
}
|
|
3788
3967
|
async publishPermissionEvent(action, userId, targetId, type, companyId) {
|
|
3789
3968
|
await (0,_flusys_nestjs_shared_classes__rspack_import_1.publishDomainEvent)({
|
|
3790
3969
|
module: (/* inlined export .AUTH_EVENT_MODULE */"auth"),
|
|
3791
|
-
entity:
|
|
3970
|
+
entity: _config_index_js__rspack_import_9/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION,
|
|
3792
3971
|
action,
|
|
3793
3972
|
ids: [
|
|
3794
3973
|
targetId
|
|
@@ -3898,14 +4077,15 @@ UserPermissionService = _ts_decorate([
|
|
|
3898
4077
|
scope: _nestjs_common__rspack_import_3.Scope.REQUEST
|
|
3899
4078
|
}),
|
|
3900
4079
|
_ts_param(0, (0,_nestjs_common__rspack_import_3.Inject)(_auth_datasource_provider_js__rspack_import_8/* .AuthDataSourceProvider */.v)),
|
|
3901
|
-
_ts_param(1, (0,_nestjs_common__rspack_import_3.
|
|
4080
|
+
_ts_param(1, (0,_nestjs_common__rspack_import_3.Optional)()),
|
|
4081
|
+
_ts_param(1, (0,_nestjs_common__rspack_import_3.Inject)(_flusys_nestjs_shared_interfaces__rspack_import_2.PERMISSION_SYNC_ADAPTER)),
|
|
3902
4082
|
_ts_param(2, (0,_nestjs_common__rspack_import_3.Optional)()),
|
|
3903
|
-
_ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)(
|
|
4083
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_3.Inject)('CACHE_INSTANCE')),
|
|
3904
4084
|
_ts_metadata("design:type", Function),
|
|
3905
4085
|
_ts_metadata("design:paramtypes", [
|
|
3906
4086
|
typeof _auth_datasource_provider_js__rspack_import_8/* .AuthDataSourceProvider */.v === "undefined" ? Object : _auth_datasource_provider_js__rspack_import_8/* .AuthDataSourceProvider */.v,
|
|
3907
|
-
typeof
|
|
3908
|
-
typeof
|
|
4087
|
+
typeof IPermissionSyncAdapter === "undefined" ? Object : IPermissionSyncAdapter,
|
|
4088
|
+
typeof HybridCache === "undefined" ? Object : HybridCache
|
|
3909
4089
|
])
|
|
3910
4090
|
], UserPermissionService);
|
|
3911
4091
|
|
|
@@ -3925,7 +4105,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
3925
4105
|
/* import */ var _config_index_js__rspack_import_12 = __webpack_require__(7987);
|
|
3926
4106
|
/* import */ var _config_index_js__rspack_import_13 = __webpack_require__(5363);
|
|
3927
4107
|
/* import */ var _config_index_js__rspack_import_15 = __webpack_require__(5588);
|
|
3928
|
-
/* import */ var _helpers_index_js__rspack_import_6 = __webpack_require__(
|
|
4108
|
+
/* import */ var _helpers_index_js__rspack_import_6 = __webpack_require__(5634);
|
|
3929
4109
|
/* import */ var _dtos_user_dto_js__rspack_import_7 = __webpack_require__(7265);
|
|
3930
4110
|
/* import */ var _entities_app_user_entity_js__rspack_import_8 = __webpack_require__(4758);
|
|
3931
4111
|
/* import */ var _entities_user_company_permission_entity_js__rspack_import_9 = __webpack_require__(2377);
|
|
@@ -3992,6 +4172,19 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
3992
4172
|
this.dataSource = await this.getDataSourceProvider().getDataSource();
|
|
3993
4173
|
}
|
|
3994
4174
|
}
|
|
4175
|
+
// The company-scoped list reads each user's grant (companyIsActive, membership)
|
|
4176
|
+
cacheDependencies() {
|
|
4177
|
+
return [
|
|
4178
|
+
this.entityName,
|
|
4179
|
+
_config_index_js__rspack_import_12/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION
|
|
4180
|
+
];
|
|
4181
|
+
}
|
|
4182
|
+
async invalidateUserAndGrants() {
|
|
4183
|
+
await Promise.all([
|
|
4184
|
+
this.invalidateCache(),
|
|
4185
|
+
this.utilsService.clearCache(_config_index_js__rspack_import_12/* .AUTH_EVENT_ENTITIES.USER_PERMISSION */.g.USER_PERMISSION, this.cacheManager, this.cacheTenant())
|
|
4186
|
+
]);
|
|
4187
|
+
}
|
|
3995
4188
|
async getUserForProfile(userId, user) {
|
|
3996
4189
|
if (userId !== user.id) {
|
|
3997
4190
|
throw new _nestjs_common__rspack_import_4.ForbiddenException({
|
|
@@ -4018,32 +4211,24 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4018
4211
|
await this.repository.update(id, {
|
|
4019
4212
|
emailVerified: true
|
|
4020
4213
|
});
|
|
4021
|
-
await
|
|
4022
|
-
this.clearCacheForAll(),
|
|
4023
|
-
this.clearCacheForId([
|
|
4024
|
-
{
|
|
4025
|
-
id
|
|
4026
|
-
}
|
|
4027
|
-
])
|
|
4028
|
-
]);
|
|
4214
|
+
await this.invalidateCache();
|
|
4029
4215
|
}
|
|
4030
4216
|
async verifyPhone(id) {
|
|
4031
4217
|
await this.ensureDataSourceRepository();
|
|
4032
4218
|
await this.repository.update(id, {
|
|
4033
4219
|
phoneVerified: true
|
|
4034
4220
|
});
|
|
4035
|
-
await
|
|
4036
|
-
this.clearCacheForAll(),
|
|
4037
|
-
this.clearCacheForId([
|
|
4038
|
-
{
|
|
4039
|
-
id
|
|
4040
|
-
}
|
|
4041
|
-
])
|
|
4042
|
-
]);
|
|
4221
|
+
await this.invalidateCache();
|
|
4043
4222
|
}
|
|
4044
4223
|
async updateStatus(id, isActive, user) {
|
|
4045
4224
|
await this.ensureDataSourceRepository();
|
|
4046
4225
|
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
4226
|
+
if (isCompanyFeatureEnabled && user && !user.companyId) {
|
|
4227
|
+
throw new _nestjs_common__rspack_import_4.ForbiddenException({
|
|
4228
|
+
message: 'Select a company before changing a user status',
|
|
4229
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.COMPANY_REQUIRED */.mK.COMPANY_REQUIRED
|
|
4230
|
+
});
|
|
4231
|
+
}
|
|
4047
4232
|
if (isCompanyFeatureEnabled && user?.companyId && this.permissionRepository) {
|
|
4048
4233
|
const permission = await this.permissionRepository.findOne({
|
|
4049
4234
|
where: {
|
|
@@ -4069,14 +4254,7 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4069
4254
|
});
|
|
4070
4255
|
}
|
|
4071
4256
|
if (!isActive) await (0,_helpers_index_js__rspack_import_6/* .revokeUserSessions */.XP)(this.cacheManager, this.authConfig, id);
|
|
4072
|
-
await
|
|
4073
|
-
this.clearCacheForAll(),
|
|
4074
|
-
this.clearCacheForId([
|
|
4075
|
-
{
|
|
4076
|
-
id
|
|
4077
|
-
}
|
|
4078
|
-
])
|
|
4079
|
-
]);
|
|
4257
|
+
await this.invalidateUserAndGrants();
|
|
4080
4258
|
}
|
|
4081
4259
|
async convertSingleDtoToEntity(dto, user) {
|
|
4082
4260
|
const entity = await super.convertSingleDtoToEntity(dto, user);
|
|
@@ -4211,6 +4389,10 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4211
4389
|
return superQuery;
|
|
4212
4390
|
}
|
|
4213
4391
|
// --- Company feature path ---
|
|
4392
|
+
if (user && !user.companyId) {
|
|
4393
|
+
query.andWhere('1 = 0');
|
|
4394
|
+
return superQuery;
|
|
4395
|
+
}
|
|
4214
4396
|
if (!user?.companyId || !this.permissionRepository) {
|
|
4215
4397
|
return superQuery;
|
|
4216
4398
|
}
|
|
@@ -4297,12 +4479,7 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4297
4479
|
}
|
|
4298
4480
|
}
|
|
4299
4481
|
await queryRunner.commitTransaction();
|
|
4300
|
-
await
|
|
4301
|
-
this.clearCacheForAll(),
|
|
4302
|
-
this.clearCacheForId([
|
|
4303
|
-
savedUser
|
|
4304
|
-
])
|
|
4305
|
-
]);
|
|
4482
|
+
await this.invalidateUserAndGrants();
|
|
4306
4483
|
return this.convertEntityToResponseDto(savedUser, false);
|
|
4307
4484
|
} catch (error) {
|
|
4308
4485
|
await queryRunner.rollbackTransaction();
|
|
@@ -4331,6 +4508,10 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4331
4508
|
total: result.total
|
|
4332
4509
|
};
|
|
4333
4510
|
}
|
|
4511
|
+
// The actionCode filter reads IAM grants, whose writes never move this cache's stamps
|
|
4512
|
+
bypassListCache(filterAndPaginationDto) {
|
|
4513
|
+
return !!filterAndPaginationDto?.filter?.actionCode;
|
|
4514
|
+
}
|
|
4334
4515
|
async getEnrichedList(users, user) {
|
|
4335
4516
|
if (this.userEnricher?.enrichListItems) {
|
|
4336
4517
|
return this.userEnricher.enrichListItems(users, user);
|
|
@@ -4370,12 +4551,7 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4370
4551
|
try {
|
|
4371
4552
|
await this.userEnricher.updateProfileSection(userId, sectionId, data, queryRunner);
|
|
4372
4553
|
await queryRunner.commitTransaction();
|
|
4373
|
-
await
|
|
4374
|
-
this.clearCacheForAll(),
|
|
4375
|
-
this.clearCacheForId([
|
|
4376
|
-
userEntity
|
|
4377
|
-
])
|
|
4378
|
-
]);
|
|
4554
|
+
await this.invalidateCache();
|
|
4379
4555
|
} catch (error) {
|
|
4380
4556
|
await queryRunner.rollbackTransaction();
|
|
4381
4557
|
throw error;
|
|
@@ -4391,6 +4567,12 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4391
4567
|
return 100;
|
|
4392
4568
|
}
|
|
4393
4569
|
async profile(dto, user) {
|
|
4570
|
+
if (dto.id && dto.id !== user.id) {
|
|
4571
|
+
throw new _nestjs_common__rspack_import_4.ForbiddenException({
|
|
4572
|
+
message: "Cannot access another user's profile",
|
|
4573
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PROFILE_ACCESS_DENIED */.mK.PROFILE_ACCESS_DENIED
|
|
4574
|
+
});
|
|
4575
|
+
}
|
|
4394
4576
|
await this.ensureDataSourceRepository();
|
|
4395
4577
|
if (!this.dataSource) {
|
|
4396
4578
|
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
@@ -4398,7 +4580,7 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4398
4580
|
messageKey: _flusys_nestjs_shared_constants__rspack_import_2.SYSTEM_MESSAGES.DATASOURCE_NOT_AVAILABLE
|
|
4399
4581
|
});
|
|
4400
4582
|
}
|
|
4401
|
-
const userId =
|
|
4583
|
+
const userId = user.id;
|
|
4402
4584
|
if (dto.extras && this.userEnricher?.validateProfileExtras) {
|
|
4403
4585
|
await this.userEnricher.validateProfileExtras(userId, dto.extras, user);
|
|
4404
4586
|
}
|
|
@@ -4418,37 +4600,29 @@ class UserService extends _flusys_nestjs_shared_classes__rspack_import_1.ApiServ
|
|
|
4418
4600
|
});
|
|
4419
4601
|
}
|
|
4420
4602
|
if (dto.password) {
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
message: 'Current password is incorrect',
|
|
4433
|
-
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_CHANGE_INVALID_CURRENT */.mK.PASSWORD_CHANGE_INVALID_CURRENT
|
|
4434
|
-
});
|
|
4435
|
-
}
|
|
4603
|
+
if (!dto.oldPassword) {
|
|
4604
|
+
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
4605
|
+
message: 'Current password is required to change your password',
|
|
4606
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_CHANGE_CURRENT_REQUIRED */.mK.PASSWORD_CHANGE_CURRENT_REQUIRED
|
|
4607
|
+
});
|
|
4608
|
+
}
|
|
4609
|
+
if (!findUser.password || !await bcrypt__rspack_import_5.compare(dto.oldPassword, findUser.password)) {
|
|
4610
|
+
throw new _nestjs_common__rspack_import_4.BadRequestException({
|
|
4611
|
+
message: 'Current password is incorrect',
|
|
4612
|
+
messageKey: _config_index_js__rspack_import_13/* .AUTH_MESSAGES.PASSWORD_CHANGE_INVALID_CURRENT */.mK.PASSWORD_CHANGE_INVALID_CURRENT
|
|
4613
|
+
});
|
|
4436
4614
|
}
|
|
4437
4615
|
findUser.password = await bcrypt__rspack_import_5.hash(dto.password, (/* inlined export .BCRYPT_SALT_ROUNDS */12));
|
|
4438
4616
|
}
|
|
4439
|
-
const { password, oldPassword, extras, emailVerified, phoneVerified, isActive, ...restDto } = dto;
|
|
4617
|
+
const { id, password, oldPassword, extras, emailVerified, phoneVerified, isActive, ...restDto } = dto;
|
|
4618
|
+
if (restDto.email && restDto.email !== findUser.email) findUser.emailVerified = false;
|
|
4440
4619
|
Object.assign(findUser, restDto);
|
|
4441
4620
|
await queryRunner.manager.save(this.repository.target, findUser);
|
|
4442
4621
|
if (extras && this.userEnricher?.updateProfileExtras) {
|
|
4443
4622
|
await this.userEnricher.updateProfileExtras(userId, extras, queryRunner);
|
|
4444
4623
|
}
|
|
4445
4624
|
await queryRunner.commitTransaction();
|
|
4446
|
-
await
|
|
4447
|
-
this.clearCacheForAll(),
|
|
4448
|
-
this.clearCacheForId([
|
|
4449
|
-
findUser
|
|
4450
|
-
])
|
|
4451
|
-
]);
|
|
4625
|
+
await this.invalidateCache();
|
|
4452
4626
|
return findUser.id;
|
|
4453
4627
|
} catch (error) {
|
|
4454
4628
|
await queryRunner.rollbackTransaction();
|