diff --git a/backend-node/src/controllers/adminController.ts b/backend-node/src/controllers/adminController.ts index d7aa247d..bea2a69b 100644 --- a/backend-node/src/controllers/adminController.ts +++ b/backend-node/src/controllers/adminController.ts @@ -2696,6 +2696,35 @@ export const saveUser = async (req: AuthenticatedRequest, res: Response) => { }); return; } + + // SUPER_ADMIN 권한 부여는 최고관리자만 가능 + const requestUser = req.user; + const isRequesterSuperAdmin = requestUser?.companyCode === "*" && requestUser?.userType === "SUPER_ADMIN"; + + if (userData.userType.trim() === "SUPER_ADMIN" && !isRequesterSuperAdmin) { + res.status(403).json({ + success: false, + message: "최고 관리자 권한은 최고 관리자만 부여할 수 있습니다.", + error: { code: "FORBIDDEN_SUPER_ADMIN_GRANT" }, + }); + return; + } + + // 기존 SUPER_ADMIN 사용자의 권한은 최고관리자만 변경 가능 + if (isUpdate && !isRequesterSuperAdmin) { + const targetUser = await queryOne<{ user_type: string }>( + `SELECT user_type FROM user_info WHERE user_id = $1`, + [userData.userId?.trim()] + ); + if (targetUser?.user_type === "SUPER_ADMIN") { + res.status(403).json({ + success: false, + message: "최고 관리자의 권한은 다른 최고 관리자만 변경할 수 있습니다.", + error: { code: "FORBIDDEN_SUPER_ADMIN_MODIFY" }, + }); + return; + } + } } // 4. 비밀번호 최소 길이 검증 (신규 등록 시) diff --git a/frontend/app/(main)/admin/menu/page.tsx b/frontend/app/(main)/admin/menu/page.tsx index 85d5b346..a07da807 100644 --- a/frontend/app/(main)/admin/menu/page.tsx +++ b/frontend/app/(main)/admin/menu/page.tsx @@ -415,30 +415,27 @@ export default function MenuPage() { }; const loadMenus = async (showLoading = true) => { - // console.log(`📋 메뉴 목록 조회 시작 (showLoading: ${showLoading})`); try { if (showLoading) { setLoading(true); } - - // 선택된 메뉴 타입에 해당하는 메뉴만 로드 - if (selectedMenuType === "admin") { - const adminResponse = await menuApi.getAdminMenusForManagement(); - if (adminResponse.success && adminResponse.data) { - setLocalAdminMenus(adminResponse.data); - } - } else { - const userResponse = await menuApi.getUserMenusForManagement(); - if (userResponse.success && userResponse.data) { - setLocalUserMenus(userResponse.data); - } + + // 양쪽 메뉴를 모두 로드 (카운트 표시용) + const [adminResponse, userResponse] = await Promise.all([ + menuApi.getAdminMenusForManagement(), + menuApi.getUserMenusForManagement(), + ]); + + if (adminResponse.success && adminResponse.data) { + setLocalAdminMenus(adminResponse.data); } - + if (userResponse.success && userResponse.data) { + setLocalUserMenus(userResponse.data); + } + // 전역 메뉴 상태도 업데이트 (좌측 사이드바용) await refreshMenus(); - // console.log("📋 메뉴 목록 조회 성공"); } catch (error) { - // console.error("❌ 메뉴 목록 조회 실패:", error); toast.error(getUITextSync("message.error.load.menu.list")); } finally { if (showLoading) { diff --git a/frontend/app/(main)/admin/userMng/userAuthList/page.tsx b/frontend/app/(main)/admin/userMng/userAuthList/page.tsx index 4ad69183..c49adbd6 100644 --- a/frontend/app/(main)/admin/userMng/userAuthList/page.tsx +++ b/frontend/app/(main)/admin/userMng/userAuthList/page.tsx @@ -13,14 +13,16 @@ import { ScrollToTop } from "@/components/common/ScrollToTop"; * 사용자 권한 관리 페이지 * URL: /admin/userAuth * - * 최고 관리자만 접근 가능 + * 관리자(최고관리자 + 회사관리자) 접근 가능 * 사용자별 권한 레벨(SUPER_ADMIN, COMPANY_ADMIN, USER 등) 관리 + * 회사관리자는 SUPER_ADMIN 권한 부여 불가 */ export default function UserAuthPage() { const { user: currentUser } = useAuth(); - // 최고 관리자 여부 + // 관리자 여부 const isSuperAdmin = currentUser?.companyCode === "*" && currentUser?.userType === "SUPER_ADMIN"; + const isAdmin = isSuperAdmin || currentUser?.userType === "COMPANY_ADMIN"; // 상태 관리 const [users, setUsers] = useState([]); @@ -49,6 +51,9 @@ export default function UserAuthPage() { const response = await userAPI.getList({ page, size: paginationInfo.pageSize, + ...(currentUser?.userType === "COMPANY_ADMIN" && currentUser?.companyCode + ? { companyCode: currentUser.companyCode } + : {}), }); if (response.success && response.data) { @@ -103,21 +108,21 @@ export default function UserAuthPage() { loadUsers(page); }; - // 최고 관리자가 아닌 경우 - if (!isSuperAdmin) { + // 관리자가 아닌 경우 + if (!isAdmin) { return (

사용자 권한 관리

-

사용자별 권한 레벨을 관리합니다. (최고 관리자 전용)

+

사용자별 권한 레벨을 관리합니다.

접근 권한 없음

- 권한 관리는 최고 관리자만 접근할 수 있습니다. + 권한 관리는 관리자만 접근할 수 있습니다.

diff --git a/frontend/components/admin/UserAuthEditModal.tsx b/frontend/components/admin/UserAuthEditModal.tsx index 7ce04f4c..0b56cec1 100644 --- a/frontend/components/admin/UserAuthEditModal.tsx +++ b/frontend/components/admin/UserAuthEditModal.tsx @@ -20,14 +20,16 @@ interface UserAuthEditModalProps { onClose: () => void; onSuccess?: () => void; user: any | null; + currentUser?: any | null; } /** * 사용자 권한 변경 모달 * - * 권한 레벨만 변경 가능 (최고 관리자 전용) + * 권한 레벨만 변경 가능 (관리자 전용) + * 회사관리자는 SUPER_ADMIN 권한 부여 불가 */ -export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuthEditModalProps) { +export function UserAuthEditModal({ isOpen, onClose, onSuccess, user, currentUser }: UserAuthEditModalProps) { const [selectedUserType, setSelectedUserType] = useState(""); const [isLoading, setIsLoading] = useState(false); const [showConfirmation, setShowConfirmation] = useState(false); @@ -79,7 +81,19 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth }, ]; - const selectedOption = userTypeOptions.find((opt) => opt.value === selectedUserType); + // 현재 사용자가 SUPER_ADMIN인지 확인 + const isCurrentUserSuperAdmin = currentUser?.companyCode === "*" && currentUser?.userType === "SUPER_ADMIN"; + + // COMPANY_ADMIN은 SUPER_ADMIN 옵션 제외 + const availableOptions = isCurrentUserSuperAdmin + ? userTypeOptions + : userTypeOptions.filter((opt) => opt.value !== "SUPER_ADMIN"); + + // 대상 사용자가 SUPER_ADMIN이면 COMPANY_ADMIN은 변경 불가 + const isTargetSuperAdmin = user?.userType === "SUPER_ADMIN"; + const canEdit = isCurrentUserSuperAdmin || !isTargetSuperAdmin; + + const selectedOption = availableOptions.find((opt) => opt.value === selectedUserType); // 권한 변경 여부 확인 const isUserTypeChanged = user && selectedUserType !== user.userType; @@ -160,22 +174,32 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth - - {selectedOption &&

{selectedOption.description}

} + {!canEdit ? ( +
+

+ 최고 관리자의 권한은 다른 최고 관리자만 변경할 수 있습니다. +

+
+ ) : ( + <> + + {selectedOption &&

{selectedOption.description}

} + + )}
{/* SUPER_ADMIN 경고 */} @@ -206,7 +230,7 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth