권한 관리 시스템 개선

 새로운 기능:
- 사용자별 세분화된 권한 체크 (can_manage_books, can_manage_notes, can_manage_novels)
- 페이지별 권한 가드 시스템 추가 (permission-guard.js)
- 헤더 메뉴 권한별 표시/숨김 기능

🔧 백엔드 개선:
- 모든 문서 관련 API에서 can_manage_books 권한 체크 추가
- documents.py: 개별 문서 조회, PDF 조회 권한 로직 수정
- highlights.py: 하이라이트 생성/조회 권한 체크 개선
- bookmarks.py: 북마크 생성/조회 권한 체크 개선
- document_links.py: 문서 링크 관련 권한 체크 개선

🎨 프론트엔드 개선:
- header-loader.js: updateMenuPermissions 함수 추가로 권한별 메뉴 제어
- permission-guard.js: 페이지 접근 권한 체크 및 리다이렉트 처리
- 권한 없는 페이지 접근 시 메인 페이지로 안전한 리다이렉트
- 헤더 사용자 정보 상태 보존 로직 추가

🛡️ 보안 강화:
- 403 Forbidden 에러 해결
- 권한 없는 사용자의 무단 페이지 접근 차단
- 문서 관리 권한이 있는 사용자는 모든 문서 공유 가능

📱 사용자 경험 개선:
- 권한에 따른 메뉴 자동 표시/숨김
- 로그인 상태 유지 개선
- 권한 없는 기능 접근 시 친화적인 알림 및 리다이렉트
This commit is contained in:
hyungi
2025-09-05 12:36:05 +09:00
parent 6a537008db
commit aebce091a9
12 changed files with 426 additions and 38 deletions

View File

@@ -36,6 +36,9 @@ class HeaderLoader {
// 헤더 HTML 삽입
container.innerHTML = headerHtml;
// 헤더 로드 후 필요한 함수들 정의
this.initializeHeaderFunctions();
this.headerLoaded = true;
console.log('✅ 헤더 로드 완료');
@@ -48,6 +51,140 @@ class HeaderLoader {
}
}
/**
* 헤더 로드 후 필요한 함수들 초기화
*/
initializeHeaderFunctions() {
console.log('🔧 헤더 함수들 초기화 중...');
// handleLogin 함수 정의
window.handleLogin = () => {
console.log('🔐 handleLogin 호출됨 - 로그인 페이지로 이동');
const currentUrl = encodeURIComponent(window.location.href);
window.location.href = `login.html?redirect=${currentUrl}`;
};
// handleLogout 함수 정의
window.handleLogout = async () => {
try {
console.log('🔄 로그아웃 시작...');
console.log('🔍 window.api 존재 여부:', !!window.api);
console.log('🔍 logout 함수 존재 여부:', typeof logout);
// 각 페이지의 로그아웃 함수가 있으면 호출
if (typeof logout === 'function') {
await logout();
} else {
console.log('🔄 직접 로그아웃 처리 시작...');
// API 로그아웃 시도
if (window.api && typeof window.api.logout === 'function') {
console.log('🌐 API 로그아웃 호출...');
try {
await window.api.logout();
console.log('✅ API 로그아웃 성공');
} catch (apiError) {
console.log('⚠️ API 로그아웃 실패:', apiError);
}
} else {
console.log('⚠️ window.api.logout 함수를 찾을 수 없음');
}
// 로컬 스토리지 정리 (항상 실행)
console.log('🧹 로컬 스토리지 정리...');
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user_info');
console.log('✅ 로그아웃 완료');
}
// 로그인 페이지로 리다이렉트
const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
window.location.href = `login.html?redirect=${currentUrl}`;
} catch (error) {
console.error('❌ 로그아웃 실패:', error);
// 에러가 발생해도 로컬 데이터는 정리하고 로그인 페이지로 이동
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user_info');
window.location.href = 'login.html';
}
};
console.log('✅ 헤더 함수들 초기화 완료');
}
/**
* 사용자 권한에 따른 메뉴 표시/숨김
*/
updateMenuPermissions(user) {
// 메뉴 요소들 가져오기
const menuItems = {
// 문서 관리 관련
'pdf-manager-nav-item': user.can_manage_books || user.is_admin,
'book-documents-nav-item': user.can_manage_books || user.is_admin,
'book-editor-nav-item': user.can_manage_books || user.is_admin,
// 노트 관리 관련
'notes-list-nav-item': user.can_manage_notes || user.is_admin,
'notebooks-nav-item': user.can_manage_notes || user.is_admin,
'note-editor-nav-item': user.can_manage_notes || user.is_admin,
// 소설 관리 관련
'story-view-nav-item': user.can_manage_novels || user.is_admin,
'story-reader-nav-item': user.can_manage_novels || user.is_admin,
'memo-tree-nav-item': user.can_manage_novels || user.is_admin,
// 할일 관리 - 노트 관리 권한 필요 (올바른 ID 사용)
'todos-nav-link': user.can_manage_notes || user.is_admin,
// 검색은 모든 사용자 허용
'search-nav-link': true,
// 메인 페이지는 모든 사용자 허용 (문서 보기만)
'index-nav-item': true
};
// 각 메뉴 아이템의 표시/숨김 처리
Object.entries(menuItems).forEach(([itemId, hasPermission]) => {
const menuItem = document.getElementById(itemId);
if (menuItem) {
if (hasPermission) {
menuItem.classList.remove('hidden');
console.log(`${itemId} 메뉴 표시`);
} else {
menuItem.classList.add('hidden');
console.log(`${itemId} 메뉴 숨김`);
}
}
});
// 드롭다운 메뉴의 링크들도 체크
const dropdownLinks = document.querySelectorAll('.user-dropdown a, .user-dropdown button');
dropdownLinks.forEach(link => {
const href = link.getAttribute('href') || '';
let hasPermission = true;
if (href.includes('pdf-manager') || href.includes('book-')) {
hasPermission = user.can_manage_books || user.is_admin;
} else if (href.includes('note') || href.includes('notebook')) {
hasPermission = user.can_manage_notes || user.is_admin;
} else if (href.includes('story') || href.includes('memo-tree')) {
hasPermission = user.can_manage_novels || user.is_admin;
}
if (hasPermission) {
link.classList.remove('hidden');
} else {
link.classList.add('hidden');
}
});
}
/**
* 헤더 로드 실패 시 폴백 헤더 표시
*/
@@ -194,7 +331,7 @@ document.addEventListener('headerLoaded', () => {
if (dropdownUserEmail) dropdownUserEmail.textContent = user.email || '';
if (dropdownUserRole) dropdownUserRole.textContent = roleText;
// 관리자 메뉴 표시/숨김
// 사용자별 메뉴 권한 체크
console.log('🔍 사용자 권한 확인:', {
role: user.role,
is_admin: user.is_admin,
@@ -203,6 +340,10 @@ document.addEventListener('headerLoaded', () => {
can_manage_novels: user.can_manage_novels
});
// 개별 메뉴 권한 체크
window.headerLoader.updateMenuPermissions(user);
// 관리자 메뉴 표시/숨김 (전체 관리자만)
if (adminMenuSection) {
if (user.role === 'root' || user.role === 'admin' || user.is_admin) {
console.log('✅ 관리자 메뉴 표시');