@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.
@@ -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 _config = require("../config");
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: 'Returns actions available for permission assignment. If company feature enabled, filtered by company whitelist.'
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
- _ts_param(0, (0, _nestjsshared.CurrentUser)()),
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',
@@ -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
- if (this.iamConfigService.isCompanyFeatureEnabled() && user.companyId) {
116
- const companyActionIds = await this.permissionService.getCompanyActionIds(user.companyId);
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
- Object.defineProperty(exports, "PermissionCacheService", {
6
- enumerable: true,
7
- get: function() {
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
- generateCacheKey(options) {
43
- return this.buildCacheKey(this.CACHE_PREFIX, options);
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
- generateMyPermissionsCacheKey(options) {
46
- return this.buildCacheKey(this.MY_PERMISSIONS_PREFIX, options);
72
+ buildCacheKey(options) {
73
+ return (0, _nestjsshared.buildPermissionCacheKey)(options, this.guardConfig);
47
74
  }
48
- buildCacheKey(prefix, options) {
49
- const { userId, companyId, branchId, enableCompanyFeature } = options;
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
- // Cache Operations
56
- async setPermissions(options, permissions) {
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
- // My-Permissions Cache Operations
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.generateMyPermissionsCacheKey(options);
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.generateMyPermissionsCacheKey(options);
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
- // Action Code Cache Operations (tenant-aware for multi-tenant mode)
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 key = this.generateActionCodeCacheKey(tenantId);
88
- const fullMap = await this.cacheManager.get(key);
89
- if (!fullMap) {
90
- return null;
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 result = {};
93
- for (const code of codes){
94
- if (fullMap[code]) {
95
- result[code] = fullMap[code];
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
- return null;
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
- const branches = branchIds?.length ? branchIds : [
111
- null
112
- ];
113
- for (const branchId of branches){
114
- keysToDelete.push(`${this.CACHE_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`, `${this.MY_PERMISSIONS_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`);
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(keysToDelete.map((key)=>this.cacheManager.del(key)));
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
- async invalidateRole(_roleId, userIds, companyId, branchIds) {
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, "TTL", void 0); // 1 hour
135
- _define_property(this, "ACTION_CODE_TTL", void 0); // 2 hours for action codes (less frequent changes)
136
- _define_property(this, "CACHE_PREFIX", void 0);
137
- _define_property(this, "MY_PERMISSIONS_PREFIX", void 0);
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.TTL = 3600000;
141
- this.ACTION_CODE_TTL = 7200000;
142
- this.CACHE_PREFIX = 'permissions';
143
- this.MY_PERMISSIONS_PREFIX = 'my-permissions';
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);