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
JavaScript
"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;