98 lines
2.4 KiB
TypeScript
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();
|
|
};
|
|
}
|