@flusys/nestjs-shared 6.1.2 → 6.2.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.
@@ -148,7 +148,8 @@ const FILE_PERMISSIONS = {
148
148
  CREATE: 'file.create',
149
149
  READ: 'file.read',
150
150
  UPDATE: 'file.update',
151
- DELETE: 'file.delete'
151
+ DELETE: 'file.delete',
152
+ VIEW_PRIVATE_ACTION: 'file.view.private'
152
153
  };
153
154
  const FOLDER_PERMISSIONS = {
154
155
  CREATE: 'folder.create',
@@ -10,11 +10,9 @@ Object.defineProperty(exports, "PermissionGuard", {
10
10
  });
11
11
  const _common = require("@nestjs/common");
12
12
  const _core = require("@nestjs/core");
13
- const _hybridcacheclass = require("../classes/hybrid-cache.class");
14
13
  const _constants = require("../constants");
15
14
  const _permissionexception = require("../exceptions/permission.exception");
16
- const _permissioninterface = require("../interfaces/permission.interface");
17
- const _permissioncachekeyutil = require("../utils/permission-cache-key.util");
15
+ const _sharedpermissioncacheservice = require("../modules/shared-permission-cache/shared-permission-cache.service");
18
16
  function _define_property(obj, key, value) {
19
17
  if (key in obj) {
20
18
  Object.defineProperty(obj, key, {
@@ -60,16 +58,21 @@ let PermissionGuard = class PermissionGuard {
60
58
  message: 'Authentication required',
61
59
  messageKey: _constants.AUTH_MESSAGES.TOKEN_REQUIRED
62
60
  });
63
- if (!this.cache) {
61
+ if (!this.permissionCacheService?.isCacheConfigured()) {
64
62
  throw new _permissionexception.PermissionSystemUnavailableException();
65
63
  }
66
- const userPermissions = await this.getUserPermissions(user);
64
+ const permissionService = this.permissionCacheService;
65
+ const userPermissions = await permissionService.getPermissions({
66
+ userId: user.id,
67
+ companyId: user.companyId,
68
+ branchId: user.branchId
69
+ });
67
70
  if (!userPermissions || userPermissions.length === 0) {
68
71
  throw new _permissionexception.NoPermissionsFoundException();
69
72
  }
70
73
  const logicNode = this.normalizeToLogicNode(permissionConfig);
71
74
  if (!logicNode) return true;
72
- const result = this.evaluateLogicNode(logicNode, userPermissions);
75
+ const result = this.evaluateLogicNode(logicNode, userPermissions, permissionService);
73
76
  if (!result.passed) {
74
77
  throw new _permissionexception.InsufficientPermissionsException(result.missingPermissions, result.operator);
75
78
  }
@@ -119,9 +122,9 @@ let PermissionGuard = class PermissionGuard {
119
122
  // ILogicNode
120
123
  return config;
121
124
  }
122
- evaluateLogicNode(node, userPermissions) {
125
+ evaluateLogicNode(node, userPermissions, permissionService) {
123
126
  if (node.type === 'action') {
124
- const passed = this.hasPermission(userPermissions, node.actionId);
127
+ const passed = permissionService.matchPermission(userPermissions, node.actionId);
125
128
  return {
126
129
  passed,
127
130
  missingPermissions: passed ? [] : [
@@ -143,7 +146,7 @@ let PermissionGuard = class PermissionGuard {
143
146
  const results = [];
144
147
  const missingPermissions = [];
145
148
  for (const child of children){
146
- const childResult = this.evaluateLogicNode(child, userPermissions);
149
+ const childResult = this.evaluateLogicNode(child, userPermissions, permissionService);
147
150
  results.push(childResult.passed);
148
151
  if (!childResult.passed) {
149
152
  missingPermissions.push(...childResult.missingPermissions);
@@ -156,52 +159,21 @@ let PermissionGuard = class PermissionGuard {
156
159
  operator
157
160
  };
158
161
  }
159
- async getUserPermissions(user) {
160
- if (!this.cache) throw new _permissionexception.PermissionSystemUnavailableException();
161
- const cacheKey = (0, _permissioncachekeyutil.buildPermissionCacheKey)({
162
- userId: user.id,
163
- companyId: user.companyId,
164
- branchId: user.branchId
165
- }, this.config);
166
- const cached = await this.cache.get(cacheKey);
167
- if (!cached) return [];
168
- return Array.isArray(cached) ? cached : cached.backendCodes || [];
169
- }
170
- hasPermission(userPermissions, requiredPermission) {
171
- if (userPermissions.includes(requiredPermission)) return true;
172
- for (const permission of userPermissions){
173
- if (permission === '*') return true;
174
- if (permission.endsWith('.*') && requiredPermission.startsWith(permission.slice(0, -1))) {
175
- return true;
176
- }
177
- }
178
- return false;
179
- }
180
- constructor(reflector, cache, config){
162
+ constructor(reflector, permissionCacheService){
181
163
  _define_property(this, "reflector", void 0);
182
- _define_property(this, "cache", void 0);
183
- _define_property(this, "config", void 0);
164
+ _define_property(this, "permissionCacheService", void 0);
184
165
  this.reflector = reflector;
185
- this.cache = cache;
186
- this.config = {
187
- enableCompanyFeature: false,
188
- userPermissionKeyFormat: _permissioncachekeyutil.DEFAULT_USER_PERMISSION_KEY_FORMAT,
189
- companyPermissionKeyFormat: _permissioncachekeyutil.DEFAULT_COMPANY_PERMISSION_KEY_FORMAT,
190
- ...config
191
- };
166
+ this.permissionCacheService = permissionCacheService;
192
167
  }
193
168
  };
194
169
  PermissionGuard = _ts_decorate([
195
170
  (0, _common.Injectable)(),
196
171
  _ts_param(0, (0, _common.Inject)(_core.Reflector)),
197
172
  _ts_param(1, (0, _common.Optional)()),
198
- _ts_param(1, (0, _common.Inject)(_constants.CACHE_INSTANCE)),
199
- _ts_param(2, (0, _common.Optional)()),
200
- _ts_param(2, (0, _common.Inject)(_constants.PERMISSION_GUARD_CONFIG)),
173
+ _ts_param(1, (0, _common.Inject)(_sharedpermissioncacheservice.SharedPermissionCacheService)),
201
174
  _ts_metadata("design:type", Function),
202
175
  _ts_metadata("design:paramtypes", [
203
176
  typeof _core.Reflector === "undefined" ? Object : _core.Reflector,
204
- typeof _hybridcacheclass.HybridCache === "undefined" ? Object : _hybridcacheclass.HybridCache,
205
- typeof _permissioninterface.PermissionGuardConfig === "undefined" ? Object : _permissioninterface.PermissionGuardConfig
177
+ typeof _sharedpermissioncacheservice.SharedPermissionCacheService === "undefined" ? Object : _sharedpermissioncacheservice.SharedPermissionCacheService
206
178
  ])
207
179
  ], PermissionGuard);
@@ -6,6 +6,7 @@ const _common = require("@nestjs/common");
6
6
  const _core = require("@nestjs/core");
7
7
  const _constants = require("../constants");
8
8
  const _permissionexception = require("../exceptions/permission.exception");
9
+ const _sharedpermissioncacheservice = require("../modules/shared-permission-cache/shared-permission-cache.service");
9
10
  const _permissionguard = require("./permission.guard");
10
11
  function buildContext(request) {
11
12
  return {
@@ -16,9 +17,13 @@ function buildContext(request) {
16
17
  })
17
18
  };
18
19
  }
20
+ function buildPermissionService(cache, config) {
21
+ return new _sharedpermissioncacheservice.SharedPermissionCacheService(cache, config);
22
+ }
19
23
  describe('PermissionGuard', ()=>{
20
24
  let reflector;
21
25
  let cache;
26
+ let permissionCacheService;
22
27
  let getAllAndOverrideMock;
23
28
  beforeEach(()=>{
24
29
  reflector = new _core.Reflector();
@@ -27,6 +32,7 @@ describe('PermissionGuard', ()=>{
27
32
  set: jest.fn(),
28
33
  del: jest.fn()
29
34
  };
35
+ permissionCacheService = buildPermissionService(cache);
30
36
  getAllAndOverrideMock = jest.fn();
31
37
  jest.spyOn(reflector, 'getAllAndOverride').mockImplementation(getAllAndOverrideMock);
32
38
  });
@@ -40,12 +46,12 @@ describe('PermissionGuard', ()=>{
40
46
  describe('public routes and missing permission metadata', ()=>{
41
47
  it('should allow access when the route is public', async ()=>{
42
48
  mockMetadata(true, undefined);
43
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
49
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
44
50
  await expect(guard.canActivate(buildContext({}))).resolves.toBe(true);
45
51
  });
46
52
  it('should allow access when no permission metadata is set', async ()=>{
47
53
  mockMetadata(false, undefined);
48
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
54
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
49
55
  await expect(guard.canActivate(buildContext({
50
56
  user: {
51
57
  id: 'u1'
@@ -56,7 +62,7 @@ describe('PermissionGuard', ()=>{
56
62
  describe('authentication requirement', ()=>{
57
63
  it('should throw UnauthorizedException when there is no user on the request', async ()=>{
58
64
  mockMetadata(false, 'user.read');
59
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
65
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
60
66
  await expect(guard.canActivate(buildContext({}))).rejects.toMatchObject({
61
67
  constructor: _common.UnauthorizedException
62
68
  });
@@ -71,6 +77,15 @@ describe('PermissionGuard', ()=>{
71
77
  });
72
78
  describe('cache availability', ()=>{
73
79
  it('should throw PermissionSystemUnavailableException when no cache is injected', async ()=>{
80
+ mockMetadata(false, 'user.read');
81
+ const guard = new _permissionguard.PermissionGuard(reflector, buildPermissionService(undefined));
82
+ await expect(guard.canActivate(buildContext({
83
+ user: {
84
+ id: 'u1'
85
+ }
86
+ }))).rejects.toBeInstanceOf(_permissionexception.PermissionSystemUnavailableException);
87
+ });
88
+ it('should throw PermissionSystemUnavailableException when no permission service is injected', async ()=>{
74
89
  mockMetadata(false, 'user.read');
75
90
  const guard = new _permissionguard.PermissionGuard(reflector, undefined);
76
91
  await expect(guard.canActivate(buildContext({
@@ -84,7 +99,7 @@ describe('PermissionGuard', ()=>{
84
99
  it('should throw NoPermissionsFoundException when the user has no cached permissions', async ()=>{
85
100
  mockMetadata(false, 'user.read');
86
101
  cache.get.mockResolvedValue(undefined);
87
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
102
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
88
103
  await expect(guard.canActivate(buildContext({
89
104
  user: {
90
105
  id: 'u1'
@@ -94,7 +109,7 @@ describe('PermissionGuard', ()=>{
94
109
  it('should throw NoPermissionsFoundException when the cached permission array is empty', async ()=>{
95
110
  mockMetadata(false, 'user.read');
96
111
  cache.get.mockResolvedValue([]);
97
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
112
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
98
113
  await expect(guard.canActivate(buildContext({
99
114
  user: {
100
115
  id: 'u1'
@@ -106,7 +121,7 @@ describe('PermissionGuard', ()=>{
106
121
  cache.get.mockResolvedValue([
107
122
  'user.read'
108
123
  ]);
109
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
124
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
110
125
  await expect(guard.canActivate(buildContext({
111
126
  user: {
112
127
  id: 'u1'
@@ -118,7 +133,7 @@ describe('PermissionGuard', ()=>{
118
133
  cache.get.mockResolvedValue([
119
134
  'user.read'
120
135
  ]);
121
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
136
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
122
137
  await expect(guard.canActivate(buildContext({
123
138
  user: {
124
139
  id: 'u1'
@@ -130,7 +145,7 @@ describe('PermissionGuard', ()=>{
130
145
  cache.get.mockResolvedValue([
131
146
  '*'
132
147
  ]);
133
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
148
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
134
149
  await expect(guard.canActivate(buildContext({
135
150
  user: {
136
151
  id: 'u1'
@@ -142,7 +157,7 @@ describe('PermissionGuard', ()=>{
142
157
  cache.get.mockResolvedValue([
143
158
  'user.*'
144
159
  ]);
145
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
160
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
146
161
  await expect(guard.canActivate(buildContext({
147
162
  user: {
148
163
  id: 'u1'
@@ -157,7 +172,7 @@ describe('PermissionGuard', ()=>{
157
172
  cache.get.mockResolvedValue([
158
173
  'user.read'
159
174
  ]);
160
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
175
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
161
176
  await expect(guard.canActivate(buildContext({
162
177
  user: {
163
178
  id: 'u1'
@@ -173,7 +188,7 @@ describe('PermissionGuard', ()=>{
173
188
  'user.read',
174
189
  'user.write'
175
190
  ]);
176
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
191
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
177
192
  await expect(guard.canActivate(buildContext({
178
193
  user: {
179
194
  id: 'u1'
@@ -185,7 +200,7 @@ describe('PermissionGuard', ()=>{
185
200
  cache.get.mockResolvedValue([
186
201
  'user.read'
187
202
  ]);
188
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
203
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
189
204
  await expect(guard.canActivate(buildContext({
190
205
  user: {
191
206
  id: 'u1'
@@ -203,7 +218,7 @@ describe('PermissionGuard', ()=>{
203
218
  cache.get.mockResolvedValue([
204
219
  'manager'
205
220
  ]);
206
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
221
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
207
222
  await expect(guard.canActivate(buildContext({
208
223
  user: {
209
224
  id: 'u1'
@@ -221,7 +236,7 @@ describe('PermissionGuard', ()=>{
221
236
  cache.get.mockResolvedValue([
222
237
  'guest'
223
238
  ]);
224
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
239
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
225
240
  await expect(guard.canActivate(buildContext({
226
241
  user: {
227
242
  id: 'u1'
@@ -236,7 +251,7 @@ describe('PermissionGuard', ()=>{
236
251
  cache.get.mockResolvedValue([
237
252
  'user.read'
238
253
  ]);
239
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
254
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
240
255
  await expect(guard.canActivate(buildContext({
241
256
  user: {
242
257
  id: 'u1'
@@ -272,7 +287,7 @@ describe('PermissionGuard', ()=>{
272
287
  'users.read',
273
288
  'manager'
274
289
  ]);
275
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
290
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
276
291
  await expect(guard.canActivate(buildContext({
277
292
  user: {
278
293
  id: 'u1'
@@ -288,7 +303,7 @@ describe('PermissionGuard', ()=>{
288
303
  cache.get.mockResolvedValue([
289
304
  'user.read'
290
305
  ]);
291
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
306
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
292
307
  await expect(guard.canActivate(buildContext({
293
308
  user: {
294
309
  id: 'u1'
@@ -302,7 +317,7 @@ describe('PermissionGuard', ()=>{
302
317
  'user.read'
303
318
  ]
304
319
  });
305
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
320
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
306
321
  await expect(guard.canActivate(buildContext({
307
322
  user: {
308
323
  id: 'u1'
@@ -312,7 +327,7 @@ describe('PermissionGuard', ()=>{
312
327
  it('should treat an object-shaped cache value with no backendCodes as no permissions', async ()=>{
313
328
  mockMetadata(false, 'user.read');
314
329
  cache.get.mockResolvedValue({});
315
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
330
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
316
331
  await expect(guard.canActivate(buildContext({
317
332
  user: {
318
333
  id: 'u1'
@@ -326,9 +341,9 @@ describe('PermissionGuard', ()=>{
326
341
  cache.get.mockResolvedValue([
327
342
  'user.read'
328
343
  ]);
329
- const guard = new _permissionguard.PermissionGuard(reflector, cache, {
344
+ const guard = new _permissionguard.PermissionGuard(reflector, buildPermissionService(cache, {
330
345
  enableCompanyFeature: true
331
- });
346
+ }));
332
347
  await guard.canActivate(buildContext({
333
348
  user: {
334
349
  id: 'u1',
@@ -343,7 +358,7 @@ describe('PermissionGuard', ()=>{
343
358
  cache.get.mockResolvedValue([
344
359
  'user.read'
345
360
  ]);
346
- const guard = new _permissionguard.PermissionGuard(reflector, cache);
361
+ const guard = new _permissionguard.PermissionGuard(reflector, permissionCacheService);
347
362
  await guard.canActivate(buildContext({
348
363
  user: {
349
364
  id: 'u1',
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
4
4
  });
5
5
  _export_star(require("./cache/cache.module"), exports);
6
6
  _export_star(require("./datasource"), exports);
7
+ _export_star(require("./shared-permission-cache/shared-permission-cache.module"), exports);
8
+ _export_star(require("./shared-permission-cache/shared-permission-cache.service"), exports);
7
9
  _export_star(require("./utils/utils.module"), exports);
8
10
  _export_star(require("./utils/utils.service"), exports);
9
11
  function _export_star(from, to) {
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "SharedPermissionCacheModule", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return SharedPermissionCacheModule;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ const _sharedpermissioncacheservice = require("./shared-permission-cache.service");
13
+ function _ts_decorate(decorators, target, key, desc) {
14
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
15
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
16
+ 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;
17
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
18
+ }
19
+ let SharedPermissionCacheModule = class SharedPermissionCacheModule {
20
+ };
21
+ SharedPermissionCacheModule = _ts_decorate([
22
+ (0, _common.Global)(),
23
+ (0, _common.Module)({
24
+ imports: [],
25
+ providers: [
26
+ _sharedpermissioncacheservice.SharedPermissionCacheService
27
+ ],
28
+ exports: [
29
+ _sharedpermissioncacheservice.SharedPermissionCacheService
30
+ ]
31
+ })
32
+ ], SharedPermissionCacheModule);
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "SharedPermissionCacheService", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return SharedPermissionCacheService;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ const _hybridcacheclass = require("../../classes/hybrid-cache.class");
13
+ const _constants = require("../../constants");
14
+ const _interfaces = require("../../interfaces");
15
+ const _permissioncachekeyutil = require("../../utils/permission-cache-key.util");
16
+ function _define_property(obj, key, value) {
17
+ if (key in obj) {
18
+ Object.defineProperty(obj, key, {
19
+ value: value,
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true
23
+ });
24
+ } else {
25
+ obj[key] = value;
26
+ }
27
+ return obj;
28
+ }
29
+ function _ts_decorate(decorators, target, key, desc) {
30
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
32
+ 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;
33
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
34
+ }
35
+ function _ts_metadata(k, v) {
36
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
37
+ }
38
+ function _ts_param(paramIndex, decorator) {
39
+ return function(target, key) {
40
+ decorator(target, key, paramIndex);
41
+ };
42
+ }
43
+ let SharedPermissionCacheService = class SharedPermissionCacheService {
44
+ /** Read the cached permission codes for a user; empty array when cache is unavailable or unset */ async getPermissions(options) {
45
+ if (!this.cache) return [];
46
+ const cacheKey = (0, _permissioncachekeyutil.buildPermissionCacheKey)(options, this.config);
47
+ const cached = await this.cache.get(cacheKey);
48
+ if (!cached) return [];
49
+ return Array.isArray(cached) ? cached : cached.backendCodes || [];
50
+ }
51
+ /** Overwrite the cached permission codes for a user with a fixed list */ async setPermissions(options, permissions, ttl) {
52
+ if (!this.cache) return;
53
+ const cacheKey = (0, _permissioncachekeyutil.buildPermissionCacheKey)(options, this.config);
54
+ await this.cache.set(cacheKey, permissions, ttl);
55
+ }
56
+ /** Clear the cached permission codes for a user */ async clearPermissions(options) {
57
+ if (!this.cache) return;
58
+ const cacheKey = (0, _permissioncachekeyutil.buildPermissionCacheKey)(options, this.config);
59
+ await this.cache.del(cacheKey);
60
+ }
61
+ /** Check a single permission against the cached list, honoring `*` and `prefix.*` wildcards */ async hasPermission(options, requiredPermission) {
62
+ const permissions = await this.getPermissions(options);
63
+ return this.matchPermission(permissions, requiredPermission);
64
+ }
65
+ /** Whether a cache instance is wired up; false means permission checks can't be served at all */ isCacheConfigured() {
66
+ return !!this.cache;
67
+ }
68
+ /** Match an already-fetched permission list against a required permission, honoring `*` and `prefix.*` wildcards */ matchPermission(userPermissions, requiredPermission) {
69
+ if (userPermissions.includes(requiredPermission)) return true;
70
+ for (const permission of userPermissions){
71
+ if (permission === '*') return true;
72
+ if (permission.endsWith('.*') && requiredPermission.startsWith(permission.slice(0, -1))) {
73
+ return true;
74
+ }
75
+ }
76
+ return false;
77
+ }
78
+ constructor(cache, config){
79
+ _define_property(this, "cache", void 0);
80
+ _define_property(this, "config", void 0);
81
+ this.cache = cache;
82
+ this.config = {
83
+ enableCompanyFeature: false,
84
+ userPermissionKeyFormat: _permissioncachekeyutil.DEFAULT_USER_PERMISSION_KEY_FORMAT,
85
+ companyPermissionKeyFormat: _permissioncachekeyutil.DEFAULT_COMPANY_PERMISSION_KEY_FORMAT,
86
+ ...config
87
+ };
88
+ }
89
+ };
90
+ SharedPermissionCacheService = _ts_decorate([
91
+ (0, _common.Injectable)(),
92
+ _ts_param(0, (0, _common.Optional)()),
93
+ _ts_param(0, (0, _common.Inject)(_constants.CACHE_INSTANCE)),
94
+ _ts_param(1, (0, _common.Optional)()),
95
+ _ts_param(1, (0, _common.Inject)(_constants.PERMISSION_GUARD_CONFIG)),
96
+ _ts_metadata("design:type", Function),
97
+ _ts_metadata("design:paramtypes", [
98
+ typeof _hybridcacheclass.HybridCache === "undefined" ? Object : _hybridcacheclass.HybridCache,
99
+ typeof _interfaces.PermissionGuardConfig === "undefined" ? Object : _interfaces.PermissionGuardConfig
100
+ ])
101
+ ], SharedPermissionCacheService);
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ const _sharedpermissioncacheservice = require("./shared-permission-cache.service");
6
+ describe('SharedPermissionCacheService', ()=>{
7
+ let service;
8
+ let mockCache;
9
+ beforeEach(()=>{
10
+ mockCache = {
11
+ get: jest.fn(),
12
+ set: jest.fn(),
13
+ del: jest.fn()
14
+ };
15
+ service = new _sharedpermissioncacheservice.SharedPermissionCacheService(mockCache);
16
+ });
17
+ describe('getPermissions', ()=>{
18
+ it('returns an empty array when nothing is cached', async ()=>{
19
+ mockCache.get.mockResolvedValue(undefined);
20
+ const permissions = await service.getPermissions({
21
+ userId: 'user-1'
22
+ });
23
+ expect(permissions).toEqual([]);
24
+ expect(mockCache.get).toHaveBeenCalledWith('my-permissions:user:user-1');
25
+ });
26
+ it('returns the cached array as-is', async ()=>{
27
+ mockCache.get.mockResolvedValue([
28
+ 'file.read',
29
+ 'file.update'
30
+ ]);
31
+ const permissions = await service.getPermissions({
32
+ userId: 'user-1'
33
+ });
34
+ expect(permissions).toEqual([
35
+ 'file.read',
36
+ 'file.update'
37
+ ]);
38
+ });
39
+ it('unwraps a { backendCodes } shaped cache value', async ()=>{
40
+ mockCache.get.mockResolvedValue({
41
+ backendCodes: [
42
+ 'file.read'
43
+ ]
44
+ });
45
+ const permissions = await service.getPermissions({
46
+ userId: 'user-1'
47
+ });
48
+ expect(permissions).toEqual([
49
+ 'file.read'
50
+ ]);
51
+ });
52
+ it('scopes the cache key by company and branch when enableCompanyFeature is on', async ()=>{
53
+ const scopedService = new _sharedpermissioncacheservice.SharedPermissionCacheService(mockCache, {
54
+ enableCompanyFeature: true
55
+ });
56
+ mockCache.get.mockResolvedValue(undefined);
57
+ await scopedService.getPermissions({
58
+ userId: 'user-1',
59
+ companyId: 'company-1',
60
+ branchId: 'branch-1'
61
+ });
62
+ expect(mockCache.get).toHaveBeenCalledWith('my-permissions:company:company-1:branch:branch-1:user:user-1');
63
+ });
64
+ it('returns an empty array when no cache instance is configured', async ()=>{
65
+ const noCacheService = new _sharedpermissioncacheservice.SharedPermissionCacheService();
66
+ await expect(noCacheService.getPermissions({
67
+ userId: 'user-1'
68
+ })).resolves.toEqual([]);
69
+ });
70
+ });
71
+ describe('setPermissions', ()=>{
72
+ it('writes the fixed permission list under the user cache key', async ()=>{
73
+ await service.setPermissions({
74
+ userId: 'user-1'
75
+ }, [
76
+ 'file.read',
77
+ 'file.update'
78
+ ]);
79
+ expect(mockCache.set).toHaveBeenCalledWith('my-permissions:user:user-1', [
80
+ 'file.read',
81
+ 'file.update'
82
+ ], undefined);
83
+ });
84
+ it('forwards a ttl when given', async ()=>{
85
+ await service.setPermissions({
86
+ userId: 'user-1'
87
+ }, [
88
+ 'file.read'
89
+ ], 60);
90
+ expect(mockCache.set).toHaveBeenCalledWith('my-permissions:user:user-1', [
91
+ 'file.read'
92
+ ], 60);
93
+ });
94
+ it('does nothing when no cache instance is configured', async ()=>{
95
+ const noCacheService = new _sharedpermissioncacheservice.SharedPermissionCacheService();
96
+ await expect(noCacheService.setPermissions({
97
+ userId: 'user-1'
98
+ }, [
99
+ 'file.read'
100
+ ])).resolves.toBeUndefined();
101
+ });
102
+ });
103
+ describe('clearPermissions', ()=>{
104
+ it('deletes the cache key for the user', async ()=>{
105
+ await service.clearPermissions({
106
+ userId: 'user-1'
107
+ });
108
+ expect(mockCache.del).toHaveBeenCalledWith('my-permissions:user:user-1');
109
+ });
110
+ });
111
+ describe('hasPermission', ()=>{
112
+ it('returns true when the exact permission is cached', async ()=>{
113
+ mockCache.get.mockResolvedValue([
114
+ 'file.view.private'
115
+ ]);
116
+ await expect(service.hasPermission({
117
+ userId: 'user-1'
118
+ }, 'file.view.private')).resolves.toBe(true);
119
+ });
120
+ it('returns false when the permission is missing', async ()=>{
121
+ mockCache.get.mockResolvedValue([
122
+ 'file.read'
123
+ ]);
124
+ await expect(service.hasPermission({
125
+ userId: 'user-1'
126
+ }, 'file.view.private')).resolves.toBe(false);
127
+ });
128
+ it('honors the global "*" wildcard', async ()=>{
129
+ mockCache.get.mockResolvedValue([
130
+ '*'
131
+ ]);
132
+ await expect(service.hasPermission({
133
+ userId: 'user-1'
134
+ }, 'file.view.private')).resolves.toBe(true);
135
+ });
136
+ it('honors a "prefix.*" wildcard', async ()=>{
137
+ mockCache.get.mockResolvedValue([
138
+ 'file.*'
139
+ ]);
140
+ await expect(service.hasPermission({
141
+ userId: 'user-1'
142
+ }, 'file.view.private')).resolves.toBe(true);
143
+ });
144
+ });
145
+ describe('matchPermission', ()=>{
146
+ it('matches an already-fetched list without touching the cache', ()=>{
147
+ expect(service.matchPermission([
148
+ 'file.read'
149
+ ], 'file.read')).toBe(true);
150
+ expect(service.matchPermission([
151
+ 'file.read'
152
+ ], 'file.write')).toBe(false);
153
+ expect(mockCache.get).not.toHaveBeenCalled();
154
+ });
155
+ });
156
+ describe('isCacheConfigured', ()=>{
157
+ it('returns true when a cache instance is injected', ()=>{
158
+ expect(service.isCacheConfigured()).toBe(true);
159
+ });
160
+ it('returns false when no cache instance is injected', ()=>{
161
+ const noCacheService = new _sharedpermissioncacheservice.SharedPermissionCacheService();
162
+ expect(noCacheService.isCacheConfigured()).toBe(false);
163
+ });
164
+ });
165
+ });