exisjs 0.7.12 → 0.7.14
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 +195 -39
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +3 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/config/types.d.ts +2 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/decorators/params.d.ts +21 -0
- package/dist/decorators/params.d.ts.map +1 -1
- package/dist/decorators/params.js +21 -1
- package/dist/decorators/params.js.map +1 -1
- package/dist/di/container.d.ts +15 -6
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js +26 -3
- package/dist/di/container.js.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/index.js.map +1 -1
- package/dist/di/inject.d.ts +2 -1
- package/dist/di/inject.d.ts.map +1 -1
- package/dist/di/inject.js +2 -1
- package/dist/di/inject.js.map +1 -1
- package/dist/docs/ARCHITECTURE.md +130 -78
- package/dist/docs/BENCHMARKS.md +4 -4
- package/dist/docs/content/README.md +43 -44
- package/dist/docs/content/boundaries.mdx +138 -61
- package/dist/docs/content/cli/commands.mdx +151 -128
- package/dist/docs/content/cli/overview.mdx +61 -47
- package/dist/docs/content/cli/usage.mdx +107 -30
- package/dist/docs/content/config.mdx +145 -158
- package/dist/docs/content/controllers.mdx +193 -464
- package/dist/docs/content/cron.mdx +183 -69
- package/dist/docs/content/decorators.mdx +449 -154
- package/dist/docs/content/errors.mdx +202 -305
- package/dist/docs/content/exisjs-mcp.mdx +59 -49
- package/dist/docs/content/installation.mdx +243 -157
- package/dist/docs/content/introduction.mdx +220 -48
- package/dist/docs/content/middleware.mdx +171 -497
- package/dist/docs/content/modules.mdx +141 -83
- package/dist/docs/content/providers.mdx +216 -180
- package/dist/docs/content/requests.mdx +168 -488
- package/dist/docs/content/responses.mdx +200 -533
- package/dist/docs/content/routing.mdx +235 -598
- package/dist/docs/content/security.mdx +233 -57
- package/dist/docs/content/structure.mdx +151 -240
- package/dist/docs/content/swagger.mdx +192 -91
- package/dist/docs/content/testing.mdx +205 -74
- package/dist/docs/content/validation.mdx +479 -341
- package/dist/docs/reference/README.md +34 -2
- package/dist/docs/reference/config.mdx +277 -75
- package/dist/docs/reference/context.mdx +197 -106
- package/dist/docs/reference/cron.mdx +266 -152
- package/dist/docs/reference/database.mdx +135 -53
- package/dist/docs/reference/decorators.mdx +234 -116
- package/dist/docs/reference/di.mdx +146 -112
- package/dist/docs/reference/error.mdx +155 -178
- package/dist/docs/reference/exis.mdx +141 -70
- package/dist/docs/reference/logger.mdx +110 -126
- package/dist/docs/reference/middleware.mdx +235 -158
- package/dist/docs/reference/plugin.mdx +136 -64
- package/dist/docs/reference/router.mdx +275 -293
- package/dist/docs/reference/sanitize.mdx +91 -87
- package/dist/docs/reference/swagger.mdx +280 -100
- package/dist/docs/reference/testing.mdx +223 -89
- package/dist/docs/reference/validator.mdx +194 -206
- package/dist/lib/start-server.js +22 -8
- package/dist/lib/start-server.js.map +1 -1
- package/dist/module/module.d.ts +6 -3
- package/dist/module/module.d.ts.map +1 -1
- package/dist/module/module.js +17 -3
- package/dist/module/module.js.map +1 -1
- package/dist/router/controller-registrar.d.ts.map +1 -1
- package/dist/router/controller-registrar.js +26 -4
- package/dist/router/controller-registrar.js.map +1 -1
- package/dist/router/scanner/fs-scanner.d.ts.map +1 -1
- package/dist/router/scanner/fs-scanner.js +22 -0
- package/dist/router/scanner/fs-scanner.js.map +1 -1
- package/dist/router/types.d.ts +6 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/server/app.d.ts +3 -1
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +24 -8
- package/dist/server/app.js.map +1 -1
- package/dist/server/bootstrapper.d.ts.map +1 -1
- package/dist/server/bootstrapper.js +19 -0
- package/dist/server/bootstrapper.js.map +1 -1
- package/dist/server/request.d.ts +2 -0
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +11 -0
- package/dist/server/request.js.map +1 -1
- package/dist/server/response.d.ts +10 -5
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +65 -18
- package/dist/server/response.js.map +1 -1
- package/dist/swagger/generator.d.ts.map +1 -1
- package/dist/swagger/generator.js +130 -10
- package/dist/swagger/generator.js.map +1 -1
- package/dist/telemetry/index.d.ts +12 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +121 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/testing/client.d.ts +1 -0
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +4 -1
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +11 -4
- package/dist/testing/index.js.map +1 -1
- package/dist/validator/builder.d.ts +26 -1
- package/dist/validator/builder.d.ts.map +1 -1
- package/dist/validator/builder.js +31 -0
- package/dist/validator/builder.js.map +1 -1
- package/dist/validator/engine.d.ts +23 -0
- package/dist/validator/engine.d.ts.map +1 -1
- package/dist/validator/engine.js +228 -31
- package/dist/validator/engine.js.map +1 -1
- package/dist/validator/index.d.ts +8 -4
- package/dist/validator/index.d.ts.map +1 -1
- package/dist/validator/index.js +4 -1
- package/dist/validator/index.js.map +1 -1
- package/dist/validator/tex-types.d.ts +19 -4
- package/dist/validator/tex-types.d.ts.map +1 -1
- package/dist/validator/tex-types.js +102 -0
- package/dist/validator/tex-types.js.map +1 -1
- package/dist/validator/tex.d.ts +1 -1
- package/dist/validator/tex.d.ts.map +1 -1
- package/dist/validator/tex.js +3 -1
- package/dist/validator/tex.js.map +1 -1
- package/dist/validator/types.d.ts +1 -0
- package/dist/validator/types.d.ts.map +1 -1
- package/package.json +46 -14
- package/dist/docs/content/dataloader.mdx +0 -170
- package/dist/docs/reference/dataloader.mdx +0 -91
|
@@ -1,109 +1,238 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
title: "Security and Traffic Control"
|
|
3
|
+
description: "Configure HTTP security headers, CSRF double-submit cookies, CIDR IP filtering, rate limiting, and exploit probe protection in ExisJS."
|
|
4
|
+
nextUrl: "/docs/middleware"
|
|
5
|
+
nextTitle: "Middleware Pipeline"
|
|
4
6
|
---
|
|
5
7
|
|
|
6
|
-
# Security
|
|
8
|
+
# Security and Traffic Control
|
|
7
9
|
|
|
8
|
-
ExisJS
|
|
10
|
+
ExisJS includes built-in security and traffic control middlewares in `exisjs/middleware` and security decorators in `exisjs/decorators`. These components protect against common web vulnerabilities, brute-force requests, parameter pollution, and scanner abuse.
|
|
9
11
|
|
|
10
12
|
---
|
|
11
13
|
|
|
12
|
-
##
|
|
14
|
+
## Security Middleware Reference
|
|
13
15
|
|
|
14
|
-
The following security middlewares are
|
|
16
|
+
The following security middlewares are exported from `exisjs/middleware` and can be registered globally in `server.ts`, in `exis.config.ts`, or scoped to route hierarchies in `boundary.ts`:
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
| Middleware | Signature | Description |
|
|
19
|
+
| :--- | :--- | :--- |
|
|
20
|
+
| `helmet` | `helmet(options?)` | Sets secure HTTP headers (`Strict-Transport-Security`, `X-Content-Type-Options`, `X-Frame-Options`, `Content-Security-Policy`, etc.). |
|
|
21
|
+
| `csrf` | `csrf(options)` | Protects state-changing requests (`POST`, `PUT`, `DELETE`, `PATCH`) using the Signed Double-Submit Cookie pattern. |
|
|
22
|
+
| `rateLimit` | `rateLimit(options?)` | Enforces request volume limits per IP or custom key, returning `429 Too Many Requests` with standard rate-limit headers. |
|
|
23
|
+
| `ipFilter` | `ipFilter(options)` | Grants or restricts access based on client IP addresses and CIDR subnets. |
|
|
24
|
+
| `idempotent` | `idempotent(options?)` | Caches mutation responses keyed by the `Idempotency-Key` header to prevent duplicate operations. |
|
|
25
|
+
| `hpp` | `hpp()` | Protects against HTTP Parameter Pollution by normalizing repeated query parameters and form fields. |
|
|
26
|
+
| `mongoSanitize` | `mongoSanitize()` | Strips MongoDB query operator keys (keys starting with `$` or containing `.`) from bodies, queries, and parameters. |
|
|
27
|
+
| `timeout` | `timeout(ms \| options)` | Terminates requests exceeding a specified duration with an HTTP `503 Service Unavailable` response. |
|
|
28
|
+
| `blockSuspiciousProbes` | `blockSuspiciousProbes(options?)` | Intercepts automated vulnerability scanners (probing for `.env`, `/.git`, WordPress files) and terminates them immediately. |
|
|
29
|
+
| `timingSafeEqual` | `timingSafeEqual(a, b)` | Compares secrets, tokens, or hashes in constant time to prevent side-channel timing attacks. |
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 1. Security Headers (`helmet`)
|
|
34
|
+
|
|
35
|
+
The `helmet` middleware sets secure response headers to harden the application against cross-site scripting (XSS), clickjacking, and content sniffing:
|
|
25
36
|
|
|
26
37
|
```typescript filename="src/http/server.ts"
|
|
27
38
|
import { exis } from 'exisjs'
|
|
28
|
-
import { helmet
|
|
39
|
+
import { helmet } from 'exisjs/middleware'
|
|
29
40
|
|
|
30
41
|
export default exis({
|
|
31
42
|
async onStart(app) {
|
|
32
|
-
app.use(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
app.use(
|
|
44
|
+
helmet({
|
|
45
|
+
hsts: { maxAge: 31536000, includeSubDomains: true },
|
|
46
|
+
frameguard: { action: 'DENY' },
|
|
47
|
+
noSniff: true,
|
|
48
|
+
xssFilter: true,
|
|
49
|
+
contentSecurityPolicy: "default-src 'self'",
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 2. CSRF Protection (`csrf`)
|
|
59
|
+
|
|
60
|
+
ExisJS implements the **Signed Double-Submit Cookie** pattern for CSRF defense:
|
|
61
|
+
- On safe requests (`GET`, `HEAD`, `OPTIONS`), a cryptographically signed cookie is issued to the client.
|
|
62
|
+
- On state-changing requests (`POST`, `PUT`, `DELETE`, `PATCH`), the client must supply the unsigned token in the `x-csrf-token` header.
|
|
63
|
+
|
|
64
|
+
```typescript filename="src/http/server.ts"
|
|
65
|
+
import { exis } from 'exisjs'
|
|
66
|
+
import { csrf } from 'exisjs/middleware'
|
|
67
|
+
|
|
68
|
+
export default exis({
|
|
69
|
+
async onStart(app) {
|
|
70
|
+
app.use(
|
|
71
|
+
csrf({
|
|
72
|
+
secret: process.env.CSRNF_SECRET!, // Must be at least 32 characters
|
|
73
|
+
cookieName: 'csrf-token',
|
|
74
|
+
headerName: 'x-csrf-token',
|
|
75
|
+
cookieOptions: {
|
|
76
|
+
secure: process.env.NODE_ENV === 'production',
|
|
77
|
+
sameSite: 'Lax',
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
)
|
|
36
81
|
},
|
|
37
82
|
})
|
|
38
83
|
```
|
|
39
84
|
|
|
40
85
|
---
|
|
41
86
|
|
|
42
|
-
##
|
|
87
|
+
## 3. Rate Limiting (`rateLimit`)
|
|
43
88
|
|
|
44
|
-
|
|
89
|
+
Use `rateLimit` to prevent brute-force attacks and abuse by capping incoming requests over a sliding time window.
|
|
45
90
|
|
|
46
91
|
```typescript filename="src/http/boundary.ts"
|
|
47
92
|
import { defineBoundary } from 'exisjs/router'
|
|
48
93
|
import { rateLimit } from 'exisjs/middleware'
|
|
49
94
|
|
|
50
|
-
export const config = defineBoundary({})
|
|
51
|
-
|
|
52
|
-
// Apply rate limiting chain step across the boundary
|
|
53
95
|
export const limiter = rateLimit({
|
|
54
|
-
windowMs: 60 * 1000, // 1 minute
|
|
55
|
-
max: 100, // 100 requests per
|
|
96
|
+
windowMs: 60 * 1000, // 1 minute
|
|
97
|
+
max: 100, // Limit each IP to 100 requests per windowMs
|
|
56
98
|
message: 'Too many requests from this IP, please try again after a minute',
|
|
57
|
-
keyGenerator: (req) => req.ip,
|
|
99
|
+
keyGenerator: (req) => req.ip,
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
export const config = defineBoundary({
|
|
103
|
+
middleware: [limiter],
|
|
58
104
|
})
|
|
59
105
|
```
|
|
60
106
|
|
|
61
|
-
###
|
|
107
|
+
### Rate Limit Response Headers
|
|
108
|
+
|
|
109
|
+
`rateLimit` attaches standard IETF and legacy rate-limiting headers to every response:
|
|
62
110
|
|
|
63
|
-
|
|
111
|
+
| Header | Description | Example |
|
|
112
|
+
| :--- | :--- | :--- |
|
|
113
|
+
| `RateLimit-Limit` | Request quota allocated for the configured window. | `100` |
|
|
114
|
+
| `RateLimit-Remaining` | Remaining requests permitted within the active window. | `42` |
|
|
115
|
+
| `RateLimit-Reset` | Time in seconds until the current quota window resets. | `18` |
|
|
116
|
+
| `Retry-After` | Included on `429 Too Many Requests` responses, indicating wait time. | `18` |
|
|
117
|
+
| `X-RateLimit-*` | Legacy header equivalents provided for compatibility. | `X-RateLimit-Reset: 1727871234` |
|
|
118
|
+
|
|
119
|
+
---
|
|
64
120
|
|
|
65
|
-
|
|
66
|
-
| :-------------------- | :------------------------------------------------------------------------- | :---------------------- |
|
|
67
|
-
| `RateLimit-Limit` | Maximum request quota for the configured window. | `100` |
|
|
68
|
-
| `RateLimit-Remaining` | Remaining requests permitted within the current window. | `42` |
|
|
69
|
-
| `RateLimit-Reset` | Time remaining in seconds until the window quota resets. | `18` |
|
|
70
|
-
| `Retry-After` | Included on `429 Too Many Requests` (RFC 6585) indicating seconds to wait. | `18` |
|
|
71
|
-
| `X-RateLimit-*` | Legacy header equivalents for backwards compatibility. | `X-RateLimit-Reset: 18` |
|
|
121
|
+
## 4. IP Filtering and CIDR Subnet Control (`ipFilter`)
|
|
72
122
|
|
|
73
|
-
|
|
74
|
-
|
|
123
|
+
Restrict access to specific IP addresses or CIDR ranges using `ipFilter`:
|
|
124
|
+
|
|
125
|
+
```typescript filename="src/http/admin/boundary.ts"
|
|
126
|
+
import { defineBoundary } from 'exisjs/router'
|
|
127
|
+
import { ipFilter } from 'exisjs/middleware'
|
|
128
|
+
|
|
129
|
+
export const adminIpGuard = ipFilter({
|
|
130
|
+
allow: [
|
|
131
|
+
'127.0.0.1',
|
|
132
|
+
'10.0.0.0/8', // Internal VPN subnet
|
|
133
|
+
'192.168.1.0/24',
|
|
134
|
+
],
|
|
135
|
+
deny: [
|
|
136
|
+
'10.0.0.99', // Block specific compromised host
|
|
137
|
+
],
|
|
138
|
+
message: 'Access Denied: IP address not authorized',
|
|
139
|
+
trustProxy: true,
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
export const config = defineBoundary({
|
|
143
|
+
middleware: [adminIpGuard],
|
|
144
|
+
})
|
|
145
|
+
```
|
|
75
146
|
|
|
76
147
|
---
|
|
77
148
|
|
|
78
|
-
##
|
|
149
|
+
## 5. Idempotency Support (`idempotent`)
|
|
150
|
+
|
|
151
|
+
Prevent duplicate mutations (e.g. double-charging a credit card or duplicate database inserts) by caching responses associated with an `Idempotency-Key` header:
|
|
152
|
+
|
|
153
|
+
```typescript filename="src/http/checkout/route.ts"
|
|
154
|
+
import { controller, route } from 'exisjs/router'
|
|
155
|
+
import { idempotent } from 'exisjs/middleware'
|
|
156
|
+
|
|
157
|
+
export default controller({
|
|
158
|
+
charge: route.post('/', {
|
|
159
|
+
middleware: [
|
|
160
|
+
idempotent({
|
|
161
|
+
headerName: 'Idempotency-Key',
|
|
162
|
+
ttlMs: 24 * 60 * 60 * 1000, // Cache for 24 hours
|
|
163
|
+
}),
|
|
164
|
+
],
|
|
165
|
+
async handle({ body, res }) {
|
|
166
|
+
const charge = await paymentService.createCharge(body)
|
|
167
|
+
res.status(201).json({ success: true, charge })
|
|
168
|
+
},
|
|
169
|
+
}),
|
|
170
|
+
})
|
|
171
|
+
```
|
|
79
172
|
|
|
80
173
|
---
|
|
81
174
|
|
|
82
|
-
##
|
|
175
|
+
## 6. Parameter Pollution and NoSQL Injection Protection
|
|
83
176
|
|
|
84
|
-
|
|
177
|
+
### HTTP Parameter Pollution (`hpp`)
|
|
85
178
|
|
|
86
|
-
|
|
87
|
-
import { timingSafeEqual } from 'exisjs/middleware'
|
|
88
|
-
import { UnauthorizedException } from 'exisjs/error'
|
|
179
|
+
Prevents parameter pollution attacks where query string or body keys are duplicated (e.g. `?role=user&role=admin`):
|
|
89
180
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
181
|
+
```typescript filename="src/http/server.ts"
|
|
182
|
+
import { exis } from 'exisjs'
|
|
183
|
+
import { hpp } from 'exisjs/middleware'
|
|
93
184
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
185
|
+
export default exis({
|
|
186
|
+
async onStart(app) {
|
|
187
|
+
app.use(hpp())
|
|
188
|
+
},
|
|
189
|
+
})
|
|
98
190
|
```
|
|
99
191
|
|
|
100
|
-
|
|
192
|
+
### NoSQL Injection Sanitization (`mongoSanitize` / `dbSanitize`)
|
|
193
|
+
|
|
194
|
+
Strips prohibited operator keys containing `$` or `.` from `req.body`, `req.query`, and `req.params`:
|
|
195
|
+
|
|
196
|
+
```typescript filename="src/http/server.ts"
|
|
197
|
+
import { exis } from 'exisjs'
|
|
198
|
+
import { mongoSanitize } from 'exisjs/middleware'
|
|
199
|
+
|
|
200
|
+
export default exis({
|
|
201
|
+
async onStart(app) {
|
|
202
|
+
app.use(mongoSanitize())
|
|
203
|
+
},
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 7. Request Timeout Control (`timeout`)
|
|
210
|
+
|
|
211
|
+
Set maximum request duration thresholds to avoid hung connections:
|
|
212
|
+
|
|
213
|
+
```typescript filename="src/http/server.ts"
|
|
214
|
+
import { exis } from 'exisjs'
|
|
215
|
+
import { timeout } from 'exisjs/middleware'
|
|
216
|
+
|
|
217
|
+
export default exis({
|
|
218
|
+
async onStart(app) {
|
|
219
|
+
app.use(
|
|
220
|
+
timeout({
|
|
221
|
+
ms: 30000, // 30 seconds
|
|
222
|
+
statusCode: 503,
|
|
223
|
+
message: 'Request timed out',
|
|
224
|
+
exclude: ['/upload', '/stream'],
|
|
225
|
+
})
|
|
226
|
+
)
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
```
|
|
101
230
|
|
|
102
231
|
---
|
|
103
232
|
|
|
104
|
-
##
|
|
233
|
+
## 8. Bot Probe Blackholing (`blockSuspiciousProbes`)
|
|
105
234
|
|
|
106
|
-
|
|
235
|
+
Automated vulnerability bots frequently scan applications for sensitive files (`.env`, `/.git`, `wp-login.php`, `actuator/heapdump`). Use `blockSuspiciousProbes` to intercept and terminate these requests immediately without polluting production application logs:
|
|
107
236
|
|
|
108
237
|
```typescript filename="src/http/server.ts"
|
|
109
238
|
import { exis } from 'exisjs'
|
|
@@ -111,13 +240,60 @@ import { blockSuspiciousProbes } from 'exisjs/middleware'
|
|
|
111
240
|
|
|
112
241
|
export default exis({
|
|
113
242
|
async onStart(app) {
|
|
114
|
-
// Intercepts scanner probes and blackholes them without triggering downstream error logs
|
|
115
243
|
app.use(
|
|
116
244
|
blockSuspiciousProbes({
|
|
117
|
-
blackhole: true, //
|
|
118
|
-
|
|
245
|
+
blackhole: true, // Terminates connection immediately with empty body
|
|
246
|
+
statusCode: 404,
|
|
247
|
+
silent: true, // Suppresses 404 noise from standard logger
|
|
248
|
+
patterns: [/^\/admin-backup/i],
|
|
119
249
|
})
|
|
120
250
|
)
|
|
121
251
|
},
|
|
122
252
|
})
|
|
123
253
|
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 9. Timing-Safe Comparison (`timingSafeEqual`)
|
|
258
|
+
|
|
259
|
+
Use `timingSafeEqual` from `exisjs/middleware` when validating API tokens, signatures, or webhook secrets to guard against timing attacks:
|
|
260
|
+
|
|
261
|
+
```typescript filename="src/common/guards/api-key.guard.ts"
|
|
262
|
+
import { timingSafeEqual } from 'exisjs/middleware'
|
|
263
|
+
import { UnauthorizedException } from 'exisjs/error'
|
|
264
|
+
|
|
265
|
+
export function apiKeyGuard(req: any) {
|
|
266
|
+
const providedKey = req.headers['x-api-key']
|
|
267
|
+
const expectedKey = process.env.API_KEY
|
|
268
|
+
|
|
269
|
+
if (!timingSafeEqual(providedKey, expectedKey)) {
|
|
270
|
+
throw new UnauthorizedException('Invalid API Key')
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## 10. Role-Based Access Control (RBAC) Decorators
|
|
278
|
+
|
|
279
|
+
ExisJS provides declarative security decorators in `exisjs/decorators` for class-based controllers:
|
|
280
|
+
|
|
281
|
+
```typescript filename="src/http/users/controller.ts"
|
|
282
|
+
import { Controller, Get, Post, Roles, Permissions, Public } from 'exisjs/decorators'
|
|
283
|
+
|
|
284
|
+
@Controller('/users')
|
|
285
|
+
@Roles('admin', 'manager')
|
|
286
|
+
export class UsersController {
|
|
287
|
+
@Get('/')
|
|
288
|
+
@Permissions('users:read')
|
|
289
|
+
async listUsers() {
|
|
290
|
+
return []
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@Post('/login')
|
|
294
|
+
@Public() // Bypasses authentication guards
|
|
295
|
+
async login() {
|
|
296
|
+
return { token: 'jwt' }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|