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 +14 -43
- package/dist/index.cjs +21 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -7
- package/dist/index.d.ts +5 -7
- package/dist/index.js +22 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/setup.cjs +0 -260
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
|
-
|
|
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
|
-
|
|
23
|
+
## Usage
|
|
20
24
|
|
|
21
25
|
```typescript
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
45
|
+
## Environment
|
|
60
46
|
|
|
61
47
|
```env
|
|
62
|
-
EXGUARD_BASE_URL=
|
|
48
|
+
EXGUARD_BASE_URL=http://localhost:3000
|
|
63
49
|
```
|
|
64
50
|
|
|
65
|
-
## Caching
|
|
51
|
+
## Caching
|
|
66
52
|
|
|
67
53
|
- **TTL**: 5 minutes
|
|
68
|
-
- **
|
|
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
|
|
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(
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -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,
|
|
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,
|
|
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(
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
|
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":";;;;;;;;;;;;;AAAA,SAAS,QAAQ,cAA6B;AAYvC,IAAM,gBAAN,MAAoB;AAAA,EACzB,OAAO,QAAQ,SAA8C;AAC3D,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,QACT;AAAA,UACE,SAAS;AAAA,UACT,UAAU,QAAQ;AAAA,QACpB;AAAA,MACF;AAAA,MACA,SAAS,CAAC,kBAAkB;AAAA,IAC9B;AAAA,EACF;AACF;AAba,gBAAN;AAAA,EAFN,OAAO;AAAA,EACP,OAAO,CAAC,CAAC;AAAA,GACG;;;ACZb,SAAS,YAA2C,uBAAuB,oBAAoB,QAAQ,gBAAgB;AAGhH,IAAM,0BAA0B;AAChC,IAAM,oBAAoB;AAEjC,IAAM,QAAQ,oBAAI,IAA8C;AAChE,IAAM,YAAY,IAAI,KAAK;AAE3B,SAAS,UAAU,KAAkB;AACnC,QAAM,QAAQ,MAAM,IAAI,GAAG;AAC3B,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,KAAK,IAAI,IAAI,MAAM,YAAY,WAAW;AAC5C,UAAM,OAAO,GAAG;AAChB,WAAO;AAAA,EACT;AACA,SAAO,MAAM;AACf;AAEA,SAAS,SAAS,KAAa,MAAiB;AAC9C,QAAM,IAAI,KAAK,EAAE,MAAM,WAAW,KAAK,IAAI,EAAE,CAAC;AAChD;AAGO,IAAM,yBAAN,MAAoD;AAAA,EACzD,YACkD,SACxC,WACR;AAFgD;AACxC;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,sBAAsB,mBAAmB;AAAA,IACrD;AAEA,QAAI,CAAC,KAAK,SAAS;AACjB,cAAQ,KAAK,mDAAmD;AAChE,aAAO;AAAA,IACT;AAGA,UAAM,WAAW,QAAQ,KAAK;AAC9B,QAAI,aAAa,UAAU,QAAQ;AAEnC,QAAI,CAAC,YAAY;AACf,UAAI;AAEF,cAAM,iBAAiB,MAAM,MAAM,GAAG,KAAK,OAAO,uBAAuB;AAAA,UACvE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,iBAAiB,UAAU,KAAK;AAAA,UAClC;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,UAAU,MAAM,CAAC;AAAA,QAC1C,CAAC;AAED,YAAI,CAAC,eAAe,IAAI;AACtB,gBAAM,IAAI,mBAAmB,eAAe;AAAA,QAC9C;AAGA,cAAM,aAAa,MAAM,MAAM,GAAG,KAAK,OAAO,aAAa;AAAA,UACzD,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,iBAAiB,UAAU,KAAK;AAAA,UAClC;AAAA,QACF,CAAC;AAED,YAAI,CAAC,WAAW,IAAI;AAClB,gBAAM,IAAI,mBAAmB,2BAA2B;AAAA,QAC1D;AAEA,cAAM,SAAS,MAAM,WAAW,KAAK;AACrC,qBAAa,EAAE,SAAS,MAAM,MAAM,OAAO,QAAQ,OAAO;AAG1D,iBAAS,UAAU,UAAU;AAAA,MAC/B,SAAS,OAAO;AACd,gBAAQ,MAAM,yBAAyB,KAAK;AAC5C,cAAM,IAAI,mBAAmB,uBAAuB;AAAA,MACtD;AAAA,IACF;AAEA,QAAI,CAAC,WAAW,SAAS;AACvB,YAAM,IAAI,mBAAmB,WAAW,SAAS,eAAe;AAAA,IAClE;AAEA,QAAI,CAAC,WAAW,MAAM;AACpB,YAAM,IAAI,mBAAmB,gBAAgB;AAAA,IAC/C;AAEA,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,WAAW,KAAK,UAAU,IAAI,yBAAyB,OAAO;AAEpE,QAAI,UAAU;AACZ,YAAM,EAAE,aAAa,WAAW,IAAI;AACpC,YAAM,kBAAkB,WAAW,KAAK,UAAU,WAAW,KAAK,QAAQ,QAAQ,CAAC,MAAW,EAAE,WAAW,IAAI,CAAC;AAEhH,UAAI,YAAY;AACd,YAAI,CAAC,YAAY,MAAM,CAAC,MAAc,gBAAgB,SAAS,CAAC,CAAC,GAAG;AAClE,gBAAM,IAAI,mBAAmB,0BAA0B;AAAA,QACzD;AAAA,MACF,OAAO;AACL,YAAI,CAAC,YAAY,KAAK,CAAC,MAAc,gBAAgB,SAAS,CAAC,CAAC,GAAG;AACjE,gBAAM,IAAI,mBAAmB,0BAA0B;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,UAAU,IAAI,mBAAmB,OAAO;AAE9D,QAAI,UAAU;AACZ,YAAM,EAAE,OAAO,WAAW,IAAI;AAC9B,YAAM,YAAY,WAAW,KAAK,SAAS,CAAC;AAE5C,UAAI,YAAY;AACd,YAAI,CAAC,MAAM,MAAM,CAAC,MAAc,UAAU,SAAS,CAAC,CAAC,GAAG;AACtD,gBAAM,IAAI,mBAAmB,oBAAoB;AAAA,QACnD;AAAA,MACF,OAAO;AACL,YAAI,CAAC,MAAM,KAAK,CAAC,MAAc,UAAU,SAAS,CAAC,CAAC,GAAG;AACrD,gBAAM,IAAI,mBAAmB,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;AApHa,yBAAN;AAAA,EADN,WAAW;AAAA,EAGP,4BAAS;AAAA,EAAG,0BAAO,kBAAkB;AAAA,GAF7B;AAsHN,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;AAEO,SAAS,oBAA0B;AACxC,QAAM,MAAM;AACd;","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
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
|
-
}
|