UNPKG

@codai/cbd

Version:

Codai Better Database - High-Performance Vector Memory System with HPKV-inspired architecture and MCP server

488 lines 17.4 kB
/** * Enhanced Security Framework * JWT authentication, RBAC, API key management, request signing, encryption, audit logging */ import { EventEmitter } from 'events'; import { randomBytes, createHash, createHmac } from 'crypto'; import { performance } from 'perf_hooks'; class EnhancedSecurityFramework extends EventEmitter { config; userStore = new Map(); apiKeyStore = new Map(); sessionStore = new Map(); auditLog = []; rateLimitStore = new Map(); encryptionKeys = new Map(); rbacEngine; jwtHandler; encryptionManager; constructor(config) { super(); this.config = config; this.rbacEngine = new RBACEngine(config.rbac); this.jwtHandler = new JWTHandler(config.jwtSecret, config.jwtExpirationTime); this.encryptionManager = new EncryptionManager(config.encryption); this.initializeSecurity(); } initializeSecurity() { // Initialize RBAC system this.rbacEngine.initialize(); // Setup audit logging if (this.config.auditLogging) { this.setupAuditLogging(); } // Initialize encryption keys this.initializeEncryptionKeys(); // Setup rate limiting cleanup this.setupRateLimitCleanup(); // Setup session cleanup this.setupSessionCleanup(); } /** * JWT Authentication */ async authenticateUser(username, password, options = {}) { const startTime = performance.now(); try { // Find user const user = Array.from(this.userStore.values()).find(u => u.username === username && u.isActive); if (!user) { await this.logAuditEvent({ action: 'authentication_failed', resource: 'auth', method: 'login', success: false, details: { reason: 'user_not_found', username }, duration: performance.now() - startTime }); return { success: false, error: 'Invalid credentials' }; } // Verify password (simplified - in reality would use bcrypt) const isValidPassword = await this.verifyPassword(password, user); if (!isValidPassword) { await this.logAuditEvent({ userId: user.id, action: 'authentication_failed', resource: 'auth', method: 'login', success: false, details: { reason: 'invalid_password', username }, duration: performance.now() - startTime }); return { success: false, error: 'Invalid credentials' }; } // Generate JWT token const tokenData = { userId: user.id, username: user.username, roles: user.roles, permissions: user.permissions }; const expirationTime = options.rememberMe ? '30d' : this.config.jwtExpirationTime; const token = await this.jwtHandler.generateToken(tokenData, expirationTime); const expiresAt = new Date(Date.now() + this.parseExpirationTime(expirationTime)); // Update user last login user.lastLogin = new Date(); // Create session const sessionId = this.generateSessionId(); this.sessionStore.set(sessionId, { userId: user.id, token, expiresAt, clientInfo: options.clientInfo, createdAt: new Date() }); await this.logAuditEvent({ userId: user.id, action: 'authentication_success', resource: 'auth', method: 'login', success: true, details: { username, rememberMe: options.rememberMe }, duration: performance.now() - startTime }); this.emit('userAuthenticated', { userId: user.id, username: user.username, sessionId }); return { success: true, user, token, expiresAt, permissions: user.permissions }; } catch (error) { await this.logAuditEvent({ action: 'authentication_error', resource: 'auth', method: 'login', success: false, details: { error: error.message, username }, duration: performance.now() - startTime }); this.emit('authenticationError', { username, error }); throw error; } } /** * API Key Management */ async generateAPIKey(userId, name, options = {}) { try { const keyId = `key_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const rawKey = this.generateRandomKey(this.config.apiKeyLength); const hashedKey = createHash('sha256').update(rawKey).digest('hex'); const apiKey = { id: keyId, name, key: rawKey, hashedKey, userId, permissions: options.permissions || [], rateLimit: options.rateLimit, expiresAt: options.expiresAt, isActive: true, createdAt: new Date(), usageCount: 0 }; this.apiKeyStore.set(keyId, apiKey); await this.logAuditEvent({ userId, action: 'api_key_generated', resource: 'api_keys', method: 'create', success: true, details: { keyId, name, permissions: options.permissions } }); this.emit('apiKeyGenerated', { keyId, userId, name, permissions: options.permissions }); return apiKey; } catch (error) { this.emit('apiKeyGenerationError', { userId, name, error }); throw error; } } /** * Role-Based Access Control (RBAC) */ async checkPermission(userId, resource, action, context) { try { const user = this.userStore.get(userId); if (!user || !user.isActive) { return { granted: false, reason: 'User not found or inactive' }; } const result = await this.rbacEngine.checkPermission(user.roles, user.permissions, resource, action, context); await this.logAuditEvent({ userId, action: 'permission_check', resource, method: action, success: result.granted, details: { resource, action, granted: result.granted, reason: result.reason } }); return result; } catch (error) { this.emit('permissionCheckError', { userId, resource, action, error }); throw error; } } /** * Request Signing & Validation */ async signRequest(method, url, body, apiKey, timestamp) { try { const requestTimestamp = timestamp || Date.now(); const nonce = randomBytes(16).toString('hex'); const stringToSign = [ method.toUpperCase(), url, JSON.stringify(body || {}), apiKey, requestTimestamp.toString(), nonce ].join('\n'); const signature = createHmac('sha256', this.config.jwtSecret) .update(stringToSign) .digest('hex'); return { signature, timestamp: requestTimestamp, nonce }; } catch (error) { this.emit('requestSigningError', { method, url, error }); throw error; } } async validateRequestSignature(method, url, body, apiKey, signature, timestamp, nonce) { try { // Check timestamp validity (5 minute window) const now = Date.now(); const timeDiff = Math.abs(now - timestamp); if (timeDiff > 5 * 60 * 1000) { return { valid: false, reason: 'Request timestamp too old' }; } // Regenerate signature const expectedSignature = await this.signRequest(method, url, body, apiKey, timestamp); // Compare signatures if (signature !== expectedSignature.signature) { return { valid: false, reason: 'Invalid signature' }; } return { valid: true }; } catch (error) { this.emit('signatureValidationError', { method, url, error }); return { valid: false, reason: 'Signature validation error' }; } } /** * Data Encryption at Rest */ async encryptData(data, keyId) { try { const dataString = JSON.stringify(data); const encryptionKeyId = keyId || 'default'; const result = await this.encryptionManager.encrypt(dataString, encryptionKeyId); await this.logAuditEvent({ action: 'data_encrypted', resource: 'encryption', method: 'encrypt', success: true, details: { keyId: encryptionKeyId, dataSize: dataString.length } }); return result; } catch (error) { this.emit('encryptionError', { error }); throw error; } } async decryptData(encryptedData, keyId, iv) { try { const decryptedString = await this.encryptionManager.decrypt(encryptedData, keyId, iv); const data = JSON.parse(decryptedString); await this.logAuditEvent({ action: 'data_decrypted', resource: 'encryption', method: 'decrypt', success: true, details: { keyId, dataSize: decryptedString.length } }); return data; } catch (error) { this.emit('decryptionError', { keyId, error }); throw error; } } /** * Audit Logging */ async getAuditLogs(filters = {}) { try { let filteredLogs = [...this.auditLog]; // Apply filters if (filters.userId) { filteredLogs = filteredLogs.filter(log => log.userId === filters.userId); } if (filters.action) { filteredLogs = filteredLogs.filter(log => log.action === filters.action); } if (filters.resource) { filteredLogs = filteredLogs.filter(log => log.resource === filters.resource); } if (filters.startDate) { filteredLogs = filteredLogs.filter(log => log.timestamp >= filters.startDate); } if (filters.endDate) { filteredLogs = filteredLogs.filter(log => log.timestamp <= filters.endDate); } if (filters.success !== undefined) { filteredLogs = filteredLogs.filter(log => log.success === filters.success); } // Sort by timestamp (newest first) filteredLogs.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); // Apply pagination const offset = filters.offset || 0; const limit = filters.limit || 100; const paginatedLogs = filteredLogs.slice(offset, offset + limit); return { logs: paginatedLogs, total: this.auditLog.length, filtered: filteredLogs.length }; } catch (error) { this.emit('auditLogRetrievalError', { filters, error }); throw error; } } // Private helper methods async verifyPassword(password, user) { // Simplified password verification - in reality would use bcrypt return password === 'password'; // Placeholder } parseExpirationTime(expirationTime) { // Parse expiration time string (e.g., '1h', '7d', '30d') const unit = expirationTime.slice(-1); const value = parseInt(expirationTime.slice(0, -1)); switch (unit) { case 'h': return value * 60 * 60 * 1000; case 'd': return value * 24 * 60 * 60 * 1000; case 'm': return value * 60 * 1000; default: return 60 * 60 * 1000; // Default 1 hour } } generateSessionId() { return `session_${Date.now()}_${randomBytes(16).toString('hex')}`; } generateRandomKey(length) { return randomBytes(length).toString('base64url'); } async logAuditEvent(event) { if (!this.config.auditLogging) return; const auditEntry = { id: `audit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, timestamp: new Date(), userId: event.userId, action: event.action || 'unknown', resource: event.resource || 'unknown', method: event.method || 'unknown', ip: event.ip || 'unknown', userAgent: event.userAgent || 'unknown', success: event.success !== undefined ? event.success : true, details: event.details, duration: event.duration || 0 }; this.auditLog.push(auditEntry); // Emit audit event this.emit('auditLogEntry', auditEntry); // Keep only last 10000 entries to prevent memory overflow if (this.auditLog.length > 10000) { this.auditLog = this.auditLog.slice(-10000); } } initializeEncryptionKeys() { // Generate default encryption key const defaultKey = randomBytes(32); // 256-bit key this.encryptionKeys.set('default', defaultKey); } setupAuditLogging() { // Setup audit log file rotation and persistence // This would typically write to files or a database } setupRateLimitCleanup() { if (!this.config.rateLimiting.enabled) return; setInterval(() => { const now = Date.now(); const windowMs = this.config.rateLimiting.windowMs; Array.from(this.rateLimitStore.entries()).forEach(([key, data]) => { if (now - data.resetTime > windowMs) { this.rateLimitStore.delete(key); } }); }, this.config.rateLimiting.windowMs); } setupSessionCleanup() { setInterval(() => { const now = new Date(); Array.from(this.sessionStore.entries()).forEach(([sessionId, session]) => { if (session.expiresAt < now) { this.sessionStore.delete(sessionId); this.emit('sessionExpired', { sessionId, userId: session.userId }); } }); }, 5 * 60 * 1000); // Check every 5 minutes } } // Supporting classes class RBACEngine { config; constructor(config) { this.config = config; } async initialize() { // Initialize RBAC system } async checkPermission(userRoles, userPermissions, resource, action, context) { // RBAC permission checking logic return { granted: true, matchedPermissions: userPermissions }; } } class JWTHandler { secret; defaultExpiration; constructor(secret, defaultExpiration) { this.secret = secret; this.defaultExpiration = defaultExpiration; } async generateToken(payload, expiration) { // JWT token generation (simplified) const exp = expiration || this.defaultExpiration; return `jwt_token_${Date.now()}_${JSON.stringify(payload)}_${exp}`; } async verifyToken(token) { // JWT token verification (simplified) return { valid: true, payload: {} }; } } class EncryptionManager { config; constructor(config) { this.config = config; } async encrypt(data, keyId) { // Data encryption logic const iv = randomBytes(16).toString('hex'); return { encryptedData: `encrypted_${data}`, keyId, algorithm: this.config.algorithm, iv }; } async decrypt(encryptedData, keyId, iv) { // Data decryption logic return encryptedData.replace('encrypted_', ''); } } export { EnhancedSecurityFramework }; //# sourceMappingURL=security-framework.js.map