173 lines
4.9 KiB
TypeScript
173 lines
4.9 KiB
TypeScript
import { redirect } from "@remix-run/node";
|
|
import { getUser, requireUserId } from "./auth.server";
|
|
import { requireAuthLevel } from "./auth-helpers.server";
|
|
import type { AuthLevel, SafeUser, RouteProtectionOptions } from "~/types/auth";
|
|
import { AUTH_LEVELS, USER_STATUS } from "~/types/auth";
|
|
import { AUTH_ERRORS } from "./auth-constants";
|
|
|
|
// Enhanced middleware for protecting routes that require authentication
|
|
export async function requireAuthentication(
|
|
request: Request,
|
|
options: RouteProtectionOptions = {}
|
|
): Promise<SafeUser> {
|
|
const { allowInactive = false, redirectTo = "/signin" } = options;
|
|
|
|
await requireUserId(request, redirectTo);
|
|
const user = await getUser(request);
|
|
|
|
if (!user) {
|
|
throw redirect(redirectTo);
|
|
}
|
|
|
|
// Check if user is active (unless explicitly allowed)
|
|
if (!allowInactive && user.status !== USER_STATUS.ACTIVE) {
|
|
throw redirect("/signin?error=account_inactive");
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
// Middleware for protecting admin routes (admin and superadmin)
|
|
export async function requireAdmin(
|
|
request: Request,
|
|
options: RouteProtectionOptions = {}
|
|
): Promise<SafeUser> {
|
|
return requireAuthLevel(request, AUTH_LEVELS.ADMIN, options);
|
|
}
|
|
|
|
// Middleware for protecting superadmin routes
|
|
export async function requireSuperAdmin(
|
|
request: Request,
|
|
options: RouteProtectionOptions = {}
|
|
): Promise<SafeUser> {
|
|
return requireAuthLevel(request, AUTH_LEVELS.SUPERADMIN, options);
|
|
}
|
|
|
|
// Middleware for protecting routes with custom auth level
|
|
export async function requireAuth(
|
|
request: Request,
|
|
authLevel: AuthLevel,
|
|
options: RouteProtectionOptions = {}
|
|
): Promise<SafeUser> {
|
|
return requireAuthLevel(request, authLevel, options);
|
|
}
|
|
|
|
// Middleware for redirecting authenticated users away from auth pages
|
|
export async function redirectIfAuthenticated(
|
|
request: Request,
|
|
redirectTo: string = "/dashboard"
|
|
) {
|
|
const user = await getUser(request);
|
|
if (user && user.status === USER_STATUS.ACTIVE) {
|
|
throw redirect(redirectTo);
|
|
}
|
|
}
|
|
|
|
// Middleware for optional authentication (user may or may not be logged in)
|
|
export async function getOptionalUser(request: Request): Promise<SafeUser | null> {
|
|
try {
|
|
const user = await getUser(request);
|
|
return user && user.status === USER_STATUS.ACTIVE ? user : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Session validation middleware
|
|
export async function validateSession(request: Request): Promise<{
|
|
isValid: boolean;
|
|
user: SafeUser | null;
|
|
error?: string;
|
|
}> {
|
|
try {
|
|
const user = await getUser(request);
|
|
if (!user) {
|
|
return {
|
|
isValid: false,
|
|
user: null,
|
|
error: "no_user",
|
|
};
|
|
}
|
|
|
|
if (user.status !== USER_STATUS.ACTIVE) {
|
|
return {
|
|
isValid: false,
|
|
user: null,
|
|
error: "inactive_user",
|
|
};
|
|
}
|
|
|
|
return {
|
|
isValid: true,
|
|
user,
|
|
};
|
|
} catch {
|
|
return {
|
|
isValid: false,
|
|
user: null,
|
|
error: "session_error",
|
|
};
|
|
}
|
|
}
|
|
|
|
// Route-specific protection functions
|
|
export async function protectUserManagementRoute(request: Request): Promise<SafeUser> {
|
|
const user = await requireAdmin(request);
|
|
|
|
// Additional business logic: ensure user can manage users
|
|
if (user.authLevel > AUTH_LEVELS.ADMIN) {
|
|
throw redirect("/dashboard?error=insufficient_permissions");
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
export async function protectFinancialRoute(request: Request): Promise<SafeUser> {
|
|
// Financial routes require at least admin level
|
|
return requireAdmin(request);
|
|
}
|
|
|
|
export async function protectCustomerRoute(request: Request): Promise<SafeUser> {
|
|
// Customer routes require authentication but any auth level can access
|
|
return requireAuthentication(request);
|
|
}
|
|
|
|
export async function protectVehicleRoute(request: Request): Promise<SafeUser> {
|
|
// Vehicle routes require authentication but any auth level can access
|
|
return requireAuthentication(request);
|
|
}
|
|
|
|
export async function protectMaintenanceRoute(request: Request): Promise<SafeUser> {
|
|
// Maintenance routes require authentication but any auth level can access
|
|
return requireAuthentication(request);
|
|
}
|
|
|
|
// Utility function to check specific permissions
|
|
export function checkPermission(
|
|
user: SafeUser,
|
|
permission: 'view_all_users' | 'create_users' | 'manage_finances' | 'view_reports'
|
|
): boolean {
|
|
switch (permission) {
|
|
case 'view_all_users':
|
|
return user.authLevel === AUTH_LEVELS.SUPERADMIN;
|
|
case 'create_users':
|
|
return user.authLevel <= AUTH_LEVELS.ADMIN;
|
|
case 'manage_finances':
|
|
return user.authLevel <= AUTH_LEVELS.ADMIN;
|
|
case 'view_reports':
|
|
return user.authLevel <= AUTH_LEVELS.ADMIN;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Error handling for unauthorized access
|
|
export function createUnauthorizedResponse(message?: string) {
|
|
const errorMessage = message || AUTH_ERRORS.INSUFFICIENT_PERMISSIONS;
|
|
return new Response(errorMessage, {
|
|
status: 403,
|
|
headers: {
|
|
'Content-Type': 'text/plain; charset=utf-8'
|
|
}
|
|
});
|
|
} |