회사 관리자 메뉴 권한 필터링 적용

This commit is contained in:
dohyeons 2025-12-11 13:15:09 +09:00
parent c4e81dd740
commit f6e0e02ddf
2 changed files with 56 additions and 34 deletions

View File

@ -19,15 +19,21 @@ export class AdminService {
// menuType에 따른 WHERE 조건 생성 // menuType에 따른 WHERE 조건 생성
const menuTypeCondition = const menuTypeCondition =
menuType !== undefined ? `MENU.MENU_TYPE = ${parseInt(menuType)}` : "1 = 1"; menuType !== undefined
? `MENU.MENU_TYPE = ${parseInt(menuType)}`
: "1 = 1";
// 메뉴 관리 화면인지 좌측 사이드바인지 구분 // 메뉴 관리 화면인지 좌측 사이드바인지 구분
// includeInactive가 true거나 menuType이 undefined면 메뉴 관리 화면 // includeInactive가 true거나 menuType이 undefined면 메뉴 관리 화면
const includeInactive = paramMap.includeInactive === true; const includeInactive = paramMap.includeInactive === true;
const isManagementScreen = includeInactive || menuType === undefined; const isManagementScreen = includeInactive || menuType === undefined;
// 메뉴 관리 화면: 모든 상태의 메뉴 표시, 좌측 사이드바: active만 표시 // 메뉴 관리 화면: 모든 상태의 메뉴 표시, 좌측 사이드바: active만 표시
const statusCondition = isManagementScreen ? "1 = 1" : "MENU.STATUS = 'active'"; const statusCondition = isManagementScreen
const subStatusCondition = isManagementScreen ? "1 = 1" : "MENU_SUB.STATUS = 'active'"; ? "1 = 1"
: "MENU.STATUS = 'active'";
const subStatusCondition = isManagementScreen
? "1 = 1"
: "MENU_SUB.STATUS = 'active'";
// 1. 권한 그룹 기반 필터링 (좌측 사이드바인 경우만) // 1. 권한 그룹 기반 필터링 (좌측 사이드바인 경우만)
let authFilter = ""; let authFilter = "";
@ -35,7 +41,11 @@ export class AdminService {
let queryParams: any[] = [userLang]; let queryParams: any[] = [userLang];
let paramIndex = 2; let paramIndex = 2;
if (menuType !== undefined && userType !== "SUPER_ADMIN" && !isManagementScreen) { if (
menuType !== undefined &&
userType !== "SUPER_ADMIN" &&
!isManagementScreen
) {
// 좌측 사이드바 + SUPER_ADMIN이 아닌 경우만 권한 체크 // 좌측 사이드바 + SUPER_ADMIN이 아닌 경우만 권한 체크
const userRoleGroups = await query<any>( const userRoleGroups = await query<any>(
` `
@ -56,45 +66,45 @@ export class AdminService {
); );
if (userType === "COMPANY_ADMIN") { if (userType === "COMPANY_ADMIN") {
// 회사 관리자: 자기 회사 메뉴는 모두, 공통 메뉴는 권한 있는 것만 // 회사 관리자: 권한 그룹 기반 필터링 적용
if (userRoleGroups.length > 0) { if (userRoleGroups.length > 0) {
const roleObjids = userRoleGroups.map((rg: any) => rg.role_objid); const roleObjids = userRoleGroups.map((rg: any) => rg.role_objid);
// 루트 메뉴: 회사 코드만 체크 (권한 체크 X) // 회사 관리자도 권한 그룹 설정에 따라 메뉴 필터링
authFilter = `AND MENU.COMPANY_CODE IN ($${paramIndex}, '*')`; authFilter = `
AND MENU.COMPANY_CODE IN ($${paramIndex}, '*')
AND EXISTS (
SELECT 1
FROM rel_menu_auth rma
WHERE rma.menu_objid = MENU.OBJID
AND rma.auth_objid = ANY($${paramIndex + 1})
AND rma.read_yn = 'Y'
)
`;
queryParams.push(userCompanyCode); queryParams.push(userCompanyCode);
const companyParamIndex = paramIndex;
paramIndex++; paramIndex++;
// 하위 메뉴: 회사 메뉴는 모두, 공통 메뉴는 권한 체크 // 하위 메뉴 권한 체크
unionFilter = ` unionFilter = `
AND ( AND MENU_SUB.COMPANY_CODE IN ($${paramIndex - 1}, '*')
MENU_SUB.COMPANY_CODE = $${companyParamIndex} AND EXISTS (
OR ( SELECT 1
MENU_SUB.COMPANY_CODE = '*' FROM rel_menu_auth rma
AND EXISTS ( WHERE rma.menu_objid = MENU_SUB.OBJID
SELECT 1 AND rma.auth_objid = ANY($${paramIndex})
FROM rel_menu_auth rma AND rma.read_yn = 'Y'
WHERE rma.menu_objid = MENU_SUB.OBJID
AND rma.auth_objid = ANY($${paramIndex})
AND rma.read_yn = 'Y'
)
)
) )
`; `;
queryParams.push(roleObjids); queryParams.push(roleObjids);
paramIndex++; paramIndex++;
logger.info( logger.info(
`✅ 회사 관리자: 회사 ${userCompanyCode} 메뉴 전체 + 권한 있는 공통 메뉴` `✅ 회사 관리자: 권한 있는 메뉴만 (${roleObjids.length}개 그룹)`
); );
} else { } else {
// 권한 그룹이 없는 회사 관리자: 자기 회사 메뉴만 // 권한 그룹이 없는 회사 관리자: 메뉴 없음
authFilter = `AND MENU.COMPANY_CODE = $${paramIndex}`; logger.warn(
unionFilter = `AND MENU_SUB.COMPANY_CODE = $${paramIndex}`; `⚠️ 회사 관리자 ${userId}는 권한 그룹이 없어 메뉴가 표시되지 않습니다.`
queryParams.push(userCompanyCode);
paramIndex++;
logger.info(
`✅ 회사 관리자 (권한 그룹 없음): 회사 ${userCompanyCode} 메뉴만`
); );
return [];
} }
} else { } else {
// 일반 사용자: 권한 그룹 필수 // 일반 사용자: 권한 그룹 필수
@ -131,7 +141,11 @@ export class AdminService {
return []; return [];
} }
} }
} else if (menuType !== undefined && userType === "SUPER_ADMIN" && !isManagementScreen) { } else if (
menuType !== undefined &&
userType === "SUPER_ADMIN" &&
!isManagementScreen
) {
// 좌측 사이드바 + SUPER_ADMIN: 권한 그룹 체크 없이 모든 공통 메뉴 표시 // 좌측 사이드바 + SUPER_ADMIN: 권한 그룹 체크 없이 모든 공통 메뉴 표시
logger.info(`✅ 최고 관리자는 권한 그룹 체크 없이 모든 공통 메뉴 표시`); logger.info(`✅ 최고 관리자는 권한 그룹 체크 없이 모든 공통 메뉴 표시`);
// unionFilter는 비워둠 (하위 메뉴도 공통 메뉴만) // unionFilter는 비워둠 (하위 메뉴도 공통 메뉴만)
@ -167,7 +181,7 @@ export class AdminService {
companyFilter = `AND MENU.COMPANY_CODE = $${paramIndex}`; companyFilter = `AND MENU.COMPANY_CODE = $${paramIndex}`;
queryParams.push(userCompanyCode); queryParams.push(userCompanyCode);
paramIndex++; paramIndex++;
// 하위 메뉴에도 회사 필터링 적용 (공통 메뉴 제외) // 하위 메뉴에도 회사 필터링 적용 (공통 메뉴 제외)
if (unionFilter === "") { if (unionFilter === "") {
unionFilter = `AND MENU_SUB.COMPANY_CODE = $${paramIndex - 1}`; unionFilter = `AND MENU_SUB.COMPANY_CODE = $${paramIndex - 1}`;

View File

@ -9,6 +9,7 @@ import { useRouter } from "next/navigation";
import { AlertCircle } from "lucide-react"; import { AlertCircle } from "lucide-react";
import { DualListBox } from "@/components/common/DualListBox"; import { DualListBox } from "@/components/common/DualListBox";
import { MenuPermissionsTable } from "./MenuPermissionsTable"; import { MenuPermissionsTable } from "./MenuPermissionsTable";
import { useMenu } from "@/contexts/MenuContext";
interface RoleDetailManagementProps { interface RoleDetailManagementProps {
roleId: string; roleId: string;
@ -25,6 +26,7 @@ interface RoleDetailManagementProps {
export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) { export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
const { user: currentUser } = useAuth(); const { user: currentUser } = useAuth();
const router = useRouter(); const router = useRouter();
const { refreshMenus } = useMenu();
const isSuperAdmin = currentUser?.companyCode === "*" && currentUser?.userType === "SUPER_ADMIN"; const isSuperAdmin = currentUser?.companyCode === "*" && currentUser?.userType === "SUPER_ADMIN";
@ -178,6 +180,9 @@ export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
if (response.success) { if (response.success) {
alert("멤버가 성공적으로 저장되었습니다."); alert("멤버가 성공적으로 저장되었습니다.");
loadMembers(); // 새로고침 loadMembers(); // 새로고침
// 사이드바 메뉴 새로고침 (현재 사용자가 영향받을 수 있음)
await refreshMenus();
} else { } else {
alert(response.message || "멤버 저장에 실패했습니다."); alert(response.message || "멤버 저장에 실패했습니다.");
} }
@ -187,7 +192,7 @@ export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
} finally { } finally {
setIsSavingMembers(false); setIsSavingMembers(false);
} }
}, [roleGroup, selectedUsers, loadMembers]); }, [roleGroup, selectedUsers, loadMembers, refreshMenus]);
// 메뉴 권한 저장 핸들러 // 메뉴 권한 저장 핸들러
const handleSavePermissions = useCallback(async () => { const handleSavePermissions = useCallback(async () => {
@ -200,6 +205,9 @@ export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
if (response.success) { if (response.success) {
alert("메뉴 권한이 성공적으로 저장되었습니다."); alert("메뉴 권한이 성공적으로 저장되었습니다.");
loadMenuPermissions(); // 새로고침 loadMenuPermissions(); // 새로고침
// 사이드바 메뉴 새로고침 (권한 변경 즉시 반영)
await refreshMenus();
} else { } else {
alert(response.message || "메뉴 권한 저장에 실패했습니다."); alert(response.message || "메뉴 권한 저장에 실패했습니다.");
} }
@ -209,7 +217,7 @@ export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
} finally { } finally {
setIsSavingPermissions(false); setIsSavingPermissions(false);
} }
}, [roleGroup, menuPermissions, loadMenuPermissions]); }, [roleGroup, menuPermissions, loadMenuPermissions, refreshMenus]);
if (isLoading) { if (isLoading) {
return ( return (