@plyaz/auth 1.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/.github/pull_request_template.md +71 -0
- package/.github/workflows/deploy.yml +9 -0
- package/.github/workflows/publish.yml +14 -0
- package/.github/workflows/security.yml +20 -0
- package/README.md +89 -0
- package/commits.txt +5 -0
- package/dist/common/index.cjs +48 -0
- package/dist/common/index.cjs.map +1 -0
- package/dist/common/index.mjs +43 -0
- package/dist/common/index.mjs.map +1 -0
- package/dist/index.cjs +20411 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +5139 -0
- package/dist/index.mjs.map +1 -0
- package/eslint.config.mjs +13 -0
- package/index.html +13 -0
- package/package.json +141 -0
- package/src/adapters/auth-adapter-factory.ts +26 -0
- package/src/adapters/auth-adapter.mapper.ts +53 -0
- package/src/adapters/base-auth.adapter.ts +119 -0
- package/src/adapters/clerk/clerk.adapter.ts +204 -0
- package/src/adapters/custom/custom.adapter.ts +119 -0
- package/src/adapters/index.ts +4 -0
- package/src/adapters/next-auth/authOptions.ts +81 -0
- package/src/adapters/next-auth/next-auth.adapter.ts +211 -0
- package/src/api/client.ts +37 -0
- package/src/audit/audit.logger.ts +52 -0
- package/src/client/components/ProtectedRoute.tsx +37 -0
- package/src/client/hooks/useAuth.ts +128 -0
- package/src/client/hooks/useConnectedAccounts.ts +108 -0
- package/src/client/hooks/usePermissions.ts +36 -0
- package/src/client/hooks/useRBAC.ts +36 -0
- package/src/client/hooks/useSession.ts +18 -0
- package/src/client/providers/AuthProvider.tsx +104 -0
- package/src/client/store/auth.store.ts +306 -0
- package/src/client/utils/storage.ts +70 -0
- package/src/common/constants/oauth-providers.ts +49 -0
- package/src/common/errors/auth.errors.ts +64 -0
- package/src/common/errors/specific-auth-errors.ts +201 -0
- package/src/common/index.ts +19 -0
- package/src/common/regex/index.ts +27 -0
- package/src/common/types/auth.types.ts +641 -0
- package/src/common/types/index.ts +297 -0
- package/src/common/utils/index.ts +84 -0
- package/src/core/blacklist/token.blacklist.ts +60 -0
- package/src/core/index.ts +2 -0
- package/src/core/jwt/jwt.manager.ts +131 -0
- package/src/core/session/session.manager.ts +56 -0
- package/src/db/repositories/connected-account.repository.ts +415 -0
- package/src/db/repositories/role.repository.ts +519 -0
- package/src/db/repositories/session.repository.ts +308 -0
- package/src/db/repositories/user.repository.ts +320 -0
- package/src/flows/index.ts +2 -0
- package/src/flows/sign-in.flow.ts +106 -0
- package/src/flows/sign-up.flow.ts +121 -0
- package/src/index.ts +54 -0
- package/src/libs/clerk.helper.ts +36 -0
- package/src/libs/supabase.helper.ts +255 -0
- package/src/libs/supabaseClient.ts +6 -0
- package/src/providers/base/auth-provider.interface.ts +42 -0
- package/src/providers/base/index.ts +1 -0
- package/src/providers/index.ts +2 -0
- package/src/providers/oauth/facebook.provider.ts +97 -0
- package/src/providers/oauth/github.provider.ts +148 -0
- package/src/providers/oauth/google.provider.ts +126 -0
- package/src/providers/oauth/index.ts +3 -0
- package/src/rbac/dynamic-roles.ts +552 -0
- package/src/rbac/index.ts +4 -0
- package/src/rbac/permission-checker.ts +464 -0
- package/src/rbac/role-hierarchy.ts +545 -0
- package/src/rbac/role.manager.ts +75 -0
- package/src/security/csrf/csrf.protection.ts +37 -0
- package/src/security/index.ts +3 -0
- package/src/security/rate-limiting/auth/auth.controller.ts +12 -0
- package/src/security/rate-limiting/auth/rate-limiting.interface.ts +67 -0
- package/src/security/rate-limiting/auth.module.ts +32 -0
- package/src/server/auth.module.ts +158 -0
- package/src/server/decorators/auth.decorator.ts +43 -0
- package/src/server/decorators/auth.decorators.ts +31 -0
- package/src/server/decorators/current-user.decorator.ts +49 -0
- package/src/server/decorators/permission.decorator.ts +49 -0
- package/src/server/guards/auth.guard.ts +56 -0
- package/src/server/guards/custom-throttler.guard.ts +46 -0
- package/src/server/guards/permissions.guard.ts +115 -0
- package/src/server/guards/roles.guard.ts +31 -0
- package/src/server/middleware/auth.middleware.ts +46 -0
- package/src/server/middleware/index.ts +2 -0
- package/src/server/middleware/middleware.ts +11 -0
- package/src/server/middleware/session.middleware.ts +255 -0
- package/src/server/services/account.service.ts +269 -0
- package/src/server/services/auth.service.ts +79 -0
- package/src/server/services/brute-force.service.ts +98 -0
- package/src/server/services/index.ts +15 -0
- package/src/server/services/rate-limiter.service.ts +60 -0
- package/src/server/services/session.service.ts +287 -0
- package/src/server/services/token.service.ts +262 -0
- package/src/session/cookie-store.ts +255 -0
- package/src/session/enhanced-session-manager.ts +406 -0
- package/src/session/index.ts +14 -0
- package/src/session/memory-store.ts +320 -0
- package/src/session/redis-store.ts +443 -0
- package/src/strategies/oauth.strategy.ts +128 -0
- package/src/strategies/traditional-auth.strategy.ts +116 -0
- package/src/tokens/index.ts +4 -0
- package/src/tokens/refresh-token-manager.ts +448 -0
- package/src/tokens/token-validator.ts +311 -0
- package/tsconfig.build.json +28 -0
- package/tsconfig.json +38 -0
- package/tsup.config.mjs +28 -0
- package/vitest.config.mjs +16 -0
- package/vitest.setup.d.ts +2 -0
- package/vitest.setup.d.ts.map +1 -0
- package/vitest.setup.ts +1 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Permission checker for @plyaz/auth
|
|
3
|
+
* @module @plyaz/auth/rbac/permission-checker
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Provides permission validation logic for role-based access control.
|
|
7
|
+
* Handles permission checking with conditions, resource-based permissions,
|
|
8
|
+
* and hierarchical role inheritance. Used by guards and services to
|
|
9
|
+
* enforce authorization policies.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { PermissionChecker } from '@plyaz/auth';
|
|
14
|
+
*
|
|
15
|
+
* const checker = new PermissionChecker();
|
|
16
|
+
* const hasPermission = await checker.checkPermission(
|
|
17
|
+
* userId,
|
|
18
|
+
* 'campaigns',
|
|
19
|
+
* 'create',
|
|
20
|
+
* { ownerId: userId }
|
|
21
|
+
* );
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { NUMERIX } from "@plyaz/config";
|
|
26
|
+
import type { Permission, PermissionCheckerConfig, PermissionCheckResult, PermissionContext, Role } from "@plyaz/types";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Permission checker implementation
|
|
30
|
+
* Validates user permissions with context and conditions
|
|
31
|
+
*/
|
|
32
|
+
// RoleRepository interface based on PermissionChecker usage
|
|
33
|
+
export interface RoleRepository {
|
|
34
|
+
/**
|
|
35
|
+
* Get all roles assigned to a user
|
|
36
|
+
* @param userId - User identifier
|
|
37
|
+
* @returns Array of Role objects
|
|
38
|
+
*/
|
|
39
|
+
getUserRoles(userId: string): Promise<Role[]>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get all permissions assigned to a role
|
|
43
|
+
* @param roleId - Role identifier
|
|
44
|
+
* @returns Array of Permission objects
|
|
45
|
+
*/
|
|
46
|
+
getRolePermissions(roleId: string): Promise<Permission[]>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
// UserRepository interface based on PermissionChecker usage
|
|
51
|
+
export interface UserRepository {
|
|
52
|
+
/**
|
|
53
|
+
* Get all roles assigned to a user
|
|
54
|
+
* @param userId - User identifier
|
|
55
|
+
* @returns Array of Role objects
|
|
56
|
+
*/
|
|
57
|
+
getUserRoles(userId: string): Promise<Role[]>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
export class PermissionChecker {
|
|
62
|
+
private readonly config: PermissionCheckerConfig;
|
|
63
|
+
private readonly permissionCache = new Map<string, { result: PermissionCheckResult; expires: number }>();
|
|
64
|
+
|
|
65
|
+
constructor(
|
|
66
|
+
config: Partial<PermissionCheckerConfig> = {},
|
|
67
|
+
private userRepository?: UserRepository,
|
|
68
|
+
private roleRepository?:RoleRepository
|
|
69
|
+
) {
|
|
70
|
+
this.config = {
|
|
71
|
+
enableCaching: true,
|
|
72
|
+
cacheTTL: 300, // 5 minutes
|
|
73
|
+
enableAuditLog: true,
|
|
74
|
+
...config
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if user has permission for resource and action
|
|
80
|
+
* @param userId - User identifier
|
|
81
|
+
* @param resource - Resource name (e.g., 'campaigns', 'users')
|
|
82
|
+
* @param action - Action name (e.g., 'create', 'read', 'update', 'delete')
|
|
83
|
+
* @param context - Permission context for conditional checks
|
|
84
|
+
* @returns Permission check result
|
|
85
|
+
*/
|
|
86
|
+
async checkPermission(
|
|
87
|
+
userId: string,
|
|
88
|
+
resource: string,
|
|
89
|
+
action: string,
|
|
90
|
+
context: PermissionContext = {}
|
|
91
|
+
): Promise<PermissionCheckResult> {
|
|
92
|
+
// Check cache first
|
|
93
|
+
if (this.config.enableCaching) {
|
|
94
|
+
const cacheKey = this.getCacheKey(userId, resource, action, context);
|
|
95
|
+
const cached = this.permissionCache.get(cacheKey);
|
|
96
|
+
|
|
97
|
+
if (cached && cached.expires > Date.now()) {
|
|
98
|
+
return cached.result;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Get user permissions
|
|
103
|
+
const userPermissions = await this.getUserPermissions(userId);
|
|
104
|
+
|
|
105
|
+
// Check direct permissions
|
|
106
|
+
const directResult = this.checkDirectPermission(userPermissions, resource, action, context);
|
|
107
|
+
|
|
108
|
+
if (directResult.granted) {
|
|
109
|
+
return this.cacheAndReturn(userId, resource, action, context, directResult);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Check role-based permissions
|
|
113
|
+
const roleResult = await this.checkRoleBasedPermission(userId, resource, action, context);
|
|
114
|
+
|
|
115
|
+
if (roleResult.granted) {
|
|
116
|
+
return this.cacheAndReturn(userId, resource, action, context, roleResult);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check wildcard permissions
|
|
120
|
+
const wildcardResult = this.checkWildcardPermission(userPermissions, resource, action, context);
|
|
121
|
+
|
|
122
|
+
if (wildcardResult.granted) {
|
|
123
|
+
return this.cacheAndReturn(userId, resource, action, context, wildcardResult);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Permission denied
|
|
127
|
+
const deniedResult: PermissionCheckResult = {
|
|
128
|
+
granted: false,
|
|
129
|
+
reason: `No permission found for ${resource}:${action}`
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
return this.cacheAndReturn(userId, resource, action, context, deniedResult);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check multiple permissions at once
|
|
137
|
+
* @param userId - User identifier
|
|
138
|
+
* @param permissions - Array of permission checks
|
|
139
|
+
* @returns Array of permission check results
|
|
140
|
+
*/
|
|
141
|
+
async checkMultiplePermissions(
|
|
142
|
+
userId: string,
|
|
143
|
+
permissions: Array<{ resource: string; action: string; context?: PermissionContext }>
|
|
144
|
+
): Promise<PermissionCheckResult[]> {
|
|
145
|
+
const results: PermissionCheckResult[] = [];
|
|
146
|
+
|
|
147
|
+
for (const permission of permissions) {
|
|
148
|
+
const result = await this.checkPermission(
|
|
149
|
+
userId,
|
|
150
|
+
permission.resource,
|
|
151
|
+
permission.action,
|
|
152
|
+
permission.context ?? {}
|
|
153
|
+
);
|
|
154
|
+
results.push(result);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if user has any of the specified permissions
|
|
162
|
+
* @param userId - User identifier
|
|
163
|
+
* @param permissions - Array of permission checks
|
|
164
|
+
* @returns True if user has at least one permission
|
|
165
|
+
*/
|
|
166
|
+
async hasAnyPermission(
|
|
167
|
+
userId: string,
|
|
168
|
+
permissions: Array<{ resource: string; action: string; context?: PermissionContext }>
|
|
169
|
+
): Promise<boolean> {
|
|
170
|
+
const results = await this.checkMultiplePermissions(userId, permissions);
|
|
171
|
+
return results.some(result => result.granted);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if user has all specified permissions
|
|
176
|
+
* @param userId - User identifier
|
|
177
|
+
* @param permissions - Array of permission checks
|
|
178
|
+
* @returns True if user has all permissions
|
|
179
|
+
*/
|
|
180
|
+
async hasAllPermissions(
|
|
181
|
+
userId: string,
|
|
182
|
+
permissions: Array<{ resource: string; action: string; context?: PermissionContext }>
|
|
183
|
+
): Promise<boolean> {
|
|
184
|
+
const results = await this.checkMultiplePermissions(userId, permissions);
|
|
185
|
+
return results.every(result => result.granted);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get all permissions for a user
|
|
190
|
+
* @param userId - User identifier
|
|
191
|
+
* @returns Array of user permissions
|
|
192
|
+
*/
|
|
193
|
+
async getUserPermissions(userId: string): Promise<Permission[]> {
|
|
194
|
+
if (!this.userRepository) {
|
|
195
|
+
throw new Error('UserRepository not configured');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
const userRoles = await this.getUserRoles(userId);
|
|
200
|
+
const permissions: Permission[] = [];
|
|
201
|
+
|
|
202
|
+
for (const role of userRoles) {
|
|
203
|
+
const rolePermissions = await this.getRolePermissions(role.id);
|
|
204
|
+
permissions.push(...rolePermissions);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return permissions;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Clear permission cache for user
|
|
212
|
+
* @param userId - User identifier
|
|
213
|
+
*/
|
|
214
|
+
clearUserCache(userId: string): void {
|
|
215
|
+
for (const [key] of this.permissionCache.entries()) {
|
|
216
|
+
if (key.startsWith(`${userId}:`)) {
|
|
217
|
+
this.permissionCache.delete(key);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Clear all permission cache
|
|
224
|
+
*/
|
|
225
|
+
clearAllCache(): void {
|
|
226
|
+
this.permissionCache.clear();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get cache statistics
|
|
231
|
+
* @returns Cache statistics
|
|
232
|
+
*/
|
|
233
|
+
getCacheStats(): { size: number; hitRate: number } {
|
|
234
|
+
return {
|
|
235
|
+
size: this.permissionCache.size,
|
|
236
|
+
hitRate: 0
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Check direct user permissions
|
|
242
|
+
* @param permissions - User permissions
|
|
243
|
+
* @param resource - Resource name
|
|
244
|
+
* @param action - Action name
|
|
245
|
+
* @param context - Permission context
|
|
246
|
+
* @returns Permission check result
|
|
247
|
+
* @private
|
|
248
|
+
*/
|
|
249
|
+
private checkDirectPermission(
|
|
250
|
+
permissions: Permission[],
|
|
251
|
+
resource: string,
|
|
252
|
+
action: string,
|
|
253
|
+
context: PermissionContext
|
|
254
|
+
): PermissionCheckResult {
|
|
255
|
+
for (const permission of permissions) {
|
|
256
|
+
if (permission.resource === resource && permission.action === action) {
|
|
257
|
+
// Check conditions if present
|
|
258
|
+
if (permission.conditions && Object.keys(permission.conditions).length > 0) {
|
|
259
|
+
const conditionsMet = this.evaluateConditions(permission.conditions, context);
|
|
260
|
+
|
|
261
|
+
// eslint-disable-next-line max-depth
|
|
262
|
+
if (conditionsMet) {
|
|
263
|
+
return {
|
|
264
|
+
granted: true,
|
|
265
|
+
reason: 'Direct permission with conditions met',
|
|
266
|
+
matchedPermission: permission,
|
|
267
|
+
conditions: permission.conditions
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
return {
|
|
272
|
+
granted: true,
|
|
273
|
+
reason: 'Direct permission without conditions',
|
|
274
|
+
matchedPermission: permission
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
granted: false,
|
|
282
|
+
reason: 'No direct permission found'
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Check role-based permissions
|
|
288
|
+
* @param userId - User identifier
|
|
289
|
+
* @param resource - Resource name
|
|
290
|
+
* @param action - Action name
|
|
291
|
+
* @param context - Permission context
|
|
292
|
+
* @returns Permission check result
|
|
293
|
+
* @private
|
|
294
|
+
*/
|
|
295
|
+
private async checkRoleBasedPermission(
|
|
296
|
+
userId: string,
|
|
297
|
+
resource: string,
|
|
298
|
+
action: string,
|
|
299
|
+
context: PermissionContext
|
|
300
|
+
): Promise<PermissionCheckResult> {
|
|
301
|
+
if (!this.roleRepository) {
|
|
302
|
+
return { granted: false, reason: 'RoleRepository not configured' };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const userRoles = await this.getUserRoles(userId);
|
|
306
|
+
|
|
307
|
+
for (const role of userRoles) {
|
|
308
|
+
const rolePermissions = await this.getRolePermissions(role.id);
|
|
309
|
+
const result = this.checkDirectPermission(rolePermissions, resource, action, context);
|
|
310
|
+
|
|
311
|
+
if (result.granted) {
|
|
312
|
+
return {
|
|
313
|
+
...result,
|
|
314
|
+
reason: `Role-based permission from role: ${role.name}`
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
granted: false,
|
|
321
|
+
reason: 'No role-based permission found'
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Check wildcard permissions
|
|
327
|
+
* @param permissions - User permissions
|
|
328
|
+
* @param resource - Resource name
|
|
329
|
+
* @param action - Action name
|
|
330
|
+
* @param context - Permission context
|
|
331
|
+
* @returns Permission check result
|
|
332
|
+
* @private
|
|
333
|
+
*/
|
|
334
|
+
private checkWildcardPermission(
|
|
335
|
+
permissions: Permission[],
|
|
336
|
+
resource: string,
|
|
337
|
+
action: string,
|
|
338
|
+
context: PermissionContext
|
|
339
|
+
): PermissionCheckResult {
|
|
340
|
+
// Check for wildcard permissions like "campaigns:*" or "*:read"
|
|
341
|
+
for (const permission of permissions) {
|
|
342
|
+
const resourceMatch = permission.resource === '*' || permission.resource === resource;
|
|
343
|
+
const actionMatch = permission.action === '*' || permission.action === action;
|
|
344
|
+
|
|
345
|
+
if (resourceMatch && actionMatch) {
|
|
346
|
+
// Check conditions if present
|
|
347
|
+
if (permission.conditions && Object.keys(permission.conditions).length > 0) {
|
|
348
|
+
const conditionsMet = this.evaluateConditions(permission.conditions, context);
|
|
349
|
+
|
|
350
|
+
// eslint-disable-next-line max-depth
|
|
351
|
+
if (conditionsMet) {
|
|
352
|
+
return {
|
|
353
|
+
granted: true,
|
|
354
|
+
reason: 'Wildcard permission with conditions met',
|
|
355
|
+
matchedPermission: permission,
|
|
356
|
+
conditions: permission.conditions
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
return {
|
|
361
|
+
granted: true,
|
|
362
|
+
reason: 'Wildcard permission without conditions',
|
|
363
|
+
matchedPermission: permission
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
granted: false,
|
|
371
|
+
reason: 'No wildcard permission found'
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Evaluate permission conditions
|
|
377
|
+
* @param conditions - Permission conditions
|
|
378
|
+
* @param context - Permission context
|
|
379
|
+
* @returns True if conditions are met
|
|
380
|
+
* @private
|
|
381
|
+
*/
|
|
382
|
+
private evaluateConditions(conditions: Record<string, unknown>, context: PermissionContext): boolean {
|
|
383
|
+
for (const [key, expectedValue] of Object.entries(conditions)) {
|
|
384
|
+
const contextValue = context[key];
|
|
385
|
+
|
|
386
|
+
if (contextValue !== expectedValue) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get user roles
|
|
396
|
+
* @param userId - User identifier
|
|
397
|
+
* @returns Array of user roles
|
|
398
|
+
* @private
|
|
399
|
+
*/
|
|
400
|
+
private async getUserRoles(userId: string): Promise<Role[]> {
|
|
401
|
+
if (!this.userRepository) {
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return await this.userRepository.getUserRoles(userId);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Get role permissions
|
|
410
|
+
* @param roleId - Role identifier
|
|
411
|
+
* @returns Array of role permissions
|
|
412
|
+
* @private
|
|
413
|
+
*/
|
|
414
|
+
private async getRolePermissions(roleId: string): Promise<Permission[]> {
|
|
415
|
+
if (!this.roleRepository) {
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return await this.roleRepository.getRolePermissions(roleId);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Generate cache key
|
|
424
|
+
* @param userId - User identifier
|
|
425
|
+
* @param resource - Resource name
|
|
426
|
+
* @param action - Action name
|
|
427
|
+
* @param context - Permission context
|
|
428
|
+
* @returns Cache key
|
|
429
|
+
* @private
|
|
430
|
+
*/
|
|
431
|
+
private getCacheKey(userId: string, resource: string, action: string, context: PermissionContext): string {
|
|
432
|
+
const contextHash = JSON.stringify(context);
|
|
433
|
+
return `${userId}:${resource}:${action}:${contextHash}`;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Cache result and return
|
|
438
|
+
* @param userId - User identifier
|
|
439
|
+
* @param resource - Resource name
|
|
440
|
+
* @param action - Action name
|
|
441
|
+
* @param context - Permission context
|
|
442
|
+
* @param result - Permission check result
|
|
443
|
+
* @returns Permission check result
|
|
444
|
+
* @private
|
|
445
|
+
*/
|
|
446
|
+
// eslint-disable-next-line max-params
|
|
447
|
+
private cacheAndReturn(
|
|
448
|
+
userId: string,
|
|
449
|
+
resource: string,
|
|
450
|
+
action: string,
|
|
451
|
+
context: PermissionContext,
|
|
452
|
+
result: PermissionCheckResult
|
|
453
|
+
): PermissionCheckResult {
|
|
454
|
+
if (this.config.enableCaching) {
|
|
455
|
+
const cacheKey = this.getCacheKey(userId, resource, action, context);
|
|
456
|
+
this.permissionCache.set(cacheKey, {
|
|
457
|
+
result,
|
|
458
|
+
expires: Date.now() + this.config.cacheTTL * NUMERIX.THOUSAND
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
}
|