2.4 페이지/컴포넌트 가드 훅 구현(usePermission)
This commit is contained in:
11
src/lib/auth.ts
Normal file
11
src/lib/auth.ts
Normal 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
55
src/lib/rbac.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user