feat: 사용자별 페이지 접근 권한 시스템 구현

- 기존 4단계 권한을 admin/user 2단계로 단순화
- 페이지별 세부 접근 권한 관리 시스템 추가
- 부적합 조회 시 일반 사용자는 본인 등록 건만 조회 가능하도록 제한
- 관리자 전용 전체 부적합 조회 API 추가 (/api/issues/admin/all)

Backend Changes:
- models.py: UserPagePermission 모델 추가, UserRole 단순화
- page_permissions.py: 페이지 권한 관리 API 라우터 추가
- auth.py: 사용자 목록 조회 및 비밀번호 초기화 API 추가
- issues.py: 권한별 부적합 조회 제한 로직 구현
- 마이그레이션: 010~012 권한 시스템 관련 DB 스키마 변경
This commit is contained in:
Hyungi Ahn
2025-10-25 08:59:52 +09:00
parent 41b557a709
commit b68bf78e40
8 changed files with 676 additions and 8 deletions

View File

@@ -0,0 +1,324 @@
"""
페이지 권한 관리 API 라우터
사용자별 페이지 접근 권한을 관리하는 엔드포인트들
"""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List, Optional
from pydantic import BaseModel
from database.database import get_db
from database.models import User, UserPagePermission, UserRole
from routers.auth import get_current_user
router = APIRouter(prefix="/api", tags=["page-permissions"])
# Pydantic 모델들
class PagePermissionRequest(BaseModel):
user_id: int
page_name: str
can_access: bool
notes: Optional[str] = None
class PagePermissionResponse(BaseModel):
id: int
user_id: int
page_name: str
can_access: bool
granted_by_id: Optional[int]
granted_at: str
notes: Optional[str]
class Config:
from_attributes = True
class UserPagePermissionSummary(BaseModel):
user_id: int
username: str
full_name: Optional[str]
role: str
permissions: List[PagePermissionResponse]
# 기본 페이지 목록
DEFAULT_PAGES = {
'issues_create': {'title': '부적합 등록', 'default_access': True},
'issues_view': {'title': '부적합 조회', 'default_access': True},
'issues_manage': {'title': '부적합 관리', 'default_access': False},
'projects_manage': {'title': '프로젝트 관리', 'default_access': False},
'daily_work': {'title': '일일 공수', 'default_access': False},
'reports': {'title': '보고서', 'default_access': False}
}
@router.post("/page-permissions/grant")
async def grant_page_permission(
request: PagePermissionRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""페이지 권한 부여/취소"""
# 관리자만 권한 설정 가능
if current_user.role != UserRole.admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="관리자만 권한을 설정할 수 있습니다."
)
# 대상 사용자 확인
target_user = db.query(User).filter(User.id == request.user_id).first()
if not target_user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="사용자를 찾을 수 없습니다."
)
# 유효한 페이지명 확인
if request.page_name not in DEFAULT_PAGES:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="유효하지 않은 페이지명입니다."
)
# 기존 권한 확인
existing_permission = db.query(UserPagePermission).filter(
UserPagePermission.user_id == request.user_id,
UserPagePermission.page_name == request.page_name
).first()
if existing_permission:
# 기존 권한 업데이트
existing_permission.can_access = request.can_access
existing_permission.granted_by_id = current_user.id
existing_permission.notes = request.notes
db.commit()
db.refresh(existing_permission)
return {"message": "권한이 업데이트되었습니다.", "permission_id": existing_permission.id}
else:
# 새 권한 생성
new_permission = UserPagePermission(
user_id=request.user_id,
page_name=request.page_name,
can_access=request.can_access,
granted_by_id=current_user.id,
notes=request.notes
)
db.add(new_permission)
db.commit()
db.refresh(new_permission)
return {"message": "권한이 설정되었습니다.", "permission_id": new_permission.id}
@router.get("/users/{user_id}/page-permissions", response_model=List[PagePermissionResponse])
async def get_user_page_permissions(
user_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""특정 사용자의 페이지 권한 목록 조회"""
# 관리자이거나 본인의 권한만 조회 가능
if current_user.role != UserRole.admin and current_user.id != user_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="권한이 없습니다."
)
# 사용자 존재 확인
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="사용자를 찾을 수 없습니다."
)
# 사용자의 페이지 권한 조회
permissions = db.query(UserPagePermission).filter(
UserPagePermission.user_id == user_id
).all()
return permissions
@router.get("/page-permissions/check/{user_id}/{page_name}")
async def check_page_access(
user_id: int,
page_name: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""특정 사용자의 특정 페이지 접근 권한 확인"""
# 사용자 존재 확인
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="사용자를 찾을 수 없습니다."
)
# admin은 모든 페이지 접근 가능
if user.role == UserRole.admin:
return {"can_access": True, "reason": "admin_role"}
# 유효한 페이지명 확인
if page_name not in DEFAULT_PAGES:
return {"can_access": False, "reason": "invalid_page"}
# 개별 권한 확인
permission = db.query(UserPagePermission).filter(
UserPagePermission.user_id == user_id,
UserPagePermission.page_name == page_name
).first()
if permission:
return {
"can_access": permission.can_access,
"reason": "explicit_permission",
"granted_at": permission.granted_at.isoformat() if permission.granted_at else None
}
# 기본 권한 확인
default_access = DEFAULT_PAGES[page_name]['default_access']
return {
"can_access": default_access,
"reason": "default_permission"
}
@router.get("/page-permissions/all-users", response_model=List[UserPagePermissionSummary])
async def get_all_users_permissions(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""모든 사용자의 페이지 권한 요약 조회 (관리자용)"""
if current_user.role != UserRole.admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="관리자만 접근할 수 있습니다."
)
# 모든 사용자 조회
users = db.query(User).filter(User.is_active == True).all()
result = []
for user in users:
# 각 사용자의 권한 조회
permissions = db.query(UserPagePermission).filter(
UserPagePermission.user_id == user.id
).all()
result.append(UserPagePermissionSummary(
user_id=user.id,
username=user.username,
full_name=user.full_name,
role=user.role.value,
permissions=permissions
))
return result
@router.get("/page-permissions/available-pages")
async def get_available_pages(
current_user: User = Depends(get_current_user)
):
"""사용 가능한 페이지 목록 조회"""
return {
"pages": DEFAULT_PAGES,
"total_count": len(DEFAULT_PAGES)
}
@router.delete("/page-permissions/{permission_id}")
async def delete_page_permission(
permission_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""페이지 권한 삭제 (기본값으로 되돌림)"""
if current_user.role != UserRole.admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="관리자만 권한을 삭제할 수 있습니다."
)
# 권한 조회
permission = db.query(UserPagePermission).filter(
UserPagePermission.id == permission_id
).first()
if not permission:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="권한을 찾을 수 없습니다."
)
# 권한 삭제
db.delete(permission)
db.commit()
return {"message": "권한이 삭제되었습니다. 기본값이 적용됩니다."}
class BulkPermissionRequest(BaseModel):
user_id: int
permissions: List[dict] # [{"page_name": "issues_manage", "can_access": true}, ...]
@router.post("/page-permissions/bulk-grant")
async def bulk_grant_permissions(
request: BulkPermissionRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""사용자의 여러 페이지 권한을 일괄 설정"""
if current_user.role != UserRole.admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="관리자만 권한을 설정할 수 있습니다."
)
# 대상 사용자 확인
target_user = db.query(User).filter(User.id == request.user_id).first()
if not target_user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="사용자를 찾을 수 없습니다."
)
updated_permissions = []
for perm_data in request.permissions:
page_name = perm_data.get('page_name')
can_access = perm_data.get('can_access', False)
# 유효한 페이지명 확인
if page_name not in DEFAULT_PAGES:
continue
# 기존 권한 확인
existing_permission = db.query(UserPagePermission).filter(
UserPagePermission.user_id == request.user_id,
UserPagePermission.page_name == page_name
).first()
if existing_permission:
# 기존 권한 업데이트
existing_permission.can_access = can_access
existing_permission.granted_by_id = current_user.id
updated_permissions.append(existing_permission)
else:
# 새 권한 생성
new_permission = UserPagePermission(
user_id=request.user_id,
page_name=page_name,
can_access=can_access,
granted_by_id=current_user.id
)
db.add(new_permission)
updated_permissions.append(new_permission)
db.commit()
return {
"message": f"{len(updated_permissions)}개의 권한이 설정되었습니다.",
"updated_count": len(updated_permissions)
}