exguard-endpoint 1.0.7 → 1.0.9

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 CHANGED
@@ -8,34 +8,24 @@ Simple RBAC permission guard for NestJS with built-in caching.
8
8
  npm install exguard-endpoint
9
9
  ```
10
10
 
11
- ## Quick Setup
11
+ ## Quick Setup (Auto-Configures Everything)
12
12
 
13
13
  ```bash
14
14
  npx exguard-endpoint setup
15
15
  ```
16
16
 
17
- ## Manual Setup
17
+ This will:
18
+ 1. Create `src/exguard/exguard.guard.ts`
19
+ 2. Create `src/exguard/exguard.module.ts`
20
+ 3. Update `src/app.module.ts` with ExGuardModule
21
+ 4. Create `.env.example` with EXGUARD_BASE_URL
18
22
 
19
- ### 1. Configure AppModule
23
+ ## Usage
20
24
 
21
25
  ```typescript
22
- import { Module } from '@nestjs/common';
23
- import { ExGuardModule } from 'exguard-endpoint';
24
-
25
- @Module({
26
- imports: [
27
- ExGuardModule.forRoot({
28
- baseUrl: process.env.EXGUARD_BASE_URL,
29
- }),
30
- ],
31
- })
32
- export class AppModule {}
33
- ```
34
-
35
- ### 2. Use in Controllers
26
+ // app.module.ts is auto-configured!
27
+ // Just use in controllers:
36
28
 
37
- ```typescript
38
- import { Controller, Get, Post, UseGuards } from '@nestjs/common';
39
29
  import { ExGuardPermissionGuard, RequirePermissions } from './exguard/exguard.guard';
40
30
 
41
31
  @Controller('items')
@@ -49,44 +39,27 @@ export class ItemsController {
49
39
  @Post()
50
40
  @RequirePermissions(['item:create'])
51
41
  create() { }
52
-
53
- @Delete(':id')
54
- @RequirePermissions(['item:delete', 'admin'], true)
55
- delete() { }
56
42
  }
57
43
  ```
58
44
 
59
- ## Environment Variables
45
+ ## Environment
60
46
 
61
47
  ```env
62
- EXGUARD_BASE_URL=https://your-guard-api.com
48
+ EXGUARD_BASE_URL=http://localhost:3000
63
49
  ```
64
50
 
65
- ## Caching (Enabled by Default)
51
+ ## Caching
66
52
 
67
53
  - **TTL**: 5 minutes
68
- - **Auto-clear**: When permissions change via realtime
69
- - **Manual clear**: `clearExGuardCache()`
70
-
71
- ```typescript
72
- import { clearExGuardCache } from './exguard/exguard.guard';
73
-
74
- // Call when needed
75
- clearExGuardCache();
76
- ```
54
+ - **Clear cache**: `clearExGuardCache()`
77
55
 
78
56
  ## Decorators
79
57
 
80
58
  | Decorator | Description |
81
59
  |-----------|-------------|
82
- | `@RequirePermissions(['item:read'])` | User needs ANY of the permissions |
60
+ | `@RequirePermissions(['item:read'])` | User needs ANY permission |
83
61
  | `@RequirePermissions(['item:delete', 'admin'], true)` | User needs ALL permissions |
84
62
 
85
- ## Token Extraction
86
-
87
- - `Authorization: Bearer <token>` header
88
- - `x-access-token` header
89
-
90
63
  ## User Object
91
64
 
92
65
  ```typescript
@@ -99,6 +72,4 @@ req.user = {
99
72
  }
100
73
  ```
101
74
 
102
- ## License
103
-
104
75
  MIT
package/dist/index.cjs CHANGED
@@ -42,13 +42,14 @@ module.exports = __toCommonJS(src_exports);
42
42
  // src/module.ts
43
43
  var import_common = require("@nestjs/common");
44
44
  var ExGuardModule = class {
45
- static forRoot(options) {
45
+ static forRoot(optionsOrUrl) {
46
+ const baseUrl = typeof optionsOrUrl === "string" ? optionsOrUrl : optionsOrUrl.baseUrl;
46
47
  return {
47
48
  module: ExGuardModule,
48
49
  providers: [
49
50
  {
50
51
  provide: "EXGUARD_BASE_URL",
51
- useValue: options.baseUrl
52
+ useValue: baseUrl
52
53
  }
53
54
  ],
54
55
  exports: ["EXGUARD_BASE_URL"]
@@ -64,6 +65,10 @@ ExGuardModule = __decorateClass([
64
65
  var import_common2 = require("@nestjs/common");
65
66
  var EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
66
67
  var EXGUARD_ROLES_KEY = "exguard_roles";
68
+ var EXGUARD_PERMISSIONS_ALL_KEY = "exguard_permissions_all";
69
+ var EXGUARD_ROLES_ALL_KEY = "exguard_roles_all";
70
+ var RequirePermissions = (permissions) => (0, import_common2.SetMetadata)(EXGUARD_PERMISSIONS_KEY, permissions);
71
+ var RequireRoles = (roles) => (0, import_common2.SetMetadata)(EXGUARD_ROLES_KEY, roles);
67
72
  var cache = /* @__PURE__ */ new Map();
68
73
  var CACHE_TTL = 5 * 60 * 1e3;
69
74
  function getCached(key) {
@@ -119,6 +124,7 @@ var ExGuardPermissionGuard = class {
119
124
  }
120
125
  const meData = await meResponse.json();
121
126
  authResult = { allowed: true, user: meData.data || meData };
127
+ console.log("[ExGuard DEBUG] Full user object:", JSON.stringify(authResult.user, null, 2));
122
128
  setCache(cacheKey, authResult);
123
129
  } catch (error) {
124
130
  console.error("[ExGuard] Auth error:", error);
@@ -133,22 +139,32 @@ var ExGuardPermissionGuard = class {
133
139
  }
134
140
  const handler = context.getHandler();
135
141
  const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
142
+ console.log("[ExGuard DEBUG] permMeta from reflector:", permMeta);
143
+ console.log("[ExGuard DEBUG] handler:", handler);
144
+ console.log("[ExGuard DEBUG] handler name:", handler.name);
136
145
  if (permMeta) {
137
- const { permissions, requireAll } = permMeta;
146
+ const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;
147
+ const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;
138
148
  const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m) => m.permissions) : [];
149
+ console.log("[ExGuard DEBUG] Required permissions:", permissions);
150
+ console.log("[ExGuard DEBUG] User permissions:", userPermissions);
151
+ console.log("[ExGuard DEBUG] requireAll:", requireAll);
139
152
  if (requireAll) {
140
153
  if (!permissions.every((p) => userPermissions.includes(p))) {
141
154
  throw new import_common2.ForbiddenException("Insufficient permissions");
142
155
  }
143
156
  } else {
144
- if (!permissions.some((p) => userPermissions.includes(p))) {
157
+ const hasPermission = permissions.some((p) => userPermissions.includes(p));
158
+ console.log("[ExGuard DEBUG] Permission check result:", hasPermission);
159
+ if (!hasPermission) {
145
160
  throw new import_common2.ForbiddenException("Insufficient permissions");
146
161
  }
147
162
  }
148
163
  }
149
164
  const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
150
165
  if (roleMeta) {
151
- const { roles, requireAll } = roleMeta;
166
+ const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;
167
+ const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;
152
168
  const userRoles = authResult.user.roles || [];
153
169
  if (requireAll) {
154
170
  if (!roles.every((r) => userRoles.includes(r))) {
@@ -176,18 +192,6 @@ ExGuardPermissionGuard = __decorateClass([
176
192
  __decorateParam(0, (0, import_common2.Optional)()),
177
193
  __decorateParam(0, (0, import_common2.Inject)("EXGUARD_BASE_URL"))
178
194
  ], ExGuardPermissionGuard);
179
- function RequirePermissions(permissions, requireAll = false) {
180
- return function(target, propertyKey, descriptor) {
181
- Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
182
- return descriptor;
183
- };
184
- }
185
- function RequireRoles(roles, requireAll = false) {
186
- return function(target, propertyKey, descriptor) {
187
- Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
188
- return descriptor;
189
- };
190
- }
191
195
  function clearExGuardCache() {
192
196
  cache.clear();
193
197
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export { ExGuardModule } from './module.js';\nexport type { ExGuardModuleOptions } from './module.js';\nexport { ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache, EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY } from './guard.js';\n","import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule {\n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: options.baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":["import_common"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export { ExGuardModule } from './module.js';\nexport { ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache, EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY } from './guard.js';\n","import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule;\n static forRoot(baseUrl: string): DynamicModule;\n static forRoot(optionsOrUrl: ExGuardModuleOptions | string): DynamicModule {\n const baseUrl = typeof optionsOrUrl === 'string' ? optionsOrUrl : optionsOrUrl.baseUrl;\n \n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional, SetMetadata } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\nexport const EXGUARD_PERMISSIONS_ALL_KEY = 'exguard_permissions_all';\nexport const EXGUARD_ROLES_ALL_KEY = 'exguard_roles_all';\n\nexport const RequirePermissions = (permissions: string[]) => SetMetadata(EXGUARD_PERMISSIONS_KEY, permissions);\nexport const RequireRoles = (roles: string[]) => SetMetadata(EXGUARD_ROLES_KEY, roles);\nexport const RequirePermissionsAll = () => SetMetadata(EXGUARD_PERMISSIONS_ALL_KEY, true);\nexport const RequireRolesAll = () => SetMetadata(EXGUARD_ROLES_ALL_KEY, true);\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n console.log('[ExGuard DEBUG] Full user object:', JSON.stringify(authResult.user, null, 2));\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n console.log('[ExGuard DEBUG] permMeta from reflector:', permMeta);\n console.log('[ExGuard DEBUG] handler:', handler);\n console.log('[ExGuard DEBUG] handler name:', handler.name);\n\n if (permMeta) {\n const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;\n const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n console.log('[ExGuard DEBUG] Required permissions:', permissions);\n console.log('[ExGuard DEBUG] User permissions:', userPermissions);\n console.log('[ExGuard DEBUG] requireAll:', requireAll);\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n const hasPermission = permissions.some((p: string) => userPermissions.includes(p));\n console.log('[ExGuard DEBUG] Permission check result:', hasPermission);\n if (!hasPermission) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;\n const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":["import_common"]}
package/dist/index.d.cts CHANGED
@@ -1,19 +1,19 @@
1
+ import * as _nestjs_common from '@nestjs/common';
1
2
  import { DynamicModule, CanActivate, ExecutionContext } from '@nestjs/common';
2
3
  import { Reflector } from '@nestjs/core';
3
4
 
4
5
  interface ExGuardModuleOptions {
5
6
  baseUrl: string;
6
- cache?: {
7
- enabled?: boolean;
8
- ttl?: number;
9
- };
10
7
  }
11
8
  declare class ExGuardModule {
12
9
  static forRoot(options: ExGuardModuleOptions): DynamicModule;
10
+ static forRoot(baseUrl: string): DynamicModule;
13
11
  }
14
12
 
15
13
  declare const EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
16
14
  declare const EXGUARD_ROLES_KEY = "exguard_roles";
15
+ declare const RequirePermissions: (permissions: string[]) => _nestjs_common.CustomDecorator<string>;
16
+ declare const RequireRoles: (roles: string[]) => _nestjs_common.CustomDecorator<string>;
17
17
  declare class ExGuardPermissionGuard implements CanActivate {
18
18
  private baseUrl;
19
19
  private reflector;
@@ -21,8 +21,6 @@ declare class ExGuardPermissionGuard implements CanActivate {
21
21
  canActivate(context: ExecutionContext): Promise<boolean>;
22
22
  private extractToken;
23
23
  }
24
- declare function RequirePermissions(permissions: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
25
- declare function RequireRoles(roles: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
26
24
  declare function clearExGuardCache(): void;
27
25
 
28
- export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardModule, type ExGuardModuleOptions, ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache };
26
+ export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardModule, ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache };
package/dist/index.d.ts CHANGED
@@ -1,19 +1,19 @@
1
+ import * as _nestjs_common from '@nestjs/common';
1
2
  import { DynamicModule, CanActivate, ExecutionContext } from '@nestjs/common';
2
3
  import { Reflector } from '@nestjs/core';
3
4
 
4
5
  interface ExGuardModuleOptions {
5
6
  baseUrl: string;
6
- cache?: {
7
- enabled?: boolean;
8
- ttl?: number;
9
- };
10
7
  }
11
8
  declare class ExGuardModule {
12
9
  static forRoot(options: ExGuardModuleOptions): DynamicModule;
10
+ static forRoot(baseUrl: string): DynamicModule;
13
11
  }
14
12
 
15
13
  declare const EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
16
14
  declare const EXGUARD_ROLES_KEY = "exguard_roles";
15
+ declare const RequirePermissions: (permissions: string[]) => _nestjs_common.CustomDecorator<string>;
16
+ declare const RequireRoles: (roles: string[]) => _nestjs_common.CustomDecorator<string>;
17
17
  declare class ExGuardPermissionGuard implements CanActivate {
18
18
  private baseUrl;
19
19
  private reflector;
@@ -21,8 +21,6 @@ declare class ExGuardPermissionGuard implements CanActivate {
21
21
  canActivate(context: ExecutionContext): Promise<boolean>;
22
22
  private extractToken;
23
23
  }
24
- declare function RequirePermissions(permissions: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
25
- declare function RequireRoles(roles: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
26
24
  declare function clearExGuardCache(): void;
27
25
 
28
- export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardModule, type ExGuardModuleOptions, ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache };
26
+ export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardModule, ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache };
package/dist/index.js CHANGED
@@ -13,13 +13,14 @@ var __decorateParam = (index, decorator) => (target, key) => decorator(target, k
13
13
  // src/module.ts
14
14
  import { Module, Global } from "@nestjs/common";
15
15
  var ExGuardModule = class {
16
- static forRoot(options) {
16
+ static forRoot(optionsOrUrl) {
17
+ const baseUrl = typeof optionsOrUrl === "string" ? optionsOrUrl : optionsOrUrl.baseUrl;
17
18
  return {
18
19
  module: ExGuardModule,
19
20
  providers: [
20
21
  {
21
22
  provide: "EXGUARD_BASE_URL",
22
- useValue: options.baseUrl
23
+ useValue: baseUrl
23
24
  }
24
25
  ],
25
26
  exports: ["EXGUARD_BASE_URL"]
@@ -32,9 +33,13 @@ ExGuardModule = __decorateClass([
32
33
  ], ExGuardModule);
33
34
 
34
35
  // src/guard.ts
35
- import { Injectable, UnauthorizedException, ForbiddenException, Inject, Optional } from "@nestjs/common";
36
+ import { Injectable, UnauthorizedException, ForbiddenException, Inject, Optional, SetMetadata } from "@nestjs/common";
36
37
  var EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
37
38
  var EXGUARD_ROLES_KEY = "exguard_roles";
39
+ var EXGUARD_PERMISSIONS_ALL_KEY = "exguard_permissions_all";
40
+ var EXGUARD_ROLES_ALL_KEY = "exguard_roles_all";
41
+ var RequirePermissions = (permissions) => SetMetadata(EXGUARD_PERMISSIONS_KEY, permissions);
42
+ var RequireRoles = (roles) => SetMetadata(EXGUARD_ROLES_KEY, roles);
38
43
  var cache = /* @__PURE__ */ new Map();
39
44
  var CACHE_TTL = 5 * 60 * 1e3;
40
45
  function getCached(key) {
@@ -90,6 +95,7 @@ var ExGuardPermissionGuard = class {
90
95
  }
91
96
  const meData = await meResponse.json();
92
97
  authResult = { allowed: true, user: meData.data || meData };
98
+ console.log("[ExGuard DEBUG] Full user object:", JSON.stringify(authResult.user, null, 2));
93
99
  setCache(cacheKey, authResult);
94
100
  } catch (error) {
95
101
  console.error("[ExGuard] Auth error:", error);
@@ -104,22 +110,32 @@ var ExGuardPermissionGuard = class {
104
110
  }
105
111
  const handler = context.getHandler();
106
112
  const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
113
+ console.log("[ExGuard DEBUG] permMeta from reflector:", permMeta);
114
+ console.log("[ExGuard DEBUG] handler:", handler);
115
+ console.log("[ExGuard DEBUG] handler name:", handler.name);
107
116
  if (permMeta) {
108
- const { permissions, requireAll } = permMeta;
117
+ const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;
118
+ const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;
109
119
  const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m) => m.permissions) : [];
120
+ console.log("[ExGuard DEBUG] Required permissions:", permissions);
121
+ console.log("[ExGuard DEBUG] User permissions:", userPermissions);
122
+ console.log("[ExGuard DEBUG] requireAll:", requireAll);
110
123
  if (requireAll) {
111
124
  if (!permissions.every((p) => userPermissions.includes(p))) {
112
125
  throw new ForbiddenException("Insufficient permissions");
113
126
  }
114
127
  } else {
115
- if (!permissions.some((p) => userPermissions.includes(p))) {
128
+ const hasPermission = permissions.some((p) => userPermissions.includes(p));
129
+ console.log("[ExGuard DEBUG] Permission check result:", hasPermission);
130
+ if (!hasPermission) {
116
131
  throw new ForbiddenException("Insufficient permissions");
117
132
  }
118
133
  }
119
134
  }
120
135
  const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
121
136
  if (roleMeta) {
122
- const { roles, requireAll } = roleMeta;
137
+ const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;
138
+ const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;
123
139
  const userRoles = authResult.user.roles || [];
124
140
  if (requireAll) {
125
141
  if (!roles.every((r) => userRoles.includes(r))) {
@@ -147,18 +163,6 @@ ExGuardPermissionGuard = __decorateClass([
147
163
  __decorateParam(0, Optional()),
148
164
  __decorateParam(0, Inject("EXGUARD_BASE_URL"))
149
165
  ], ExGuardPermissionGuard);
150
- function RequirePermissions(permissions, requireAll = false) {
151
- return function(target, propertyKey, descriptor) {
152
- Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
153
- return descriptor;
154
- };
155
- }
156
- function RequireRoles(roles, requireAll = false) {
157
- return function(target, propertyKey, descriptor) {
158
- Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
159
- return descriptor;
160
- };
161
- }
162
166
  function clearExGuardCache() {
163
167
  cache.clear();
164
168
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/module.ts","../src/guard.ts"],"sourcesContent":["import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule {\n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: options.baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/module.ts","../src/guard.ts"],"sourcesContent":["import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule;\n static forRoot(baseUrl: string): DynamicModule;\n static forRoot(optionsOrUrl: ExGuardModuleOptions | string): DynamicModule {\n const baseUrl = typeof optionsOrUrl === 'string' ? optionsOrUrl : optionsOrUrl.baseUrl;\n \n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional, SetMetadata } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\nexport const EXGUARD_PERMISSIONS_ALL_KEY = 'exguard_permissions_all';\nexport const EXGUARD_ROLES_ALL_KEY = 'exguard_roles_all';\n\nexport const RequirePermissions = (permissions: string[]) => SetMetadata(EXGUARD_PERMISSIONS_KEY, permissions);\nexport const RequireRoles = (roles: string[]) => SetMetadata(EXGUARD_ROLES_KEY, roles);\nexport const RequirePermissionsAll = () => SetMetadata(EXGUARD_PERMISSIONS_ALL_KEY, true);\nexport const RequireRolesAll = () => SetMetadata(EXGUARD_ROLES_ALL_KEY, true);\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n console.log('[ExGuard DEBUG] Full user object:', JSON.stringify(authResult.user, null, 2));\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n console.log('[ExGuard DEBUG] permMeta from reflector:', permMeta);\n console.log('[ExGuard DEBUG] handler:', handler);\n console.log('[ExGuard DEBUG] handler name:', handler.name);\n\n if (permMeta) {\n const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;\n const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n console.log('[ExGuard DEBUG] Required permissions:', permissions);\n console.log('[ExGuard DEBUG] User permissions:', userPermissions);\n console.log('[ExGuard DEBUG] requireAll:', requireAll);\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n const hasPermission = permissions.some((p: string) => userPermissions.includes(p));\n console.log('[ExGuard DEBUG] Permission check result:', hasPermission);\n if (!hasPermission) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;\n const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exguard-endpoint",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
package/dist/setup.cjs DELETED
@@ -1,260 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const GUARD_CONTENT = `import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';
5
- import { Reflector } from '@nestjs/core';
6
-
7
- export const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';
8
- export const EXGUARD_ROLES_KEY = 'exguard_roles';
9
-
10
- const cache = new Map();
11
- const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
12
-
13
- function getCached(key) {
14
- const entry = cache.get(key);
15
- if (!entry) return null;
16
- if (Date.now() - entry.timestamp > CACHE_TTL) {
17
- cache.delete(key);
18
- return null;
19
- }
20
- return entry.data;
21
- }
22
-
23
- function setCache(key, data) {
24
- cache.set(key, { data, timestamp: Date.now() });
25
- }
26
-
27
- @Injectable()
28
- export class ExGuardPermissionGuard {
29
- constructor(
30
- @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,
31
- private reflector: Reflector,
32
- ) {}
33
-
34
- async canActivate(context) {
35
- const request = context.switchToHttp().getRequest();
36
- const token = this.extractToken(request);
37
-
38
- if (!token) {
39
- throw new UnauthorizedException('No token provided');
40
- }
41
-
42
- if (!this.baseUrl) {
43
- console.warn('[ExGuard] Base URL not configured. Access denied.');
44
- return false;
45
- }
46
-
47
- const cacheKey = 'auth:' + token;
48
- let authResult = getCached(cacheKey);
49
-
50
- if (!authResult) {
51
- try {
52
- const verifyResponse = await fetch(this.baseUrl + '/guard/verify-token', {
53
- method: 'POST',
54
- headers: {
55
- 'Content-Type': 'application/json',
56
- 'Authorization': 'Bearer ' + token,
57
- },
58
- body: JSON.stringify({ id_token: token }),
59
- });
60
-
61
- if (!verifyResponse.ok) {
62
- throw new ForbiddenException('Invalid token');
63
- }
64
-
65
- const meResponse = await fetch(this.baseUrl + '/guard/me', {
66
- method: 'GET',
67
- headers: {
68
- 'Authorization': 'Bearer ' + token,
69
- },
70
- });
71
-
72
- if (!meResponse.ok) {
73
- throw new ForbiddenException('Failed to get user access');
74
- }
75
-
76
- const meData = await meResponse.json();
77
- authResult = { allowed: true, user: meData.data || meData };
78
- setCache(cacheKey, authResult);
79
- } catch (error) {
80
- console.error('[ExGuard] Auth error:', error);
81
- throw new ForbiddenException('Authentication failed');
82
- }
83
- }
84
-
85
- if (!authResult.allowed) {
86
- throw new ForbiddenException(authResult.error || 'Access denied');
87
- }
88
-
89
- if (!authResult.user) {
90
- throw new ForbiddenException('User not found');
91
- }
92
-
93
- const handler = context.getHandler();
94
- const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
95
-
96
- if (permMeta) {
97
- const { permissions, requireAll } = permMeta;
98
- const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap(m => m.permissions) : [];
99
-
100
- if (requireAll) {
101
- if (!permissions.every(p => userPermissions.includes(p))) {
102
- throw new ForbiddenException('Insufficient permissions');
103
- }
104
- } else {
105
- if (!permissions.some(p => userPermissions.includes(p))) {
106
- throw new ForbiddenException('Insufficient permissions');
107
- }
108
- }
109
- }
110
-
111
- const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
112
-
113
- if (roleMeta) {
114
- const { roles, requireAll } = roleMeta;
115
- const userRoles = authResult.user.roles || [];
116
-
117
- if (requireAll) {
118
- if (!roles.every(r => userRoles.includes(r))) {
119
- throw new ForbiddenException('Insufficient roles');
120
- }
121
- } else {
122
- if (!roles.some(r => userRoles.includes(r))) {
123
- throw new ForbiddenException('Insufficient roles');
124
- }
125
- }
126
- }
127
-
128
- request.user = authResult.user;
129
- return true;
130
- }
131
-
132
- extractToken(request) {
133
- const auth = request.headers ? request.headers.authorization : null;
134
- if (auth && auth.startsWith('Bearer ')) {
135
- return auth.substring(7);
136
- }
137
- return request.headers ? request.headers['x-access-token'] : null;
138
- }
139
- }
140
-
141
- export function RequirePermissions(permissions, requireAll = false) {
142
- return function (target, propertyKey, descriptor) {
143
- Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
144
- return descriptor;
145
- };
146
- }
147
-
148
- export function RequireRoles(roles, requireAll = false) {
149
- return function (target, propertyKey, descriptor) {
150
- Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
151
- return descriptor;
152
- };
153
- }
154
-
155
- export function clearExGuardCache() {
156
- cache.clear();
157
- }
158
- `;
159
-
160
- const MODULE_CONTENT = `import { Module, Global, DynamicModule } from '@nestjs/common';
161
-
162
- @Global()
163
- @Module({})
164
- export class ExGuardModule {
165
- static forRoot(options) {
166
- return {
167
- module: ExGuardModule,
168
- providers: [
169
- {
170
- provide: 'EXGUARD_BASE_URL',
171
- useValue: options.baseUrl,
172
- },
173
- ],
174
- exports: ['EXGUARD_BASE_URL'],
175
- };
176
- }
177
- }
178
- `;
179
-
180
- function ensureDir(dirPath) {
181
- if (!fs.existsSync(dirPath)) {
182
- fs.mkdirSync(dirPath, { recursive: true });
183
- }
184
- }
185
-
186
- function writeFile(filePath, content) {
187
- const dir = path.dirname(filePath);
188
- ensureDir(dir);
189
- fs.writeFileSync(filePath, content);
190
- console.log('Created: ' + filePath);
191
- }
192
-
193
- function setup() {
194
- const srcDir = path.join(process.cwd(), 'src');
195
- const exguardDir = path.join(srcDir, 'exguard');
196
-
197
- if (!fs.existsSync(srcDir)) {
198
- console.error('Error: Run this command in your NestJS project root');
199
- process.exit(1);
200
- }
201
-
202
- ensureDir(exguardDir);
203
-
204
- writeFile(path.join(exguardDir, 'exguard.guard.ts'), GUARD_CONTENT);
205
- writeFile(path.join(exguardDir, 'exguard.module.ts'), MODULE_CONTENT);
206
-
207
- console.log('');
208
- console.log('===========================================');
209
- console.log('✅ ExGuard Setup Complete!');
210
- console.log('===========================================');
211
- console.log('');
212
- console.log('Next steps:');
213
- console.log('');
214
- console.log('1. Add to app.module.ts:');
215
- console.log('');
216
- console.log(' import { ExGuardModule } from "./exguard/exguard.module";');
217
- console.log('');
218
- console.log(' @Module({');
219
- console.log(' imports: [');
220
- console.log(' ExGuardModule.forRoot({');
221
- console.log(' baseUrl: process.env.EXGUARD_BASE_URL,');
222
- console.log(' }),');
223
- console.log(' ],');
224
- console.log(' })');
225
- console.log(' export class AppModule {}');
226
- console.log('');
227
- console.log('2. Add environment variable:');
228
- console.log('');
229
- console.log(' EXGUARD_BASE_URL=https://your-guard-api.com');
230
- console.log('');
231
- console.log('3. Use in controllers:');
232
- console.log('');
233
- console.log(' import { ExGuardPermissionGuard, RequirePermissions } from "./exguard/exguard.guard";');
234
- console.log('');
235
- console.log(' @Controller("items")');
236
- console.log(' @UseGuards(ExGuardPermissionGuard)');
237
- console.log(' export class ItemsController {');
238
- console.log(' @Get() @RequirePermissions(["item:read"]) findAll() {}');
239
- console.log(' @Post() @RequirePermissions(["item:create"]) create() {}');
240
- console.log(' }');
241
- console.log('');
242
- console.log('===========================================');
243
- console.log('');
244
- console.log('💡 Caching is enabled by default (5 min TTL)');
245
- console.log('===========================================');
246
- }
247
-
248
- const args = process.argv.slice(2);
249
-
250
- if (args[0] === 'setup') {
251
- setup();
252
- } else {
253
- console.log('===========================================');
254
- console.log('ExGuard Endpoint - NestJS RBAC Guard');
255
- console.log('===========================================');
256
- console.log('');
257
- console.log('To setup in your NestJS project:');
258
- console.log(' npx exguard-endpoint setup');
259
- console.log('');
260
- }