/** * Route Guards - Phase 8 Enterprise Pattern * * Validates route access, enforces permissions, and handles * authentication/authorization before allowing navigation. */ import store from '../stores/app-store.js'; import auditLogger from './audit-logger.js'; class RouteGuard { constructor() { this.guards = new Map(); this.setupDefaultGuards(); } /** * Register a guard for a specific route */ register(route, guard) { this.guards.set(route, guard); } /** * Check if user can access route */ canActivate(route) { const state = store.get(); // Not authenticated if (!state.user) { auditLogger.logPermissionCheck('route_access', false, 'anonymous', `Unauthenticated access to ${route}`); return { allowed: false, reason: 'User must be logged in', redirect: '/#/login' }; } // Check route-specific guard const guard = this.guards.get(route); if (guard) { const result = guard(state); if (!result.allowed) { auditLogger.logPermissionCheck('route_access', false, state.user.id, `Access denied to ${route}: ${result.reason}`); } return result; } auditLogger.logPermissionCheck('route_access', true, state.user.id, `Access granted to ${route}`); return { allowed: true }; } /** * Check user permission */ hasPermission(permission) { const state = store.get(); if (!state.user) { auditLogger.logPermissionCheck('permission_check', false, 'anonymous', `Checking ${permission}`); return false; } const allowed = store.hasPermission(permission); auditLogger.logPermissionCheck('permission_check', allowed, state.user.id, `Checking ${permission}`); return allowed; } /** * Require permission before action */ requirePermission(permission) { if (!this.hasPermission(permission)) { throw new Error(`Permission denied: ${permission}`); } return true; } /** * Setup default route guards */ setupDefaultGuards() { // Settings page - requires TEAM_LEAD or SUPER_ADMIN this.register('settings', (state) => { const allowed = ['TEAM_LEAD', 'SUPER_ADMIN'].includes(state.role); return { allowed, reason: allowed ? '' : 'Settings access requires team lead or admin role' }; }); // Admin page - requires SUPER_ADMIN this.register('admin', (state) => { const allowed = state.role === 'SUPER_ADMIN'; return { allowed, reason: allowed ? '' : 'Admin access requires super admin role' }; }); // Projects page - requires active team this.register('projects', (state) => { const allowed = !!state.team; return { allowed, reason: allowed ? '' : 'Must select a team first' }; }); // Figma integration - requires DEVELOPER or above this.register('figma', (state) => { const allowed = ['DEVELOPER', 'TEAM_LEAD', 'SUPER_ADMIN'].includes(state.role); return { allowed, reason: allowed ? '' : 'Figma integration requires developer or higher role' }; }); } /** * Validate action before execution */ validateAction(action, resource) { const state = store.get(); if (!state.user) { throw new Error('User must be authenticated'); } const requiredPermission = this.getRequiredPermission(action, resource); if (!this.hasPermission(requiredPermission)) { throw new Error(`Permission denied: ${action} on ${resource}`); } auditLogger.logAction(`${action}_${resource}`, { user: state.user.id, role: state.role }); return true; } /** * Map action to required permission */ getRequiredPermission(action, resource) { const permissions = { 'create_project': 'write', 'delete_project': 'write', 'sync_figma': 'write', 'export_tokens': 'read', 'modify_settings': 'write', 'manage_team': 'manage_team', 'view_audit': 'read', }; return permissions[`${action}_${resource}`] || 'read'; } /** * Get all available routes */ getRoutes() { return Array.from(this.guards.keys()); } /** * Check if route is protected */ isProtected(route) { return this.guards.has(route); } } // Create and export singleton const routeGuard = new RouteGuard(); export { RouteGuard }; export default routeGuard;