import * as plugins from '../../plugins.js'; import type { OpsServer } from '../classes.opsserver.js'; import * as interfaces from '../../../ts_interfaces/index.js'; export interface IJwtData { userId: string; status: 'loggedIn' | 'loggedOut'; expiresAt: number; } export class AdminHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); // JWT instance public smartjwtInstance: plugins.smartjwt.SmartJwt; // Simple in-memory user storage (in production, use proper database) private users = new Map(); constructor(private opsServerRef: OpsServer) { // Add this handler's router to the parent this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); } public async initialize(): Promise { await this.initializeJwt(); this.initializeDefaultUsers(); this.registerHandlers(); } private async initializeJwt(): Promise { this.smartjwtInstance = new plugins.smartjwt.SmartJwt(); await this.smartjwtInstance.init(); // For development, create new keypair each time // In production, load from storage like cloudly does await this.smartjwtInstance.createNewKeyPair(); } private initializeDefaultUsers(): void { // Add default admin user const adminId = plugins.uuid.v4(); this.users.set(adminId, { id: adminId, username: 'admin', password: 'admin', role: 'admin', }); } private registerHandlers(): void { // Admin Login Handler this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'adminLoginWithUsernameAndPassword', async (dataArg) => { try { // Find user by username and password let user: { id: string; username: string; password: string; role: string } | null = null; for (const [_, userData] of this.users) { if (userData.username === dataArg.username && userData.password === dataArg.password) { user = userData; break; } } if (!user) { throw new plugins.typedrequest.TypedResponseError('login failed'); } const expiresAtTimestamp = Date.now() + 3600 * 1000 * 24 * 7; // 7 days const jwt = await this.smartjwtInstance.createJWT({ userId: user.id, status: 'loggedIn', expiresAt: expiresAtTimestamp, }); return { identity: { jwt, userId: user.id, name: user.username, expiresAt: expiresAtTimestamp, role: user.role, type: 'user', }, }; } catch (error) { if (error instanceof plugins.typedrequest.TypedResponseError) { throw error; } throw new plugins.typedrequest.TypedResponseError('login failed'); } } ) ); // Admin Logout Handler this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'adminLogout', async (dataArg) => { // In a real implementation, you might want to blacklist the JWT // For now, just return success return { success: true, }; } ) ); // Verify Identity Handler this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'verifyIdentity', async (dataArg) => { if (!dataArg.identity?.jwt) { return { valid: false, }; } try { const jwtData = await this.smartjwtInstance.verifyJWTAndGetData(dataArg.identity.jwt); // Check if expired if (jwtData.expiresAt < Date.now()) { return { valid: false, }; } // Check if logged in if (jwtData.status !== 'loggedIn') { return { valid: false, }; } // Find user const user = this.users.get(jwtData.userId); if (!user) { return { valid: false, }; } return { valid: true, identity: { jwt: dataArg.identity.jwt, userId: user.id, name: user.username, expiresAt: jwtData.expiresAt, role: user.role, type: 'user', }, }; } catch (error) { return { valid: false, }; } } ) ); } /** * Create a guard for valid identity (matching cloudly pattern) */ public validIdentityGuard = new plugins.smartguard.Guard<{ identity: interfaces.data.IIdentity; }>( async (dataArg) => { if (!dataArg.identity?.jwt) { return false; } try { const jwtData = await this.smartjwtInstance.verifyJWTAndGetData(dataArg.identity.jwt); // Check expiration if (jwtData.expiresAt < Date.now()) { return false; } // Check status if (jwtData.status !== 'loggedIn') { return false; } // Verify data hasn't been tampered with if (dataArg.identity.expiresAt !== jwtData.expiresAt) { return false; } if (dataArg.identity.userId !== jwtData.userId) { return false; } return true; } catch (error) { return false; } }, { failedHint: 'identity is not valid', name: 'validIdentityGuard', } ); /** * Create a guard for admin identity (matching cloudly pattern) */ public adminIdentityGuard = new plugins.smartguard.Guard<{ identity: interfaces.data.IIdentity; }>( async (dataArg) => { // First check if identity is valid const isValid = await this.validIdentityGuard.exec(dataArg); if (!isValid) { return false; } // Check if user has admin role return dataArg.identity.role === 'admin'; }, { failedHint: 'user is not admin', name: 'adminIdentityGuard', } ); }