@flusys/nestjs-iam 6.0.0 → 6.0.2
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/cjs/controllers/action.controller.js +17 -9
- package/cjs/controllers/user-action-permission.controller.js +0 -3
- package/cjs/docs/iam-swagger.config.js +6 -0
- package/cjs/dtos/action.dto.js +17 -0
- package/cjs/modules/iam.module.js +3 -1
- package/cjs/services/action.service.js +90 -7
- package/cjs/services/permission-cache.service.js +113 -110
- package/cjs/services/permission.service.js +213 -92
- package/cjs/services/role.service.js +47 -3
- package/controllers/action.controller.d.ts +5 -3
- package/dtos/action.dto.d.ts +3 -0
- package/entities/index.d.ts +2 -2
- package/fesm/controllers/action.controller.js +18 -10
- package/fesm/controllers/user-action-permission.controller.js +0 -3
- package/fesm/docs/iam-swagger.config.js +6 -0
- package/fesm/dtos/action.dto.js +14 -0
- package/fesm/modules/iam.module.js +4 -2
- package/fesm/services/action.service.js +90 -7
- package/fesm/services/permission-cache.service.js +101 -108
- package/fesm/services/permission.service.js +213 -92
- package/fesm/services/role.service.js +48 -4
- package/package.json +3 -3
- package/services/action.service.d.ts +7 -3
- package/services/permission-cache.service.d.ts +13 -19
- package/services/permission.service.d.ts +6 -3
- package/services/role.service.d.ts +7 -3
|
@@ -8,13 +8,14 @@ Object.defineProperty(exports, "ActionController", {
|
|
|
8
8
|
return ActionController;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const _guards = require("@flusys/nestjs-shared/guards");
|
|
12
11
|
const _nestjsshared = require("@flusys/nestjs-shared");
|
|
13
|
-
const
|
|
12
|
+
const _guards = require("@flusys/nestjs-shared/guards");
|
|
14
13
|
const _common = require("@nestjs/common");
|
|
15
14
|
const _swagger = require("@nestjs/swagger");
|
|
15
|
+
const _config = require("../config");
|
|
16
16
|
const _actiondto = require("../dtos/action.dto");
|
|
17
17
|
const _actionservice = require("../services/action.service");
|
|
18
|
+
const _iamconfigservice = require("../services/iam-config.service");
|
|
18
19
|
function _define_property(obj, key, value) {
|
|
19
20
|
if (key in obj) {
|
|
20
21
|
Object.defineProperty(obj, key, {
|
|
@@ -89,8 +90,8 @@ let ActionController = class ActionController extends (0, _nestjsshared.createAp
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
}) {
|
|
92
|
-
async getActionsForPermission(user) {
|
|
93
|
-
const actions = await this.actionService.getActionsForPermission(user);
|
|
93
|
+
async getActionsForPermission(dto, user) {
|
|
94
|
+
const actions = await this.actionService.getActionsForPermission(user, dto?.companyId);
|
|
94
95
|
return {
|
|
95
96
|
success: true,
|
|
96
97
|
message: 'Actions retrieved successfully',
|
|
@@ -107,8 +108,8 @@ let ActionController = class ActionController extends (0, _nestjsshared.createAp
|
|
|
107
108
|
data: tree
|
|
108
109
|
};
|
|
109
110
|
}
|
|
110
|
-
constructor(actionService){
|
|
111
|
-
super(actionService), _define_property(this, "actionService", void 0), this.actionService = actionService;
|
|
111
|
+
constructor(actionService, iamConfigService){
|
|
112
|
+
super(actionService), _define_property(this, "actionService", void 0), _define_property(this, "iamConfigService", void 0), this.actionService = actionService, this.iamConfigService = iamConfigService;
|
|
112
113
|
}
|
|
113
114
|
};
|
|
114
115
|
_ts_decorate([
|
|
@@ -117,15 +118,20 @@ _ts_decorate([
|
|
|
117
118
|
(0, _swagger.ApiBearerAuth)(),
|
|
118
119
|
(0, _swagger.ApiOperation)({
|
|
119
120
|
summary: 'Get actions for permission assignment',
|
|
120
|
-
description:
|
|
121
|
+
description: "Returns actions available for permission assignment, filtered by company whitelist when company feature is enabled. Optional companyId scopes the whitelist to a specific company (must match the caller's own company); defaults to the caller's session company when omitted."
|
|
121
122
|
}),
|
|
122
123
|
(0, _swagger.ApiResponse)({
|
|
123
124
|
status: 200,
|
|
124
125
|
type: _nestjsshared.SingleResponseDto
|
|
125
126
|
}),
|
|
126
|
-
|
|
127
|
+
(0, _swagger.ApiBody)({
|
|
128
|
+
type: _actiondto.ActionTreeForPermissionDto
|
|
129
|
+
}),
|
|
130
|
+
_ts_param(0, (0, _common.Body)()),
|
|
131
|
+
_ts_param(1, (0, _nestjsshared.CurrentUser)()),
|
|
127
132
|
_ts_metadata("design:type", Function),
|
|
128
133
|
_ts_metadata("design:paramtypes", [
|
|
134
|
+
typeof _actiondto.ActionTreeForPermissionDto === "undefined" ? Object : _actiondto.ActionTreeForPermissionDto,
|
|
129
135
|
typeof _nestjsshared.ILoggedUserInfo === "undefined" ? Object : _nestjsshared.ILoggedUserInfo
|
|
130
136
|
]),
|
|
131
137
|
_ts_metadata("design:returntype", Promise)
|
|
@@ -159,8 +165,10 @@ ActionController = _ts_decorate([
|
|
|
159
165
|
(0, _swagger.ApiTags)('IAM - Actions'),
|
|
160
166
|
(0, _common.Controller)('iam/actions'),
|
|
161
167
|
_ts_param(0, (0, _common.Inject)(_actionservice.ActionService)),
|
|
168
|
+
_ts_param(1, (0, _common.Inject)(_iamconfigservice.IAMConfigService)),
|
|
162
169
|
_ts_metadata("design:type", Function),
|
|
163
170
|
_ts_metadata("design:paramtypes", [
|
|
164
|
-
typeof _actionservice.ActionService === "undefined" ? Object : _actionservice.ActionService
|
|
171
|
+
typeof _actionservice.ActionService === "undefined" ? Object : _actionservice.ActionService,
|
|
172
|
+
typeof _iamconfigservice.IAMConfigService === "undefined" ? Object : _iamconfigservice.IAMConfigService
|
|
165
173
|
])
|
|
166
174
|
], ActionController);
|
|
@@ -13,7 +13,6 @@ const _common = require("@nestjs/common");
|
|
|
13
13
|
const _swagger = require("@nestjs/swagger");
|
|
14
14
|
const _config = require("../config");
|
|
15
15
|
const _permissiondto = require("../dtos/permission.dto");
|
|
16
|
-
const _helpers = require("../helpers");
|
|
17
16
|
const _iamconfigservice = require("../services/iam-config.service");
|
|
18
17
|
const _permissionservice = require("../services/permission.service");
|
|
19
18
|
function _define_property(obj, key, value) {
|
|
@@ -45,7 +44,6 @@ function _ts_param(paramIndex, decorator) {
|
|
|
45
44
|
}
|
|
46
45
|
let UserActionPermissionController = class UserActionPermissionController {
|
|
47
46
|
async assignUserActions(dto, user) {
|
|
48
|
-
(0, _helpers.validateCompanyAccess)(this.config, dto.companyId, user);
|
|
49
47
|
const result = await this.permissionService.assignUserActions(dto);
|
|
50
48
|
return {
|
|
51
49
|
success: true,
|
|
@@ -60,7 +58,6 @@ let UserActionPermissionController = class UserActionPermissionController {
|
|
|
60
58
|
};
|
|
61
59
|
}
|
|
62
60
|
async getUserActions(dto, user) {
|
|
63
|
-
(0, _helpers.validateCompanyAccess)(this.config, dto.companyId, user);
|
|
64
61
|
const actions = await this.permissionService.getUserActions(dto.userId, dto.branchId, dto.companyId);
|
|
65
62
|
return {
|
|
66
63
|
success: true,
|
|
@@ -49,6 +49,12 @@ function iamSwaggerConfig(enableCompanyFeature = false, permissionMode = _permis
|
|
|
49
49
|
'branchId'
|
|
50
50
|
]
|
|
51
51
|
},
|
|
52
|
+
{
|
|
53
|
+
schemaName: 'ActionTreeForPermissionDto',
|
|
54
|
+
properties: [
|
|
55
|
+
'companyId'
|
|
56
|
+
]
|
|
57
|
+
},
|
|
52
58
|
// Response DTOs with branchId
|
|
53
59
|
{
|
|
54
60
|
schemaName: 'UserActionResponseDto',
|
package/cjs/dtos/action.dto.js
CHANGED
|
@@ -15,6 +15,9 @@ _export(exports, {
|
|
|
15
15
|
get ActionTreeDto () {
|
|
16
16
|
return ActionTreeDto;
|
|
17
17
|
},
|
|
18
|
+
get ActionTreeForPermissionDto () {
|
|
19
|
+
return ActionTreeForPermissionDto;
|
|
20
|
+
},
|
|
18
21
|
get ActionTreeQueryDto () {
|
|
19
22
|
return ActionTreeQueryDto;
|
|
20
23
|
},
|
|
@@ -226,6 +229,20 @@ _ts_decorate([
|
|
|
226
229
|
}),
|
|
227
230
|
_ts_metadata("design:type", Array)
|
|
228
231
|
], ActionTreeDto.prototype, "children", void 0);
|
|
232
|
+
let ActionTreeForPermissionDto = class ActionTreeForPermissionDto {
|
|
233
|
+
constructor(){
|
|
234
|
+
_define_property(this, "companyId", void 0);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
_ts_decorate([
|
|
238
|
+
(0, _swagger.ApiProperty)({
|
|
239
|
+
description: 'Company to scope the action whitelist to. Defaults to the current user session company when omitted.',
|
|
240
|
+
required: false
|
|
241
|
+
}),
|
|
242
|
+
(0, _classvalidator.IsUUID)(),
|
|
243
|
+
(0, _classvalidator.IsOptional)(),
|
|
244
|
+
_ts_metadata("design:type", String)
|
|
245
|
+
], ActionTreeForPermissionDto.prototype, "companyId", void 0);
|
|
229
246
|
let ActionTreeQueryDto = class ActionTreeQueryDto {
|
|
230
247
|
constructor(){
|
|
231
248
|
_define_property(this, "search", void 0);
|
|
@@ -68,7 +68,9 @@ let IAMModule = class IAMModule {
|
|
|
68
68
|
return {
|
|
69
69
|
provide: _nestjsshared.PERMISSION_GUARD_CONFIG,
|
|
70
70
|
useValue: {
|
|
71
|
-
enableCompanyFeature
|
|
71
|
+
enableCompanyFeature,
|
|
72
|
+
userPermissionKeyFormat: `${_permissioncacheservice.MY_PERMISSIONS_CACHE_PREFIX}:user:{userId}`,
|
|
73
|
+
companyPermissionKeyFormat: `${_permissioncacheservice.MY_PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`
|
|
72
74
|
}
|
|
73
75
|
};
|
|
74
76
|
}
|
|
@@ -14,8 +14,13 @@ const _common = require("@nestjs/common");
|
|
|
14
14
|
const _typeorm = require("typeorm");
|
|
15
15
|
const _config = require("../config");
|
|
16
16
|
const _actionentity = require("../entities/action.entity");
|
|
17
|
+
const _permissionwithcompanyentity = require("../entities/permission-with-company.entity");
|
|
18
|
+
const _useriampermissionentity = require("../entities/user-iam-permission.entity");
|
|
19
|
+
const _iamentitytypeenum = require("../enums/iam-entity-type.enum");
|
|
20
|
+
const _iampermissiontypeenum = require("../enums/iam-permission-type.enum");
|
|
17
21
|
const _iamconfigservice = require("./iam-config.service");
|
|
18
22
|
const _iamdatasourceservice = require("./iam-datasource.service");
|
|
23
|
+
const _permissioncacheservice = require("./permission-cache.service");
|
|
19
24
|
const _permissionservice = require("./permission.service");
|
|
20
25
|
function _define_property(obj, key, value) {
|
|
21
26
|
if (key in obj) {
|
|
@@ -45,6 +50,81 @@ function _ts_param(paramIndex, decorator) {
|
|
|
45
50
|
};
|
|
46
51
|
}
|
|
47
52
|
let ActionService = class ActionService extends _classes.ApiService {
|
|
53
|
+
async beforeDeleteOperation(dto, user, queryRunner) {
|
|
54
|
+
const actionIds = Array.isArray(dto.id) ? dto.id : [
|
|
55
|
+
dto.id
|
|
56
|
+
];
|
|
57
|
+
if (actionIds.length === 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
61
|
+
const permissionEntity = enableCompanyFeature ? _permissionwithcompanyentity.UserIamPermissionWithCompany : _useriampermissionentity.UserIamPermission;
|
|
62
|
+
const permissionRepo = queryRunner.manager.getRepository(permissionEntity);
|
|
63
|
+
const [roleActionRows, userActionRows, companyActionRows] = await Promise.all([
|
|
64
|
+
permissionRepo.find({
|
|
65
|
+
where: {
|
|
66
|
+
permissionType: _iampermissiontypeenum.IamPermissionType.ROLE_ACTION,
|
|
67
|
+
sourceType: _iamentitytypeenum.IamEntityType.ROLE,
|
|
68
|
+
targetType: _iamentitytypeenum.IamEntityType.ACTION,
|
|
69
|
+
targetId: (0, _typeorm.In)(actionIds)
|
|
70
|
+
}
|
|
71
|
+
}),
|
|
72
|
+
permissionRepo.find({
|
|
73
|
+
where: {
|
|
74
|
+
permissionType: _iampermissiontypeenum.IamPermissionType.USER_ACTION,
|
|
75
|
+
targetType: _iamentitytypeenum.IamEntityType.ACTION,
|
|
76
|
+
targetId: (0, _typeorm.In)(actionIds)
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
enableCompanyFeature ? permissionRepo.find({
|
|
80
|
+
where: {
|
|
81
|
+
permissionType: _iampermissiontypeenum.IamPermissionType.COMPANY_ACTION,
|
|
82
|
+
sourceType: _iamentitytypeenum.IamEntityType.COMPANY,
|
|
83
|
+
targetType: _iamentitytypeenum.IamEntityType.ACTION,
|
|
84
|
+
targetId: (0, _typeorm.In)(actionIds)
|
|
85
|
+
}
|
|
86
|
+
}) : Promise.resolve([])
|
|
87
|
+
]);
|
|
88
|
+
const affectedUserIds = new Set(userActionRows.map((row)=>row.userId).filter((id)=>!!id));
|
|
89
|
+
const affectedCompanyIds = [
|
|
90
|
+
...new Set(companyActionRows.map((row)=>row.sourceId).filter((id)=>!!id))
|
|
91
|
+
];
|
|
92
|
+
const affectedRoleIds = [
|
|
93
|
+
...new Set(roleActionRows.map((row)=>row.sourceId))
|
|
94
|
+
];
|
|
95
|
+
if (affectedRoleIds.length > 0) {
|
|
96
|
+
const userRoleRows = await permissionRepo.find({
|
|
97
|
+
where: {
|
|
98
|
+
permissionType: _iampermissiontypeenum.IamPermissionType.USER_ROLE,
|
|
99
|
+
targetType: _iamentitytypeenum.IamEntityType.ROLE,
|
|
100
|
+
targetId: (0, _typeorm.In)(affectedRoleIds)
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
userRoleRows.forEach((row)=>row.userId && affectedUserIds.add(row.userId));
|
|
104
|
+
}
|
|
105
|
+
await permissionRepo.delete({
|
|
106
|
+
permissionType: _iampermissiontypeenum.IamPermissionType.ROLE_ACTION,
|
|
107
|
+
targetType: _iamentitytypeenum.IamEntityType.ACTION,
|
|
108
|
+
targetId: (0, _typeorm.In)(actionIds)
|
|
109
|
+
});
|
|
110
|
+
await permissionRepo.delete({
|
|
111
|
+
permissionType: _iampermissiontypeenum.IamPermissionType.USER_ACTION,
|
|
112
|
+
targetType: _iamentitytypeenum.IamEntityType.ACTION,
|
|
113
|
+
targetId: (0, _typeorm.In)(actionIds)
|
|
114
|
+
});
|
|
115
|
+
await permissionRepo.delete({
|
|
116
|
+
permissionType: _iampermissiontypeenum.IamPermissionType.COMPANY_ACTION,
|
|
117
|
+
sourceType: _iamentitytypeenum.IamEntityType.COMPANY,
|
|
118
|
+
targetType: _iamentitytypeenum.IamEntityType.ACTION,
|
|
119
|
+
targetId: (0, _typeorm.In)(actionIds)
|
|
120
|
+
});
|
|
121
|
+
await Promise.all([
|
|
122
|
+
affectedUserIds.size > 0 ? this.permissionCacheService.invalidateUsers([
|
|
123
|
+
...affectedUserIds
|
|
124
|
+
]) : Promise.resolve(),
|
|
125
|
+
...affectedCompanyIds.map((companyId)=>this.permissionService.invalidateCompanyMembersCache(companyId))
|
|
126
|
+
]);
|
|
127
|
+
}
|
|
48
128
|
// Query Customization
|
|
49
129
|
async getSelectQuery(query, _user, select) {
|
|
50
130
|
if (!select?.length) {
|
|
@@ -108,12 +188,13 @@ let ActionService = class ActionService extends _classes.ApiService {
|
|
|
108
188
|
});
|
|
109
189
|
}
|
|
110
190
|
}
|
|
111
|
-
/** Get actions available for permission assignment (filtered by company whitelist) */ async getActionsForPermission(user) {
|
|
191
|
+
/** Get actions available for permission assignment (filtered by company whitelist) */ async getActionsForPermission(user, companyId) {
|
|
112
192
|
await this.ensureDataSourceRepository();
|
|
113
193
|
this.requireUser(user, 'getActionsForPermission');
|
|
114
194
|
let whereClause = {};
|
|
115
|
-
|
|
116
|
-
|
|
195
|
+
const targetCompanyId = companyId || user.companyId;
|
|
196
|
+
if (this.iamConfigService.isCompanyFeatureEnabled() && targetCompanyId) {
|
|
197
|
+
const companyActionIds = await this.permissionService.getCompanyActionIds(targetCompanyId);
|
|
117
198
|
if (companyActionIds.length === 0) {
|
|
118
199
|
return [];
|
|
119
200
|
}
|
|
@@ -176,9 +257,9 @@ let ActionService = class ActionService extends _classes.ApiService {
|
|
|
176
257
|
}
|
|
177
258
|
return rootNodes;
|
|
178
259
|
}
|
|
179
|
-
constructor(cacheManager, utilsService, iamConfigService, dataSourceProvider, permissionService){
|
|
180
|
-
super('action', cacheManager, utilsService, ActionService.name, true, 'iam', _actionentity.Action, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "iamConfigService", void 0), _define_property(this, "permissionService", void 0), // Custom Methods
|
|
181
|
-
_define_property(this, "actionSelectFields", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.iamConfigService = iamConfigService, this.permissionService = permissionService, this.actionSelectFields = [
|
|
260
|
+
constructor(cacheManager, utilsService, iamConfigService, dataSourceProvider, permissionService, permissionCacheService){
|
|
261
|
+
super('action', cacheManager, utilsService, ActionService.name, true, 'iam', _actionentity.Action, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "iamConfigService", void 0), _define_property(this, "permissionService", void 0), _define_property(this, "permissionCacheService", void 0), // Custom Methods
|
|
262
|
+
_define_property(this, "actionSelectFields", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.iamConfigService = iamConfigService, this.permissionService = permissionService, this.permissionCacheService = permissionCacheService, this.actionSelectFields = [
|
|
182
263
|
'id',
|
|
183
264
|
'code',
|
|
184
265
|
'name',
|
|
@@ -200,12 +281,14 @@ ActionService = _ts_decorate([
|
|
|
200
281
|
_ts_param(2, (0, _common.Inject)(_iamconfigservice.IAMConfigService)),
|
|
201
282
|
_ts_param(3, (0, _common.Inject)(_iamdatasourceservice.IAMDataSourceService)),
|
|
202
283
|
_ts_param(4, (0, _common.Inject)(_permissionservice.PermissionService)),
|
|
284
|
+
_ts_param(5, (0, _common.Inject)(_permissioncacheservice.PermissionCacheService)),
|
|
203
285
|
_ts_metadata("design:type", Function),
|
|
204
286
|
_ts_metadata("design:paramtypes", [
|
|
205
287
|
typeof _classes.HybridCache === "undefined" ? Object : _classes.HybridCache,
|
|
206
288
|
typeof _modules.UtilsService === "undefined" ? Object : _modules.UtilsService,
|
|
207
289
|
typeof _iamconfigservice.IAMConfigService === "undefined" ? Object : _iamconfigservice.IAMConfigService,
|
|
208
290
|
typeof _iamdatasourceservice.IAMDataSourceService === "undefined" ? Object : _iamdatasourceservice.IAMDataSourceService,
|
|
209
|
-
typeof _permissionservice.PermissionService === "undefined" ? Object : _permissionservice.PermissionService
|
|
291
|
+
typeof _permissionservice.PermissionService === "undefined" ? Object : _permissionservice.PermissionService,
|
|
292
|
+
typeof _permissioncacheservice.PermissionCacheService === "undefined" ? Object : _permissioncacheservice.PermissionCacheService
|
|
210
293
|
])
|
|
211
294
|
], ActionService);
|
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get MY_PERMISSIONS_CACHE_PREFIX () {
|
|
13
|
+
return MY_PERMISSIONS_CACHE_PREFIX;
|
|
14
|
+
},
|
|
15
|
+
get PermissionCacheKeyOptions () {
|
|
16
|
+
return _nestjsshared.PermissionCacheKeyOptions;
|
|
17
|
+
},
|
|
18
|
+
get PermissionCacheService () {
|
|
8
19
|
return PermissionCacheService;
|
|
9
20
|
}
|
|
10
21
|
});
|
|
22
|
+
const _config = require("@flusys/nestjs-core/config");
|
|
11
23
|
const _nestjsshared = require("@flusys/nestjs-shared");
|
|
12
24
|
const _common = require("@nestjs/common");
|
|
13
25
|
function _define_property(obj, key, value) {
|
|
@@ -37,84 +49,118 @@ function _ts_param(paramIndex, decorator) {
|
|
|
37
49
|
decorator(target, key, paramIndex);
|
|
38
50
|
};
|
|
39
51
|
}
|
|
52
|
+
const MY_PERMISSIONS_CACHE_PREFIX = _nestjsshared.PERMISSIONS_CACHE_PREFIX;
|
|
53
|
+
const NO_COMPANY_SCOPE = 'none';
|
|
40
54
|
let PermissionCacheService = class PermissionCacheService {
|
|
41
55
|
// Cache Key Generation
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
parseDurationMs(duration) {
|
|
57
|
+
const unitMs = {
|
|
58
|
+
ms: 1,
|
|
59
|
+
s: 1000,
|
|
60
|
+
m: 60000,
|
|
61
|
+
h: 3600000,
|
|
62
|
+
d: 86400000,
|
|
63
|
+
w: 604800000
|
|
64
|
+
};
|
|
65
|
+
const match = /^(\d+)\s*(ms|s|m|h|d|w)?$/i.exec(duration.trim());
|
|
66
|
+
if (!match) {
|
|
67
|
+
return unitMs.h; // fallback: 1 hour
|
|
68
|
+
}
|
|
69
|
+
const unit = (match[2] || 'ms').toLowerCase();
|
|
70
|
+
return Number(match[1]) * unitMs[unit];
|
|
44
71
|
}
|
|
45
|
-
|
|
46
|
-
return
|
|
72
|
+
buildCacheKey(options) {
|
|
73
|
+
return (0, _nestjsshared.buildPermissionCacheKey)(options, this.guardConfig);
|
|
47
74
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (enableCompanyFeature && companyId) {
|
|
51
|
-
return `${prefix}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
|
|
52
|
-
}
|
|
53
|
-
return `${prefix}:user:${userId}`;
|
|
75
|
+
scopeToken(companyId) {
|
|
76
|
+
return companyId || NO_COMPANY_SCOPE;
|
|
54
77
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const key = this.generateCacheKey(options);
|
|
58
|
-
await this.cacheManager.set(key, permissions, this.TTL);
|
|
78
|
+
buildTrackedKeysKey(userId, companyId) {
|
|
79
|
+
return `${this.TRACKED_KEYS_PREFIX}:user:${userId}:company:${this.scopeToken(companyId)}`;
|
|
59
80
|
}
|
|
60
|
-
|
|
81
|
+
buildTrackedScopesKey(userId) {
|
|
82
|
+
return `${this.TRACKED_SCOPES_PREFIX}:user:${userId}`;
|
|
83
|
+
}
|
|
84
|
+
// Cache Operations
|
|
61
85
|
async setMyPermissions(options, data) {
|
|
62
|
-
const key = this.
|
|
86
|
+
const key = this.buildCacheKey(options);
|
|
63
87
|
await this.cacheManager.set(key, data, this.TTL);
|
|
88
|
+
await this.trackKey(options.userId, key, options.companyId);
|
|
64
89
|
}
|
|
65
90
|
async getMyPermissions(options) {
|
|
66
91
|
try {
|
|
67
|
-
const key = this.
|
|
92
|
+
const key = this.buildCacheKey(options);
|
|
68
93
|
const result = await this.cacheManager.get(key);
|
|
69
94
|
return result || null;
|
|
70
95
|
} catch {
|
|
71
96
|
return null;
|
|
72
97
|
}
|
|
73
98
|
}
|
|
74
|
-
|
|
75
|
-
/** Generate tenant-aware cache key for action codes */ generateActionCodeCacheKey(tenantId) {
|
|
76
|
-
if (tenantId) {
|
|
77
|
-
return `${this.ACTION_CODE_PREFIX}:tenant:${tenantId}:map`;
|
|
78
|
-
}
|
|
79
|
-
return `${this.ACTION_CODE_PREFIX}:map`;
|
|
80
|
-
}
|
|
81
|
-
async setActionCodeMap(codeToIdMap, tenantId) {
|
|
82
|
-
const key = this.generateActionCodeCacheKey(tenantId);
|
|
83
|
-
await this.cacheManager.set(key, codeToIdMap, this.ACTION_CODE_TTL);
|
|
84
|
-
}
|
|
85
|
-
async getActionIdsByCodes(codes, tenantId) {
|
|
99
|
+
async trackKey(userId, key, companyId) {
|
|
86
100
|
try {
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
if (!
|
|
90
|
-
|
|
101
|
+
const trackedKeysKey = this.buildTrackedKeysKey(userId, companyId);
|
|
102
|
+
const keys = await this.cacheManager.get(trackedKeysKey) || [];
|
|
103
|
+
if (!keys.includes(key)) {
|
|
104
|
+
keys.push(key);
|
|
105
|
+
await this.cacheManager.set(trackedKeysKey, keys, this.TTL);
|
|
91
106
|
}
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
const scopesKey = this.buildTrackedScopesKey(userId);
|
|
108
|
+
const scopes = await this.cacheManager.get(scopesKey) || [];
|
|
109
|
+
const scope = this.scopeToken(companyId);
|
|
110
|
+
if (!scopes.includes(scope)) {
|
|
111
|
+
scopes.push(scope);
|
|
112
|
+
await this.cacheManager.set(scopesKey, scopes, this.TTL);
|
|
97
113
|
}
|
|
98
|
-
return Object.keys(result).length > 0 ? result : null;
|
|
99
114
|
} catch {
|
|
100
|
-
|
|
115
|
+
// tracking is best-effort; a missed entry only widens invalidation, never narrows it
|
|
101
116
|
}
|
|
102
117
|
}
|
|
103
118
|
// Cache Invalidation
|
|
104
119
|
async invalidateUser(userId, companyId, branchIds) {
|
|
105
|
-
const keysToDelete = [
|
|
106
|
-
`${this.CACHE_PREFIX}:user:${userId}`,
|
|
107
|
-
`${this.MY_PERMISSIONS_PREFIX}:user:${userId}`
|
|
108
|
-
];
|
|
109
120
|
if (companyId) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
await this.invalidateScope(userId, companyId, branchIds);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// No company scope given: clear every company/branch scope tracked for this user
|
|
125
|
+
const scopesKey = this.buildTrackedScopesKey(userId);
|
|
126
|
+
let scopes = [];
|
|
127
|
+
try {
|
|
128
|
+
scopes = await this.cacheManager.get(scopesKey) || [];
|
|
129
|
+
} catch {
|
|
130
|
+
scopes = [];
|
|
131
|
+
}
|
|
132
|
+
await Promise.all(scopes.map((scope)=>this.invalidateScope(userId, scope === NO_COMPANY_SCOPE ? null : scope, branchIds)));
|
|
133
|
+
await this.cacheManager.del(scopesKey);
|
|
134
|
+
}
|
|
135
|
+
async invalidateScope(userId, companyId, branchIds) {
|
|
136
|
+
const keysToDelete = new Set();
|
|
137
|
+
const trackedKeysKey = this.buildTrackedKeysKey(userId, companyId);
|
|
138
|
+
let trackedKeys = [];
|
|
139
|
+
try {
|
|
140
|
+
trackedKeys = await this.cacheManager.get(trackedKeysKey) || [];
|
|
141
|
+
} catch {
|
|
142
|
+
trackedKeys = [];
|
|
143
|
+
}
|
|
144
|
+
if (!branchIds?.length) {
|
|
145
|
+
trackedKeys.forEach((key)=>keysToDelete.add(key));
|
|
146
|
+
keysToDelete.add(trackedKeysKey);
|
|
147
|
+
} else {
|
|
148
|
+
// Branch scope given: only invalidate those branches, keep the rest tracked
|
|
149
|
+
branchIds.forEach((branchId)=>keysToDelete.add(this.buildCacheKey({
|
|
150
|
+
userId,
|
|
151
|
+
companyId,
|
|
152
|
+
branchId
|
|
153
|
+
})));
|
|
154
|
+
const remainingKeys = trackedKeys.filter((key)=>!keysToDelete.has(key));
|
|
155
|
+
if (remainingKeys.length) {
|
|
156
|
+
await this.cacheManager.set(trackedKeysKey, remainingKeys, this.TTL);
|
|
157
|
+
} else {
|
|
158
|
+
keysToDelete.add(trackedKeysKey);
|
|
115
159
|
}
|
|
116
160
|
}
|
|
117
|
-
await Promise.all(
|
|
161
|
+
await Promise.all([
|
|
162
|
+
...keysToDelete
|
|
163
|
+
].map((key)=>this.cacheManager.del(key)));
|
|
118
164
|
}
|
|
119
165
|
async invalidateUsers(userIds, companyId, branchIds) {
|
|
120
166
|
if (userIds.length === 0) {
|
|
@@ -123,39 +169,19 @@ let PermissionCacheService = class PermissionCacheService {
|
|
|
123
169
|
const results = await Promise.allSettled(userIds.map((userId)=>this.invalidateUser(userId, companyId, branchIds)));
|
|
124
170
|
return results.filter((r)=>r.status === 'fulfilled').length;
|
|
125
171
|
}
|
|
126
|
-
|
|
127
|
-
if (userIds.length === 0) {
|
|
128
|
-
return 0;
|
|
129
|
-
}
|
|
130
|
-
return await this.invalidateUsers(userIds, companyId, branchIds);
|
|
131
|
-
}
|
|
132
|
-
constructor(cacheManager){
|
|
172
|
+
constructor(cacheManager, guardConfig){
|
|
133
173
|
_define_property(this, "cacheManager", void 0);
|
|
134
|
-
_define_property(this, "
|
|
135
|
-
_define_property(this, "
|
|
136
|
-
_define_property(this, "
|
|
137
|
-
_define_property(this, "
|
|
138
|
-
_define_property(this, "ACTION_CODE_PREFIX", void 0);
|
|
174
|
+
_define_property(this, "guardConfig", void 0);
|
|
175
|
+
_define_property(this, "TRACKED_KEYS_PREFIX", void 0);
|
|
176
|
+
_define_property(this, "TRACKED_SCOPES_PREFIX", void 0);
|
|
177
|
+
_define_property(this, "TTL", void 0);
|
|
139
178
|
this.cacheManager = cacheManager;
|
|
140
|
-
this.
|
|
141
|
-
this.
|
|
142
|
-
this.
|
|
143
|
-
this.
|
|
144
|
-
this.ACTION_CODE_PREFIX = 'action-codes';
|
|
179
|
+
this.guardConfig = guardConfig;
|
|
180
|
+
this.TRACKED_KEYS_PREFIX = 'permission-tracked-keys';
|
|
181
|
+
this.TRACKED_SCOPES_PREFIX = 'permission-tracked-scopes';
|
|
182
|
+
this.TTL = this.parseDurationMs(_config.envConfig.getJwtConfig().refreshExpiration);
|
|
145
183
|
}
|
|
146
184
|
};
|
|
147
|
-
_ts_decorate([
|
|
148
|
-
(0, _nestjsshared.LogAction)({
|
|
149
|
-
action: 'permissionCache.setPermissions',
|
|
150
|
-
module: 'iam'
|
|
151
|
-
}),
|
|
152
|
-
_ts_metadata("design:type", Function),
|
|
153
|
-
_ts_metadata("design:paramtypes", [
|
|
154
|
-
typeof PermissionCacheKeyOptions === "undefined" ? Object : PermissionCacheKeyOptions,
|
|
155
|
-
Array
|
|
156
|
-
]),
|
|
157
|
-
_ts_metadata("design:returntype", Promise)
|
|
158
|
-
], PermissionCacheService.prototype, "setPermissions", null);
|
|
159
185
|
_ts_decorate([
|
|
160
186
|
(0, _nestjsshared.LogAction)({
|
|
161
187
|
action: 'permissionCache.setMyPermissions',
|
|
@@ -163,23 +189,11 @@ _ts_decorate([
|
|
|
163
189
|
}),
|
|
164
190
|
_ts_metadata("design:type", Function),
|
|
165
191
|
_ts_metadata("design:paramtypes", [
|
|
166
|
-
typeof PermissionCacheKeyOptions === "undefined" ? Object : PermissionCacheKeyOptions,
|
|
192
|
+
typeof _nestjsshared.PermissionCacheKeyOptions === "undefined" ? Object : _nestjsshared.PermissionCacheKeyOptions,
|
|
167
193
|
typeof CachedMyPermissions === "undefined" ? Object : CachedMyPermissions
|
|
168
194
|
]),
|
|
169
195
|
_ts_metadata("design:returntype", Promise)
|
|
170
196
|
], PermissionCacheService.prototype, "setMyPermissions", null);
|
|
171
|
-
_ts_decorate([
|
|
172
|
-
(0, _nestjsshared.LogAction)({
|
|
173
|
-
action: 'permissionCache.setActionCodeMap',
|
|
174
|
-
module: 'iam'
|
|
175
|
-
}),
|
|
176
|
-
_ts_metadata("design:type", Function),
|
|
177
|
-
_ts_metadata("design:paramtypes", [
|
|
178
|
-
typeof Record === "undefined" ? Object : Record,
|
|
179
|
-
String
|
|
180
|
-
]),
|
|
181
|
-
_ts_metadata("design:returntype", Promise)
|
|
182
|
-
], PermissionCacheService.prototype, "setActionCodeMap", null);
|
|
183
197
|
_ts_decorate([
|
|
184
198
|
(0, _nestjsshared.LogAction)({
|
|
185
199
|
action: 'permissionCache.invalidateUser',
|
|
@@ -206,25 +220,14 @@ _ts_decorate([
|
|
|
206
220
|
]),
|
|
207
221
|
_ts_metadata("design:returntype", Promise)
|
|
208
222
|
], PermissionCacheService.prototype, "invalidateUsers", null);
|
|
209
|
-
_ts_decorate([
|
|
210
|
-
(0, _nestjsshared.LogAction)({
|
|
211
|
-
action: 'permissionCache.invalidateRole',
|
|
212
|
-
module: 'iam'
|
|
213
|
-
}),
|
|
214
|
-
_ts_metadata("design:type", Function),
|
|
215
|
-
_ts_metadata("design:paramtypes", [
|
|
216
|
-
String,
|
|
217
|
-
Array,
|
|
218
|
-
Object,
|
|
219
|
-
Array
|
|
220
|
-
]),
|
|
221
|
-
_ts_metadata("design:returntype", Promise)
|
|
222
|
-
], PermissionCacheService.prototype, "invalidateRole", null);
|
|
223
223
|
PermissionCacheService = _ts_decorate([
|
|
224
224
|
(0, _common.Injectable)(),
|
|
225
225
|
_ts_param(0, (0, _common.Inject)('CACHE_INSTANCE')),
|
|
226
|
+
_ts_param(1, (0, _common.Optional)()),
|
|
227
|
+
_ts_param(1, (0, _common.Inject)(_nestjsshared.PERMISSION_GUARD_CONFIG)),
|
|
226
228
|
_ts_metadata("design:type", Function),
|
|
227
229
|
_ts_metadata("design:paramtypes", [
|
|
228
|
-
typeof _nestjsshared.HybridCache === "undefined" ? Object : _nestjsshared.HybridCache
|
|
230
|
+
typeof _nestjsshared.HybridCache === "undefined" ? Object : _nestjsshared.HybridCache,
|
|
231
|
+
typeof _nestjsshared.PermissionGuardConfig === "undefined" ? Object : _nestjsshared.PermissionGuardConfig
|
|
229
232
|
])
|
|
230
233
|
], PermissionCacheService);
|