UNPKG

@noony-serverless/core

Version:

A Middy base framework compatible with Firebase and GCP Cloud Functions with TypeScript

710 lines 25.8 kB
/** * Route Guards Facade * * Main entry point for the guard system providing a clean, NestJS-inspired API * for protecting routes with authentication and authorization. This facade * orchestrates all guard components to provide three distinct protection methods * optimized for different use cases. * * Three Protection Methods: * 1. `requirePermissions()` - Simple permission list checks (fastest) * 2. `requireWildcardPermissions()` - Hierarchical wildcard patterns * 3. `requireComplexPermissions()` - Boolean expression evaluation * * Key Features: * - Automatic resolver selection for optimal performance * - Intelligent caching strategies per protection method * - Conservative security approach with automatic cache invalidation * - Built-in authentication with cached user context loading * - Comprehensive monitoring and audit trails * - Framework-agnostic middleware integration * * @example * Complete guard system setup: * ```typescript * import { RouteGuards, GuardSetup } from '@noony-serverless/core'; * * // Define user permission source * const userPermissionSource = { * async getUserPermissions(userId: string): Promise<string[]> { * const user = await getUserFromDatabase(userId); * return user.permissions; * } * }; * * // Define token validator * const tokenValidator = { * async validateToken(token: string) { * try { * const decoded = jwt.verify(token, process.env.JWT_SECRET); * return { valid: true, decoded }; * } catch (error) { * return { valid: false, error: error.message }; * } * }, * extractUserId: (decoded: any) => decoded.sub, * isTokenExpired: (decoded: any) => decoded.exp < Date.now() / 1000 * }; * * // Configure guard system * await RouteGuards.configure( * GuardSetup.production(), * userPermissionSource, * tokenValidator, * { * tokenHeader: 'authorization', * tokenPrefix: 'Bearer ', * requireEmailVerification: true, * allowInactiveUsers: false * } * ); * ``` * * @example * Simple permission checks (fastest - ~0.1ms cached): * ```typescript * import { Handler, RouteGuards } from '@noony-serverless/core'; * * const userManagementHandler = new Handler() * .use(RouteGuards.requirePermissions(['user:read', 'user:update'])) * .handle(async (context) => { * // User has either 'user:read' OR 'user:update' permission * const users = await getUsers(); * return { success: true, users }; * }); * ``` * * @example * Wildcard permission patterns (hierarchical - ~0.2ms cached): * ```typescript * const adminHandler = new Handler() * .use(RouteGuards.requireWildcardPermissions(['admin.*', 'org.reports.*'])) * .handle(async (context) => { * // User has any permission starting with 'admin.' OR 'org.reports.' * const adminData = await getAdminDashboard(); * return { success: true, data: adminData }; * }); * ``` * * @example * Complex boolean expressions (~0.5ms cached): * ```typescript * const complexAccessHandler = new Handler() * .use(RouteGuards.requireComplexPermissions({ * or: [ * { permission: 'admin.users' }, * { and: [ * { permission: 'moderator.content' }, * { permission: 'org.reports.view' } * ]} * ] * })) * .handle(async (context) => { * // User has 'admin.users' OR ('moderator.content' AND 'org.reports.view') * return { success: true, accessGranted: true }; * }); * ``` * * @example * Authentication-only (no permissions): * ```typescript * const profileHandler = new Handler() * .use(RouteGuards.requireAuth()) * .handle(async (context) => { * // Only checks if user is authenticated * const profile = await getUserProfile(context.user.id); * return { success: true, profile }; * }); * ``` * * @example * Cache invalidation for security: * ```typescript * // Invalidate specific user when permissions change * await RouteGuards.invalidateUserPermissions('user-123', 'Permission update'); * * // System-wide invalidation for major updates * await RouteGuards.invalidateAllPermissions('System update deployed'); * * // Emergency invalidation for security incidents * await RouteGuards.emergencyInvalidation('Security breach detected'); * ``` * * @example * Monitoring and health checks: * ```typescript * // Get comprehensive system statistics * const stats = RouteGuards.getSystemStats(); * console.log('Guard system performance:', stats.systemHealth); * * // Perform health check * const health = await RouteGuards.healthCheck(); * console.log('System status:', health.status); * console.log('Recommendations:', health.details.recommendations); * ``` * * @author Noony Framework Team * @version 1.0.0 */ import { BaseMiddleware } from '../../core/handler'; import { GuardConfiguration, GuardEnvironmentProfile } from './config/GuardConfiguration'; import { CacheAdapter } from './cache/CacheAdapter'; import { FastUserContextService, UserPermissionSource } from './services/FastUserContextService'; import { ConservativeCacheInvalidation } from './cache/ConservativeCacheInvalidation'; import { FastAuthGuard, AuthGuardConfig, TokenValidator } from './guards/FastAuthGuard'; import { PermissionGuardFactory } from './guards/PermissionGuardFactory'; import { PermissionRegistry } from './registry/PermissionRegistry'; import { PermissionExpression } from './resolvers/PermissionResolver'; import { CustomTokenVerificationPort } from '../authenticationMiddleware'; import { TokenVerificationAdapterFactory } from './adapters/CustomTokenVerificationPortAdapter'; /** * Union type supporting both RouteGuards TokenValidator and AuthenticationMiddleware CustomTokenVerificationPort. * This enables seamless integration between the two authentication systems. */ export type AnyTokenValidator = TokenValidator | CustomTokenVerificationPort<unknown>; /** * Route guard configuration for the facade. * Provides fine-grained control over guard behavior for specific endpoints. * * @example * Basic guard options: * ```typescript * const options: RouteGuardOptions = { * requireAuth: true, * cacheResults: true, * auditTrail: false, * errorMessage: 'Access denied to this resource' * }; * * const handler = new Handler() * .use(RouteGuards.requirePermissions(['admin:read'], options)) * .handle(async (context) => { * return { success: true, data: 'admin data' }; * }); * ``` * * @example * High-security endpoint with audit trail: * ```typescript * const secureOptions: RouteGuardOptions = { * requireAuth: true, * cacheResults: false, // Always check fresh permissions * auditTrail: true, // Enable detailed logging * errorMessage: 'Unauthorized access to sensitive data', * cacheTtlMs: 30000 // Short cache TTL for security * }; * * const sensitiveHandler = new Handler() * .use(RouteGuards.requirePermissions(['sensitive:access'], secureOptions)) * .handle(async (context) => { * return { success: true, data: 'sensitive information' }; * }); * ``` * * @example * Public endpoint with authentication check only: * ```typescript * const publicOptions: RouteGuardOptions = { * requireAuth: false, // Allow unauthenticated access * cacheResults: true, * auditTrail: false * }; * * const publicHandler = new Handler() * .use(RouteGuards.requirePermissions(['public:read'], publicOptions)) * .handle(async (context) => { * return { success: true, data: 'public data' }; * }); * ``` */ export interface RouteGuardOptions { /** Enable authentication requirement (default: true) */ requireAuth?: boolean; /** Enable permission result caching (default: true) */ cacheResults?: boolean; /** Enable detailed audit logging (default: false) */ auditTrail?: boolean; /** Custom error message for access denials */ errorMessage?: string; /** Cache TTL in milliseconds (overrides global config) */ cacheTtlMs?: number; } /** * Guard system statistics for monitoring and performance analysis. * Provides comprehensive metrics about all guard system components. * * @example * Monitoring guard system performance: * ```typescript * const stats = RouteGuards.getSystemStats(); * * console.log('System Health:', { * totalChecks: stats.systemHealth.totalGuardChecks, * avgResponseTime: stats.systemHealth.averageResponseTime, * errorRate: stats.systemHealth.errorRate, * cacheEfficiency: stats.systemHealth.cacheEfficiency, * uptime: Math.round(stats.systemHealth.uptime / 1000) + 's' * }); * * console.log('Cache Performance:', { * adapter: stats.cacheAdapter.name, * stats: stats.cacheAdapter.stats * }); * ``` * * @example * Setting up monitoring alerts: * ```typescript * setInterval(async () => { * const stats = RouteGuards.getSystemStats(); * const health = await RouteGuards.healthCheck(); * * if (health.status === 'unhealthy') { * await sendAlert('Guard system unhealthy', { * status: health.status, * errorRate: stats.systemHealth.errorRate, * avgResponseTime: stats.systemHealth.averageResponseTime, * recommendations: health.details.recommendations * }); * } * * if (stats.systemHealth.cacheEfficiency < 50) { * await sendAlert('Low cache efficiency detected', { * efficiency: stats.systemHealth.cacheEfficiency, * totalChecks: stats.systemHealth.totalGuardChecks * }); * } * }, 60000); // Check every minute * ``` * * @example * Performance optimization based on stats: * ```typescript * const stats = RouteGuards.getSystemStats(); * * if (stats.systemHealth.averageResponseTime > 10) { * console.warn('Slow guard performance detected'); * console.log('Consider:'); * console.log('- Increasing cache TTL values'); * console.log('- Optimizing permission source queries'); * console.log('- Using simpler permission patterns'); * } * * if (stats.systemHealth.errorRate > 2) { * console.error('High error rate in guard system'); * console.log('Check authentication service health'); * } * ``` */ export interface GuardSystemStats { authentication: Record<string, unknown>; userContextService: Record<string, unknown>; permissionGuardFactory: Record<string, unknown>; cacheInvalidation: Record<string, unknown>; cacheAdapter: Record<string, unknown>; systemHealth: { totalGuardChecks: number; averageResponseTime: number; errorRate: number; cacheEfficiency: number; uptime: number; }; } /** * Route Guards Facade Implementation * * This class provides the main API for the guard system and handles * the orchestration of all guard components. It follows the facade pattern * to simplify the complex underlying guard architecture. */ export declare class RouteGuards { private static instance; private static isConfigured; private readonly _config; private readonly cache; private readonly userContextService; private readonly cacheInvalidation; private readonly authGuard; private readonly guardFactory; private readonly _permissionRegistry; private systemStartTime; private totalGuardChecks; private totalErrors; private totalResponseTime; constructor(config: GuardConfiguration, cache: CacheAdapter, userContextService: FastUserContextService, cacheInvalidation: ConservativeCacheInvalidation, authGuard: FastAuthGuard, guardFactory: PermissionGuardFactory, permissionRegistry: PermissionRegistry); /** * Configure the guard system with environment-specific settings * * This method must be called once before using any guard methods. * It sets up all guard components with optimal configurations for * the target environment (development, production, serverless). * * @param profile - Environment profile with guard configurations * @param permissionSource - User permission data source * @param tokenValidator - Token validation service (supports both TokenValidator and CustomTokenVerificationPort) * @param authConfig - Authentication guard configuration * @returns Promise resolving when configuration is complete * * @example * Using with CustomTokenVerificationPort from AuthenticationMiddleware: * ```typescript * import { CustomTokenVerificationPort } from '@/middlewares/authenticationMiddleware'; * import { RouteGuards, GuardSetup } from '@/middlewares/guards'; * * // Same token verifier used across the framework * const tokenVerifier: CustomTokenVerificationPort<User> = { * async verifyToken(token: string): Promise<User> { * const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload; * return { * id: payload.sub, * email: payload.email, * roles: payload.roles || [], * sub: payload.sub, * exp: payload.exp * }; * } * }; * * // Configure RouteGuards with the same verifier * await RouteGuards.configure( * GuardSetup.production(), * userPermissionSource, * tokenVerifier, // Automatically wrapped with adapter * authConfig * ); * ``` * * @example * Traditional usage with TokenValidator (backward compatible): * ```typescript * const tokenValidator: TokenValidator = { * async validateToken(token: string) { * // Your existing validation logic * return { valid: true, decoded: userPayload }; * }, * extractUserId: (decoded) => decoded.sub, * isTokenExpired: (decoded) => decoded.exp < Date.now() / 1000 * }; * * await RouteGuards.configure( * GuardSetup.production(), * userPermissionSource, * tokenValidator, // Works as before * authConfig * ); * ``` */ static configure(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, tokenValidator: AnyTokenValidator, authConfig: AuthGuardConfig): Promise<void>; /** * Get the configured RouteGuards instance * * @returns Configured RouteGuards instance * @throws Error if not configured */ static getInstance(): RouteGuards; /** * Create middleware for simple permission list checks * * This is the fastest protection method using direct O(1) set membership * checks. Ideal for high-traffic endpoints with straightforward permission * requirements. * * Performance: ~0.1ms cached, ~1-2ms uncached * * @param permissions - Array of required permissions (OR logic) * @param options - Optional guard configuration * @returns Middleware instance for permission checking */ static requirePermissions(permissions: string[], options?: RouteGuardOptions): BaseMiddleware; /** * Create middleware for wildcard permission pattern checks * * Supports hierarchical permission patterns with wildcards for flexible * permission management. Uses configurable pre-expansion or on-demand * matching strategies. * * Performance: ~0.2ms cached (pre-expansion), ~2-5ms cached (on-demand) * * @param wildcardPatterns - Array of wildcard patterns * @param options - Optional guard configuration * @returns Middleware instance for wildcard permission checking */ static requireWildcardPermissions(wildcardPatterns: string[], options?: RouteGuardOptions): BaseMiddleware; /** * Create middleware for complex boolean expression checks * * Supports advanced permission logic with AND, OR, and NOT operations. * Includes expression caching and complexity tracking for performance * optimization. * * Performance: ~0.5ms cached, ~5-15ms uncached (depends on complexity) * * @param expression - Permission expression with boolean logic * @param options - Optional guard configuration * @returns Middleware instance for expression permission checking */ static requireComplexPermissions(expression: PermissionExpression, options?: RouteGuardOptions): BaseMiddleware; /** * Create middleware with automatic resolver selection * * Analyzes permission requirements and automatically selects the optimal * resolution strategy for best performance. Useful when you want the * system to choose the best approach. * * @param permissions - Any type of permission requirement * @param options - Optional guard configuration * @returns Optimally configured middleware instance */ static requireAny(permissions: string[] | PermissionExpression, options?: RouteGuardOptions): BaseMiddleware; /** * Get authentication-only middleware * * Provides user authentication without permission checking. * Useful for endpoints that only need to verify user identity. * * @param options - Optional guard configuration * @returns Authentication-only middleware */ static requireAuth(_options?: RouteGuardOptions): BaseMiddleware; /** * Invalidate user permissions cache * * Use when user permissions change to ensure fresh permission checks. * Implements conservative invalidation strategy for security. * * @param userId - User ID to invalidate * @param reason - Reason for invalidation (for audit) * @returns Promise resolving when invalidation is complete */ static invalidateUserPermissions(userId: string, reason: string): Promise<void>; /** * System-wide cache invalidation * * Nuclear option for clearing all permission-related caches. * Use for major system updates or security incidents. * * @param reason - Reason for system-wide invalidation * @returns Promise resolving when invalidation is complete */ static invalidateAllPermissions(reason: string): Promise<void>; /** * Emergency security invalidation * * Immediate cache clearing for security incidents. * Bypasses backup creation for maximum speed. * * @param reason - Security incident description * @returns Promise resolving when emergency invalidation is complete */ static emergencyInvalidation(reason: string): Promise<void>; /** * Get comprehensive system statistics * * @returns Complete guard system performance and health metrics */ static getSystemStats(): GuardSystemStats; /** * Reset all system statistics */ static resetSystemStats(): void; /** * Health check for the guard system * * @returns Health status with key metrics */ static healthCheck(): Promise<{ status: 'healthy' | 'degraded' | 'unhealthy'; details: Record<string, unknown>; timestamp: string; }>; /** * Factory method: Configure RouteGuards with CustomTokenVerificationPort for JWT tokens. * Provides a streamlined setup for JWT-based authentication with common field extraction. * * @example * Quick JWT setup with CustomTokenVerificationPort: * ```typescript * import { CustomTokenVerificationPort } from '@/middlewares/authenticationMiddleware'; * * interface JWTUser { * sub: string; * email: string; * roles: string[]; * exp: number; * } * * const jwtVerifier: CustomTokenVerificationPort<JWTUser> = { * async verifyToken(token: string): Promise<JWTUser> { * const payload = jwt.verify(token, process.env.JWT_SECRET!) as any; * return { * sub: payload.sub, * email: payload.email, * roles: payload.roles || [], * exp: payload.exp * }; * } * }; * * // One-line setup for JWT authentication * await RouteGuards.configureWithJWT( * GuardSetup.production(), * userPermissionSource, * jwtVerifier, * { * tokenHeader: 'authorization', * tokenPrefix: 'Bearer ', * requireEmailVerification: true * } * ); * ``` */ static configureWithJWT<T extends { sub: string; exp?: number; }>(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, jwtVerifier: CustomTokenVerificationPort<T>, authConfig: AuthGuardConfig): Promise<void>; /** * Factory method: Configure RouteGuards with CustomTokenVerificationPort for API keys. * Provides setup for API key-based authentication with flexible field mapping. * * @example * API key authentication setup: * ```typescript * interface APIKeyUser { * keyId: string; * permissions: string[]; * organization: string; * expiresAt?: number; * isActive: boolean; * } * * const apiKeyVerifier: CustomTokenVerificationPort<APIKeyUser> = { * async verifyToken(token: string): Promise<APIKeyUser> { * const keyData = await validateAPIKeyInDatabase(token); * if (!keyData || !keyData.isActive) { * throw new Error('Invalid or inactive API key'); * } * return keyData; * } * }; * * await RouteGuards.configureWithAPIKey( * GuardSetup.production(), * userPermissionSource, * apiKeyVerifier, * { * tokenHeader: 'x-api-key', * tokenPrefix: '', * allowInactiveUsers: false * }, * 'keyId', * 'expiresAt' * ); * ``` */ static configureWithAPIKey<T extends Record<string, unknown>>(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, apiKeyVerifier: CustomTokenVerificationPort<T>, authConfig: AuthGuardConfig, userIdField: keyof T, expirationField?: keyof T): Promise<void>; /** * Factory method: Configure RouteGuards with CustomTokenVerificationPort for OAuth tokens. * Provides setup for OAuth-based authentication with scope validation. * * @example * OAuth token authentication with scope requirements: * ```typescript * interface OAuthUser { * sub: string; * email: string; * scope: string[]; * exp: number; * client_id: string; * } * * const oauthVerifier: CustomTokenVerificationPort<OAuthUser> = { * async verifyToken(token: string): Promise<OAuthUser> { * const response = await fetch(`${OAUTH_INTROSPECT_URL}`, { * method: 'POST', * headers: { 'Authorization': `Bearer ${token}` }, * body: new URLSearchParams({ token }) * }); * * const tokenInfo = await response.json(); * if (!tokenInfo.active) { * throw new Error('Token is not active'); * } * * return tokenInfo as OAuthUser; * } * }; * * await RouteGuards.configureWithOAuth( * GuardSetup.production(), * userPermissionSource, * oauthVerifier, * { * tokenHeader: 'authorization', * tokenPrefix: 'Bearer ', * requireEmailVerification: false * }, * ['read:profile', 'write:data'] // Required OAuth scopes * ); * ``` */ static configureWithOAuth<T extends { sub: string; exp?: number; scope?: string[]; }>(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, oauthVerifier: CustomTokenVerificationPort<T>, authConfig: AuthGuardConfig, requiredScopes?: string[]): Promise<void>; /** * Factory method: Configure RouteGuards with a custom CustomTokenVerificationPort adapter. * Provides maximum flexibility for custom token validation scenarios. * * @example * Custom token validation with business-specific logic: * ```typescript * interface CustomUser { * userId: string; * tenantId: string; * roles: string[]; * sessionExpiry: number; * isVerified: boolean; * } * * const customVerifier: CustomTokenVerificationPort<CustomUser> = { * async verifyToken(token: string): Promise<CustomUser> { * // Your custom verification logic * return await verifyCustomToken(token); * } * }; * * await RouteGuards.configureWithCustom( * GuardSetup.production(), * userPermissionSource, * customVerifier, * { * tokenHeader: 'x-auth-token', * tokenPrefix: 'Custom ', * customValidation: async (token, user) => { * return user.isVerified && user.tenantId === 'valid-tenant'; * } * }, * { * userIdExtractor: (user) => user.userId, * expirationExtractor: (user) => user.sessionExpiry, * additionalValidation: (user) => user.isVerified * } * ); * ``` */ static configureWithCustom<T>(profile: GuardEnvironmentProfile, permissionSource: UserPermissionSource, customVerifier: CustomTokenVerificationPort<T>, authConfig: AuthGuardConfig, adapterConfig: Omit<Parameters<typeof TokenVerificationAdapterFactory.custom<T>>[1], 'userIdExtractor'> & { userIdExtractor: (user: T) => string; }): Promise<void>; private createPlainPermissionGuard; private createWildcardPermissionGuard; private createExpressionPermissionGuard; private createAutoPermissionGuard; private wrapGuardWithStats; private trackGuardCreation; private getSystemStats; private resetSystemStats; private performHealthCheck; private getHealthRecommendations; } //# sourceMappingURL=RouteGuards.d.ts.map