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

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 조건 생성
const menuTypeCondition =
menuType !== undefined ? `MENU.MENU_TYPE = ${parseInt(menuType)}` : "1 = 1";
menuType !== undefined
? `MENU.MENU_TYPE = ${parseInt(menuType)}`
: "1 = 1";
// 메뉴 관리 화면인지 좌측 사이드바인지 구분
// includeInactive가 true거나 menuType이 undefined면 메뉴 관리 화면
const includeInactive = paramMap.includeInactive === true;
const isManagementScreen = includeInactive || menuType === undefined;
// 메뉴 관리 화면: 모든 상태의 메뉴 표시, 좌측 사이드바: active만 표시
const statusCondition = isManagementScreen ? "1 = 1" : "MENU.STATUS = 'active'";
const subStatusCondition = isManagementScreen ? "1 = 1" : "MENU_SUB.STATUS = 'active'";
const statusCondition = isManagementScreen
? "1 = 1"
: "MENU.STATUS = 'active'";
const subStatusCondition = isManagementScreen
? "1 = 1"
: "MENU_SUB.STATUS = 'active'";
// 1. 권한 그룹 기반 필터링 (좌측 사이드바인 경우만)
let authFilter = "";
@ -35,7 +41,11 @@ export class AdminService {
let queryParams: any[] = [userLang];
let paramIndex = 2;
if (menuType !== undefined && userType !== "SUPER_ADMIN" && !isManagementScreen) {
if (
menuType !== undefined &&
userType !== "SUPER_ADMIN" &&
!isManagementScreen
) {
// 좌측 사이드바 + SUPER_ADMIN이 아닌 경우만 권한 체크
const userRoleGroups = await query<any>(
`
@ -56,45 +66,45 @@ export class AdminService {
);
if (userType === "COMPANY_ADMIN") {
// 회사 관리자: 자기 회사 메뉴는 모두, 공통 메뉴는 권한 있는 것만
// 회사 관리자: 권한 그룹 기반 필터링 적용
if (userRoleGroups.length > 0) {
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);
const companyParamIndex = paramIndex;
paramIndex++;
// 하위 메뉴: 회사 메뉴는 모두, 공통 메뉴는 권한 체크
// 하위 메뉴 권한 체크
unionFilter = `
AND (
MENU_SUB.COMPANY_CODE = $${companyParamIndex}
OR (
MENU_SUB.COMPANY_CODE = '*'
AND EXISTS (
SELECT 1
FROM rel_menu_auth rma
WHERE rma.menu_objid = MENU_SUB.OBJID
AND rma.auth_objid = ANY($${paramIndex})
AND rma.read_yn = 'Y'
)
)
AND MENU_SUB.COMPANY_CODE IN ($${paramIndex - 1}, '*')
AND EXISTS (
SELECT 1
FROM rel_menu_auth rma
WHERE rma.menu_objid = MENU_SUB.OBJID
AND rma.auth_objid = ANY($${paramIndex})
AND rma.read_yn = 'Y'
)
`;
queryParams.push(roleObjids);
paramIndex++;
logger.info(
`✅ 회사 관리자: 회사 ${userCompanyCode} 메뉴 전체 + 권한 있는 공통 메뉴`
`✅ 회사 관리자: 권한 있는 메뉴만 (${roleObjids.length}개 그룹)`
);
} else {
// 권한 그룹이 없는 회사 관리자: 자기 회사 메뉴만
authFilter = `AND MENU.COMPANY_CODE = $${paramIndex}`;
unionFilter = `AND MENU_SUB.COMPANY_CODE = $${paramIndex}`;
queryParams.push(userCompanyCode);
paramIndex++;
logger.info(
`✅ 회사 관리자 (권한 그룹 없음): 회사 ${userCompanyCode} 메뉴만`
// 권한 그룹이 없는 회사 관리자: 메뉴 없음
logger.warn(
`⚠️ 회사 관리자 ${userId}는 권한 그룹이 없어 메뉴가 표시되지 않습니다.`
);
return [];
}
} else {
// 일반 사용자: 권한 그룹 필수
@ -131,7 +141,11 @@ export class AdminService {
return [];
}
}
} else if (menuType !== undefined && userType === "SUPER_ADMIN" && !isManagementScreen) {
} else if (
menuType !== undefined &&
userType === "SUPER_ADMIN" &&
!isManagementScreen
) {
// 좌측 사이드바 + SUPER_ADMIN: 권한 그룹 체크 없이 모든 공통 메뉴 표시
logger.info(`✅ 최고 관리자는 권한 그룹 체크 없이 모든 공통 메뉴 표시`);
// unionFilter는 비워둠 (하위 메뉴도 공통 메뉴만)
@ -167,7 +181,7 @@ export class AdminService {
companyFilter = `AND MENU.COMPANY_CODE = $${paramIndex}`;
queryParams.push(userCompanyCode);
paramIndex++;
// 하위 메뉴에도 회사 필터링 적용 (공통 메뉴 제외)
if (unionFilter === "") {
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 { DualListBox } from "@/components/common/DualListBox";
import { MenuPermissionsTable } from "./MenuPermissionsTable";
import { useMenu } from "@/contexts/MenuContext";
interface RoleDetailManagementProps {
roleId: string;
@ -25,6 +26,7 @@ interface RoleDetailManagementProps {
export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
const { user: currentUser } = useAuth();
const router = useRouter();
const { refreshMenus } = useMenu();
const isSuperAdmin = currentUser?.companyCode === "*" && currentUser?.userType === "SUPER_ADMIN";
@ -178,6 +180,9 @@ export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
if (response.success) {
alert("멤버가 성공적으로 저장되었습니다.");
loadMembers(); // 새로고침
// 사이드바 메뉴 새로고침 (현재 사용자가 영향받을 수 있음)
await refreshMenus();
} else {
alert(response.message || "멤버 저장에 실패했습니다.");
}
@ -187,7 +192,7 @@ export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
} finally {
setIsSavingMembers(false);
}
}, [roleGroup, selectedUsers, loadMembers]);
}, [roleGroup, selectedUsers, loadMembers, refreshMenus]);
// 메뉴 권한 저장 핸들러
const handleSavePermissions = useCallback(async () => {
@ -200,6 +205,9 @@ export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
if (response.success) {
alert("메뉴 권한이 성공적으로 저장되었습니다.");
loadMenuPermissions(); // 새로고침
// 사이드바 메뉴 새로고침 (권한 변경 즉시 반영)
await refreshMenus();
} else {
alert(response.message || "메뉴 권한 저장에 실패했습니다.");
}
@ -209,7 +217,7 @@ export function RoleDetailManagement({ roleId }: RoleDetailManagementProps) {
} finally {
setIsSavingPermissions(false);
}
}, [roleGroup, menuPermissions, loadMenuPermissions]);
}, [roleGroup, menuPermissions, loadMenuPermissions, refreshMenus]);
if (isLoading) {
return (