@flusys/nestjs-shared 9.1.1 → 9.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.
- package/README.md +119 -18
- package/classes/api-controller.class.d.ts +1 -0
- package/classes/api-service.class.d.ts +7 -2
- package/classes/hybrid-cache.class.d.ts +17 -4
- package/constants/index.d.ts +1 -0
- package/constants/permissions.d.ts +9 -0
- package/fesm/183.js +7 -7
- package/fesm/47.js +1520 -0
- package/fesm/470.js +333 -382
- package/fesm/667.js +17 -17
- package/fesm/681.js +326 -129
- package/fesm/{948.js → 997.js} +45 -23
- package/fesm/chunks/123.js +17 -9
- package/fesm/chunks/37.js +5 -5
- package/fesm/classes/index.js +4 -3
- package/fesm/constants/index.js +18 -7
- package/fesm/decorators/index.js +120 -39
- package/fesm/dtos/index.js +1 -1
- package/fesm/entities/index.js +8 -8
- package/fesm/enums/index.js +2 -2
- package/fesm/exceptions/index.js +12 -12
- package/fesm/guards/index.js +201 -68
- package/fesm/index.js +95 -42
- package/fesm/interceptors/index.js +290 -590
- package/fesm/interfaces/index.js +97 -8
- package/fesm/middlewares/index.js +4 -4
- package/fesm/modules/index.js +2 -2
- package/fesm/pipes/index.js +8 -8
- package/fesm/utils/index.js +150 -478
- package/interceptors/idempotency.interceptor.d.ts +14 -2
- package/interceptors/slug.interceptor.d.ts +1 -0
- package/interfaces/expression.interface.d.ts +45 -0
- package/interfaces/index.d.ts +1 -0
- package/interfaces/permission-action-registry.interface.d.ts +1 -0
- package/interfaces/permission.interface.d.ts +1 -0
- package/modules/shared-permission-cache/shared-permission-cache.service.d.ts +3 -1
- package/modules/utils/utils.service.d.ts +6 -6
- package/package.json +3 -2
- package/utils/date-time.util.d.ts +0 -1
- package/utils/event-pattern.util.d.ts +1 -0
- package/utils/expression.util.d.ts +35 -0
- package/utils/index.d.ts +2 -0
- package/utils/permission-cache-key.util.d.ts +3 -0
- package/utils/query-helpers.util.d.ts +2 -2
- package/utils/safe-regex.util.d.ts +10 -0
- package/fesm/270.js +0 -659
package/fesm/guards/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import * as __rspack_external_keyv from "keyv";
|
|
|
6
6
|
export const __rspack_esm_id = 333;
|
|
7
7
|
export const __rspack_esm_ids = [333];
|
|
8
8
|
export const __webpack_modules__ = {
|
|
9
|
-
|
|
9
|
+
5375(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
10
10
|
|
|
11
11
|
// EXPORTS
|
|
12
12
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -14,25 +14,41 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
// EXTERNAL MODULE: external "@flusys/nestjs-core/config"
|
|
17
|
-
var config_ = __webpack_require__(
|
|
17
|
+
var config_ = __webpack_require__(8307);
|
|
18
18
|
;// CONCATENATED MODULE: external "@keyv/redis"
|
|
19
19
|
|
|
20
20
|
;// CONCATENATED MODULE: external "cacheable"
|
|
21
21
|
|
|
22
22
|
;// CONCATENATED MODULE: external "keyv"
|
|
23
23
|
|
|
24
|
+
// EXTERNAL MODULE: external "node:crypto"
|
|
25
|
+
var external_node_crypto_ = __webpack_require__(2561);
|
|
24
26
|
;// CONCATENATED MODULE: ./projects/nestjs-shared/src/classes/hybrid-cache.class.ts
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
|
|
32
|
+
const RAW_KEY_PREFIX = 'flusys';
|
|
33
|
+
const STAMP_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
34
|
+
/**
|
|
35
|
+
* L1 in-process memory + L2 Redis, chosen by `USE_CACHE_LABEL` (memory | redis | hybrid).
|
|
36
|
+
*
|
|
37
|
+
* Plain `get`/`set` may be served by L1, which other instances cannot invalidate, so a value
|
|
38
|
+
* read that way must be immutable for its key - which is what version stamps give: data keys
|
|
39
|
+
* embed the current stamp, and invalidation moves the stamp instead of deleting entries.
|
|
40
|
+
* Stamps and claims live outside Keyv so they can be set atomically (Redis `SET NX`).
|
|
41
|
+
*/ class HybridCache {
|
|
42
|
+
mode;
|
|
43
|
+
defaultTtl;
|
|
30
44
|
memory;
|
|
31
45
|
redis;
|
|
46
|
+
redisStore;
|
|
47
|
+
localAtomic;
|
|
32
48
|
constructor(memoryTtl = 60 * 1000, memorySize = 5000){
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
49
|
+
this.mode = config_.envConfig.tryGetValue('USE_CACHE_LABEL', false) || 'memory';
|
|
50
|
+
this.defaultTtl = memoryTtl;
|
|
51
|
+
if (this.mode === 'memory' || this.mode === 'hybrid') {
|
|
36
52
|
this.memory = new __rspack_external_keyv["default"]({
|
|
37
53
|
store: new __rspack_external_cacheable.CacheableMemory({
|
|
38
54
|
ttl: memoryTtl,
|
|
@@ -40,35 +56,51 @@ class HybridCache {
|
|
|
40
56
|
})
|
|
41
57
|
});
|
|
42
58
|
}
|
|
43
|
-
if (
|
|
59
|
+
if (this.mode === 'redis' || this.mode === 'hybrid') {
|
|
60
|
+
this.redisStore = new __rspack_external__keyv_redis_139d03ca["default"](config_.envConfig.getRedisUrl());
|
|
44
61
|
this.redis = new __rspack_external_keyv["default"]({
|
|
45
|
-
store:
|
|
62
|
+
store: this.redisStore
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
// Stamps and claims must never be LRU-evicted while still referenced
|
|
66
|
+
this.localAtomic = new __rspack_external_cacheable.CacheableMemory({
|
|
67
|
+
ttl: STAMP_TTL_MS,
|
|
68
|
+
lruSize: 0,
|
|
69
|
+
checkInterval: 60_000
|
|
46
70
|
});
|
|
47
71
|
}
|
|
48
72
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
73
|
+
getMode() {
|
|
74
|
+
return this.mode;
|
|
75
|
+
}
|
|
76
|
+
async get(key, options = {}) {
|
|
77
|
+
const useMemory = this.memory && !(options.skipMemory && this.redis);
|
|
78
|
+
if (useMemory) {
|
|
52
79
|
const value = await this.memory.get(key);
|
|
53
80
|
if (value !== undefined) return value;
|
|
54
81
|
}
|
|
55
|
-
// Fallback to redis
|
|
56
82
|
if (this.redis) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
const raw = await this.redis.get(key, {
|
|
84
|
+
raw: true
|
|
85
|
+
});
|
|
86
|
+
if (raw === undefined) return undefined;
|
|
87
|
+
if (useMemory) {
|
|
88
|
+
// Never let the L1 copy outlive the Redis entry (time-bound values such as permissions)
|
|
89
|
+
const remaining = raw.expires ? raw.expires - Date.now() : this.defaultTtl;
|
|
90
|
+
if (remaining > 0) await this.memory.set(key, raw.value, Math.min(remaining, this.defaultTtl));
|
|
61
91
|
}
|
|
62
|
-
return value;
|
|
92
|
+
return raw.value;
|
|
63
93
|
}
|
|
64
94
|
return undefined;
|
|
65
95
|
}
|
|
66
|
-
async set(key, value, ttl) {
|
|
67
|
-
|
|
68
|
-
|
|
96
|
+
async set(key, value, ttl, options = {}) {
|
|
97
|
+
const effectiveTtl = ttl ?? this.defaultTtl;
|
|
98
|
+
const useMemory = this.memory && !(options.skipMemory && this.redis);
|
|
99
|
+
if (useMemory) {
|
|
100
|
+
await this.memory.set(key, value, effectiveTtl);
|
|
69
101
|
}
|
|
70
102
|
if (this.redis) {
|
|
71
|
-
await this.redis.set(key, value,
|
|
103
|
+
await this.redis.set(key, value, effectiveTtl);
|
|
72
104
|
}
|
|
73
105
|
}
|
|
74
106
|
async del(key) {
|
|
@@ -79,23 +111,88 @@ class HybridCache {
|
|
|
79
111
|
await this.redis.delete(key);
|
|
80
112
|
}
|
|
81
113
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
114
|
+
/**
|
|
115
|
+
* The current version stamp for `name`, creating one when absent. A missing stamp is always
|
|
116
|
+
* replaced by a fresh random one - never a fixed default - so entries cached under a stamp that
|
|
117
|
+
* expired or was lost can never become current again.
|
|
118
|
+
*/ async getStamp(name) {
|
|
119
|
+
const key = this.rawKey('stamp', name);
|
|
120
|
+
if (this.localAtomic) {
|
|
121
|
+
let stamp = this.localAtomic.get(key);
|
|
122
|
+
if (!stamp) {
|
|
123
|
+
stamp = (0,external_node_crypto_.randomUUID)();
|
|
124
|
+
this.localAtomic.set(key, stamp);
|
|
125
|
+
}
|
|
126
|
+
return stamp;
|
|
85
127
|
}
|
|
128
|
+
const client = await this.redisStore.getClient();
|
|
129
|
+
const existing = await client.get(key);
|
|
130
|
+
if (existing) return String(existing);
|
|
131
|
+
const candidate = (0,external_node_crypto_.randomUUID)();
|
|
132
|
+
await client.set(key, candidate, {
|
|
133
|
+
NX: true,
|
|
134
|
+
PX: STAMP_TTL_MS
|
|
135
|
+
});
|
|
136
|
+
// A key evicted between SET NX and GET must not turn into the fixed stamp "null"
|
|
137
|
+
return String(await client.get(key) ?? candidate);
|
|
138
|
+
}
|
|
139
|
+
/** Moves the stamp for `name`, orphaning every entry cached under the previous one on all instances */ async bumpStamp(name) {
|
|
140
|
+
const key = this.rawKey('stamp', name);
|
|
141
|
+
if (this.localAtomic) {
|
|
142
|
+
this.localAtomic.set(key, (0,external_node_crypto_.randomUUID)());
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const client = await this.redisStore.getClient();
|
|
146
|
+
await client.set(key, (0,external_node_crypto_.randomUUID)(), {
|
|
147
|
+
PX: STAMP_TTL_MS
|
|
148
|
+
});
|
|
86
149
|
}
|
|
87
|
-
async
|
|
88
|
-
|
|
89
|
-
|
|
150
|
+
/** Atomically claims `name` for `ttl` ms; true only for the first caller while the claim lives */ async claim(name, ttl) {
|
|
151
|
+
const key = this.rawKey('claim', name);
|
|
152
|
+
if (this.localAtomic) {
|
|
153
|
+
if (this.localAtomic.has(key)) return false;
|
|
154
|
+
this.localAtomic.set(key, true, ttl);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
const client = await this.redisStore.getClient();
|
|
158
|
+
return await client.set(key, '1', {
|
|
159
|
+
NX: true,
|
|
160
|
+
PX: ttl
|
|
161
|
+
}) !== null;
|
|
162
|
+
}
|
|
163
|
+
async releaseClaim(name) {
|
|
164
|
+
const key = this.rawKey('claim', name);
|
|
165
|
+
if (this.localAtomic) {
|
|
166
|
+
this.localAtomic.delete(key);
|
|
167
|
+
return;
|
|
90
168
|
}
|
|
169
|
+
const client = await this.redisStore.getClient();
|
|
170
|
+
await client.del(key);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Reads and removes a one-time value; only one caller across all instances ever receives it.
|
|
174
|
+
* The claim is per value, so a fresh value later stored under the same key (a resent code)
|
|
175
|
+
* can be taken again while the claim on the consumed one is still alive.
|
|
176
|
+
*/ async take(key, ttl) {
|
|
177
|
+
const value = await this.get(key, {
|
|
178
|
+
skipMemory: true
|
|
179
|
+
});
|
|
180
|
+
if (value === undefined) return undefined;
|
|
181
|
+
const digest = (0,external_node_crypto_.createHash)('sha256').update(JSON.stringify(value) ?? '').digest('hex');
|
|
182
|
+
if (!await this.claim(`take:${key}:${digest}`, ttl)) return undefined;
|
|
183
|
+
await this.del(key);
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
rawKey(kind, name) {
|
|
187
|
+
return `${RAW_KEY_PREFIX}:${kind}:${name}`;
|
|
91
188
|
}
|
|
92
189
|
}
|
|
93
190
|
|
|
94
191
|
|
|
95
192
|
},
|
|
96
|
-
|
|
97
|
-
/* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(
|
|
98
|
-
/* import */ var _constants_message_keys_js__rspack_import_1 = __webpack_require__(
|
|
193
|
+
8374(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
194
|
+
/* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
|
|
195
|
+
/* import */ var _constants_message_keys_js__rspack_import_1 = __webpack_require__(4389);
|
|
99
196
|
|
|
100
197
|
|
|
101
198
|
class PermissionSystemUnavailableException extends _nestjs_common__rspack_import_0.InternalServerErrorException {
|
|
@@ -140,7 +237,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
140
237
|
|
|
141
238
|
|
|
142
239
|
},
|
|
143
|
-
|
|
240
|
+
3510(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
144
241
|
|
|
145
242
|
// EXPORTS
|
|
146
243
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -150,13 +247,13 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
150
247
|
});
|
|
151
248
|
|
|
152
249
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/index.ts
|
|
153
|
-
var constants = __webpack_require__(
|
|
154
|
-
// EXTERNAL MODULE: ./projects/nestjs-shared/src/middlewares/logger.middleware.ts +
|
|
155
|
-
var logger_middleware = __webpack_require__(
|
|
250
|
+
var constants = __webpack_require__(6043);
|
|
251
|
+
// EXTERNAL MODULE: ./projects/nestjs-shared/src/middlewares/logger.middleware.ts + 1 modules
|
|
252
|
+
var logger_middleware = __webpack_require__(8244);
|
|
156
253
|
// EXTERNAL MODULE: external "@nestjs/common"
|
|
157
|
-
var common_ = __webpack_require__(
|
|
254
|
+
var common_ = __webpack_require__(6157);
|
|
158
255
|
// EXTERNAL MODULE: external "@nestjs/core"
|
|
159
|
-
var core_ = __webpack_require__(
|
|
256
|
+
var core_ = __webpack_require__(3075);
|
|
160
257
|
;// CONCATENATED MODULE: external "@nestjs/passport"
|
|
161
258
|
|
|
162
259
|
;// CONCATENATED MODULE: ./projects/nestjs-shared/src/guards/jwt-auth.guard.ts
|
|
@@ -284,9 +381,9 @@ OptionalJwtGuard = optional_jwt_auth_guard_ts_decorate([
|
|
|
284
381
|
], OptionalJwtGuard);
|
|
285
382
|
|
|
286
383
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/exceptions/permission.exception.ts
|
|
287
|
-
var permission_exception = __webpack_require__(
|
|
384
|
+
var permission_exception = __webpack_require__(8374);
|
|
288
385
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/modules/shared-permission-cache/shared-permission-cache.service.ts
|
|
289
|
-
var shared_permission_cache_service = __webpack_require__(
|
|
386
|
+
var shared_permission_cache_service = __webpack_require__(4657);
|
|
290
387
|
// EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/permission-match.util.ts
|
|
291
388
|
var permission_match_util = __webpack_require__(788);
|
|
292
389
|
;// CONCATENATED MODULE: ./projects/nestjs-shared/src/guards/permission.guard.ts
|
|
@@ -336,13 +433,15 @@ class PermissionGuard {
|
|
|
336
433
|
context.getHandler(),
|
|
337
434
|
context.getClass()
|
|
338
435
|
]);
|
|
339
|
-
if (
|
|
436
|
+
if (permissionConfig === undefined || permissionConfig === null) return true;
|
|
340
437
|
const request = context.switchToHttp().getRequest();
|
|
341
438
|
const user = request.user;
|
|
342
439
|
if (!user) throw new common_.UnauthorizedException({
|
|
343
440
|
message: 'Authentication required',
|
|
344
441
|
messageKey: constants/* .AUTH_MESSAGES.TOKEN_REQUIRED */.mK.TOKEN_REQUIRED
|
|
345
442
|
});
|
|
443
|
+
const logicNode = this.normalizeToLogicNode(permissionConfig);
|
|
444
|
+
if (!logicNode) throw new permission_exception/* .InsufficientPermissionsException */.z8([], 'AND');
|
|
346
445
|
if (!this.permissionCacheService?.isCacheConfigured()) {
|
|
347
446
|
throw new permission_exception/* .PermissionSystemUnavailableException */.GS();
|
|
348
447
|
}
|
|
@@ -354,8 +453,6 @@ class PermissionGuard {
|
|
|
354
453
|
if (!userPermissions || userPermissions.length === 0) {
|
|
355
454
|
throw new permission_exception/* .NoPermissionsFoundException */.d$();
|
|
356
455
|
}
|
|
357
|
-
const logicNode = this.normalizeToLogicNode(permissionConfig);
|
|
358
|
-
if (!logicNode) return true;
|
|
359
456
|
const result = (0,permission_match_util/* .evaluatePermissionLogic */.Ng)(logicNode, userPermissions);
|
|
360
457
|
if (!result.passed) {
|
|
361
458
|
throw new permission_exception/* .InsufficientPermissionsException */.z8(result.missingPermissions, result.operator);
|
|
@@ -363,14 +460,12 @@ class PermissionGuard {
|
|
|
363
460
|
return true;
|
|
364
461
|
}
|
|
365
462
|
normalizeToLogicNode(config) {
|
|
366
|
-
// string - single permission
|
|
367
463
|
if (typeof config === 'string') {
|
|
368
464
|
return config ? {
|
|
369
465
|
type: 'action',
|
|
370
466
|
actionId: config
|
|
371
467
|
} : null;
|
|
372
468
|
}
|
|
373
|
-
// string[] - treat as AND of multiple permissions
|
|
374
469
|
if (Array.isArray(config)) {
|
|
375
470
|
if (config.length === 0) return null;
|
|
376
471
|
if (config.length === 1) return {
|
|
@@ -386,7 +481,6 @@ class PermissionGuard {
|
|
|
386
481
|
}))
|
|
387
482
|
};
|
|
388
483
|
}
|
|
389
|
-
// SimplePermissionConfig - { permissions: string[], operator: 'AND'|'OR' }
|
|
390
484
|
if ('permissions' in config && Array.isArray(config.permissions)) {
|
|
391
485
|
const simple = config;
|
|
392
486
|
if (simple.permissions.length === 0) return null;
|
|
@@ -403,7 +497,6 @@ class PermissionGuard {
|
|
|
403
497
|
}))
|
|
404
498
|
};
|
|
405
499
|
}
|
|
406
|
-
// ILogicNode
|
|
407
500
|
return config;
|
|
408
501
|
}
|
|
409
502
|
}
|
|
@@ -426,12 +519,23 @@ PermissionGuard = permission_guard_ts_decorate([
|
|
|
426
519
|
|
|
427
520
|
|
|
428
521
|
},
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
522
|
+
1647(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
523
|
+
const PERMISSION_RESOLVER = 'PERMISSION_RESOLVER';
|
|
524
|
+
|
|
525
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
526
|
+
}, {
|
|
527
|
+
j: PERMISSION_RESOLVER
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
},
|
|
532
|
+
4657(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
533
|
+
/* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
|
|
534
|
+
/* import */ var _classes_hybrid_cache_class_js__rspack_import_1 = __webpack_require__(5375);
|
|
535
|
+
/* import */ var _constants_index_js__rspack_import_2 = __webpack_require__(6043);
|
|
536
|
+
/* import */ var _exceptions_permission_exception_js__rspack_import_3 = __webpack_require__(8374);
|
|
537
|
+
/* import */ var _interfaces_permission_resolver_interface_js__rspack_import_6 = __webpack_require__(1647);
|
|
538
|
+
/* import */ var _utils_permission_cache_key_util_js__rspack_import_4 = __webpack_require__(3199);
|
|
435
539
|
/* import */ var _utils_permission_match_util_js__rspack_import_5 = __webpack_require__(788);
|
|
436
540
|
function _ts_decorate(decorators, target, key, desc) {
|
|
437
541
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -462,11 +566,14 @@ function _ts_param(paramIndex, decorator) {
|
|
|
462
566
|
|
|
463
567
|
|
|
464
568
|
|
|
569
|
+
|
|
465
570
|
class SharedPermissionCacheService {
|
|
466
571
|
cache;
|
|
572
|
+
resolver;
|
|
467
573
|
config;
|
|
468
|
-
constructor(cache, config){
|
|
574
|
+
constructor(cache, config, resolver){
|
|
469
575
|
this.cache = cache;
|
|
576
|
+
this.resolver = resolver;
|
|
470
577
|
this.config = {
|
|
471
578
|
enableCompanyFeature: false,
|
|
472
579
|
userPermissionKeyFormat: _utils_permission_cache_key_util_js__rspack_import_4/* .DEFAULT_USER_PERMISSION_KEY_FORMAT */.q6,
|
|
@@ -474,22 +581,21 @@ class SharedPermissionCacheService {
|
|
|
474
581
|
...config
|
|
475
582
|
};
|
|
476
583
|
}
|
|
477
|
-
/**
|
|
584
|
+
/** The user's permission codes in a scope: cached, else rebuilt by the resolver; empty when neither has them */ async getPermissions(options) {
|
|
478
585
|
if (!this.cache) return [];
|
|
479
|
-
const cacheKey = (0,_utils_permission_cache_key_util_js__rspack_import_4/* .
|
|
586
|
+
const cacheKey = await (0,_utils_permission_cache_key_util_js__rspack_import_4/* .resolvePermissionCacheKey */.Rw)(this.cache, options, this.config);
|
|
480
587
|
const cached = await this.cache.get(cacheKey);
|
|
481
|
-
if (
|
|
482
|
-
return
|
|
588
|
+
if (cached) return Array.isArray(cached) ? cached : cached.backendCodes || [];
|
|
589
|
+
return this.resolver ? this.resolver.getPermissionCodes(options) : [];
|
|
483
590
|
}
|
|
484
591
|
/** Overwrite the cached permission codes for a user with a fixed list */ async setPermissions(options, permissions, ttl) {
|
|
485
592
|
if (!this.cache) return;
|
|
486
|
-
const cacheKey = (0,_utils_permission_cache_key_util_js__rspack_import_4/* .
|
|
593
|
+
const cacheKey = await (0,_utils_permission_cache_key_util_js__rspack_import_4/* .resolvePermissionCacheKey */.Rw)(this.cache, options, this.config);
|
|
487
594
|
await this.cache.set(cacheKey, permissions, ttl);
|
|
488
595
|
}
|
|
489
|
-
/** Clear the cached permission codes
|
|
596
|
+
/** Clear the cached permission codes of a user - every company and branch scope */ async clearPermissions(options) {
|
|
490
597
|
if (!this.cache) return;
|
|
491
|
-
|
|
492
|
-
await this.cache.del(cacheKey);
|
|
598
|
+
await (0,_utils_permission_cache_key_util_js__rspack_import_4/* .invalidatePermissionCache */.xp)(this.cache, options.userId, this.config);
|
|
493
599
|
}
|
|
494
600
|
/** Check a single permission against the cached list, honoring `*` and `prefix.*` wildcards */ async hasPermission(options, requiredPermission) {
|
|
495
601
|
const permissions = await this.getPermissions(options);
|
|
@@ -529,10 +635,13 @@ SharedPermissionCacheService = _ts_decorate([
|
|
|
529
635
|
_ts_param(0, (0,_nestjs_common__rspack_import_0.Inject)(_constants_index_js__rspack_import_2/* .CACHE_INSTANCE */.zl)),
|
|
530
636
|
_ts_param(1, (0,_nestjs_common__rspack_import_0.Optional)()),
|
|
531
637
|
_ts_param(1, (0,_nestjs_common__rspack_import_0.Inject)(_constants_index_js__rspack_import_2/* .PERMISSION_GUARD_CONFIG */.Lw)),
|
|
638
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_0.Optional)()),
|
|
639
|
+
_ts_param(2, (0,_nestjs_common__rspack_import_0.Inject)(_interfaces_permission_resolver_interface_js__rspack_import_6/* .PERMISSION_RESOLVER */.j)),
|
|
532
640
|
_ts_metadata("design:type", Function),
|
|
533
641
|
_ts_metadata("design:paramtypes", [
|
|
534
642
|
typeof _classes_hybrid_cache_class_js__rspack_import_1/* .HybridCache */.l === "undefined" ? Object : _classes_hybrid_cache_class_js__rspack_import_1/* .HybridCache */.l,
|
|
535
|
-
typeof PermissionGuardConfig === "undefined" ? Object : PermissionGuardConfig
|
|
643
|
+
typeof PermissionGuardConfig === "undefined" ? Object : PermissionGuardConfig,
|
|
644
|
+
typeof IPermissionResolver === "undefined" ? Object : IPermissionResolver
|
|
536
645
|
])
|
|
537
646
|
], SharedPermissionCacheService);
|
|
538
647
|
|
|
@@ -542,8 +651,10 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
542
651
|
|
|
543
652
|
|
|
544
653
|
},
|
|
545
|
-
|
|
546
|
-
/* import */ var _constants_index_js__rspack_import_0 = __webpack_require__(
|
|
654
|
+
3199(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
655
|
+
/* import */ var _constants_index_js__rspack_import_0 = __webpack_require__(6043);
|
|
656
|
+
/* import */ var _middlewares_logger_middleware_js__rspack_import_1 = __webpack_require__(8244);
|
|
657
|
+
|
|
547
658
|
|
|
548
659
|
const DEFAULT_USER_PERMISSION_KEY_FORMAT = `${_constants_index_js__rspack_import_0/* .PERMISSIONS_CACHE_PREFIX */.fG}:user:{userId}`;
|
|
549
660
|
const DEFAULT_COMPANY_PERMISSION_KEY_FORMAT = `${_constants_index_js__rspack_import_0/* .PERMISSIONS_CACHE_PREFIX */.fG}:company:{companyId}:branch:{branchId}:user:{userId}`;
|
|
@@ -554,9 +665,31 @@ const DEFAULT_COMPANY_PERMISSION_KEY_FORMAT = `${_constants_index_js__rspack_imp
|
|
|
554
665
|
}
|
|
555
666
|
return (config?.userPermissionKeyFormat || DEFAULT_USER_PERMISSION_KEY_FORMAT).replace('{userId}', options.userId);
|
|
556
667
|
}
|
|
668
|
+
/**
|
|
669
|
+
* Tenant databases can share user ids (they are cloned from one template), so multi-tenant keys
|
|
670
|
+
* carry the request's tenant. A single database never does: a stray tenant header must not move
|
|
671
|
+
* a user onto a cache entry that invalidation never reaches.
|
|
672
|
+
*/ function permissionTenant(config) {
|
|
673
|
+
return config?.multiTenant ? `tenant:${(0,_middlewares_logger_middleware_js__rspack_import_1/* .getTenantId */.EO)() ?? 'none'}:` : '';
|
|
674
|
+
}
|
|
675
|
+
function permissionStampName(userId, config) {
|
|
676
|
+
return `${_constants_index_js__rspack_import_0/* .PERMISSIONS_CACHE_PREFIX */.fG}:${permissionTenant(config)}user:${userId}`;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* The key a user's permissions are cached under right now: tenant + scope + the user's version
|
|
680
|
+
* stamp. Resolve it once, BEFORE reading grants from the database, and write under that same key.
|
|
681
|
+
*/ async function resolvePermissionCacheKey(cache, options, config) {
|
|
682
|
+
const stamp = await cache.getStamp(permissionStampName(options.userId, config));
|
|
683
|
+
return `${permissionTenant(config)}${buildPermissionCacheKey(options, config)}:v:${stamp}`;
|
|
684
|
+
}
|
|
685
|
+
/** Drops every cached permission scope (each company and branch) of the user, on every instance */ async function invalidatePermissionCache(cache, userId, config) {
|
|
686
|
+
await cache.bumpStamp(permissionStampName(userId, config));
|
|
687
|
+
}
|
|
557
688
|
|
|
558
689
|
__webpack_require__.d(__webpack_exports__, {
|
|
559
|
-
|
|
690
|
+
Rw: () => (resolvePermissionCacheKey),
|
|
691
|
+
nt: () => (buildPermissionCacheKey),
|
|
692
|
+
xp: () => (invalidatePermissionCache)
|
|
560
693
|
}, {
|
|
561
694
|
q6: DEFAULT_USER_PERMISSION_KEY_FORMAT,
|
|
562
695
|
s8: DEFAULT_COMPANY_PERMISSION_KEY_FORMAT
|
|
@@ -565,7 +698,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
565
698
|
|
|
566
699
|
},
|
|
567
700
|
788(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
568
|
-
/* import */ var _exceptions_permission_exception_js__rspack_import_0 = __webpack_require__(
|
|
701
|
+
/* import */ var _exceptions_permission_exception_js__rspack_import_0 = __webpack_require__(8374);
|
|
569
702
|
|
|
570
703
|
/** Whether `granted` covers `required`, honoring `*` (everything) and `prefix.*` (everything under prefix) wildcards */ function matchesPermission(granted, required) {
|
|
571
704
|
if (granted.includes(required)) return true;
|
|
@@ -646,7 +779,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
646
779
|
|
|
647
780
|
|
|
648
781
|
},
|
|
649
|
-
|
|
782
|
+
3075(module) {
|
|
650
783
|
|
|
651
784
|
module.exports = __rspack_external__nestjs_core_d7b00a1a;
|
|
652
785
|
|
|
@@ -656,11 +789,11 @@ module.exports = __rspack_external__nestjs_core_d7b00a1a;
|
|
|
656
789
|
};
|
|
657
790
|
import { __webpack_require__ } from '../runtime.js';
|
|
658
791
|
var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
|
|
659
|
-
import * as __rspack_chunk_1 from '../
|
|
792
|
+
import * as __rspack_chunk_1 from '../997.js';
|
|
660
793
|
__webpack_require__.C(__rspack_chunk_1);
|
|
661
794
|
import * as __rspack_chunk_2 from './index.js';
|
|
662
795
|
__webpack_require__.C(__rspack_chunk_2);
|
|
663
|
-
var __webpack_exports__ = __webpack_exec__(
|
|
796
|
+
var __webpack_exports__ = __webpack_exec__(3510);var __webpack_exports__JwtAuthGuard = __webpack_exports__.TD;
|
|
664
797
|
var __webpack_exports__OptionalJwtGuard = __webpack_exports__.Zb;
|
|
665
798
|
var __webpack_exports__PermissionGuard = __webpack_exports__.LQ;
|
|
666
799
|
export { __webpack_exports__JwtAuthGuard as JwtAuthGuard, __webpack_exports__OptionalJwtGuard as OptionalJwtGuard, __webpack_exports__PermissionGuard as PermissionGuard };
|