// services/roleGuard.ts // Role-based access control helpers import { Context } from "fresh"; import type { State } from "../utils.ts"; import { getUserById } from "./userService.ts"; import type { User } from "./userService.ts"; export interface RoleCheckResult { authorized: boolean; response?: Response; user?: User; } /** * Check if user has required role * @param ctx Fresh context * @param requiredRole The role required ('landlord' or 'tenant') * @returns RoleCheckResult with authorization status */ export async function requireRole( ctx: Context, requiredRole: "landlord" | "tenant", ): Promise { const { user_id } = ctx.state.data; // 1. Check authentication if (!user_id) { return { authorized: false, response: new Response( JSON.stringify({ error: "Authentication required", redirectTo: "/auth/login", }), { status: 401, headers: { "Content-Type": "application/json" } }, ), }; } // 2. Get user to verify role const user = await getUserById(user_id); if (!user) { return { authorized: false, response: new Response( JSON.stringify({ error: "User not found" }), { status: 404, headers: { "Content-Type": "application/json" } }, ), }; } // 3. Verify user has required role if (user.user_type !== requiredRole) { return { authorized: false, response: new Response( JSON.stringify({ error: `Forbidden: ${ requiredRole.charAt(0).toUpperCase() + requiredRole.slice(1) } access required`, user_type: user.user_type, required_role: requiredRole, }), { status: 403, headers: { "Content-Type": "application/json" } }, ), user, }; } return { authorized: true, user }; } /** * Middleware wrapper to require landlord role */ export function requireLandlord() { return async (ctx: Context, next: () => Promise) => { const check = await requireRole(ctx, "landlord"); if (!check.authorized) { return check.response!; } return await next(); }; } /** * Middleware wrapper to require tenant role */ export function requireTenant() { return async (ctx: Context, next: () => Promise) => { const check = await requireRole(ctx, "tenant"); if (!check.authorized) { return check.response!; } return await next(); }; }