diff options
Diffstat (limited to 'lib/permissions')
| -rw-r--r-- | lib/permissions/permission-assignment-actions.ts | 83 | ||||
| -rw-r--r-- | lib/permissions/permission-group-actions.ts | 270 | ||||
| -rw-r--r-- | lib/permissions/permission-settings-actions.ts | 229 | ||||
| -rw-r--r-- | lib/permissions/service.ts | 434 |
4 files changed, 1016 insertions, 0 deletions
diff --git a/lib/permissions/permission-assignment-actions.ts b/lib/permissions/permission-assignment-actions.ts new file mode 100644 index 00000000..75181c40 --- /dev/null +++ b/lib/permissions/permission-assignment-actions.ts @@ -0,0 +1,83 @@ +// app/actions/permission-assignment-actions.ts + +"use server"; + +import db from "@/db/db"; +import { eq, and ,sql} from "drizzle-orm"; +import { + permissions, + roles, + rolePermissions, + users, + userPermissions, + userRoles +} from "@/db/schema"; + +// 권한별 할당 정보 조회 +export async function getPermissionAssignments(permissionId?: number) { + if (!permissionId) { + // 모든 권한 목록 + const allPermissions = await db.select().from(permissions) + .where(eq(permissions.isActive, true)) + .orderBy(permissions.resource, permissions.name); + + return { permissions: allPermissions, roles: [], users: [] }; + } + + // 특정 권한의 할당 정보 + const assignedRoles = await db + .select({ + id: roles.id, + name: roles.name, + domain: roles.domain, + userCount: sql<number>`count(distinct ${userRoles.userId})`.mapWith(Number), + }) + .from(rolePermissions) + .innerJoin(roles, eq(roles.id, rolePermissions.roleId)) + .leftJoin(userRoles, eq(userRoles.roleId, roles.id)) + .where(eq(rolePermissions.permissionId, permissionId)) + .groupBy(roles.id); + + const assignedUsers = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + imageUrl: users.imageUrl, + domain: users.domain, + isGrant: userPermissions.isGrant, + reason: userPermissions.reason, + }) + .from(userPermissions) + .innerJoin(users, eq(users.id, userPermissions.userId)) + .where(eq(userPermissions.permissionId, permissionId)); + + return { + permissions: [], + roles: assignedRoles, + users: assignedUsers, + }; +} + +// 역할에서 권한 제거 +export async function removePermissionFromRole(permissionId: number, roleId: number) { + await db.delete(rolePermissions) + .where( + and( + eq(rolePermissions.permissionId, permissionId), + eq(rolePermissions.roleId, roleId) + ) + ); +} + +// 사용자에서 권한 제거 +export async function removePermissionFromUser(permissionId: number, userId: number) { + await db.update(userPermissions) + .set({ isActive: false }) + .where( + and( + eq(userPermissions.permissionId, permissionId), + eq(userPermissions.userId, userId) + ) + ); +}
\ No newline at end of file diff --git a/lib/permissions/permission-group-actions.ts b/lib/permissions/permission-group-actions.ts new file mode 100644 index 00000000..51e3c2c0 --- /dev/null +++ b/lib/permissions/permission-group-actions.ts @@ -0,0 +1,270 @@ +// app/actions/permission-group-actions.ts + +"use server"; + +import db from "@/db/db"; +import { eq, and, inArray, sql } from "drizzle-orm"; +import { + permissionGroups, + permissionGroupMembers, + permissions, + rolePermissions, + userPermissions, + roles, + users +} from "@/db/schema"; +import { checkUserPermission } from "./service"; +import { getServerSession } from "next-auth/next" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" + +// 권한 그룹 목록 조회 +export async function getPermissionGroups() { + const groups = await db + .select({ + id: permissionGroups.id, + groupKey: permissionGroups.groupKey, + name: permissionGroups.name, + description: permissionGroups.description, + domain: permissionGroups.domain, + isActive: permissionGroups.isActive, + createdAt: permissionGroups.createdAt, + updatedAt: permissionGroups.updatedAt, + permissionCount: sql<number>`count(distinct ${permissionGroupMembers.permissionId})`.mapWith(Number), + }) + .from(permissionGroups) + .leftJoin(permissionGroupMembers, eq(permissionGroupMembers.groupId, permissionGroups.id)) + .groupBy(permissionGroups.id) + .orderBy(permissionGroups.name); + + // 각 그룹의 역할 및 사용자 수 계산 + const groupsWithCounts = await Promise.all( + groups.map(async (group) => { + const roleCount = await db + .selectDistinct({ roleId: rolePermissions.roleId }) + .from(rolePermissions) + .where(eq(rolePermissions.permissionGroupId, group.id)); + + const userCount = await db + .selectDistinct({ userId: userPermissions.userId }) + .from(userPermissions) + .where(eq(userPermissions.permissionGroupId, group.id)); + + return { + ...group, + roleCount: roleCount.length, + userCount: userCount.length, + }; + }) + ); + + return groupsWithCounts; +} + +// 권한 그룹 생성 +export async function createPermissionGroup(data: { + groupKey: string; + name: string; + description?: string; + domain?: string; + isActive: boolean; +}) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + // 중복 체크 + const existing = await db.select() + .from(permissionGroups) + .where(eq(permissionGroups.groupKey, data.groupKey)) + .limit(1); + + if (existing.length > 0) { + throw new Error("이미 존재하는 그룹 키입니다."); + } + + const [created] = await db.insert(permissionGroups).values(data).returning(); + return created; +} + +// 권한 그룹 수정 +export async function updatePermissionGroup(id: number, data: any) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + const [updated] = await db.update(permissionGroups) + .set({ + ...data, + updatedAt: new Date(), + }) + .where(eq(permissionGroups.id, id)) + .returning(); + + return updated; +} + +// 권한 그룹 삭제 +export async function deletePermissionGroup(id: number) { + const currentUser = await getCurrentUser(); + if (!currentUser) throw new Error("Unauthorized"); + + if (!await checkUserPermission(currentUser.id, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + await db.transaction(async (tx) => { + // 그룹 멤버 삭제 + await tx.delete(permissionGroupMembers) + .where(eq(permissionGroupMembers.groupId, id)); + + // 그룹 삭제 + await tx.delete(permissionGroups) + .where(eq(permissionGroups.id, id)); + }); +} + +// 그룹의 권한 조회 +export async function getGroupPermissions(groupId: number) { + const groupPermissions = await db + .select({ + id: permissions.id, + permissionKey: permissions.permissionKey, + name: permissions.name, + description: permissions.description, + resource: permissions.resource, + action: permissions.action, + permissionType: permissions.permissionType, + scope: permissions.scope, + }) + .from(permissionGroupMembers) + .innerJoin(permissions, eq(permissions.id, permissionGroupMembers.permissionId)) + .where(eq(permissionGroupMembers.groupId, groupId)); + + const allPermissions = await db.select().from(permissions) + .where(eq(permissions.isActive, true)); + + return { + permissions: groupPermissions, + availablePermissions: allPermissions, + }; +} + +// 그룹 권한 업데이트 +export async function updateGroupPermissions(groupId: number, permissionIds: number[]) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + await db.transaction(async (tx) => { + // 기존 권한 삭제 + await tx.delete(permissionGroupMembers) + .where(eq(permissionGroupMembers.groupId, groupId)); + + // 새 권한 추가 + if (permissionIds.length > 0) { + await tx.insert(permissionGroupMembers).values( + permissionIds.map(permissionId => ({ + groupId, + permissionId, + })) + ); + } + }); +} + +// 권한 그룹 복제 +export async function clonePermissionGroup(groupId: number) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + // 원본 그룹 조회 + const [originalGroup] = await db.select() + .from(permissionGroups) + .where(eq(permissionGroups.id, groupId)); + + if (!originalGroup) { + throw new Error("그룹을 찾을 수 없습니다."); + } + + // 원본 그룹의 권한 조회 + const originalPermissions = await db.select() + .from(permissionGroupMembers) + .where(eq(permissionGroupMembers.groupId, groupId)); + + // 새 그룹 생성 + const timestamp = Date.now(); + const [newGroup] = await db.insert(permissionGroups).values({ + groupKey: `${originalGroup.groupKey}_copy_${timestamp}`, + name: `${originalGroup.name} (복사본)`, + description: originalGroup.description, + domain: originalGroup.domain, + isActive: originalGroup.isActive, + }).returning(); + + // 권한 복사 + if (originalPermissions.length > 0) { + await db.insert(permissionGroupMembers).values( + originalPermissions.map(p => ({ + groupId: newGroup.id, + permissionId: p.permissionId, + })) + ); + } + + return newGroup; +} + +// 그룹 할당 정보 조회 +export async function getGroupAssignments(groupId: number) { + const assignedRoles = await db + .select({ + id: roles.id, + name: roles.name, + domain: roles.domain, + }) + .from(rolePermissions) + .innerJoin(roles, eq(roles.id, rolePermissions.roleId)) + .where(eq(rolePermissions.permissionGroupId, groupId)); + + const assignedUsers = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + imageUrl: users.imageUrl, + domain: users.domain, + }) + .from(userPermissions) + .innerJoin(users, eq(users.id, userPermissions.userId)) + .where(eq(userPermissions.permissionGroupId, groupId)); + + return { + roles: assignedRoles, + users: assignedUsers, + }; +}
\ No newline at end of file diff --git a/lib/permissions/permission-settings-actions.ts b/lib/permissions/permission-settings-actions.ts new file mode 100644 index 00000000..5d04a1d3 --- /dev/null +++ b/lib/permissions/permission-settings-actions.ts @@ -0,0 +1,229 @@ +// app/actions/permission-settings-actions.ts + +"use server"; + +import db from "@/db/db"; +import { eq, and, inArray, sql } from "drizzle-orm"; +import { + permissions, + menuAssignments, + menuRequiredPermissions +} from "@/db/schema"; +import { getServerSession } from "next-auth/next" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import { checkUserPermission } from "./service"; + +// 모든 권한 조회 +export async function getAllPermissions() { + return await db.select().from(permissions).orderBy(permissions.resource, permissions.action); +} + +// 권한 카테고리 조회 +export async function getPermissionCategories() { + const result = await db + .select({ + resource: permissions.resource, + count: sql<number>`count(*)`.mapWith(Number), + }) + .from(permissions) + .groupBy(permissions.resource) + .orderBy(permissions.resource); + + return result; +} + +// 권한 생성 +export async function createPermission(data: { + permissionKey: string; + name: string; + description?: string; + permissionType: string; + resource: string; + action: string; + scope: string; + menuPath?: string; + uiElement?: string; + isActive: boolean; +}) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + // 중복 체크 + const existing = await db.select() + .from(permissions) + .where(eq(permissions.permissionKey, data.permissionKey)) + .limit(1); + + if (existing.length > 0) { + throw new Error("이미 존재하는 권한 키입니다."); + } + + const [created] = await db.insert(permissions).values({ + ...data, + isSystem: false, + }).returning(); + + return created; +} + +// 권한 수정 +export async function updatePermission(id: number, data: any) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + const [updated] = await db.update(permissions) + .set({ + ...data, + updatedAt: new Date(), + }) + .where(eq(permissions.id, id)) + .returning(); + + return updated; +} + +// 권한 삭제 +export async function deletePermission(id: number) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + await db.delete(permissions).where(eq(permissions.id, id)); +} + +// 메뉴 권한 분석 +export async function analyzeMenuPermissions() { + const menus = await db.select().from(menuAssignments); + + const analysis = await Promise.all( + menus.map(async (menu) => { + // 기존 권한 조회 + const existing = await db + .select({ + id: permissions.id, + permissionKey: permissions.permissionKey, + name: permissions.name, + }) + .from(menuRequiredPermissions) + .innerJoin(permissions, eq(permissions.id, menuRequiredPermissions.permissionId)) + .where(eq(menuRequiredPermissions.menuPath, menu.menuPath)); + + // 제안할 권한 생성 + const suggestedPermissions = []; + const resourceName = menu.menuPath.split('/').pop() || 'unknown'; + + // 기본 메뉴 접근 권한 + suggestedPermissions.push({ + permissionKey: `${resourceName}.menu_access`, + name: `${menu.menuTitle} 접근`, + permissionType: "menu_access", + action: "access", + scope: "assigned", + }); + + // CRUD 권한 제안 + const actions = [ + { action: "view", name: "조회", type: "data_read" }, + { action: "create", name: "생성", type: "data_write" }, + { action: "update", name: "수정", type: "data_write" }, + { action: "delete", name: "삭제", type: "data_delete" }, + ]; + + actions.forEach(({ action, name, type }) => { + suggestedPermissions.push({ + permissionKey: `${resourceName}.${action}`, + name: `${menu.menuTitle} ${name}`, + permissionType: type, + action, + scope: "assigned", + }); + }); + + return { + menuPath: menu.menuPath, + menuTitle: menu.menuTitle, + domain: menu.domain, + existingPermissions: existing, + suggestedPermissions: suggestedPermissions.filter( + sp => !existing.some(ep => ep.permissionKey === sp.permissionKey) + ), + }; + }) + ); + + return analysis; +} + +// 메뉴 기반 권한 생성 +export async function generateMenuPermissions( + permissionsToCreate: Array<{ + permissionKey: string; + name: string; + permissionType: string; + action: string; + scope: string; + menuPath: string; + }> +) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + let created = 0; + let skipped = 0; + + await db.transaction(async (tx) => { + for (const perm of permissionsToCreate) { + // 중복 체크 + const existing = await tx.select() + .from(permissions) + .where(eq(permissions.permissionKey, perm.permissionKey)) + .limit(1); + + if (existing.length === 0) { + const resource = perm.menuPath.split('/').pop() || 'unknown'; + + await tx.insert(permissions).values({ + permissionKey: perm.permissionKey, + name: perm.name, + permissionType: perm.permissionType, + resource, + action: perm.action, + scope: perm.scope, + menuPath: perm.menuPath, + isSystem: false, + isActive: true, + }); + created++; + } else { + skipped++; + } + } + }); + + return { created, skipped }; +}
\ No newline at end of file diff --git a/lib/permissions/service.ts b/lib/permissions/service.ts new file mode 100644 index 00000000..3ef1ff04 --- /dev/null +++ b/lib/permissions/service.ts @@ -0,0 +1,434 @@ +// lib/permission/servicee.ts + +"use server"; + +import db from "@/db/db"; +import { eq, and, inArray, or, ilike } from "drizzle-orm"; +import { + permissions, + rolePermissions, + userPermissions, + permissionAuditLogs, + userRoles, + menuAssignments, + menuRequiredPermissions, + users, + vendors, + roles, +} from "@/db/schema"; +import { getServerSession } from "next-auth/next" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" + +// 역할에 권한 할당 +export async function assignPermissionsToRole( + roleId: number, + permissionIds: number[] +) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + + const currentUserId = Number(session.user.id) + + // 권한 체크 + if (!await checkUserPermission(currentUserId, "admin.permissions.manage")) { + throw new Error("권한 관리 권한이 없습니다."); + } + + await db.transaction(async (tx) => { + // 기존 권한 삭제 + await tx.delete(rolePermissions) + .where(eq(rolePermissions.roleId, roleId)); + + // 새 권한 추가 + if (permissionIds.length > 0) { + await tx.insert(rolePermissions).values( + permissionIds.map(permissionId => ({ + roleId, + permissionId, + grantedBy: currentUserId, + })) + ); + + // 감사 로그 + await tx.insert(permissionAuditLogs).values( + permissionIds.map(permissionId => ({ + targetType: "role", + targetId: roleId, + permissionId, + action: "grant", + performedBy: currentUserId, + reason: "역할 권한 일괄 업데이트", + })) + ); + } + }); + + return { success: true }; +} + + +// 역할의 권한 목록 조회 +export async function getRolePermissions(roleId: number) { + const allPermissions = await db.select().from(permissions) + .where(eq(permissions.isActive, true)); + + const rolePerms = await db.select({ + permissionId: rolePermissions.permissionId, + }) + .from(rolePermissions) + .where(eq(rolePermissions.roleId, roleId)); + + return { + permissions: allPermissions, + assignedPermissionIds: rolePerms.map(rp => rp.permissionId), + }; +} + +// 권한 체크 함수 +export async function checkUserPermission( + userId: number, + permissionKey: string +): Promise<boolean> { + // 역할 기반 권한 + const roleBasedPerms = await db + .selectDistinct({ permissionKey: permissions.permissionKey }) + .from(userRoles) + .innerJoin(rolePermissions, eq(rolePermissions.roleId, userRoles.roleId)) + .innerJoin(permissions, eq(permissions.id, rolePermissions.permissionId)) + .where( + and( + eq(userRoles.userId, userId), + eq(permissions.permissionKey, permissionKey), + eq(permissions.isActive, true), + eq(rolePermissions.isActive, true) + ) + ); + + if (roleBasedPerms.length > 0) return true; + + // 사용자 직접 권한 + const directPerms = await db + .selectDistinct({ permissionKey: permissions.permissionKey }) + .from(userPermissions) + .innerJoin(permissions, eq(permissions.id, userPermissions.permissionId)) + .where( + and( + eq(userPermissions.userId, userId), + eq(permissions.permissionKey, permissionKey), + eq(permissions.isActive, true), + eq(userPermissions.isActive, true), + eq(userPermissions.isGrant, true) // 부여된 권한만 + ) + ); + + return directPerms.length > 0; +} + +// 메뉴 접근 권한 체크 +export async function checkMenuAccess( + userId: number, + menuPath: string +): Promise<boolean> { + // 메뉴 담당자인 경우 자동 허용 + const isManager = await db + .selectDistinct({ id: menuAssignments.id }) + .from(menuAssignments) + .where( + and( + eq(menuAssignments.menuPath, menuPath), + or( + eq(menuAssignments.manager1Id, userId), + eq(menuAssignments.manager2Id, userId) + ) + ) + ); + + if (isManager.length > 0) return true; + + // 메뉴 필수 권한 체크 + const requiredPerms = await db + .select({ permissionKey: permissions.permissionKey }) + .from(menuRequiredPermissions) + .innerJoin(permissions, eq(permissions.id, menuRequiredPermissions.permissionId)) + .where( + and( + eq(menuRequiredPermissions.menuPath, menuPath), + eq(menuRequiredPermissions.isRequired, true) + ) + ); + + if (requiredPerms.length === 0) return true; // 필수 권한이 없으면 모두 접근 가능 + + // 사용자가 필수 권한을 모두 가지고 있는지 확인 + for (const perm of requiredPerms) { + if (!await checkUserPermission(userId, perm.permissionKey)) { + return false; + } + } + + return true; +} + + +export async function searchUsers(query: string) { + const usersData = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + imageUrl: users.imageUrl, + domain: users.domain, + companyName: vendors.vendorName, + }) + .from(users) + .leftJoin(vendors, eq(vendors.id, users.companyId)) + .where( + or( + ilike(users.name, `%${query}%`), + ilike(users.email, `%${query}%`) + ) + ) + .limit(20); + + // 각 사용자의 역할 조회 + const usersWithRoles = await Promise.all( + usersData.map(async (user) => { + const userRolesData = await db + .select({ + id: roles.id, + name: roles.name, + }) + .from(userRoles) + .innerJoin(roles, eq(roles.id, userRoles.roleId)) + .where(eq(userRoles.userId, user.id)); + + return { + ...user, + roles: userRolesData, + }; + }) + ); + + return usersWithRoles; +} + +export async function getUserPermissionDetails(userId: number) { + // 역할 기반 권한 + const rolePermissionsData = await db + .select({ + id: permissions.id, + permissionKey: permissions.permissionKey, + name: permissions.name, + description: permissions.description, + permissionType: permissions.permissionType, + resource: permissions.resource, + action: permissions.action, + scope: permissions.scope, + menuPath: permissions.menuPath, + roleName: roles.name, + }) + .from(userRoles) + .innerJoin(roles, eq(roles.id, userRoles.roleId)) + .innerJoin(rolePermissions, eq(rolePermissions.roleId, roles.id)) + .innerJoin(permissions, eq(permissions.id, rolePermissions.permissionId)) + .where(eq(userRoles.userId, userId)); + + // 직접 부여된 권한 + const directPermissions = await db + .select({ + id: permissions.id, + permissionKey: permissions.permissionKey, + name: permissions.name, + description: permissions.description, + permissionType: permissions.permissionType, + resource: permissions.resource, + action: permissions.action, + scope: permissions.scope, + menuPath: permissions.menuPath, + isGrant: userPermissions.isGrant, + grantedBy: users.name, + grantedAt: userPermissions.grantedAt, + expiresAt: userPermissions.expiresAt, + reason: userPermissions.reason, + }) + .from(userPermissions) + .innerJoin(permissions, eq(permissions.id, userPermissions.permissionId)) + .leftJoin(users, eq(users.id, userPermissions.grantedBy)) + .where(eq(userPermissions.userId, userId)); + + // 모든 권한 목록 + const allPermissions = await db.select().from(permissions); + + return { + permissions: [ + ...rolePermissionsData.map(p => ({ ...p, source: "role" as const })), + ...directPermissions.map(p => ({ ...p, source: "direct" as const })), + ], + availablePermissions: allPermissions, + }; +} + +export async function grantPermissionToUser(params: { + userId: number; + permissionIds: number[]; + isGrant: boolean; + reason?: string; + expiresAt?: Date; +}) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + const currentUserId = Number(session.user.id) + + await db.transaction(async (tx) => { + for (const permissionId of params.permissionIds) { + await tx.insert(userPermissions).values({ + userId: params.userId, + permissionId, + isGrant: params.isGrant, + grantedBy: Number(session.user.id), + reason: params.reason, + expiresAt: params.expiresAt, + }).onConflictDoUpdate({ + target: [userPermissions.userId, userPermissions.permissionId], + set: { + isGrant: params.isGrant, + grantedBy: Number(session.user.id), + grantedAt: new Date(), + reason: params.reason, + expiresAt: params.expiresAt, + isActive: true, + } + }); + + // 감사 로그 + await tx.insert(permissionAuditLogs).values({ + targetType: "user", + targetId: params.userId, + permissionId, + action: params.isGrant ? "grant" : "restrict", + performedBy: currentUserId, + reason: params.reason, + }); + } + }); +} + +export async function revokePermissionFromUser(userId: number, permissionId: number) { + const session = await getServerSession(authOptions) + if (!session?.user?.id) { + throw new Error("인증이 필요합니다.") + } + + await db.transaction(async (tx) => { + await tx.update(userPermissions) + .set({ isActive: false }) + .where( + and( + eq(userPermissions.userId, userId), + eq(userPermissions.permissionId, permissionId) + ) + ); + + // 감사 로그 + await tx.insert(permissionAuditLogs).values({ + targetType: "user", + targetId: userId, + permissionId, + action: "revoke", + performedBy: Number(session.user.id), + }); + }); +} + + +export async function getMenuPermissions(domain: string = "all") { + const menus = await db + .select({ + menuPath: menuAssignments.menuPath, + menuTitle: menuAssignments.menuTitle, + menuDescription: menuAssignments.menuDescription, + sectionTitle: menuAssignments.sectionTitle, + menuGroup: menuAssignments.menuGroup, + domain: menuAssignments.domain, + isActive: menuAssignments.isActive, + manager1Id: menuAssignments.manager1Id, + manager2Id: menuAssignments.manager2Id, + }) + .from(menuAssignments) + .where(domain === "all" ? undefined : eq(menuAssignments.domain, domain)); + + // 각 메뉴의 권한과 담당자 정보 조회 + const menusWithDetails = await Promise.all( + menus.map(async (menu) => { + // 필수 권한 조회 + const requiredPerms = await db + .select({ + id: permissions.id, + permissionKey: permissions.permissionKey, + name: permissions.name, + description: permissions.description, + isRequired: menuRequiredPermissions.isRequired, + }) + .from(menuRequiredPermissions) + .innerJoin(permissions, eq(permissions.id, menuRequiredPermissions.permissionId)) + .where(eq(menuRequiredPermissions.menuPath, menu.menuPath)); + + // 담당자 정보 조회 + const [manager1, manager2] = await Promise.all([ + menu.manager1Id ? db.select({ + id: users.id, + name: users.name, + email: users.email, + imageUrl: users.imageUrl, + }).from(users).where(eq(users.id, menu.manager1Id)).then(r => r[0]) : null, + menu.manager2Id ? db.select({ + id: users.id, + name: users.name, + email: users.email, + imageUrl: users.imageUrl, + }).from(users).where(eq(users.id, menu.manager2Id)).then(r => r[0]) : null, + ]); + + return { + ...menu, + requiredPermissions: requiredPerms, + manager1, + manager2, + }; + }) + ); + + // 사용 가능한 모든 권한 목록 + const availablePermissions = await db.select().from(permissions); + + return { + menus: menusWithDetails, + availablePermissions, + }; +} + +export async function updateMenuPermissions( + menuPath: string, + permissions: Array<{ id: number; isRequired: boolean }> +) { + await db.transaction(async (tx) => { + // 기존 권한 삭제 + await tx.delete(menuRequiredPermissions) + .where(eq(menuRequiredPermissions.menuPath, menuPath)); + + // 새 권한 추가 + if (permissions.length > 0) { + await tx.insert(menuRequiredPermissions).values( + permissions.map(p => ({ + menuPath, + permissionId: p.id, + isRequired: p.isRequired, + })) + ); + } + }); +}
\ No newline at end of file |
