@flusys/nestjs-auth 5.0.1 → 5.0.4
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 +13 -14
- package/cjs/controllers/user-permission.controller.js +2 -2
- package/cjs/controllers/user.controller.js +5 -8
- package/cjs/entities/{user.entity.js → app-user.entity.js} +6 -6
- package/cjs/entities/index.js +2 -2
- package/cjs/entities/user-company-permission.entity.js +3 -3
- package/cjs/services/auth-config.service.js +3 -0
- package/cjs/services/auth-email.service.js +6 -6
- package/cjs/services/authentication.service.js +8 -8
- package/cjs/services/user.service.js +117 -54
- package/cjs/strategies/jwt.strategy.js +2 -2
- package/entities/{user.entity.d.ts → app-user.entity.d.ts} +1 -1
- package/entities/index.d.ts +4 -4
- package/entities/user-company-permission.entity.d.ts +2 -2
- package/fesm/controllers/user-permission.controller.js +3 -3
- package/fesm/controllers/user.controller.js +6 -9
- package/fesm/entities/{user.entity.js → app-user.entity.js} +4 -4
- package/fesm/entities/index.js +3 -3
- package/fesm/entities/user-company-permission.entity.js +3 -3
- package/fesm/services/auth-config.service.js +3 -0
- package/fesm/services/auth-email.service.js +6 -6
- package/fesm/services/authentication.service.js +8 -8
- package/fesm/services/branch.service.js +1 -1
- package/fesm/services/company.service.js +1 -1
- package/fesm/services/user.service.js +118 -55
- package/fesm/strategies/jwt.strategy.js +2 -2
- package/interfaces/user-enricher.interface.d.ts +1 -5
- package/package.json +3 -3
- package/services/auth-config.service.d.ts +1 -0
- package/services/branch.service.d.ts +4 -4
- package/services/company.service.d.ts +3 -3
- package/services/user.service.d.ts +16 -11
|
@@ -25,10 +25,10 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import { JwtAuthGuard, RequirePermission, USER_PERMISSIONS } from '@flusys/nestjs-shared';
|
|
29
|
-
import { USER_PERMISSION_MESSAGES } from '../config';
|
|
28
|
+
import { JwtAuthGuard, PermissionGuard, RequirePermission, USER_PERMISSIONS } from '@flusys/nestjs-shared';
|
|
30
29
|
import { Body, Controller, Inject, Post, UseGuards } from '@nestjs/common';
|
|
31
30
|
import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|
31
|
+
import { USER_PERMISSION_MESSAGES } from '../config';
|
|
32
32
|
import { AssignUserBranchDto, AssignUserCompanyDto, UserBranchesResponseDto, UserCompaniesResponseDto } from '../dtos/user-permission.dto';
|
|
33
33
|
import { PermissionType } from '../entities/user-company-permission.entity';
|
|
34
34
|
import { UserPermissionService } from '../services/user-permission.service';
|
|
@@ -204,7 +204,7 @@ UserPermissionController = _ts_decorate([
|
|
|
204
204
|
ApiTags('User Permissions'),
|
|
205
205
|
ApiBearerAuth(),
|
|
206
206
|
Controller('administration/permissions'),
|
|
207
|
-
UseGuards(JwtAuthGuard),
|
|
207
|
+
UseGuards(JwtAuthGuard, PermissionGuard),
|
|
208
208
|
_ts_param(0, Inject(UserPermissionService)),
|
|
209
209
|
_ts_metadata("design:type", Function),
|
|
210
210
|
_ts_metadata("design:paramtypes", [
|
|
@@ -26,12 +26,12 @@ function _ts_param(paramIndex, decorator) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
import { createApiController, USER_PERMISSIONS } from '@flusys/nestjs-shared/classes';
|
|
29
|
-
import { USER_MESSAGES } from '../config';
|
|
30
29
|
import { CurrentUser, RequirePermission } from '@flusys/nestjs-shared/decorators';
|
|
31
|
-
import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
|
|
30
|
+
import { JwtAuthGuard, PermissionGuard } from '@flusys/nestjs-shared/guards';
|
|
32
31
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
33
32
|
import { Body, Controller, Inject, Param, Post, UseGuards } from '@nestjs/common';
|
|
34
33
|
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
|
|
34
|
+
import { USER_MESSAGES } from '../config';
|
|
35
35
|
import { CreateUserDto, UpdateUserDto, UserResponseDto } from '../dtos/user.dto';
|
|
36
36
|
import { UserService } from '../services/user.service';
|
|
37
37
|
export class UserController extends createApiController(CreateUserDto, UpdateUserDto, UserResponseDto, {
|
|
@@ -166,7 +166,7 @@ export class UserController extends createApiController(CreateUserDto, UpdateUse
|
|
|
166
166
|
}
|
|
167
167
|
_ts_decorate([
|
|
168
168
|
Post(':id/profile-extras'),
|
|
169
|
-
UseGuards(JwtAuthGuard),
|
|
169
|
+
UseGuards(JwtAuthGuard, PermissionGuard),
|
|
170
170
|
ApiBearerAuth(),
|
|
171
171
|
RequirePermission(USER_PERMISSIONS.READ),
|
|
172
172
|
ApiOperation({
|
|
@@ -188,7 +188,7 @@ _ts_decorate([
|
|
|
188
188
|
], UserController.prototype, "getProfileWithExtras", null);
|
|
189
189
|
_ts_decorate([
|
|
190
190
|
Post(':id/verify-email'),
|
|
191
|
-
UseGuards(JwtAuthGuard),
|
|
191
|
+
UseGuards(JwtAuthGuard, PermissionGuard),
|
|
192
192
|
ApiBearerAuth(),
|
|
193
193
|
RequirePermission(USER_PERMISSIONS.UPDATE),
|
|
194
194
|
ApiOperation({
|
|
@@ -207,7 +207,7 @@ _ts_decorate([
|
|
|
207
207
|
], UserController.prototype, "verifyEmail", null);
|
|
208
208
|
_ts_decorate([
|
|
209
209
|
Post(':id/verify-phone'),
|
|
210
|
-
UseGuards(JwtAuthGuard),
|
|
210
|
+
UseGuards(JwtAuthGuard, PermissionGuard),
|
|
211
211
|
ApiBearerAuth(),
|
|
212
212
|
RequirePermission(USER_PERMISSIONS.UPDATE),
|
|
213
213
|
ApiOperation({
|
|
@@ -226,7 +226,7 @@ _ts_decorate([
|
|
|
226
226
|
], UserController.prototype, "verifyPhone", null);
|
|
227
227
|
_ts_decorate([
|
|
228
228
|
Post(':id/status'),
|
|
229
|
-
UseGuards(JwtAuthGuard),
|
|
229
|
+
UseGuards(JwtAuthGuard, PermissionGuard),
|
|
230
230
|
ApiBearerAuth(),
|
|
231
231
|
RequirePermission(USER_PERMISSIONS.UPDATE),
|
|
232
232
|
ApiOperation({
|
|
@@ -291,7 +291,6 @@ _ts_decorate([
|
|
|
291
291
|
Post(':id/profile-section/:sectionId'),
|
|
292
292
|
UseGuards(JwtAuthGuard),
|
|
293
293
|
ApiBearerAuth(),
|
|
294
|
-
RequirePermission(USER_PERMISSIONS.READ),
|
|
295
294
|
ApiOperation({
|
|
296
295
|
summary: 'Get profile section data',
|
|
297
296
|
description: 'Returns data for a specific profile section.'
|
|
@@ -319,7 +318,6 @@ _ts_decorate([
|
|
|
319
318
|
Post(':id/profile-section/:sectionId/update'),
|
|
320
319
|
UseGuards(JwtAuthGuard),
|
|
321
320
|
ApiBearerAuth(),
|
|
322
|
-
RequirePermission(USER_PERMISSIONS.UPDATE),
|
|
323
321
|
ApiOperation({
|
|
324
322
|
summary: 'Update profile section',
|
|
325
323
|
description: 'Updates a specific profile section data.'
|
|
@@ -349,7 +347,6 @@ _ts_decorate([
|
|
|
349
347
|
Post(':id/profile-completion'),
|
|
350
348
|
UseGuards(JwtAuthGuard),
|
|
351
349
|
ApiBearerAuth(),
|
|
352
|
-
RequirePermission(USER_PERMISSIONS.READ),
|
|
353
350
|
ApiOperation({
|
|
354
351
|
summary: 'Get profile completion percentage',
|
|
355
352
|
description: 'Returns overall profile completion percentage.'
|
|
@@ -6,10 +6,10 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
6
6
|
}
|
|
7
7
|
import { UserRoot } from '@flusys/nestjs-shared';
|
|
8
8
|
import { Entity } from 'typeorm';
|
|
9
|
-
export class
|
|
9
|
+
export class AppUser extends UserRoot {
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
AppUser = _ts_decorate([
|
|
12
12
|
Entity({
|
|
13
|
-
name: '
|
|
13
|
+
name: 'app_user'
|
|
14
14
|
})
|
|
15
|
-
],
|
|
15
|
+
], AppUser);
|
package/fesm/entities/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
+
export * from './app-user.entity';
|
|
1
2
|
export * from './company-branch.entity';
|
|
2
3
|
export * from './company.entity';
|
|
3
4
|
export * from './pending-registration.entity';
|
|
4
5
|
export { PermissionType, UserCompanyPermission } from './user-company-permission.entity';
|
|
5
|
-
export * from './user.entity';
|
|
6
6
|
export * from './user-token.entity';
|
|
7
|
+
import { AppUser } from './app-user.entity';
|
|
7
8
|
import { CompanyBranch } from './company-branch.entity';
|
|
8
9
|
import { Company } from './company.entity';
|
|
9
10
|
import { PendingRegistration } from './pending-registration.entity';
|
|
10
11
|
import { UserCompanyPermission } from './user-company-permission.entity';
|
|
11
|
-
import { User } from './user.entity';
|
|
12
12
|
import { UserToken } from './user-token.entity';
|
|
13
13
|
const BaseEntities = [
|
|
14
|
-
|
|
14
|
+
AppUser,
|
|
15
15
|
UserToken
|
|
16
16
|
];
|
|
17
17
|
const EmailEntities = [
|
|
@@ -22,9 +22,9 @@ function _ts_metadata(k, v) {
|
|
|
22
22
|
}
|
|
23
23
|
import { Column, CreateDateColumn, Entity, Index, JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
|
24
24
|
import { PermissionType } from '../enums';
|
|
25
|
+
import { AppUser } from './app-user.entity';
|
|
25
26
|
import { CompanyBranch } from './company-branch.entity';
|
|
26
27
|
import { Company } from './company.entity';
|
|
27
|
-
import { User } from './user.entity';
|
|
28
28
|
export { PermissionType };
|
|
29
29
|
export class UserCompanyPermission {
|
|
30
30
|
constructor(){
|
|
@@ -52,13 +52,13 @@ _ts_decorate([
|
|
|
52
52
|
_ts_metadata("design:type", String)
|
|
53
53
|
], UserCompanyPermission.prototype, "userId", void 0);
|
|
54
54
|
_ts_decorate([
|
|
55
|
-
ManyToOne(()=>
|
|
55
|
+
ManyToOne(()=>AppUser, {
|
|
56
56
|
onDelete: 'CASCADE'
|
|
57
57
|
}),
|
|
58
58
|
JoinColumn({
|
|
59
59
|
name: 'user_id'
|
|
60
60
|
}),
|
|
61
|
-
_ts_metadata("design:type", typeof
|
|
61
|
+
_ts_metadata("design:type", typeof AppUser === "undefined" ? Object : AppUser)
|
|
62
62
|
], UserCompanyPermission.prototype, "user", void 0);
|
|
63
63
|
_ts_decorate([
|
|
64
64
|
Column({
|
|
@@ -45,6 +45,9 @@ export class AuthConfigService {
|
|
|
45
45
|
isEmailVerificationEnabled() {
|
|
46
46
|
return this.options.bootstrapAppConfig?.enableEmailVerification ?? true;
|
|
47
47
|
}
|
|
48
|
+
isPermissionModeFound() {
|
|
49
|
+
return this.options.bootstrapAppConfig?.permissionMode !== undefined;
|
|
50
|
+
}
|
|
48
51
|
// JWT Configuration
|
|
49
52
|
getJwtSecret() {
|
|
50
53
|
if (!this.config.jwtSecret) {
|
|
@@ -31,9 +31,9 @@ import { BadRequestException, Inject, Injectable, InternalServerErrorException,
|
|
|
31
31
|
import * as bcrypt from 'bcrypt';
|
|
32
32
|
import * as crypto from 'crypto';
|
|
33
33
|
import { AUTH_MESSAGES, BCRYPT_SALT_ROUNDS, EMAIL_VERIFICATION_MESSAGES, USER_MESSAGES } from '../config';
|
|
34
|
+
import { AppUser } from '../entities/app-user.entity';
|
|
34
35
|
import { PendingRegistration } from '../entities/pending-registration.entity';
|
|
35
36
|
import { TokenType, UserToken } from '../entities/user-token.entity';
|
|
36
|
-
import { User } from '../entities/user.entity';
|
|
37
37
|
import { AUTH_EMAIL_PROVIDER, IAuthEmailProvider } from '../interfaces';
|
|
38
38
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
39
39
|
export class AuthEmailService {
|
|
@@ -102,7 +102,7 @@ export class AuthEmailService {
|
|
|
102
102
|
}
|
|
103
103
|
async createPendingRegistration(data) {
|
|
104
104
|
this.assertEmailEnabled();
|
|
105
|
-
const userRepository = await this.getRepository(
|
|
105
|
+
const userRepository = await this.getRepository(AppUser);
|
|
106
106
|
const pendingRepository = await this.getRepository(PendingRegistration);
|
|
107
107
|
// Check if user already exists
|
|
108
108
|
const existingUser = await userRepository.findOne({
|
|
@@ -147,7 +147,7 @@ export class AuthEmailService {
|
|
|
147
147
|
}
|
|
148
148
|
async forgotPassword(email, resetUrlBase) {
|
|
149
149
|
this.assertEmailEnabled();
|
|
150
|
-
const userRepository = await this.getRepository(
|
|
150
|
+
const userRepository = await this.getRepository(AppUser);
|
|
151
151
|
const tokenRepository = await this.getRepository(UserToken);
|
|
152
152
|
const user = await userRepository.findOne({
|
|
153
153
|
where: {
|
|
@@ -198,7 +198,7 @@ export class AuthEmailService {
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
async resetPassword(token, newPassword) {
|
|
201
|
-
const userRepository = await this.getRepository(
|
|
201
|
+
const userRepository = await this.getRepository(AppUser);
|
|
202
202
|
const tokenRepository = await this.getRepository(UserToken);
|
|
203
203
|
// Hash the incoming token to compare with stored hash
|
|
204
204
|
const tokenHash = this.hashToken(token);
|
|
@@ -240,7 +240,7 @@ export class AuthEmailService {
|
|
|
240
240
|
}
|
|
241
241
|
async verifyEmail(token) {
|
|
242
242
|
const pendingRepository = await this.getRepository(PendingRegistration);
|
|
243
|
-
const userRepository = await this.getRepository(
|
|
243
|
+
const userRepository = await this.getRepository(AppUser);
|
|
244
244
|
const tokenRepository = await this.getRepository(UserToken);
|
|
245
245
|
// Hash the incoming token to compare with stored hash
|
|
246
246
|
const tokenHash = this.hashToken(token);
|
|
@@ -324,7 +324,7 @@ export class AuthEmailService {
|
|
|
324
324
|
async resendVerificationEmail(email, verifyUrlBase) {
|
|
325
325
|
this.assertEmailEnabled();
|
|
326
326
|
const pendingRepository = await this.getRepository(PendingRegistration);
|
|
327
|
-
const userRepository = await this.getRepository(
|
|
327
|
+
const userRepository = await this.getRepository(AppUser);
|
|
328
328
|
const tokenRepository = await this.getRepository(UserToken);
|
|
329
329
|
// Check pending registration first
|
|
330
330
|
const pending = await pendingRepository.findOne({
|
|
@@ -36,11 +36,11 @@ import { JwtService } from '@nestjs/jwt';
|
|
|
36
36
|
import * as bcrypt from 'bcrypt';
|
|
37
37
|
import { AUTH_MESSAGES, BCRYPT_SALT_ROUNDS, EMAIL_VERIFICATION_MESSAGES, TOKEN_REVOKED_PREFIX, USER_ENRICHER, USER_MESSAGES } from '../config';
|
|
38
38
|
import { RegistrationDto } from '../dtos/authentication.dto';
|
|
39
|
+
import { AppUser } from '../entities/app-user.entity';
|
|
39
40
|
import { CompanyBranch } from '../entities/company-branch.entity';
|
|
40
41
|
import { Company } from '../entities/company.entity';
|
|
41
42
|
import { PendingRegistration } from '../entities/pending-registration.entity';
|
|
42
43
|
import { UserCompanyPermission } from '../entities/user-company-permission.entity';
|
|
43
|
-
import { User } from '../entities/user.entity';
|
|
44
44
|
import { PermissionType } from '../enums';
|
|
45
45
|
import { IUserEnricher } from '../interfaces/user-enricher.interface';
|
|
46
46
|
import { AuthConfigService } from './auth-config.service';
|
|
@@ -62,11 +62,11 @@ export class AuthenticationService {
|
|
|
62
62
|
message: 'User repository not available',
|
|
63
63
|
messageKey: SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE,
|
|
64
64
|
messageVariables: {
|
|
65
|
-
entity: '
|
|
65
|
+
entity: 'AppUser'
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
return this.dataSourceProvider.getRepository(
|
|
69
|
+
return this.dataSourceProvider.getRepository(AppUser);
|
|
70
70
|
}
|
|
71
71
|
async getPermissionRepository() {
|
|
72
72
|
if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
|
|
@@ -248,7 +248,7 @@ export class AuthenticationService {
|
|
|
248
248
|
await queryRunner.startTransaction();
|
|
249
249
|
try {
|
|
250
250
|
const hashedPassword = await bcrypt.hash(dto.password, BCRYPT_SALT_ROUNDS);
|
|
251
|
-
const user = queryRunner.manager.create(
|
|
251
|
+
const user = queryRunner.manager.create(AppUser, {
|
|
252
252
|
name: dto.name,
|
|
253
253
|
email: dto.email,
|
|
254
254
|
phone: dto.phone,
|
|
@@ -257,7 +257,7 @@ export class AuthenticationService {
|
|
|
257
257
|
emailVerified: false,
|
|
258
258
|
phoneVerified: false
|
|
259
259
|
});
|
|
260
|
-
await queryRunner.manager.save(
|
|
260
|
+
await queryRunner.manager.save(AppUser, user);
|
|
261
261
|
if (this.userEnricher?.onUserCreated) {
|
|
262
262
|
await this.userEnricher.onUserCreated(user.id, dto.additionalFields ?? null, queryRunner);
|
|
263
263
|
}
|
|
@@ -291,7 +291,7 @@ export class AuthenticationService {
|
|
|
291
291
|
await queryRunner.connect();
|
|
292
292
|
await queryRunner.startTransaction();
|
|
293
293
|
try {
|
|
294
|
-
const existingUser = await queryRunner.manager.findOne(
|
|
294
|
+
const existingUser = await queryRunner.manager.findOne(AppUser, {
|
|
295
295
|
where: {
|
|
296
296
|
email: pending.email
|
|
297
297
|
}
|
|
@@ -302,7 +302,7 @@ export class AuthenticationService {
|
|
|
302
302
|
messageKey: AUTH_MESSAGES.REGISTER_EMAIL_EXISTS
|
|
303
303
|
});
|
|
304
304
|
}
|
|
305
|
-
const user = queryRunner.manager.create(
|
|
305
|
+
const user = queryRunner.manager.create(AppUser, {
|
|
306
306
|
name: pending.name,
|
|
307
307
|
email: pending.email,
|
|
308
308
|
phone: pending.phone,
|
|
@@ -311,7 +311,7 @@ export class AuthenticationService {
|
|
|
311
311
|
emailVerified: true,
|
|
312
312
|
phoneVerified: false
|
|
313
313
|
});
|
|
314
|
-
await queryRunner.manager.save(
|
|
314
|
+
await queryRunner.manager.save(AppUser, user);
|
|
315
315
|
if (this.userEnricher?.onUserCreated) {
|
|
316
316
|
await this.userEnricher.onUserCreated(user.id, pending.additionalFields ?? null, queryRunner);
|
|
317
317
|
}
|
|
@@ -25,7 +25,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
28
|
+
import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
|
|
29
29
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
30
|
import { applyCompanyFilter } from '@flusys/nestjs-shared/utils';
|
|
31
31
|
import { Inject, Injectable, Scope } from '@nestjs/common';
|
|
@@ -25,7 +25,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
28
|
+
import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
|
|
29
29
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
30
|
import { Inject, Injectable, Scope } from '@nestjs/common';
|
|
31
31
|
import { Company } from '../entities/company.entity';
|
|
@@ -26,7 +26,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
import { LogAction } from '@flusys/nestjs-shared';
|
|
29
|
-
import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
|
|
29
|
+
import { createModuleLogger, HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
|
|
30
30
|
import { SYSTEM_MESSAGES } from '@flusys/nestjs-shared/constants';
|
|
31
31
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
32
32
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
@@ -34,15 +34,15 @@ import { BadRequestException, ForbiddenException, Inject, Injectable, NotFoundEx
|
|
|
34
34
|
import * as bcrypt from 'bcrypt';
|
|
35
35
|
import { AUTH_MESSAGES, BCRYPT_SALT_ROUNDS, USER_ENRICHER, USER_MESSAGES, USER_PERMISSION_MESSAGES } from '../config';
|
|
36
36
|
import { CreateUserDto } from '../dtos/user.dto';
|
|
37
|
+
import { AppUser } from '../entities/app-user.entity';
|
|
37
38
|
import { UserCompanyPermission } from '../entities/user-company-permission.entity';
|
|
38
|
-
import { User } from '../entities/user.entity';
|
|
39
39
|
import { PermissionType } from '../enums';
|
|
40
40
|
import { IUserEnricher } from '../interfaces/user-enricher.interface';
|
|
41
41
|
import { AuthConfigService } from './auth-config.service';
|
|
42
42
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
43
43
|
export class UserService extends RequestScopedApiService {
|
|
44
44
|
resolveEntity() {
|
|
45
|
-
return
|
|
45
|
+
return AppUser;
|
|
46
46
|
}
|
|
47
47
|
getDataSourceProvider() {
|
|
48
48
|
return this.dataSourceProvider;
|
|
@@ -149,24 +149,24 @@ export class UserService extends RequestScopedApiService {
|
|
|
149
149
|
return this.convertEntityToDto(entity);
|
|
150
150
|
}
|
|
151
151
|
convertRawToResponseDto(raw) {
|
|
152
|
-
const isActive = raw.
|
|
152
|
+
const isActive = raw.appuser_companyIsActive !== undefined ? Boolean(raw.appuser_companyIsActive) : raw.appuser_is_active;
|
|
153
153
|
return {
|
|
154
|
-
id: raw.
|
|
155
|
-
name: raw.
|
|
156
|
-
phone: raw.
|
|
157
|
-
email: raw.
|
|
158
|
-
profilePictureId: raw.
|
|
154
|
+
id: raw.appuser_id,
|
|
155
|
+
name: raw.appuser_name,
|
|
156
|
+
phone: raw.appuser_phone,
|
|
157
|
+
email: raw.appuser_email,
|
|
158
|
+
profilePictureId: raw.appuser_profile_picture_id,
|
|
159
159
|
isActive,
|
|
160
|
-
emailVerified: raw.
|
|
161
|
-
phoneVerified: raw.
|
|
162
|
-
lastLoginAt: raw.
|
|
163
|
-
additionalFields: raw.
|
|
164
|
-
createdAt: raw.
|
|
165
|
-
updatedAt: raw.
|
|
166
|
-
deletedAt: raw.
|
|
167
|
-
createdById: raw.
|
|
168
|
-
updatedById: raw.
|
|
169
|
-
deletedById: raw.
|
|
160
|
+
emailVerified: raw.appuser_email_verified,
|
|
161
|
+
phoneVerified: raw.appuser_phone_verified,
|
|
162
|
+
lastLoginAt: raw.appuser_last_login_at,
|
|
163
|
+
additionalFields: raw.appuser_additional_fields,
|
|
164
|
+
createdAt: raw.appuser_created_at,
|
|
165
|
+
updatedAt: raw.appuser_updated_at,
|
|
166
|
+
deletedAt: raw.appuser_deleted_at,
|
|
167
|
+
createdById: raw.appuser_created_by_id,
|
|
168
|
+
updatedById: raw.appuser_updated_by_id,
|
|
169
|
+
deletedById: raw.appuser_deleted_by_id
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
172
|
convertEntityToDto(entity) {
|
|
@@ -192,8 +192,22 @@ export class UserService extends RequestScopedApiService {
|
|
|
192
192
|
convertEntityListToResponseListDto(entities, isRaw) {
|
|
193
193
|
return entities.map((entity)=>this.convertEntityToResponseDto(entity, isRaw));
|
|
194
194
|
}
|
|
195
|
+
async getFilterQuery(query, filter, _user) {
|
|
196
|
+
for (const [key, value] of Object.entries(filter)){
|
|
197
|
+
if (key === 'actionCode' || key === 'isActive') {
|
|
198
|
+
continue; // real continue
|
|
199
|
+
}
|
|
200
|
+
query.andWhere(`${this.entityName}.${key} = :value`, {
|
|
201
|
+
value
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
query,
|
|
206
|
+
isRaw: false
|
|
207
|
+
};
|
|
208
|
+
}
|
|
195
209
|
async getGlobalSearchQuery(query, search, _user) {
|
|
196
|
-
query.andWhere('(
|
|
210
|
+
query.andWhere('(appuser.name LIKE :search OR appuser.email LIKE :search OR appuser.phone LIKE :search)', {
|
|
197
211
|
search: `%${search}%`
|
|
198
212
|
});
|
|
199
213
|
return {
|
|
@@ -202,50 +216,99 @@ export class UserService extends RequestScopedApiService {
|
|
|
202
216
|
};
|
|
203
217
|
}
|
|
204
218
|
async getExtraManipulateQuery(query, filterAndPaginationDto, user) {
|
|
205
|
-
|
|
206
|
-
if (this.userEnricher?.enrichListQuery) {
|
|
207
|
-
await this.userEnricher.enrichListQuery(query, user);
|
|
208
|
-
}
|
|
219
|
+
const superQuery = await super.getExtraManipulateQuery(query, filterAndPaginationDto, user);
|
|
209
220
|
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
const filter = filterAndPaginationDto?.filter;
|
|
222
|
+
// --- Action code filter (applies regardless of company feature) ---
|
|
223
|
+
if (filter?.actionCode) {
|
|
224
|
+
const actionCode = filter.actionCode;
|
|
225
|
+
const companyId = isCompanyFeatureEnabled ? user?.companyId : undefined;
|
|
226
|
+
if (isCompanyFeatureEnabled && !companyId) {
|
|
227
|
+
// Company feature is on but no companyId — no results should match
|
|
228
|
+
query.andWhere('1 = 0');
|
|
229
|
+
return {
|
|
230
|
+
query,
|
|
231
|
+
isRaw: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const isPermissionModeFound = this.authConfig.isPermissionModeFound();
|
|
235
|
+
if (!isPermissionModeFound) {
|
|
236
|
+
// Optionally log a warning so the caller knows the filter was ignored
|
|
237
|
+
this.logAdapter.warn(`actionCode filter was requested but permission mode is not set to 'found' — filter ignored`);
|
|
238
|
+
} else {
|
|
222
239
|
query.andWhere(`EXISTS (
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
240
|
+
SELECT 1 FROM user_iam_permission perm
|
|
241
|
+
WHERE perm.permission_type = 'user_role'
|
|
242
|
+
AND perm.source_id = appuser.id
|
|
243
|
+
AND perm.target_id IN (
|
|
244
|
+
SELECT source_id FROM user_iam_permission
|
|
245
|
+
WHERE permission_type = 'role_action'
|
|
246
|
+
AND target_id = (SELECT id FROM action WHERE code = :actionCode)
|
|
247
|
+
${isCompanyFeatureEnabled ? 'AND company_id = :actionCompanyId' : ''}
|
|
248
|
+
)
|
|
249
|
+
) OR EXISTS (
|
|
250
|
+
SELECT 1 FROM user_iam_permission perm
|
|
251
|
+
WHERE perm.permission_type = 'user_action'
|
|
252
|
+
AND perm.source_id = appuser.id
|
|
253
|
+
AND perm.target_id = (SELECT id FROM action WHERE code = :actionCode)
|
|
254
|
+
${isCompanyFeatureEnabled ? 'AND company_id = :actionCompanyId' : ''}
|
|
255
|
+
)`, {
|
|
256
|
+
actionCode,
|
|
257
|
+
...isCompanyFeatureEnabled && {
|
|
258
|
+
actionCompanyId: companyId
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// --- Non-company path ---
|
|
264
|
+
if (!isCompanyFeatureEnabled) {
|
|
265
|
+
if (filter?.isActive !== undefined) {
|
|
266
|
+
query.andWhere(`${this.entityName}.isActive = :value`, {
|
|
267
|
+
value: filter.isActive
|
|
232
268
|
});
|
|
233
269
|
}
|
|
270
|
+
return superQuery;
|
|
271
|
+
}
|
|
272
|
+
// --- Company feature path ---
|
|
273
|
+
if (!user?.companyId || !this.permissionRepository) {
|
|
274
|
+
return superQuery;
|
|
275
|
+
}
|
|
276
|
+
// Add companyIsActive as a selected column
|
|
277
|
+
query.addSelect(`(SELECT perm.is_active FROM user_company_permissions perm
|
|
278
|
+
WHERE perm.user_id = appuser.id
|
|
279
|
+
AND perm.permission_type = :permissionType
|
|
280
|
+
AND perm.target_id = :companyId
|
|
281
|
+
LIMIT 1)`, 'user_companyIsActive');
|
|
282
|
+
query.setParameter('permissionType', PermissionType.COMPANY);
|
|
283
|
+
query.setParameter('companyId', user.companyId);
|
|
284
|
+
// Filter by isActive within the company if requested
|
|
285
|
+
if (filter?.isActive !== undefined) {
|
|
234
286
|
query.andWhere(`EXISTS (
|
|
235
|
-
|
|
236
|
-
|
|
287
|
+
SELECT 1 FROM user_company_permissions perm
|
|
288
|
+
WHERE perm.user_id = appuser.id
|
|
237
289
|
AND perm.permission_type = :permissionType
|
|
238
290
|
AND perm.target_id = :companyId
|
|
239
|
-
|
|
291
|
+
AND perm.is_active = :isActiveValue
|
|
292
|
+
)`, {
|
|
240
293
|
permissionType: PermissionType.COMPANY,
|
|
241
|
-
companyId: user.companyId
|
|
294
|
+
companyId: user.companyId,
|
|
295
|
+
isActiveValue: filter.isActive
|
|
242
296
|
});
|
|
243
|
-
return {
|
|
244
|
-
query,
|
|
245
|
-
isRaw: true
|
|
246
|
-
};
|
|
247
297
|
}
|
|
248
|
-
|
|
298
|
+
// Always restrict to users belonging to this company
|
|
299
|
+
query.andWhere(`EXISTS (
|
|
300
|
+
SELECT 1 FROM user_company_permissions perm
|
|
301
|
+
WHERE perm.user_id = appuser.id
|
|
302
|
+
AND perm.permission_type = :permissionType
|
|
303
|
+
AND perm.target_id = :companyId
|
|
304
|
+
)`, {
|
|
305
|
+
permissionType: PermissionType.COMPANY,
|
|
306
|
+
companyId: user.companyId
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
query,
|
|
310
|
+
isRaw: true
|
|
311
|
+
};
|
|
249
312
|
}
|
|
250
313
|
async insert(dto, user) {
|
|
251
314
|
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
@@ -445,7 +508,7 @@ export class UserService extends RequestScopedApiService {
|
|
|
445
508
|
}
|
|
446
509
|
}
|
|
447
510
|
constructor(cacheManager, utilsService, dataSourceProvider, authConfig, userEnricher){
|
|
448
|
-
super('
|
|
511
|
+
super('appuser', null, cacheManager, utilsService, UserService.name, true, 'auth'), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "authConfig", void 0), _define_property(this, "userEnricher", void 0), _define_property(this, "permissionRepository", void 0), _define_property(this, "dataSource", void 0), _define_property(this, "logAdapter", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig, this.userEnricher = userEnricher, this.logAdapter = createModuleLogger(this.constructor.name);
|
|
449
512
|
}
|
|
450
513
|
}
|
|
451
514
|
_ts_decorate([
|
|
@@ -26,7 +26,7 @@ import { ModuleRef } from '@nestjs/core';
|
|
|
26
26
|
import { PassportStrategy } from '@nestjs/passport';
|
|
27
27
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
|
28
28
|
import { AUTH_MESSAGES, USER_MESSAGES } from '../config';
|
|
29
|
-
import {
|
|
29
|
+
import { AppUser } from '../entities/app-user.entity';
|
|
30
30
|
import { AuthConfigService } from '../services/auth-config.service';
|
|
31
31
|
import { AuthDataSourceProvider } from '../services/auth-datasource.provider';
|
|
32
32
|
import { AuthenticationService } from '../services/authentication.service';
|
|
@@ -57,7 +57,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
|
|
57
57
|
strict: false
|
|
58
58
|
});
|
|
59
59
|
if (provider) {
|
|
60
|
-
return provider.getRepository(
|
|
60
|
+
return provider.getRepository(AppUser);
|
|
61
61
|
}
|
|
62
62
|
} catch {
|
|
63
63
|
throw new UnauthorizedException({
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ILoggedUserInfo, IPermissionLogic } from '@flusys/nestjs-shared/interfaces';
|
|
2
|
-
import { QueryRunner
|
|
3
|
-
import { User } from '../entities/user.entity';
|
|
2
|
+
import { QueryRunner } from 'typeorm';
|
|
4
3
|
import { IUser } from './user.interface';
|
|
5
4
|
export interface IProfileSection {
|
|
6
5
|
id: string;
|
|
@@ -43,9 +42,6 @@ export interface IProfileAction {
|
|
|
43
42
|
}
|
|
44
43
|
export type IEnrichedUser = IUser & IProfileExtras;
|
|
45
44
|
export interface IUserEnricher {
|
|
46
|
-
enrichListQuery?(query: SelectQueryBuilder<User>, user: ILoggedUserInfo | null): Promise<{
|
|
47
|
-
extraFields: string[];
|
|
48
|
-
}>;
|
|
49
45
|
enrichListItems?(users: IUser[], user: ILoggedUserInfo | null): Promise<IEnrichedUser[]>;
|
|
50
46
|
getProfileExtras?(userId: string, user: ILoggedUserInfo): Promise<IProfileExtras>;
|
|
51
47
|
updateProfileExtras?(userId: string, extras: Record<string, any>, queryRunner: QueryRunner): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-auth",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.4",
|
|
4
4
|
"description": "Authentication and authorization infrastructure for NestJS applications",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "fesm/index.js",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"rxjs": "^7.8.0"
|
|
94
94
|
},
|
|
95
95
|
"dependencies": {
|
|
96
|
-
"@flusys/nestjs-core": "5.0.
|
|
97
|
-
"@flusys/nestjs-shared": "5.0.
|
|
96
|
+
"@flusys/nestjs-core": "5.0.4",
|
|
97
|
+
"@flusys/nestjs-shared": "5.0.4"
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -9,6 +9,7 @@ export declare class AuthConfigService implements IModuleConfigService {
|
|
|
9
9
|
isMultiTenant(): boolean;
|
|
10
10
|
isCompanyFeatureEnabled(): boolean;
|
|
11
11
|
isEmailVerificationEnabled(): boolean;
|
|
12
|
+
isPermissionModeFound(): boolean;
|
|
12
13
|
getJwtSecret(): string;
|
|
13
14
|
getJwtExpiration(): string;
|
|
14
15
|
getRefreshTokenSecret(): string;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HybridCache, RequestScopedApiService } from '@flusys/nestjs-shared/classes';
|
|
2
2
|
import { FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
|
|
3
3
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
4
4
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
5
|
-
import { EntityTarget,
|
|
5
|
+
import { EntityTarget, SelectQueryBuilder } from 'typeorm';
|
|
6
6
|
import { CreateCompanyBranchDto, UpdateCompanyBranchDto } from '../dtos/company-branch.dto';
|
|
7
|
-
import { ICompanyBranch } from '../interfaces/company-branch.interface';
|
|
8
7
|
import { CompanyBranch } from '../entities/company-branch.entity';
|
|
8
|
+
import { ICompanyBranch } from '../interfaces/company-branch.interface';
|
|
9
9
|
import { AuthConfigService } from './auth-config.service';
|
|
10
10
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
11
|
-
export declare class BranchService extends RequestScopedApiService<CreateCompanyBranchDto, UpdateCompanyBranchDto, ICompanyBranch, CompanyBranch
|
|
11
|
+
export declare class BranchService extends RequestScopedApiService<CreateCompanyBranchDto, UpdateCompanyBranchDto, ICompanyBranch, CompanyBranch> {
|
|
12
12
|
protected cacheManager: HybridCache;
|
|
13
13
|
protected utilsService: UtilsService;
|
|
14
14
|
private readonly dataSourceProvider;
|