@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.
Files changed (113) hide show
  1. package/.github/pull_request_template.md +71 -0
  2. package/.github/workflows/deploy.yml +9 -0
  3. package/.github/workflows/publish.yml +14 -0
  4. package/.github/workflows/security.yml +20 -0
  5. package/README.md +89 -0
  6. package/commits.txt +5 -0
  7. package/dist/common/index.cjs +48 -0
  8. package/dist/common/index.cjs.map +1 -0
  9. package/dist/common/index.mjs +43 -0
  10. package/dist/common/index.mjs.map +1 -0
  11. package/dist/index.cjs +20411 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.mjs +5139 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/eslint.config.mjs +13 -0
  16. package/index.html +13 -0
  17. package/package.json +141 -0
  18. package/src/adapters/auth-adapter-factory.ts +26 -0
  19. package/src/adapters/auth-adapter.mapper.ts +53 -0
  20. package/src/adapters/base-auth.adapter.ts +119 -0
  21. package/src/adapters/clerk/clerk.adapter.ts +204 -0
  22. package/src/adapters/custom/custom.adapter.ts +119 -0
  23. package/src/adapters/index.ts +4 -0
  24. package/src/adapters/next-auth/authOptions.ts +81 -0
  25. package/src/adapters/next-auth/next-auth.adapter.ts +211 -0
  26. package/src/api/client.ts +37 -0
  27. package/src/audit/audit.logger.ts +52 -0
  28. package/src/client/components/ProtectedRoute.tsx +37 -0
  29. package/src/client/hooks/useAuth.ts +128 -0
  30. package/src/client/hooks/useConnectedAccounts.ts +108 -0
  31. package/src/client/hooks/usePermissions.ts +36 -0
  32. package/src/client/hooks/useRBAC.ts +36 -0
  33. package/src/client/hooks/useSession.ts +18 -0
  34. package/src/client/providers/AuthProvider.tsx +104 -0
  35. package/src/client/store/auth.store.ts +306 -0
  36. package/src/client/utils/storage.ts +70 -0
  37. package/src/common/constants/oauth-providers.ts +49 -0
  38. package/src/common/errors/auth.errors.ts +64 -0
  39. package/src/common/errors/specific-auth-errors.ts +201 -0
  40. package/src/common/index.ts +19 -0
  41. package/src/common/regex/index.ts +27 -0
  42. package/src/common/types/auth.types.ts +641 -0
  43. package/src/common/types/index.ts +297 -0
  44. package/src/common/utils/index.ts +84 -0
  45. package/src/core/blacklist/token.blacklist.ts +60 -0
  46. package/src/core/index.ts +2 -0
  47. package/src/core/jwt/jwt.manager.ts +131 -0
  48. package/src/core/session/session.manager.ts +56 -0
  49. package/src/db/repositories/connected-account.repository.ts +415 -0
  50. package/src/db/repositories/role.repository.ts +519 -0
  51. package/src/db/repositories/session.repository.ts +308 -0
  52. package/src/db/repositories/user.repository.ts +320 -0
  53. package/src/flows/index.ts +2 -0
  54. package/src/flows/sign-in.flow.ts +106 -0
  55. package/src/flows/sign-up.flow.ts +121 -0
  56. package/src/index.ts +54 -0
  57. package/src/libs/clerk.helper.ts +36 -0
  58. package/src/libs/supabase.helper.ts +255 -0
  59. package/src/libs/supabaseClient.ts +6 -0
  60. package/src/providers/base/auth-provider.interface.ts +42 -0
  61. package/src/providers/base/index.ts +1 -0
  62. package/src/providers/index.ts +2 -0
  63. package/src/providers/oauth/facebook.provider.ts +97 -0
  64. package/src/providers/oauth/github.provider.ts +148 -0
  65. package/src/providers/oauth/google.provider.ts +126 -0
  66. package/src/providers/oauth/index.ts +3 -0
  67. package/src/rbac/dynamic-roles.ts +552 -0
  68. package/src/rbac/index.ts +4 -0
  69. package/src/rbac/permission-checker.ts +464 -0
  70. package/src/rbac/role-hierarchy.ts +545 -0
  71. package/src/rbac/role.manager.ts +75 -0
  72. package/src/security/csrf/csrf.protection.ts +37 -0
  73. package/src/security/index.ts +3 -0
  74. package/src/security/rate-limiting/auth/auth.controller.ts +12 -0
  75. package/src/security/rate-limiting/auth/rate-limiting.interface.ts +67 -0
  76. package/src/security/rate-limiting/auth.module.ts +32 -0
  77. package/src/server/auth.module.ts +158 -0
  78. package/src/server/decorators/auth.decorator.ts +43 -0
  79. package/src/server/decorators/auth.decorators.ts +31 -0
  80. package/src/server/decorators/current-user.decorator.ts +49 -0
  81. package/src/server/decorators/permission.decorator.ts +49 -0
  82. package/src/server/guards/auth.guard.ts +56 -0
  83. package/src/server/guards/custom-throttler.guard.ts +46 -0
  84. package/src/server/guards/permissions.guard.ts +115 -0
  85. package/src/server/guards/roles.guard.ts +31 -0
  86. package/src/server/middleware/auth.middleware.ts +46 -0
  87. package/src/server/middleware/index.ts +2 -0
  88. package/src/server/middleware/middleware.ts +11 -0
  89. package/src/server/middleware/session.middleware.ts +255 -0
  90. package/src/server/services/account.service.ts +269 -0
  91. package/src/server/services/auth.service.ts +79 -0
  92. package/src/server/services/brute-force.service.ts +98 -0
  93. package/src/server/services/index.ts +15 -0
  94. package/src/server/services/rate-limiter.service.ts +60 -0
  95. package/src/server/services/session.service.ts +287 -0
  96. package/src/server/services/token.service.ts +262 -0
  97. package/src/session/cookie-store.ts +255 -0
  98. package/src/session/enhanced-session-manager.ts +406 -0
  99. package/src/session/index.ts +14 -0
  100. package/src/session/memory-store.ts +320 -0
  101. package/src/session/redis-store.ts +443 -0
  102. package/src/strategies/oauth.strategy.ts +128 -0
  103. package/src/strategies/traditional-auth.strategy.ts +116 -0
  104. package/src/tokens/index.ts +4 -0
  105. package/src/tokens/refresh-token-manager.ts +448 -0
  106. package/src/tokens/token-validator.ts +311 -0
  107. package/tsconfig.build.json +28 -0
  108. package/tsconfig.json +38 -0
  109. package/tsup.config.mjs +28 -0
  110. package/vitest.config.mjs +16 -0
  111. package/vitest.setup.d.ts +2 -0
  112. package/vitest.setup.d.ts.map +1 -0
  113. package/vitest.setup.ts +1 -0
@@ -0,0 +1,545 @@
1
+ /**
2
+ * @fileoverview Role hierarchy manager for @plyaz/auth
3
+ * @module @plyaz/auth/rbac/role-hierarchy
4
+ *
5
+ * @description
6
+ * Manages role hierarchy and inheritance for role-based access control.
7
+ * Handles role precedence, permission inheritance, and hierarchical
8
+ * role validation. Enables complex organizational structures with
9
+ * inherited permissions and role-based delegation.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { RoleHierarchy } from '@plyaz/auth';
14
+ *
15
+ * const hierarchy = new RoleHierarchy();
16
+ * await hierarchy.addRole('admin', 100);
17
+ * await hierarchy.addRole('moderator', 50);
18
+ *
19
+ * const hasPermission = await hierarchy.checkInheritedPermission(
20
+ * 'admin', 'moderator'
21
+ * );
22
+ * ```
23
+ */
24
+
25
+ import type { Role, RoleHierarchyConfig, RoleNode } from "@plyaz/types";
26
+
27
+ // /**
28
+ // * Role hierarchy node
29
+ // */
30
+ // export interface RoleNode {
31
+ // /** Role information */
32
+ // role: Role;
33
+ // /** Parent roles (higher hierarchy) */
34
+ // parents: Set<string>;
35
+ // /** Child roles (lower hierarchy) */
36
+ // children: Set<string>;
37
+ // /** Direct permissions */
38
+ // permissions: Set<string>;
39
+ // /** Inherited permissions (computed) */
40
+ // inheritedPermissions?: Set<string>;
41
+ // }
42
+
43
+ // /**
44
+ // * Role hierarchy configuration
45
+ // */
46
+ // export interface RoleHierarchyConfig {
47
+ // /** Enable permission inheritance */
48
+ // enableInheritance: boolean;
49
+ // /** Maximum hierarchy depth */
50
+ // maxDepth: number;
51
+ // /** Enable circular dependency detection */
52
+ // detectCircular: boolean;
53
+ // /** Cache inherited permissions */
54
+ // cacheInheritance: boolean;
55
+ // }
56
+
57
+ /**
58
+ * Role hierarchy manager implementation
59
+ * Manages role relationships and permission inheritance
60
+ */
61
+ export class RoleHierarchy {
62
+ private readonly config: RoleHierarchyConfig;
63
+ private readonly roles = new Map<string, RoleNode>();
64
+ private readonly hierarchyCache = new Map<string, Set<string>>();
65
+
66
+ constructor(config: Partial<RoleHierarchyConfig> = {}) {
67
+ this.config = {
68
+ enableInheritance: true,
69
+ maxDepth: 10,
70
+ detectCircular: true,
71
+ cacheInheritance: true,
72
+ ...config,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Add role to hierarchy
78
+ * @param role - Role to add
79
+ * @returns Promise that resolves when role is added
80
+ */
81
+ async addRole(role: Role): Promise<void> {
82
+ const roleNode: RoleNode = {
83
+ role,
84
+ parents: new Set(),
85
+ children: new Set(),
86
+ permissions: new Set(),
87
+ };
88
+
89
+ this.roles.set(role.id, roleNode);
90
+
91
+ // Clear cache when hierarchy changes
92
+ if (this.config.cacheInheritance) {
93
+ this.hierarchyCache.clear();
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Remove role from hierarchy
99
+ * @param roleId - Role identifier
100
+ * @returns Promise that resolves when role is removed
101
+ */
102
+ async removeRole(roleId: string): Promise<void> {
103
+ const roleNode = this.roles.get(roleId);
104
+
105
+ if (!roleNode) {
106
+ return;
107
+ }
108
+
109
+ // Remove from parent-child relationships
110
+ for (const parentId of roleNode.parents) {
111
+ const parent = this.roles.get(parentId);
112
+ if (parent) {
113
+ parent.children.delete(roleId);
114
+ }
115
+ }
116
+
117
+ for (const childId of roleNode.children) {
118
+ const child = this.roles.get(childId);
119
+ if (child) {
120
+ child.parents.delete(roleId);
121
+ }
122
+ }
123
+
124
+ this.roles.delete(roleId);
125
+
126
+ // Clear cache when hierarchy changes
127
+ if (this.config.cacheInheritance) {
128
+ this.hierarchyCache.clear();
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Add parent-child relationship between roles
134
+ * @param childRoleId - Child role identifier
135
+ * @param parentRoleId - Parent role identifier
136
+ * @returns Promise that resolves when relationship is added
137
+ */
138
+ async addRoleRelationship(
139
+ childRoleId: string,
140
+ parentRoleId: string
141
+ ): Promise<void> {
142
+ const childRole = this.roles.get(childRoleId);
143
+ const parentRole = this.roles.get(parentRoleId);
144
+
145
+ if (!childRole || !parentRole) {
146
+ throw new Error("Role not found");
147
+ }
148
+
149
+ // Check for circular dependencies
150
+ if (
151
+ this.config.detectCircular &&
152
+ (await this.wouldCreateCircular(childRoleId, parentRoleId))
153
+ ) {
154
+ throw new Error("Adding relationship would create circular dependency");
155
+ }
156
+
157
+ // Check hierarchy depth
158
+ const depth = await this.calculateDepth(parentRoleId);
159
+ if (depth >= this.config.maxDepth) {
160
+ throw new Error("Maximum hierarchy depth exceeded");
161
+ }
162
+
163
+ // Add relationship
164
+ childRole.parents.add(parentRoleId);
165
+ parentRole.children.add(childRoleId);
166
+
167
+ // Clear cache when hierarchy changes
168
+ if (this.config.cacheInheritance) {
169
+ this.hierarchyCache.clear();
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Remove parent-child relationship between roles
175
+ * @param childRoleId - Child role identifier
176
+ * @param parentRoleId - Parent role identifier
177
+ * @returns Promise that resolves when relationship is removed
178
+ */
179
+ async removeRoleRelationship(
180
+ childRoleId: string,
181
+ parentRoleId: string
182
+ ): Promise<void> {
183
+ const childRole = this.roles.get(childRoleId);
184
+ const parentRole = this.roles.get(parentRoleId);
185
+
186
+ if (childRole) {
187
+ childRole.parents.delete(parentRoleId);
188
+ }
189
+
190
+ if (parentRole) {
191
+ parentRole.children.delete(childRoleId);
192
+ }
193
+
194
+ // Clear cache when hierarchy changes
195
+ if (this.config.cacheInheritance) {
196
+ this.hierarchyCache.clear();
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Check if role inherits from another role
202
+ * @param roleId - Role identifier
203
+ * @param ancestorRoleId - Ancestor role identifier
204
+ * @returns True if role inherits from ancestor
205
+ */
206
+ async inheritsFrom(roleId: string, ancestorRoleId: string): Promise<boolean> {
207
+ if (roleId === ancestorRoleId) {
208
+ return true;
209
+ }
210
+
211
+ const ancestors = await this.getAncestors(roleId);
212
+ return ancestors.has(ancestorRoleId);
213
+ }
214
+
215
+ /**
216
+ * Get all ancestor roles (roles this role inherits from)
217
+ * @param roleId - Role identifier
218
+ * @returns Set of ancestor role IDs
219
+ */
220
+ async getAncestors(roleId: string): Promise<Set<string>> {
221
+ if (this.config.cacheInheritance) {
222
+ const cached = this.hierarchyCache.get(`ancestors:${roleId}`);
223
+ if (cached) {
224
+ return cached;
225
+ }
226
+ }
227
+
228
+ const ancestors = new Set<string>();
229
+ const visited = new Set<string>();
230
+
231
+ await this.collectAncestors(roleId, ancestors, visited);
232
+
233
+ if (this.config.cacheInheritance) {
234
+ this.hierarchyCache.set(`ancestors:${roleId}`, ancestors);
235
+ }
236
+
237
+ return ancestors;
238
+ }
239
+
240
+ /**
241
+ * Get all descendant roles (roles that inherit from this role)
242
+ * @param roleId - Role identifier
243
+ * @returns Set of descendant role IDs
244
+ */
245
+ async getDescendants(roleId: string): Promise<Set<string>> {
246
+ if (this.config.cacheInheritance) {
247
+ const cached = this.hierarchyCache.get(`descendants:${roleId}`);
248
+ if (cached) {
249
+ return cached;
250
+ }
251
+ }
252
+
253
+ const descendants = new Set<string>();
254
+ const visited = new Set<string>();
255
+
256
+ await this.collectDescendants(roleId, descendants, visited);
257
+
258
+ if (this.config.cacheInheritance) {
259
+ this.hierarchyCache.set(`descendants:${roleId}`, descendants);
260
+ }
261
+
262
+ return descendants;
263
+ }
264
+
265
+ /**
266
+ * Get effective permissions for role (including inherited)
267
+ * @param roleId - Role identifier
268
+ * @returns Set of permission IDs
269
+ */
270
+ async getEffectivePermissions(roleId: string): Promise<Set<string>> {
271
+ if (!this.config.enableInheritance) {
272
+ const roleNode = this.roles.get(roleId);
273
+ return roleNode ? roleNode.permissions : new Set();
274
+ }
275
+
276
+ if (this.config.cacheInheritance) {
277
+ const cached = this.hierarchyCache.get(`permissions:${roleId}`);
278
+ if (cached) {
279
+ return cached;
280
+ }
281
+ }
282
+
283
+ const effectivePermissions = new Set<string>();
284
+ const roleNode = this.roles.get(roleId);
285
+
286
+ if (!roleNode) {
287
+ return effectivePermissions;
288
+ }
289
+
290
+ // Add direct permissions
291
+ for (const permission of roleNode.permissions) {
292
+ effectivePermissions.add(permission);
293
+ }
294
+
295
+ // Add inherited permissions
296
+ const ancestors = await this.getAncestors(roleId);
297
+ for (const ancestorId of ancestors) {
298
+ const ancestorNode = this.roles.get(ancestorId);
299
+ if (ancestorNode) {
300
+ for (const permission of ancestorNode.permissions) {
301
+ effectivePermissions.add(permission);
302
+ }
303
+ }
304
+ }
305
+
306
+ if (this.config.cacheInheritance) {
307
+ this.hierarchyCache.set(`permissions:${roleId}`, effectivePermissions);
308
+ }
309
+
310
+ return effectivePermissions;
311
+ }
312
+
313
+ /**
314
+ * Validate hierarchy integrity
315
+ * @returns Validation result with any issues found
316
+ */
317
+ async validateHierarchy(): Promise<{
318
+ valid: boolean;
319
+ issues: string[];
320
+ }> {
321
+ const issues: string[] = [];
322
+
323
+ // Check for circular dependencies
324
+ if (this.config.detectCircular) {
325
+ for (const roleId of this.roles.keys()) {
326
+ if (await this.hasCircularDependency(roleId)) {
327
+ issues.push(`Circular dependency detected involving role: ${roleId}`);
328
+ }
329
+ }
330
+ }
331
+
332
+ // Check hierarchy depth
333
+ for (const roleId of this.roles.keys()) {
334
+ const depth = await this.calculateDepth(roleId);
335
+ if (depth > this.config.maxDepth) {
336
+ issues.push(`Role ${roleId} exceeds maximum hierarchy depth: ${depth}`);
337
+ }
338
+ }
339
+
340
+ // Check for orphaned relationships
341
+ for (const [roleId, roleNode] of this.roles.entries()) {
342
+ for (const parentId of roleNode.parents) {
343
+ if (!this.roles.has(parentId)) {
344
+ issues.push(
345
+ `Role ${roleId} references non-existent parent: ${parentId}`
346
+ );
347
+ }
348
+ }
349
+
350
+ for (const childId of roleNode.children) {
351
+ if (!this.roles.has(childId)) {
352
+ issues.push(
353
+ `Role ${roleId} references non-existent child: ${childId}`
354
+ );
355
+ }
356
+ }
357
+ }
358
+
359
+ return {
360
+ valid: issues.length === 0,
361
+ issues,
362
+ };
363
+ }
364
+
365
+ /**
366
+ * Clear hierarchy cache
367
+ */
368
+ clearCache(): void {
369
+ this.hierarchyCache.clear();
370
+ }
371
+
372
+ /**
373
+ * Get hierarchy statistics
374
+ * @returns Hierarchy statistics
375
+ */
376
+ getStats(): {
377
+ totalRoles: number;
378
+ maxDepth: number;
379
+ rootRoles: number;
380
+ leafRoles: number;
381
+ } {
382
+ let maxDepth = 0;
383
+ let rootRoles = 0;
384
+ let leafRoles = 0;
385
+
386
+ for (const roleNode of this.roles.values()) {
387
+ if (roleNode.parents.size === 0) {
388
+ rootRoles++;
389
+ }
390
+
391
+ if (roleNode.children.size === 0) {
392
+ leafRoles++;
393
+ }
394
+ }
395
+
396
+ return {
397
+ totalRoles: this.roles.size,
398
+ maxDepth,
399
+ rootRoles,
400
+ leafRoles,
401
+ };
402
+ }
403
+
404
+ /**
405
+ * Collect ancestors recursively
406
+ * @param roleId - Role identifier
407
+ * @param ancestors - Set to collect ancestors
408
+ * @param visited - Set to track visited roles
409
+ * @private
410
+ */
411
+ private async collectAncestors(
412
+ roleId: string,
413
+ ancestors: Set<string>,
414
+ visited: Set<string>
415
+ ): Promise<void> {
416
+ if (visited.has(roleId)) {
417
+ return;
418
+ }
419
+
420
+ visited.add(roleId);
421
+ const roleNode = this.roles.get(roleId);
422
+
423
+ if (!roleNode) {
424
+ return;
425
+ }
426
+
427
+ for (const parentId of roleNode.parents) {
428
+ ancestors.add(parentId);
429
+ await this.collectAncestors(parentId, ancestors, visited);
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Collect descendants recursively
435
+ * @param roleId - Role identifier
436
+ * @param descendants - Set to collect descendants
437
+ * @param visited - Set to track visited roles
438
+ * @private
439
+ */
440
+ private async collectDescendants(
441
+ roleId: string,
442
+ descendants: Set<string>,
443
+ visited: Set<string>
444
+ ): Promise<void> {
445
+ if (visited.has(roleId)) {
446
+ return;
447
+ }
448
+
449
+ visited.add(roleId);
450
+ const roleNode = this.roles.get(roleId);
451
+
452
+ if (!roleNode) {
453
+ return;
454
+ }
455
+
456
+ for (const childId of roleNode.children) {
457
+ descendants.add(childId);
458
+ await this.collectDescendants(childId, descendants, visited);
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Check if adding relationship would create circular dependency
464
+ * @param childRoleId - Child role identifier
465
+ * @param parentRoleId - Parent role identifier
466
+ * @returns True if would create circular dependency
467
+ * @private
468
+ */
469
+ private async wouldCreateCircular(
470
+ childRoleId: string,
471
+ parentRoleId: string
472
+ ): Promise<boolean> {
473
+ // If parent inherits from child, adding this relationship would create a circle
474
+ return await this.inheritsFrom(parentRoleId, childRoleId);
475
+ }
476
+
477
+ /**
478
+ * Check for circular dependency starting from role
479
+ * @param roleId - Role identifier
480
+ * @returns True if circular dependency exists
481
+ * @private
482
+ */
483
+ private async hasCircularDependency(roleId: string): Promise<boolean> {
484
+ const visited = new Set<string>();
485
+ const recursionStack = new Set<string>();
486
+
487
+ return await this.detectCircularRecursive(roleId, visited, recursionStack);
488
+ }
489
+
490
+ /**
491
+ * Detect circular dependency recursively
492
+ * @param roleId - Current role identifier
493
+ * @param visited - Set of visited roles
494
+ * @param recursionStack - Current recursion stack
495
+ * @returns True if circular dependency found
496
+ * @private
497
+ */
498
+ private async detectCircularRecursive(
499
+ roleId: string,
500
+ visited: Set<string>,
501
+ recursionStack: Set<string>
502
+ ): Promise<boolean> {
503
+ visited.add(roleId);
504
+ recursionStack.add(roleId);
505
+
506
+ const roleNode = this.roles.get(roleId);
507
+ if (!roleNode) {
508
+ recursionStack.delete(roleId);
509
+ return false;
510
+ }
511
+
512
+ for (const parentId of roleNode.parents) {
513
+ if (!visited.has(parentId)) {
514
+ if (
515
+ await this.detectCircularRecursive(parentId, visited, recursionStack)
516
+ ) {
517
+ return true;
518
+ }
519
+ } else if (recursionStack.has(parentId)) {
520
+ return true; // Circular dependency found
521
+ }
522
+ }
523
+
524
+ recursionStack.delete(roleId);
525
+ return false;
526
+ }
527
+
528
+ /**
529
+ * Calculate hierarchy depth for role
530
+ * @param roleId - Role identifier
531
+ * @returns Hierarchy depth
532
+ * @private
533
+ */
534
+ private async calculateDepth(roleId: string): Promise<number> {
535
+ const ancestors = await this.getAncestors(roleId);
536
+ return ancestors.size;
537
+ }
538
+
539
+ /**
540
+ * Build role tree recursively
541
+ * @param roleId - Role identifier
542
+ * @returns Role tree node
543
+ * @private
544
+ */
545
+ }
@@ -0,0 +1,75 @@
1
+ import type { RoleConfig, UserRepository } from '@plyaz/types';
2
+
3
+
4
+ // export interface RoleConfig {
5
+ // hierarchyEnabled: boolean;
6
+ // cacheEnabled: boolean;
7
+ // cacheTTL: number;
8
+ // }
9
+
10
+ export class RoleManager {
11
+ private roleCache = new Map<string, { roles: string[]; expires: number }>();
12
+
13
+ constructor(
14
+ private userRepo: UserRepository,
15
+ private config: RoleConfig
16
+ ) {}
17
+
18
+ async assignRole(userId: string, role: string, assignedBy?: string): Promise<void> {
19
+ await this.userRepo.assignRole(userId, role, assignedBy);
20
+ this.invalidateCache(userId);
21
+ }
22
+
23
+ async removeRole(userId: string, role: string): Promise<void> {
24
+ await this.userRepo.removeRole(userId, role);
25
+ this.invalidateCache(userId);
26
+ }
27
+
28
+ async getUserRoles(userId: string): Promise<string[]> {
29
+ if (this.config.cacheEnabled) {
30
+ const cached = this.roleCache.get(userId);
31
+ if (cached && cached.expires > Date.now()) {
32
+ return cached.roles;
33
+ }
34
+ }
35
+
36
+ const roles = await this.userRepo.getUserRoles(userId);
37
+
38
+ if (this.config.cacheEnabled) {
39
+ this.roleCache.set(userId, {
40
+ roles,
41
+ expires: Date.now() + this.config.cacheTTL
42
+ });
43
+ }
44
+
45
+ return roles;
46
+ }
47
+
48
+ async hasRole(userId: string, role: string): Promise<boolean> {
49
+ const roles = await this.getUserRoles(userId);
50
+ return roles.includes(role);
51
+ }
52
+
53
+ async hasAnyRole(userId: string, roles: string[]): Promise<boolean> {
54
+ const userRoles = await this.getUserRoles(userId);
55
+ return roles.some(role => userRoles.includes(role));
56
+ }
57
+
58
+ async hasPermission(userId: string, permission: string): Promise<boolean> {
59
+ const roles = await this.getUserRoles(userId);
60
+ return this.userRepo.checkPermission(roles, permission);
61
+ }
62
+
63
+ private invalidateCache(userId: string): void {
64
+ this.roleCache.delete(userId);
65
+ }
66
+
67
+ cleanup(): void {
68
+ const now = Date.now();
69
+ for (const [userId, data] of this.roleCache.entries()) {
70
+ if (data.expires < now) {
71
+ this.roleCache.delete(userId);
72
+ }
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,37 @@
1
+ import { NUMERIX } from '@plyaz/config';
2
+ import { randomBytes } from 'crypto';
3
+
4
+ export class CSRFProtection {
5
+ private tokens = new Map<string, { token: string; expires: number }>();
6
+
7
+ generateToken(sessionId: string): string {
8
+ const thirtyTwo = 32;
9
+ const token = randomBytes(thirtyTwo).toString('hex');
10
+ const expires = Date.now() + (NUMERIX.SIXTY * NUMERIX.SIXTY * NUMERIX.THOUSAND); // 1 hour
11
+
12
+ this.tokens.set(sessionId, { token, expires });
13
+ return token;
14
+ }
15
+
16
+ validateToken(sessionId: string, token: string): boolean {
17
+ const stored = this.tokens.get(sessionId);
18
+ if (!stored || stored.expires < Date.now()) {
19
+ this.tokens.delete(sessionId);
20
+ return false;
21
+ }
22
+
23
+ return stored.token === token;
24
+ }
25
+
26
+ removeToken(sessionId: string): void {
27
+ this.tokens.delete(sessionId);
28
+ }
29
+
30
+ cleanup(): void {
31
+ const now = Date.now();
32
+ for (const [sessionId, data] of this.tokens.entries()) {
33
+ if (data.expires < now) {
34
+ this.tokens.delete(sessionId);
35
+ }
36
+ }
37
+ }}
@@ -0,0 +1,3 @@
1
+ export * from "./rate-limiting/auth.module";
2
+ export * from "./rate-limiting/auth/auth.controller"
3
+ export * from './csrf/csrf.protection';
@@ -0,0 +1,12 @@
1
+
2
+ import { Controller, UseGuards } from "@nestjs/common";
3
+ import { RateLimiterGuard } from "../../../server/guards/custom-throttler.guard";
4
+
5
+
6
+ @Controller("auth")
7
+ export class AuthController {
8
+ @UseGuards(RateLimiterGuard)
9
+ testAuthEndPoint() : {message:string} {
10
+ return { message: 'Test endpoint works' };
11
+ }
12
+ }