@flusys/nestjs-iam 7.1.0 → 8.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/config/index.d.ts +3 -3
- package/config/message-keys.d.ts +2 -0
- package/controllers/action.controller.d.ts +4 -4
- package/controllers/company-action-permission.controller.d.ts +2 -2
- package/controllers/index.d.ts +6 -6
- package/controllers/my-permission.controller.d.ts +3 -3
- package/controllers/role-permission.controller.d.ts +5 -4
- package/controllers/role.controller.d.ts +2 -2
- package/controllers/user-action-permission.controller.d.ts +5 -4
- package/docs/iam-swagger.config.d.ts +2 -2
- package/docs/index.d.ts +1 -1
- package/dtos/action.dto.d.ts +2 -2
- package/dtos/index.d.ts +3 -3
- package/dtos/permission.dto.d.ts +16 -0
- package/entities/action.entity.d.ts +2 -2
- package/entities/index.d.ts +11 -11
- package/entities/permission-with-company.entity.d.ts +1 -1
- package/entities/role-with-company.entity.d.ts +1 -1
- package/entities/role.entity.d.ts +1 -1
- package/entities/user-iam-permission.entity.d.ts +1 -1
- package/enums/index.d.ts +4 -4
- package/fesm/284.js +1316 -0
- package/fesm/867.js +3801 -0
- package/fesm/chunks/364.js +86 -0
- package/fesm/config/index.js +135 -3
- package/fesm/controllers/index.js +18 -7
- package/fesm/docs/index.js +240 -1
- package/fesm/dtos/index.js +1168 -3
- package/fesm/entities/index.js +670 -31
- package/fesm/enums/index.js +97 -4
- package/fesm/helpers/index.js +125 -2
- package/fesm/index.js +818 -11
- package/fesm/interfaces/index.js +17 -3
- package/fesm/modules/index.js +259 -1
- package/fesm/runtime.js +134 -0
- package/fesm/services/index.js +43 -6
- package/fesm/types/index.js +15 -1
- package/helpers/company-access.helper.d.ts +2 -2
- package/helpers/index.d.ts +2 -2
- package/helpers/permission-mode.helper.d.ts +1 -1
- package/index.d.ts +11 -11
- package/interfaces/action.interface.d.ts +2 -2
- package/interfaces/iam-module-options.interface.d.ts +3 -3
- package/interfaces/index.d.ts +3 -3
- package/modules/iam.module.d.ts +2 -2
- package/modules/index.d.ts +1 -1
- package/package.json +30 -37
- package/services/action.service.d.ts +9 -9
- package/services/iam-config.service.d.ts +4 -4
- package/services/iam-datasource.service.d.ts +3 -3
- package/services/index.d.ts +6 -6
- package/services/permission-cache.service.d.ts +5 -5
- package/services/permission.service.d.ts +6 -4
- package/services/role.service.d.ts +8 -8
- package/types/index.d.ts +1 -1
- package/cjs/config/event-actions.js +0 -36
- package/cjs/config/iam.constants.js +0 -11
- package/cjs/config/index.js +0 -20
- package/cjs/config/message-keys.js +0 -71
- package/cjs/controllers/action.controller.js +0 -174
- package/cjs/controllers/action.controller.spec.js +0 -79
- package/cjs/controllers/company-action-permission.controller.js +0 -119
- package/cjs/controllers/company-action-permission.controller.spec.js +0 -65
- package/cjs/controllers/index.js +0 -23
- package/cjs/controllers/my-permission.controller.js +0 -147
- package/cjs/controllers/my-permission.controller.spec.js +0 -128
- package/cjs/controllers/role-permission.controller.js +0 -191
- package/cjs/controllers/role-permission.controller.spec.js +0 -140
- package/cjs/controllers/role.controller.js +0 -102
- package/cjs/controllers/role.controller.spec.js +0 -86
- package/cjs/controllers/user-action-permission.controller.js +0 -129
- package/cjs/controllers/user-action-permission.controller.spec.js +0 -68
- package/cjs/docs/iam-swagger.config.js +0 -209
- package/cjs/docs/iam-swagger.config.spec.js +0 -99
- package/cjs/docs/index.js +0 -18
- package/cjs/dtos/action.dto.js +0 -282
- package/cjs/dtos/index.js +0 -21
- package/cjs/dtos/permission.dto.js +0 -637
- package/cjs/dtos/role.dto.js +0 -155
- package/cjs/entities/action.entity.js +0 -138
- package/cjs/entities/index.js +0 -80
- package/cjs/entities/index.spec.js +0 -56
- package/cjs/entities/permission-base.entity.js +0 -107
- package/cjs/entities/permission-with-company.entity.js +0 -100
- package/cjs/entities/role-base.entity.js +0 -80
- package/cjs/entities/role-with-company.entity.js +0 -55
- package/cjs/entities/role.entity.js +0 -25
- package/cjs/entities/user-iam-permission.entity.js +0 -43
- package/cjs/enums/action-type.enum.js +0 -22
- package/cjs/enums/iam-entity-type.enum.js +0 -17
- package/cjs/enums/iam-permission-type.enum.js +0 -17
- package/cjs/enums/index.js +0 -21
- package/cjs/enums/permission-type.enum.js +0 -16
- package/cjs/helpers/company-access.helper.js +0 -23
- package/cjs/helpers/company-access.helper.spec.js +0 -68
- package/cjs/helpers/index.js +0 -19
- package/cjs/helpers/permission-mode.helper.js +0 -49
- package/cjs/helpers/permission-mode.helper.spec.js +0 -54
- package/cjs/index.js +0 -28
- package/cjs/interfaces/action.interface.js +0 -4
- package/cjs/interfaces/iam-module-options.interface.js +0 -4
- package/cjs/interfaces/index.js +0 -20
- package/cjs/interfaces/role.interface.js +0 -7
- package/cjs/modules/iam.module.js +0 -186
- package/cjs/modules/iam.module.spec.js +0 -282
- package/cjs/modules/index.js +0 -18
- package/cjs/services/action.service.js +0 -337
- package/cjs/services/action.service.spec.js +0 -351
- package/cjs/services/iam-config.service.js +0 -87
- package/cjs/services/iam-config.service.spec.js +0 -110
- package/cjs/services/iam-datasource.service.js +0 -129
- package/cjs/services/iam-datasource.service.spec.js +0 -110
- package/cjs/services/index.js +0 -23
- package/cjs/services/permission-cache.service.js +0 -544
- package/cjs/services/permission-cache.service.spec.js +0 -718
- package/cjs/services/permission.service.js +0 -914
- package/cjs/services/permission.service.spec.js +0 -918
- package/cjs/services/role.service.js +0 -224
- package/cjs/services/role.service.spec.js +0 -347
- package/cjs/types/index.js +0 -18
- package/cjs/types/logic-node.type.js +0 -30
- package/fesm/config/event-actions.js +0 -15
- package/fesm/config/iam.constants.js +0 -1
- package/fesm/config/message-keys.js +0 -35
- package/fesm/controllers/action.controller.js +0 -164
- package/fesm/controllers/action.controller.spec.js +0 -75
- package/fesm/controllers/company-action-permission.controller.js +0 -109
- package/fesm/controllers/company-action-permission.controller.spec.js +0 -61
- package/fesm/controllers/my-permission.controller.js +0 -137
- package/fesm/controllers/my-permission.controller.spec.js +0 -124
- package/fesm/controllers/role-permission.controller.js +0 -181
- package/fesm/controllers/role-permission.controller.spec.js +0 -136
- package/fesm/controllers/role.controller.js +0 -92
- package/fesm/controllers/role.controller.spec.js +0 -82
- package/fesm/controllers/user-action-permission.controller.js +0 -119
- package/fesm/controllers/user-action-permission.controller.spec.js +0 -64
- package/fesm/docs/iam-swagger.config.js +0 -199
- package/fesm/docs/iam-swagger.config.spec.js +0 -95
- package/fesm/dtos/action.dto.js +0 -252
- package/fesm/dtos/permission.dto.js +0 -571
- package/fesm/dtos/role.dto.js +0 -134
- package/fesm/entities/action.entity.js +0 -128
- package/fesm/entities/index.spec.js +0 -52
- package/fesm/entities/permission-base.entity.js +0 -100
- package/fesm/entities/permission-with-company.entity.js +0 -90
- package/fesm/entities/role-base.entity.js +0 -70
- package/fesm/entities/role-with-company.entity.js +0 -45
- package/fesm/entities/role.entity.js +0 -15
- package/fesm/entities/user-iam-permission.entity.js +0 -33
- package/fesm/enums/action-type.enum.js +0 -12
- package/fesm/enums/iam-entity-type.enum.js +0 -7
- package/fesm/enums/iam-permission-type.enum.js +0 -7
- package/fesm/enums/permission-type.enum.js +0 -6
- package/fesm/helpers/company-access.helper.js +0 -18
- package/fesm/helpers/company-access.helper.spec.js +0 -64
- package/fesm/helpers/permission-mode.helper.js +0 -49
- package/fesm/helpers/permission-mode.helper.spec.js +0 -50
- package/fesm/interfaces/action.interface.js +0 -3
- package/fesm/interfaces/iam-module-options.interface.js +0 -3
- package/fesm/interfaces/role.interface.js +0 -4
- package/fesm/modules/iam.module.js +0 -176
- package/fesm/modules/iam.module.spec.js +0 -278
- package/fesm/services/action.service.js +0 -327
- package/fesm/services/action.service.spec.js +0 -347
- package/fesm/services/iam-config.service.js +0 -77
- package/fesm/services/iam-config.service.spec.js +0 -106
- package/fesm/services/iam-datasource.service.js +0 -78
- package/fesm/services/iam-datasource.service.spec.js +0 -106
- package/fesm/services/permission-cache.service.js +0 -527
- package/fesm/services/permission-cache.service.spec.js +0 -714
- package/fesm/services/permission.service.js +0 -904
- package/fesm/services/permission.service.spec.js +0 -914
- package/fesm/services/role.service.js +0 -214
- package/fesm/services/role.service.spec.js +0 -343
- package/fesm/types/logic-node.type.js +0 -27
|
@@ -1,527 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
-
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;
|
|
18
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
-
}
|
|
20
|
-
function _ts_metadata(k, v) {
|
|
21
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
-
}
|
|
23
|
-
function _ts_param(paramIndex, decorator) {
|
|
24
|
-
return function(target, key) {
|
|
25
|
-
decorator(target, key, paramIndex);
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
import { envConfig } from '@flusys/nestjs-core/config';
|
|
29
|
-
import { buildPermissionCacheKey, HybridCache, LogAction, PERMISSION_GUARD_CONFIG, PermissionCacheKeyOptions, PermissionGuardConfig } from '@flusys/nestjs-shared';
|
|
30
|
-
import { Inject, Injectable, Optional, Scope } from '@nestjs/common';
|
|
31
|
-
import { In, IsNull } from 'typeorm';
|
|
32
|
-
import { Action } from '../entities/action.entity';
|
|
33
|
-
import { UserIamPermissionWithCompany } from '../entities/permission-with-company.entity';
|
|
34
|
-
import { RoleWithCompany } from '../entities/role-with-company.entity';
|
|
35
|
-
import { Role } from '../entities/role.entity';
|
|
36
|
-
import { UserIamPermission } from '../entities/user-iam-permission.entity';
|
|
37
|
-
import { ActionType } from '../enums/action-type.enum';
|
|
38
|
-
import { IamEntityType } from '../enums/iam-entity-type.enum';
|
|
39
|
-
import { IamPermissionType } from '../enums/iam-permission-type.enum';
|
|
40
|
-
import { IAMPermissionMode } from '../enums/permission-type.enum';
|
|
41
|
-
import { IAMConfigService } from './iam-config.service';
|
|
42
|
-
import { IAMDataSourceService } from './iam-datasource.service';
|
|
43
|
-
export { PermissionCacheKeyOptions } from '@flusys/nestjs-shared';
|
|
44
|
-
const NO_COMPANY_SCOPE = 'none';
|
|
45
|
-
export class PermissionCacheService {
|
|
46
|
-
// Repository Getters
|
|
47
|
-
async getPermissionRepository() {
|
|
48
|
-
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
49
|
-
const entity = enableCompanyFeature ? UserIamPermissionWithCompany : UserIamPermission;
|
|
50
|
-
return this.dataSourceProvider.getRepository(entity);
|
|
51
|
-
}
|
|
52
|
-
async getActionRepository() {
|
|
53
|
-
return this.dataSourceProvider.getRepository(Action);
|
|
54
|
-
}
|
|
55
|
-
async getRoleRepository() {
|
|
56
|
-
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
57
|
-
const entity = enableCompanyFeature ? RoleWithCompany : Role;
|
|
58
|
-
return this.dataSourceProvider.getRepository(entity);
|
|
59
|
-
}
|
|
60
|
-
// Cache Key Generation
|
|
61
|
-
parseDurationMs(duration) {
|
|
62
|
-
const unitMs = {
|
|
63
|
-
ms: 1,
|
|
64
|
-
s: 1000,
|
|
65
|
-
m: 60000,
|
|
66
|
-
h: 3600000,
|
|
67
|
-
d: 86400000,
|
|
68
|
-
w: 604800000
|
|
69
|
-
};
|
|
70
|
-
const match = /^(\d+)\s*(ms|s|m|h|d|w)?$/i.exec(duration.trim());
|
|
71
|
-
if (!match) {
|
|
72
|
-
return unitMs.h; // fallback: 1 hour
|
|
73
|
-
}
|
|
74
|
-
const unit = (match[2] || 'ms').toLowerCase();
|
|
75
|
-
return Number(match[1]) * unitMs[unit];
|
|
76
|
-
}
|
|
77
|
-
buildCacheKey(options) {
|
|
78
|
-
return buildPermissionCacheKey(options, this.guardConfig);
|
|
79
|
-
}
|
|
80
|
-
scopeToken(companyId) {
|
|
81
|
-
return companyId || NO_COMPANY_SCOPE;
|
|
82
|
-
}
|
|
83
|
-
buildTrackedKeysKey(userId, companyId) {
|
|
84
|
-
return `${this.TRACKED_KEYS_PREFIX}:user:${userId}:company:${this.scopeToken(companyId)}`;
|
|
85
|
-
}
|
|
86
|
-
buildTrackedScopesKey(userId) {
|
|
87
|
-
return `${this.TRACKED_SCOPES_PREFIX}:user:${userId}`;
|
|
88
|
-
}
|
|
89
|
-
// Cache Operations
|
|
90
|
-
async setMyPermissions(options, data) {
|
|
91
|
-
const key = this.buildCacheKey(options);
|
|
92
|
-
await this.cacheManager.set(key, data, this.TTL);
|
|
93
|
-
await this.trackKey(options.userId, key, options.companyId);
|
|
94
|
-
}
|
|
95
|
-
async getMyPermissions(options) {
|
|
96
|
-
try {
|
|
97
|
-
const key = this.buildCacheKey(options);
|
|
98
|
-
const result = await this.cacheManager.get(key);
|
|
99
|
-
return result || null;
|
|
100
|
-
} catch {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
async trackKey(userId, key, companyId) {
|
|
105
|
-
try {
|
|
106
|
-
let scopeKey = key;
|
|
107
|
-
if (companyId) {
|
|
108
|
-
scopeKey = this.buildTrackedKeysKey(userId, companyId);
|
|
109
|
-
const keys = await this.cacheManager.get(scopeKey) || [];
|
|
110
|
-
if (!keys.includes(key)) {
|
|
111
|
-
keys.push(key);
|
|
112
|
-
await this.cacheManager.set(scopeKey, keys, this.TTL);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
const scopesKey = this.buildTrackedScopesKey(userId);
|
|
116
|
-
const scopes = await this.cacheManager.get(scopesKey) || [];
|
|
117
|
-
if (!scopes.includes(scopeKey)) {
|
|
118
|
-
scopes.push(scopeKey);
|
|
119
|
-
await this.cacheManager.set(scopesKey, scopes, this.TTL);
|
|
120
|
-
}
|
|
121
|
-
} catch {
|
|
122
|
-
// tracking is best-effort; a missed entry only widens invalidation, never narrows it
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// My Permissions (cache-first read, DB-backed rebuild)
|
|
126
|
-
/** Get user's effective permissions (cache-first approach) */ async getMyPermissionsResponse(userId, branchId, companyId, parentCodes) {
|
|
127
|
-
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
128
|
-
const cacheOptions = {
|
|
129
|
-
userId,
|
|
130
|
-
companyId: enableCompanyFeature ? companyId : null,
|
|
131
|
-
branchId: enableCompanyFeature ? branchId : null
|
|
132
|
-
};
|
|
133
|
-
const cachedData = await this.getMyPermissions(cacheOptions);
|
|
134
|
-
if (cachedData) {
|
|
135
|
-
return this.buildResponseFromCache(cachedData, parentCodes);
|
|
136
|
-
}
|
|
137
|
-
const freshData = await this.fetchAndCachePermissions(userId, branchId, companyId);
|
|
138
|
-
return this.buildResponseFromCache(freshData, parentCodes);
|
|
139
|
-
}
|
|
140
|
-
/** Build response from cached data, applying optional parentCodes filter */ async buildResponseFromCache(cachedData, parentCodes) {
|
|
141
|
-
let frontendActions = cachedData.frontendActions;
|
|
142
|
-
if (parentCodes?.length) {
|
|
143
|
-
const parentIds = await this.getParentIdsByCodesWithCache(parentCodes);
|
|
144
|
-
if (parentIds.size > 0) {
|
|
145
|
-
frontendActions = frontendActions.filter((a)=>a.parentId && parentIds.has(a.parentId));
|
|
146
|
-
} else {
|
|
147
|
-
frontendActions = [];
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
frontendActions: frontendActions.map((a)=>({
|
|
152
|
-
code: a.code,
|
|
153
|
-
name: a.name,
|
|
154
|
-
description: a.description
|
|
155
|
-
})),
|
|
156
|
-
cachedEndpoints: cachedData.backendCodes.length
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
/** Resolve action IDs for the given codes */ async getParentIdsByCodesWithCache(codes) {
|
|
160
|
-
const actionRepo = await this.getActionRepository();
|
|
161
|
-
const actions = await actionRepo.find({
|
|
162
|
-
where: {
|
|
163
|
-
code: In(codes)
|
|
164
|
-
},
|
|
165
|
-
select: [
|
|
166
|
-
'id',
|
|
167
|
-
'code'
|
|
168
|
-
]
|
|
169
|
-
});
|
|
170
|
-
return new Set(actions.map((action)=>action.id));
|
|
171
|
-
}
|
|
172
|
-
/** Fetch permissions from DB and cache them (empty permissions are also cached) */ async fetchAndCachePermissions(userId, branchId, companyId) {
|
|
173
|
-
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
174
|
-
const cacheOptions = {
|
|
175
|
-
userId,
|
|
176
|
-
companyId: enableCompanyFeature ? companyId : null,
|
|
177
|
-
branchId
|
|
178
|
-
};
|
|
179
|
-
const emptyData = {
|
|
180
|
-
frontendActions: [],
|
|
181
|
-
backendCodes: []
|
|
182
|
-
};
|
|
183
|
-
const allActionIds = await this.collectAllActionIds(userId, branchId, companyId);
|
|
184
|
-
if (allActionIds.size === 0) {
|
|
185
|
-
await this.setMyPermissions(cacheOptions, emptyData);
|
|
186
|
-
return emptyData;
|
|
187
|
-
}
|
|
188
|
-
return this.buildAndCachePermissionData(allActionIds, cacheOptions);
|
|
189
|
-
}
|
|
190
|
-
/** Collect all action IDs based on permission mode (RBAC, DIRECT, or FULL) */ async collectAllActionIds(userId, branchId, companyId) {
|
|
191
|
-
const permissionMode = this.iamConfigService.getPermissionMode();
|
|
192
|
-
const allActionIds = new Set();
|
|
193
|
-
if (permissionMode === IAMPermissionMode.RBAC || permissionMode === IAMPermissionMode.FULL) {
|
|
194
|
-
const userRoleIds = await this.getUserRoleIds(userId, branchId, companyId);
|
|
195
|
-
if (userRoleIds.length > 0) {
|
|
196
|
-
const roleActionIds = await this.getRoleActionIds(userRoleIds);
|
|
197
|
-
roleActionIds.forEach((id)=>allActionIds.add(id));
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (permissionMode === IAMPermissionMode.DIRECT || permissionMode === IAMPermissionMode.FULL) {
|
|
201
|
-
const userActionIds = await this.getUserActionIds(userId, branchId, companyId);
|
|
202
|
-
userActionIds.forEach((id)=>allActionIds.add(id));
|
|
203
|
-
}
|
|
204
|
-
return allActionIds;
|
|
205
|
-
}
|
|
206
|
-
/** Fetch actions from DB, separate by type, build cache data, and cache it */ async buildAndCachePermissionData(actionIds, cacheOptions) {
|
|
207
|
-
const actionRepo = await this.getActionRepository();
|
|
208
|
-
const actions = await actionRepo.find({
|
|
209
|
-
where: {
|
|
210
|
-
id: In(Array.from(actionIds)),
|
|
211
|
-
isActive: true
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
const backendActions = actions.filter((a)=>a.actionType === ActionType.BACKEND || a.actionType === ActionType.BOTH);
|
|
215
|
-
const frontendActions = actions.filter((a)=>a.actionType === ActionType.FRONTEND || a.actionType === ActionType.BOTH);
|
|
216
|
-
const backendCodes = backendActions.map((a)=>a.code).filter((c)=>!!c);
|
|
217
|
-
const cacheData = {
|
|
218
|
-
frontendActions: frontendActions.map((a)=>({
|
|
219
|
-
id: a.id,
|
|
220
|
-
code: a.code ?? '',
|
|
221
|
-
name: a.name,
|
|
222
|
-
description: a.description,
|
|
223
|
-
parentId: a.parentId
|
|
224
|
-
})),
|
|
225
|
-
backendCodes
|
|
226
|
-
};
|
|
227
|
-
await this.setMyPermissions(cacheOptions, cacheData);
|
|
228
|
-
return cacheData;
|
|
229
|
-
}
|
|
230
|
-
/** Get role IDs assigned to a user (merges company-wide + branch-specific roles) */ async getUserRoleIds(userId, branchId, companyId) {
|
|
231
|
-
const permissionRepo = await this.getPermissionRepository();
|
|
232
|
-
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
233
|
-
if (!enableCompanyFeature) {
|
|
234
|
-
const permissions = await permissionRepo.find({
|
|
235
|
-
where: {
|
|
236
|
-
permissionType: IamPermissionType.USER_ROLE,
|
|
237
|
-
sourceType: IamEntityType.USER,
|
|
238
|
-
sourceId: userId
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
return permissions.filter((p)=>p.isValid()).map((p)=>p.targetId);
|
|
242
|
-
}
|
|
243
|
-
const roleIds = new Set();
|
|
244
|
-
const baseWhere = {
|
|
245
|
-
permissionType: IamPermissionType.USER_ROLE,
|
|
246
|
-
sourceType: IamEntityType.USER,
|
|
247
|
-
sourceId: userId
|
|
248
|
-
};
|
|
249
|
-
if (companyId) {
|
|
250
|
-
baseWhere.companyId = companyId;
|
|
251
|
-
}
|
|
252
|
-
if (branchId) {
|
|
253
|
-
// Get company-wide + branch-specific roles
|
|
254
|
-
const companyWidePermissions = await permissionRepo.find({
|
|
255
|
-
where: {
|
|
256
|
-
...baseWhere,
|
|
257
|
-
branchId: IsNull()
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
companyWidePermissions.filter((p)=>p.isValid()).forEach((p)=>roleIds.add(p.targetId));
|
|
261
|
-
const branchPermissions = await permissionRepo.find({
|
|
262
|
-
where: {
|
|
263
|
-
...baseWhere,
|
|
264
|
-
branchId
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
branchPermissions.filter((p)=>p.isValid()).forEach((p)=>roleIds.add(p.targetId));
|
|
268
|
-
} else {
|
|
269
|
-
// No branch context: get all roles for this company
|
|
270
|
-
const allPermissions = await permissionRepo.find({
|
|
271
|
-
where: baseWhere
|
|
272
|
-
});
|
|
273
|
-
allPermissions.filter((p)=>p.isValid()).forEach((p)=>roleIds.add(p.targetId));
|
|
274
|
-
}
|
|
275
|
-
return Array.from(roleIds);
|
|
276
|
-
}
|
|
277
|
-
async getRoleActionIds(roleIds) {
|
|
278
|
-
const permissionRepo = await this.getPermissionRepository();
|
|
279
|
-
const permissions = await permissionRepo.find({
|
|
280
|
-
where: {
|
|
281
|
-
permissionType: IamPermissionType.ROLE_ACTION,
|
|
282
|
-
sourceType: IamEntityType.ROLE,
|
|
283
|
-
sourceId: In(roleIds)
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
return permissions.filter((p)=>p.isValid()).map((p)=>p.targetId);
|
|
287
|
-
}
|
|
288
|
-
/** Get action IDs directly assigned to a user (merges company-wide + branch-specific actions) */ async getUserActionIds(userId, branchId, companyId) {
|
|
289
|
-
const permissionRepo = await this.getPermissionRepository();
|
|
290
|
-
const enableCompanyFeature = this.iamConfigService.isCompanyFeatureEnabled();
|
|
291
|
-
if (!enableCompanyFeature) {
|
|
292
|
-
const permissions = await permissionRepo.find({
|
|
293
|
-
where: {
|
|
294
|
-
permissionType: IamPermissionType.USER_ACTION,
|
|
295
|
-
sourceType: IamEntityType.USER,
|
|
296
|
-
sourceId: userId
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
return permissions.filter((p)=>p.isValid()).map((p)=>p.targetId);
|
|
300
|
-
}
|
|
301
|
-
const actionIds = new Set();
|
|
302
|
-
const baseWhere = {
|
|
303
|
-
permissionType: IamPermissionType.USER_ACTION,
|
|
304
|
-
sourceType: IamEntityType.USER,
|
|
305
|
-
sourceId: userId
|
|
306
|
-
};
|
|
307
|
-
if (companyId) {
|
|
308
|
-
baseWhere.companyId = companyId;
|
|
309
|
-
}
|
|
310
|
-
if (branchId) {
|
|
311
|
-
// Get company-wide + branch-specific actions
|
|
312
|
-
const companyWidePermissions = await permissionRepo.find({
|
|
313
|
-
where: {
|
|
314
|
-
...baseWhere,
|
|
315
|
-
branchId: IsNull()
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
companyWidePermissions.filter((p)=>p.isValid()).forEach((p)=>actionIds.add(p.targetId));
|
|
319
|
-
const branchPermissions = await permissionRepo.find({
|
|
320
|
-
where: {
|
|
321
|
-
...baseWhere,
|
|
322
|
-
branchId
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
branchPermissions.filter((p)=>p.isValid()).forEach((p)=>actionIds.add(p.targetId));
|
|
326
|
-
} else {
|
|
327
|
-
// No branch context: get all actions for this company
|
|
328
|
-
const allPermissions = await permissionRepo.find({
|
|
329
|
-
where: baseWhere
|
|
330
|
-
});
|
|
331
|
-
allPermissions.filter((p)=>p.isValid()).forEach((p)=>actionIds.add(p.targetId));
|
|
332
|
-
}
|
|
333
|
-
return Array.from(actionIds);
|
|
334
|
-
}
|
|
335
|
-
// Cache Invalidation
|
|
336
|
-
async invalidateUser(userId, companyId, branchIds) {
|
|
337
|
-
const keysToDelete = new Set();
|
|
338
|
-
/** Drop a scope entry from the tracked-scopes index, or queue the whole index for deletion once nothing remains */ const untrackScope = async (scopeKey)=>{
|
|
339
|
-
const scopesKeysKey = this.buildTrackedScopesKey(userId);
|
|
340
|
-
let scopesKeys = [];
|
|
341
|
-
try {
|
|
342
|
-
scopesKeys = await this.cacheManager.get(scopesKeysKey) || [];
|
|
343
|
-
} catch {
|
|
344
|
-
scopesKeys = [];
|
|
345
|
-
}
|
|
346
|
-
if (scopeKey) {
|
|
347
|
-
const remainingScopes = scopesKeys.filter((scope)=>scope !== scopeKey);
|
|
348
|
-
if (remainingScopes.length > 0) {
|
|
349
|
-
await this.cacheManager.set(scopesKeysKey, remainingScopes, this.TTL);
|
|
350
|
-
} else {
|
|
351
|
-
keysToDelete.add(scopesKeysKey);
|
|
352
|
-
}
|
|
353
|
-
} else {
|
|
354
|
-
for (const scope of scopesKeys){
|
|
355
|
-
if (scope.startsWith(this.TRACKED_KEYS_PREFIX)) {
|
|
356
|
-
let nestedKeys = [];
|
|
357
|
-
try {
|
|
358
|
-
nestedKeys = await this.cacheManager.get(scope) || [];
|
|
359
|
-
} catch {
|
|
360
|
-
nestedKeys = [];
|
|
361
|
-
}
|
|
362
|
-
nestedKeys.forEach((key)=>keysToDelete.add(key));
|
|
363
|
-
}
|
|
364
|
-
keysToDelete.add(scope);
|
|
365
|
-
}
|
|
366
|
-
keysToDelete.add(scopesKeysKey);
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
if (companyId) {
|
|
370
|
-
const trackedKeysKey = this.buildTrackedKeysKey(userId, companyId);
|
|
371
|
-
let trackedKeys = [];
|
|
372
|
-
try {
|
|
373
|
-
trackedKeys = await this.cacheManager.get(trackedKeysKey) || [];
|
|
374
|
-
} catch {
|
|
375
|
-
trackedKeys = [];
|
|
376
|
-
}
|
|
377
|
-
const hasCompanyWideScope = branchIds?.some((branchId)=>!branchId);
|
|
378
|
-
if (!branchIds?.length || hasCompanyWideScope) {
|
|
379
|
-
trackedKeys.forEach((key)=>keysToDelete.add(key));
|
|
380
|
-
keysToDelete.add(trackedKeysKey);
|
|
381
|
-
await untrackScope(trackedKeysKey);
|
|
382
|
-
} else {
|
|
383
|
-
// Branch scope given: only invalidate those branches, keep the rest tracked
|
|
384
|
-
branchIds.forEach((branchId)=>keysToDelete.add(this.buildCacheKey({
|
|
385
|
-
userId,
|
|
386
|
-
companyId,
|
|
387
|
-
branchId
|
|
388
|
-
})));
|
|
389
|
-
const remainingKeys = trackedKeys.filter((key)=>!keysToDelete.has(key));
|
|
390
|
-
if (remainingKeys.length) {
|
|
391
|
-
await this.cacheManager.set(trackedKeysKey, remainingKeys, this.TTL);
|
|
392
|
-
} else {
|
|
393
|
-
keysToDelete.add(trackedKeysKey);
|
|
394
|
-
await untrackScope(trackedKeysKey);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
} else {
|
|
398
|
-
await untrackScope();
|
|
399
|
-
}
|
|
400
|
-
await Promise.all(Array.from(keysToDelete).map(async (key)=>{
|
|
401
|
-
try {
|
|
402
|
-
await this.cacheManager.del(key);
|
|
403
|
-
} catch {}
|
|
404
|
-
}));
|
|
405
|
-
}
|
|
406
|
-
async invalidateUsers(userIds, companyId, branchIds) {
|
|
407
|
-
if (userIds.length === 0) {
|
|
408
|
-
return 0;
|
|
409
|
-
}
|
|
410
|
-
const results = await Promise.allSettled(userIds.map((userId)=>this.invalidateUser(userId, companyId, branchIds)));
|
|
411
|
-
return results.filter((r)=>r.status === 'fulfilled').length;
|
|
412
|
-
}
|
|
413
|
-
/** Invalidate permission cache for all users holding a given role */ async invalidateRoleMembersCache(roleId) {
|
|
414
|
-
const permissionRepo = await this.getPermissionRepository();
|
|
415
|
-
const roleRepo = await this.getRoleRepository();
|
|
416
|
-
const userRoles = await permissionRepo.find({
|
|
417
|
-
where: {
|
|
418
|
-
permissionType: IamPermissionType.USER_ROLE,
|
|
419
|
-
sourceType: IamEntityType.USER,
|
|
420
|
-
targetType: IamEntityType.ROLE,
|
|
421
|
-
targetId: roleId
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
const userIds = [
|
|
425
|
-
...new Set(userRoles.map((ur)=>ur.sourceId))
|
|
426
|
-
];
|
|
427
|
-
if (userIds.length === 0) {
|
|
428
|
-
return 0;
|
|
429
|
-
}
|
|
430
|
-
const role = await roleRepo.findOne({
|
|
431
|
-
where: {
|
|
432
|
-
id: roleId
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
const companyId = role?.companyId || null;
|
|
436
|
-
return this.invalidateUsers(userIds, companyId);
|
|
437
|
-
}
|
|
438
|
-
/** Invalidate permission cache for all users in a company */ async invalidateCompanyMembersCache(companyId) {
|
|
439
|
-
if (!this.iamConfigService.isCompanyFeatureEnabled()) {
|
|
440
|
-
return 0;
|
|
441
|
-
}
|
|
442
|
-
const permissionRepo = await this.getPermissionRepository();
|
|
443
|
-
const userPermissions = await permissionRepo.createQueryBuilder('p').select('DISTINCT p.source_id', 'userId').where('p.company_id = :companyId', {
|
|
444
|
-
companyId
|
|
445
|
-
}).andWhere('p.source_type = :sourceType', {
|
|
446
|
-
sourceType: IamEntityType.USER
|
|
447
|
-
}).getRawMany();
|
|
448
|
-
const userIds = [
|
|
449
|
-
...new Set(userPermissions.map((p)=>p.userId).filter(Boolean))
|
|
450
|
-
];
|
|
451
|
-
if (userIds.length === 0) {
|
|
452
|
-
return 0;
|
|
453
|
-
}
|
|
454
|
-
return this.invalidateUsers(userIds, companyId);
|
|
455
|
-
}
|
|
456
|
-
constructor(cacheManager, iamConfigService, dataSourceProvider, guardConfig){
|
|
457
|
-
_define_property(this, "cacheManager", void 0);
|
|
458
|
-
_define_property(this, "iamConfigService", void 0);
|
|
459
|
-
_define_property(this, "dataSourceProvider", void 0);
|
|
460
|
-
_define_property(this, "guardConfig", void 0);
|
|
461
|
-
_define_property(this, "TRACKED_KEYS_PREFIX", void 0);
|
|
462
|
-
_define_property(this, "TRACKED_SCOPES_PREFIX", void 0);
|
|
463
|
-
_define_property(this, "TTL", void 0);
|
|
464
|
-
this.cacheManager = cacheManager;
|
|
465
|
-
this.iamConfigService = iamConfigService;
|
|
466
|
-
this.dataSourceProvider = dataSourceProvider;
|
|
467
|
-
this.guardConfig = guardConfig;
|
|
468
|
-
this.TRACKED_KEYS_PREFIX = 'permission-tracked-keys';
|
|
469
|
-
this.TRACKED_SCOPES_PREFIX = 'permission-tracked-scopes';
|
|
470
|
-
this.TTL = this.parseDurationMs(envConfig.getJwtConfig().refreshExpiration);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
_ts_decorate([
|
|
474
|
-
LogAction({
|
|
475
|
-
action: 'permissionCache.setMyPermissions',
|
|
476
|
-
module: 'iam'
|
|
477
|
-
}),
|
|
478
|
-
_ts_metadata("design:type", Function),
|
|
479
|
-
_ts_metadata("design:paramtypes", [
|
|
480
|
-
typeof PermissionCacheKeyOptions === "undefined" ? Object : PermissionCacheKeyOptions,
|
|
481
|
-
typeof CachedMyPermissions === "undefined" ? Object : CachedMyPermissions
|
|
482
|
-
]),
|
|
483
|
-
_ts_metadata("design:returntype", Promise)
|
|
484
|
-
], PermissionCacheService.prototype, "setMyPermissions", null);
|
|
485
|
-
_ts_decorate([
|
|
486
|
-
LogAction({
|
|
487
|
-
action: 'permissionCache.invalidateUser',
|
|
488
|
-
module: 'iam'
|
|
489
|
-
}),
|
|
490
|
-
_ts_metadata("design:type", Function),
|
|
491
|
-
_ts_metadata("design:paramtypes", [
|
|
492
|
-
String,
|
|
493
|
-
Object,
|
|
494
|
-
Array
|
|
495
|
-
]),
|
|
496
|
-
_ts_metadata("design:returntype", Promise)
|
|
497
|
-
], PermissionCacheService.prototype, "invalidateUser", null);
|
|
498
|
-
_ts_decorate([
|
|
499
|
-
LogAction({
|
|
500
|
-
action: 'permissionCache.invalidateUsers',
|
|
501
|
-
module: 'iam'
|
|
502
|
-
}),
|
|
503
|
-
_ts_metadata("design:type", Function),
|
|
504
|
-
_ts_metadata("design:paramtypes", [
|
|
505
|
-
Array,
|
|
506
|
-
Object,
|
|
507
|
-
Array
|
|
508
|
-
]),
|
|
509
|
-
_ts_metadata("design:returntype", Promise)
|
|
510
|
-
], PermissionCacheService.prototype, "invalidateUsers", null);
|
|
511
|
-
PermissionCacheService = _ts_decorate([
|
|
512
|
-
Injectable({
|
|
513
|
-
scope: Scope.REQUEST
|
|
514
|
-
}),
|
|
515
|
-
_ts_param(0, Inject('CACHE_INSTANCE')),
|
|
516
|
-
_ts_param(1, Inject(IAMConfigService)),
|
|
517
|
-
_ts_param(2, Inject(IAMDataSourceService)),
|
|
518
|
-
_ts_param(3, Optional()),
|
|
519
|
-
_ts_param(3, Inject(PERMISSION_GUARD_CONFIG)),
|
|
520
|
-
_ts_metadata("design:type", Function),
|
|
521
|
-
_ts_metadata("design:paramtypes", [
|
|
522
|
-
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
523
|
-
typeof IAMConfigService === "undefined" ? Object : IAMConfigService,
|
|
524
|
-
typeof IAMDataSourceService === "undefined" ? Object : IAMDataSourceService,
|
|
525
|
-
typeof PermissionGuardConfig === "undefined" ? Object : PermissionGuardConfig
|
|
526
|
-
])
|
|
527
|
-
], PermissionCacheService);
|