exguard-endpoint 1.0.8 → 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/dist/index.cjs +18 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +19 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/setup.cjs +0 -303
package/dist/index.cjs
CHANGED
|
@@ -65,6 +65,10 @@ ExGuardModule = __decorateClass([
|
|
|
65
65
|
var import_common2 = require("@nestjs/common");
|
|
66
66
|
var EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
|
|
67
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);
|
|
68
72
|
var cache = /* @__PURE__ */ new Map();
|
|
69
73
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
70
74
|
function getCached(key) {
|
|
@@ -120,6 +124,7 @@ var ExGuardPermissionGuard = class {
|
|
|
120
124
|
}
|
|
121
125
|
const meData = await meResponse.json();
|
|
122
126
|
authResult = { allowed: true, user: meData.data || meData };
|
|
127
|
+
console.log("[ExGuard DEBUG] Full user object:", JSON.stringify(authResult.user, null, 2));
|
|
123
128
|
setCache(cacheKey, authResult);
|
|
124
129
|
} catch (error) {
|
|
125
130
|
console.error("[ExGuard] Auth error:", error);
|
|
@@ -134,22 +139,32 @@ var ExGuardPermissionGuard = class {
|
|
|
134
139
|
}
|
|
135
140
|
const handler = context.getHandler();
|
|
136
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);
|
|
137
145
|
if (permMeta) {
|
|
138
|
-
const
|
|
146
|
+
const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;
|
|
147
|
+
const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;
|
|
139
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);
|
|
140
152
|
if (requireAll) {
|
|
141
153
|
if (!permissions.every((p) => userPermissions.includes(p))) {
|
|
142
154
|
throw new import_common2.ForbiddenException("Insufficient permissions");
|
|
143
155
|
}
|
|
144
156
|
} else {
|
|
145
|
-
|
|
157
|
+
const hasPermission = permissions.some((p) => userPermissions.includes(p));
|
|
158
|
+
console.log("[ExGuard DEBUG] Permission check result:", hasPermission);
|
|
159
|
+
if (!hasPermission) {
|
|
146
160
|
throw new import_common2.ForbiddenException("Insufficient permissions");
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
163
|
}
|
|
150
164
|
const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
|
|
151
165
|
if (roleMeta) {
|
|
152
|
-
const
|
|
166
|
+
const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;
|
|
167
|
+
const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;
|
|
153
168
|
const userRoles = authResult.user.roles || [];
|
|
154
169
|
if (requireAll) {
|
|
155
170
|
if (!roles.every((r) => userRoles.includes(r))) {
|
|
@@ -177,18 +192,6 @@ ExGuardPermissionGuard = __decorateClass([
|
|
|
177
192
|
__decorateParam(0, (0, import_common2.Optional)()),
|
|
178
193
|
__decorateParam(0, (0, import_common2.Inject)("EXGUARD_BASE_URL"))
|
|
179
194
|
], ExGuardPermissionGuard);
|
|
180
|
-
function RequirePermissions(permissions, requireAll = false) {
|
|
181
|
-
return function(target, propertyKey, descriptor) {
|
|
182
|
-
Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
|
|
183
|
-
return descriptor;
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
function RequireRoles(roles, requireAll = false) {
|
|
187
|
-
return function(target, propertyKey, descriptor) {
|
|
188
|
-
Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
|
|
189
|
-
return descriptor;
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
195
|
function clearExGuardCache() {
|
|
193
196
|
cache.clear();
|
|
194
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 { 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 } 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,3 +1,4 @@
|
|
|
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
|
|
|
@@ -11,6 +12,8 @@ declare class ExGuardModule {
|
|
|
11
12
|
|
|
12
13
|
declare const EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
|
|
13
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>;
|
|
14
17
|
declare class ExGuardPermissionGuard implements CanActivate {
|
|
15
18
|
private baseUrl;
|
|
16
19
|
private reflector;
|
|
@@ -18,8 +21,6 @@ declare class ExGuardPermissionGuard implements CanActivate {
|
|
|
18
21
|
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
19
22
|
private extractToken;
|
|
20
23
|
}
|
|
21
|
-
declare function RequirePermissions(permissions: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
22
|
-
declare function RequireRoles(roles: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
23
24
|
declare function clearExGuardCache(): void;
|
|
24
25
|
|
|
25
26
|
export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardModule, ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
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
|
|
|
@@ -11,6 +12,8 @@ declare class ExGuardModule {
|
|
|
11
12
|
|
|
12
13
|
declare const EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
|
|
13
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>;
|
|
14
17
|
declare class ExGuardPermissionGuard implements CanActivate {
|
|
15
18
|
private baseUrl;
|
|
16
19
|
private reflector;
|
|
@@ -18,8 +21,6 @@ declare class ExGuardPermissionGuard implements CanActivate {
|
|
|
18
21
|
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
19
22
|
private extractToken;
|
|
20
23
|
}
|
|
21
|
-
declare function RequirePermissions(permissions: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
22
|
-
declare function RequireRoles(roles: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
23
24
|
declare function clearExGuardCache(): void;
|
|
24
25
|
|
|
25
26
|
export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardModule, ExGuardPermissionGuard, RequirePermissions, RequireRoles, clearExGuardCache };
|
package/dist/index.js
CHANGED
|
@@ -33,9 +33,13 @@ ExGuardModule = __decorateClass([
|
|
|
33
33
|
], ExGuardModule);
|
|
34
34
|
|
|
35
35
|
// src/guard.ts
|
|
36
|
-
import { Injectable, UnauthorizedException, ForbiddenException, Inject, Optional } from "@nestjs/common";
|
|
36
|
+
import { Injectable, UnauthorizedException, ForbiddenException, Inject, Optional, SetMetadata } from "@nestjs/common";
|
|
37
37
|
var EXGUARD_PERMISSIONS_KEY = "exguard_permissions";
|
|
38
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);
|
|
39
43
|
var cache = /* @__PURE__ */ new Map();
|
|
40
44
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
41
45
|
function getCached(key) {
|
|
@@ -91,6 +95,7 @@ var ExGuardPermissionGuard = class {
|
|
|
91
95
|
}
|
|
92
96
|
const meData = await meResponse.json();
|
|
93
97
|
authResult = { allowed: true, user: meData.data || meData };
|
|
98
|
+
console.log("[ExGuard DEBUG] Full user object:", JSON.stringify(authResult.user, null, 2));
|
|
94
99
|
setCache(cacheKey, authResult);
|
|
95
100
|
} catch (error) {
|
|
96
101
|
console.error("[ExGuard] Auth error:", error);
|
|
@@ -105,22 +110,32 @@ var ExGuardPermissionGuard = class {
|
|
|
105
110
|
}
|
|
106
111
|
const handler = context.getHandler();
|
|
107
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);
|
|
108
116
|
if (permMeta) {
|
|
109
|
-
const
|
|
117
|
+
const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;
|
|
118
|
+
const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;
|
|
110
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);
|
|
111
123
|
if (requireAll) {
|
|
112
124
|
if (!permissions.every((p) => userPermissions.includes(p))) {
|
|
113
125
|
throw new ForbiddenException("Insufficient permissions");
|
|
114
126
|
}
|
|
115
127
|
} else {
|
|
116
|
-
|
|
128
|
+
const hasPermission = permissions.some((p) => userPermissions.includes(p));
|
|
129
|
+
console.log("[ExGuard DEBUG] Permission check result:", hasPermission);
|
|
130
|
+
if (!hasPermission) {
|
|
117
131
|
throw new ForbiddenException("Insufficient permissions");
|
|
118
132
|
}
|
|
119
133
|
}
|
|
120
134
|
}
|
|
121
135
|
const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
|
|
122
136
|
if (roleMeta) {
|
|
123
|
-
const
|
|
137
|
+
const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;
|
|
138
|
+
const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;
|
|
124
139
|
const userRoles = authResult.user.roles || [];
|
|
125
140
|
if (requireAll) {
|
|
126
141
|
if (!roles.every((r) => userRoles.includes(r))) {
|
|
@@ -148,18 +163,6 @@ ExGuardPermissionGuard = __decorateClass([
|
|
|
148
163
|
__decorateParam(0, Optional()),
|
|
149
164
|
__decorateParam(0, Inject("EXGUARD_BASE_URL"))
|
|
150
165
|
], ExGuardPermissionGuard);
|
|
151
|
-
function RequirePermissions(permissions, requireAll = false) {
|
|
152
|
-
return function(target, propertyKey, descriptor) {
|
|
153
|
-
Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
|
|
154
|
-
return descriptor;
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
function RequireRoles(roles, requireAll = false) {
|
|
158
|
-
return function(target, propertyKey, descriptor) {
|
|
159
|
-
Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
|
|
160
|
-
return descriptor;
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
166
|
function clearExGuardCache() {
|
|
164
167
|
cache.clear();
|
|
165
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}\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 } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/module.ts","../src/guard.ts"],"sourcesContent":["import { Module, Global, DynamicModule } from '@nestjs/common';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule;\n static forRoot(baseUrl: string): DynamicModule;\n static forRoot(optionsOrUrl: ExGuardModuleOptions | string): DynamicModule {\n const baseUrl = typeof optionsOrUrl === 'string' ? optionsOrUrl : optionsOrUrl.baseUrl;\n \n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_BASE_URL',\n useValue: baseUrl,\n },\n ],\n exports: ['EXGUARD_BASE_URL'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional, SetMetadata } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\nexport const EXGUARD_PERMISSIONS_ALL_KEY = 'exguard_permissions_all';\nexport const EXGUARD_ROLES_ALL_KEY = 'exguard_roles_all';\n\nexport const RequirePermissions = (permissions: string[]) => SetMetadata(EXGUARD_PERMISSIONS_KEY, permissions);\nexport const RequireRoles = (roles: string[]) => SetMetadata(EXGUARD_ROLES_KEY, roles);\nexport const RequirePermissionsAll = () => SetMetadata(EXGUARD_PERMISSIONS_ALL_KEY, true);\nexport const RequireRolesAll = () => SetMetadata(EXGUARD_ROLES_ALL_KEY, true);\n\nconst cache = new Map<string, { data: any; timestamp: number }>();\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nfunction getCached(key: string): any {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > CACHE_TTL) {\n cache.delete(key);\n return null;\n }\n return entry.data;\n}\n\nfunction setCache(key: string, data: any): void {\n cache.set(key, { data, timestamp: Date.now() });\n}\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_BASE_URL') private baseUrl: string,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.baseUrl) {\n console.warn('[ExGuard] Base URL not configured. Access denied.');\n return false;\n }\n\n // Check cache first\n const cacheKey = `auth:${token}`;\n let authResult = getCached(cacheKey);\n\n if (!authResult) {\n try {\n // Verify token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${token}`,\n },\n body: JSON.stringify({ id_token: token }),\n });\n\n if (!verifyResponse.ok) {\n throw new ForbiddenException('Invalid token');\n }\n\n // Get user access\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n\n if (!meResponse.ok) {\n throw new ForbiddenException('Failed to get user access');\n }\n\n const meData = await meResponse.json();\n authResult = { allowed: true, user: meData.data || meData };\n \n console.log('[ExGuard DEBUG] Full user object:', JSON.stringify(authResult.user, null, 2));\n \n // Cache the result\n setCache(cacheKey, authResult);\n } catch (error) {\n console.error('[ExGuard] Auth error:', error);\n throw new ForbiddenException('Authentication failed');\n }\n }\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);\n\n console.log('[ExGuard DEBUG] permMeta from reflector:', permMeta);\n console.log('[ExGuard DEBUG] handler:', handler);\n console.log('[ExGuard DEBUG] handler name:', handler.name);\n\n if (permMeta) {\n const permissions = Array.isArray(permMeta) ? permMeta : permMeta.permissions;\n const requireAll = permMeta.requireAll ?? this.reflector.get(EXGUARD_PERMISSIONS_ALL_KEY, handler) ?? false;\n const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap((m: any) => m.permissions) : [];\n\n console.log('[ExGuard DEBUG] Required permissions:', permissions);\n console.log('[ExGuard DEBUG] User permissions:', userPermissions);\n console.log('[ExGuard DEBUG] requireAll:', requireAll);\n\n if (requireAll) {\n if (!permissions.every((p: string) => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n const hasPermission = permissions.some((p: string) => userPermissions.includes(p));\n console.log('[ExGuard DEBUG] Permission check result:', hasPermission);\n if (!hasPermission) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);\n\n if (roleMeta) {\n const roles = Array.isArray(roleMeta) ? roleMeta : roleMeta.roles;\n const requireAll = roleMeta.requireAll ?? this.reflector.get(EXGUARD_ROLES_ALL_KEY, handler) ?? false;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some((r: string) => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function clearExGuardCache(): void {\n cache.clear();\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
package/dist/setup.cjs
DELETED
|
@@ -1,303 +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;
|
|
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(baseUrl: string): DynamicModule {
|
|
166
|
-
return {
|
|
167
|
-
module: ExGuardModule,
|
|
168
|
-
providers: [
|
|
169
|
-
{
|
|
170
|
-
provide: 'EXGUARD_BASE_URL',
|
|
171
|
-
useValue: 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 updateAppModule(baseUrl) {
|
|
194
|
-
const appModulePath = path.join(process.cwd(), 'src/app.module.ts');
|
|
195
|
-
|
|
196
|
-
if (!fs.existsSync(appModulePath)) {
|
|
197
|
-
console.log('app.module.ts not found. Skipping auto-configure.');
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
let content = fs.readFileSync(appModulePath, 'utf8');
|
|
202
|
-
|
|
203
|
-
// Check if already configured
|
|
204
|
-
if (content.includes('ExGuardModule')) {
|
|
205
|
-
console.log('ExGuardModule already configured in app.module.ts');
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Add import
|
|
210
|
-
const importStatement = "import { ExGuardModule } from './exguard/exguard.module';";
|
|
211
|
-
|
|
212
|
-
if (!content.includes(importStatement)) {
|
|
213
|
-
// Find the last import
|
|
214
|
-
const importMatch = content.match(/^import .+$/gm);
|
|
215
|
-
if (importMatch) {
|
|
216
|
-
const lastImport = importMatch[importMatch.length - 1];
|
|
217
|
-
content = content.replace(lastImport, lastImport + '\n' + importStatement);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Add to imports array
|
|
222
|
-
if (content.includes('imports: [')) {
|
|
223
|
-
content = content.replace(
|
|
224
|
-
'imports: [',
|
|
225
|
-
"imports: [\n ExGuardModule.forRoot(process.env.EXGUARD_BASE_URL || '" + baseUrl + "'),"
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
fs.writeFileSync(appModulePath, content);
|
|
230
|
-
console.log('Updated app.module.ts with ExGuardModule');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function updateEnvFile() {
|
|
234
|
-
const envPath = path.join(process.cwd(), '.env');
|
|
235
|
-
const envExamplePath = path.join(process.cwd(), '.env.example');
|
|
236
|
-
|
|
237
|
-
const envContent = `# ExGuard Configuration
|
|
238
|
-
EXGUARD_BASE_URL=http://localhost:3000
|
|
239
|
-
`;
|
|
240
|
-
|
|
241
|
-
if (!fs.existsSync(envPath) && !fs.existsSync(envExamplePath)) {
|
|
242
|
-
fs.writeFileSync(envExamplePath, envContent);
|
|
243
|
-
console.log('Created .env.example with EXGUARD_BASE_URL');
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function setup() {
|
|
248
|
-
const srcDir = path.join(process.cwd(), 'src');
|
|
249
|
-
const baseUrl = process.env.EXGUARD_BASE_URL || 'http://localhost:3000';
|
|
250
|
-
|
|
251
|
-
if (!fs.existsSync(srcDir)) {
|
|
252
|
-
console.error('Error: Run this command in your NestJS project root');
|
|
253
|
-
process.exit(1);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const exguardDir = path.join(srcDir, 'exguard');
|
|
257
|
-
ensureDir(exguardDir);
|
|
258
|
-
|
|
259
|
-
console.log('');
|
|
260
|
-
console.log('===========================================');
|
|
261
|
-
console.log('🔧 Setting up ExGuard...');
|
|
262
|
-
console.log('===========================================');
|
|
263
|
-
|
|
264
|
-
writeFile(path.join(exguardDir, 'exguard.guard.ts'), GUARD_CONTENT);
|
|
265
|
-
writeFile(path.join(exguardDir, 'exguard.module.ts'), MODULE_CONTENT);
|
|
266
|
-
|
|
267
|
-
updateEnvFile();
|
|
268
|
-
updateAppModule(baseUrl);
|
|
269
|
-
|
|
270
|
-
console.log('');
|
|
271
|
-
console.log('✅ ExGuard Setup Complete!');
|
|
272
|
-
console.log('===========================================');
|
|
273
|
-
console.log('');
|
|
274
|
-
console.log('Environment:');
|
|
275
|
-
console.log(' EXGUARD_BASE_URL=' + baseUrl);
|
|
276
|
-
console.log('');
|
|
277
|
-
console.log('Usage in controllers:');
|
|
278
|
-
console.log('');
|
|
279
|
-
console.log(' import { ExGuardPermissionGuard, RequirePermissions } from "./exguard/exguard.guard";');
|
|
280
|
-
console.log('');
|
|
281
|
-
console.log(' @Controller("items")');
|
|
282
|
-
console.log(' @UseGuards(ExGuardPermissionGuard)');
|
|
283
|
-
console.log(' export class ItemsController {');
|
|
284
|
-
console.log(' @Get() @RequirePermissions(["item:read"]) findAll() {}');
|
|
285
|
-
console.log(' }');
|
|
286
|
-
console.log('');
|
|
287
|
-
console.log('💡 Caching enabled (5 min TTL)');
|
|
288
|
-
console.log('===========================================');
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const args = process.argv.slice(2);
|
|
292
|
-
|
|
293
|
-
if (args[0] === 'setup') {
|
|
294
|
-
setup();
|
|
295
|
-
} else {
|
|
296
|
-
console.log('===========================================');
|
|
297
|
-
console.log('ExGuard Endpoint - NestJS RBAC Guard');
|
|
298
|
-
console.log('===========================================');
|
|
299
|
-
console.log('');
|
|
300
|
-
console.log('To setup in your NestJS project:');
|
|
301
|
-
console.log(' npx exguard-endpoint setup');
|
|
302
|
-
console.log('');
|
|
303
|
-
}
|