✨ 새로운 기능: - 사용자별 세분화된 권한 체크 (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 에러 해결 - 권한 없는 사용자의 무단 페이지 접근 차단 - 문서 관리 권한이 있는 사용자는 모든 문서 공유 가능 📱 사용자 경험 개선: - 권한에 따른 메뉴 자동 표시/숨김 - 로그인 상태 유지 개선 - 권한 없는 기능 접근 시 친화적인 알림 및 리다이렉트
156 lines
6.1 KiB
JavaScript
156 lines
6.1 KiB
JavaScript
/**
|
|
* 페이지별 권한 체크 가드
|
|
*/
|
|
class PermissionGuard {
|
|
constructor() {
|
|
this.pagePermissions = {
|
|
// 문서 관리 관련
|
|
'pdf-manager.html': (user) => user.can_manage_books || user.is_admin,
|
|
'book-documents.html': (user) => user.can_manage_books || user.is_admin,
|
|
'book-editor.html': (user) => user.can_manage_books || user.is_admin,
|
|
|
|
// 노트 관리 관련
|
|
'notes.html': (user) => user.can_manage_notes || user.is_admin,
|
|
'notebooks.html': (user) => user.can_manage_notes || user.is_admin,
|
|
'note-editor.html': (user) => user.can_manage_notes || user.is_admin,
|
|
'todos.html': (user) => user.can_manage_notes || user.is_admin,
|
|
|
|
// 소설 관리 관련
|
|
'story-view.html': (user) => user.can_manage_novels || user.is_admin,
|
|
'story-reader.html': (user) => user.can_manage_novels || user.is_admin,
|
|
'memo-tree.html': (user) => user.can_manage_novels || user.is_admin,
|
|
|
|
// 관리자 전용
|
|
'user-management.html': (user) => user.is_admin,
|
|
'system-settings.html': (user) => user.is_admin,
|
|
'backup-restore.html': (user) => user.is_admin,
|
|
'logs.html': (user) => user.is_admin,
|
|
'setup.html': (user) => user.is_admin,
|
|
|
|
// 모든 사용자 허용
|
|
'index.html': () => true,
|
|
'search.html': () => true,
|
|
'upload.html': () => true,
|
|
'viewer.html': () => true,
|
|
'login.html': () => true,
|
|
'profile.html': () => true
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 현재 페이지의 권한을 체크합니다
|
|
*/
|
|
async checkCurrentPagePermission() {
|
|
try {
|
|
// 현재 페이지 파일명 추출
|
|
const currentPage = window.location.pathname.split('/').pop() || 'index.html';
|
|
console.log(`🔐 권한 체크 시작 - 페이지: ${currentPage}`);
|
|
|
|
// 권한 체크 함수 가져오기
|
|
const permissionCheck = this.pagePermissions[currentPage];
|
|
|
|
// 권한 체크가 정의되지 않은 페이지는 허용
|
|
if (!permissionCheck) {
|
|
console.log(`✅ 권한 체크 없음 - 페이지 허용: ${currentPage}`);
|
|
return true;
|
|
}
|
|
|
|
// 사용자 정보 가져오기
|
|
const token = localStorage.getItem('access_token');
|
|
if (!token) {
|
|
console.log(`❌ 토큰 없음 - 로그인 페이지로 이동`);
|
|
this.redirectToLogin();
|
|
return false;
|
|
}
|
|
|
|
// 먼저 기존 사용자 정보가 있는지 확인 (헤더 상태 보존)
|
|
let user = window.currentUser;
|
|
|
|
// 사용자 정보가 없으면 API로 조회
|
|
if (!user) {
|
|
const response = await fetch('/api/auth/me', {
|
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.log(`❌ 사용자 정보 조회 실패 - 로그인 페이지로 이동`);
|
|
this.redirectToLogin();
|
|
return false;
|
|
}
|
|
|
|
user = await response.json();
|
|
// 전역 사용자 정보 저장 (헤더에서 사용)
|
|
window.currentUser = user;
|
|
}
|
|
|
|
console.log(`👤 사용자 정보: ${user.email}, 권한: 관리자=${user.is_admin}, 문서=${user.can_manage_books}, 노트=${user.can_manage_notes}, 소설=${user.can_manage_novels}`);
|
|
|
|
// 권한 체크 실행
|
|
const hasPermission = permissionCheck(user);
|
|
|
|
if (hasPermission) {
|
|
console.log(`✅ 권한 확인 - 페이지 접근 허용: ${currentPage}`);
|
|
window.permissionGuardExecuted = true;
|
|
return true;
|
|
} else {
|
|
console.log(`❌ 권한 없음 - 메인 페이지로 이동: ${currentPage}`);
|
|
window.permissionGuardExecuted = true;
|
|
this.redirectToMain();
|
|
return false;
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`🚫 권한 체크 오류:`, error);
|
|
this.redirectToLogin();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 로그인 페이지로 리다이렉트
|
|
*/
|
|
redirectToLogin() {
|
|
const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
|
|
window.location.href = `login.html?redirect=${currentUrl}`;
|
|
}
|
|
|
|
/**
|
|
* 메인 페이지로 리다이렉트
|
|
*/
|
|
redirectToMain() {
|
|
// 사용자에게 알림 표시
|
|
if (typeof alert !== 'undefined') {
|
|
alert('이 페이지에 접근할 권한이 없습니다. 메인 페이지로 이동합니다.');
|
|
}
|
|
window.location.href = 'index.html';
|
|
}
|
|
}
|
|
|
|
// 전역 인스턴스 생성
|
|
window.permissionGuard = new PermissionGuard();
|
|
|
|
// 헤더 로드 완료 후 권한 체크 실행 (헤더 상태 보존)
|
|
document.addEventListener('headerLoaded', () => {
|
|
console.log('🔐 헤더 로드 완료 후 권한 체크 시작');
|
|
// 로그인 페이지는 권한 체크 제외
|
|
const currentPage = window.location.pathname.split('/').pop() || 'index.html';
|
|
if (currentPage !== 'login.html') {
|
|
// 약간의 지연을 두어 헤더 초기화 완료 대기
|
|
setTimeout(() => {
|
|
window.permissionGuard.checkCurrentPagePermission();
|
|
}, 100);
|
|
}
|
|
});
|
|
|
|
// 백업: DOM 로드 완료 시 권한 체크 실행 (헤더 이벤트가 없는 경우)
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// 헤더 로드 이벤트를 기다리되, 3초 후에는 강제 실행
|
|
setTimeout(() => {
|
|
const currentPage = window.location.pathname.split('/').pop() || 'index.html';
|
|
if (currentPage !== 'login.html' && !window.permissionGuardExecuted) {
|
|
console.log('🔐 백업 권한 체크 실행');
|
|
window.permissionGuard.checkCurrentPagePermission();
|
|
}
|
|
}, 3000);
|
|
});
|