2.4 페이지/컴포넌트 가드 훅 구현(usePermission)

This commit is contained in:
koreacomp5
2025-10-09 14:46:16 +09:00
parent c34a814d28
commit 03924c45b0
5 changed files with 85 additions and 3 deletions

11
src/lib/auth.ts Normal file
View File

@@ -0,0 +1,11 @@
// 간이 인증 헬퍼: 실제 세션/쿠키 연동 전 임시로 헤더에서 userId를 읽어옵니다.
export function getUserIdFromRequest(req: Request): string | null {
try {
const id = req.headers.get("x-user-id");
return id && id.length > 0 ? id : null;
} catch {
return null;
}
}

55
src/lib/rbac.ts Normal file
View File

@@ -0,0 +1,55 @@
import prisma from "@/lib/prisma";
// 권한 확인: userId가 주어지면 역할 → 권한 매핑(RolePermission)으로 검사
export async function checkPermission(options: {
userId: string | null | undefined;
resource: "BOARD" | "POST" | "COMMENT" | "USER" | "ADMIN";
action: "READ" | "CREATE" | "UPDATE" | "DELETE" | "MODERATE" | "ADMINISTER";
}): Promise<boolean> {
const { userId, resource, action } = options;
if (!userId) return false;
const userRoles = await prisma.userRole.findMany({
where: { userId },
select: { roleId: true },
});
if (userRoles.length === 0) return false;
const roleIds = userRoles.map((r) => r.roleId);
const has = await prisma.rolePermission.findFirst({
where: {
roleId: { in: roleIds },
resource,
action,
allowed: true,
},
select: { id: true },
});
if (has) return true;
// ADMIN.ADMINISTER 이면 모든 리소스/액션 허용
const isAdmin = await prisma.rolePermission.findFirst({
where: {
roleId: { in: roleIds },
resource: "ADMIN",
action: "ADMINISTER",
allowed: true,
},
select: { id: true },
});
return !!isAdmin;
}
export async function requirePermission(options: {
userId: string | null | undefined;
resource: "BOARD" | "POST" | "COMMENT" | "USER" | "ADMIN";
action: "READ" | "CREATE" | "UPDATE" | "DELETE" | "MODERATE" | "ADMINISTER";
}): Promise<void> {
const ok = await checkPermission(options);
if (!ok) {
const err = new Error("Forbidden");
// @ts-expect-error attach status for route handlers
err.status = 403;
throw err;
}
}