langgenius / langgenius/dify

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

Open
#41,316 3 comments 1 reaction 0 assignees View on GitHub
1.17.0
Dominant language
TypeScript
Stars
156k
Forks
24.6k
Avg merge
22h 9m
Merged PRs (30d)
610

Description

### Self Checks

- [x] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542).
- [x] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general).
- [x] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones.
- [x] I confirm that I am using English to submit this report, otherwise it will be closed.
- [x] 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
- [x] Please do not modify this template :) and fill in all the required fields.

### Dify version

1.17.0 (langgenius/dify-api:1.17.0)

### Cloud or Self Hosted

Self Hosted (Docker Compose)

### Steps to reproduce

1. In a workspace, have an **Owner** account and a **Member** account (editor).
2. As the Member, create 5 knowledge bases with permission **`only_me`**, and 1 knowledge base with **`partial_members`** shared to the Owner.
3. As the Owner, create 3 knowledge bases with permission **`only_me`**.
4. Create a Knowledge Base Service API key (it does not matter which account creates it — keys are workspace-scoped, `api_tokens` has no creator column).
5. Call `GET /v1/datasets` with that token.

**Observed result:** the API returns exactly the **Owner's view** — the Owner's three `only_me` datasets plus the `partial_members` dataset shared to the Owner — and **omits all five `only_me` datasets created by the Member**, even when the token is used by the Member who created them.

Two different API keys created by different accounts return byte-identical results.

Additionally:

6. With the same token, call `GET /v1/datasets/{id_of_member_only_me_dataset}` directly.

**Observed result:** HTTP **200** with full dataset detail. So the dataset is invisible in the list but fully operable via its direct endpoints.

### ✔️ Expected Behavior

Consistent, predictable semantics for the workspace-scoped Knowledge Base API. Per the docs ("a single knowledge base API key can access all knowledge bases **visible** to ..."), at minimum:

- Listing and direct access should agree on visibility, and
- A Member's `only_me` datasets should not silently disappear from `GET /v1/datasets` (the Member cannot manage their own private datasets programmatically with any token), or this limitation should be explicitly documented.

### ❌ Actual Behavior

- Every dataset Service API request is executed **as the workspace Owner account**: `validate_dataset_token` looks up `TenantAccountJoin.role IN ('owner')`, fetches that Account and calls `_update_request_context_with_user(account)` (see `api/controllers/service_api/wraps.py`, ~lines 342–361; the code even carries a `TODO: only owner information is required` comment).
- The list endpoint then applies the legacy per-user visibility filter against that impersonated identity (`DatasetService.get_datasets` in `api/services/dataset_service.py`: `ONLY_ME AND maintainer == user.id OR ALL_TEAM OR PARTIAL_TEAM AND id IN permitted_ids`) — so results reflect the Owner's personal view, not workspace-level API access.
- The single-dataset path (`DatasetApiResource` dispatch in the same wraps module) only validates tenant ownership and `enable_api`, with no permission filter — hence list/direct-GET inconsistency.

### Evidence from our instance (9 datasets in DB, sanitized)

| Dataset | permission | creator | in `GET /v1/datasets`? | direct `GET /v1/datasets/{id}` |
|---|---|---|---|---|
| kb-A | only_me | Owner | ✅ | ✅ |
| kb-B | only_me | Owner | ✅ | ✅ |
| kb-C | only_me | Owner | ✅ | ✅ |
| kb-D…kb-H (5) | only_me | Member | ❌ missing | ✅ 200 |
| kb-I | partial_members (shared to Owner) | Member | ✅ | ✅ |

Both tokens (created by Owner and by Member respectively) returned identical results.

### Root cause analysis

Knowledge Base Service API keys are intentionally workspace-scoped (`api_tokens.tenant_id`, no creator binding). However `validate_dataset_token` additionally impersonates the tenant's Owner account to satisfy code paths that read `current_user`. Because `GET /v1/datasets` feeds that same `current_user` into the per-user visibility filter, the workspace-scoped key effectively inherits the **Owner's private-view semantics** — which contradicts both the documented contract and the unfiltered single-resource endpoints.

### Suggested fix

Pick one consistent contract, e.g.:

1. Treat the dataset Service API as workspace-administrative access: skip the per-user visibility filter on list (matching today's single-resource behavior); **or**
2. Bind dataset API keys to their creating account (`created_by`) and apply that account's visibility rules; **or**
3. Keep current behavior but document it clearly and align the docs ("visible" = visible to the workspace Owner).

Option 1 is the smallest change and removes the list/direct inconsistency.

### Environment details

- Docker Compose deployment (api 1.17.0) behind bundled nginx.
- Reproduced 100% with two distinct API keys; verified against DB state directly.

Contributor guide

Open the contributing guide

Research direction

Start in api/controllers/service_api/wraps.py at validate_dataset_token and the DatasetApiResource dispatch, then trace DatasetService.get_datasets in api/services/dataset_service.py. Reproduce the list and direct-GET calls with owner and member-only datasets, and confirm the intended workspace-key contract with maintainers. Done means list visibility and direct access follow one documented, tested permission model.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, authorization, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.