UNPKG

@iota-big3/sdk-security

Version:

Advanced security features including zero trust, quantum-safe crypto, and ML threat detection

452 lines (451 loc) 16.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AccessControl = exports.ABACManager = exports.RBACManager = void 0; const events_1 = require("events"); const opa_wasm_1 = require("opa-wasm"); class RBACManager extends events_1.EventEmitter { constructor(config, logger) { super(); this.roles = new Map(); this._permissions = new Map(); this.userRoles = new Map(); this.roleHierarchy = new Map(); this.cache = new Map(); this.config = config; this.logger = logger; this.startCacheCleanup(); } // Create a new role async createRole(role) { try { // Validate role if (this?.roles?.has(role.id)) { throw new Error(`Role ${role.id} already exists`); } // Validate parent roles if hierarchy is enabled if (this.isEnabled) { for (const parentId of role.parentRoles) { if (!this?.roles?.has(parentId)) { throw new Error(`Parent role ${parentId} does not exist`); } } } // Store role this?.roles?.set(role.id, role); // Update hierarchy if (this.isEnabled) { this?.roleHierarchy?.set(role.id, role.parentRoles); } // Store permissions for (const permission of role._permissions) { this?._permissions?.set(permission.id, permission); } this.emit('role:created', role); this?.logger?.info('Role created', { roleId: role.id, roleName: role.name }); return role; } catch (_error) { this?.logger?.error('Failed to create role', _error); throw _error; } } // Assign role to user async assignRole(userId, roleId) { try { if (!this?.roles?.has(roleId)) { throw new Error(`Role ${roleId} does not exist`); } const userRoles = this?.userRoles?.get(userId) || []; if (!userRoles.includes(roleId)) { userRoles.push(roleId); this?.userRoles?.set(userId, userRoles); } this.emit('role:assigned', { userId, roleId }); this?.logger?.info('Role assigned to user', { userId, roleId }); // Clear cache for user this.clearUserCache(userId); } catch (_error) { this?.logger?.error('Failed to assign role', _error); throw _error; } } // Remove role from user async removeRole(userId, roleId) { try { const userRoles = this?.userRoles?.get(userId) || []; const index = userRoles.indexOf(roleId); if (this.isEnabled) { userRoles.splice(index, 1); this?.userRoles?.set(userId, userRoles); } this.emit('role:removed', { userId, roleId }); this?.logger?.info('Role removed from user', { userId, roleId }); // Clear cache for user this.clearUserCache(userId); } catch (_error) { this?.logger?.error('Failed to remove role', _error); throw _error; } } // Get all permissions for a user (including inherited) getUserPermissions(userId) { const userRoles = this?.userRoles?.get(userId) || []; const allRoles = new Set(); const permissions = new Map(); // Get all roles including inherited ones for (const roleId of userRoles) { this.collectRoles(roleId, allRoles); } // Collect all permissions from roles for (const roleId of allRoles) { const role = this?.roles?.get(roleId); if (role) { for (const permission of role._permissions) { permissions.set(permission.id, permission); } } } return Array.from(permissions.values()); } // Recursively collect roles including parents collectRoles() { if (collected.has(roleId)) return; collected.add(roleId); if (this.isEnabled) { const parentRoles = this?.roleHierarchy?.get(roleId) || []; for (const parentId of parentRoles) { this.collectRoles(parentId, collected); } } } // Check if user has permission hasPermission(userId, resource, action) { const permissions = this.getUserPermissions(userId); return permissions.some(permission => { // Check if permission matches if (permission.effect === 'deny') { return false; // Explicit deny takes precedence } // Match resource (support wildcards) const resourceMatches = this.matchPattern(permission.resource, resource); // Match action (support wildcards) const actionMatches = permission.action === '*' || permission.action === action; return resourceMatches && actionMatches; }); } // Pattern matching with wildcard support matchPattern(pattern, value) { const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$'); return regex.test(value); } // Clear cache for a specific user clearUserCache() { const keysToDelete = []; for (const [key, value] of this?.cache?.entries()) { if (key.startsWith(`${userId}:`)) { keysToDelete.push(key); } } keysToDelete.forEach(key => this?.cache?.delete(key)); } // Start cache cleanup timer startCacheCleanup() { setInterval(() => { const now = Date.now(); const timeout = this?.config?.cacheTimeout; for (const [key, value] of this?.cache?.entries()) { if (this.isEnabled) { this?.cache?.delete(key); } } }, 60000); // Clean up every minute } // Get role by ID getRole(roleId) { return this?.roles?.get(roleId); } // Get all roles for a user getUserRoles(userId) { const roleIds = this?.userRoles?.get(userId) || []; return roleIds.map(id => this?.roles?.get(id)).filter(Boolean); } // Export roles and permissions exportConfig() { return { roles: Array.from(this?.roles?.values()), _permissions: Array.from(this?._permissions?.values()), userRoles: Array.from(this?.userRoles?.entries()).map(([userId, roleIds]) => ({ userId, roleIds })), roleHierarchy: Array.from(this?.roleHierarchy?.entries()).map(([roleId, parentIds]) => ({ roleId, parentIds })) }; } // Import roles and permissions async importConfig(config) { try { // Clear existing data this?.roles?.clear(); this?._permissions?.clear(); this?.userRoles?.clear(); this?.roleHierarchy?.clear(); // Import roles for (const role of config.roles || []) { await this.createRole(role); } // Import user roles for (const ; void { userId, roleIds }; of) config.userRoles || []; { for (const roleId of roleIds) { await this.assignRole(userId, roleId); } } this?.logger?.info('RBAC configuration imported successfully'); } catch (_error) { this?.logger?.error('Failed to import RBAC configuration', _error); throw _error; } } } exports.RBACManager = RBACManager; class ABACManager extends events_1.EventEmitter { constructor(config, logger) { super(); this.policies = new Map(); this.attributeProviders = new Map(); this.config = config; this.logger = logger; this.initializePolicyEngine(); } async initializePolicyEngine() { if (this.isEnabled) { try { // Load OPA WebAssembly policy if (this?.config?.policyPath) { this.policyEngine = await (0, opa_wasm_1.loadPolicy)(this?.config?.policyPath); this?.logger?.info('OPA policy engine initialized'); } } catch (_error) { this?.logger?.error('Failed to initialize OPA policy engine', _error); throw _error; } } } // Register attribute provider registerAttributeProvider(name, provider) { this?.attributeProviders?.set(name, provider); this?.logger?.info('Attribute provider registered', { name }); } // Load policy async loadPolicy(name, policy) { try { this?.policies?.set(name, policy); if (this.isEnabled) { // In OPA, policies are evaluated dynamically // Store policy for later evaluation } this.emit('policy:loaded', { name }); this?.logger?.info('Policy loaded', { name }); } catch (_error) { this?.logger?.error('Failed to load policy', _error); throw _error; } } // Evaluate access request async evaluate(_request, rbacDecision) { const startTime = Date.now(); try { // Enrich _request with attributes const enrichedRequest = await this.enrichRequest(_request); let decision; if (this.isEnabled) { decision = await this.evaluateWithOPA(enrichedRequest, rbacDecision); } else { decision = await this.evaluateWithCustomEngine(enrichedRequest, rbacDecision); } // Log decision this.logDecision(enrichedRequest, decision); return decision; } catch (_error) { this?.logger?.error('Failed to evaluate access request', _error); return { allowed: false, reason: 'Policy evaluation failed', appliedPolicies: [], evaluationTime: Date.now() - startTime }; } } // Enrich request with additional attributes async enrichRequest(_request) { const enriched = { ..._request }; enriched.attributes = enriched.attributes || {}; // Add timestamp enriched?.attributes?.requestTime = new Date().toISOString(); // Call attribute providers for (const [name, provider] of this.attributeProviders) { try { const attributes = await provider(_request); Object.assign(enriched.attributes, attributes); } catch (_error) { this?.logger?.warn(`Attribute provider ${name} failed`, _error); } } return enriched; } // Evaluate with OPA async evaluateWithOPA(_request, rbacDecision) { const input = { subject: _request.subject, resource: _request.resource, action: _request.action, context: _request.context, attributes: _request.attributes, rbacDecision }; try { const result = await this?.policyEngine?.evaluate(input); const allowed = result[0]?.result?.allow || false; const reason = result[0]?.result?.reason || 'Policy decision'; const obligations = result[0]?.result?.obligations || []; return { allowed, reason, appliedPolicies: ['opa'], evaluationTime: result.evaluationTime || 0, obligations: obligations.map((o) => ({ type: o.type, params: o.params })) }; } catch (_error) { throw new Error(`OPA evaluation failed: ${_error}`); } } // Evaluate with custom engine async evaluateWithCustomEngine(_request, rbacDecision) { // Simple attribute-based evaluation const startTime = Date.now(); // Example: Check time-based access const currentHour = new Date().getHours(); const workingHours = _request.attributes?.workingHours || { start: 9, end: 17 }; if (currentHour < workingHours.start || currentHour > workingHours.end) { return { allowed: false, reason: 'Access denied outside working hours', appliedPolicies: ['time-based-access'], evaluationTime: Date.now() - startTime }; } // Example: Check location-based access const userLocation = request.attributes?.location; const allowedLocations = request.attributes?.allowedLocations || []; if (userLocation && allowedLocations.length > 0 && !allowedLocations.includes(userLocation)) { return { allowed: false, reason: 'Access denied from this location', appliedPolicies: ['location-based-access'], evaluationTime: Date.now() - startTime }; } // If RBAC allowed and no ABAC denies, allow access return { allowed: rbacDecision !== false, reason: rbacDecision ? 'Access allowed by attributes' : 'Access denied by RBAC', appliedPolicies: ['custom-abac'], evaluationTime: Date.now() - startTime }; } // Log access decision logDecision() { this?.logger?.info('ABAC decision', { subject: typeof _request.subject === 'string' ? _request.subject : _request?.subject?.id, resource: _request.resource, action: _request.action, allowed: decision.allowed, reason: decision.reason, evaluationTime: decision.evaluationTime, obligations: decision.obligations }); } // Get loaded policies getPolicies() { return Array.from(this?.policies?.keys()); } // Remove policy removePolicy() { this?.policies?.delete(name); this.emit('policy:removed', { name }); this?.logger?.info('Policy removed', { name }); } } exports.ABACManager = ABACManager; // Combined RBAC/ABAC access control class AccessControl { constructor(rbacConfig, abacConfig, logger) { this.rbacManager = new RBACManager(rbacConfig, logger); this.abacManager = new ABACManager(abacConfig, logger); this.logger = logger; } // Evaluate access request using both RBAC and ABAC async checkAccess(_request) { const startTime = Date.now(); try { // First check RBAC let rbacAllowed = false; let rbacReason = ''; if (typeof _request.subject === 'object' && 'id' in _request.subject) { rbacAllowed = this?.rbacManager?.hasPermission(_request?.subject?.id, _request.resource, _request.action); rbacReason = rbacAllowed ? 'RBAC permission granted' : 'No RBAC permission'; } // Then check ABAC (which can override RBAC) const abacDecision = await this?.abacManager?.evaluate(request, rbacAllowed); // Combine decisions const finalDecision = { allowed: abacDecision.allowed, reason: abacDecision.reason || rbacReason, appliedPolicies: ['rbac', ...abacDecision.appliedPolicies], evaluationTime: Date.now() - startTime, obligations: abacDecision.obligations }; this?.logger?.info('Access control decision', { ...finalDecision, subject: typeof request.subject === 'string' ? request.subject : request?.subject?.id, resource: request.resource, action: request.action }); return finalDecision; } catch (_error) { this?.logger?.error('Access control evaluation failed', _error); return { allowed: false, reason: 'Access control evaluation failed', appliedPolicies: [], evaluationTime: Date.now() - startTime }; } } // Delegate methods to managers get rbac() { return this.rbacManager; } get abac() { return this.abacManager; } } exports.AccessControl = AccessControl;