@flusys/nestjs-iam 1.1.0-beta → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +285 -115
  2. package/cjs/controllers/action.controller.js +45 -2
  3. package/cjs/controllers/company-action-permission.controller.js +16 -10
  4. package/cjs/controllers/my-permission.controller.js +7 -3
  5. package/cjs/controllers/role-permission.controller.js +35 -17
  6. package/cjs/controllers/role.controller.js +46 -3
  7. package/cjs/controllers/user-action-permission.controller.js +26 -11
  8. package/cjs/dtos/action.dto.js +0 -27
  9. package/cjs/dtos/permission.dto.js +117 -27
  10. package/cjs/dtos/role.dto.js +0 -27
  11. package/cjs/entities/permission-base.entity.js +0 -12
  12. package/cjs/helpers/company-access.helper.js +19 -0
  13. package/cjs/helpers/index.js +1 -1
  14. package/cjs/interfaces/iam-module-options.interface.js +0 -14
  15. package/cjs/interfaces/index.js +0 -1
  16. package/cjs/modules/iam.module.js +50 -102
  17. package/cjs/services/action.service.js +30 -41
  18. package/cjs/services/iam-config.service.js +2 -5
  19. package/cjs/services/{iam-datasource.provider.js → iam-datasource.service.js} +33 -36
  20. package/cjs/services/index.js +1 -1
  21. package/cjs/services/permission-cache.service.js +31 -61
  22. package/cjs/services/permission.service.js +160 -188
  23. package/cjs/services/role.service.js +8 -8
  24. package/cjs/types/logic-node.type.js +0 -24
  25. package/controllers/company-action-permission.controller.d.ts +3 -3
  26. package/controllers/my-permission.controller.d.ts +2 -2
  27. package/controllers/role-permission.controller.d.ts +7 -5
  28. package/controllers/user-action-permission.controller.d.ts +6 -4
  29. package/dtos/action.dto.d.ts +0 -7
  30. package/dtos/permission.dto.d.ts +4 -0
  31. package/dtos/role.dto.d.ts +0 -7
  32. package/entities/permission-base.entity.d.ts +0 -4
  33. package/fesm/controllers/action.controller.js +47 -4
  34. package/fesm/controllers/company-action-permission.controller.js +18 -12
  35. package/fesm/controllers/index.js +1 -1
  36. package/fesm/controllers/my-permission.controller.js +7 -3
  37. package/fesm/controllers/role-permission.controller.js +37 -19
  38. package/fesm/controllers/role.controller.js +45 -2
  39. package/fesm/controllers/user-action-permission.controller.js +28 -13
  40. package/fesm/dtos/action.dto.js +0 -24
  41. package/fesm/dtos/permission.dto.js +117 -29
  42. package/fesm/dtos/role.dto.js +0 -24
  43. package/fesm/entities/permission-base.entity.js +0 -12
  44. package/fesm/helpers/company-access.helper.js +14 -0
  45. package/fesm/helpers/index.js +1 -1
  46. package/fesm/interfaces/iam-module-options.interface.js +3 -1
  47. package/fesm/interfaces/index.js +0 -1
  48. package/fesm/modules/iam.module.js +52 -104
  49. package/fesm/services/action.service.js +32 -43
  50. package/fesm/services/iam-config.service.js +2 -5
  51. package/fesm/services/{iam-datasource.provider.js → iam-datasource.service.js} +31 -34
  52. package/fesm/services/index.js +1 -1
  53. package/fesm/services/permission-cache.service.js +31 -61
  54. package/fesm/services/permission.service.js +161 -189
  55. package/fesm/services/role.service.js +8 -8
  56. package/fesm/types/logic-node.type.js +1 -10
  57. package/helpers/company-access.helper.d.ts +3 -0
  58. package/helpers/index.d.ts +1 -1
  59. package/interfaces/iam-module-options.interface.d.ts +9 -1
  60. package/interfaces/index.d.ts +0 -1
  61. package/modules/iam.module.d.ts +2 -2
  62. package/package.json +3 -3
  63. package/services/action.service.d.ts +6 -4
  64. package/services/iam-config.service.d.ts +2 -2
  65. package/services/{iam-datasource.provider.d.ts → iam-datasource.service.d.ts} +4 -5
  66. package/services/index.d.ts +1 -1
  67. package/services/permission-cache.service.d.ts +4 -6
  68. package/services/permission.service.d.ts +8 -4
  69. package/services/role.service.d.ts +3 -3
  70. package/types/logic-node.type.d.ts +0 -8
  71. package/cjs/helpers/permission-evaluator.helper.js +0 -175
  72. package/cjs/interfaces/iam-module-async-options.interface.js +0 -4
  73. package/fesm/helpers/permission-evaluator.helper.js +0 -165
  74. package/fesm/interfaces/iam-module-async-options.interface.js +0 -3
  75. package/helpers/permission-evaluator.helper.d.ts +0 -26
  76. package/interfaces/iam-module-async-options.interface.d.ts +0 -11
@@ -26,22 +26,22 @@ function _ts_param(paramIndex, decorator) {
26
26
  };
27
27
  }
28
28
  import { HybridCache } from '@flusys/nestjs-shared';
29
+ import { ErrorHandler } from '@flusys/nestjs-shared/utils';
29
30
  import { Inject, Injectable, Logger } from '@nestjs/common';
30
31
  export class PermissionCacheService {
31
32
  // Cache Key Generation
32
33
  generateCacheKey(options) {
33
- const { userId, companyId, branchId, enableCompanyFeature } = options;
34
- if (enableCompanyFeature && companyId) {
35
- return `${this.CACHE_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
36
- }
37
- return `${this.CACHE_PREFIX}:user:${userId}`;
34
+ return this.buildCacheKey(this.CACHE_PREFIX, options);
38
35
  }
39
36
  generateMyPermissionsCacheKey(options) {
37
+ return this.buildCacheKey(this.MY_PERMISSIONS_PREFIX, options);
38
+ }
39
+ buildCacheKey(prefix, options) {
40
40
  const { userId, companyId, branchId, enableCompanyFeature } = options;
41
41
  if (enableCompanyFeature && companyId) {
42
- return `${this.MY_PERMISSIONS_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
42
+ return `${prefix}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
43
43
  }
44
- return `${this.MY_PERMISSIONS_PREFIX}:user:${userId}`;
44
+ return `${prefix}:user:${userId}`;
45
45
  }
46
46
  // Cache Operations
47
47
  async setPermissions(options, permissions) {
@@ -50,20 +50,11 @@ export class PermissionCacheService {
50
50
  await this.cacheManager.set(key, permissions, this.TTL);
51
51
  this.logger.debug(`Cached ${permissions.length} permissions for key: ${key}`);
52
52
  } catch (error) {
53
- this.logger.error(`Failed to cache permissions: ${error}`);
53
+ const errorMessage = ErrorHandler.getErrorMessage(error);
54
+ this.logger.error(`Failed to cache permissions: ${errorMessage}`);
54
55
  // Don't throw - cache failure shouldn't break the operation
55
56
  }
56
57
  }
57
- async getPermissions(options) {
58
- try {
59
- const key = this.generateCacheKey(options);
60
- const result = await this.cacheManager.get(key);
61
- return result || null;
62
- } catch (error) {
63
- this.logger.error(`Failed to get permissions from cache: ${error}`);
64
- return null;
65
- }
66
- }
67
58
  // My-Permissions Cache Operations
68
59
  async setMyPermissions(options, data) {
69
60
  try {
@@ -71,7 +62,8 @@ export class PermissionCacheService {
71
62
  await this.cacheManager.set(key, data, this.TTL);
72
63
  this.logger.debug(`Cached my-permissions for key: ${key} (${data.frontendActions.length} frontend, ${data.backendCodes.length} backend)`);
73
64
  } catch (error) {
74
- this.logger.error(`Failed to cache my-permissions: ${error}`);
65
+ const errorMessage = ErrorHandler.getErrorMessage(error);
66
+ this.logger.error(`Failed to cache my-permissions: ${errorMessage}`);
75
67
  }
76
68
  }
77
69
  async getMyPermissions(options) {
@@ -83,28 +75,35 @@ export class PermissionCacheService {
83
75
  }
84
76
  return result || null;
85
77
  } catch (error) {
86
- this.logger.error(`Failed to get my-permissions from cache: ${error}`);
78
+ const errorMessage = ErrorHandler.getErrorMessage(error);
79
+ this.logger.error(`Failed to get my-permissions from cache: ${errorMessage}`);
87
80
  return null;
88
81
  }
89
82
  }
90
- // Action Code Cache Operations
91
- async setActionCodeMap(codeToIdMap) {
83
+ // Action Code Cache Operations (tenant-aware for multi-tenant mode)
84
+ /** Generate tenant-aware cache key for action codes */ generateActionCodeCacheKey(tenantId) {
85
+ if (tenantId) {
86
+ return `${this.ACTION_CODE_PREFIX}:tenant:${tenantId}:map`;
87
+ }
88
+ return `${this.ACTION_CODE_PREFIX}:map`;
89
+ }
90
+ async setActionCodeMap(codeToIdMap, tenantId) {
92
91
  try {
93
- const key = `${this.ACTION_CODE_PREFIX}:map`;
92
+ const key = this.generateActionCodeCacheKey(tenantId);
94
93
  await this.cacheManager.set(key, codeToIdMap, this.ACTION_CODE_TTL);
95
- this.logger.debug(`Cached ${Object.keys(codeToIdMap).length} action code mappings`);
94
+ this.logger.debug(`Cached ${Object.keys(codeToIdMap).length} action code mappings${tenantId ? ` for tenant ${tenantId}` : ''}`);
96
95
  } catch (error) {
97
- this.logger.error(`Failed to cache action code map: ${error}`);
96
+ const errorMessage = ErrorHandler.getErrorMessage(error);
97
+ this.logger.error(`Failed to cache action code map: ${errorMessage}`);
98
98
  }
99
99
  }
100
- async getActionIdsByCodes(codes) {
100
+ async getActionIdsByCodes(codes, tenantId) {
101
101
  try {
102
- const key = `${this.ACTION_CODE_PREFIX}:map`;
102
+ const key = this.generateActionCodeCacheKey(tenantId);
103
103
  const fullMap = await this.cacheManager.get(key);
104
104
  if (!fullMap) {
105
105
  return null;
106
106
  }
107
- // Return only requested codes
108
107
  const result = {};
109
108
  for (const code of codes){
110
109
  if (fullMap[code]) {
@@ -113,19 +112,11 @@ export class PermissionCacheService {
113
112
  }
114
113
  return Object.keys(result).length > 0 ? result : null;
115
114
  } catch (error) {
116
- this.logger.error(`Failed to get action IDs from cache: ${error}`);
115
+ const errorMessage = ErrorHandler.getErrorMessage(error);
116
+ this.logger.error(`Failed to get action IDs from cache: ${errorMessage}`);
117
117
  return null;
118
118
  }
119
119
  }
120
- async invalidateActionCodeCache() {
121
- try {
122
- const key = `${this.ACTION_CODE_PREFIX}:map`;
123
- await this.cacheManager.del(key);
124
- this.logger.debug('Invalidated action code cache');
125
- } catch (error) {
126
- this.logger.warn(`Failed to invalidate action code cache: ${error}`);
127
- }
128
- }
129
120
  // Cache Invalidation
130
121
  async invalidateUser(userId, companyId, branchIds) {
131
122
  try {
@@ -135,10 +126,7 @@ export class PermissionCacheService {
135
126
  // My-permissions cache (full response) - user-based key
136
127
  `${this.MY_PERMISSIONS_PREFIX}:user:${userId}`
137
128
  ];
138
- // Add company-based keys if companyId provided
139
129
  if (companyId) {
140
- // If branchIds provided, invalidate all specified branches
141
- // Otherwise, invalidate only null branch (company-wide)
142
130
  const branches = branchIds?.length ? branchIds : [
143
131
  null
144
132
  ];
@@ -146,18 +134,17 @@ export class PermissionCacheService {
146
134
  keysToDelete.push(`${this.CACHE_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`, `${this.MY_PERMISSIONS_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`);
147
135
  }
148
136
  }
149
- // Parallel deletion for better performance
150
137
  await Promise.all(keysToDelete.map((key)=>this.cacheManager.del(key)));
151
138
  this.logger.debug(`Invalidated ${keysToDelete.length} cache keys for user ${userId}`);
152
139
  } catch (error) {
153
- this.logger.warn(`Failed to invalidate user cache for ${userId}: ${error}`);
140
+ const errorMessage = ErrorHandler.getErrorMessage(error);
141
+ this.logger.warn(`Failed to invalidate user cache for ${userId}: ${errorMessage}`);
154
142
  }
155
143
  }
156
144
  async invalidateUsers(userIds, companyId, branchIds) {
157
145
  if (userIds.length === 0) {
158
146
  return 0;
159
147
  }
160
- // Parallel invalidation for better performance
161
148
  const results = await Promise.allSettled(userIds.map((userId)=>this.invalidateUser(userId, companyId, branchIds)));
162
149
  const successCount = results.filter((r)=>r.status === 'fulfilled').length;
163
150
  const failedCount = results.filter((r)=>r.status === 'rejected').length;
@@ -169,13 +156,6 @@ export class PermissionCacheService {
169
156
  }
170
157
  return successCount;
171
158
  }
172
- /** Invalidate cache for all users in company (requires userIds via invalidateUsers) */ async invalidateCompany(companyId) {
173
- // Note: HybridCache doesn't support pattern matching (keys() method)
174
- // Company-wide invalidation requires passing individual user IDs
175
- // This is a placeholder that logs a warning
176
- this.logger.warn(`invalidateCompany called for ${companyId}, but pattern matching is not supported. ` + `Use invalidateUsers() with specific user IDs instead.`);
177
- return 0;
178
- }
179
159
  async invalidateRole(roleId, userIds, companyId, branchIds) {
180
160
  if (userIds.length === 0) {
181
161
  this.logger.debug(`No users found for role ${roleId}`);
@@ -187,16 +167,6 @@ export class PermissionCacheService {
187
167
  }
188
168
  return count;
189
169
  }
190
- // Administrative Operations
191
- /** Clear all permission caches (memory and redis) */ async clearAll() {
192
- try {
193
- await this.cacheManager.reset(); // Clear memory cache
194
- await this.cacheManager.resetL2(); // Clear redis cache
195
- this.logger.warn('Cleared all cache entries (memory and redis)');
196
- } catch (error) {
197
- this.logger.error(`Failed to clear all caches: ${error}`);
198
- }
199
- }
200
170
  constructor(cacheManager){
201
171
  _define_property(this, "cacheManager", void 0);
202
172
  _define_property(this, "logger", void 0);