UNPKG

codecrucible-synth

Version:

Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability

368 lines 13.7 kB
/** * Role-Based Access Control (RBAC) Policy Engine * Implements enterprise-grade authorization with policies and conditions */ import { logger } from '../logger.js'; export class RBACPolicyEngine { policies = []; roles = new Map(); permissions = new Map(); constructor() { this.initializeDefaultPolicies(); } /** * Initialize default security policies */ initializeDefaultPolicies() { // Default permissions const permissions = [ { id: 'read:own', name: 'Read Own Resources', resource: 'user:*', action: 'read' }, { id: 'read:public', name: 'Read Public Resources', resource: 'public:*', action: 'read' }, { id: 'write:own', name: 'Write Own Resources', resource: 'user:*', action: 'write' }, { id: 'admin:all', name: 'Full Admin Access', resource: '*', action: '*' }, { id: 'analyze:code', name: 'Analyze Code', resource: 'analysis:*', action: 'read' }, { id: 'generate:code', name: 'Generate Code', resource: 'generation:*', action: 'write' }, { id: 'manage:voices', name: 'Manage Voice Systems', resource: 'voices:*', action: '*' }, { id: 'view:metrics', name: 'View Metrics', resource: 'metrics:*', action: 'read' }, { id: 'manage:config', name: 'Manage Configuration', resource: 'config:*', action: '*' }, ]; permissions.forEach(perm => this.permissions.set(perm.id, perm)); // Default roles const roles = [ { id: 'guest', name: 'Guest User', description: 'Limited read-only access', permissions: permissions[1] ? [permissions[1]] : [], // read:public only }, { id: 'user', name: 'Regular User', description: 'Standard user with basic AI features', permissions: [permissions[0], permissions[1], permissions[4], permissions[5]].filter(Boolean), // read own/public, analyze, generate }, { id: 'developer', name: 'Developer', description: 'Developer with enhanced code generation capabilities', permissions: [ permissions[0], permissions[1], permissions[2], permissions[4], permissions[5], permissions[6], ].filter(Boolean), }, { id: 'admin', name: 'Administrator', description: 'Full system access', permissions: permissions[3] ? [permissions[3]] : [], // admin:all covers everything }, { id: 'analyst', name: 'Data Analyst', description: 'Read access with metrics viewing', permissions: [permissions[0], permissions[1], permissions[4], permissions[7]], }, ]; roles.forEach(role => this.roles.set(role.id, role)); // Default policies this.policies = [ { effect: 'allow', principal: ['user', 'developer', 'admin'], action: ['read'], resource: ['user:*'], condition: { 'user.owns_resource': true }, }, { effect: 'allow', principal: ['admin'], action: ['*'], resource: ['*'], }, { effect: 'deny', principal: ['*'], action: ['delete'], resource: ['system:*'], }, { effect: 'allow', principal: ['developer', 'admin'], action: ['write'], resource: ['generation:*', 'analysis:*'], }, { effect: 'deny', principal: ['guest'], action: ['write'], resource: ['*'], }, { effect: 'allow', principal: ['analyst', 'admin'], action: ['read'], resource: ['metrics:*', 'logs:*'], }, ]; logger.info('RBAC Policy Engine initialized', { policies: this.policies.length, roles: this.roles.size, permissions: this.permissions.size, }); } /** * Evaluate authorization for a given context */ authorize(context) { try { const applicablePolicies = this.getApplicablePolicies(context); // Log authorization attempt logger.debug('Authorization evaluation', { principal: context.principal, action: context.action, resource: context.resource, applicablePolicies: applicablePolicies.length, }); // Check for explicit deny first (deny overrides allow) const hasDeny = applicablePolicies.some(policy => policy.effect === 'deny' && this.evaluateConditions(policy.condition, context.environment)); if (hasDeny) { logger.warn('Authorization denied by explicit policy', context); return false; } // Check for explicit allow const hasAllow = applicablePolicies.some(policy => policy.effect === 'allow' && this.evaluateConditions(policy.condition, context.environment)); const authorized = hasAllow; if (authorized) { logger.info('Authorization granted', { principal: context.principal, action: context.action, resource: context.resource, }); } else { logger.warn('Authorization denied - no matching allow policy', context); } return authorized; } catch (error) { logger.error('Authorization evaluation failed', error, context); return false; // Fail secure } } /** * Get applicable policies for a context */ getApplicablePolicies(context) { return this.policies.filter(policy => this.matchesPrincipal(policy.principal, context.principal) && this.matchesAction(policy.action, context.action) && this.matchesResource(policy.resource, context.resource)); } /** * Check if principal matches policy */ matchesPrincipal(policyPrincipal, contextPrincipal) { const principals = Array.isArray(policyPrincipal) ? policyPrincipal : [policyPrincipal]; return principals.some(p => this.matchesPattern(p, contextPrincipal)); } /** * Check if action matches policy */ matchesAction(policyAction, contextAction) { const actions = Array.isArray(policyAction) ? policyAction : [policyAction]; return actions.some(a => this.matchesPattern(a, contextAction)); } /** * Check if resource matches policy */ matchesResource(policyResource, contextResource) { const resources = Array.isArray(policyResource) ? policyResource : [policyResource]; return resources.some(r => this.matchesPattern(r, contextResource)); } /** * Pattern matching with wildcards */ matchesPattern(pattern, value) { if (pattern === '*') return true; if (pattern === value) return true; if (pattern.includes('*')) { const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$'); return regex.test(value); } return false; } /** * Evaluate policy conditions */ evaluateConditions(conditions, environment) { if (!conditions) return true; return Object.entries(conditions).every(([key, value]) => { switch (key) { case 'user.owns_resource': return environment.userId === environment.resourceOwnerId; case 'ip_address_in_range': return this.isIpInRange(environment.ipAddress, value); case 'time_between': return this.isTimeBetween(environment.currentTime, value.start, value.end); case 'user.has_role': return environment.userRoles?.includes(value); case 'request.has_header': return environment.headers?.[value.name] === value.value; case 'rate_limit.under_threshold': return environment.requestCount < value; default: // Direct equality check return environment[key] === value; } }); } /** * Check if IP address is in range */ isIpInRange(ip, range) { // Simplified IP range check - in production use proper CIDR library if (range === '*') return true; if (range === ip) return true; // Basic subnet check (e.g., 192.168.1.*) if (range.endsWith('*')) { const prefix = range.slice(0, -1); return ip.startsWith(prefix); } return false; } /** * Check if current time is between start and end */ isTimeBetween(current, start, end) { const currentTime = current.getHours() * 100 + current.getMinutes(); const startTime = parseInt(start.replace(':', '')); const endTime = parseInt(end.replace(':', '')); return currentTime >= startTime && currentTime <= endTime; } /** * Add a new policy */ addPolicy(policy) { this.policies.push(policy); logger.info('Policy added', { policy }); } /** * Remove a policy */ removePolicy(index) { if (index >= 0 && index < this.policies.length) { const removed = this.policies.splice(index, 1); logger.info('Policy removed', { policy: removed[0] }); } } /** * Get user permissions based on roles */ getUserPermissions(user) { const userPermissions = []; // Get permissions from user's roles for (const roleId of user.roles) { const role = this.roles.get(roleId); if (role) { userPermissions.push(...role.permissions); } } // Add direct user permissions for (const permId of user.permissions) { const permission = this.permissions.get(permId); if (permission) { userPermissions.push(permission); } } // Remove duplicates const uniquePermissions = userPermissions.filter((perm, index, self) => index === self.findIndex(p => p.id === perm.id)); return uniquePermissions; } /** * Check if user has specific permission */ userHasPermission(user, permissionId) { const userPermissions = this.getUserPermissions(user); return userPermissions.some(perm => perm.id === permissionId); } /** * Express middleware for authorization */ middleware(requiredAction, requiredResource) { return (req, res, next) => { try { // User should be attached by authentication middleware const user = req.user; if (!user) { return res.status(401).json({ error: 'Authentication required', code: 'NO_USER_CONTEXT', }); } // Build authorization context const context = { principal: user.userId, action: requiredAction, resource: requiredResource, environment: { userId: user.userId, userRoles: user.roles, ipAddress: req.ip, currentTime: new Date(), headers: req.headers, method: req.method, path: req.path, }, }; // Check authorization const authorized = this.authorize(context); if (!authorized) { return res.status(403).json({ error: 'Insufficient permissions', code: 'INSUFFICIENT_PERMISSIONS', required: { action: requiredAction, resource: requiredResource, }, }); } next(); } catch (error) { logger.error('Authorization middleware error', error); return res.status(500).json({ error: 'Internal authorization error', code: 'AUTHZ_INTERNAL_ERROR', }); } }; } /** * Get all policies (for admin interface) */ getPolicies() { return [...this.policies]; } /** * Get all roles (for admin interface) */ getRoles() { return Array.from(this.roles.values()); } /** * Get all permissions (for admin interface) */ getPermissions() { return Array.from(this.permissions.values()); } } //# sourceMappingURL=rbac-policy-engine.js.map