UNPKG

recoder-code

Version:

Complete AI-powered development platform with ML model training, plugin registry, real-time collaboration, monitoring, infrastructure automation, and enterprise deployment capabilities

306 lines (305 loc) 9.71 kB
"use strict"; /** * Authentication Service for Collaboration Service * Handles user authentication and authorization for real-time collaboration */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthService = void 0; const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const events_1 = require("events"); class AuthService extends events_1.EventEmitter { constructor(jwtSecret, jwtExpiration) { super(); this.connectedUsers = new Map(); // socketId -> User this.userSockets = new Map(); // userId -> socketIds this.jwtSecret = jwtSecret || process.env.JWT_SECRET || 'dev-secret-key-collaboration'; this.jwtExpiration = jwtExpiration || process.env.JWT_EXPIRATION || '24h'; } /** * Authenticate a user with JWT token */ async authenticateToken(token) { try { const decoded = jsonwebtoken_1.default.verify(token, this.jwtSecret); const user = { id: decoded.userId, username: decoded.username, email: decoded.email, role: decoded.role }; return { success: true, user }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Invalid token' }; } } /** * Generate a JWT token for a user */ generateToken(user) { const payload = { userId: user.id, username: user.username, email: user.email, role: user.role }; return jsonwebtoken_1.default.sign(payload, this.jwtSecret, { expiresIn: this.jwtExpiration }); } /** * Register a connected user with socket ID */ registerConnection(socketId, user) { this.connectedUsers.set(socketId, user); if (!this.userSockets.has(user.id)) { this.userSockets.set(user.id, new Set()); } this.userSockets.get(user.id).add(socketId); this.emit('userConnected', { socketId, user }); } /** * Unregister a disconnected user */ unregisterConnection(socketId) { const user = this.connectedUsers.get(socketId); if (!user) { return undefined; } this.connectedUsers.delete(socketId); const userSocketSet = this.userSockets.get(user.id); if (userSocketSet) { userSocketSet.delete(socketId); if (userSocketSet.size === 0) { this.userSockets.delete(user.id); } } this.emit('userDisconnected', { socketId, user }); return user; } /** * Get user by socket ID */ getUserBySocket(socketId) { return this.connectedUsers.get(socketId); } /** * Get all socket IDs for a user */ getUserSockets(userId) { const socketSet = this.userSockets.get(userId); return socketSet ? Array.from(socketSet) : []; } /** * Check if user is connected */ isUserConnected(userId) { return this.userSockets.has(userId); } /** * Get all connected users */ getConnectedUsers() { const uniqueUsers = new Map(); for (const user of this.connectedUsers.values()) { uniqueUsers.set(user.id, user); } return Array.from(uniqueUsers.values()); } /** * Check if user has permission to access a document */ async canAccessDocument(userId, documentId) { // This is a simplified permission check // In a real implementation, you would check against a database or external service const user = Array.from(this.connectedUsers.values()).find(u => u.id === userId); if (!user) { return false; } // Admins can access any document if (user.role === 'admin') { return true; } // For now, all authenticated users can access documents // In a real system, you would implement proper ACL return true; } /** * Check if user has permission to edit a document */ async canEditDocument(userId, documentId) { // This is a simplified permission check const user = Array.from(this.connectedUsers.values()).find(u => u.id === userId); if (!user) { return false; } // Admins can edit any document if (user.role === 'admin') { return true; } // Users and collaborators can edit (simplified logic) return user.role === 'user' || user.role === 'collaborator'; } /** * Check if user has admin privileges */ isAdmin(userId) { const user = Array.from(this.connectedUsers.values()).find(u => u.id === userId); return user?.role === 'admin'; } /** * Authenticate socket connection */ async authenticateSocket(socketId, token) { const authResult = await this.authenticateToken(token); if (!authResult.success || !authResult.user) { return { success: false, error: authResult.error || 'Authentication failed' }; } this.registerConnection(socketId, authResult.user); return { success: true, user: authResult.user }; } /** * Validate token without full authentication */ validateToken(token) { try { const decoded = jsonwebtoken_1.default.verify(token, this.jwtSecret); return { valid: true, decoded }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : 'Invalid token' }; } } /** * Verify token and return user data (alias for authenticateToken) */ async verifyToken(token) { return this.authenticateToken(token); } /** * Refresh a token (if it's close to expiration) */ refreshToken(token) { try { const decoded = jsonwebtoken_1.default.verify(token, this.jwtSecret); // Check if token expires within 1 hour const now = Math.floor(Date.now() / 1000); const timeUntilExpiry = decoded.exp - now; if (timeUntilExpiry > 3600) { // More than 1 hour remaining return { success: false, error: 'Token does not need refresh yet' }; } // Generate new token const user = { id: decoded.userId, username: decoded.username, email: decoded.email, role: decoded.role }; const newToken = this.generateToken(user); return { success: true, newToken }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Invalid token' }; } } /** * Get authentication statistics */ getStats() { const totalConnections = this.connectedUsers.size; const uniqueUsers = this.userSockets.size; const usersByRole = {}; const uniqueUserMap = new Map(); for (const user of this.connectedUsers.values()) { uniqueUserMap.set(user.id, user); } for (const user of uniqueUserMap.values()) { usersByRole[user.role] = (usersByRole[user.role] || 0) + 1; } return { connectedUsers: totalConnections, uniqueUsers, totalConnections, usersByRole }; } /** * Disconnect all sessions for a user */ disconnectUser(userId) { const socketIds = this.getUserSockets(userId); for (const socketId of socketIds) { this.unregisterConnection(socketId); } this.emit('userForceDisconnected', { userId, disconnectedSockets: socketIds }); return socketIds; } /** * Clean up expired connections (call this periodically) */ cleanupExpiredConnections() { const now = Date.now(); const expiredSockets = []; // This is a simplified cleanup - in a real implementation, // you'd track connection timestamps and clean up based on inactivity for (const [socketId, user] of this.connectedUsers.entries()) { // Check if any tokens are expired (this is simplified) // In reality, you'd want to track actual connection timestamps try { // Try to verify if the user's token is still valid // This is just an example - you'd implement proper session management } catch (error) { expiredSockets.push(socketId); } } for (const socketId of expiredSockets) { this.unregisterConnection(socketId); } if (expiredSockets.length > 0) { this.emit('expiredConnectionsCleanup', { cleanedSockets: expiredSockets.length }); } } } exports.AuthService = AuthService;