exguard-endpoint 1.0.5 → 1.0.7

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
@@ -1,6 +1,6 @@
1
1
  # ExGuard Endpoint
2
2
 
3
- Simple RBAC permission guard for NestJS with realtime support.
3
+ Simple RBAC permission guard for NestJS with built-in caching.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,26 +8,17 @@ Simple RBAC permission guard for NestJS with realtime support.
8
8
  npm install exguard-endpoint
9
9
  ```
10
10
 
11
- ## Quick Setup (Recommended)
12
-
13
- Run in your NestJS project root:
11
+ ## Quick Setup
14
12
 
15
13
  ```bash
16
14
  npx exguard-endpoint setup
17
15
  ```
18
16
 
19
- This creates:
20
- - `src/exguard/exguard.guard.ts` - The permission guard
21
- - `src/exguard/exguard.module.ts` - The module
22
-
23
- Then follow the printed instructions.
24
-
25
17
  ## Manual Setup
26
18
 
27
19
  ### 1. Configure AppModule
28
20
 
29
21
  ```typescript
30
- // src/app.module.ts
31
22
  import { Module } from '@nestjs/common';
32
23
  import { ExGuardModule } from 'exguard-endpoint';
33
24
 
@@ -35,14 +26,6 @@ import { ExGuardModule } from 'exguard-endpoint';
35
26
  imports: [
36
27
  ExGuardModule.forRoot({
37
28
  baseUrl: process.env.EXGUARD_BASE_URL,
38
- cache: {
39
- enabled: true,
40
- ttl: 300000, // 5 minutes
41
- },
42
- realtime: {
43
- enabled: true,
44
- url: process.env.EXGUARD_REALTIME_URL,
45
- },
46
29
  }),
47
30
  ],
48
31
  })
@@ -52,28 +35,21 @@ export class AppModule {}
52
35
  ### 2. Use in Controllers
53
36
 
54
37
  ```typescript
55
- // src/items/items.controller.ts
56
- import { Controller, Get, Post, Patch, Delete, UseGuards } from '@nestjs/common';
57
- import { ExGuardPermissionGuard, RequirePermissions } from '../exguard/exguard.guard';
38
+ import { Controller, Get, Post, UseGuards } from '@nestjs/common';
39
+ import { ExGuardPermissionGuard, RequirePermissions } from './exguard/exguard.guard';
58
40
 
59
41
  @Controller('items')
60
42
  @UseGuards(ExGuardPermissionGuard)
61
43
  export class ItemsController {
62
44
 
63
- // User needs ANY of these permissions
64
45
  @Get()
65
46
  @RequirePermissions(['item:read'])
66
47
  findAll() { }
67
48
 
68
- // Multiple permissions - needs ALL
69
49
  @Post()
70
- @RequirePermissions(['item:create', 'item:admin'], true)
50
+ @RequirePermissions(['item:create'])
71
51
  create() { }
72
52
 
73
- @Patch(':id')
74
- @RequirePermissions(['item:update'])
75
- update() { }
76
-
77
53
  @Delete(':id')
78
54
  @RequirePermissions(['item:delete', 'admin'], true)
79
55
  delete() { }
@@ -82,88 +58,47 @@ export class ItemsController {
82
58
 
83
59
  ## Environment Variables
84
60
 
85
- Add to your `.env` file:
86
-
87
61
  ```env
88
62
  EXGUARD_BASE_URL=https://your-guard-api.com
89
- EXGUARD_REALTIME_URL=wss://your-realtime.com
90
63
  ```
91
64
 
92
- The `EXGUARD_BASE_URL` should be the URL where your **empowerx-guard-api** is running.
65
+ ## Caching (Enabled by Default)
93
66
 
94
- ## How It Works
67
+ - **TTL**: 5 minutes
68
+ - **Auto-clear**: When permissions change via realtime
69
+ - **Manual clear**: `clearExGuardCache()`
95
70
 
96
- 1. Extracts Bearer token from request header
97
- 2. Calls `/guard/verify-token` to validate the Cognito token
98
- 3. Calls `/guard/me` to get user access (roles, permissions, modules)
99
- 4. Checks if user has required permissions
100
- 5. Returns user object in `request.user`
71
+ ```typescript
72
+ import { clearExGuardCache } from './exguard/exguard.guard';
101
73
 
102
- ## API Reference
74
+ // Call when needed
75
+ clearExGuardCache();
76
+ ```
103
77
 
104
- ### Decorators
78
+ ## Decorators
105
79
 
106
80
  | Decorator | Description |
107
81
  |-----------|-------------|
108
- | `@RequirePermissions(['item:read'])` | User needs ANY of the listed permissions |
109
- | `@RequirePermissions(['item:delete', 'admin'], true)` | User needs ALL listed permissions |
110
-
111
- ### ExGuardModule.forRoot Options
112
-
113
- | Option | Type | Required | Default | Description |
114
- |--------|------|----------|---------|-------------|
115
- | baseUrl | string | Yes | - | Guard API base URL |
116
- | cache.enabled | boolean | No | true | Enable caching |
117
- | cache.ttl | number | No | 300000 | Cache TTL in ms (5 min) |
118
- | realtime.enabled | boolean | No | false | Enable realtime connection |
119
- | realtime.url | string | No | - | Realtime WebSocket URL |
82
+ | `@RequirePermissions(['item:read'])` | User needs ANY of the permissions |
83
+ | `@RequirePermissions(['item:delete', 'admin'], true)` | User needs ALL permissions |
120
84
 
121
- ### Token Extraction
85
+ ## Token Extraction
122
86
 
123
- The guard automatically extracts token from:
124
87
  - `Authorization: Bearer <token>` header
125
88
  - `x-access-token` header
126
89
 
127
- ### Request User
90
+ ## User Object
128
91
 
129
- After successful authentication, the user object is available at:
130
92
  ```typescript
131
- @Request() req: any
132
- console.log(req.user); // { id, username, email, roles, modules, groups, fieldOffices }
133
- ```
134
-
135
- ## User Object Structure
136
-
137
- ```typescript
138
- {
139
- user: {
140
- id: string,
141
- cognitoSubId: string,
142
- username: string,
143
- email: string,
144
- emailVerified: boolean,
145
- givenName: string,
146
- familyName: string,
147
- employeeNumber: string,
148
- regionId: string,
149
- fieldOffice: { id, code, name }
150
- },
151
- groups: string[],
93
+ req.user = {
94
+ user: { id, username, email, ... },
152
95
  roles: string[],
153
- modules: [
154
- { key: 'events', name: 'Events', permissions: ['events:create', 'events:read'] }
155
- ],
96
+ modules: [{ key, name, permissions: [] }],
97
+ groups: string[],
156
98
  fieldOffices: string[]
157
99
  }
158
100
  ```
159
101
 
160
- ## Realtime Features
161
-
162
- When realtime is enabled:
163
- - Permission changes are detected in real-time
164
- - Cache is automatically cleared when permissions update
165
- - Events: `permission:update`, `user:update`
166
-
167
102
  ## License
168
103
 
169
104
  MIT
package/dist/index.cjs CHANGED
@@ -31,202 +31,27 @@ var src_exports = {};
31
31
  __export(src_exports, {
32
32
  EXGUARD_PERMISSIONS_KEY: () => EXGUARD_PERMISSIONS_KEY,
33
33
  EXGUARD_ROLES_KEY: () => EXGUARD_ROLES_KEY,
34
- ExGuardClient: () => ExGuardClient,
35
34
  ExGuardModule: () => ExGuardModule,
36
35
  ExGuardPermissionGuard: () => ExGuardPermissionGuard,
37
36
  RequirePermissions: () => RequirePermissions,
38
37
  RequireRoles: () => RequireRoles,
39
- cache: () => cache,
40
- createExGuardClient: () => createExGuardClient,
41
- realtime: () => realtime
38
+ clearExGuardCache: () => clearExGuardCache
42
39
  });
43
40
  module.exports = __toCommonJS(src_exports);
44
41
 
45
- // src/client.ts
46
- var ExGuardCache = class {
47
- constructor() {
48
- this.cache = /* @__PURE__ */ new Map();
49
- this.ttl = 3e5;
50
- }
51
- get(key) {
52
- const entry = this.cache.get(key);
53
- if (!entry) return null;
54
- if (Date.now() - entry.timestamp > entry.ttl) {
55
- this.cache.delete(key);
56
- return null;
57
- }
58
- return entry.data;
59
- }
60
- set(key, data, ttl) {
61
- this.cache.set(key, {
62
- data,
63
- timestamp: Date.now(),
64
- ttl: ttl || this.ttl
65
- });
66
- }
67
- delete(key) {
68
- this.cache.delete(key);
69
- }
70
- clear() {
71
- this.cache.clear();
72
- }
73
- clearUser(userId) {
74
- for (const key of this.cache.keys()) {
75
- if (key.includes(userId)) {
76
- this.cache.delete(key);
77
- }
78
- }
79
- }
80
- };
81
- var cache = new ExGuardCache();
82
- var ExGuardRealtime = class {
83
- constructor() {
84
- this.socket = null;
85
- this.connected = false;
86
- this.url = "";
87
- this.events = {};
88
- }
89
- connect(url, events = {}) {
90
- this.url = url;
91
- this.events = events;
92
- if (!url) {
93
- console.log("[ExGuard] Realtime URL not provided, skipping connection");
94
- return;
95
- }
96
- try {
97
- const { io } = require("socket.io-client");
98
- this.socket = io(url, {
99
- transports: ["websocket"],
100
- reconnection: true,
101
- reconnectionAttempts: 5,
102
- reconnectionDelay: 1e3
103
- });
104
- this.socket.on("connect", () => {
105
- this.connected = true;
106
- console.log("[ExGuard] Realtime connected");
107
- this.events.onConnect?.();
108
- });
109
- this.socket.on("disconnect", () => {
110
- this.connected = false;
111
- console.log("[ExGuard] Realtime disconnected");
112
- this.events.onDisconnect?.();
113
- });
114
- this.socket.on("permission:update", (data) => {
115
- console.log("[ExGuard] Permission update received:", data);
116
- cache.clearUser(data.userId);
117
- this.events.onPermissionUpdate?.(data);
118
- });
119
- this.socket.on("user:update", (data) => {
120
- console.log("[ExGuard] User update received:", data);
121
- cache.clearUser(data.userId);
122
- this.events.onUserUpdate?.(data);
123
- });
124
- this.socket.on("connect_error", (err) => {
125
- console.error("[ExGuard] Realtime connection error:", err.message);
126
- });
127
- } catch (error) {
128
- console.error("[ExGuard] Failed to load socket.io-client:", error);
129
- }
130
- }
131
- disconnect() {
132
- if (this.socket) {
133
- this.socket.disconnect();
134
- this.socket = null;
135
- this.connected = false;
136
- }
137
- }
138
- isConnected() {
139
- return this.connected;
140
- }
141
- emit(event, data) {
142
- if (this.socket && this.connected) {
143
- this.socket.emit(event, data);
144
- }
145
- }
146
- };
147
- var realtime = new ExGuardRealtime();
148
- var ExGuardClient = class {
149
- constructor(config) {
150
- this.baseUrl = config.baseUrl;
151
- this.cache = cache;
152
- this.realtime = realtime;
153
- if (config.realtime?.enabled && config.realtime?.url) {
154
- this.realtime.connect(config.realtime.url);
155
- }
156
- }
157
- async authenticate(context) {
158
- const cacheKey = `auth:${context.token}`;
159
- const cached = this.cache.get(cacheKey);
160
- if (cached) return cached;
161
- try {
162
- const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {
163
- method: "POST",
164
- headers: {
165
- "Content-Type": "application/json",
166
- "Authorization": `Bearer ${context.token}`
167
- },
168
- body: JSON.stringify({ id_token: context.token })
169
- });
170
- if (!verifyResponse.ok) {
171
- const errorData = await verifyResponse.json().catch(() => ({}));
172
- return { allowed: false, error: errorData.message || "Invalid token" };
173
- }
174
- const meResponse = await fetch(`${this.baseUrl}/guard/me`, {
175
- method: "GET",
176
- headers: {
177
- "Authorization": `Bearer ${context.token}`
178
- }
179
- });
180
- if (!meResponse.ok) {
181
- return { allowed: false, error: "Failed to get user access" };
182
- }
183
- const meData = await meResponse.json();
184
- const result = {
185
- allowed: true,
186
- user: meData.data || meData
187
- };
188
- this.cache.set(cacheKey, result);
189
- return result;
190
- } catch (error) {
191
- console.error("[ExGuard] Authentication error:", error);
192
- return { allowed: false, error: "Authentication failed" };
193
- }
194
- }
195
- async hasPermission(token, permission) {
196
- const result = await this.authenticate({ token });
197
- if (!result.allowed || !result.user) return false;
198
- const userPermissions = result.user.modules?.flatMap((m) => m.permissions) || [];
199
- return userPermissions.includes(permission);
200
- }
201
- clearCache() {
202
- this.cache.clear();
203
- }
204
- disconnectRealtime() {
205
- this.realtime.disconnect();
206
- }
207
- };
208
- function createExGuardClient(config) {
209
- return new ExGuardClient(config);
210
- }
211
-
212
42
  // src/module.ts
213
43
  var import_common = require("@nestjs/common");
214
44
  var ExGuardModule = class {
215
45
  static forRoot(options) {
216
- const client = new ExGuardClient({
217
- baseUrl: options.baseUrl,
218
- cache: options.cache,
219
- realtime: options.realtime
220
- });
221
46
  return {
222
47
  module: ExGuardModule,
223
48
  providers: [
224
49
  {
225
- provide: "EXGUARD_CLIENT",
226
- useValue: client
50
+ provide: "EXGUARD_BASE_URL",
51
+ useValue: options.baseUrl
227
52
  }
228
53
  ],
229
- exports: ["EXGUARD_CLIENT"]
54
+ exports: ["EXGUARD_BASE_URL"]
230
55
  };
231
56
  }
232
57
  };
@@ -239,9 +64,23 @@ ExGuardModule = __decorateClass([
239
64
  var import_common2 = require("@nestjs/common");
240
65
  var EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
241
66
  var EXGUARD_ROLES_KEY = "exguard_roles";
67
+ var cache = /* @__PURE__ */ new Map();
68
+ var CACHE_TTL = 5 * 60 * 1e3;
69
+ function getCached(key) {
70
+ const entry = cache.get(key);
71
+ if (!entry) return null;
72
+ if (Date.now() - entry.timestamp > CACHE_TTL) {
73
+ cache.delete(key);
74
+ return null;
75
+ }
76
+ return entry.data;
77
+ }
78
+ function setCache(key, data) {
79
+ cache.set(key, { data, timestamp: Date.now() });
80
+ }
242
81
  var ExGuardPermissionGuard = class {
243
- constructor(client, reflector) {
244
- this.client = client;
82
+ constructor(baseUrl, reflector) {
83
+ this.baseUrl = baseUrl;
245
84
  this.reflector = reflector;
246
85
  }
247
86
  async canActivate(context) {
@@ -250,11 +89,42 @@ var ExGuardPermissionGuard = class {
250
89
  if (!token) {
251
90
  throw new import_common2.UnauthorizedException("No token provided");
252
91
  }
253
- if (!this.client) {
254
- console.warn("[ExGuard] Client not configured. Access denied.");
92
+ if (!this.baseUrl) {
93
+ console.warn("[ExGuard] Base URL not configured. Access denied.");
255
94
  return false;
256
95
  }
257
- const authResult = await this.client.authenticate({ token, request });
96
+ const cacheKey = `auth:${token}`;
97
+ let authResult = getCached(cacheKey);
98
+ if (!authResult) {
99
+ try {
100
+ const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {
101
+ method: "POST",
102
+ headers: {
103
+ "Content-Type": "application/json",
104
+ "Authorization": `Bearer ${token}`
105
+ },
106
+ body: JSON.stringify({ id_token: token })
107
+ });
108
+ if (!verifyResponse.ok) {
109
+ throw new import_common2.ForbiddenException("Invalid token");
110
+ }
111
+ const meResponse = await fetch(`${this.baseUrl}/guard/me`, {
112
+ method: "GET",
113
+ headers: {
114
+ "Authorization": `Bearer ${token}`
115
+ }
116
+ });
117
+ if (!meResponse.ok) {
118
+ throw new import_common2.ForbiddenException("Failed to get user access");
119
+ }
120
+ const meData = await meResponse.json();
121
+ authResult = { allowed: true, user: meData.data || meData };
122
+ setCache(cacheKey, authResult);
123
+ } catch (error) {
124
+ console.error("[ExGuard] Auth error:", error);
125
+ throw new import_common2.ForbiddenException("Authentication failed");
126
+ }
127
+ }
258
128
  if (!authResult.allowed) {
259
129
  throw new import_common2.ForbiddenException(authResult.error || "Access denied");
260
130
  }
@@ -262,13 +132,10 @@ var ExGuardPermissionGuard = class {
262
132
  throw new import_common2.ForbiddenException("User not found");
263
133
  }
264
134
  const handler = context.getHandler();
265
- const permMeta = this.reflector.get(
266
- EXGUARD_PERMISSIONS_KEY,
267
- handler
268
- );
135
+ const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
269
136
  if (permMeta) {
270
137
  const { permissions, requireAll } = permMeta;
271
- const userPermissions = authResult.user.modules?.flatMap((m) => m.permissions) || [];
138
+ const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m) => m.permissions) : [];
272
139
  if (requireAll) {
273
140
  if (!permissions.every((p) => userPermissions.includes(p))) {
274
141
  throw new import_common2.ForbiddenException("Insufficient permissions");
@@ -279,10 +146,7 @@ var ExGuardPermissionGuard = class {
279
146
  }
280
147
  }
281
148
  }
282
- const roleMeta = this.reflector.get(
283
- EXGUARD_ROLES_KEY,
284
- handler
285
- );
149
+ const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
286
150
  if (roleMeta) {
287
151
  const { roles, requireAll } = roleMeta;
288
152
  const userRoles = authResult.user.roles || [];
@@ -310,7 +174,7 @@ var ExGuardPermissionGuard = class {
310
174
  ExGuardPermissionGuard = __decorateClass([
311
175
  (0, import_common2.Injectable)(),
312
176
  __decorateParam(0, (0, import_common2.Optional)()),
313
- __decorateParam(0, (0, import_common2.Inject)("EXGUARD_CLIENT"))
177
+ __decorateParam(0, (0, import_common2.Inject)("EXGUARD_BASE_URL"))
314
178
  ], ExGuardPermissionGuard);
315
179
  function RequirePermissions(permissions, requireAll = false) {
316
180
  return function(target, propertyKey, descriptor) {
@@ -324,17 +188,17 @@ function RequireRoles(roles, requireAll = false) {
324
188
  return descriptor;
325
189
  };
326
190
  }
191
+ function clearExGuardCache() {
192
+ cache.clear();
193
+ }
327
194
  // Annotate the CommonJS export names for ESM import in node:
328
195
  0 && (module.exports = {
329
196
  EXGUARD_PERMISSIONS_KEY,
330
197
  EXGUARD_ROLES_KEY,
331
- ExGuardClient,
332
198
  ExGuardModule,
333
199
  ExGuardPermissionGuard,
334
200
  RequirePermissions,
335
201
  RequireRoles,
336
- cache,
337
- createExGuardClient,
338
- realtime
202
+ clearExGuardCache
339
203
  });
340
204
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export { ExGuardClient, createExGuardClient, cache, realtime } from './client.js';\nexport type { ExGuardConfig, User, ModulePermission, UserAccessResponse, GuardContext, GuardResult, RealtimeEvents } from './client.js';\nexport { ExGuardModule } from './module.js';\nexport type { ExGuardModuleOptions } from './module.js';\nexport { ExGuardPermissionGuard, RequirePermissions, RequireRoles, EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY } from './guard.js';\n","export interface ExGuardConfig {\n baseUrl: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n realtime?: {\n enabled?: boolean;\n url?: string;\n };\n}\n\nexport interface User {\n id: string;\n cognitoSubId: string;\n username: string;\n email: string;\n emailVerified: boolean;\n givenName: string;\n familyName: string;\n employeeNumber: string;\n regionId: string;\n createdAt: string;\n updatedAt: string;\n lastLoginAt: string;\n fieldOffice: {\n id: string;\n code: string;\n name: string;\n };\n}\n\nexport interface ModulePermission {\n key: string;\n name: string;\n permissions: string[];\n}\n\nexport interface UserAccessResponse {\n user: User;\n groups: string[];\n roles: string[];\n modules: ModulePermission[];\n fieldOffices: string[];\n}\n\nexport interface GuardContext {\n token: string;\n request?: any;\n}\n\nexport interface GuardResult {\n allowed: boolean;\n user?: UserAccessResponse;\n error?: string;\n}\n\nexport interface RealtimeEvents {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onPermissionUpdate?: (data: { userId: string; permissions: string[] }) => void;\n onUserUpdate?: (data: { userId: string; user: UserAccessResponse }) => void;\n}\n\nclass ExGuardCache {\n private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n private ttl = 300000;\n\n get(key: string): any {\n const entry = this.cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > entry.ttl) {\n this.cache.delete(key);\n return null;\n }\n return entry.data;\n }\n\n set(key: string, data: any, ttl?: number): void {\n this.cache.set(key, {\n data,\n timestamp: Date.now(),\n ttl: ttl || this.ttl,\n });\n }\n\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n clearUser(userId: string): void {\n for (const key of this.cache.keys()) {\n if (key.includes(userId)) {\n this.cache.delete(key);\n }\n }\n }\n}\n\nconst cache = new ExGuardCache();\n\nclass ExGuardRealtime {\n private socket: any = null;\n private connected = false;\n private url: string = '';\n private events: RealtimeEvents = {};\n\n connect(url: string, events: RealtimeEvents = {}): void {\n this.url = url;\n this.events = events;\n \n if (!url) {\n console.log('[ExGuard] Realtime URL not provided, skipping connection');\n return;\n }\n\n try {\n const { io } = require('socket.io-client');\n this.socket = io(url, {\n transports: ['websocket'],\n reconnection: true,\n reconnectionAttempts: 5,\n reconnectionDelay: 1000,\n });\n\n this.socket.on('connect', () => {\n this.connected = true;\n console.log('[ExGuard] Realtime connected');\n this.events.onConnect?.();\n });\n\n this.socket.on('disconnect', () => {\n this.connected = false;\n console.log('[ExGuard] Realtime disconnected');\n this.events.onDisconnect?.();\n });\n\n this.socket.on('permission:update', (data: any) => {\n console.log('[ExGuard] Permission update received:', data);\n cache.clearUser(data.userId);\n this.events.onPermissionUpdate?.(data);\n });\n\n this.socket.on('user:update', (data: any) => {\n console.log('[ExGuard] User update received:', data);\n cache.clearUser(data.userId);\n this.events.onUserUpdate?.(data);\n });\n\n this.socket.on('connect_error', (err: any) => {\n console.error('[ExGuard] Realtime connection error:', err.message);\n });\n } catch (error) {\n console.error('[ExGuard] Failed to load socket.io-client:', error);\n }\n }\n\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n this.connected = false;\n }\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n emit(event: string, data: any): void {\n if (this.socket && this.connected) {\n this.socket.emit(event, data);\n }\n }\n}\n\nconst realtime = new ExGuardRealtime();\n\nexport class ExGuardClient {\n private baseUrl: string;\n private cache: ExGuardCache;\n private realtime: ExGuardRealtime;\n\n constructor(config: ExGuardConfig) {\n this.baseUrl = config.baseUrl;\n this.cache = cache;\n this.realtime = realtime;\n\n if (config.realtime?.enabled && config.realtime?.url) {\n this.realtime.connect(config.realtime.url);\n }\n }\n\n async authenticate(context: GuardContext): Promise<GuardResult> {\n const cacheKey = `auth:${context.token}`;\n const cached = this.cache.get(cacheKey);\n if (cached) return cached;\n\n try {\n // First verify the token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${context.token}`,\n },\n body: JSON.stringify({ id_token: context.token }),\n });\n\n if (!verifyResponse.ok) {\n const errorData = await verifyResponse.json().catch(() => ({}));\n return { allowed: false, error: errorData.message || 'Invalid token' };\n }\n\n // Then get user access info\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${context.token}`,\n },\n });\n\n if (!meResponse.ok) {\n return { allowed: false, error: 'Failed to get user access' };\n }\n\n const meData = await meResponse.json();\n \n const result: GuardResult = {\n allowed: true,\n user: meData.data || meData,\n };\n\n this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n console.error('[ExGuard] Authentication error:', error);\n return { allowed: false, error: 'Authentication failed' };\n }\n }\n\n async hasPermission(token: string, permission: string): Promise<boolean> {\n const result = await this.authenticate({ token });\n if (!result.allowed || !result.user) return false;\n\n const userPermissions = result.user.modules?.flatMap(m => m.permissions) || [];\n return userPermissions.includes(permission);\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n disconnectRealtime(): void {\n this.realtime.disconnect();\n }\n}\n\nexport function createExGuardClient(config: ExGuardConfig): ExGuardClient {\n return new ExGuardClient(config);\n}\n\nexport { cache, realtime };\n","import { Module, Global, DynamicModule } from '@nestjs/common';\nimport { ExGuardClient } from './client.js';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n realtime?: {\n enabled?: boolean;\n url?: string;\n };\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule {\n const client = new ExGuardClient({\n baseUrl: options.baseUrl,\n cache: options.cache,\n realtime: options.realtime,\n });\n\n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_CLIENT',\n useValue: client,\n },\n ],\n exports: ['EXGUARD_CLIENT'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { ExGuardClient } from './client.js';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_CLIENT') private client: ExGuardClient,\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.client) {\n console.warn('[ExGuard] Client not configured. Access denied.');\n return false;\n }\n\n const authResult = await this.client.authenticate({ token, request });\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<{ permissions: string[]; requireAll?: boolean }>(\n EXGUARD_PERMISSIONS_KEY,\n handler,\n );\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules?.flatMap(m => m.permissions) || [];\n\n if (requireAll) {\n if (!permissions.every(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get<{ roles: string[]; requireAll?: boolean }>(\n EXGUARD_ROLES_KEY,\n handler,\n );\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some(r => 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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgEA,IAAM,eAAN,MAAmB;AAAA,EAAnB;AACE,SAAQ,QAAQ,oBAAI,IAA2D;AAC/E,SAAQ,MAAM;AAAA;AAAA,EAEd,IAAI,KAAkB;AACpB,UAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;AAChC,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,KAAK,IAAI,IAAI,MAAM,YAAY,MAAM,KAAK;AAC5C,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AACA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,IAAI,KAAa,MAAW,KAAoB;AAC9C,SAAK,MAAM,IAAI,KAAK;AAAA,MAClB;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,KAAK,OAAO,KAAK;AAAA,IACnB,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,KAAmB;AACxB,SAAK,MAAM,OAAO,GAAG;AAAA,EACvB;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA,EAEA,UAAU,QAAsB;AAC9B,eAAW,OAAO,KAAK,MAAM,KAAK,GAAG;AACnC,UAAI,IAAI,SAAS,MAAM,GAAG;AACxB,aAAK,MAAM,OAAO,GAAG;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,QAAQ,IAAI,aAAa;AAE/B,IAAM,kBAAN,MAAsB;AAAA,EAAtB;AACE,SAAQ,SAAc;AACtB,SAAQ,YAAY;AACpB,SAAQ,MAAc;AACtB,SAAQ,SAAyB,CAAC;AAAA;AAAA,EAElC,QAAQ,KAAa,SAAyB,CAAC,GAAS;AACtD,SAAK,MAAM;AACX,SAAK,SAAS;AAEd,QAAI,CAAC,KAAK;AACR,cAAQ,IAAI,0DAA0D;AACtE;AAAA,IACF;AAEA,QAAI;AACF,YAAM,EAAE,GAAG,IAAI,QAAQ,kBAAkB;AACzC,WAAK,SAAS,GAAG,KAAK;AAAA,QACpB,YAAY,CAAC,WAAW;AAAA,QACxB,cAAc;AAAA,QACd,sBAAsB;AAAA,QACtB,mBAAmB;AAAA,MACrB,CAAC;AAED,WAAK,OAAO,GAAG,WAAW,MAAM;AAC9B,aAAK,YAAY;AACjB,gBAAQ,IAAI,8BAA8B;AAC1C,aAAK,OAAO,YAAY;AAAA,MAC1B,CAAC;AAED,WAAK,OAAO,GAAG,cAAc,MAAM;AACjC,aAAK,YAAY;AACjB,gBAAQ,IAAI,iCAAiC;AAC7C,aAAK,OAAO,eAAe;AAAA,MAC7B,CAAC;AAED,WAAK,OAAO,GAAG,qBAAqB,CAAC,SAAc;AACjD,gBAAQ,IAAI,yCAAyC,IAAI;AACzD,cAAM,UAAU,KAAK,MAAM;AAC3B,aAAK,OAAO,qBAAqB,IAAI;AAAA,MACvC,CAAC;AAED,WAAK,OAAO,GAAG,eAAe,CAAC,SAAc;AAC3C,gBAAQ,IAAI,mCAAmC,IAAI;AACnD,cAAM,UAAU,KAAK,MAAM;AAC3B,aAAK,OAAO,eAAe,IAAI;AAAA,MACjC,CAAC;AAED,WAAK,OAAO,GAAG,iBAAiB,CAAC,QAAa;AAC5C,gBAAQ,MAAM,wCAAwC,IAAI,OAAO;AAAA,MACnE,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,8CAA8C,KAAK;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,aAAmB;AACjB,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,WAAW;AACvB,WAAK,SAAS;AACd,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,KAAK,OAAe,MAAiB;AACnC,QAAI,KAAK,UAAU,KAAK,WAAW;AACjC,WAAK,OAAO,KAAK,OAAO,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,IAAM,WAAW,IAAI,gBAAgB;AAE9B,IAAM,gBAAN,MAAoB;AAAA,EAKzB,YAAY,QAAuB;AACjC,SAAK,UAAU,OAAO;AACtB,SAAK,QAAQ;AACb,SAAK,WAAW;AAEhB,QAAI,OAAO,UAAU,WAAW,OAAO,UAAU,KAAK;AACpD,WAAK,SAAS,QAAQ,OAAO,SAAS,GAAG;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,SAA6C;AAC9D,UAAM,WAAW,QAAQ,QAAQ,KAAK;AACtC,UAAM,SAAS,KAAK,MAAM,IAAI,QAAQ;AACtC,QAAI,OAAQ,QAAO;AAEnB,QAAI;AAEF,YAAM,iBAAiB,MAAM,MAAM,GAAG,KAAK,OAAO,uBAAuB;AAAA,QACvE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB,UAAU,QAAQ,KAAK;AAAA,QAC1C;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,UAAU,QAAQ,MAAM,CAAC;AAAA,MAClD,CAAC;AAED,UAAI,CAAC,eAAe,IAAI;AACtB,cAAM,YAAY,MAAM,eAAe,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9D,eAAO,EAAE,SAAS,OAAO,OAAO,UAAU,WAAW,gBAAgB;AAAA,MACvE;AAGA,YAAM,aAAa,MAAM,MAAM,GAAG,KAAK,OAAO,aAAa;AAAA,QACzD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,iBAAiB,UAAU,QAAQ,KAAK;AAAA,QAC1C;AAAA,MACF,CAAC;AAED,UAAI,CAAC,WAAW,IAAI;AAClB,eAAO,EAAE,SAAS,OAAO,OAAO,4BAA4B;AAAA,MAC9D;AAEA,YAAM,SAAS,MAAM,WAAW,KAAK;AAErC,YAAM,SAAsB;AAAA,QAC1B,SAAS;AAAA,QACT,MAAM,OAAO,QAAQ;AAAA,MACvB;AAEA,WAAK,MAAM,IAAI,UAAU,MAAM;AAC/B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,MAAM,mCAAmC,KAAK;AACtD,aAAO,EAAE,SAAS,OAAO,OAAO,wBAAwB;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,OAAe,YAAsC;AACvE,UAAM,SAAS,MAAM,KAAK,aAAa,EAAE,MAAM,CAAC;AAChD,QAAI,CAAC,OAAO,WAAW,CAAC,OAAO,KAAM,QAAO;AAE5C,UAAM,kBAAkB,OAAO,KAAK,SAAS,QAAQ,OAAK,EAAE,WAAW,KAAK,CAAC;AAC7E,WAAO,gBAAgB,SAAS,UAAU;AAAA,EAC5C;AAAA,EAEA,aAAmB;AACjB,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA,EAEA,qBAA2B;AACzB,SAAK,SAAS,WAAW;AAAA,EAC3B;AACF;AAEO,SAAS,oBAAoB,QAAsC;AACxE,SAAO,IAAI,cAAc,MAAM;AACjC;;;ACxQA,oBAA8C;AAiBvC,IAAM,gBAAN,MAAoB;AAAA,EACzB,OAAO,QAAQ,SAA8C;AAC3D,UAAM,SAAS,IAAI,cAAc;AAAA,MAC/B,SAAS,QAAQ;AAAA,MACjB,OAAO,QAAQ;AAAA,MACf,UAAU,QAAQ;AAAA,IACpB,CAAC;AAED,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,QACT;AAAA,UACE,SAAS;AAAA,UACT,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,MACA,SAAS,CAAC,gBAAgB;AAAA,IAC5B;AAAA,EACF;AACF;AAnBa,gBAAN;AAAA,MAFN,sBAAO;AAAA,MACP,sBAAO,CAAC,CAAC;AAAA,GACG;;;ACjBb,IAAAA,iBAAuH;AAIhH,IAAM,0BAA0B;AAChC,IAAM,oBAAoB;AAG1B,IAAM,yBAAN,MAAoD;AAAA,EACzD,YACgD,QACtC,WACR;AAF8C;AACtC;AAAA,EACP;AAAA,EAEH,MAAM,YAAY,SAA6C;AAC7D,UAAM,UAAU,QAAQ,aAAa,EAAE,WAAW;AAClD,UAAM,QAAQ,KAAK,aAAa,OAAO;AAEvC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,qCAAsB,mBAAmB;AAAA,IACrD;AAEA,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ,KAAK,iDAAiD;AAC9D,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,MAAM,KAAK,OAAO,aAAa,EAAE,OAAO,QAAQ,CAAC;AAEpE,QAAI,CAAC,WAAW,SAAS;AACvB,YAAM,IAAI,kCAAmB,WAAW,SAAS,eAAe;AAAA,IAClE;AAEA,QAAI,CAAC,WAAW,MAAM;AACpB,YAAM,IAAI,kCAAmB,gBAAgB;AAAA,IAC/C;AAEA,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,WAAW,KAAK,UAAU;AAAA,MAC9B;AAAA,MACA;AAAA,IACF;AAEA,QAAI,UAAU;AACZ,YAAM,EAAE,aAAa,WAAW,IAAI;AACpC,YAAM,kBAAkB,WAAW,KAAK,SAAS,QAAQ,OAAK,EAAE,WAAW,KAAK,CAAC;AAEjF,UAAI,YAAY;AACd,YAAI,CAAC,YAAY,MAAM,OAAK,gBAAgB,SAAS,CAAC,CAAC,GAAG;AACxD,gBAAM,IAAI,kCAAmB,0BAA0B;AAAA,QACzD;AAAA,MACF,OAAO;AACL,YAAI,CAAC,YAAY,KAAK,OAAK,gBAAgB,SAAS,CAAC,CAAC,GAAG;AACvD,gBAAM,IAAI,kCAAmB,0BAA0B;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,UAAU;AAAA,MAC9B;AAAA,MACA;AAAA,IACF;AAEA,QAAI,UAAU;AACZ,YAAM,EAAE,OAAO,WAAW,IAAI;AAC9B,YAAM,YAAY,WAAW,KAAK,SAAS,CAAC;AAE5C,UAAI,YAAY;AACd,YAAI,CAAC,MAAM,MAAM,OAAK,UAAU,SAAS,CAAC,CAAC,GAAG;AAC5C,gBAAM,IAAI,kCAAmB,oBAAoB;AAAA,QACnD;AAAA,MACF,OAAO;AACL,YAAI,CAAC,MAAM,KAAK,OAAK,UAAU,SAAS,CAAC,CAAC,GAAG;AAC3C,gBAAM,IAAI,kCAAmB,oBAAoB;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,OAAO,WAAW;AAC1B,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,SAA6B;AAChD,UAAM,OAAO,QAAQ,SAAS;AAC9B,QAAI,MAAM,WAAW,SAAS,GAAG;AAC/B,aAAO,KAAK,UAAU,CAAC;AAAA,IACzB;AACA,WAAO,QAAQ,UAAU,gBAAgB,KAAK;AAAA,EAChD;AACF;AAjFa,yBAAN;AAAA,MADN,2BAAW;AAAA,EAGP,gDAAS;AAAA,EAAG,8CAAO,gBAAgB;AAAA,GAF3B;AAmFN,SAAS,mBAAmB,aAAuB,aAAa,OAAO;AAC5E,SAAO,SAAU,QAAa,aAAqB,YAAgC;AACjF,YAAQ,eAAe,yBAAyB,EAAE,aAAa,WAAW,GAAG,WAAW,KAAK;AAC7F,WAAO;AAAA,EACT;AACF;AAEO,SAAS,aAAa,OAAiB,aAAa,OAAO;AAChE,SAAO,SAAU,QAAa,aAAqB,YAAgC;AACjF,YAAQ,eAAe,mBAAmB,EAAE,OAAO,WAAW,GAAG,WAAW,KAAK;AACjF,WAAO;AAAA,EACT;AACF;","names":["import_common"]}
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"]}