diff --git a/app/api/endpoints/history.py b/app/api/endpoints/history.py index 7083f98b..e4d451e7 100644 --- a/app/api/endpoints/history.py +++ b/app/api/endpoints/history.py @@ -22,8 +22,9 @@ from app.db.models import User from app.db.models.downloadhistory import DownloadHistory, DownloadFiles from app.db.models.transferhistory import TransferHistory from app.db.user_oper import ( - get_current_active_superuser_async, + get_current_active_manage_user, get_current_active_superuser, + get_current_active_superuser_async, ) from app.helper.progress import ProgressHelper from app.schemas.types import EventType @@ -223,7 +224,7 @@ def delete_transfer_history( deletesrc: Optional[bool] = False, deletedest: Optional[bool] = False, db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 删除整理记录 @@ -264,7 +265,7 @@ def delete_transfer_history( def ai_redo_transfer_history( history_id: int, db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 手动触发单条历史记录的 AI 重新整理,并返回进度键。 @@ -293,7 +294,7 @@ def ai_redo_transfer_history( def batch_ai_redo_transfer_history( payload: schemas.BatchTransferHistoryRedoRequest, db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 手动触发多条历史记录的 AI 批量重新整理,并返回进度键。 diff --git a/app/api/endpoints/site.py b/app/api/endpoints/site.py index d8f7602c..4d6440d4 100644 --- a/app/api/endpoints/site.py +++ b/app/api/endpoints/site.py @@ -22,6 +22,8 @@ from app.db.models.siteuserdata import SiteUserData from app.db.site_oper import SiteOper from app.db.systemconfig_oper import SystemConfigOper from app.db.user_oper import ( + get_current_active_manage_user, + get_current_active_manage_user_async, get_current_active_superuser, get_current_active_superuser_async, ) @@ -37,7 +39,7 @@ router = APIRouter() @router.get("/", summary="所有站点", response_model=List[schemas.Site]) async def read_sites( db: AsyncSession = Depends(get_async_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user_async), ) -> List[dict]: """ 获取站点列表 @@ -50,7 +52,7 @@ async def add_site( *, db: AsyncSession = Depends(get_async_db), site_in: schemas.Site, - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user_async), ) -> Any: """ 新增站点 @@ -89,7 +91,7 @@ async def update_site( *, db: AsyncSession = Depends(get_async_db), site_in: schemas.Site, - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user_async), ) -> Any: """ 更新站点信息 @@ -150,7 +152,7 @@ def reset( async def update_sites_priority( priorities: List[dict], db: AsyncSession = Depends(get_async_db), - _: User = Depends(get_current_active_superuser_async), + _: User = Depends(get_current_active_manage_user_async), ) -> Any: """ 批量更新站点优先级 @@ -203,7 +205,7 @@ def update_cookie_by_body( site_id: int, site_cookie_update: schemas.SiteCookieUpdate, db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 使用请求体中的用户密码更新站点Cookie @@ -226,7 +228,7 @@ def update_cookie( password: str, code: Optional[str] = None, db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 使用用户密码更新站点Cookie @@ -246,7 +248,7 @@ def update_cookie( def refresh_userdata( site_id: int, db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 刷新站点用户数据 @@ -273,7 +275,7 @@ def refresh_userdata( ) async def read_userdata_latest( db: AsyncSession = Depends(get_async_db), - _: User = Depends(get_current_active_superuser_async), + _: User = Depends(get_current_active_manage_user_async), ) -> Any: """ 查询所有站点最新用户数据 @@ -291,7 +293,7 @@ async def read_userdata( site_id: int, workdate: Optional[str] = None, db: AsyncSession = Depends(get_async_db), - _: User = Depends(get_current_active_superuser_async), + _: User = Depends(get_current_active_manage_user_async), ) -> Any: """ 查询站点用户数据 @@ -395,7 +397,7 @@ async def site_resource( cat: Optional[str] = None, page: Optional[int] = 0, db: AsyncSession = Depends(get_async_db), - _: User = Depends(get_current_active_superuser_async), + _: User = Depends(get_current_active_manage_user_async), ) -> Any: """ 浏览站点资源 @@ -543,7 +545,7 @@ async def support_sites(_: User = Depends(get_current_active_superuser_async)): async def read_site( site_id: int, db: AsyncSession = Depends(get_async_db), - _: User = Depends(get_current_active_superuser_async), + _: User = Depends(get_current_active_manage_user_async), ) -> Any: """ 通过ID获取站点信息 @@ -561,7 +563,7 @@ async def read_site( async def delete_site( site_id: int, db: AsyncSession = Depends(get_async_db), - _: User = Depends(get_current_active_superuser_async), + _: User = Depends(get_current_active_manage_user_async), ) -> Any: """ 删除站点 diff --git a/app/api/endpoints/storage.py b/app/api/endpoints/storage.py index 571e0bdf..617f521b 100644 --- a/app/api/endpoints/storage.py +++ b/app/api/endpoints/storage.py @@ -15,6 +15,7 @@ from app.core.config import settings from app.core.security import verify_token from app.db.models import User from app.db.user_oper import ( + get_current_active_manage_user, get_current_active_superuser, get_current_active_superuser_async, ) @@ -91,7 +92,7 @@ def list_files( fileitem: schemas.FileItem, sort: Optional[str] = "updated_at", keyword: Optional[str] = None, - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 查询当前目录下所有目录和文件 @@ -117,7 +118,7 @@ def list_files( def mkdir( fileitem: schemas.FileItem, name: str, - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 创建目录 @@ -135,7 +136,7 @@ def mkdir( @router.post("/delete", summary="删除文件或目录", response_model=schemas.Response) def delete( - fileitem: schemas.FileItem, _: User = Depends(get_current_active_superuser) + fileitem: schemas.FileItem, _: User = Depends(get_current_active_manage_user) ) -> Any: """ 删除文件或目录 @@ -150,7 +151,7 @@ def delete( @router.post("/download", summary="下载文件") def download( - fileitem: schemas.FileItem, _: User = Depends(get_current_active_superuser) + fileitem: schemas.FileItem, _: User = Depends(get_current_active_manage_user) ) -> Any: """ 下载文件或目录 @@ -166,7 +167,7 @@ def download( @router.post("/image", summary="预览图片") def image( - fileitem: schemas.FileItem, _: User = Depends(get_current_active_superuser) + fileitem: schemas.FileItem, _: User = Depends(get_current_active_manage_user) ) -> Any: """ 下载文件或目录 @@ -185,7 +186,7 @@ def rename( fileitem: schemas.FileItem, new_name: str, recursive: Optional[bool] = False, - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 重命名文件或目录 diff --git a/app/api/endpoints/transfer.py b/app/api/endpoints/transfer.py index 05f1825f..d93eb3c5 100644 --- a/app/api/endpoints/transfer.py +++ b/app/api/endpoints/transfer.py @@ -12,7 +12,10 @@ from app.core.security import verify_token, verify_apitoken from app.db import get_db from app.db.models import User from app.db.models.transferhistory import TransferHistory -from app.db.user_oper import get_current_active_superuser +from app.db.user_oper import ( + get_current_active_manage_user, + get_current_active_superuser, +) from app.helper.directory import DirectoryHelper from app.log import logger from app.schemas import ( @@ -182,7 +185,7 @@ def _get_manual_transfer_target_key( def match_manual_transfer_target_path( transer_item: ManualTransferItem, db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 根据源文件匹配手动整理目的路径。 @@ -242,7 +245,7 @@ def manual_transfer( transer_item: ManualTransferItem, background: Optional[bool] = False, db: Session = Depends(get_db), - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 手动转移,文件或历史记录,支持自定义剧集识别格式 @@ -528,7 +531,7 @@ def manual_transfer( ) def recommend_episode_format( recommend_item: EpisodeFormatRecommendItem, - _: User = Depends(get_current_active_superuser), + _: User = Depends(get_current_active_manage_user), ) -> Any: """ 根据目录样本推荐集数定位模板 diff --git a/tests/test_api_authorization.py b/tests/test_api_authorization.py index c9eabc56..8fae0c65 100644 --- a/tests/test_api_authorization.py +++ b/tests/test_api_authorization.py @@ -8,13 +8,19 @@ from fastapi import HTTPException from starlette.requests import Request from starlette.responses import Response +from app.api.endpoints import dashboard as dashboard_endpoint +from app.api.endpoints import history as history_endpoint from app.api.endpoints import login as login_endpoint from app.api.endpoints import plugin as plugin_endpoint +from app.api.endpoints import site as site_endpoint +from app.api.endpoints import storage as storage_endpoint from app.api.endpoints import system as system_endpoint +from app.api.endpoints import transfer as transfer_endpoint from app.api.endpoints import user as user_endpoint -from app.api.endpoints import dashboard as dashboard_endpoint from app.core.security import verify_resource_token from app.db.user_oper import ( + get_current_active_manage_user, + get_current_active_manage_user_async, get_current_active_superuser, get_current_active_superuser_async, get_current_active_user_async, @@ -75,6 +81,43 @@ def test_plugin_dashboard_endpoints_require_superuser(): assert _dependency_of(plugin_endpoint.plugin_dashboard, "_") is get_current_active_superuser +def test_manage_page_endpoints_accept_manage_permission(): + """管理页面接口允许具备 manage 权限的普通用户访问。""" + sync_endpoints = [ + storage_endpoint.list_files, + storage_endpoint.mkdir, + storage_endpoint.delete, + storage_endpoint.download, + storage_endpoint.image, + storage_endpoint.rename, + site_endpoint.update_cookie_by_body, + site_endpoint.update_cookie, + site_endpoint.refresh_userdata, + history_endpoint.delete_transfer_history, + history_endpoint.ai_redo_transfer_history, + history_endpoint.batch_ai_redo_transfer_history, + transfer_endpoint.match_manual_transfer_target_path, + transfer_endpoint.manual_transfer, + transfer_endpoint.recommend_episode_format, + ] + async_endpoints = [ + site_endpoint.read_sites, + site_endpoint.add_site, + site_endpoint.update_site, + site_endpoint.update_sites_priority, + site_endpoint.read_userdata_latest, + site_endpoint.read_userdata, + site_endpoint.site_resource, + site_endpoint.read_site, + site_endpoint.delete_site, + ] + + for endpoint in sync_endpoints: + assert _dependency_of(endpoint, "_") is get_current_active_manage_user + for endpoint in async_endpoints: + assert _dependency_of(endpoint, "_") is get_current_active_manage_user_async + + def test_system_public_setting_allows_only_non_sensitive_keys(monkeypatch): """公开系统设置接口只能读取明确列入白名单的非敏感配置。""" calls = []