feat: 新增用户管理(用户增删改查、密码重置、角色权限、会话认证)与 API 指南
- 新增 app/api/deps.py、app/core/users.py、app/core/sessions.py:会话鉴权依赖、 用户存储(PBKDF2-HMAC-SHA256 + 随机 salt,Redis/内存降级)、会话签发与校验(TTL 12h) - auth.py 新增用户管理端点(列表/创建/重置密码/删除)与 admin/user 角色权限边界, user 访问用户管理返回 1006,禁删自己与最后一个 admin - admin.html 新增用户管理面板(仅 admin 挂载)与 API 指南在线测试台 - Dockerfile 将 uv 放入 PATH;docker-compose 调整 qdrant 依赖为 service_started 并移除依赖 curl 的 healthcheck(官方镜像不含 curl) - 新增用户管理测试(users/sessions/auth_api/auth_integration),全量 461 项测试通过 Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
This commit is contained in:
+52
-6
@@ -1,16 +1,21 @@
|
||||
"""pytest 全局夹具:覆盖 JWT 认证依赖,让现有 API 测试默认以 admin 身份运行
|
||||
|
||||
业务接口(search/document/knowledge)已加 Depends(get_current_user)、
|
||||
DELETE /documents 加了 Depends(require_admin)。这里通过 autouse 夹具把两个依赖
|
||||
统一替换为返回固定 admin AuthUser 的 lambda,使现有 API 测试无需改动即可通过认证。
|
||||
单个测试需要走真实认证逻辑时(如 tests/test_auth.py),可在测试函数内
|
||||
pop 掉对应 override,autouse fixture yield 后会统一 clear。
|
||||
业务接口(search/knowledge/settings)仍使用 app.core.auth 的 JWT 依赖,
|
||||
这里通过 autouse 夹具把两个依赖统一替换为返回固定 admin AuthUser 的 lambda,
|
||||
使现有 API 测试无需改动即可通过认证。单个测试需要走真实认证逻辑时,
|
||||
可在测试函数内 pop 掉对应 override,autouse fixture yield 后会统一 clear。
|
||||
|
||||
文档变更类端点(POST /documents、/documents/upload、DELETE /documents/{id})
|
||||
使用 app.api.deps 的会话认证(UserStore/SessionStore),由 auth_stores /
|
||||
admin_headers 夹具注入内存存储并签发真实 session token。
|
||||
|
||||
另外清理 LLM 客户端 / 解析插件 / 去重策略三处进程级缓存,避免跨测试串扰
|
||||
(不同测试可能注入不同的 redis/ollama 实例,缓存按配置签名而非实例区分)。
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -23,13 +28,54 @@ TEST_USER = AuthUser(username="testuser", role="admin", created_at=datetime.now(
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def override_auth():
|
||||
"""所有测试默认以 admin 身份运行;测试结束清理 dependency_overrides"""
|
||||
"""所有测试默认以 admin 身份运行(旧 JWT 依赖);测试结束清理 dependency_overrides"""
|
||||
app.dependency_overrides[get_current_user] = lambda: TEST_USER
|
||||
app.dependency_overrides[require_admin] = lambda: TEST_USER
|
||||
yield
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def auth_stores(monkeypatch: pytest.MonkeyPatch):
|
||||
"""注入内存 UserStore/SessionStore 到 deps 单例,并预置 admin 账号(admin/admin-pass-123)
|
||||
|
||||
同时关闭 lifespan 的默认管理员引导,避免测试库被写入随机密码账号。
|
||||
返回 (user_store, session_store),测试可直接操作用户数据。
|
||||
"""
|
||||
import app.main as main_module
|
||||
from app.api import deps
|
||||
from app.core.sessions import SessionStore
|
||||
from app.core.users import UserStore
|
||||
|
||||
user_store = UserStore(None)
|
||||
session_store = SessionStore(None)
|
||||
monkeypatch.setattr(deps, "_user_store", user_store)
|
||||
monkeypatch.setattr(deps, "_session_store", session_store)
|
||||
|
||||
async def _noop_bootstrap(*args: Any, **kwargs: Any) -> None:
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(main_module, "bootstrap_admin", _noop_bootstrap)
|
||||
|
||||
async def _seed() -> None:
|
||||
await user_store.create("admin", "admin-pass-123", role="admin")
|
||||
|
||||
asyncio.run(_seed())
|
||||
return user_store, session_store
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def admin_headers(auth_stores) -> dict[str, str]:
|
||||
"""为内存 admin 签发真实 session,返回 Authorization Bearer 请求头"""
|
||||
_, session_store = auth_stores
|
||||
|
||||
async def _login() -> str:
|
||||
return await session_store.create("admin", "admin")
|
||||
|
||||
token = asyncio.run(_login())
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _invalidate_runtime_caches():
|
||||
"""每个测试前后清理 LLM/解析插件/去重策略进程级缓存
|
||||
|
||||
Reference in New Issue
Block a user