@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.
Files changed (32) hide show
  1. package/README.md +13 -14
  2. package/cjs/controllers/user-permission.controller.js +2 -2
  3. package/cjs/controllers/user.controller.js +5 -8
  4. package/cjs/entities/{user.entity.js → app-user.entity.js} +6 -6
  5. package/cjs/entities/index.js +2 -2
  6. package/cjs/entities/user-company-permission.entity.js +3 -3
  7. package/cjs/services/auth-config.service.js +3 -0
  8. package/cjs/services/auth-email.service.js +6 -6
  9. package/cjs/services/authentication.service.js +8 -8
  10. package/cjs/services/user.service.js +117 -54
  11. package/cjs/strategies/jwt.strategy.js +2 -2
  12. package/entities/{user.entity.d.ts → app-user.entity.d.ts} +1 -1
  13. package/entities/index.d.ts +4 -4
  14. package/entities/user-company-permission.entity.d.ts +2 -2
  15. package/fesm/controllers/user-permission.controller.js +3 -3
  16. package/fesm/controllers/user.controller.js +6 -9
  17. package/fesm/entities/{user.entity.js → app-user.entity.js} +4 -4
  18. package/fesm/entities/index.js +3 -3
  19. package/fesm/entities/user-company-permission.entity.js +3 -3
  20. package/fesm/services/auth-config.service.js +3 -0
  21. package/fesm/services/auth-email.service.js +6 -6
  22. package/fesm/services/authentication.service.js +8 -8
  23. package/fesm/services/branch.service.js +1 -1
  24. package/fesm/services/company.service.js +1 -1
  25. package/fesm/services/user.service.js +118 -55
  26. package/fesm/strategies/jwt.strategy.js +2 -2
  27. package/interfaces/user-enricher.interface.d.ts +1 -5
  28. package/package.json +3 -3
  29. package/services/auth-config.service.d.ts +1 -0
  30. package/services/branch.service.d.ts +4 -4
  31. package/services/company.service.d.ts +3 -3
  32. 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 User extends UserRoot {
9
+ export class AppUser extends UserRoot {
10
10
  }
11
- User = _ts_decorate([
11
+ AppUser = _ts_decorate([
12
12
  Entity({
13
- name: 'user'
13
+ name: 'app_user'
14
14
  })
15
- ], User);
15
+ ], AppUser);
@@ -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
- User,
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(()=>User, {
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 User === "undefined" ? Object : User)
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(User);
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(User);
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(User);
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(User);
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(User);
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: 'User'
65
+ entity: 'AppUser'
66
66
  }
67
67
  });
68
68
  }
69
- return this.dataSourceProvider.getRepository(User);
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(User, {
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(User, user);
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(User, {
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(User, {
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(User, user);
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 { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
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 { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
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 User;
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.user_companyIsActive !== undefined ? Boolean(raw.user_companyIsActive) : raw.user_is_active;
152
+ const isActive = raw.appuser_companyIsActive !== undefined ? Boolean(raw.appuser_companyIsActive) : raw.appuser_is_active;
153
153
  return {
154
- id: raw.user_id,
155
- name: raw.user_name,
156
- phone: raw.user_phone,
157
- email: raw.user_email,
158
- profilePictureId: raw.user_profile_picture_id,
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.user_email_verified,
161
- phoneVerified: raw.user_phone_verified,
162
- lastLoginAt: raw.user_last_login_at,
163
- additionalFields: raw.user_additional_fields,
164
- createdAt: raw.user_created_at,
165
- updatedAt: raw.user_updated_at,
166
- deletedAt: raw.user_deleted_at,
167
- createdById: raw.user_created_by_id,
168
- updatedById: raw.user_updated_by_id,
169
- deletedById: raw.user_deleted_by_id
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('("user".name LIKE :search OR "user".email LIKE :search OR "user".phone LIKE :search)', {
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
- let result = await super.getExtraManipulateQuery(query, filterAndPaginationDto, user);
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
- if (!isCompanyFeatureEnabled) {
211
- return result;
212
- }
213
- if (user?.companyId && this.permissionRepository) {
214
- query.addSelect(`(SELECT perm.is_active FROM user_company_permissions perm
215
- WHERE perm.user_id = "user".id
216
- AND perm.permission_type = :permissionType
217
- AND perm.target_id = :companyId
218
- LIMIT 1)`, 'user_companyIsActive');
219
- query.setParameter('permissionType', PermissionType.COMPANY);
220
- query.setParameter('companyId', user.companyId);
221
- if (filterAndPaginationDto.filter?.isActive !== undefined) {
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
- SELECT 1 FROM user_company_permissions perm
224
- WHERE perm.user_id = "user".id
225
- AND perm.permission_type = :permissionType
226
- AND perm.target_id = :companyId
227
- AND perm.is_active = :isActiveValue
228
- )`, {
229
- permissionType: PermissionType.COMPANY,
230
- companyId: user.companyId,
231
- isActiveValue: filterAndPaginationDto.filter.isActive
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
- SELECT 1 FROM user_company_permissions perm
236
- WHERE perm.user_id = "user".id
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
- return result;
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('user', 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), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig, this.userEnricher = userEnricher;
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 { User } from '../entities/user.entity';
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(User);
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, SelectQueryBuilder } from 'typeorm';
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.1",
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.1",
97
- "@flusys/nestjs-shared": "5.0.1"
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 { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
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, Repository, SelectQueryBuilder } from 'typeorm';
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, Repository<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;