import * as plugins from '../../plugins.ts'; import { logger } from '../../logging.ts'; import type { OpsServer } from '../classes.opsserver.ts'; import * as interfaces from '../../../ts_interfaces/index.ts'; export interface IJwtData { userId: string; status: 'loggedIn' | 'loggedOut'; expiresAt: number; } export class AdminHandler { public typedrouter = new plugins.typedrequest.TypedRouter(); public smartjwtInstance!: plugins.smartjwt.SmartJwt; constructor(private opsServerRef: OpsServer) { this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter); } public async initialize(): Promise { this.smartjwtInstance = new plugins.smartjwt.SmartJwt(); await this.smartjwtInstance.init(); await this.smartjwtInstance.createNewKeyPair(); this.registerHandlers(); } private registerHandlers(): void { // Login this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'adminLoginWithUsernameAndPassword', async (dataArg) => { try { const user = this.opsServerRef.oneboxRef.database.getUserByUsername(dataArg.username); if (!user) { throw new plugins.typedrequest.TypedResponseError('Invalid credentials'); } // Verify password (base64 comparison to match existing DB scheme) const passwordHash = btoa(dataArg.password); if (passwordHash !== user.passwordHash) { throw new plugins.typedrequest.TypedResponseError('Invalid credentials'); } const expiresAt = Date.now() + 24 * 3600 * 1000; const userId = String(user.id || user.username); const jwt = await this.smartjwtInstance.createJWT({ userId, status: 'loggedIn', expiresAt, }); logger.info(`User logged in: ${user.username}`); return { identity: { jwt, userId, username: user.username, expiresAt, role: user.role, }, }; } catch (error) { if (error instanceof plugins.typedrequest.TypedResponseError) throw error; throw new plugins.typedrequest.TypedResponseError('Login failed'); } }, ), ); // Logout this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'adminLogout', async (_dataArg) => { return { ok: true }; }, ), ); // Verify Identity 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); if (jwtData.expiresAt < Date.now()) return { valid: false }; if (jwtData.status !== 'loggedIn') return { valid: false }; return { valid: true, identity: { jwt: dataArg.identity.jwt, userId: jwtData.userId, username: dataArg.identity.username, expiresAt: jwtData.expiresAt, role: dataArg.identity.role, }, }; } catch { return { valid: false }; } }, ), ); // Change Password this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'changePassword', async (dataArg) => { await this.requireValidIdentity(dataArg); const user = this.opsServerRef.oneboxRef.database.getUserByUsername(dataArg.identity.username); if (!user) { throw new plugins.typedrequest.TypedResponseError('User not found'); } const currentHash = btoa(dataArg.currentPassword); if (currentHash !== user.passwordHash) { throw new plugins.typedrequest.TypedResponseError('Current password is incorrect'); } const newHash = btoa(dataArg.newPassword); this.opsServerRef.oneboxRef.database.updateUserPassword(user.username, newHash); logger.info(`Password changed for user: ${user.username}`); return { ok: true }; }, ), ); } private async requireValidIdentity(dataArg: { identity: interfaces.data.IIdentity }): Promise { const passed = await this.validIdentityGuard.exec({ identity: dataArg.identity }); if (!passed) { throw new plugins.typedrequest.TypedResponseError('Valid identity required'); } } // Guard for valid identity 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); if (jwtData.expiresAt < Date.now()) return false; if (jwtData.status !== 'loggedIn') return false; if (dataArg.identity.expiresAt !== jwtData.expiresAt) return false; if (dataArg.identity.userId !== jwtData.userId) return false; return true; } catch { return false; } }, { failedHint: 'identity is not valid', name: 'validIdentityGuard' }, ); // Guard for admin identity public adminIdentityGuard = new plugins.smartguard.Guard<{ identity: interfaces.data.IIdentity; }>( async (dataArg) => { const isValid = await this.validIdentityGuard.exec(dataArg); if (!isValid) return false; return dataArg.identity.role === 'admin'; }, { failedHint: 'user is not admin', name: 'adminIdentityGuard' }, ); }