@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.
- package/README.md +285 -115
- package/cjs/controllers/action.controller.js +45 -2
- package/cjs/controllers/company-action-permission.controller.js +16 -10
- package/cjs/controllers/my-permission.controller.js +7 -3
- package/cjs/controllers/role-permission.controller.js +35 -17
- package/cjs/controllers/role.controller.js +46 -3
- package/cjs/controllers/user-action-permission.controller.js +26 -11
- package/cjs/dtos/action.dto.js +0 -27
- package/cjs/dtos/permission.dto.js +117 -27
- package/cjs/dtos/role.dto.js +0 -27
- package/cjs/entities/permission-base.entity.js +0 -12
- package/cjs/helpers/company-access.helper.js +19 -0
- package/cjs/helpers/index.js +1 -1
- package/cjs/interfaces/iam-module-options.interface.js +0 -14
- package/cjs/interfaces/index.js +0 -1
- package/cjs/modules/iam.module.js +50 -102
- package/cjs/services/action.service.js +30 -41
- package/cjs/services/iam-config.service.js +2 -5
- package/cjs/services/{iam-datasource.provider.js → iam-datasource.service.js} +33 -36
- package/cjs/services/index.js +1 -1
- package/cjs/services/permission-cache.service.js +31 -61
- package/cjs/services/permission.service.js +160 -188
- package/cjs/services/role.service.js +8 -8
- package/cjs/types/logic-node.type.js +0 -24
- package/controllers/company-action-permission.controller.d.ts +3 -3
- package/controllers/my-permission.controller.d.ts +2 -2
- package/controllers/role-permission.controller.d.ts +7 -5
- package/controllers/user-action-permission.controller.d.ts +6 -4
- package/dtos/action.dto.d.ts +0 -7
- package/dtos/permission.dto.d.ts +4 -0
- package/dtos/role.dto.d.ts +0 -7
- package/entities/permission-base.entity.d.ts +0 -4
- package/fesm/controllers/action.controller.js +47 -4
- package/fesm/controllers/company-action-permission.controller.js +18 -12
- package/fesm/controllers/index.js +1 -1
- package/fesm/controllers/my-permission.controller.js +7 -3
- package/fesm/controllers/role-permission.controller.js +37 -19
- package/fesm/controllers/role.controller.js +45 -2
- package/fesm/controllers/user-action-permission.controller.js +28 -13
- package/fesm/dtos/action.dto.js +0 -24
- package/fesm/dtos/permission.dto.js +117 -29
- package/fesm/dtos/role.dto.js +0 -24
- package/fesm/entities/permission-base.entity.js +0 -12
- package/fesm/helpers/company-access.helper.js +14 -0
- package/fesm/helpers/index.js +1 -1
- package/fesm/interfaces/iam-module-options.interface.js +3 -1
- package/fesm/interfaces/index.js +0 -1
- package/fesm/modules/iam.module.js +52 -104
- package/fesm/services/action.service.js +32 -43
- package/fesm/services/iam-config.service.js +2 -5
- package/fesm/services/{iam-datasource.provider.js → iam-datasource.service.js} +31 -34
- package/fesm/services/index.js +1 -1
- package/fesm/services/permission-cache.service.js +31 -61
- package/fesm/services/permission.service.js +161 -189
- package/fesm/services/role.service.js +8 -8
- package/fesm/types/logic-node.type.js +1 -10
- package/helpers/company-access.helper.d.ts +3 -0
- package/helpers/index.d.ts +1 -1
- package/interfaces/iam-module-options.interface.d.ts +9 -1
- package/interfaces/index.d.ts +0 -1
- package/modules/iam.module.d.ts +2 -2
- package/package.json +3 -3
- package/services/action.service.d.ts +6 -4
- package/services/iam-config.service.d.ts +2 -2
- package/services/{iam-datasource.provider.d.ts → iam-datasource.service.d.ts} +4 -5
- package/services/index.d.ts +1 -1
- package/services/permission-cache.service.d.ts +4 -6
- package/services/permission.service.d.ts +8 -4
- package/services/role.service.d.ts +3 -3
- package/types/logic-node.type.d.ts +0 -8
- package/cjs/helpers/permission-evaluator.helper.js +0 -175
- package/cjs/interfaces/iam-module-async-options.interface.js +0 -4
- package/fesm/helpers/permission-evaluator.helper.js +0 -165
- package/fesm/interfaces/iam-module-async-options.interface.js +0 -3
- package/helpers/permission-evaluator.helper.d.ts +0 -26
- 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
|
-
|
|
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 `${
|
|
42
|
+
return `${prefix}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
|
|
43
43
|
}
|
|
44
|
-
return `${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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);
|