@noony-serverless/core 0.1.1 → 0.2.0
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/build/core/core.d.ts +16 -48
- package/build/core/core.js +2 -61
- package/build/core/handler.d.ts +37 -16
- package/build/core/handler.js +131 -42
- package/build/core/index.d.ts +0 -1
- package/build/core/index.js +0 -1
- package/build/middlewares/ConsolidatedValidationMiddleware.d.ts +126 -0
- package/build/middlewares/ConsolidatedValidationMiddleware.js +330 -0
- package/build/middlewares/ProcessingMiddleware.d.ts +138 -0
- package/build/middlewares/ProcessingMiddleware.js +425 -0
- package/build/middlewares/SecurityMiddleware.d.ts +157 -0
- package/build/middlewares/SecurityMiddleware.js +307 -0
- package/build/middlewares/authenticationMiddleware.d.ts +379 -0
- package/build/middlewares/authenticationMiddleware.js +216 -0
- package/build/middlewares/bodyParserMiddleware.d.ts +99 -0
- package/build/middlewares/bodyParserMiddleware.js +99 -0
- package/build/middlewares/bodyValidationMiddleware.d.ts +69 -3
- package/build/middlewares/bodyValidationMiddleware.js +68 -2
- package/build/middlewares/dependencyInjectionMiddleware.d.ts +238 -0
- package/build/middlewares/dependencyInjectionMiddleware.js +238 -0
- package/build/middlewares/errorHandlerMiddleware.d.ts +94 -0
- package/build/middlewares/errorHandlerMiddleware.js +105 -0
- package/build/middlewares/guards/RouteGuards.d.ts +476 -21
- package/build/middlewares/guards/RouteGuards.js +418 -21
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.d.ts +271 -0
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.js +301 -0
- package/build/middlewares/guards/cache/CacheAdapter.d.ts +369 -28
- package/build/middlewares/guards/cache/CacheAdapter.js +124 -5
- package/build/middlewares/guards/cache/MemoryCacheAdapter.d.ts +113 -4
- package/build/middlewares/guards/cache/MemoryCacheAdapter.js +113 -4
- package/build/middlewares/guards/config/GuardConfiguration.d.ts +568 -18
- package/build/middlewares/guards/config/GuardConfiguration.js +266 -10
- package/build/middlewares/guards/guards/FastAuthGuard.d.ts +5 -5
- package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +5 -13
- package/build/middlewares/guards/guards/PermissionGuardFactory.js +4 -4
- package/build/middlewares/guards/index.d.ts +43 -1
- package/build/middlewares/guards/index.js +46 -1
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.js +1 -1
- package/build/middlewares/guards/resolvers/PermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/PlainPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/WildcardPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/services/FastUserContextService.d.ts +20 -33
- package/build/middlewares/guards/services/FastUserContextService.js +19 -5
- package/build/middlewares/headerVariablesMiddleware.d.ts +118 -0
- package/build/middlewares/headerVariablesMiddleware.js +118 -0
- package/build/middlewares/httpAttributesMiddleware.d.ts +235 -0
- package/build/middlewares/httpAttributesMiddleware.js +236 -1
- package/build/middlewares/index.d.ts +3 -1
- package/build/middlewares/index.js +6 -1
- package/build/middlewares/queryParametersMiddleware.d.ts +105 -0
- package/build/middlewares/queryParametersMiddleware.js +105 -0
- package/build/middlewares/rateLimitingMiddleware.d.ts +601 -9
- package/build/middlewares/rateLimitingMiddleware.js +623 -11
- package/build/middlewares/responseWrapperMiddleware.d.ts +170 -1
- package/build/middlewares/responseWrapperMiddleware.js +170 -1
- package/build/middlewares/securityAuditMiddleware.js +5 -5
- package/package.json +11 -9
- package/build/core/containerPool.d.ts +0 -44
- package/build/core/containerPool.js +0 -103
- package/build/middlewares/validationMiddleware.d.ts +0 -9
- package/build/middlewares/validationMiddleware.js +0 -40
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.errorHandler = exports.ErrorHandlerMiddleware = void 0;
|
|
4
4
|
const core_1 = require("../core");
|
|
5
|
+
/**
|
|
6
|
+
* Handles errors thrown during request processing and sends an appropriate JSON response.
|
|
7
|
+
*
|
|
8
|
+
* - Logs error details including message, stack, request ID, user agent, and IP.
|
|
9
|
+
* - For `HttpError` instances, responds with the error message, and optionally details and code based on environment and error type.
|
|
10
|
+
* - For other errors, responds with a generic message in production, and includes stack trace in development.
|
|
11
|
+
*
|
|
12
|
+
* @param error - The error object thrown during request processing.
|
|
13
|
+
* @param context - The request context containing request and response objects.
|
|
14
|
+
* @returns A promise that resolves when the error response has been sent.
|
|
15
|
+
*/
|
|
5
16
|
const handleError = async (error, context) => {
|
|
6
17
|
const isDevelopment = process.env.NODE_ENV === 'development' || process.env.DEBUG === 'true';
|
|
7
18
|
core_1.logger.error('Error processing request', {
|
|
@@ -49,12 +60,106 @@ const handleError = async (error, context) => {
|
|
|
49
60
|
context.res.status(500).json(responsePayload);
|
|
50
61
|
}
|
|
51
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Middleware class for handling errors in the application.
|
|
65
|
+
* Implements the `BaseMiddleware` interface and provides an asynchronous
|
|
66
|
+
* `onError` method that delegates error handling to the `handleError` function.
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* This middleware should be registered to catch and process errors that occur
|
|
70
|
+
* during request handling.
|
|
71
|
+
*
|
|
72
|
+
* @method onError
|
|
73
|
+
* @param error - The error object that was thrown.
|
|
74
|
+
* @param context - The context in which the error occurred.
|
|
75
|
+
* @returns A promise that resolves when error handling is complete.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* Basic handler with error handling:
|
|
79
|
+
* ```typescript
|
|
80
|
+
* import { Handler, ErrorHandlerMiddleware, HttpError } from '@noony-serverless/core';
|
|
81
|
+
*
|
|
82
|
+
* const createUserHandler = new Handler()
|
|
83
|
+
* .use(new ErrorHandlerMiddleware())
|
|
84
|
+
* .handle(async (request, context) => {
|
|
85
|
+
* if (!request.body?.email) {
|
|
86
|
+
* throw new HttpError(400, 'Email is required', 'MISSING_EMAIL');
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
89
|
+
* return {
|
|
90
|
+
* success: true,
|
|
91
|
+
* data: { id: 'user-123', email: request.body.email }
|
|
92
|
+
* };
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* Google Cloud Functions integration:
|
|
98
|
+
* ```typescript
|
|
99
|
+
* import { http } from '@google-cloud/functions-framework';
|
|
100
|
+
* import { Handler, ErrorHandlerMiddleware } from '@noony-serverless/core';
|
|
101
|
+
*
|
|
102
|
+
* const orderHandler = new Handler()
|
|
103
|
+
* .use(new ErrorHandlerMiddleware())
|
|
104
|
+
* .handle(async (request, context) => {
|
|
105
|
+
* // Handler logic that might throw errors
|
|
106
|
+
* return { success: true, data: processedOrder };
|
|
107
|
+
* });
|
|
108
|
+
*
|
|
109
|
+
* export const processOrder = http('processOrder', (req, res) => {
|
|
110
|
+
* return orderHandler.execute(req, res);
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
52
114
|
class ErrorHandlerMiddleware {
|
|
53
115
|
async onError(error, context) {
|
|
54
116
|
await handleError(error, context);
|
|
55
117
|
}
|
|
56
118
|
}
|
|
57
119
|
exports.ErrorHandlerMiddleware = ErrorHandlerMiddleware;
|
|
120
|
+
/**
|
|
121
|
+
* Creates an error handling middleware for processing errors in the application.
|
|
122
|
+
*
|
|
123
|
+
* @returns {BaseMiddleware} An object implementing the `onError` method to handle errors.
|
|
124
|
+
*
|
|
125
|
+
* @remarks
|
|
126
|
+
* The middleware's `onError` method asynchronously delegates error handling to the `handleError` function,
|
|
127
|
+
* passing the error and context objects.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* Basic usage with factory function:
|
|
131
|
+
* ```typescript
|
|
132
|
+
* import { Handler, errorHandler, HttpError } from '@noony-serverless/core';
|
|
133
|
+
*
|
|
134
|
+
* const loginHandler = new Handler()
|
|
135
|
+
* .use(errorHandler())
|
|
136
|
+
* .handle(async (request, context) => {
|
|
137
|
+
* const { username, password } = request.body || {};
|
|
138
|
+
*
|
|
139
|
+
* if (!username || !password) {
|
|
140
|
+
* throw new HttpError(400, 'Credentials required', 'MISSING_CREDENTIALS');
|
|
141
|
+
* }
|
|
142
|
+
*
|
|
143
|
+
* const user = await authenticateUser(username, password);
|
|
144
|
+
* return { success: true, data: { token: generateToken(user) } };
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* Multiple middleware chain:
|
|
150
|
+
* ```typescript
|
|
151
|
+
* import { Handler, errorHandler, BodyParserMiddleware } from '@noony-serverless/core';
|
|
152
|
+
*
|
|
153
|
+
* const secureHandler = new Handler()
|
|
154
|
+
* .use(new BodyParserMiddleware())
|
|
155
|
+
* .use(new AuthenticationMiddleware())
|
|
156
|
+
* .use(errorHandler()) // Should be last to catch all errors
|
|
157
|
+
* .handle(async (request, context) => {
|
|
158
|
+
* // Handler logic
|
|
159
|
+
* return { success: true, data: result };
|
|
160
|
+
* });
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
58
163
|
const errorHandler = () => ({
|
|
59
164
|
onError: async (error, context) => {
|
|
60
165
|
await handleError(error, context);
|
|
@@ -19,24 +19,128 @@
|
|
|
19
19
|
* - Comprehensive monitoring and audit trails
|
|
20
20
|
* - Framework-agnostic middleware integration
|
|
21
21
|
*
|
|
22
|
-
*
|
|
22
|
+
* @example
|
|
23
|
+
* Complete guard system setup:
|
|
23
24
|
* ```typescript
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* }
|
|
25
|
+
* import { RouteGuards, GuardSetup } from '@noony-serverless/core';
|
|
26
|
+
*
|
|
27
|
+
* // Define user permission source
|
|
28
|
+
* const userPermissionSource = {
|
|
29
|
+
* async getUserPermissions(userId: string): Promise<string[]> {
|
|
30
|
+
* const user = await getUserFromDatabase(userId);
|
|
31
|
+
* return user.permissions;
|
|
32
|
+
* }
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* // Define token validator
|
|
36
|
+
* const tokenValidator = {
|
|
37
|
+
* async validateToken(token: string) {
|
|
38
|
+
* try {
|
|
39
|
+
* const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
40
|
+
* return { valid: true, decoded };
|
|
41
|
+
* } catch (error) {
|
|
42
|
+
* return { valid: false, error: error.message };
|
|
43
|
+
* }
|
|
44
|
+
* },
|
|
45
|
+
* extractUserId: (decoded: unknown) => (decoded as any).sub,
|
|
46
|
+
* isTokenExpired: (decoded: unknown) => (decoded as any).exp < Date.now() / 1000
|
|
47
|
+
* };
|
|
48
|
+
*
|
|
49
|
+
* // Configure guard system
|
|
50
|
+
* await RouteGuards.configure(
|
|
51
|
+
* GuardSetup.production(),
|
|
52
|
+
* userPermissionSource,
|
|
53
|
+
* tokenValidator,
|
|
54
|
+
* {
|
|
55
|
+
* tokenHeader: 'authorization',
|
|
56
|
+
* tokenPrefix: 'Bearer ',
|
|
57
|
+
* requireEmailVerification: true,
|
|
58
|
+
* allowInactiveUsers: false
|
|
59
|
+
* }
|
|
60
|
+
* );
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* Simple permission checks (fastest - ~0.1ms cached):
|
|
65
|
+
* ```typescript
|
|
66
|
+
* import { Handler, RouteGuards } from '@noony-serverless/core';
|
|
67
|
+
*
|
|
68
|
+
* const userManagementHandler = new Handler()
|
|
69
|
+
* .use(RouteGuards.requirePermissions(['user:read', 'user:update']))
|
|
70
|
+
* .handle(async (context) => {
|
|
71
|
+
* // User has either 'user:read' OR 'user:update' permission
|
|
72
|
+
* const users = await getUsers();
|
|
73
|
+
* return { success: true, users };
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* Wildcard permission patterns (hierarchical - ~0.2ms cached):
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const adminHandler = new Handler()
|
|
81
|
+
* .use(RouteGuards.requireWildcardPermissions(['admin.*', 'org.reports.*']))
|
|
82
|
+
* .handle(async (context) => {
|
|
83
|
+
* // User has any permission starting with 'admin.' OR 'org.reports.'
|
|
84
|
+
* const adminData = await getAdminDashboard();
|
|
85
|
+
* return { success: true, data: adminData };
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* Complex boolean expressions (~0.5ms cached):
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const complexAccessHandler = new Handler()
|
|
93
|
+
* .use(RouteGuards.requireComplexPermissions({
|
|
94
|
+
* or: [
|
|
95
|
+
* { permission: 'admin.users' },
|
|
96
|
+
* { and: [
|
|
97
|
+
* { permission: 'moderator.content' },
|
|
98
|
+
* { permission: 'org.reports.view' }
|
|
99
|
+
* ]}
|
|
100
|
+
* ]
|
|
101
|
+
* }))
|
|
102
|
+
* .handle(async (context) => {
|
|
103
|
+
* // User has 'admin.users' OR ('moderator.content' AND 'org.reports.view')
|
|
104
|
+
* return { success: true, accessGranted: true };
|
|
105
|
+
* });
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* Authentication-only (no permissions):
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const profileHandler = new Handler()
|
|
112
|
+
* .use(RouteGuards.requireAuth())
|
|
113
|
+
* .handle(async (context) => {
|
|
114
|
+
* // Only checks if user is authenticated
|
|
115
|
+
* const profile = await getUserProfile(context.user.id);
|
|
116
|
+
* return { success: true, profile };
|
|
117
|
+
* });
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* Cache invalidation for security:
|
|
122
|
+
* ```typescript
|
|
123
|
+
* // Invalidate specific user when permissions change
|
|
124
|
+
* await RouteGuards.invalidateUserPermissions('user-123', 'Permission update');
|
|
125
|
+
*
|
|
126
|
+
* // System-wide invalidation for major updates
|
|
127
|
+
* await RouteGuards.invalidateAllPermissions('System update deployed');
|
|
128
|
+
*
|
|
129
|
+
* // Emergency invalidation for security incidents
|
|
130
|
+
* await RouteGuards.emergencyInvalidation('Security breach detected');
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* Monitoring and health checks:
|
|
135
|
+
* ```typescript
|
|
136
|
+
* // Get comprehensive system statistics
|
|
137
|
+
* const stats = RouteGuards.getSystemStats();
|
|
138
|
+
* console.log('Guard system performance:', stats.systemHealth);
|
|
139
|
+
*
|
|
140
|
+
* // Perform health check
|
|
141
|
+
* const health = await RouteGuards.healthCheck();
|
|
142
|
+
* console.log('System status:', health.status);
|
|
143
|
+
* console.log('Recommendations:', health.details.recommendations);
|
|
40
144
|
* ```
|
|
41
145
|
*
|
|
42
146
|
* @author Noony Framework Team
|
|
@@ -51,8 +155,67 @@ import { FastAuthGuard, AuthGuardConfig, TokenValidator } from './guards/FastAut
|
|
|
51
155
|
import { PermissionGuardFactory } from './guards/PermissionGuardFactory';
|
|
52
156
|
import { PermissionRegistry } from './registry/PermissionRegistry';
|
|
53
157
|
import { PermissionExpression } from './resolvers/PermissionResolver';
|
|
158
|
+
import { CustomTokenVerificationPort } from '../authenticationMiddleware';
|
|
159
|
+
import { TokenVerificationAdapterFactory } from './adapters/CustomTokenVerificationPortAdapter';
|
|
160
|
+
/**
|
|
161
|
+
* Union type supporting both RouteGuards TokenValidator and AuthenticationMiddleware CustomTokenVerificationPort.
|
|
162
|
+
* This enables seamless integration between the two authentication systems.
|
|
163
|
+
*/
|
|
164
|
+
export type AnyTokenValidator = TokenValidator | CustomTokenVerificationPort<unknown>;
|
|
54
165
|
/**
|
|
55
|
-
* Route guard configuration for the facade
|
|
166
|
+
* Route guard configuration for the facade.
|
|
167
|
+
* Provides fine-grained control over guard behavior for specific endpoints.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* Basic guard options:
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const options: RouteGuardOptions = {
|
|
173
|
+
* requireAuth: true,
|
|
174
|
+
* cacheResults: true,
|
|
175
|
+
* auditTrail: false,
|
|
176
|
+
* errorMessage: 'Access denied to this resource'
|
|
177
|
+
* };
|
|
178
|
+
*
|
|
179
|
+
* const handler = new Handler()
|
|
180
|
+
* .use(RouteGuards.requirePermissions(['admin:read'], options))
|
|
181
|
+
* .handle(async (context) => {
|
|
182
|
+
* return { success: true, data: 'admin data' };
|
|
183
|
+
* });
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* High-security endpoint with audit trail:
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const secureOptions: RouteGuardOptions = {
|
|
190
|
+
* requireAuth: true,
|
|
191
|
+
* cacheResults: false, // Always check fresh permissions
|
|
192
|
+
* auditTrail: true, // Enable detailed logging
|
|
193
|
+
* errorMessage: 'Unauthorized access to sensitive data',
|
|
194
|
+
* cacheTtlMs: 30000 // Short cache TTL for security
|
|
195
|
+
* };
|
|
196
|
+
*
|
|
197
|
+
* const sensitiveHandler = new Handler()
|
|
198
|
+
* .use(RouteGuards.requirePermissions(['sensitive:access'], secureOptions))
|
|
199
|
+
* .handle(async (context) => {
|
|
200
|
+
* return { success: true, data: 'sensitive information' };
|
|
201
|
+
* });
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* Public endpoint with authentication check only:
|
|
206
|
+
* ```typescript
|
|
207
|
+
* const publicOptions: RouteGuardOptions = {
|
|
208
|
+
* requireAuth: false, // Allow unauthenticated access
|
|
209
|
+
* cacheResults: true,
|
|
210
|
+
* auditTrail: false
|
|
211
|
+
* };
|
|
212
|
+
*
|
|
213
|
+
* const publicHandler = new Handler()
|
|
214
|
+
* .use(RouteGuards.requirePermissions(['public:read'], publicOptions))
|
|
215
|
+
* .handle(async (context) => {
|
|
216
|
+
* return { success: true, data: 'public data' };
|
|
217
|
+
* });
|
|
218
|
+
* ```
|
|
56
219
|
*/
|
|
57
220
|
export interface RouteGuardOptions {
|
|
58
221
|
/** Enable authentication requirement (default: true) */
|
|
@@ -67,7 +230,71 @@ export interface RouteGuardOptions {
|
|
|
67
230
|
cacheTtlMs?: number;
|
|
68
231
|
}
|
|
69
232
|
/**
|
|
70
|
-
* Guard system statistics
|
|
233
|
+
* Guard system statistics for monitoring and performance analysis.
|
|
234
|
+
* Provides comprehensive metrics about all guard system components.
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* Monitoring guard system performance:
|
|
238
|
+
* ```typescript
|
|
239
|
+
* const stats = RouteGuards.getSystemStats();
|
|
240
|
+
*
|
|
241
|
+
* console.log('System Health:', {
|
|
242
|
+
* totalChecks: stats.systemHealth.totalGuardChecks,
|
|
243
|
+
* avgResponseTime: stats.systemHealth.averageResponseTime,
|
|
244
|
+
* errorRate: stats.systemHealth.errorRate,
|
|
245
|
+
* cacheEfficiency: stats.systemHealth.cacheEfficiency,
|
|
246
|
+
* uptime: Math.round(stats.systemHealth.uptime / 1000) + 's'
|
|
247
|
+
* });
|
|
248
|
+
*
|
|
249
|
+
* console.log('Cache Performance:', {
|
|
250
|
+
* adapter: stats.cacheAdapter.name,
|
|
251
|
+
* stats: stats.cacheAdapter.stats
|
|
252
|
+
* });
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* Setting up monitoring alerts:
|
|
257
|
+
* ```typescript
|
|
258
|
+
* setInterval(async () => {
|
|
259
|
+
* const stats = RouteGuards.getSystemStats();
|
|
260
|
+
* const health = await RouteGuards.healthCheck();
|
|
261
|
+
*
|
|
262
|
+
* if (health.status === 'unhealthy') {
|
|
263
|
+
* await sendAlert('Guard system unhealthy', {
|
|
264
|
+
* status: health.status,
|
|
265
|
+
* errorRate: stats.systemHealth.errorRate,
|
|
266
|
+
* avgResponseTime: stats.systemHealth.averageResponseTime,
|
|
267
|
+
* recommendations: health.details.recommendations
|
|
268
|
+
* });
|
|
269
|
+
* }
|
|
270
|
+
*
|
|
271
|
+
* if (stats.systemHealth.cacheEfficiency < 50) {
|
|
272
|
+
* await sendAlert('Low cache efficiency detected', {
|
|
273
|
+
* efficiency: stats.systemHealth.cacheEfficiency,
|
|
274
|
+
* totalChecks: stats.systemHealth.totalGuardChecks
|
|
275
|
+
* });
|
|
276
|
+
* }
|
|
277
|
+
* }, 60000); // Check every minute
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* Performance optimization based on stats:
|
|
282
|
+
* ```typescript
|
|
283
|
+
* const stats = RouteGuards.getSystemStats();
|
|
284
|
+
*
|
|
285
|
+
* if (stats.systemHealth.averageResponseTime > 10) {
|
|
286
|
+
* console.warn('Slow guard performance detected');
|
|
287
|
+
* console.log('Consider:');
|
|
288
|
+
* console.log('- Increasing cache TTL values');
|
|
289
|
+
* console.log('- Optimizing permission source queries');
|
|
290
|
+
* console.log('- Using simpler permission patterns');
|
|
291
|
+
* }
|
|
292
|
+
*
|
|
293
|
+
* if (stats.systemHealth.errorRate > 2) {
|
|
294
|
+
* console.error('High error rate in guard system');
|
|
295
|
+
* console.log('Check authentication service health');
|
|
296
|
+
* }
|
|
297
|
+
* ```
|
|
71
298
|
*/
|
|
72
299
|
export interface GuardSystemStats {
|
|
73
300
|
authentication: Record<string, unknown>;
|
|
@@ -114,11 +341,60 @@ export declare class RouteGuards {
|
|
|
114
341
|
*
|
|
115
342
|
* @param profile - Environment profile with guard configurations
|
|
116
343
|
* @param permissionSource - User permission data source
|
|
117
|
-
* @param tokenValidator -
|
|
344
|
+
* @param tokenValidator - Token validation service (supports both TokenValidator and CustomTokenVerificationPort)
|
|
118
345
|
* @param authConfig - Authentication guard configuration
|
|
119
346
|
* @returns Promise resolving when configuration is complete
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* Using with CustomTokenVerificationPort from AuthenticationMiddleware:
|
|
350
|
+
* ```typescript
|
|
351
|
+
* import { CustomTokenVerificationPort } from '@/middlewares/authenticationMiddleware';
|
|
352
|
+
* import { RouteGuards, GuardSetup } from '@/middlewares/guards';
|
|
353
|
+
*
|
|
354
|
+
* // Same token verifier used across the framework
|
|
355
|
+
* const tokenVerifier: CustomTokenVerificationPort<User> = {
|
|
356
|
+
* async verifyToken(token: string): Promise<User> {
|
|
357
|
+
* const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;
|
|
358
|
+
* return {
|
|
359
|
+
* id: payload.sub,
|
|
360
|
+
* email: payload.email,
|
|
361
|
+
* roles: payload.roles || [],
|
|
362
|
+
* sub: payload.sub,
|
|
363
|
+
* exp: payload.exp
|
|
364
|
+
* };
|
|
365
|
+
* }
|
|
366
|
+
* };
|
|
367
|
+
*
|
|
368
|
+
* // Configure RouteGuards with the same verifier
|
|
369
|
+
* await RouteGuards.configure(
|
|
370
|
+
* GuardSetup.production(),
|
|
371
|
+
* userPermissionSource,
|
|
372
|
+
* tokenVerifier, // Automatically wrapped with adapter
|
|
373
|
+
* authConfig
|
|
374
|
+
* );
|
|
375
|
+
* ```
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* Traditional usage with TokenValidator (backward compatible):
|
|
379
|
+
* ```typescript
|
|
380
|
+
* const tokenValidator: TokenValidator = {
|
|
381
|
+
* async validateToken(token: string) {
|
|
382
|
+
* // Your existing validation logic
|
|
383
|
+
* return { valid: true, decoded: userPayload };
|
|
384
|
+
* },
|
|
385
|
+
* extractUserId: (decoded) => decoded.sub,
|
|
386
|
+
* isTokenExpired: (decoded) => decoded.exp < Date.now() / 1000
|
|
387
|
+
* };
|
|
388
|
+
*
|
|
389
|
+
* await RouteGuards.configure(
|
|
390
|
+
* GuardSetup.production(),
|
|
391
|
+
* userPermissionSource,
|
|
392
|
+
* tokenValidator, // Works as before
|
|
393
|
+
* authConfig
|
|
394
|
+
* );
|
|
395
|
+
* ```
|
|
120
396
|
*/
|
|
121
|
-
static configure(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, tokenValidator:
|
|
397
|
+
static configure(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, tokenValidator: AnyTokenValidator, authConfig: AuthGuardConfig): Promise<void>;
|
|
122
398
|
/**
|
|
123
399
|
* Get the configured RouteGuards instance
|
|
124
400
|
*
|
|
@@ -241,6 +517,185 @@ export declare class RouteGuards {
|
|
|
241
517
|
details: Record<string, unknown>;
|
|
242
518
|
timestamp: string;
|
|
243
519
|
}>;
|
|
520
|
+
/**
|
|
521
|
+
* Factory method: Configure RouteGuards with CustomTokenVerificationPort for JWT tokens.
|
|
522
|
+
* Provides a streamlined setup for JWT-based authentication with common field extraction.
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* Quick JWT setup with CustomTokenVerificationPort:
|
|
526
|
+
* ```typescript
|
|
527
|
+
* import { CustomTokenVerificationPort } from '@/middlewares/authenticationMiddleware';
|
|
528
|
+
*
|
|
529
|
+
* interface JWTUser {
|
|
530
|
+
* sub: string;
|
|
531
|
+
* email: string;
|
|
532
|
+
* roles: string[];
|
|
533
|
+
* exp: number;
|
|
534
|
+
* }
|
|
535
|
+
*
|
|
536
|
+
* const jwtVerifier: CustomTokenVerificationPort<JWTUser> = {
|
|
537
|
+
* async verifyToken(token: string): Promise<JWTUser> {
|
|
538
|
+
* const payload = jwt.verify(token, process.env.JWT_SECRET!) as any;
|
|
539
|
+
* return {
|
|
540
|
+
* sub: payload.sub,
|
|
541
|
+
* email: payload.email,
|
|
542
|
+
* roles: payload.roles || [],
|
|
543
|
+
* exp: payload.exp
|
|
544
|
+
* };
|
|
545
|
+
* }
|
|
546
|
+
* };
|
|
547
|
+
*
|
|
548
|
+
* // One-line setup for JWT authentication
|
|
549
|
+
* await RouteGuards.configureWithJWT(
|
|
550
|
+
* GuardSetup.production(),
|
|
551
|
+
* userPermissionSource,
|
|
552
|
+
* jwtVerifier,
|
|
553
|
+
* {
|
|
554
|
+
* tokenHeader: 'authorization',
|
|
555
|
+
* tokenPrefix: 'Bearer ',
|
|
556
|
+
* requireEmailVerification: true
|
|
557
|
+
* }
|
|
558
|
+
* );
|
|
559
|
+
* ```
|
|
560
|
+
*/
|
|
561
|
+
static configureWithJWT<T extends {
|
|
562
|
+
sub: string;
|
|
563
|
+
exp?: number;
|
|
564
|
+
}>(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, jwtVerifier: CustomTokenVerificationPort<T>, authConfig: AuthGuardConfig): Promise<void>;
|
|
565
|
+
/**
|
|
566
|
+
* Factory method: Configure RouteGuards with CustomTokenVerificationPort for API keys.
|
|
567
|
+
* Provides setup for API key-based authentication with flexible field mapping.
|
|
568
|
+
*
|
|
569
|
+
* @example
|
|
570
|
+
* API key authentication setup:
|
|
571
|
+
* ```typescript
|
|
572
|
+
* interface APIKeyUser {
|
|
573
|
+
* keyId: string;
|
|
574
|
+
* permissions: string[];
|
|
575
|
+
* organization: string;
|
|
576
|
+
* expiresAt?: number;
|
|
577
|
+
* isActive: boolean;
|
|
578
|
+
* }
|
|
579
|
+
*
|
|
580
|
+
* const apiKeyVerifier: CustomTokenVerificationPort<APIKeyUser> = {
|
|
581
|
+
* async verifyToken(token: string): Promise<APIKeyUser> {
|
|
582
|
+
* const keyData = await validateAPIKeyInDatabase(token);
|
|
583
|
+
* if (!keyData || !keyData.isActive) {
|
|
584
|
+
* throw new Error('Invalid or inactive API key');
|
|
585
|
+
* }
|
|
586
|
+
* return keyData;
|
|
587
|
+
* }
|
|
588
|
+
* };
|
|
589
|
+
*
|
|
590
|
+
* await RouteGuards.configureWithAPIKey(
|
|
591
|
+
* GuardSetup.production(),
|
|
592
|
+
* userPermissionSource,
|
|
593
|
+
* apiKeyVerifier,
|
|
594
|
+
* {
|
|
595
|
+
* tokenHeader: 'x-api-key',
|
|
596
|
+
* tokenPrefix: '',
|
|
597
|
+
* allowInactiveUsers: false
|
|
598
|
+
* },
|
|
599
|
+
* 'keyId',
|
|
600
|
+
* 'expiresAt'
|
|
601
|
+
* );
|
|
602
|
+
* ```
|
|
603
|
+
*/
|
|
604
|
+
static configureWithAPIKey<T extends Record<string, unknown>>(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, apiKeyVerifier: CustomTokenVerificationPort<T>, authConfig: AuthGuardConfig, userIdField: keyof T, expirationField?: keyof T): Promise<void>;
|
|
605
|
+
/**
|
|
606
|
+
* Factory method: Configure RouteGuards with CustomTokenVerificationPort for OAuth tokens.
|
|
607
|
+
* Provides setup for OAuth-based authentication with scope validation.
|
|
608
|
+
*
|
|
609
|
+
* @example
|
|
610
|
+
* OAuth token authentication with scope requirements:
|
|
611
|
+
* ```typescript
|
|
612
|
+
* interface OAuthUser {
|
|
613
|
+
* sub: string;
|
|
614
|
+
* email: string;
|
|
615
|
+
* scope: string[];
|
|
616
|
+
* exp: number;
|
|
617
|
+
* client_id: string;
|
|
618
|
+
* }
|
|
619
|
+
*
|
|
620
|
+
* const oauthVerifier: CustomTokenVerificationPort<OAuthUser> = {
|
|
621
|
+
* async verifyToken(token: string): Promise<OAuthUser> {
|
|
622
|
+
* const response = await fetch(`${OAUTH_INTROSPECT_URL}`, {
|
|
623
|
+
* method: 'POST',
|
|
624
|
+
* headers: { 'Authorization': `Bearer ${token}` },
|
|
625
|
+
* body: new URLSearchParams({ token })
|
|
626
|
+
* });
|
|
627
|
+
*
|
|
628
|
+
* const tokenInfo = await response.json();
|
|
629
|
+
* if (!tokenInfo.active) {
|
|
630
|
+
* throw new Error('Token is not active');
|
|
631
|
+
* }
|
|
632
|
+
*
|
|
633
|
+
* return tokenInfo as OAuthUser;
|
|
634
|
+
* }
|
|
635
|
+
* };
|
|
636
|
+
*
|
|
637
|
+
* await RouteGuards.configureWithOAuth(
|
|
638
|
+
* GuardSetup.production(),
|
|
639
|
+
* userPermissionSource,
|
|
640
|
+
* oauthVerifier,
|
|
641
|
+
* {
|
|
642
|
+
* tokenHeader: 'authorization',
|
|
643
|
+
* tokenPrefix: 'Bearer ',
|
|
644
|
+
* requireEmailVerification: false
|
|
645
|
+
* },
|
|
646
|
+
* ['read:profile', 'write:data'] // Required OAuth scopes
|
|
647
|
+
* );
|
|
648
|
+
* ```
|
|
649
|
+
*/
|
|
650
|
+
static configureWithOAuth<T extends {
|
|
651
|
+
sub: string;
|
|
652
|
+
exp?: number;
|
|
653
|
+
scope?: string[];
|
|
654
|
+
}>(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, oauthVerifier: CustomTokenVerificationPort<T>, authConfig: AuthGuardConfig, requiredScopes?: string[]): Promise<void>;
|
|
655
|
+
/**
|
|
656
|
+
* Factory method: Configure RouteGuards with a custom CustomTokenVerificationPort adapter.
|
|
657
|
+
* Provides maximum flexibility for custom token validation scenarios.
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* Custom token validation with business-specific logic:
|
|
661
|
+
* ```typescript
|
|
662
|
+
* interface CustomUser {
|
|
663
|
+
* userId: string;
|
|
664
|
+
* tenantId: string;
|
|
665
|
+
* roles: string[];
|
|
666
|
+
* sessionExpiry: number;
|
|
667
|
+
* isVerified: boolean;
|
|
668
|
+
* }
|
|
669
|
+
*
|
|
670
|
+
* const customVerifier: CustomTokenVerificationPort<CustomUser> = {
|
|
671
|
+
* async verifyToken(token: string): Promise<CustomUser> {
|
|
672
|
+
* // Your custom verification logic
|
|
673
|
+
* return await verifyCustomToken(token);
|
|
674
|
+
* }
|
|
675
|
+
* };
|
|
676
|
+
*
|
|
677
|
+
* await RouteGuards.configureWithCustom(
|
|
678
|
+
* GuardSetup.production(),
|
|
679
|
+
* userPermissionSource,
|
|
680
|
+
* customVerifier,
|
|
681
|
+
* {
|
|
682
|
+
* tokenHeader: 'x-auth-token',
|
|
683
|
+
* tokenPrefix: 'Custom ',
|
|
684
|
+
* customValidation: async (token, user) => {
|
|
685
|
+
* return user.isVerified && user.tenantId === 'valid-tenant';
|
|
686
|
+
* }
|
|
687
|
+
* },
|
|
688
|
+
* {
|
|
689
|
+
* userIdExtractor: (user) => user.userId,
|
|
690
|
+
* expirationExtractor: (user) => user.sessionExpiry,
|
|
691
|
+
* additionalValidation: (user) => user.isVerified
|
|
692
|
+
* }
|
|
693
|
+
* );
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
static configureWithCustom<T>(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, customVerifier: CustomTokenVerificationPort<T>, authConfig: AuthGuardConfig, adapterConfig: Omit<Parameters<typeof TokenVerificationAdapterFactory.custom<T>>[1], 'userIdExtractor'> & {
|
|
697
|
+
userIdExtractor: (user: T) => string;
|
|
698
|
+
}): Promise<void>;
|
|
244
699
|
private createPlainPermissionGuard;
|
|
245
700
|
private createWildcardPermissionGuard;
|
|
246
701
|
private createExpressionPermissionGuard;
|