@flusys/nestjs-auth 5.0.0 → 5.0.3

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 (30) hide show
  1. package/README.md +13 -14
  2. package/cjs/entities/{user.entity.js → app-user.entity.js} +6 -6
  3. package/cjs/entities/company-branch.entity.js +1 -1
  4. package/cjs/entities/company.entity.js +2 -2
  5. package/cjs/entities/index.js +2 -2
  6. package/cjs/entities/pending-registration.entity.js +3 -2
  7. package/cjs/entities/user-company-permission.entity.js +3 -3
  8. package/cjs/services/auth-config.service.js +3 -0
  9. package/cjs/services/auth-email.service.js +6 -6
  10. package/cjs/services/authentication.service.js +8 -8
  11. package/cjs/services/user.service.js +117 -54
  12. package/cjs/strategies/jwt.strategy.js +2 -2
  13. package/entities/{user.entity.d.ts → app-user.entity.d.ts} +1 -1
  14. package/entities/index.d.ts +4 -4
  15. package/entities/user-company-permission.entity.d.ts +2 -2
  16. package/fesm/entities/{user.entity.js → app-user.entity.js} +4 -4
  17. package/fesm/entities/company-branch.entity.js +2 -2
  18. package/fesm/entities/company.entity.js +3 -3
  19. package/fesm/entities/index.js +3 -3
  20. package/fesm/entities/pending-registration.entity.js +3 -2
  21. package/fesm/entities/user-company-permission.entity.js +3 -3
  22. package/fesm/services/auth-config.service.js +3 -0
  23. package/fesm/services/auth-email.service.js +6 -6
  24. package/fesm/services/authentication.service.js +8 -8
  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 +4 -4
  29. package/services/auth-config.service.d.ts +1 -0
  30. package/services/user.service.d.ts +15 -10
package/README.md CHANGED
@@ -228,20 +228,19 @@ When `AUTH_EMAIL_PROVIDER` is not provided, registration still works but emails
228
228
 
229
229
  Extend user list and profile responses without creating circular imports. All methods are optional — implement only what you need.
230
230
 
231
- | Method | When called | Purpose |
232
- | --- | --- | --- |
233
- | `enrichListQuery` | User list query build | Add extra joins/selects to the query |
234
- | `enrichListItems` | After user list fetch | Attach computed data to list items |
235
- | `getProfileExtras` | `POST /users/get/:id` | Add roles, actions, sections to profile |
236
- | `updateProfileExtras` | Profile update (in tx) | Persist extra profile fields |
237
- | `validateProfileExtras` | Before profile update | Validate extra fields |
238
- | `getProfileSections` | Profile form init | Return multi-step section definitions |
239
- | `getProfileSectionData` | Section fetch | Return section-specific data |
240
- | `updateProfileSection` | Section save (in tx) | Persist section data |
241
- | `handleSectionFileUpload` | File upload (in tx) | Handle file attach to section |
242
- | `handleSectionFileDelete` | File delete (in tx) | Handle file removal from section |
243
- | `calculateProfileCompletion` | Profile fetch | Return 0–100 completion score |
244
- | `onUserCreated` | Registration (in tx) | Hook after user row created; throw to rollback |
231
+ | Method | When called | Purpose |
232
+ | ---------------------------- | ---------------------- | ---------------------------------------------- |
233
+ | `enrichListItems` | After user list fetch | Attach computed data to list items |
234
+ | `getProfileExtras` | `POST /users/get/:id` | Add roles, actions, sections to profile |
235
+ | `updateProfileExtras` | Profile update (in tx) | Persist extra profile fields |
236
+ | `validateProfileExtras` | Before profile update | Validate extra fields |
237
+ | `getProfileSections` | Profile form init | Return multi-step section definitions |
238
+ | `getProfileSectionData` | Section fetch | Return section-specific data |
239
+ | `updateProfileSection` | Section save (in tx) | Persist section data |
240
+ | `handleSectionFileUpload` | File upload (in tx) | Handle file attach to section |
241
+ | `handleSectionFileDelete` | File delete (in tx) | Handle file removal from section |
242
+ | `calculateProfileCompletion` | Profile fetch | Return 0–100 completion score |
243
+ | `onUserCreated` | Registration (in tx) | Hook after user row created; throw to rollback |
245
244
 
246
245
  ```typescript
247
246
  import { USER_ENRICHER, IUserEnricher, IProfileExtras } from '@flusys/nestjs-auth';
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- Object.defineProperty(exports, "User", {
5
+ Object.defineProperty(exports, "AppUser", {
6
6
  enumerable: true,
7
7
  get: function() {
8
- return User;
8
+ return AppUser;
9
9
  }
10
10
  });
11
11
  const _nestjsshared = require("@flusys/nestjs-shared");
@@ -16,10 +16,10 @@ function _ts_decorate(decorators, target, key, desc) {
16
16
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
17
17
  return c > 3 && r && Object.defineProperty(target, key, r), r;
18
18
  }
19
- let User = class User extends _nestjsshared.UserRoot {
19
+ let AppUser = class AppUser extends _nestjsshared.UserRoot {
20
20
  };
21
- User = _ts_decorate([
21
+ AppUser = _ts_decorate([
22
22
  (0, _typeorm.Entity)({
23
- name: 'user'
23
+ name: 'app_user'
24
24
  })
25
- ], User);
25
+ ], AppUser);
@@ -109,7 +109,7 @@ _ts_decorate([
109
109
  ], CompanyBranch.prototype, "isActive", void 0);
110
110
  _ts_decorate([
111
111
  (0, _typeorm.Column)({
112
- type: 'json',
112
+ type: (0, _nestjsshared.getJsonColumnType)(),
113
113
  nullable: true
114
114
  }),
115
115
  _ts_metadata("design:type", Object)
@@ -123,14 +123,14 @@ _ts_decorate([
123
123
  ], Company.prototype, "isActive", void 0);
124
124
  _ts_decorate([
125
125
  (0, _typeorm.Column)({
126
- type: 'json',
126
+ type: (0, _nestjsshared.getJsonColumnType)(),
127
127
  nullable: true
128
128
  }),
129
129
  _ts_metadata("design:type", Object)
130
130
  ], Company.prototype, "settings", void 0);
131
131
  _ts_decorate([
132
132
  (0, _typeorm.Column)({
133
- type: 'json',
133
+ type: (0, _nestjsshared.getJsonColumnType)(),
134
134
  nullable: true
135
135
  }),
136
136
  _ts_metadata("design:type", Object)
@@ -19,11 +19,11 @@ _export(exports, {
19
19
  return getEntitiesByConfig;
20
20
  }
21
21
  });
22
+ const _appuserentity = _export_star(require("./app-user.entity"), exports);
22
23
  const _companybranchentity = _export_star(require("./company-branch.entity"), exports);
23
24
  const _companyentity = _export_star(require("./company.entity"), exports);
24
25
  const _pendingregistrationentity = _export_star(require("./pending-registration.entity"), exports);
25
26
  const _usercompanypermissionentity = require("./user-company-permission.entity");
26
- const _userentity = _export_star(require("./user.entity"), exports);
27
27
  const _usertokenentity = _export_star(require("./user-token.entity"), exports);
28
28
  function _export_star(from, to) {
29
29
  Object.keys(from).forEach(function(k) {
@@ -39,7 +39,7 @@ function _export_star(from, to) {
39
39
  return from;
40
40
  }
41
41
  const BaseEntities = [
42
- _userentity.User,
42
+ _appuserentity.AppUser,
43
43
  _usertokenentity.UserToken
44
44
  ];
45
45
  const EmailEntities = [
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "PendingRegistration", {
8
8
  return PendingRegistration;
9
9
  }
10
10
  });
11
+ const _nestjsshared = require("@flusys/nestjs-shared");
11
12
  const _typeorm = require("typeorm");
12
13
  function _define_property(obj, key, value) {
13
14
  if (key in obj) {
@@ -95,7 +96,7 @@ _ts_decorate([
95
96
  ], PendingRegistration.prototype, "expiresAt", void 0);
96
97
  _ts_decorate([
97
98
  (0, _typeorm.Column)({
98
- type: 'json',
99
+ type: (0, _nestjsshared.getJsonColumnType)(),
99
100
  nullable: true,
100
101
  name: 'company_data'
101
102
  }),
@@ -103,7 +104,7 @@ _ts_decorate([
103
104
  ], PendingRegistration.prototype, "companyData", void 0);
104
105
  _ts_decorate([
105
106
  (0, _typeorm.Column)({
106
- type: 'json',
107
+ type: (0, _nestjsshared.getJsonColumnType)(),
107
108
  nullable: true,
108
109
  name: 'additional_fields'
109
110
  }),
@@ -18,9 +18,9 @@ _export(exports, {
18
18
  });
19
19
  const _typeorm = require("typeorm");
20
20
  const _enums = require("../enums");
21
+ const _appuserentity = require("./app-user.entity");
21
22
  const _companybranchentity = require("./company-branch.entity");
22
23
  const _companyentity = require("./company.entity");
23
- const _userentity = require("./user.entity");
24
24
  function _define_property(obj, key, value) {
25
25
  if (key in obj) {
26
26
  Object.defineProperty(obj, key, {
@@ -69,13 +69,13 @@ _ts_decorate([
69
69
  _ts_metadata("design:type", String)
70
70
  ], UserCompanyPermission.prototype, "userId", void 0);
71
71
  _ts_decorate([
72
- (0, _typeorm.ManyToOne)(()=>_userentity.User, {
72
+ (0, _typeorm.ManyToOne)(()=>_appuserentity.AppUser, {
73
73
  onDelete: 'CASCADE'
74
74
  }),
75
75
  (0, _typeorm.JoinColumn)({
76
76
  name: 'user_id'
77
77
  }),
78
- _ts_metadata("design:type", typeof _userentity.User === "undefined" ? Object : _userentity.User)
78
+ _ts_metadata("design:type", typeof _appuserentity.AppUser === "undefined" ? Object : _appuserentity.AppUser)
79
79
  ], UserCompanyPermission.prototype, "user", void 0);
80
80
  _ts_decorate([
81
81
  (0, _typeorm.Column)({
@@ -55,6 +55,9 @@ let AuthConfigService = class AuthConfigService {
55
55
  isEmailVerificationEnabled() {
56
56
  return this.options.bootstrapAppConfig?.enableEmailVerification ?? true;
57
57
  }
58
+ isPermissionModeFound() {
59
+ return this.options.bootstrapAppConfig?.permissionMode !== undefined;
60
+ }
58
61
  // JWT Configuration
59
62
  getJwtSecret() {
60
63
  if (!this.config.jwtSecret) {
@@ -14,9 +14,9 @@ const _common = require("@nestjs/common");
14
14
  const _bcrypt = /*#__PURE__*/ _interop_require_wildcard(require("bcrypt"));
15
15
  const _crypto = /*#__PURE__*/ _interop_require_wildcard(require("crypto"));
16
16
  const _config = require("../config");
17
+ const _appuserentity = require("../entities/app-user.entity");
17
18
  const _pendingregistrationentity = require("../entities/pending-registration.entity");
18
19
  const _usertokenentity = require("../entities/user-token.entity");
19
- const _userentity = require("../entities/user.entity");
20
20
  const _interfaces = require("../interfaces");
21
21
  const _authdatasourceprovider = require("./auth-datasource.provider");
22
22
  function _define_property(obj, key, value) {
@@ -153,7 +153,7 @@ let AuthEmailService = class AuthEmailService {
153
153
  }
154
154
  async createPendingRegistration(data) {
155
155
  this.assertEmailEnabled();
156
- const userRepository = await this.getRepository(_userentity.User);
156
+ const userRepository = await this.getRepository(_appuserentity.AppUser);
157
157
  const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
158
158
  // Check if user already exists
159
159
  const existingUser = await userRepository.findOne({
@@ -198,7 +198,7 @@ let AuthEmailService = class AuthEmailService {
198
198
  }
199
199
  async forgotPassword(email, resetUrlBase) {
200
200
  this.assertEmailEnabled();
201
- const userRepository = await this.getRepository(_userentity.User);
201
+ const userRepository = await this.getRepository(_appuserentity.AppUser);
202
202
  const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
203
203
  const user = await userRepository.findOne({
204
204
  where: {
@@ -249,7 +249,7 @@ let AuthEmailService = class AuthEmailService {
249
249
  };
250
250
  }
251
251
  async resetPassword(token, newPassword) {
252
- const userRepository = await this.getRepository(_userentity.User);
252
+ const userRepository = await this.getRepository(_appuserentity.AppUser);
253
253
  const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
254
254
  // Hash the incoming token to compare with stored hash
255
255
  const tokenHash = this.hashToken(token);
@@ -291,7 +291,7 @@ let AuthEmailService = class AuthEmailService {
291
291
  }
292
292
  async verifyEmail(token) {
293
293
  const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
294
- const userRepository = await this.getRepository(_userentity.User);
294
+ const userRepository = await this.getRepository(_appuserentity.AppUser);
295
295
  const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
296
296
  // Hash the incoming token to compare with stored hash
297
297
  const tokenHash = this.hashToken(token);
@@ -375,7 +375,7 @@ let AuthEmailService = class AuthEmailService {
375
375
  async resendVerificationEmail(email, verifyUrlBase) {
376
376
  this.assertEmailEnabled();
377
377
  const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
378
- const userRepository = await this.getRepository(_userentity.User);
378
+ const userRepository = await this.getRepository(_appuserentity.AppUser);
379
379
  const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
380
380
  // Check pending registration first
381
381
  const pending = await pendingRepository.findOne({
@@ -19,11 +19,11 @@ const _jwt = require("@nestjs/jwt");
19
19
  const _bcrypt = /*#__PURE__*/ _interop_require_wildcard(require("bcrypt"));
20
20
  const _config = require("../config");
21
21
  const _authenticationdto = require("../dtos/authentication.dto");
22
+ const _appuserentity = require("../entities/app-user.entity");
22
23
  const _companybranchentity = require("../entities/company-branch.entity");
23
24
  const _companyentity = require("../entities/company.entity");
24
25
  const _pendingregistrationentity = require("../entities/pending-registration.entity");
25
26
  const _usercompanypermissionentity = require("../entities/user-company-permission.entity");
26
- const _userentity = require("../entities/user.entity");
27
27
  const _enums1 = require("../enums");
28
28
  const _userenricherinterface = require("../interfaces/user-enricher.interface");
29
29
  const _authconfigservice = require("./auth-config.service");
@@ -113,11 +113,11 @@ let AuthenticationService = class AuthenticationService {
113
113
  message: 'User repository not available',
114
114
  messageKey: _constants.SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE,
115
115
  messageVariables: {
116
- entity: 'User'
116
+ entity: 'AppUser'
117
117
  }
118
118
  });
119
119
  }
120
- return this.dataSourceProvider.getRepository(_userentity.User);
120
+ return this.dataSourceProvider.getRepository(_appuserentity.AppUser);
121
121
  }
122
122
  async getPermissionRepository() {
123
123
  if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
@@ -299,7 +299,7 @@ let AuthenticationService = class AuthenticationService {
299
299
  await queryRunner.startTransaction();
300
300
  try {
301
301
  const hashedPassword = await _bcrypt.hash(dto.password, _config.BCRYPT_SALT_ROUNDS);
302
- const user = queryRunner.manager.create(_userentity.User, {
302
+ const user = queryRunner.manager.create(_appuserentity.AppUser, {
303
303
  name: dto.name,
304
304
  email: dto.email,
305
305
  phone: dto.phone,
@@ -308,7 +308,7 @@ let AuthenticationService = class AuthenticationService {
308
308
  emailVerified: false,
309
309
  phoneVerified: false
310
310
  });
311
- await queryRunner.manager.save(_userentity.User, user);
311
+ await queryRunner.manager.save(_appuserentity.AppUser, user);
312
312
  if (this.userEnricher?.onUserCreated) {
313
313
  await this.userEnricher.onUserCreated(user.id, dto.additionalFields ?? null, queryRunner);
314
314
  }
@@ -342,7 +342,7 @@ let AuthenticationService = class AuthenticationService {
342
342
  await queryRunner.connect();
343
343
  await queryRunner.startTransaction();
344
344
  try {
345
- const existingUser = await queryRunner.manager.findOne(_userentity.User, {
345
+ const existingUser = await queryRunner.manager.findOne(_appuserentity.AppUser, {
346
346
  where: {
347
347
  email: pending.email
348
348
  }
@@ -353,7 +353,7 @@ let AuthenticationService = class AuthenticationService {
353
353
  messageKey: _config.AUTH_MESSAGES.REGISTER_EMAIL_EXISTS
354
354
  });
355
355
  }
356
- const user = queryRunner.manager.create(_userentity.User, {
356
+ const user = queryRunner.manager.create(_appuserentity.AppUser, {
357
357
  name: pending.name,
358
358
  email: pending.email,
359
359
  phone: pending.phone,
@@ -362,7 +362,7 @@ let AuthenticationService = class AuthenticationService {
362
362
  emailVerified: true,
363
363
  phoneVerified: false
364
364
  });
365
- await queryRunner.manager.save(_userentity.User, user);
365
+ await queryRunner.manager.save(_appuserentity.AppUser, user);
366
366
  if (this.userEnricher?.onUserCreated) {
367
367
  await this.userEnricher.onUserCreated(user.id, pending.additionalFields ?? null, queryRunner);
368
368
  }
@@ -17,8 +17,8 @@ const _common = require("@nestjs/common");
17
17
  const _bcrypt = /*#__PURE__*/ _interop_require_wildcard(require("bcrypt"));
18
18
  const _config = require("../config");
19
19
  const _userdto = require("../dtos/user.dto");
20
+ const _appuserentity = require("../entities/app-user.entity");
20
21
  const _usercompanypermissionentity = require("../entities/user-company-permission.entity");
21
- const _userentity = require("../entities/user.entity");
22
22
  const _enums = require("../enums");
23
23
  const _userenricherinterface = require("../interfaces/user-enricher.interface");
24
24
  const _authconfigservice = require("./auth-config.service");
@@ -93,7 +93,7 @@ function _ts_param(paramIndex, decorator) {
93
93
  }
94
94
  let UserService = class UserService extends _classes.RequestScopedApiService {
95
95
  resolveEntity() {
96
- return _userentity.User;
96
+ return _appuserentity.AppUser;
97
97
  }
98
98
  getDataSourceProvider() {
99
99
  return this.dataSourceProvider;
@@ -200,24 +200,24 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
200
200
  return this.convertEntityToDto(entity);
201
201
  }
202
202
  convertRawToResponseDto(raw) {
203
- const isActive = raw.user_companyIsActive !== undefined ? Boolean(raw.user_companyIsActive) : raw.user_is_active;
203
+ const isActive = raw.appuser_companyIsActive !== undefined ? Boolean(raw.appuser_companyIsActive) : raw.appuser_is_active;
204
204
  return {
205
- id: raw.user_id,
206
- name: raw.user_name,
207
- phone: raw.user_phone,
208
- email: raw.user_email,
209
- profilePictureId: raw.user_profile_picture_id,
205
+ id: raw.appuser_id,
206
+ name: raw.appuser_name,
207
+ phone: raw.appuser_phone,
208
+ email: raw.appuser_email,
209
+ profilePictureId: raw.appuser_profile_picture_id,
210
210
  isActive,
211
- emailVerified: raw.user_email_verified,
212
- phoneVerified: raw.user_phone_verified,
213
- lastLoginAt: raw.user_last_login_at,
214
- additionalFields: raw.user_additional_fields,
215
- createdAt: raw.user_created_at,
216
- updatedAt: raw.user_updated_at,
217
- deletedAt: raw.user_deleted_at,
218
- createdById: raw.user_created_by_id,
219
- updatedById: raw.user_updated_by_id,
220
- deletedById: raw.user_deleted_by_id
211
+ emailVerified: raw.appuser_email_verified,
212
+ phoneVerified: raw.appuser_phone_verified,
213
+ lastLoginAt: raw.appuser_last_login_at,
214
+ additionalFields: raw.appuser_additional_fields,
215
+ createdAt: raw.appuser_created_at,
216
+ updatedAt: raw.appuser_updated_at,
217
+ deletedAt: raw.appuser_deleted_at,
218
+ createdById: raw.appuser_created_by_id,
219
+ updatedById: raw.appuser_updated_by_id,
220
+ deletedById: raw.appuser_deleted_by_id
221
221
  };
222
222
  }
223
223
  convertEntityToDto(entity) {
@@ -243,8 +243,22 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
243
243
  convertEntityListToResponseListDto(entities, isRaw) {
244
244
  return entities.map((entity)=>this.convertEntityToResponseDto(entity, isRaw));
245
245
  }
246
+ async getFilterQuery(query, filter, _user) {
247
+ for (const [key, value] of Object.entries(filter)){
248
+ if (key === 'actionCode' || key === 'isActive') {
249
+ continue; // real continue
250
+ }
251
+ query.andWhere(`${this.entityName}.${key} = :value`, {
252
+ value
253
+ });
254
+ }
255
+ return {
256
+ query,
257
+ isRaw: false
258
+ };
259
+ }
246
260
  async getGlobalSearchQuery(query, search, _user) {
247
- query.andWhere('(user.name LIKE :search OR user.email LIKE :search OR user.phone LIKE :search)', {
261
+ query.andWhere('(appuser.name LIKE :search OR appuser.email LIKE :search OR appuser.phone LIKE :search)', {
248
262
  search: `%${search}%`
249
263
  });
250
264
  return {
@@ -253,50 +267,99 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
253
267
  };
254
268
  }
255
269
  async getExtraManipulateQuery(query, filterAndPaginationDto, user) {
256
- let result = await super.getExtraManipulateQuery(query, filterAndPaginationDto, user);
257
- if (this.userEnricher?.enrichListQuery) {
258
- await this.userEnricher.enrichListQuery(query, user);
259
- }
270
+ const superQuery = await super.getExtraManipulateQuery(query, filterAndPaginationDto, user);
260
271
  const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
261
- if (!isCompanyFeatureEnabled) {
262
- return result;
263
- }
264
- if (user?.companyId && this.permissionRepository) {
265
- query.addSelect(`(SELECT perm.is_active FROM user_company_permissions perm
266
- WHERE perm.user_id = user.id
267
- AND perm.permission_type = :permissionType
268
- AND perm.target_id = :companyId
269
- LIMIT 1)`, 'user_companyIsActive');
270
- query.setParameter('permissionType', _enums.PermissionType.COMPANY);
271
- query.setParameter('companyId', user.companyId);
272
- if (filterAndPaginationDto.filter?.isActive !== undefined) {
272
+ const filter = filterAndPaginationDto?.filter;
273
+ // --- Action code filter (applies regardless of company feature) ---
274
+ if (filter?.actionCode) {
275
+ const actionCode = filter.actionCode;
276
+ const companyId = isCompanyFeatureEnabled ? user?.companyId : undefined;
277
+ if (isCompanyFeatureEnabled && !companyId) {
278
+ // Company feature is on but no companyId — no results should match
279
+ query.andWhere('1 = 0');
280
+ return {
281
+ query,
282
+ isRaw: true
283
+ };
284
+ }
285
+ const isPermissionModeFound = this.authConfig.isPermissionModeFound();
286
+ if (!isPermissionModeFound) {
287
+ // Optionally log a warning so the caller knows the filter was ignored
288
+ this.logAdapter.warn(`actionCode filter was requested but permission mode is not set to 'found' — filter ignored`);
289
+ } else {
273
290
  query.andWhere(`EXISTS (
274
- SELECT 1 FROM user_company_permissions perm
275
- WHERE perm.user_id = user.id
276
- AND perm.permission_type = :permissionType
277
- AND perm.target_id = :companyId
278
- AND perm.is_active = :isActiveValue
279
- )`, {
280
- permissionType: _enums.PermissionType.COMPANY,
281
- companyId: user.companyId,
282
- isActiveValue: filterAndPaginationDto.filter.isActive
291
+ SELECT 1 FROM user_iam_permission perm
292
+ WHERE perm.permission_type = 'user_role'
293
+ AND perm.source_id = appuser.id
294
+ AND perm.target_id IN (
295
+ SELECT source_id FROM user_iam_permission
296
+ WHERE permission_type = 'role_action'
297
+ AND target_id = (SELECT id FROM action WHERE code = :actionCode)
298
+ ${isCompanyFeatureEnabled ? 'AND company_id = :actionCompanyId' : ''}
299
+ )
300
+ ) OR EXISTS (
301
+ SELECT 1 FROM user_iam_permission perm
302
+ WHERE perm.permission_type = 'user_action'
303
+ AND perm.source_id = appuser.id
304
+ AND perm.target_id = (SELECT id FROM action WHERE code = :actionCode)
305
+ ${isCompanyFeatureEnabled ? 'AND company_id = :actionCompanyId' : ''}
306
+ )`, {
307
+ actionCode,
308
+ ...isCompanyFeatureEnabled && {
309
+ actionCompanyId: companyId
310
+ }
311
+ });
312
+ }
313
+ }
314
+ // --- Non-company path ---
315
+ if (!isCompanyFeatureEnabled) {
316
+ if (filter?.isActive !== undefined) {
317
+ query.andWhere(`${this.entityName}.isActive = :value`, {
318
+ value: filter.isActive
283
319
  });
284
320
  }
321
+ return superQuery;
322
+ }
323
+ // --- Company feature path ---
324
+ if (!user?.companyId || !this.permissionRepository) {
325
+ return superQuery;
326
+ }
327
+ // Add companyIsActive as a selected column
328
+ query.addSelect(`(SELECT perm.is_active FROM user_company_permissions perm
329
+ WHERE perm.user_id = appuser.id
330
+ AND perm.permission_type = :permissionType
331
+ AND perm.target_id = :companyId
332
+ LIMIT 1)`, 'user_companyIsActive');
333
+ query.setParameter('permissionType', _enums.PermissionType.COMPANY);
334
+ query.setParameter('companyId', user.companyId);
335
+ // Filter by isActive within the company if requested
336
+ if (filter?.isActive !== undefined) {
285
337
  query.andWhere(`EXISTS (
286
- SELECT 1 FROM user_company_permissions perm
287
- WHERE perm.user_id = user.id
338
+ SELECT 1 FROM user_company_permissions perm
339
+ WHERE perm.user_id = appuser.id
288
340
  AND perm.permission_type = :permissionType
289
341
  AND perm.target_id = :companyId
290
- )`, {
342
+ AND perm.is_active = :isActiveValue
343
+ )`, {
291
344
  permissionType: _enums.PermissionType.COMPANY,
292
- companyId: user.companyId
345
+ companyId: user.companyId,
346
+ isActiveValue: filter.isActive
293
347
  });
294
- return {
295
- query,
296
- isRaw: true
297
- };
298
348
  }
299
- return result;
349
+ // Always restrict to users belonging to this company
350
+ query.andWhere(`EXISTS (
351
+ SELECT 1 FROM user_company_permissions perm
352
+ WHERE perm.user_id = appuser.id
353
+ AND perm.permission_type = :permissionType
354
+ AND perm.target_id = :companyId
355
+ )`, {
356
+ permissionType: _enums.PermissionType.COMPANY,
357
+ companyId: user.companyId
358
+ });
359
+ return {
360
+ query,
361
+ isRaw: true
362
+ };
300
363
  }
301
364
  async insert(dto, user) {
302
365
  const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
@@ -496,7 +559,7 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
496
559
  }
497
560
  }
498
561
  constructor(cacheManager, utilsService, dataSourceProvider, authConfig, userEnricher){
499
- 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;
562
+ 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 = (0, _classes.createModuleLogger)(this.constructor.name);
500
563
  }
501
564
  };
502
565
  _ts_decorate([
@@ -14,7 +14,7 @@ const _core = require("@nestjs/core");
14
14
  const _passport = require("@nestjs/passport");
15
15
  const _passportjwt = require("passport-jwt");
16
16
  const _config = require("../config");
17
- const _userentity = require("../entities/user.entity");
17
+ const _appuserentity = require("../entities/app-user.entity");
18
18
  const _authconfigservice = require("../services/auth-config.service");
19
19
  const _authdatasourceprovider = require("../services/auth-datasource.provider");
20
20
  const _authenticationservice = require("../services/authentication.service");
@@ -67,7 +67,7 @@ let JwtStrategy = class JwtStrategy extends (0, _passport.PassportStrategy)(_pas
67
67
  strict: false
68
68
  });
69
69
  if (provider) {
70
- return provider.getRepository(_userentity.User);
70
+ return provider.getRepository(_appuserentity.AppUser);
71
71
  }
72
72
  } catch {
73
73
  throw new _common.UnauthorizedException({
@@ -1,3 +1,3 @@
1
1
  import { UserRoot } from '@flusys/nestjs-shared';
2
- export declare class User extends UserRoot {
2
+ export declare class AppUser extends UserRoot {
3
3
  }
@@ -1,20 +1,20 @@
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
- declare const BaseEntities: (typeof User | typeof UserToken)[];
13
+ declare const BaseEntities: (typeof AppUser | typeof UserToken)[];
14
14
  declare const EmailEntities: (typeof PendingRegistration)[];
15
15
  declare const CompanyEntities: (typeof Company | typeof CompanyBranch | typeof UserCompanyPermission)[];
16
16
  export interface IAuthEntitiesConfig {
17
17
  enableCompanyFeature?: boolean;
18
18
  enableEmailVerification?: boolean;
19
19
  }
20
- export declare function getEntitiesByConfig(config: IAuthEntitiesConfig): (typeof BaseEntities[number] | typeof EmailEntities[number] | typeof CompanyEntities[number])[];
20
+ export declare function getEntitiesByConfig(config: IAuthEntitiesConfig): ((typeof BaseEntities)[number] | (typeof EmailEntities)[number] | (typeof CompanyEntities)[number])[];
@@ -1,12 +1,12 @@
1
1
  import { PermissionType } from '../enums';
2
+ import { AppUser } from './app-user.entity';
2
3
  import { CompanyBranch } from './company-branch.entity';
3
4
  import { Company } from './company.entity';
4
- import { User } from './user.entity';
5
5
  export { PermissionType };
6
6
  export declare class UserCompanyPermission {
7
7
  id: string;
8
8
  userId: string;
9
- user?: User;
9
+ user?: AppUser;
10
10
  permissionType: string;
11
11
  targetId: string;
12
12
  isActive: boolean;
@@ -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);
@@ -20,7 +20,7 @@ function _ts_decorate(decorators, target, key, desc) {
20
20
  function _ts_metadata(k, v) {
21
21
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
22
  }
23
- import { Identity } from '@flusys/nestjs-shared';
23
+ import { getJsonColumnType, Identity } from '@flusys/nestjs-shared';
24
24
  import { Column, Entity, JoinColumn, ManyToOne, OneToMany, Unique } from 'typeorm';
25
25
  import { Company } from './company.entity';
26
26
  export class CompanyBranch extends Identity {
@@ -99,7 +99,7 @@ _ts_decorate([
99
99
  ], CompanyBranch.prototype, "isActive", void 0);
100
100
  _ts_decorate([
101
101
  Column({
102
- type: 'json',
102
+ type: getJsonColumnType(),
103
103
  nullable: true
104
104
  }),
105
105
  _ts_metadata("design:type", Object)
@@ -20,7 +20,7 @@ function _ts_decorate(decorators, target, key, desc) {
20
20
  function _ts_metadata(k, v) {
21
21
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
22
  }
23
- import { Identity } from '@flusys/nestjs-shared';
23
+ import { getJsonColumnType, Identity } from '@flusys/nestjs-shared';
24
24
  import { Column, Entity, OneToMany } from 'typeorm';
25
25
  import { CompanyBranch } from './company-branch.entity';
26
26
  export class Company extends Identity {
@@ -113,14 +113,14 @@ _ts_decorate([
113
113
  ], Company.prototype, "isActive", void 0);
114
114
  _ts_decorate([
115
115
  Column({
116
- type: 'json',
116
+ type: getJsonColumnType(),
117
117
  nullable: true
118
118
  }),
119
119
  _ts_metadata("design:type", Object)
120
120
  ], Company.prototype, "settings", void 0);
121
121
  _ts_decorate([
122
122
  Column({
123
- type: 'json',
123
+ type: getJsonColumnType(),
124
124
  nullable: true
125
125
  }),
126
126
  _ts_metadata("design:type", Object)
@@ -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 = [
@@ -20,6 +20,7 @@ function _ts_decorate(decorators, target, key, desc) {
20
20
  function _ts_metadata(k, v) {
21
21
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
22
  }
23
+ import { getJsonColumnType } from '@flusys/nestjs-shared';
23
24
  import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
24
25
  export class PendingRegistration {
25
26
  constructor(){
@@ -85,7 +86,7 @@ _ts_decorate([
85
86
  ], PendingRegistration.prototype, "expiresAt", void 0);
86
87
  _ts_decorate([
87
88
  Column({
88
- type: 'json',
89
+ type: getJsonColumnType(),
89
90
  nullable: true,
90
91
  name: 'company_data'
91
92
  }),
@@ -93,7 +94,7 @@ _ts_decorate([
93
94
  ], PendingRegistration.prototype, "companyData", void 0);
94
95
  _ts_decorate([
95
96
  Column({
96
- type: 'json',
97
+ type: getJsonColumnType(),
97
98
  nullable: true,
98
99
  name: 'additional_fields'
99
100
  }),
@@ -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
  }
@@ -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.0",
3
+ "version": "5.0.3",
4
4
  "description": "Authentication and authorization infrastructure for NestJS applications",
5
5
  "main": "cjs/index.js",
6
6
  "module": "fesm/index.js",
@@ -81,7 +81,7 @@
81
81
  "@nestjs/passport": "^10.0.0 || ^11.0.0",
82
82
  "@nestjs/swagger": "^7.0.0 || ^11.0.0",
83
83
  "@nestjs/typeorm": "^10.0.0 || ^11.0.0",
84
- "bcrypt": "^5.0.0 || ^6.0.0",
84
+ "bcrypt": "^5.0.1 || ^6.0.0",
85
85
  "class-transformer": "^0.5.0",
86
86
  "class-validator": "^0.14.0",
87
87
  "nodemailer": "^7.0.0",
@@ -93,7 +93,7 @@
93
93
  "rxjs": "^7.8.0"
94
94
  },
95
95
  "dependencies": {
96
- "@flusys/nestjs-core": "5.0.0",
97
- "@flusys/nestjs-shared": "5.0.0"
96
+ "@flusys/nestjs-core": "5.0.3",
97
+ "@flusys/nestjs-shared": "5.0.3"
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;
@@ -4,12 +4,12 @@ import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
4
4
  import { UtilsService } from '@flusys/nestjs-shared/modules';
5
5
  import { EntityTarget, Repository, SelectQueryBuilder } from 'typeorm';
6
6
  import { CreateUserDto, UpdateUserDto } from '../dtos/user.dto';
7
- import { User } from '../entities/user.entity';
7
+ import { AppUser } from '../entities/app-user.entity';
8
8
  import { IEnrichedUser, IProfileExtras, IProfileSection, IProfileSectionData, IUserEnricher } from '../interfaces/user-enricher.interface';
9
9
  import { IUser } from '../interfaces/user.interface';
10
10
  import { AuthConfigService } from './auth-config.service';
11
11
  import { AuthDataSourceProvider } from './auth-datasource.provider';
12
- export declare class UserService extends RequestScopedApiService<CreateUserDto, UpdateUserDto, IUser, User, Repository<User>> {
12
+ export declare class UserService extends RequestScopedApiService<CreateUserDto, UpdateUserDto, IUser, AppUser, Repository<AppUser>> {
13
13
  protected cacheManager: HybridCache;
14
14
  protected utilsService: UtilsService;
15
15
  private readonly dataSourceProvider;
@@ -17,25 +17,30 @@ export declare class UserService extends RequestScopedApiService<CreateUserDto,
17
17
  private readonly userEnricher?;
18
18
  private permissionRepository?;
19
19
  private dataSource?;
20
+ private readonly logAdapter;
20
21
  constructor(cacheManager: HybridCache, utilsService: UtilsService, dataSourceProvider: AuthDataSourceProvider, authConfig: AuthConfigService, userEnricher?: IUserEnricher);
21
- protected resolveEntity(): EntityTarget<User>;
22
+ protected resolveEntity(): EntityTarget<AppUser>;
22
23
  protected getDataSourceProvider(): AuthDataSourceProvider;
23
24
  protected ensureRepositoryInitialized(): Promise<void>;
24
25
  private getUserForProfile;
25
26
  verifyEmail(id: string): Promise<void>;
26
27
  verifyPhone(id: string): Promise<void>;
27
28
  updateStatus(id: string, isActive: boolean, user?: ILoggedUserInfo | null): Promise<void>;
28
- protected convertSingleDtoToEntity(dto: CreateUserDto | UpdateUserDto, user: ILoggedUserInfo | null): Promise<User>;
29
- protected convertEntityToResponseDto(entity: User, isRaw: boolean): IUser;
29
+ protected convertSingleDtoToEntity(dto: CreateUserDto | UpdateUserDto, user: ILoggedUserInfo | null): Promise<AppUser>;
30
+ protected convertEntityToResponseDto(entity: AppUser, isRaw: boolean): IUser;
30
31
  private convertRawToResponseDto;
31
32
  private convertEntityToDto;
32
- protected convertEntityListToResponseListDto(entities: User[], isRaw: boolean): IUser[];
33
- protected getGlobalSearchQuery(query: SelectQueryBuilder<User>, search: string, _user: ILoggedUserInfo | null): Promise<{
34
- query: SelectQueryBuilder<User>;
33
+ protected convertEntityListToResponseListDto(entities: AppUser[], isRaw: boolean): IUser[];
34
+ protected getFilterQuery(query: SelectQueryBuilder<AppUser>, filter: Record<string, unknown>, _user: ILoggedUserInfo | null): Promise<{
35
+ query: SelectQueryBuilder<AppUser>;
35
36
  isRaw: boolean;
36
37
  }>;
37
- protected getExtraManipulateQuery(query: SelectQueryBuilder<User>, filterAndPaginationDto: FilterAndPaginationDto, user: ILoggedUserInfo | null): Promise<{
38
- query: SelectQueryBuilder<User>;
38
+ protected getGlobalSearchQuery(query: SelectQueryBuilder<AppUser>, search: string, _user: ILoggedUserInfo | null): Promise<{
39
+ query: SelectQueryBuilder<AppUser>;
40
+ isRaw: boolean;
41
+ }>;
42
+ protected getExtraManipulateQuery(query: SelectQueryBuilder<AppUser>, filterAndPaginationDto: FilterAndPaginationDto, user: ILoggedUserInfo | null): Promise<{
43
+ query: SelectQueryBuilder<AppUser>;
39
44
  isRaw: boolean;
40
45
  }>;
41
46
  insert(dto: CreateUserDto, user: ILoggedUserInfo | null): Promise<IUser>;