Admin User v1
This commit is contained in:
90
backend/src/core/plugins/admin-guard.plugin.ts
Normal file
90
backend/src/core/plugins/admin-guard.plugin.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @ai-summary Fastify admin authorization plugin
|
||||
* @ai-context Checks if authenticated user is an admin and enforces access control
|
||||
*/
|
||||
|
||||
import { FastifyPluginAsync, FastifyRequest, FastifyReply } 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<void>;
|
||||
}
|
||||
}
|
||||
|
||||
// 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(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
// 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 auth0_sub, email, role, revoked_at
|
||||
FROM admin_users
|
||||
WHERE auth0_sub = $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'
|
||||
});
|
||||
Reference in New Issue
Block a user