integratedhub/services/roleGuard.ts
2026-02-14 02:20:19 +00:00

98 lines
2.4 KiB
TypeScript

// 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<State>,
requiredRole: "landlord" | "tenant",
): Promise<RoleCheckResult> {
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<State>, next: () => Promise<Response>) => {
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<State>, next: () => Promise<Response>) => {
const check = await requireRole(ctx, "tenant");
if (!check.authorized) {
return check.response!;
}
return await next();
};
}