Implement SUPER_ADMIN permission checks in user management

- Added validation to ensure that only existing SUPER_ADMIN users can grant or modify SUPER_ADMIN permissions.
- Updated the user management page to reflect that both SUPER_ADMIN and COMPANY_ADMIN can access the user permissions, while COMPANY_ADMIN cannot grant SUPER_ADMIN rights.
- Enhanced the user authorization modal to prevent COMPANY_ADMIN from changing SUPER_ADMIN permissions, ensuring proper access control.

These changes improve the security and integrity of user role management within the application.
This commit is contained in:
kjs
2026-04-01 14:17:43 +09:00
parent 8be4159f17
commit 369a201832
7 changed files with 104 additions and 43 deletions

View File

@@ -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<string>("");
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
<Label htmlFor="userType" className="text-sm font-medium">
<span className="text-destructive">*</span>
</Label>
<Select value={selectedUserType} onValueChange={setSelectedUserType}>
<SelectTrigger className="h-10">
<SelectValue placeholder="권한 선택" />
</SelectTrigger>
<SelectContent>
{userTypeOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
<div className="flex items-center gap-2">
<span className={option.color}>{option.icon}</span>
<span>{option.label}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
{selectedOption && <p className="text-muted-foreground text-xs">{selectedOption.description}</p>}
{!canEdit ? (
<div className="rounded-lg border border-orange-300 bg-amber-50 p-3">
<p className="text-xs text-orange-800">
.
</p>
</div>
) : (
<>
<Select value={selectedUserType} onValueChange={setSelectedUserType}>
<SelectTrigger className="h-10">
<SelectValue placeholder="권한 선택" />
</SelectTrigger>
<SelectContent>
{availableOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
<div className="flex items-center gap-2">
<span className={option.color}>{option.icon}</span>
<span>{option.label}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
{selectedOption && <p className="text-muted-foreground text-xs">{selectedOption.description}</p>}
</>
)}
</div>
{/* SUPER_ADMIN 경고 */}
@@ -206,7 +230,7 @@ export function UserAuthEditModal({ isOpen, onClose, onSuccess, user }: UserAuth
</Button>
<Button
onClick={handleSave}
disabled={isLoading || !isUserTypeChanged}
disabled={isLoading || !isUserTypeChanged || !canEdit}
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
>
{isLoading ? "처리중..." : showConfirmation ? "확인 및 저장" : "저장"}