exguard-endpoint 1.0.1 → 1.0.4
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 +109 -12
- package/dist/index.cjs +114 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -1
- package/dist/index.d.ts +54 -1
- package/dist/index.js +118 -9
- package/dist/index.js.map +1 -1
- package/dist/setup.cjs +204 -0
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ExGuard Endpoint
|
|
2
2
|
|
|
3
|
-
Simple RBAC permission guard for NestJS.
|
|
3
|
+
Simple RBAC permission guard for NestJS with realtime support.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,19 +8,42 @@ Simple RBAC permission guard for NestJS.
|
|
|
8
8
|
npm install exguard-endpoint
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Quick Setup
|
|
11
|
+
## Quick Setup (Recommended)
|
|
12
|
+
|
|
13
|
+
Run in your NestJS project root:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx exguard-endpoint setup
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This creates:
|
|
20
|
+
- `src/exguard/exguard.guard.ts` - The permission guard
|
|
21
|
+
- `src/exguard/exguard.module.ts` - The module
|
|
22
|
+
|
|
23
|
+
Then follow the printed instructions.
|
|
24
|
+
|
|
25
|
+
## Manual Setup
|
|
12
26
|
|
|
13
27
|
### 1. Configure AppModule
|
|
14
28
|
|
|
15
29
|
```typescript
|
|
30
|
+
// src/app.module.ts
|
|
16
31
|
import { Module } from '@nestjs/common';
|
|
17
32
|
import { ExGuardModule } from 'exguard-endpoint';
|
|
18
33
|
|
|
19
34
|
@Module({
|
|
20
35
|
imports: [
|
|
21
36
|
ExGuardModule.forRoot({
|
|
22
|
-
baseUrl: 'https://api.
|
|
37
|
+
baseUrl: process.env.EXGUARD_BASE_URL || 'https://api.yourdomain.com',
|
|
23
38
|
apiKey: process.env.EXGUARD_API_KEY,
|
|
39
|
+
cache: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
ttl: 300000, // 5 minutes
|
|
42
|
+
},
|
|
43
|
+
realtime: {
|
|
44
|
+
enabled: true,
|
|
45
|
+
url: process.env.EXGUARD_REALTIME_URL,
|
|
46
|
+
},
|
|
24
47
|
}),
|
|
25
48
|
],
|
|
26
49
|
})
|
|
@@ -30,8 +53,9 @@ export class AppModule {}
|
|
|
30
53
|
### 2. Use in Controllers
|
|
31
54
|
|
|
32
55
|
```typescript
|
|
33
|
-
|
|
34
|
-
import {
|
|
56
|
+
// src/items/items.controller.ts
|
|
57
|
+
import { Controller, Get, Post, Patch, Delete, UseGuards } from '@nestjs/common';
|
|
58
|
+
import { ExGuardPermissionGuard, RequirePermissions } from '../exguard/exguard.guard';
|
|
35
59
|
|
|
36
60
|
@Controller('items')
|
|
37
61
|
@UseGuards(ExGuardPermissionGuard)
|
|
@@ -42,30 +66,103 @@ export class ItemsController {
|
|
|
42
66
|
@RequirePermissions(['item:read'])
|
|
43
67
|
findAll() { }
|
|
44
68
|
|
|
69
|
+
// Multiple permissions - needs ALL
|
|
45
70
|
@Post()
|
|
46
|
-
@RequirePermissions(['item:create'])
|
|
71
|
+
@RequirePermissions(['item:create', 'item:admin'], true)
|
|
47
72
|
create() { }
|
|
48
73
|
|
|
49
|
-
|
|
74
|
+
@Patch(':id')
|
|
75
|
+
@RequirePermissions(['item:update'])
|
|
76
|
+
update() { }
|
|
77
|
+
|
|
50
78
|
@Delete(':id')
|
|
51
79
|
@RequirePermissions(['item:delete', 'admin'], true)
|
|
52
80
|
delete() { }
|
|
53
81
|
}
|
|
54
82
|
```
|
|
55
83
|
|
|
56
|
-
##
|
|
84
|
+
## Environment Variables
|
|
85
|
+
|
|
86
|
+
Add to your `.env` file:
|
|
87
|
+
|
|
88
|
+
```env
|
|
89
|
+
EXGUARD_BASE_URL=https://api.yourdomain.com
|
|
90
|
+
EXGUARD_API_KEY=your_api_key_here
|
|
91
|
+
EXGUARD_REALTIME_URL=wss://realtime.yourdomain.com
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## API Reference
|
|
95
|
+
|
|
96
|
+
### API Endpoints Used
|
|
97
|
+
|
|
98
|
+
The client connects to these endpoints:
|
|
99
|
+
- `POST /guard/verify-token` - Verifies the user's token
|
|
100
|
+
- `GET /guard/me` - Gets user access information (roles, permissions, modules)
|
|
101
|
+
|
|
102
|
+
### Decorators
|
|
57
103
|
|
|
58
104
|
| Decorator | Description |
|
|
59
105
|
|-----------|-------------|
|
|
60
|
-
| `@RequirePermissions(['
|
|
61
|
-
| `@RequirePermissions(['
|
|
106
|
+
| `@RequirePermissions(['item:read'])` | User needs ANY of the listed permissions |
|
|
107
|
+
| `@RequirePermissions(['item:delete', 'admin'], true)` | User needs ALL listed permissions |
|
|
108
|
+
|
|
109
|
+
### ExGuardModule.forRoot Options
|
|
62
110
|
|
|
63
|
-
|
|
111
|
+
| Option | Type | Required | Default | Description |
|
|
112
|
+
|--------|------|----------|---------|-------------|
|
|
113
|
+
| baseUrl | string | Yes | - | ExGuard API base URL |
|
|
114
|
+
| apiKey | string | Yes | - | Your API key |
|
|
115
|
+
| cache.enabled | boolean | No | true | Enable caching |
|
|
116
|
+
| cache.ttl | number | No | 300000 | Cache TTL in ms (5 min) |
|
|
117
|
+
| realtime.enabled | boolean | No | false | Enable realtime connection |
|
|
118
|
+
| realtime.url | string | No | - | Realtime WebSocket URL |
|
|
64
119
|
|
|
65
|
-
|
|
120
|
+
### Token Extraction
|
|
121
|
+
|
|
122
|
+
The guard automatically extracts token from:
|
|
66
123
|
- `Authorization: Bearer <token>` header
|
|
67
124
|
- `x-access-token` header
|
|
68
125
|
|
|
126
|
+
### Request User
|
|
127
|
+
|
|
128
|
+
After successful authentication, the user object is available at:
|
|
129
|
+
```typescript
|
|
130
|
+
@Request() req: any
|
|
131
|
+
console.log(req.user); // { id, username, email, roles, modules, groups, fieldOffices }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## User Object Structure
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
{
|
|
138
|
+
user: {
|
|
139
|
+
id: string,
|
|
140
|
+
cognitoSubId: string,
|
|
141
|
+
username: string,
|
|
142
|
+
email: string,
|
|
143
|
+
emailVerified: boolean,
|
|
144
|
+
givenName: string,
|
|
145
|
+
familyName: string,
|
|
146
|
+
employeeNumber: string,
|
|
147
|
+
regionId: string,
|
|
148
|
+
fieldOffice: { id, code, name }
|
|
149
|
+
},
|
|
150
|
+
groups: string[],
|
|
151
|
+
roles: string[],
|
|
152
|
+
modules: [
|
|
153
|
+
{ key: 'events', name: 'Events', permissions: ['events:create', 'events:read'] }
|
|
154
|
+
],
|
|
155
|
+
fieldOffices: string[]
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Realtime Features
|
|
160
|
+
|
|
161
|
+
When realtime is enabled:
|
|
162
|
+
- Permission changes are detected in real-time
|
|
163
|
+
- Cache is automatically cleared when permissions update
|
|
164
|
+
- Events: `permission:update`, `user:update`
|
|
165
|
+
|
|
69
166
|
## License
|
|
70
167
|
|
|
71
168
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -37,7 +37,8 @@ __export(src_exports, {
|
|
|
37
37
|
RequirePermissions: () => RequirePermissions,
|
|
38
38
|
RequireRoles: () => RequireRoles,
|
|
39
39
|
cache: () => cache,
|
|
40
|
-
createExGuardClient: () => createExGuardClient
|
|
40
|
+
createExGuardClient: () => createExGuardClient,
|
|
41
|
+
realtime: () => realtime
|
|
41
42
|
});
|
|
42
43
|
module.exports = __toCommonJS(src_exports);
|
|
43
44
|
|
|
@@ -63,38 +64,132 @@ var ExGuardCache = class {
|
|
|
63
64
|
ttl: ttl || this.ttl
|
|
64
65
|
});
|
|
65
66
|
}
|
|
67
|
+
delete(key) {
|
|
68
|
+
this.cache.delete(key);
|
|
69
|
+
}
|
|
70
|
+
clear() {
|
|
71
|
+
this.cache.clear();
|
|
72
|
+
}
|
|
73
|
+
clearUser(userId) {
|
|
74
|
+
for (const key of this.cache.keys()) {
|
|
75
|
+
if (key.includes(userId)) {
|
|
76
|
+
this.cache.delete(key);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
66
80
|
};
|
|
67
81
|
var cache = new ExGuardCache();
|
|
82
|
+
var ExGuardRealtime = class {
|
|
83
|
+
constructor() {
|
|
84
|
+
this.socket = null;
|
|
85
|
+
this.connected = false;
|
|
86
|
+
this.url = "";
|
|
87
|
+
this.events = {};
|
|
88
|
+
}
|
|
89
|
+
connect(url, events = {}) {
|
|
90
|
+
this.url = url;
|
|
91
|
+
this.events = events;
|
|
92
|
+
if (!url) {
|
|
93
|
+
console.log("[ExGuard] Realtime URL not provided, skipping connection");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const { io } = require("socket.io-client");
|
|
98
|
+
this.socket = io(url, {
|
|
99
|
+
transports: ["websocket"],
|
|
100
|
+
reconnection: true,
|
|
101
|
+
reconnectionAttempts: 5,
|
|
102
|
+
reconnectionDelay: 1e3
|
|
103
|
+
});
|
|
104
|
+
this.socket.on("connect", () => {
|
|
105
|
+
this.connected = true;
|
|
106
|
+
console.log("[ExGuard] Realtime connected");
|
|
107
|
+
this.events.onConnect?.();
|
|
108
|
+
});
|
|
109
|
+
this.socket.on("disconnect", () => {
|
|
110
|
+
this.connected = false;
|
|
111
|
+
console.log("[ExGuard] Realtime disconnected");
|
|
112
|
+
this.events.onDisconnect?.();
|
|
113
|
+
});
|
|
114
|
+
this.socket.on("permission:update", (data) => {
|
|
115
|
+
console.log("[ExGuard] Permission update received:", data);
|
|
116
|
+
cache.clearUser(data.userId);
|
|
117
|
+
this.events.onPermissionUpdate?.(data);
|
|
118
|
+
});
|
|
119
|
+
this.socket.on("user:update", (data) => {
|
|
120
|
+
console.log("[ExGuard] User update received:", data);
|
|
121
|
+
cache.clearUser(data.userId);
|
|
122
|
+
this.events.onUserUpdate?.(data);
|
|
123
|
+
});
|
|
124
|
+
this.socket.on("connect_error", (err) => {
|
|
125
|
+
console.error("[ExGuard] Realtime connection error:", err.message);
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error("[ExGuard] Failed to load socket.io-client:", error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
disconnect() {
|
|
132
|
+
if (this.socket) {
|
|
133
|
+
this.socket.disconnect();
|
|
134
|
+
this.socket = null;
|
|
135
|
+
this.connected = false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
isConnected() {
|
|
139
|
+
return this.connected;
|
|
140
|
+
}
|
|
141
|
+
emit(event, data) {
|
|
142
|
+
if (this.socket && this.connected) {
|
|
143
|
+
this.socket.emit(event, data);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var realtime = new ExGuardRealtime();
|
|
68
148
|
var ExGuardClient = class {
|
|
69
149
|
constructor(config) {
|
|
70
150
|
this.baseUrl = config.baseUrl;
|
|
71
151
|
this.apiKey = config.apiKey;
|
|
72
152
|
this.cache = cache;
|
|
153
|
+
this.realtime = realtime;
|
|
154
|
+
if (config.realtime?.enabled && config.realtime?.url) {
|
|
155
|
+
this.realtime.connect(config.realtime.url);
|
|
156
|
+
}
|
|
73
157
|
}
|
|
74
158
|
async authenticate(context) {
|
|
75
159
|
const cacheKey = `auth:${context.token}`;
|
|
76
160
|
const cached = this.cache.get(cacheKey);
|
|
77
161
|
if (cached) return cached;
|
|
78
162
|
try {
|
|
79
|
-
const
|
|
163
|
+
const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {
|
|
80
164
|
method: "POST",
|
|
81
165
|
headers: {
|
|
82
166
|
"Content-Type": "application/json",
|
|
83
|
-
"Authorization": `Bearer ${
|
|
167
|
+
"Authorization": `Bearer ${context.token}`
|
|
84
168
|
},
|
|
85
|
-
body: JSON.stringify({
|
|
169
|
+
body: JSON.stringify({ id_token: context.token })
|
|
86
170
|
});
|
|
87
|
-
if (!
|
|
171
|
+
if (!verifyResponse.ok) {
|
|
88
172
|
return { allowed: false, error: "Invalid token" };
|
|
89
173
|
}
|
|
90
|
-
const
|
|
174
|
+
const verifyData = await verifyResponse.json();
|
|
175
|
+
const meResponse = await fetch(`${this.baseUrl}/guard/me`, {
|
|
176
|
+
method: "GET",
|
|
177
|
+
headers: {
|
|
178
|
+
"Authorization": `Bearer ${context.token}`
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
if (!meResponse.ok) {
|
|
182
|
+
return { allowed: false, error: "Failed to get user access" };
|
|
183
|
+
}
|
|
184
|
+
const meData = await meResponse.json();
|
|
91
185
|
const result = {
|
|
92
186
|
allowed: true,
|
|
93
|
-
user: data
|
|
187
|
+
user: meData.data || meData
|
|
94
188
|
};
|
|
95
189
|
this.cache.set(cacheKey, result);
|
|
96
190
|
return result;
|
|
97
191
|
} catch (error) {
|
|
192
|
+
console.error("[ExGuard] Authentication error:", error);
|
|
98
193
|
return { allowed: false, error: "Authentication failed" };
|
|
99
194
|
}
|
|
100
195
|
}
|
|
@@ -104,6 +199,12 @@ var ExGuardClient = class {
|
|
|
104
199
|
const userPermissions = result.user.modules?.flatMap((m) => m.permissions) || [];
|
|
105
200
|
return userPermissions.includes(permission);
|
|
106
201
|
}
|
|
202
|
+
clearCache() {
|
|
203
|
+
this.cache.clear();
|
|
204
|
+
}
|
|
205
|
+
disconnectRealtime() {
|
|
206
|
+
this.realtime.disconnect();
|
|
207
|
+
}
|
|
107
208
|
};
|
|
108
209
|
function createExGuardClient(config) {
|
|
109
210
|
return new ExGuardClient(config);
|
|
@@ -116,7 +217,8 @@ var ExGuardModule = class {
|
|
|
116
217
|
const client = new ExGuardClient({
|
|
117
218
|
baseUrl: options.baseUrl,
|
|
118
219
|
apiKey: options.apiKey,
|
|
119
|
-
cache: options.cache
|
|
220
|
+
cache: options.cache,
|
|
221
|
+
realtime: options.realtime
|
|
120
222
|
});
|
|
121
223
|
return {
|
|
122
224
|
module: ExGuardModule,
|
|
@@ -151,7 +253,8 @@ var ExGuardPermissionGuard = class {
|
|
|
151
253
|
throw new import_common2.UnauthorizedException("No token provided");
|
|
152
254
|
}
|
|
153
255
|
if (!this.client) {
|
|
154
|
-
|
|
256
|
+
console.warn("[ExGuard] Client not configured. Access denied.");
|
|
257
|
+
return false;
|
|
155
258
|
}
|
|
156
259
|
const authResult = await this.client.authenticate({ token, request });
|
|
157
260
|
if (!authResult.allowed) {
|
|
@@ -233,6 +336,7 @@ function RequireRoles(roles, requireAll = false) {
|
|
|
233
336
|
RequirePermissions,
|
|
234
337
|
RequireRoles,
|
|
235
338
|
cache,
|
|
236
|
-
createExGuardClient
|
|
339
|
+
createExGuardClient,
|
|
340
|
+
realtime
|
|
237
341
|
});
|
|
238
342
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export { ExGuardClient, createExGuardClient, cache } from './client.js';\nexport type { ExGuardConfig, User, ModulePermission, UserAccessResponse, GuardContext, GuardResult } from './client.js';\nexport { ExGuardModule } from './module.js';\nexport type { ExGuardModuleOptions } from './module.js';\nexport { ExGuardPermissionGuard, RequirePermissions, RequireRoles, EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY } from './guard.js';\n","export interface ExGuardConfig {\n baseUrl: string;\n apiKey: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n}\n\nexport interface User {\n id: string;\n username: string;\n email: string;\n}\n\nexport interface ModulePermission {\n key: string;\n name: string;\n permissions: string[];\n}\n\nexport interface UserAccessResponse {\n user: User;\n roles: string[];\n modules: ModulePermission[];\n}\n\nexport interface GuardContext {\n token: string;\n request?: any;\n}\n\nexport interface GuardResult {\n allowed: boolean;\n user?: UserAccessResponse;\n error?: string;\n}\n\nclass ExGuardCache {\n private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n private ttl = 300000;\n\n get(key: string): any {\n const entry = this.cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > entry.ttl) {\n this.cache.delete(key);\n return null;\n }\n return entry.data;\n }\n\n set(key: string, data: any, ttl?: number): void {\n this.cache.set(key, {\n data,\n timestamp: Date.now(),\n ttl: ttl || this.ttl,\n });\n }\n}\n\nconst cache = new ExGuardCache();\n\nexport class ExGuardClient {\n private baseUrl: string;\n private apiKey: string;\n private cache: ExGuardCache;\n\n constructor(config: ExGuardConfig) {\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n this.cache = cache;\n }\n\n async authenticate(context: GuardContext): Promise<GuardResult> {\n const cacheKey = `auth:${context.token}`;\n const cached = this.cache.get(cacheKey);\n if (cached) return cached;\n\n try {\n const response = await fetch(`${this.baseUrl}/api/v1/auth/validate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ token: context.token }),\n });\n\n if (!response.ok) {\n return { allowed: false, error: 'Invalid token' };\n }\n\n const data = await response.json();\n const result: GuardResult = {\n allowed: true,\n user: data.user || data.data?.user,\n };\n\n this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n return { allowed: false, error: 'Authentication failed' };\n }\n }\n\n async hasPermission(token: string, permission: string): Promise<boolean> {\n const result = await this.authenticate({ token });\n if (!result.allowed || !result.user) return false;\n\n const userPermissions = result.user.modules?.flatMap(m => m.permissions) || [];\n return userPermissions.includes(permission);\n }\n}\n\nexport function createExGuardClient(config: ExGuardConfig): ExGuardClient {\n return new ExGuardClient(config);\n}\n\nexport { cache };\n","import { Module, Global, DynamicModule } from '@nestjs/common';\nimport { ExGuardClient } from './client.js';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n apiKey: 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 const client = new ExGuardClient({\n baseUrl: options.baseUrl,\n apiKey: options.apiKey,\n cache: options.cache,\n });\n\n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_CLIENT',\n useValue: client,\n },\n ],\n exports: ['EXGUARD_CLIENT'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { ExGuardClient } from './client.js';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_CLIENT') private client: ExGuardClient,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.client) {\n return true;\n }\n\n const authResult = await this.client.authenticate({ token, request });\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get<{ permissions: string[]; requireAll?: boolean }>(\n EXGUARD_PERMISSIONS_KEY,\n handler,\n );\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules?.flatMap(m => m.permissions) || [];\n\n if (requireAll) {\n if (!permissions.every(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get<{ roles: string[]; requireAll?: boolean }>(\n EXGUARD_ROLES_KEY,\n handler,\n );\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsCA,IAAM,eAAN,MAAmB;AAAA,EAAnB;AACE,SAAQ,QAAQ,oBAAI,IAA2D;AAC/E,SAAQ,MAAM;AAAA;AAAA,EAEd,IAAI,KAAkB;AACpB,UAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;AAChC,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,KAAK,IAAI,IAAI,MAAM,YAAY,MAAM,KAAK;AAC5C,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AACA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,IAAI,KAAa,MAAW,KAAoB;AAC9C,SAAK,MAAM,IAAI,KAAK;AAAA,MAClB;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,KAAK,OAAO,KAAK;AAAA,IACnB,CAAC;AAAA,EACH;AACF;AAEA,IAAM,QAAQ,IAAI,aAAa;AAExB,IAAM,gBAAN,MAAoB;AAAA,EAKzB,YAAY,QAAuB;AACjC,SAAK,UAAU,OAAO;AACtB,SAAK,SAAS,OAAO;AACrB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,aAAa,SAA6C;AAC9D,UAAM,WAAW,QAAQ,QAAQ,KAAK;AACtC,UAAM,SAAS,KAAK,MAAM,IAAI,QAAQ;AACtC,QAAI,OAAQ,QAAO;AAEnB,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,yBAAyB;AAAA,QACnE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,iBAAiB,UAAU,KAAK,MAAM;AAAA,QACxC;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,OAAO,QAAQ,MAAM,CAAC;AAAA,MAC/C,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO,EAAE,SAAS,OAAO,OAAO,gBAAgB;AAAA,MAClD;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,SAAsB;AAAA,QAC1B,SAAS;AAAA,QACT,MAAM,KAAK,QAAQ,KAAK,MAAM;AAAA,MAChC;AAEA,WAAK,MAAM,IAAI,UAAU,MAAM;AAC/B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,aAAO,EAAE,SAAS,OAAO,OAAO,wBAAwB;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,OAAe,YAAsC;AACvE,UAAM,SAAS,MAAM,KAAK,aAAa,EAAE,MAAM,CAAC;AAChD,QAAI,CAAC,OAAO,WAAW,CAAC,OAAO,KAAM,QAAO;AAE5C,UAAM,kBAAkB,OAAO,KAAK,SAAS,QAAQ,OAAK,EAAE,WAAW,KAAK,CAAC;AAC7E,WAAO,gBAAgB,SAAS,UAAU;AAAA,EAC5C;AACF;AAEO,SAAS,oBAAoB,QAAsC;AACxE,SAAO,IAAI,cAAc,MAAM;AACjC;;;ACrHA,oBAA8C;AAcvC,IAAM,gBAAN,MAAoB;AAAA,EACzB,OAAO,QAAQ,SAA8C;AAC3D,UAAM,SAAS,IAAI,cAAc;AAAA,MAC/B,SAAS,QAAQ;AAAA,MACjB,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,IACjB,CAAC;AAED,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,QACT;AAAA,UACE,SAAS;AAAA,UACT,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,MACA,SAAS,CAAC,gBAAgB;AAAA,IAC5B;AAAA,EACF;AACF;AAnBa,gBAAN;AAAA,MAFN,sBAAO;AAAA,MACP,sBAAO,CAAC,CAAC;AAAA,GACG;;;ACdb,IAAAA,iBAAuH;AAIhH,IAAM,0BAA0B;AAChC,IAAM,oBAAoB;AAG1B,IAAM,yBAAN,MAAoD;AAAA,EACzD,YACgD,QACtC,WACR;AAF8C;AACtC;AAAA,EACP;AAAA,EAEH,MAAM,YAAY,SAA6C;AAC7D,UAAM,UAAU,QAAQ,aAAa,EAAE,WAAW;AAClD,UAAM,QAAQ,KAAK,aAAa,OAAO;AAEvC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,qCAAsB,mBAAmB;AAAA,IACrD;AAEA,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,MAAM,KAAK,OAAO,aAAa,EAAE,OAAO,QAAQ,CAAC;AAEpE,QAAI,CAAC,WAAW,SAAS;AACvB,YAAM,IAAI,kCAAmB,WAAW,SAAS,eAAe;AAAA,IAClE;AAEA,QAAI,CAAC,WAAW,MAAM;AACpB,YAAM,IAAI,kCAAmB,gBAAgB;AAAA,IAC/C;AAEA,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,WAAW,KAAK,UAAU;AAAA,MAC9B;AAAA,MACA;AAAA,IACF;AAEA,QAAI,UAAU;AACZ,YAAM,EAAE,aAAa,WAAW,IAAI;AACpC,YAAM,kBAAkB,WAAW,KAAK,SAAS,QAAQ,OAAK,EAAE,WAAW,KAAK,CAAC;AAEjF,UAAI,YAAY;AACd,YAAI,CAAC,YAAY,MAAM,OAAK,gBAAgB,SAAS,CAAC,CAAC,GAAG;AACxD,gBAAM,IAAI,kCAAmB,0BAA0B;AAAA,QACzD;AAAA,MACF,OAAO;AACL,YAAI,CAAC,YAAY,KAAK,OAAK,gBAAgB,SAAS,CAAC,CAAC,GAAG;AACvD,gBAAM,IAAI,kCAAmB,0BAA0B;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,UAAU;AAAA,MAC9B;AAAA,MACA;AAAA,IACF;AAEA,QAAI,UAAU;AACZ,YAAM,EAAE,OAAO,WAAW,IAAI;AAC9B,YAAM,YAAY,WAAW,KAAK,SAAS,CAAC;AAE5C,UAAI,YAAY;AACd,YAAI,CAAC,MAAM,MAAM,OAAK,UAAU,SAAS,CAAC,CAAC,GAAG;AAC5C,gBAAM,IAAI,kCAAmB,oBAAoB;AAAA,QACnD;AAAA,MACF,OAAO;AACL,YAAI,CAAC,MAAM,KAAK,OAAK,UAAU,SAAS,CAAC,CAAC,GAAG;AAC3C,gBAAM,IAAI,kCAAmB,oBAAoB;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,OAAO,WAAW;AAC1B,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,SAA6B;AAChD,UAAM,OAAO,QAAQ,SAAS;AAC9B,QAAI,MAAM,WAAW,SAAS,GAAG;AAC/B,aAAO,KAAK,UAAU,CAAC;AAAA,IACzB;AACA,WAAO,QAAQ,UAAU,gBAAgB,KAAK;AAAA,EAChD;AACF;AAhFa,yBAAN;AAAA,MADN,2BAAW;AAAA,EAGP,gDAAS;AAAA,EAAG,8CAAO,gBAAgB;AAAA,GAF3B;AAkFN,SAAS,mBAAmB,aAAuB,aAAa,OAAO;AAC5E,SAAO,SAAU,QAAa,aAAqB,YAAgC;AACjF,YAAQ,eAAe,yBAAyB,EAAE,aAAa,WAAW,GAAG,WAAW,KAAK;AAC7F,WAAO;AAAA,EACT;AACF;AAEO,SAAS,aAAa,OAAiB,aAAa,OAAO;AAChE,SAAO,SAAU,QAAa,aAAqB,YAAgC;AACjF,YAAQ,eAAe,mBAAmB,EAAE,OAAO,WAAW,GAAG,WAAW,KAAK;AACjF,WAAO;AAAA,EACT;AACF;","names":["import_common"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export { ExGuardClient, createExGuardClient, cache, realtime } from './client.js';\nexport type { ExGuardConfig, User, ModulePermission, UserAccessResponse, GuardContext, GuardResult, RealtimeEvents } from './client.js';\nexport { ExGuardModule } from './module.js';\nexport type { ExGuardModuleOptions } from './module.js';\nexport { ExGuardPermissionGuard, RequirePermissions, RequireRoles, EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY } from './guard.js';\n","export interface ExGuardConfig {\n baseUrl: string;\n apiKey: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n realtime?: {\n enabled?: boolean;\n url?: string;\n };\n}\n\nexport interface User {\n id: string;\n cognitoSubId: string;\n username: string;\n email: string;\n emailVerified: boolean;\n givenName: string;\n familyName: string;\n employeeNumber: string;\n regionId: string;\n createdAt: string;\n updatedAt: string;\n lastLoginAt: string;\n fieldOffice: {\n id: string;\n code: string;\n name: string;\n };\n}\n\nexport interface ModulePermission {\n key: string;\n name: string;\n permissions: string[];\n}\n\nexport interface UserAccessResponse {\n user: User;\n groups: string[];\n roles: string[];\n modules: ModulePermission[];\n fieldOffices: string[];\n}\n\nexport interface GuardContext {\n token: string;\n request?: any;\n}\n\nexport interface GuardResult {\n allowed: boolean;\n user?: UserAccessResponse;\n error?: string;\n}\n\nexport interface RealtimeEvents {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onPermissionUpdate?: (data: { userId: string; permissions: string[] }) => void;\n onUserUpdate?: (data: { userId: string; user: UserAccessResponse }) => void;\n}\n\nclass ExGuardCache {\n private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n private ttl = 300000;\n\n get(key: string): any {\n const entry = this.cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > entry.ttl) {\n this.cache.delete(key);\n return null;\n }\n return entry.data;\n }\n\n set(key: string, data: any, ttl?: number): void {\n this.cache.set(key, {\n data,\n timestamp: Date.now(),\n ttl: ttl || this.ttl,\n });\n }\n\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n clearUser(userId: string): void {\n for (const key of this.cache.keys()) {\n if (key.includes(userId)) {\n this.cache.delete(key);\n }\n }\n }\n}\n\nconst cache = new ExGuardCache();\n\nclass ExGuardRealtime {\n private socket: any = null;\n private connected = false;\n private url: string = '';\n private events: RealtimeEvents = {};\n\n connect(url: string, events: RealtimeEvents = {}): void {\n this.url = url;\n this.events = events;\n \n if (!url) {\n console.log('[ExGuard] Realtime URL not provided, skipping connection');\n return;\n }\n\n try {\n const { io } = require('socket.io-client');\n this.socket = io(url, {\n transports: ['websocket'],\n reconnection: true,\n reconnectionAttempts: 5,\n reconnectionDelay: 1000,\n });\n\n this.socket.on('connect', () => {\n this.connected = true;\n console.log('[ExGuard] Realtime connected');\n this.events.onConnect?.();\n });\n\n this.socket.on('disconnect', () => {\n this.connected = false;\n console.log('[ExGuard] Realtime disconnected');\n this.events.onDisconnect?.();\n });\n\n this.socket.on('permission:update', (data: any) => {\n console.log('[ExGuard] Permission update received:', data);\n cache.clearUser(data.userId);\n this.events.onPermissionUpdate?.(data);\n });\n\n this.socket.on('user:update', (data: any) => {\n console.log('[ExGuard] User update received:', data);\n cache.clearUser(data.userId);\n this.events.onUserUpdate?.(data);\n });\n\n this.socket.on('connect_error', (err: any) => {\n console.error('[ExGuard] Realtime connection error:', err.message);\n });\n } catch (error) {\n console.error('[ExGuard] Failed to load socket.io-client:', error);\n }\n }\n\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n this.connected = false;\n }\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n emit(event: string, data: any): void {\n if (this.socket && this.connected) {\n this.socket.emit(event, data);\n }\n }\n}\n\nconst realtime = new ExGuardRealtime();\n\nexport class ExGuardClient {\n private baseUrl: string;\n private apiKey: string;\n private cache: ExGuardCache;\n private realtime: ExGuardRealtime;\n\n constructor(config: ExGuardConfig) {\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n this.cache = cache;\n this.realtime = realtime;\n\n if (config.realtime?.enabled && config.realtime?.url) {\n this.realtime.connect(config.realtime.url);\n }\n }\n\n async authenticate(context: GuardContext): Promise<GuardResult> {\n const cacheKey = `auth:${context.token}`;\n const cached = this.cache.get(cacheKey);\n if (cached) return cached;\n\n try {\n // First verify the token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${context.token}`,\n },\n body: JSON.stringify({ id_token: context.token }),\n });\n\n if (!verifyResponse.ok) {\n return { allowed: false, error: 'Invalid token' };\n }\n\n const verifyData = await verifyResponse.json();\n\n // Then get user access info\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${context.token}`,\n },\n });\n\n if (!meResponse.ok) {\n return { allowed: false, error: 'Failed to get user access' };\n }\n\n const meData = await meResponse.json();\n \n const result: GuardResult = {\n allowed: true,\n user: meData.data || meData,\n };\n\n this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n console.error('[ExGuard] Authentication error:', error);\n return { allowed: false, error: 'Authentication failed' };\n }\n }\n\n async hasPermission(token: string, permission: string): Promise<boolean> {\n const result = await this.authenticate({ token });\n if (!result.allowed || !result.user) return false;\n\n const userPermissions = result.user.modules?.flatMap(m => m.permissions) || [];\n return userPermissions.includes(permission);\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n disconnectRealtime(): void {\n this.realtime.disconnect();\n }\n}\n\nexport function createExGuardClient(config: ExGuardConfig): ExGuardClient {\n return new ExGuardClient(config);\n}\n\nexport { cache, realtime };\n","import { Module, Global, DynamicModule } from '@nestjs/common';\nimport { ExGuardClient } from './client.js';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n apiKey: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n realtime?: {\n enabled?: boolean;\n url?: string;\n };\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule {\n const client = new ExGuardClient({\n baseUrl: options.baseUrl,\n apiKey: options.apiKey,\n cache: options.cache,\n realtime: options.realtime,\n });\n\n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_CLIENT',\n useValue: client,\n },\n ],\n exports: ['EXGUARD_CLIENT'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { ExGuardClient } from './client.js';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_CLIENT') private client: ExGuardClient,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.client) {\n console.warn('[ExGuard] Client not configured. Access denied.');\n return false;\n }\n\n const authResult = await this.client.authenticate({ token, request });\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get<{ permissions: string[]; requireAll?: boolean }>(\n EXGUARD_PERMISSIONS_KEY,\n handler,\n );\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules?.flatMap(m => m.permissions) || [];\n\n if (requireAll) {\n if (!permissions.every(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get<{ roles: string[]; requireAll?: boolean }>(\n EXGUARD_ROLES_KEY,\n handler,\n );\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n"],"mappings":"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","names":["import_common"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -8,11 +8,29 @@ interface ExGuardConfig {
|
|
|
8
8
|
enabled?: boolean;
|
|
9
9
|
ttl?: number;
|
|
10
10
|
};
|
|
11
|
+
realtime?: {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
url?: string;
|
|
14
|
+
};
|
|
11
15
|
}
|
|
12
16
|
interface User {
|
|
13
17
|
id: string;
|
|
18
|
+
cognitoSubId: string;
|
|
14
19
|
username: string;
|
|
15
20
|
email: string;
|
|
21
|
+
emailVerified: boolean;
|
|
22
|
+
givenName: string;
|
|
23
|
+
familyName: string;
|
|
24
|
+
employeeNumber: string;
|
|
25
|
+
regionId: string;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
lastLoginAt: string;
|
|
29
|
+
fieldOffice: {
|
|
30
|
+
id: string;
|
|
31
|
+
code: string;
|
|
32
|
+
name: string;
|
|
33
|
+
};
|
|
16
34
|
}
|
|
17
35
|
interface ModulePermission {
|
|
18
36
|
key: string;
|
|
@@ -21,8 +39,10 @@ interface ModulePermission {
|
|
|
21
39
|
}
|
|
22
40
|
interface UserAccessResponse {
|
|
23
41
|
user: User;
|
|
42
|
+
groups: string[];
|
|
24
43
|
roles: string[];
|
|
25
44
|
modules: ModulePermission[];
|
|
45
|
+
fieldOffices: string[];
|
|
26
46
|
}
|
|
27
47
|
interface GuardContext {
|
|
28
48
|
token: string;
|
|
@@ -33,20 +53,49 @@ interface GuardResult {
|
|
|
33
53
|
user?: UserAccessResponse;
|
|
34
54
|
error?: string;
|
|
35
55
|
}
|
|
56
|
+
interface RealtimeEvents {
|
|
57
|
+
onConnect?: () => void;
|
|
58
|
+
onDisconnect?: () => void;
|
|
59
|
+
onPermissionUpdate?: (data: {
|
|
60
|
+
userId: string;
|
|
61
|
+
permissions: string[];
|
|
62
|
+
}) => void;
|
|
63
|
+
onUserUpdate?: (data: {
|
|
64
|
+
userId: string;
|
|
65
|
+
user: UserAccessResponse;
|
|
66
|
+
}) => void;
|
|
67
|
+
}
|
|
36
68
|
declare class ExGuardCache {
|
|
37
69
|
private cache;
|
|
38
70
|
private ttl;
|
|
39
71
|
get(key: string): any;
|
|
40
72
|
set(key: string, data: any, ttl?: number): void;
|
|
73
|
+
delete(key: string): void;
|
|
74
|
+
clear(): void;
|
|
75
|
+
clearUser(userId: string): void;
|
|
41
76
|
}
|
|
42
77
|
declare const cache: ExGuardCache;
|
|
78
|
+
declare class ExGuardRealtime {
|
|
79
|
+
private socket;
|
|
80
|
+
private connected;
|
|
81
|
+
private url;
|
|
82
|
+
private events;
|
|
83
|
+
connect(url: string, events?: RealtimeEvents): void;
|
|
84
|
+
disconnect(): void;
|
|
85
|
+
isConnected(): boolean;
|
|
86
|
+
emit(event: string, data: any): void;
|
|
87
|
+
}
|
|
88
|
+
declare const realtime: ExGuardRealtime;
|
|
43
89
|
declare class ExGuardClient {
|
|
44
90
|
private baseUrl;
|
|
45
91
|
private apiKey;
|
|
46
92
|
private cache;
|
|
93
|
+
private realtime;
|
|
47
94
|
constructor(config: ExGuardConfig);
|
|
48
95
|
authenticate(context: GuardContext): Promise<GuardResult>;
|
|
49
96
|
hasPermission(token: string, permission: string): Promise<boolean>;
|
|
97
|
+
clearCache(): void;
|
|
98
|
+
disconnectRealtime(): void;
|
|
50
99
|
}
|
|
51
100
|
declare function createExGuardClient(config: ExGuardConfig): ExGuardClient;
|
|
52
101
|
|
|
@@ -57,6 +106,10 @@ interface ExGuardModuleOptions {
|
|
|
57
106
|
enabled?: boolean;
|
|
58
107
|
ttl?: number;
|
|
59
108
|
};
|
|
109
|
+
realtime?: {
|
|
110
|
+
enabled?: boolean;
|
|
111
|
+
url?: string;
|
|
112
|
+
};
|
|
60
113
|
}
|
|
61
114
|
declare class ExGuardModule {
|
|
62
115
|
static forRoot(options: ExGuardModuleOptions): DynamicModule;
|
|
@@ -74,4 +127,4 @@ declare class ExGuardPermissionGuard implements CanActivate {
|
|
|
74
127
|
declare function RequirePermissions(permissions: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
75
128
|
declare function RequireRoles(roles: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
76
129
|
|
|
77
|
-
export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardClient, type ExGuardConfig, ExGuardModule, type ExGuardModuleOptions, ExGuardPermissionGuard, type GuardContext, type GuardResult, type ModulePermission, RequirePermissions, RequireRoles, type User, type UserAccessResponse, cache, createExGuardClient };
|
|
130
|
+
export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardClient, type ExGuardConfig, ExGuardModule, type ExGuardModuleOptions, ExGuardPermissionGuard, type GuardContext, type GuardResult, type ModulePermission, type RealtimeEvents, RequirePermissions, RequireRoles, type User, type UserAccessResponse, cache, createExGuardClient, realtime };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,11 +8,29 @@ interface ExGuardConfig {
|
|
|
8
8
|
enabled?: boolean;
|
|
9
9
|
ttl?: number;
|
|
10
10
|
};
|
|
11
|
+
realtime?: {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
url?: string;
|
|
14
|
+
};
|
|
11
15
|
}
|
|
12
16
|
interface User {
|
|
13
17
|
id: string;
|
|
18
|
+
cognitoSubId: string;
|
|
14
19
|
username: string;
|
|
15
20
|
email: string;
|
|
21
|
+
emailVerified: boolean;
|
|
22
|
+
givenName: string;
|
|
23
|
+
familyName: string;
|
|
24
|
+
employeeNumber: string;
|
|
25
|
+
regionId: string;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
lastLoginAt: string;
|
|
29
|
+
fieldOffice: {
|
|
30
|
+
id: string;
|
|
31
|
+
code: string;
|
|
32
|
+
name: string;
|
|
33
|
+
};
|
|
16
34
|
}
|
|
17
35
|
interface ModulePermission {
|
|
18
36
|
key: string;
|
|
@@ -21,8 +39,10 @@ interface ModulePermission {
|
|
|
21
39
|
}
|
|
22
40
|
interface UserAccessResponse {
|
|
23
41
|
user: User;
|
|
42
|
+
groups: string[];
|
|
24
43
|
roles: string[];
|
|
25
44
|
modules: ModulePermission[];
|
|
45
|
+
fieldOffices: string[];
|
|
26
46
|
}
|
|
27
47
|
interface GuardContext {
|
|
28
48
|
token: string;
|
|
@@ -33,20 +53,49 @@ interface GuardResult {
|
|
|
33
53
|
user?: UserAccessResponse;
|
|
34
54
|
error?: string;
|
|
35
55
|
}
|
|
56
|
+
interface RealtimeEvents {
|
|
57
|
+
onConnect?: () => void;
|
|
58
|
+
onDisconnect?: () => void;
|
|
59
|
+
onPermissionUpdate?: (data: {
|
|
60
|
+
userId: string;
|
|
61
|
+
permissions: string[];
|
|
62
|
+
}) => void;
|
|
63
|
+
onUserUpdate?: (data: {
|
|
64
|
+
userId: string;
|
|
65
|
+
user: UserAccessResponse;
|
|
66
|
+
}) => void;
|
|
67
|
+
}
|
|
36
68
|
declare class ExGuardCache {
|
|
37
69
|
private cache;
|
|
38
70
|
private ttl;
|
|
39
71
|
get(key: string): any;
|
|
40
72
|
set(key: string, data: any, ttl?: number): void;
|
|
73
|
+
delete(key: string): void;
|
|
74
|
+
clear(): void;
|
|
75
|
+
clearUser(userId: string): void;
|
|
41
76
|
}
|
|
42
77
|
declare const cache: ExGuardCache;
|
|
78
|
+
declare class ExGuardRealtime {
|
|
79
|
+
private socket;
|
|
80
|
+
private connected;
|
|
81
|
+
private url;
|
|
82
|
+
private events;
|
|
83
|
+
connect(url: string, events?: RealtimeEvents): void;
|
|
84
|
+
disconnect(): void;
|
|
85
|
+
isConnected(): boolean;
|
|
86
|
+
emit(event: string, data: any): void;
|
|
87
|
+
}
|
|
88
|
+
declare const realtime: ExGuardRealtime;
|
|
43
89
|
declare class ExGuardClient {
|
|
44
90
|
private baseUrl;
|
|
45
91
|
private apiKey;
|
|
46
92
|
private cache;
|
|
93
|
+
private realtime;
|
|
47
94
|
constructor(config: ExGuardConfig);
|
|
48
95
|
authenticate(context: GuardContext): Promise<GuardResult>;
|
|
49
96
|
hasPermission(token: string, permission: string): Promise<boolean>;
|
|
97
|
+
clearCache(): void;
|
|
98
|
+
disconnectRealtime(): void;
|
|
50
99
|
}
|
|
51
100
|
declare function createExGuardClient(config: ExGuardConfig): ExGuardClient;
|
|
52
101
|
|
|
@@ -57,6 +106,10 @@ interface ExGuardModuleOptions {
|
|
|
57
106
|
enabled?: boolean;
|
|
58
107
|
ttl?: number;
|
|
59
108
|
};
|
|
109
|
+
realtime?: {
|
|
110
|
+
enabled?: boolean;
|
|
111
|
+
url?: string;
|
|
112
|
+
};
|
|
60
113
|
}
|
|
61
114
|
declare class ExGuardModule {
|
|
62
115
|
static forRoot(options: ExGuardModuleOptions): DynamicModule;
|
|
@@ -74,4 +127,4 @@ declare class ExGuardPermissionGuard implements CanActivate {
|
|
|
74
127
|
declare function RequirePermissions(permissions: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
75
128
|
declare function RequireRoles(roles: string[], requireAll?: boolean): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
76
129
|
|
|
77
|
-
export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardClient, type ExGuardConfig, ExGuardModule, type ExGuardModuleOptions, ExGuardPermissionGuard, type GuardContext, type GuardResult, type ModulePermission, RequirePermissions, RequireRoles, type User, type UserAccessResponse, cache, createExGuardClient };
|
|
130
|
+
export { EXGUARD_PERMISSIONS_KEY, EXGUARD_ROLES_KEY, ExGuardClient, type ExGuardConfig, ExGuardModule, type ExGuardModuleOptions, ExGuardPermissionGuard, type GuardContext, type GuardResult, type ModulePermission, type RealtimeEvents, RequirePermissions, RequireRoles, type User, type UserAccessResponse, cache, createExGuardClient, realtime };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
10
|
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
11
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
@@ -32,38 +38,132 @@ var ExGuardCache = class {
|
|
|
32
38
|
ttl: ttl || this.ttl
|
|
33
39
|
});
|
|
34
40
|
}
|
|
41
|
+
delete(key) {
|
|
42
|
+
this.cache.delete(key);
|
|
43
|
+
}
|
|
44
|
+
clear() {
|
|
45
|
+
this.cache.clear();
|
|
46
|
+
}
|
|
47
|
+
clearUser(userId) {
|
|
48
|
+
for (const key of this.cache.keys()) {
|
|
49
|
+
if (key.includes(userId)) {
|
|
50
|
+
this.cache.delete(key);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
35
54
|
};
|
|
36
55
|
var cache = new ExGuardCache();
|
|
56
|
+
var ExGuardRealtime = class {
|
|
57
|
+
constructor() {
|
|
58
|
+
this.socket = null;
|
|
59
|
+
this.connected = false;
|
|
60
|
+
this.url = "";
|
|
61
|
+
this.events = {};
|
|
62
|
+
}
|
|
63
|
+
connect(url, events = {}) {
|
|
64
|
+
this.url = url;
|
|
65
|
+
this.events = events;
|
|
66
|
+
if (!url) {
|
|
67
|
+
console.log("[ExGuard] Realtime URL not provided, skipping connection");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const { io } = __require("socket.io-client");
|
|
72
|
+
this.socket = io(url, {
|
|
73
|
+
transports: ["websocket"],
|
|
74
|
+
reconnection: true,
|
|
75
|
+
reconnectionAttempts: 5,
|
|
76
|
+
reconnectionDelay: 1e3
|
|
77
|
+
});
|
|
78
|
+
this.socket.on("connect", () => {
|
|
79
|
+
this.connected = true;
|
|
80
|
+
console.log("[ExGuard] Realtime connected");
|
|
81
|
+
this.events.onConnect?.();
|
|
82
|
+
});
|
|
83
|
+
this.socket.on("disconnect", () => {
|
|
84
|
+
this.connected = false;
|
|
85
|
+
console.log("[ExGuard] Realtime disconnected");
|
|
86
|
+
this.events.onDisconnect?.();
|
|
87
|
+
});
|
|
88
|
+
this.socket.on("permission:update", (data) => {
|
|
89
|
+
console.log("[ExGuard] Permission update received:", data);
|
|
90
|
+
cache.clearUser(data.userId);
|
|
91
|
+
this.events.onPermissionUpdate?.(data);
|
|
92
|
+
});
|
|
93
|
+
this.socket.on("user:update", (data) => {
|
|
94
|
+
console.log("[ExGuard] User update received:", data);
|
|
95
|
+
cache.clearUser(data.userId);
|
|
96
|
+
this.events.onUserUpdate?.(data);
|
|
97
|
+
});
|
|
98
|
+
this.socket.on("connect_error", (err) => {
|
|
99
|
+
console.error("[ExGuard] Realtime connection error:", err.message);
|
|
100
|
+
});
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error("[ExGuard] Failed to load socket.io-client:", error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
disconnect() {
|
|
106
|
+
if (this.socket) {
|
|
107
|
+
this.socket.disconnect();
|
|
108
|
+
this.socket = null;
|
|
109
|
+
this.connected = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
isConnected() {
|
|
113
|
+
return this.connected;
|
|
114
|
+
}
|
|
115
|
+
emit(event, data) {
|
|
116
|
+
if (this.socket && this.connected) {
|
|
117
|
+
this.socket.emit(event, data);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var realtime = new ExGuardRealtime();
|
|
37
122
|
var ExGuardClient = class {
|
|
38
123
|
constructor(config) {
|
|
39
124
|
this.baseUrl = config.baseUrl;
|
|
40
125
|
this.apiKey = config.apiKey;
|
|
41
126
|
this.cache = cache;
|
|
127
|
+
this.realtime = realtime;
|
|
128
|
+
if (config.realtime?.enabled && config.realtime?.url) {
|
|
129
|
+
this.realtime.connect(config.realtime.url);
|
|
130
|
+
}
|
|
42
131
|
}
|
|
43
132
|
async authenticate(context) {
|
|
44
133
|
const cacheKey = `auth:${context.token}`;
|
|
45
134
|
const cached = this.cache.get(cacheKey);
|
|
46
135
|
if (cached) return cached;
|
|
47
136
|
try {
|
|
48
|
-
const
|
|
137
|
+
const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {
|
|
49
138
|
method: "POST",
|
|
50
139
|
headers: {
|
|
51
140
|
"Content-Type": "application/json",
|
|
52
|
-
"Authorization": `Bearer ${
|
|
141
|
+
"Authorization": `Bearer ${context.token}`
|
|
53
142
|
},
|
|
54
|
-
body: JSON.stringify({
|
|
143
|
+
body: JSON.stringify({ id_token: context.token })
|
|
55
144
|
});
|
|
56
|
-
if (!
|
|
145
|
+
if (!verifyResponse.ok) {
|
|
57
146
|
return { allowed: false, error: "Invalid token" };
|
|
58
147
|
}
|
|
59
|
-
const
|
|
148
|
+
const verifyData = await verifyResponse.json();
|
|
149
|
+
const meResponse = await fetch(`${this.baseUrl}/guard/me`, {
|
|
150
|
+
method: "GET",
|
|
151
|
+
headers: {
|
|
152
|
+
"Authorization": `Bearer ${context.token}`
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
if (!meResponse.ok) {
|
|
156
|
+
return { allowed: false, error: "Failed to get user access" };
|
|
157
|
+
}
|
|
158
|
+
const meData = await meResponse.json();
|
|
60
159
|
const result = {
|
|
61
160
|
allowed: true,
|
|
62
|
-
user: data
|
|
161
|
+
user: meData.data || meData
|
|
63
162
|
};
|
|
64
163
|
this.cache.set(cacheKey, result);
|
|
65
164
|
return result;
|
|
66
165
|
} catch (error) {
|
|
166
|
+
console.error("[ExGuard] Authentication error:", error);
|
|
67
167
|
return { allowed: false, error: "Authentication failed" };
|
|
68
168
|
}
|
|
69
169
|
}
|
|
@@ -73,6 +173,12 @@ var ExGuardClient = class {
|
|
|
73
173
|
const userPermissions = result.user.modules?.flatMap((m) => m.permissions) || [];
|
|
74
174
|
return userPermissions.includes(permission);
|
|
75
175
|
}
|
|
176
|
+
clearCache() {
|
|
177
|
+
this.cache.clear();
|
|
178
|
+
}
|
|
179
|
+
disconnectRealtime() {
|
|
180
|
+
this.realtime.disconnect();
|
|
181
|
+
}
|
|
76
182
|
};
|
|
77
183
|
function createExGuardClient(config) {
|
|
78
184
|
return new ExGuardClient(config);
|
|
@@ -85,7 +191,8 @@ var ExGuardModule = class {
|
|
|
85
191
|
const client = new ExGuardClient({
|
|
86
192
|
baseUrl: options.baseUrl,
|
|
87
193
|
apiKey: options.apiKey,
|
|
88
|
-
cache: options.cache
|
|
194
|
+
cache: options.cache,
|
|
195
|
+
realtime: options.realtime
|
|
89
196
|
});
|
|
90
197
|
return {
|
|
91
198
|
module: ExGuardModule,
|
|
@@ -120,7 +227,8 @@ var ExGuardPermissionGuard = class {
|
|
|
120
227
|
throw new UnauthorizedException("No token provided");
|
|
121
228
|
}
|
|
122
229
|
if (!this.client) {
|
|
123
|
-
|
|
230
|
+
console.warn("[ExGuard] Client not configured. Access denied.");
|
|
231
|
+
return false;
|
|
124
232
|
}
|
|
125
233
|
const authResult = await this.client.authenticate({ token, request });
|
|
126
234
|
if (!authResult.allowed) {
|
|
@@ -201,6 +309,7 @@ export {
|
|
|
201
309
|
RequirePermissions,
|
|
202
310
|
RequireRoles,
|
|
203
311
|
cache,
|
|
204
|
-
createExGuardClient
|
|
312
|
+
createExGuardClient,
|
|
313
|
+
realtime
|
|
205
314
|
};
|
|
206
315
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export interface ExGuardConfig {\n baseUrl: string;\n apiKey: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n}\n\nexport interface User {\n id: string;\n username: string;\n email: string;\n}\n\nexport interface ModulePermission {\n key: string;\n name: string;\n permissions: string[];\n}\n\nexport interface UserAccessResponse {\n user: User;\n roles: string[];\n modules: ModulePermission[];\n}\n\nexport interface GuardContext {\n token: string;\n request?: any;\n}\n\nexport interface GuardResult {\n allowed: boolean;\n user?: UserAccessResponse;\n error?: string;\n}\n\nclass ExGuardCache {\n private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n private ttl = 300000;\n\n get(key: string): any {\n const entry = this.cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > entry.ttl) {\n this.cache.delete(key);\n return null;\n }\n return entry.data;\n }\n\n set(key: string, data: any, ttl?: number): void {\n this.cache.set(key, {\n data,\n timestamp: Date.now(),\n ttl: ttl || this.ttl,\n });\n }\n}\n\nconst cache = new ExGuardCache();\n\nexport class ExGuardClient {\n private baseUrl: string;\n private apiKey: string;\n private cache: ExGuardCache;\n\n constructor(config: ExGuardConfig) {\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n this.cache = cache;\n }\n\n async authenticate(context: GuardContext): Promise<GuardResult> {\n const cacheKey = `auth:${context.token}`;\n const cached = this.cache.get(cacheKey);\n if (cached) return cached;\n\n try {\n const response = await fetch(`${this.baseUrl}/api/v1/auth/validate`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ token: context.token }),\n });\n\n if (!response.ok) {\n return { allowed: false, error: 'Invalid token' };\n }\n\n const data = await response.json();\n const result: GuardResult = {\n allowed: true,\n user: data.user || data.data?.user,\n };\n\n this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n return { allowed: false, error: 'Authentication failed' };\n }\n }\n\n async hasPermission(token: string, permission: string): Promise<boolean> {\n const result = await this.authenticate({ token });\n if (!result.allowed || !result.user) return false;\n\n const userPermissions = result.user.modules?.flatMap(m => m.permissions) || [];\n return userPermissions.includes(permission);\n }\n}\n\nexport function createExGuardClient(config: ExGuardConfig): ExGuardClient {\n return new ExGuardClient(config);\n}\n\nexport { cache };\n","import { Module, Global, DynamicModule } from '@nestjs/common';\nimport { ExGuardClient } from './client.js';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n apiKey: 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 const client = new ExGuardClient({\n baseUrl: options.baseUrl,\n apiKey: options.apiKey,\n cache: options.cache,\n });\n\n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_CLIENT',\n useValue: client,\n },\n ],\n exports: ['EXGUARD_CLIENT'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { ExGuardClient } from './client.js';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_CLIENT') private client: ExGuardClient,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.client) {\n return true;\n }\n\n const authResult = await this.client.authenticate({ token, request });\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get<{ permissions: string[]; requireAll?: boolean }>(\n EXGUARD_PERMISSIONS_KEY,\n handler,\n );\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules?.flatMap(m => m.permissions) || [];\n\n if (requireAll) {\n if (!permissions.every(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get<{ roles: string[]; requireAll?: boolean }>(\n EXGUARD_ROLES_KEY,\n handler,\n );\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/module.ts","../src/guard.ts"],"sourcesContent":["export interface ExGuardConfig {\n baseUrl: string;\n apiKey: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n realtime?: {\n enabled?: boolean;\n url?: string;\n };\n}\n\nexport interface User {\n id: string;\n cognitoSubId: string;\n username: string;\n email: string;\n emailVerified: boolean;\n givenName: string;\n familyName: string;\n employeeNumber: string;\n regionId: string;\n createdAt: string;\n updatedAt: string;\n lastLoginAt: string;\n fieldOffice: {\n id: string;\n code: string;\n name: string;\n };\n}\n\nexport interface ModulePermission {\n key: string;\n name: string;\n permissions: string[];\n}\n\nexport interface UserAccessResponse {\n user: User;\n groups: string[];\n roles: string[];\n modules: ModulePermission[];\n fieldOffices: string[];\n}\n\nexport interface GuardContext {\n token: string;\n request?: any;\n}\n\nexport interface GuardResult {\n allowed: boolean;\n user?: UserAccessResponse;\n error?: string;\n}\n\nexport interface RealtimeEvents {\n onConnect?: () => void;\n onDisconnect?: () => void;\n onPermissionUpdate?: (data: { userId: string; permissions: string[] }) => void;\n onUserUpdate?: (data: { userId: string; user: UserAccessResponse }) => void;\n}\n\nclass ExGuardCache {\n private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n private ttl = 300000;\n\n get(key: string): any {\n const entry = this.cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.timestamp > entry.ttl) {\n this.cache.delete(key);\n return null;\n }\n return entry.data;\n }\n\n set(key: string, data: any, ttl?: number): void {\n this.cache.set(key, {\n data,\n timestamp: Date.now(),\n ttl: ttl || this.ttl,\n });\n }\n\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n clearUser(userId: string): void {\n for (const key of this.cache.keys()) {\n if (key.includes(userId)) {\n this.cache.delete(key);\n }\n }\n }\n}\n\nconst cache = new ExGuardCache();\n\nclass ExGuardRealtime {\n private socket: any = null;\n private connected = false;\n private url: string = '';\n private events: RealtimeEvents = {};\n\n connect(url: string, events: RealtimeEvents = {}): void {\n this.url = url;\n this.events = events;\n \n if (!url) {\n console.log('[ExGuard] Realtime URL not provided, skipping connection');\n return;\n }\n\n try {\n const { io } = require('socket.io-client');\n this.socket = io(url, {\n transports: ['websocket'],\n reconnection: true,\n reconnectionAttempts: 5,\n reconnectionDelay: 1000,\n });\n\n this.socket.on('connect', () => {\n this.connected = true;\n console.log('[ExGuard] Realtime connected');\n this.events.onConnect?.();\n });\n\n this.socket.on('disconnect', () => {\n this.connected = false;\n console.log('[ExGuard] Realtime disconnected');\n this.events.onDisconnect?.();\n });\n\n this.socket.on('permission:update', (data: any) => {\n console.log('[ExGuard] Permission update received:', data);\n cache.clearUser(data.userId);\n this.events.onPermissionUpdate?.(data);\n });\n\n this.socket.on('user:update', (data: any) => {\n console.log('[ExGuard] User update received:', data);\n cache.clearUser(data.userId);\n this.events.onUserUpdate?.(data);\n });\n\n this.socket.on('connect_error', (err: any) => {\n console.error('[ExGuard] Realtime connection error:', err.message);\n });\n } catch (error) {\n console.error('[ExGuard] Failed to load socket.io-client:', error);\n }\n }\n\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n this.connected = false;\n }\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n emit(event: string, data: any): void {\n if (this.socket && this.connected) {\n this.socket.emit(event, data);\n }\n }\n}\n\nconst realtime = new ExGuardRealtime();\n\nexport class ExGuardClient {\n private baseUrl: string;\n private apiKey: string;\n private cache: ExGuardCache;\n private realtime: ExGuardRealtime;\n\n constructor(config: ExGuardConfig) {\n this.baseUrl = config.baseUrl;\n this.apiKey = config.apiKey;\n this.cache = cache;\n this.realtime = realtime;\n\n if (config.realtime?.enabled && config.realtime?.url) {\n this.realtime.connect(config.realtime.url);\n }\n }\n\n async authenticate(context: GuardContext): Promise<GuardResult> {\n const cacheKey = `auth:${context.token}`;\n const cached = this.cache.get(cacheKey);\n if (cached) return cached;\n\n try {\n // First verify the token\n const verifyResponse = await fetch(`${this.baseUrl}/guard/verify-token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${context.token}`,\n },\n body: JSON.stringify({ id_token: context.token }),\n });\n\n if (!verifyResponse.ok) {\n return { allowed: false, error: 'Invalid token' };\n }\n\n const verifyData = await verifyResponse.json();\n\n // Then get user access info\n const meResponse = await fetch(`${this.baseUrl}/guard/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${context.token}`,\n },\n });\n\n if (!meResponse.ok) {\n return { allowed: false, error: 'Failed to get user access' };\n }\n\n const meData = await meResponse.json();\n \n const result: GuardResult = {\n allowed: true,\n user: meData.data || meData,\n };\n\n this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n console.error('[ExGuard] Authentication error:', error);\n return { allowed: false, error: 'Authentication failed' };\n }\n }\n\n async hasPermission(token: string, permission: string): Promise<boolean> {\n const result = await this.authenticate({ token });\n if (!result.allowed || !result.user) return false;\n\n const userPermissions = result.user.modules?.flatMap(m => m.permissions) || [];\n return userPermissions.includes(permission);\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n disconnectRealtime(): void {\n this.realtime.disconnect();\n }\n}\n\nexport function createExGuardClient(config: ExGuardConfig): ExGuardClient {\n return new ExGuardClient(config);\n}\n\nexport { cache, realtime };\n","import { Module, Global, DynamicModule } from '@nestjs/common';\nimport { ExGuardClient } from './client.js';\n\nexport interface ExGuardModuleOptions {\n baseUrl: string;\n apiKey: string;\n cache?: {\n enabled?: boolean;\n ttl?: number;\n };\n realtime?: {\n enabled?: boolean;\n url?: string;\n };\n}\n\n@Global()\n@Module({})\nexport class ExGuardModule {\n static forRoot(options: ExGuardModuleOptions): DynamicModule {\n const client = new ExGuardClient({\n baseUrl: options.baseUrl,\n apiKey: options.apiKey,\n cache: options.cache,\n realtime: options.realtime,\n });\n\n return {\n module: ExGuardModule,\n providers: [\n {\n provide: 'EXGUARD_CLIENT',\n useValue: client,\n },\n ],\n exports: ['EXGUARD_CLIENT'],\n };\n }\n}\n","import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException, Inject, Optional } from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { ExGuardClient } from './client.js';\n\nexport const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';\nexport const EXGUARD_ROLES_KEY = 'exguard_roles';\n\n@Injectable()\nexport class ExGuardPermissionGuard implements CanActivate {\n constructor(\n @Optional() @Inject('EXGUARD_CLIENT') private client: ExGuardClient,\n private reflector: Reflector,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest();\n const token = this.extractToken(request);\n\n if (!token) {\n throw new UnauthorizedException('No token provided');\n }\n\n if (!this.client) {\n console.warn('[ExGuard] Client not configured. Access denied.');\n return false;\n }\n\n const authResult = await this.client.authenticate({ token, request });\n\n if (!authResult.allowed) {\n throw new ForbiddenException(authResult.error || 'Access denied');\n }\n\n if (!authResult.user) {\n throw new ForbiddenException('User not found');\n }\n\n const handler = context.getHandler();\n const permMeta = this.reflector.get<{ permissions: string[]; requireAll?: boolean }>(\n EXGUARD_PERMISSIONS_KEY,\n handler,\n );\n\n if (permMeta) {\n const { permissions, requireAll } = permMeta;\n const userPermissions = authResult.user.modules?.flatMap(m => m.permissions) || [];\n\n if (requireAll) {\n if (!permissions.every(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n } else {\n if (!permissions.some(p => userPermissions.includes(p))) {\n throw new ForbiddenException('Insufficient permissions');\n }\n }\n }\n\n const roleMeta = this.reflector.get<{ roles: string[]; requireAll?: boolean }>(\n EXGUARD_ROLES_KEY,\n handler,\n );\n\n if (roleMeta) {\n const { roles, requireAll } = roleMeta;\n const userRoles = authResult.user.roles || [];\n\n if (requireAll) {\n if (!roles.every(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n } else {\n if (!roles.some(r => userRoles.includes(r))) {\n throw new ForbiddenException('Insufficient roles');\n }\n }\n }\n\n request.user = authResult.user;\n return true;\n }\n\n private extractToken(request: any): string | null {\n const auth = request.headers?.authorization;\n if (auth?.startsWith('Bearer ')) {\n return auth.substring(7);\n }\n return request.headers?.['x-access-token'] || null;\n }\n}\n\nexport function RequirePermissions(permissions: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n\nexport function RequireRoles(roles: string[], requireAll = false) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);\n return descriptor;\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/setup.cjs
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
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
|
+
import { ExGuardClient } from 'exguard-endpoint';
|
|
7
|
+
|
|
8
|
+
export const EXGUARD_PERMISSIONS_KEY = 'exguard_permissions';
|
|
9
|
+
export const EXGUARD_ROLES_KEY = 'exguard_roles';
|
|
10
|
+
|
|
11
|
+
@Injectable()
|
|
12
|
+
export class ExGuardPermissionGuard implements CanActivate {
|
|
13
|
+
constructor(
|
|
14
|
+
@Optional() @Inject('EXGUARD_CLIENT') private client: ExGuardClient,
|
|
15
|
+
private reflector: Reflector,
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
19
|
+
const request = context.switchToHttp().getRequest();
|
|
20
|
+
const token = this.extractToken(request);
|
|
21
|
+
|
|
22
|
+
if (!token) {
|
|
23
|
+
throw new UnauthorizedException('No token provided');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!this.client) {
|
|
27
|
+
console.warn('[ExGuard] Client not configured. Access denied.');
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const authResult = await this.client.authenticate({ token, request });
|
|
32
|
+
|
|
33
|
+
if (!authResult.allowed) {
|
|
34
|
+
throw new ForbiddenException(authResult.error || 'Access denied');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!authResult.user) {
|
|
38
|
+
throw new ForbiddenException('User not found');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const handler = context.getHandler();
|
|
42
|
+
const permMeta = this.reflector.get(EXGUARD_PERMISSIONS_KEY, handler);
|
|
43
|
+
|
|
44
|
+
if (permMeta) {
|
|
45
|
+
const { permissions, requireAll } = permMeta;
|
|
46
|
+
const userPermissions = authResult.user.modules ? authResult.user.modules.flatMap(m => m.permissions) : [];
|
|
47
|
+
|
|
48
|
+
if (requireAll) {
|
|
49
|
+
if (!permissions.every(p => userPermissions.includes(p))) {
|
|
50
|
+
throw new ForbiddenException('Insufficient permissions');
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
if (!permissions.some(p => userPermissions.includes(p))) {
|
|
54
|
+
throw new ForbiddenException('Insufficient permissions');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const roleMeta = this.reflector.get(EXGUARD_ROLES_KEY, handler);
|
|
60
|
+
|
|
61
|
+
if (roleMeta) {
|
|
62
|
+
const { roles, requireAll } = roleMeta;
|
|
63
|
+
const userRoles = authResult.user.roles || [];
|
|
64
|
+
|
|
65
|
+
if (requireAll) {
|
|
66
|
+
if (!roles.every(r => userRoles.includes(r))) {
|
|
67
|
+
throw new ForbiddenException('Insufficient roles');
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
if (!roles.some(r => userRoles.includes(r))) {
|
|
71
|
+
throw new ForbiddenException('Insufficient roles');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
request.user = authResult.user;
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private extractToken(request) {
|
|
81
|
+
const auth = request.headers ? request.headers.authorization : null;
|
|
82
|
+
if (auth && auth.startsWith('Bearer ')) {
|
|
83
|
+
return auth.substring(7);
|
|
84
|
+
}
|
|
85
|
+
return request.headers ? request.headers['x-access-token'] : null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function RequirePermissions(permissions, requireAll = false) {
|
|
90
|
+
return function (target, propertyKey, descriptor) {
|
|
91
|
+
Reflect.defineMetadata(EXGUARD_PERMISSIONS_KEY, { permissions, requireAll }, descriptor.value);
|
|
92
|
+
return descriptor;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function RequireRoles(roles, requireAll = false) {
|
|
97
|
+
return function (target, propertyKey, descriptor) {
|
|
98
|
+
Reflect.defineMetadata(EXGUARD_ROLES_KEY, { roles, requireAll }, descriptor.value);
|
|
99
|
+
return descriptor;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
`;
|
|
103
|
+
|
|
104
|
+
const MODULE_CONTENT = `import { Module, Global, DynamicModule } from '@nestjs/common';
|
|
105
|
+
import { ExGuardModule } from 'exguard-endpoint';
|
|
106
|
+
|
|
107
|
+
@Global()
|
|
108
|
+
@Module({})
|
|
109
|
+
export class ExGuardModuleSetup {
|
|
110
|
+
static forRoot(options) {
|
|
111
|
+
return ExGuardModule.forRoot(options);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
function ensureDir(dirPath) {
|
|
117
|
+
if (!fs.existsSync(dirPath)) {
|
|
118
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function writeFile(filePath, content) {
|
|
123
|
+
const dir = path.dirname(filePath);
|
|
124
|
+
ensureDir(dir);
|
|
125
|
+
fs.writeFileSync(filePath, content);
|
|
126
|
+
console.log('Created: ' + filePath);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function setup() {
|
|
130
|
+
const srcDir = path.join(process.cwd(), 'src');
|
|
131
|
+
const exguardDir = path.join(srcDir, 'exguard');
|
|
132
|
+
|
|
133
|
+
if (!fs.existsSync(srcDir)) {
|
|
134
|
+
console.error('Error: Run this command in your NestJS project root');
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
ensureDir(exguardDir);
|
|
139
|
+
|
|
140
|
+
writeFile(path.join(exguardDir, 'exguard.guard.ts'), GUARD_CONTENT);
|
|
141
|
+
writeFile(path.join(exguardDir, 'exguard.module.ts'), MODULE_CONTENT);
|
|
142
|
+
|
|
143
|
+
console.log('');
|
|
144
|
+
console.log('===========================================');
|
|
145
|
+
console.log('✅ ExGuard Setup Complete!');
|
|
146
|
+
console.log('===========================================');
|
|
147
|
+
console.log('');
|
|
148
|
+
console.log('Next steps:');
|
|
149
|
+
console.log('');
|
|
150
|
+
console.log('1. Add to app.module.ts:');
|
|
151
|
+
console.log('');
|
|
152
|
+
console.log(' import { ExGuardModule } from "./exguard/exguard.module";');
|
|
153
|
+
console.log('');
|
|
154
|
+
console.log(' @Module({');
|
|
155
|
+
console.log(' imports: [');
|
|
156
|
+
console.log(' ExGuardModule.forRoot({');
|
|
157
|
+
console.log(' baseUrl: process.env.EXGUARD_BASE_URL,');
|
|
158
|
+
console.log(' apiKey: process.env.EXGUARD_API_KEY,');
|
|
159
|
+
console.log(' realtime: {');
|
|
160
|
+
console.log(' enabled: true,');
|
|
161
|
+
console.log(' url: process.env.EXGUARD_REALTIME_URL,');
|
|
162
|
+
console.log(' },');
|
|
163
|
+
console.log(' }),');
|
|
164
|
+
console.log(' ],');
|
|
165
|
+
console.log(' })');
|
|
166
|
+
console.log(' export class AppModule {}');
|
|
167
|
+
console.log('');
|
|
168
|
+
console.log('2. Add environment variables:');
|
|
169
|
+
console.log('');
|
|
170
|
+
console.log(' EXGUARD_BASE_URL=https://api.exguard.com');
|
|
171
|
+
console.log(' EXGUARD_API_KEY=your_api_key');
|
|
172
|
+
console.log(' EXGUARD_REALTIME_URL=wss://realtime.exguard.com');
|
|
173
|
+
console.log('');
|
|
174
|
+
console.log('3. Use in controllers:');
|
|
175
|
+
console.log('');
|
|
176
|
+
console.log(' import { ExGuardPermissionGuard, RequirePermissions } from "./exguard/exguard.guard";');
|
|
177
|
+
console.log('');
|
|
178
|
+
console.log(' @Controller("items")');
|
|
179
|
+
console.log(' @UseGuards(ExGuardPermissionGuard)');
|
|
180
|
+
console.log(' export class ItemsController {');
|
|
181
|
+
console.log(' @Get() @RequirePermissions(["item:read"]) findAll() {}');
|
|
182
|
+
console.log(' @Post() @RequirePermissions(["item:create"]) create() {}');
|
|
183
|
+
console.log(' }');
|
|
184
|
+
console.log('');
|
|
185
|
+
console.log('===========================================');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const args = process.argv.slice(2);
|
|
189
|
+
|
|
190
|
+
if (args[0] === 'setup') {
|
|
191
|
+
setup();
|
|
192
|
+
} else {
|
|
193
|
+
console.log('===========================================');
|
|
194
|
+
console.log('ExGuard Endpoint - NestJS RBAC Guard');
|
|
195
|
+
console.log('===========================================');
|
|
196
|
+
console.log('');
|
|
197
|
+
console.log('To setup in your NestJS project:');
|
|
198
|
+
console.log(' npx exguard-endpoint setup');
|
|
199
|
+
console.log('');
|
|
200
|
+
console.log('Or add manually:');
|
|
201
|
+
console.log(' 1. Import ExGuardModule in app.module.ts');
|
|
202
|
+
console.log(' 2. Use ExGuardPermissionGuard in controllers');
|
|
203
|
+
console.log('');
|
|
204
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exguard-endpoint",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
-
"description": "Simple RBAC permission guard for NestJS",
|
|
9
|
+
"description": "Simple RBAC permission guard for NestJS with realtime support",
|
|
10
10
|
"main": "./dist/index.cjs",
|
|
11
11
|
"module": "./dist/index.js",
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
@@ -17,25 +17,31 @@
|
|
|
17
17
|
"require": "./dist/index.cjs"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"exguard-endpoint": "./dist/setup.cjs"
|
|
22
|
+
},
|
|
20
23
|
"files": [
|
|
21
24
|
"dist"
|
|
22
25
|
],
|
|
23
26
|
"scripts": {
|
|
24
27
|
"build": "tsup",
|
|
25
28
|
"clean": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\"",
|
|
26
|
-
"prebuild": "npm run clean"
|
|
29
|
+
"prebuild": "npm run clean",
|
|
30
|
+
"setup": "node dist/setup.cjs"
|
|
27
31
|
},
|
|
28
32
|
"keywords": [
|
|
29
33
|
"exguard",
|
|
30
34
|
"rbac",
|
|
31
35
|
"nestjs",
|
|
32
36
|
"permissions",
|
|
33
|
-
"guards"
|
|
37
|
+
"guards",
|
|
38
|
+
"realtime"
|
|
34
39
|
],
|
|
35
40
|
"author": "EmpowerX",
|
|
36
41
|
"license": "MIT",
|
|
37
42
|
"dependencies": {
|
|
38
|
-
"axios": "^1.0.0"
|
|
43
|
+
"axios": "^1.0.0",
|
|
44
|
+
"socket.io-client": "^4.8.3"
|
|
39
45
|
},
|
|
40
46
|
"devDependencies": {
|
|
41
47
|
"@nestjs/common": "^10.0.0",
|