@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
|
@@ -30,12 +30,10 @@ import { Body, Controller, Inject, Post, UseGuards } from '@nestjs/common';
|
|
|
30
30
|
import { ApiBearerAuth, ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
|
|
31
31
|
import { PERMISSION_OPERATION_MESSAGES, USER_ACTION_PERMISSION_MESSAGES } from '../config';
|
|
32
32
|
import { AssignUserActionsDto, GetUserActionsDto, PermissionOperationResultDto, UserActionResponseDto } from '../dtos/permission.dto';
|
|
33
|
-
import { validateCompanyAccess } from '../helpers';
|
|
34
33
|
import { IAMConfigService } from '../services/iam-config.service';
|
|
35
34
|
import { PermissionService } from '../services/permission.service';
|
|
36
35
|
export class UserActionPermissionController {
|
|
37
36
|
async assignUserActions(dto, user) {
|
|
38
|
-
validateCompanyAccess(this.config, dto.companyId, user);
|
|
39
37
|
const result = await this.permissionService.assignUserActions(dto);
|
|
40
38
|
return {
|
|
41
39
|
success: true,
|
|
@@ -50,7 +48,6 @@ export class UserActionPermissionController {
|
|
|
50
48
|
};
|
|
51
49
|
}
|
|
52
50
|
async getUserActions(dto, user) {
|
|
53
|
-
validateCompanyAccess(this.config, dto.companyId, user);
|
|
54
51
|
const actions = await this.permissionService.getUserActions(dto.userId, dto.branchId, dto.companyId);
|
|
55
52
|
return {
|
|
56
53
|
success: true,
|
|
@@ -39,6 +39,12 @@ export function iamSwaggerConfig(enableCompanyFeature = false, permissionMode =
|
|
|
39
39
|
'branchId'
|
|
40
40
|
]
|
|
41
41
|
},
|
|
42
|
+
{
|
|
43
|
+
schemaName: 'ActionTreeForPermissionDto',
|
|
44
|
+
properties: [
|
|
45
|
+
'companyId'
|
|
46
|
+
]
|
|
47
|
+
},
|
|
42
48
|
// Response DTOs with branchId
|
|
43
49
|
{
|
|
44
50
|
schemaName: 'UserActionResponseDto',
|
package/fesm/dtos/action.dto.js
CHANGED
|
@@ -199,6 +199,20 @@ _ts_decorate([
|
|
|
199
199
|
}),
|
|
200
200
|
_ts_metadata("design:type", Array)
|
|
201
201
|
], ActionTreeDto.prototype, "children", void 0);
|
|
202
|
+
export class ActionTreeForPermissionDto {
|
|
203
|
+
constructor(){
|
|
204
|
+
_define_property(this, "companyId", void 0);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
_ts_decorate([
|
|
208
|
+
ApiProperty({
|
|
209
|
+
description: 'Company to scope the action whitelist to. Defaults to the current user session company when omitted.',
|
|
210
|
+
required: false
|
|
211
|
+
}),
|
|
212
|
+
IsUUID(),
|
|
213
|
+
IsOptional(),
|
|
214
|
+
_ts_metadata("design:type", String)
|
|
215
|
+
], ActionTreeForPermissionDto.prototype, "companyId", void 0);
|
|
202
216
|
export class ActionTreeQueryDto {
|
|
203
217
|
constructor(){
|
|
204
218
|
_define_property(this, "search", void 0);
|
|
@@ -14,7 +14,7 @@ import { PermissionModeHelper } from '../helpers';
|
|
|
14
14
|
import { ActionService, PermissionService, RoleService } from '../services';
|
|
15
15
|
import { IAMConfigService } from '../services/iam-config.service';
|
|
16
16
|
import { IAMDataSourceService } from '../services/iam-datasource.service';
|
|
17
|
-
import { PermissionCacheService } from '../services/permission-cache.service';
|
|
17
|
+
import { MY_PERMISSIONS_CACHE_PREFIX, PermissionCacheService } from '../services/permission-cache.service';
|
|
18
18
|
export class IAMModule {
|
|
19
19
|
static getControllers(permissionMode, enableCompanyFeature) {
|
|
20
20
|
const baseControllers = [
|
|
@@ -58,7 +58,9 @@ export class IAMModule {
|
|
|
58
58
|
return {
|
|
59
59
|
provide: PERMISSION_GUARD_CONFIG,
|
|
60
60
|
useValue: {
|
|
61
|
-
enableCompanyFeature
|
|
61
|
+
enableCompanyFeature,
|
|
62
|
+
userPermissionKeyFormat: `${MY_PERMISSIONS_CACHE_PREFIX}:user:{userId}`,
|
|
63
|
+
companyPermissionKeyFormat: `${MY_PERMISSIONS_CACHE_PREFIX}:company:{companyId}:branch:{branchId}:user:{userId}`
|
|
62
64
|
}
|
|
63
65
|
};
|
|
64
66
|
}
|
|
@@ -31,10 +31,90 @@ import { BadRequestException, Inject, Injectable, Scope } from '@nestjs/common';
|
|
|
31
31
|
import { In } from 'typeorm';
|
|
32
32
|
import { PERMISSION_OPERATION_MESSAGES } from '../config';
|
|
33
33
|
import { Action } from '../entities/action.entity';
|
|
34
|
+
import { UserIamPermissionWithCompany } from '../entities/permission-with-company.entity';
|
|
35
|
+
import { UserIamPermission } from '../entities/user-iam-permission.entity';
|
|
36
|
+
import { IamEntityType } from '../enums/iam-entity-type.enum';
|
|
37
|
+
import { IamPermissionType } from '../enums/iam-permission-type.enum';
|
|
34
38
|
import { IAMConfigService } from './iam-config.service';
|
|
35
39
|
import { IAMDataSourceService } from './iam-datasource.service';
|
|
40
|
+
import { PermissionCacheService } from './permission-cache.service';
|
|
36
41
|
import { PermissionService } from './permission.service';
|
|
37
42
|
export class ActionService extends ApiService {
|
|
43
|
+
async beforeDeleteOperation(dto, user, queryRunner) {
|
|
44
|
+
const actionIds = Array.isArray(dto.id) ? dto.id : [
|
|
45
|
+
dto.id
|
|
46
|
+
];
|
|
47
|
+
if (actionIds.length === 0) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
51
|
+
const permissionEntity = enableCompanyFeature ? UserIamPermissionWithCompany : UserIamPermission;
|
|
52
|
+
const permissionRepo = queryRunner.manager.getRepository(permissionEntity);
|
|
53
|
+
const [roleActionRows, userActionRows, companyActionRows] = await Promise.all([
|
|
54
|
+
permissionRepo.find({
|
|
55
|
+
where: {
|
|
56
|
+
permissionType: IamPermissionType.ROLE_ACTION,
|
|
57
|
+
sourceType: IamEntityType.ROLE,
|
|
58
|
+
targetType: IamEntityType.ACTION,
|
|
59
|
+
targetId: In(actionIds)
|
|
60
|
+
}
|
|
61
|
+
}),
|
|
62
|
+
permissionRepo.find({
|
|
63
|
+
where: {
|
|
64
|
+
permissionType: IamPermissionType.USER_ACTION,
|
|
65
|
+
targetType: IamEntityType.ACTION,
|
|
66
|
+
targetId: In(actionIds)
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
enableCompanyFeature ? permissionRepo.find({
|
|
70
|
+
where: {
|
|
71
|
+
permissionType: IamPermissionType.COMPANY_ACTION,
|
|
72
|
+
sourceType: IamEntityType.COMPANY,
|
|
73
|
+
targetType: IamEntityType.ACTION,
|
|
74
|
+
targetId: In(actionIds)
|
|
75
|
+
}
|
|
76
|
+
}) : Promise.resolve([])
|
|
77
|
+
]);
|
|
78
|
+
const affectedUserIds = new Set(userActionRows.map((row)=>row.userId).filter((id)=>!!id));
|
|
79
|
+
const affectedCompanyIds = [
|
|
80
|
+
...new Set(companyActionRows.map((row)=>row.sourceId).filter((id)=>!!id))
|
|
81
|
+
];
|
|
82
|
+
const affectedRoleIds = [
|
|
83
|
+
...new Set(roleActionRows.map((row)=>row.sourceId))
|
|
84
|
+
];
|
|
85
|
+
if (affectedRoleIds.length > 0) {
|
|
86
|
+
const userRoleRows = await permissionRepo.find({
|
|
87
|
+
where: {
|
|
88
|
+
permissionType: IamPermissionType.USER_ROLE,
|
|
89
|
+
targetType: IamEntityType.ROLE,
|
|
90
|
+
targetId: In(affectedRoleIds)
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
userRoleRows.forEach((row)=>row.userId && affectedUserIds.add(row.userId));
|
|
94
|
+
}
|
|
95
|
+
await permissionRepo.delete({
|
|
96
|
+
permissionType: IamPermissionType.ROLE_ACTION,
|
|
97
|
+
targetType: IamEntityType.ACTION,
|
|
98
|
+
targetId: In(actionIds)
|
|
99
|
+
});
|
|
100
|
+
await permissionRepo.delete({
|
|
101
|
+
permissionType: IamPermissionType.USER_ACTION,
|
|
102
|
+
targetType: IamEntityType.ACTION,
|
|
103
|
+
targetId: In(actionIds)
|
|
104
|
+
});
|
|
105
|
+
await permissionRepo.delete({
|
|
106
|
+
permissionType: IamPermissionType.COMPANY_ACTION,
|
|
107
|
+
sourceType: IamEntityType.COMPANY,
|
|
108
|
+
targetType: IamEntityType.ACTION,
|
|
109
|
+
targetId: In(actionIds)
|
|
110
|
+
});
|
|
111
|
+
await Promise.all([
|
|
112
|
+
affectedUserIds.size > 0 ? this.permissionCacheService.invalidateUsers([
|
|
113
|
+
...affectedUserIds
|
|
114
|
+
]) : Promise.resolve(),
|
|
115
|
+
...affectedCompanyIds.map((companyId)=>this.permissionService.invalidateCompanyMembersCache(companyId))
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
38
118
|
// Query Customization
|
|
39
119
|
async getSelectQuery(query, _user, select) {
|
|
40
120
|
if (!select?.length) {
|
|
@@ -98,12 +178,13 @@ export class ActionService extends ApiService {
|
|
|
98
178
|
});
|
|
99
179
|
}
|
|
100
180
|
}
|
|
101
|
-
/** Get actions available for permission assignment (filtered by company whitelist) */ async getActionsForPermission(user) {
|
|
181
|
+
/** Get actions available for permission assignment (filtered by company whitelist) */ async getActionsForPermission(user, companyId) {
|
|
102
182
|
await this.ensureDataSourceRepository();
|
|
103
183
|
this.requireUser(user, 'getActionsForPermission');
|
|
104
184
|
let whereClause = {};
|
|
105
|
-
|
|
106
|
-
|
|
185
|
+
const targetCompanyId = companyId || user.companyId;
|
|
186
|
+
if (this.iamConfigService.isCompanyFeatureEnabled() && targetCompanyId) {
|
|
187
|
+
const companyActionIds = await this.permissionService.getCompanyActionIds(targetCompanyId);
|
|
107
188
|
if (companyActionIds.length === 0) {
|
|
108
189
|
return [];
|
|
109
190
|
}
|
|
@@ -166,9 +247,9 @@ export class ActionService extends ApiService {
|
|
|
166
247
|
}
|
|
167
248
|
return rootNodes;
|
|
168
249
|
}
|
|
169
|
-
constructor(cacheManager, utilsService, iamConfigService, dataSourceProvider, permissionService){
|
|
170
|
-
super('action', cacheManager, utilsService, ActionService.name, true, 'iam', 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
|
|
171
|
-
_define_property(this, "actionSelectFields", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.iamConfigService = iamConfigService, this.permissionService = permissionService, this.actionSelectFields = [
|
|
250
|
+
constructor(cacheManager, utilsService, iamConfigService, dataSourceProvider, permissionService, permissionCacheService){
|
|
251
|
+
super('action', cacheManager, utilsService, ActionService.name, true, 'iam', 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
|
|
252
|
+
_define_property(this, "actionSelectFields", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.iamConfigService = iamConfigService, this.permissionService = permissionService, this.permissionCacheService = permissionCacheService, this.actionSelectFields = [
|
|
172
253
|
'id',
|
|
173
254
|
'code',
|
|
174
255
|
'name',
|
|
@@ -190,12 +271,14 @@ ActionService = _ts_decorate([
|
|
|
190
271
|
_ts_param(2, Inject(IAMConfigService)),
|
|
191
272
|
_ts_param(3, Inject(IAMDataSourceService)),
|
|
192
273
|
_ts_param(4, Inject(PermissionService)),
|
|
274
|
+
_ts_param(5, Inject(PermissionCacheService)),
|
|
193
275
|
_ts_metadata("design:type", Function),
|
|
194
276
|
_ts_metadata("design:paramtypes", [
|
|
195
277
|
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
196
278
|
typeof UtilsService === "undefined" ? Object : UtilsService,
|
|
197
279
|
typeof IAMConfigService === "undefined" ? Object : IAMConfigService,
|
|
198
280
|
typeof IAMDataSourceService === "undefined" ? Object : IAMDataSourceService,
|
|
199
|
-
typeof PermissionService === "undefined" ? Object : PermissionService
|
|
281
|
+
typeof PermissionService === "undefined" ? Object : PermissionService,
|
|
282
|
+
typeof PermissionCacheService === "undefined" ? Object : PermissionCacheService
|
|
200
283
|
])
|
|
201
284
|
], ActionService);
|
|
@@ -25,86 +25,122 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
28
|
+
import { envConfig } from '@flusys/nestjs-core/config';
|
|
29
|
+
import { buildPermissionCacheKey, HybridCache, LogAction, PERMISSION_GUARD_CONFIG, PERMISSIONS_CACHE_PREFIX, PermissionCacheKeyOptions, PermissionGuardConfig } from '@flusys/nestjs-shared';
|
|
30
|
+
import { Inject, Injectable, Optional } from '@nestjs/common';
|
|
31
|
+
export { PermissionCacheKeyOptions } from '@flusys/nestjs-shared';
|
|
32
|
+
export const MY_PERMISSIONS_CACHE_PREFIX = PERMISSIONS_CACHE_PREFIX;
|
|
33
|
+
const NO_COMPANY_SCOPE = 'none';
|
|
30
34
|
export class PermissionCacheService {
|
|
31
35
|
// Cache Key Generation
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
parseDurationMs(duration) {
|
|
37
|
+
const unitMs = {
|
|
38
|
+
ms: 1,
|
|
39
|
+
s: 1000,
|
|
40
|
+
m: 60000,
|
|
41
|
+
h: 3600000,
|
|
42
|
+
d: 86400000,
|
|
43
|
+
w: 604800000
|
|
44
|
+
};
|
|
45
|
+
const match = /^(\d+)\s*(ms|s|m|h|d|w)?$/i.exec(duration.trim());
|
|
46
|
+
if (!match) {
|
|
47
|
+
return unitMs.h; // fallback: 1 hour
|
|
48
|
+
}
|
|
49
|
+
const unit = (match[2] || 'ms').toLowerCase();
|
|
50
|
+
return Number(match[1]) * unitMs[unit];
|
|
34
51
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
52
|
+
buildCacheKey(options) {
|
|
53
|
+
return buildPermissionCacheKey(options, this.guardConfig);
|
|
37
54
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (enableCompanyFeature && companyId) {
|
|
41
|
-
return `${prefix}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
|
|
42
|
-
}
|
|
43
|
-
return `${prefix}:user:${userId}`;
|
|
55
|
+
scopeToken(companyId) {
|
|
56
|
+
return companyId || NO_COMPANY_SCOPE;
|
|
44
57
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
buildTrackedKeysKey(userId, companyId) {
|
|
59
|
+
return `${this.TRACKED_KEYS_PREFIX}:user:${userId}:company:${this.scopeToken(companyId)}`;
|
|
60
|
+
}
|
|
61
|
+
buildTrackedScopesKey(userId) {
|
|
62
|
+
return `${this.TRACKED_SCOPES_PREFIX}:user:${userId}`;
|
|
49
63
|
}
|
|
50
|
-
//
|
|
64
|
+
// Cache Operations
|
|
51
65
|
async setMyPermissions(options, data) {
|
|
52
|
-
const key = this.
|
|
66
|
+
const key = this.buildCacheKey(options);
|
|
53
67
|
await this.cacheManager.set(key, data, this.TTL);
|
|
68
|
+
await this.trackKey(options.userId, key, options.companyId);
|
|
54
69
|
}
|
|
55
70
|
async getMyPermissions(options) {
|
|
56
71
|
try {
|
|
57
|
-
const key = this.
|
|
72
|
+
const key = this.buildCacheKey(options);
|
|
58
73
|
const result = await this.cacheManager.get(key);
|
|
59
74
|
return result || null;
|
|
60
75
|
} catch {
|
|
61
76
|
return null;
|
|
62
77
|
}
|
|
63
78
|
}
|
|
64
|
-
|
|
65
|
-
/** Generate tenant-aware cache key for action codes */ generateActionCodeCacheKey(tenantId) {
|
|
66
|
-
if (tenantId) {
|
|
67
|
-
return `${this.ACTION_CODE_PREFIX}:tenant:${tenantId}:map`;
|
|
68
|
-
}
|
|
69
|
-
return `${this.ACTION_CODE_PREFIX}:map`;
|
|
70
|
-
}
|
|
71
|
-
async setActionCodeMap(codeToIdMap, tenantId) {
|
|
72
|
-
const key = this.generateActionCodeCacheKey(tenantId);
|
|
73
|
-
await this.cacheManager.set(key, codeToIdMap, this.ACTION_CODE_TTL);
|
|
74
|
-
}
|
|
75
|
-
async getActionIdsByCodes(codes, tenantId) {
|
|
79
|
+
async trackKey(userId, key, companyId) {
|
|
76
80
|
try {
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
80
|
-
|
|
81
|
+
const trackedKeysKey = this.buildTrackedKeysKey(userId, companyId);
|
|
82
|
+
const keys = await this.cacheManager.get(trackedKeysKey) || [];
|
|
83
|
+
if (!keys.includes(key)) {
|
|
84
|
+
keys.push(key);
|
|
85
|
+
await this.cacheManager.set(trackedKeysKey, keys, this.TTL);
|
|
81
86
|
}
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
const scopesKey = this.buildTrackedScopesKey(userId);
|
|
88
|
+
const scopes = await this.cacheManager.get(scopesKey) || [];
|
|
89
|
+
const scope = this.scopeToken(companyId);
|
|
90
|
+
if (!scopes.includes(scope)) {
|
|
91
|
+
scopes.push(scope);
|
|
92
|
+
await this.cacheManager.set(scopesKey, scopes, this.TTL);
|
|
87
93
|
}
|
|
88
|
-
return Object.keys(result).length > 0 ? result : null;
|
|
89
94
|
} catch {
|
|
90
|
-
|
|
95
|
+
// tracking is best-effort; a missed entry only widens invalidation, never narrows it
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
// Cache Invalidation
|
|
94
99
|
async invalidateUser(userId, companyId, branchIds) {
|
|
95
|
-
const keysToDelete = [
|
|
96
|
-
`${this.CACHE_PREFIX}:user:${userId}`,
|
|
97
|
-
`${this.MY_PERMISSIONS_PREFIX}:user:${userId}`
|
|
98
|
-
];
|
|
99
100
|
if (companyId) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
await this.invalidateScope(userId, companyId, branchIds);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// No company scope given: clear every company/branch scope tracked for this user
|
|
105
|
+
const scopesKey = this.buildTrackedScopesKey(userId);
|
|
106
|
+
let scopes = [];
|
|
107
|
+
try {
|
|
108
|
+
scopes = await this.cacheManager.get(scopesKey) || [];
|
|
109
|
+
} catch {
|
|
110
|
+
scopes = [];
|
|
111
|
+
}
|
|
112
|
+
await Promise.all(scopes.map((scope)=>this.invalidateScope(userId, scope === NO_COMPANY_SCOPE ? null : scope, branchIds)));
|
|
113
|
+
await this.cacheManager.del(scopesKey);
|
|
114
|
+
}
|
|
115
|
+
async invalidateScope(userId, companyId, branchIds) {
|
|
116
|
+
const keysToDelete = new Set();
|
|
117
|
+
const trackedKeysKey = this.buildTrackedKeysKey(userId, companyId);
|
|
118
|
+
let trackedKeys = [];
|
|
119
|
+
try {
|
|
120
|
+
trackedKeys = await this.cacheManager.get(trackedKeysKey) || [];
|
|
121
|
+
} catch {
|
|
122
|
+
trackedKeys = [];
|
|
123
|
+
}
|
|
124
|
+
if (!branchIds?.length) {
|
|
125
|
+
trackedKeys.forEach((key)=>keysToDelete.add(key));
|
|
126
|
+
keysToDelete.add(trackedKeysKey);
|
|
127
|
+
} else {
|
|
128
|
+
// Branch scope given: only invalidate those branches, keep the rest tracked
|
|
129
|
+
branchIds.forEach((branchId)=>keysToDelete.add(this.buildCacheKey({
|
|
130
|
+
userId,
|
|
131
|
+
companyId,
|
|
132
|
+
branchId
|
|
133
|
+
})));
|
|
134
|
+
const remainingKeys = trackedKeys.filter((key)=>!keysToDelete.has(key));
|
|
135
|
+
if (remainingKeys.length) {
|
|
136
|
+
await this.cacheManager.set(trackedKeysKey, remainingKeys, this.TTL);
|
|
137
|
+
} else {
|
|
138
|
+
keysToDelete.add(trackedKeysKey);
|
|
105
139
|
}
|
|
106
140
|
}
|
|
107
|
-
await Promise.all(
|
|
141
|
+
await Promise.all([
|
|
142
|
+
...keysToDelete
|
|
143
|
+
].map((key)=>this.cacheManager.del(key)));
|
|
108
144
|
}
|
|
109
145
|
async invalidateUsers(userIds, companyId, branchIds) {
|
|
110
146
|
if (userIds.length === 0) {
|
|
@@ -113,39 +149,19 @@ export class PermissionCacheService {
|
|
|
113
149
|
const results = await Promise.allSettled(userIds.map((userId)=>this.invalidateUser(userId, companyId, branchIds)));
|
|
114
150
|
return results.filter((r)=>r.status === 'fulfilled').length;
|
|
115
151
|
}
|
|
116
|
-
|
|
117
|
-
if (userIds.length === 0) {
|
|
118
|
-
return 0;
|
|
119
|
-
}
|
|
120
|
-
return await this.invalidateUsers(userIds, companyId, branchIds);
|
|
121
|
-
}
|
|
122
|
-
constructor(cacheManager){
|
|
152
|
+
constructor(cacheManager, guardConfig){
|
|
123
153
|
_define_property(this, "cacheManager", void 0);
|
|
124
|
-
_define_property(this, "
|
|
125
|
-
_define_property(this, "
|
|
126
|
-
_define_property(this, "
|
|
127
|
-
_define_property(this, "
|
|
128
|
-
_define_property(this, "ACTION_CODE_PREFIX", void 0);
|
|
154
|
+
_define_property(this, "guardConfig", void 0);
|
|
155
|
+
_define_property(this, "TRACKED_KEYS_PREFIX", void 0);
|
|
156
|
+
_define_property(this, "TRACKED_SCOPES_PREFIX", void 0);
|
|
157
|
+
_define_property(this, "TTL", void 0);
|
|
129
158
|
this.cacheManager = cacheManager;
|
|
130
|
-
this.
|
|
131
|
-
this.
|
|
132
|
-
this.
|
|
133
|
-
this.
|
|
134
|
-
this.ACTION_CODE_PREFIX = 'action-codes';
|
|
159
|
+
this.guardConfig = guardConfig;
|
|
160
|
+
this.TRACKED_KEYS_PREFIX = 'permission-tracked-keys';
|
|
161
|
+
this.TRACKED_SCOPES_PREFIX = 'permission-tracked-scopes';
|
|
162
|
+
this.TTL = this.parseDurationMs(envConfig.getJwtConfig().refreshExpiration);
|
|
135
163
|
}
|
|
136
164
|
}
|
|
137
|
-
_ts_decorate([
|
|
138
|
-
LogAction({
|
|
139
|
-
action: 'permissionCache.setPermissions',
|
|
140
|
-
module: 'iam'
|
|
141
|
-
}),
|
|
142
|
-
_ts_metadata("design:type", Function),
|
|
143
|
-
_ts_metadata("design:paramtypes", [
|
|
144
|
-
typeof PermissionCacheKeyOptions === "undefined" ? Object : PermissionCacheKeyOptions,
|
|
145
|
-
Array
|
|
146
|
-
]),
|
|
147
|
-
_ts_metadata("design:returntype", Promise)
|
|
148
|
-
], PermissionCacheService.prototype, "setPermissions", null);
|
|
149
165
|
_ts_decorate([
|
|
150
166
|
LogAction({
|
|
151
167
|
action: 'permissionCache.setMyPermissions',
|
|
@@ -158,18 +174,6 @@ _ts_decorate([
|
|
|
158
174
|
]),
|
|
159
175
|
_ts_metadata("design:returntype", Promise)
|
|
160
176
|
], PermissionCacheService.prototype, "setMyPermissions", null);
|
|
161
|
-
_ts_decorate([
|
|
162
|
-
LogAction({
|
|
163
|
-
action: 'permissionCache.setActionCodeMap',
|
|
164
|
-
module: 'iam'
|
|
165
|
-
}),
|
|
166
|
-
_ts_metadata("design:type", Function),
|
|
167
|
-
_ts_metadata("design:paramtypes", [
|
|
168
|
-
typeof Record === "undefined" ? Object : Record,
|
|
169
|
-
String
|
|
170
|
-
]),
|
|
171
|
-
_ts_metadata("design:returntype", Promise)
|
|
172
|
-
], PermissionCacheService.prototype, "setActionCodeMap", null);
|
|
173
177
|
_ts_decorate([
|
|
174
178
|
LogAction({
|
|
175
179
|
action: 'permissionCache.invalidateUser',
|
|
@@ -196,25 +200,14 @@ _ts_decorate([
|
|
|
196
200
|
]),
|
|
197
201
|
_ts_metadata("design:returntype", Promise)
|
|
198
202
|
], PermissionCacheService.prototype, "invalidateUsers", null);
|
|
199
|
-
_ts_decorate([
|
|
200
|
-
LogAction({
|
|
201
|
-
action: 'permissionCache.invalidateRole',
|
|
202
|
-
module: 'iam'
|
|
203
|
-
}),
|
|
204
|
-
_ts_metadata("design:type", Function),
|
|
205
|
-
_ts_metadata("design:paramtypes", [
|
|
206
|
-
String,
|
|
207
|
-
Array,
|
|
208
|
-
Object,
|
|
209
|
-
Array
|
|
210
|
-
]),
|
|
211
|
-
_ts_metadata("design:returntype", Promise)
|
|
212
|
-
], PermissionCacheService.prototype, "invalidateRole", null);
|
|
213
203
|
PermissionCacheService = _ts_decorate([
|
|
214
204
|
Injectable(),
|
|
215
205
|
_ts_param(0, Inject('CACHE_INSTANCE')),
|
|
206
|
+
_ts_param(1, Optional()),
|
|
207
|
+
_ts_param(1, Inject(PERMISSION_GUARD_CONFIG)),
|
|
216
208
|
_ts_metadata("design:type", Function),
|
|
217
209
|
_ts_metadata("design:paramtypes", [
|
|
218
|
-
typeof HybridCache === "undefined" ? Object : HybridCache
|
|
210
|
+
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
211
|
+
typeof PermissionGuardConfig === "undefined" ? Object : PermissionGuardConfig
|
|
219
212
|
])
|
|
220
213
|
], PermissionCacheService);
|