Bug: Dataset Service API ignores per-user knowledge base permissions — every token is authorized as the workspace Owner (list vs direct GET

该报错通常出现在自托管 Dify 1.17.0 中,通过 Dataset Service API(如 GET /v1/datasets )调用知识库时,Member 账号创建的 only_me 知识库不会出现在列表里,但直接 GET /v1/datasets/{id} 却返回 200。优先排查 Se

快速结论:该报错通常出现在自托管 Dify 1.17.0 中,通过 Dataset Service API(如 GET /v1/datasets)调用知识库时,Member 账号创建的 only_me 知识库不会出现在列表里,但直接 GET /v1/datasets/{id} 却返回 200。优先排查 Service API 令牌校验时是否固定以工作区 Owner 身份执行,以及列表端点是否沿用了按用户可见性过滤的旧逻辑。

适用环境:Issue 已确认:Dify 1.17.0(langgenius/dify-api:1.17.0),Self Hosted(Docker Compose)。其他 Python、CUDA、显卡或依赖版本信息 Issue 未提供。

最快修复方案:暂无确认的一步修复方案。Issue 讨论中仅把“在 Service API 的列表端点跳过按用户的可见性过滤,使其与单资源 GET 行为一致”作为可优先尝试的最小改动方向,尚未验证或合入。

注意事项:上述改动属于代码层修复,涉及 api/controllers/service_api/dataset/dataset.py 与 api/services/dataset_service.py 的过滤逻辑;讨论还提到服务层 user=None 的回退分支当前只返回 ALL_TEAM 数据集,若要改为返回 tenant 内全部数据集需要额外调整。升级前请评估 Service API 令牌的权限语义变化,不要据此认为问题已官方修复。

问题场景

用户在自托管 Dify 1.17.0 的工作区中同时存在 Owner 与 Member(editor)账号,并用 Knowledge Base Service API key 调用 GET /v1/datasets 拉取知识库列表。结果列表恰好等于 Owner 的个人视图:Owner 自己创建的 3 个 only_me 数据集和共享给 Owner 的 partial_members 数据集可见,而 Member 创建的 5 个 only_me 数据集全部缺失,即便使用由该 Member 创建的 API key 也一样。随后用同一令牌直接请求 Member 私有数据集的 GET /v1/datasets/{id},却返回 HTTP 200 和完整详情,形成列表与直读不一致。

报错原文

Bug: Dataset Service API ignores per-user knowledge base permissions — every token is authorized as the workspace Owner (list vs direct GET inconsistent)

GET /v1/datasets  -> Owner's only_me datasets returned, Member's only_me datasets omitted
GET /v1/datasets/{member_only_me_dataset_id} -> HTTP 200 with full dataset detail

原因分析

根据 Issue 正文与维护者回复的代码定位,原因大致如下:

  • validate_dataset_token(api/controllers/service_api/wraps.py,约 342–361 行)会通过 TenantAccountJoin.role IN ('owner') 查出工作区 Owner 账号,并调用 _update_request_context_with_user(account) 将其设为当前用户;代码中带 TODO: only owner information is required 注释,说明这是有意的设计选择,但带来了非预期的副作用。
  • GET /v1/datasets 把被冒充的 Owner 作为 current_user 传入 DatasetService.get_datasets(api/services/dataset_service.py),随后套用旧的按用户过滤条件 ONLY_ME AND maintainer == user.id OR ALL_TEAM OR PARTIAL_TEAM AND id IN permitted_ids,因此结果反映的是 Owner 个人视图,而非工作区级 API 访问。
  • 单数据集路径 DatasetApiResource 走 check_dataset_permission(dataset, current_user, session),当 user.current_role == TenantAccountRole.OWNER 时跳过 ONLY_ME 检查,所以被冒充的 Owner 能读取 tenant 内任意数据集,包括 Member 私有数据集,这解释了列表与直读不一致。
  • 由于 api_tokens 没有创建者列,密钥是工作区级的,不同账号创建的令牌返回完全相同的结果。

可能原因补充:讨论中提到该问题属于更广泛的资源边界一致性工作(#37983),但其子任务 #37985 只覆盖了元数据/文档所有权,未覆盖数据集列表可见性。

环境排查

  • 确认 Dify 版本是否为 1.17.0(langgenius/dify-api:1.17.0),其他版本行为可能不同。
  • 确认部署方式为 Self Hosted(Docker Compose)。
  • 确认调用使用的是 Knowledge Base Service API key,而非 Web 前端会话。
  • 核对测试账号的角色:是否存在 Owner 与 Member(editor)两类账号,并记录各自创建的 only_me / partial_members 数据集数量。
  • 对照 api/controllers/service_api/wraps.py 中 validate_dataset_token 的实际实现,确认是否仍按 Owner 身份注入当前用户。
  • Issue 未提供 Python、CUDA、PyTorch、显卡或节点依赖信息,这些不作为本问题的排查项。

解决步骤

  1. 先复现并记录基线:用 Owner 与 Member 各创建一个 Service API key,分别调用 GET /v1/datasets,确认两个令牌返回结果是否完全一致,以及 Member 的 only_me 数据集是否缺失。
  2. 用同一令牌对缺失的数据集调用 GET /v1/datasets/{id},确认是否返回 HTTP 200。若列表缺失但直读 200,即符合本 Issue 的典型症状。
  3. 在代码中定位 validate_dataset_token,确认当前用户是否被固定设置为工作区 Owner,并记录其与列表继续往下走的调用链路。
  4. 检查 DatasetService.get_datasets 中的可见性过滤分支,确认当传入用户为 Owner 时会返回哪些数据集。
  5. 可优先尝试的修复方向:在 DatasetListApi.get 中为 Service API 上下文绕过按用户的可见性过滤,使其与单资源 GET 的“仅校验 tenant 归属与 enable_api”行为保持一致。
  6. 若采用上述方向,同时检查服务层 user=None 的回退分支;讨论指出该分支当前只限制返回 ALL_TEAM 数据集,需要调整为返回 tenant 内全部数据集才能达到一致效果。
  7. 在改动生效后重新用两个令牌调用列表与直读端点,对比结果。

验证方法

构造与 Issue 相同的场景:Owner 3 个 only_me、Member 5 个 only_me 加 1 个共享给 Owner 的 partial_members。修复后,GET /v1/datasets 与 GET /v1/datasets/{id} 对同一数据集的可见性应一致,且 Member 的 only_me 数据集不再从列表中静默消失;由不同账号创建的 API key 结果一致性应按最终确定的权限语义核对。若未做代码修复,该问题在 1.17.0 上仍会复现,不能以单次调用成功作为解决依据。

参考来源

langgenius/dify #41316

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 32033

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注