/** * @ai-summary Fastify admin authorization plugin * @ai-context Checks if authenticated user is an admin and enforces access control */ import { FastifyPluginAsync, FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'; import fp from 'fastify-plugin'; import { Pool } from 'pg'; import { logger } from '../logging/logger'; declare module 'fastify' { interface FastifyInstance { requireAdmin: (request: FastifyRequest, reply: FastifyReply) => Promise; } } // Store pool reference for use in handler let dbPool: Pool | null = null; export function setAdminGuardPool(pool: Pool): void { dbPool = pool; } const adminGuardPlugin: FastifyPluginAsync = async (fastify) => { // Decorate with requireAdmin function that enforces admin authorization fastify.decorate('requireAdmin', async function(this: FastifyInstance, request: FastifyRequest, reply: FastifyReply) { try { if (typeof this.authenticate !== 'function') { logger.error('Admin guard: authenticate handler missing'); return reply.code(500).send({ error: 'Internal server error', message: 'Authentication handler missing' }); } await this.authenticate(request, reply); if (reply.sent) { return; } // Ensure user is authenticated first if (!request.userContext?.userId) { logger.warn('Admin guard: user context missing'); return reply.code(401).send({ error: 'Unauthorized', message: 'Authentication required' }); } // If pool not initialized, return 500 if (!dbPool) { logger.error('Admin guard: database pool not initialized'); return reply.code(500).send({ error: 'Internal server error', message: 'Admin check unavailable' }); } // Check if user is in admin_users table and not revoked const query = ` SELECT id, user_profile_id, email, role, revoked_at FROM admin_users WHERE user_profile_id = $1 AND revoked_at IS NULL LIMIT 1 `; const result = await dbPool.query(query, [request.userContext.userId]); if (result.rows.length === 0) { logger.warn('Admin guard: user is not authorized as admin', { userId: request.userContext.userId?.substring(0, 8) + '...' }); return reply.code(403).send({ error: 'Forbidden', message: 'Admin access required' }); } // Set admin flag in userContext request.userContext.isAdmin = true; request.userContext.adminRecord = result.rows[0]; logger.info('Admin guard: admin authorization successful', { userId: request.userContext.userId?.substring(0, 8) + '...', role: result.rows[0].role }); } catch (error) { logger.error('Admin guard: authorization check failed', { error: error instanceof Error ? error.message : 'Unknown error', userId: request.userContext?.userId?.substring(0, 8) + '...' }); return reply.code(500).send({ error: 'Internal server error', message: 'Admin check failed' }); } }); }; export default fp(adminGuardPlugin, { name: 'admin-guard-plugin' });