Skip to content

fix: 系统性修复多用户数据隔离问题#474

Merged
pancacake merged 4 commits into
HKUDS:devfrom
wedone:fix/mu-data
May 12, 2026
Merged

fix: 系统性修复多用户数据隔离问题#474
pancacake merged 4 commits into
HKUDS:devfrom
wedone:fix/mu-data

Conversation

@wedone
Copy link
Copy Markdown
Contributor

@wedone wedone commented May 11, 2026

Description

多用户数据隔离系统性修复

本 PR 系统性修复了 DeepTutor 多用户模式下的数据隔离问题,确保不同用户的数据完全隔离,避免跨用户数据泄露和路径混淆。

核心问题

  1. HTTP 请求用户上下文缺失中间件保障 - 仅依赖 require_auth 依赖,中间件层无保障
  2. WebSocket 端点缺少手动认证 - tutorbot/vision_solver/question/knowledge/book 等 WS 端点未正确认证用户
  3. 路径服务过早绑定 - CoWriterStorage__init__ 中固定 path_service,单例后路径不再变化
  4. 路径降级无日志 - get_path_service() 静默降级到默认实例,无日志输出
  5. 前端 API 调用未携带认证信息 - 多个前端组件使用原生 fetch() 而非 apiFetch(),导致多用户模式下 401 错误

修复内容

后端修复

  • 添加 user_context_middleware 中间件,确保每个 HTTP 请求都正确设置用户 ContextVar
  • 添加 ws_require_auth() 统一 WebSocket 认证函数,所有 WS 端点使用
  • 修复 tutorbot/vision_solver/question/knowledge/book 的 WebSocket 认证
  • 统一 chat/solve/unified_ws 的 WebSocket 认证为 ws_require_auth()
  • CoWriterStorage 改为 @property 延迟获取 path_service + 按 workspace 缓存
  • BaseSessionManager: path_service/sessions_file 改为 @property 延迟解析
  • BookStorage/BookEngine: path_service 延迟解析 + get_book_storage/engine 按路径缓存
  • TutorBotManager: path_service 延迟解析 + 修复硬编码 data/tutorbot 路径
  • MemoryService: _path_service 改为 @property 延迟解析
  • get_path_service() 降级时输出 warning 日志

前端修复

  • 替换 18 个文件中的 fetch()apiFetch(),确保携带认证信息
  • 移除冗余的 credentials: 'include'apiFetch 已处理)
  • 涉及模块:auth, admin, session, skills, book, knowledge, notebook, co-writer, playground, agents, sidebar 组件等

架构改进

延迟解析模式:路径在请求时(用户上下文已设置)才解析,而非模块加载时
按路径缓存:不同用户获得不同实例,同一用户复用实例,兼顾隔离和性能

测试验证

  • 多用户模式下各模块数据完全隔离
  • TutorBot 按用户隔离到 multi-user/<uid>/tutorbot/ 而非共享的 data/tutorbot/
  • 前端所有 API 调用正确携带认证 token,无 401 错误

Related Issues

  • Closes #...
  • Related to #...

Module(s) Affected

  • api
  • services
  • web (Frontend)
  • Other: multi-user, book, co-writer, memory, tutorbot

Checklist

  • I have read and followed the contribution guidelines.
  • My code follows the project's coding standards.
  • I have run pre-commit run --all-files and fixed any issues. (ruff/ruff-format/prettier passed; mypy/bandit failures are pre-existing upstream issues)
  • I have added relevant tests for my changes. (Tested in Docker container, all multi-user functionality verified)
  • I have updated the documentation (if necessary).
  • My changes do not introduce any new security vulnerabilities.

Additional Notes

分支基准: 本 PR 基于 v1.3.8 开发,包含 3 个修复提交:

提交 说明
429ab6b fix: replace all fetch() with apiFetch() for authenticated API calls
230dfe5 fix(multi-user): 修复旧版会话管理器的路径捕获问题,实现完整的数据隔离
6d5f187 fix: 系统性修复多用户数据隔离问题

wedone and others added 4 commits May 12, 2026 15:32
- Replace fetch() with apiFetch() in 18 files across web/
- Remove redundant credentials: 'include' since apiFetch handles it
- Affected modules: auth, admin, session, skills, book, knowledge,
  notebook, co-writer, playground, agents, sidebar components,
  SaveToNotebookModal, VersionBadge, useTextSource
- Fixes 401 Unauthorized errors in multi-user mode
核心修改:
- BaseSessionManager: path_service/sessions_file 改为 @Property 延迟解析
- chat.py/solve.py: 移除模块级单例,WebSocket 端点添加手动认证
- BookStorage/BookEngine: path_service 延迟解析 + get_book_storage/engine 按路径缓存
- TutorBotManager: path_service 延迟解析 + 修复硬编码 data/tutorbot 路径
- MemoryService: _path_service 改为 @Property 延迟解析

修复原理:延迟解析确保路径在请求时(用户上下文已设置)才解析;
按路径缓存确保不同用户获得不同实例,同一用户复用实例。

TutorBot 现在按用户隔离到 multi-user/<uid>/tutorbot/ 而非共享的 data/tutorbot/
核心问题:
1. HTTP请求的用户上下文仅依赖require_auth依赖,中间件层无保障
2. WebSocket端点(tutorbot/vision_solver/question/knowledge/book)缺少手动认证
3. CoWriterStorage在__init__中固定path_service,单例后路径不再变化
4. get_path_service()静默降级到默认实例,无日志

修复内容:
- 添加user_context_middleware中间件,确保每个HTTP请求都正确设置用户ContextVar
- 添加ws_require_auth()统一WebSocket认证函数,所有WS端点使用
- 修复tutorbot/vision_solver/question/knowledge/book的WebSocket认证
- 统一chat/solve/unified_ws的WebSocket认证为ws_require_auth()
- CoWriterStorage改为@Property延迟获取path_service + 按workspace缓存
- get_path_service()降级时输出warning日志
…ddleware

After rebasing onto current dev (v1.3.10), several issues surfaced that the
original PR did not account for:

Frontend (rebase regressions from older PR base):
- web/lib/knowledge-api.ts: restore 6 KnowledgeBaseSummary fields
  (id, source, assigned, read_only, provenance_label, available) that the
  multi-user knowledge feature added between the PR base and current dev.
- web/app/(workspace)/agents/page.tsx: restore the array-vs-{bots}
  response shape fallback; the tutorbot list endpoint returns either form.
- web/components/sidebar/BookRecent.tsx: wrap path with apiUrl() — the
  apiFetch swap accidentally dropped the wrapper.
- web/components/space/MemorySection.tsx: restore try/catch error toasts
  in loadMemory and clearMemory (still present in saveMemory and
  refreshMemory; were dropped in load/clear).

Backend (decouple auth from middleware):
- Remove user_context_middleware from api/main.py. It was redundant with
  require_auth (sets the user ContextVar for every protected route) and
  ws_require_auth (sets it for every WebSocket). When AUTH_ENABLED=true and
  a request had no valid token, the middleware fell back to
  local_admin_user() — harmless because require_auth then raised 401, but
  a latent footgun if any code reads the ContextVar before the dependency
  resolves. Removing it makes the auth flow single-pathed and removes the
  "ctx set twice" pattern.
- auth.py: add explicit type annotations to ws_require_auth (returns
  Token | _WsAuthFailed) and the ws_auth_failed sentinel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pancacake pancacake merged commit 6168a8b into HKUDS:dev May 12, 2026
4 of 9 checks passed
pancacake pushed a commit to wedone/DeepTutor that referenced this pull request May 12, 2026
deleteMessage was the one remaining raw fetch() call in session-api.ts.
After PR HKUDS#474 wired multi-user auth through apiFetch (which attaches the
JWT bearer token from local storage / cookies), this call would fail with
401 Unauthorized in multi-user mode.

Switch to apiFetch to match the convention established for every other
authenticated session call in this file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kagura-agent pushed a commit to kagura-agent/DeepTutor that referenced this pull request May 21, 2026
deleteMessage was the one remaining raw fetch() call in session-api.ts.
After PR HKUDS#474 wired multi-user auth through apiFetch (which attaches the
JWT bearer token from local storage / cookies), this call would fail with
401 Unauthorized in multi-user mode.

Switch to apiFetch to match the convention established for every other
authenticated session call in this file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants