# Super Admin Protection Feature **날짜:** 2025-12-01 **작업자:** Claude Code **요청:** 슈퍼관리자 보호 및 복원/영구삭제 권한 분리 --- ## 1. 요구사항 ### 1.1 슈퍼관리자 보호 - 일반관리자는 슈퍼관리자를 **볼 수 없음** (목록에서 숨김) - 일반관리자는 슈퍼관리자를 **수정/삭제할 수 없음** - 슈퍼관리자만 다른 슈퍼관리자를 관리 가능 ### 1.2 복원/영구삭제 권한 분리 - **복원 (Restore)**: 일반관리자도 가능 - **영구삭제 (Force Delete)**: 슈퍼관리자 전용 --- ## 2. 구현 내용 ### 2.1 라우트 수정 (`routes/api.php`) 8개 엔티티의 restore 라우트를 `super.admin` 미들웨어 밖으로 이동: | 엔티티 | 라인 | 복원 라우트 | 영구삭제 라우트 | |--------|------|-------------|-----------------| | Tenants | 42-48 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | | Departments | 76-82 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | | Users | 93-99 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | | Menus | 117-123 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | | Boards | 151-157 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | | PM Projects | 234-240 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | | PM Tasks | 260-266 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | | PM Issues | 292-298 | `POST /{id}/restore` | `DELETE /{id}/force` (super.admin) | **패턴:** ```php // 복원 (일반관리자 가능) Route::post('/{id}/restore', [Controller::class, 'restore'])->name('restore'); // 슈퍼관리자 전용 액션 (영구삭제) Route::middleware('super.admin')->group(function () { Route::delete('/{id}/force', [Controller::class, 'forceDestroy'])->name('forceDestroy'); }); ``` ### 2.2 서비스 레이어 수정 #### `app/Services/UserService.php` ```php public function canAccessUser(int $targetUserId): bool { // withTrashed()를 사용하여 soft-deleted 사용자도 확인 (복원 시 필요) $targetUser = User::withTrashed()->find($targetUserId); $currentUser = auth()->user(); // 대상 사용자가 슈퍼관리자이고 현재 사용자가 슈퍼관리자가 아니면 접근 불가 if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) { return false; } return true; } ``` #### `app/Services/UserPermissionService.php` ```php public function canModifyUser(int $targetUserId): bool { // withTrashed()를 사용하여 일관성 유지 $targetUser = User::withTrashed()->find($targetUserId); $currentUser = auth()->user(); if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) { return false; } return true; } ``` **핵심 수정**: `User::find()` → `User::withTrashed()->find()` - Soft-deleted 사용자도 조회 가능하게 변경 - 복원 작업 시 권한 체크가 정상 작동 ### 2.3 뷰 레이어 수정 6개 테이블 뷰에 권한별 버튼 표시 로직 적용: | 파일 | 복원 버튼 | 영구삭제 버튼 | |------|-----------|---------------| | `users/partials/table.blade.php` | `$canModify` 체크 | `is_super_admin` 체크 | | `users/partials/modal-info.blade.php` | 슈퍼관리자이거나 대상이 일반사용자 | - | | `departments/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | | `menus/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | | `boards/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | | `tenants/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | | `project-management/projects/partials/table.blade.php` | 항상 표시 | `is_super_admin` 체크 | **Blade 패턴:** ```blade @if($item->deleted_at) @if(auth()->user()?->is_super_admin) @endif @endif ``` --- ## 3. 수정된 파일 목록 ### 라우트 - `routes/api.php` - 8개 엔티티 restore 라우트 분리 ### 서비스 - `app/Services/UserService.php` - `canAccessUser()` withTrashed 적용 - `app/Services/UserPermissionService.php` - `canModifyUser()` withTrashed 적용 ### 뷰 (Blade) - `resources/views/users/partials/table.blade.php` - `resources/views/users/partials/modal-info.blade.php` - `resources/views/departments/partials/table.blade.php` - `resources/views/menus/partials/table.blade.php` - `resources/views/boards/partials/table.blade.php` - `resources/views/tenants/partials/table.blade.php` - `resources/views/project-management/projects/partials/table.blade.php` --- ## 4. 테스트 시나리오 ### 4.1 일반관리자 테스트 - [ ] 사용자 목록에서 슈퍼관리자가 보이지 않음 - [ ] 삭제된 사용자 복원 가능 - [ ] 삭제된 부서/메뉴/게시판/테넌트/프로젝트 복원 가능 - [ ] 영구삭제 버튼이 보이지 않음 - [ ] 슈퍼관리자 수정/삭제 불가 (API 레벨) ### 4.2 슈퍼관리자 테스트 - [ ] 모든 사용자 조회 가능 (슈퍼관리자 포함) - [ ] 삭제된 항목 복원 가능 - [ ] 영구삭제 가능 - [ ] 다른 슈퍼관리자 관리 가능 --- ## 5. 이슈 해결 ### 5.1 302 Found 에러 **문제**: 일반관리자가 복원 API 호출 시 302 리다이렉트 발생 **원인**: restore 라우트가 `super.admin` 미들웨어 내부에 있었음 **해결**: restore 라우트를 미들웨어 밖으로 이동 ### 5.2 Soft-deleted 사용자 권한 체크 실패 **문제**: `User::find()`가 soft-deleted 사용자를 조회하지 못함 **원인**: Eloquent 기본 동작으로 soft-deleted 레코드 제외 **해결**: `User::withTrashed()->find()` 사용 --- ## 6. 관련 문서 - `claudedocs/archive-restore-feature-analysis.md` - 아카이브/복원 기능 분석 - `CURRENT_WORKS.md` - 작업 히스토리