[PR #31669] feat(api): add dataset-scoped API key permission validation #33346

Open
opened 2026-02-21 20:53:07 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/langgenius/dify/pull/31669

State: open
Merged: No


Description

Important

  1. Make sure you have read our contribution guidelines
  2. Ensure there is an associated issue and you have been assigned to it
  3. Use the correct syntax to link this PR: #31409

Summary

This PR introduces dataset-scoped API keys that are bound to specific datasets, providing finer-grained access control compared to existing tenant-level keys.

Resolve https://github.com/langgenius/dify/issues/31409

Problem

Currently, Dataset API Keys are tenant-level only (app_id=NULL), which means:

  • A single key can access all datasets within the tenant
  • No isolation between different datasets
  • Over-privileged access for scenarios requiring single-dataset access

Solution

Introduce dataset-scoped API keys that coexist with tenant-level keys:

Key Type app_id Value Access Scope
Tenant-level NULL All datasets in tenant
Dataset-scoped <dataset_id> Only the bound dataset

Changes

  1. Permission validation (controllers/service_api/wraps.py)

    • Added dataset-scoped key permission check in validate_dataset_token decorator
    • Dataset-scoped keys can only access their bound dataset (403 Forbidden otherwise)
    • Dataset-scoped keys cannot access tenant-level endpoints (e.g., list all datasets)
  2. API key management (controllers/console/apikey.py)

    • Changed DatasetApiKeyListResource.resource_id_field to use app_id for storing dataset_id
    • Changed DatasetApiKeyResource.resource_id_field to use app_id
  3. Tenant-level key listing (controllers/console/datasets/datasets.py)

    • Added app_id.is_(None) filter to ensure tenant-level endpoint only returns tenant-level keys
  4. Unit tests (tests/unit_tests/controllers/service_api/test_dataset_token_validation.py)

    • Added 5 test cases covering permission validation scenarios

Design Principles

  • No database migration: Reuse existing app_id field to store dataset_id
  • Backward compatible: Existing tenant-level keys continue to work unchanged
  • Minimal invasion: Only modify necessary code paths

Screenshots

N/A - This is a backend API change with no UI modifications.

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.
  • I ran make lint and make type-check (backend) and cd web && npx lint-staged (frontend) to appease the lint gods

**Original Pull Request:** https://github.com/langgenius/dify/pull/31669 **State:** open **Merged:** No --- ### Description > [!IMPORTANT] > > 1. Make sure you have read our [contribution guidelines](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) > 1. Ensure there is an associated issue and you have been assigned to it > 1. Use the correct syntax to link this PR: #31409 ## Summary This PR introduces dataset-scoped API keys that are bound to specific datasets, providing finer-grained access control compared to existing tenant-level keys. Resolve https://github.com/langgenius/dify/issues/31409 ### Problem Currently, Dataset API Keys are tenant-level only (`app_id=NULL`), which means: - A single key can access all datasets within the tenant - No isolation between different datasets - Over-privileged access for scenarios requiring single-dataset access ### Solution Introduce dataset-scoped API keys that coexist with tenant-level keys: | Key Type | `app_id` Value | Access Scope | |----------|----------------|--------------| | Tenant-level | `NULL` | All datasets in tenant | | Dataset-scoped | `<dataset_id>` | Only the bound dataset | ### Changes 1. **Permission validation** (`controllers/service_api/wraps.py`) - Added dataset-scoped key permission check in `validate_dataset_token` decorator - Dataset-scoped keys can only access their bound dataset (403 Forbidden otherwise) - Dataset-scoped keys cannot access tenant-level endpoints (e.g., list all datasets) 2. **API key management** (`controllers/console/apikey.py`) - Changed `DatasetApiKeyListResource.resource_id_field` to use `app_id` for storing dataset_id - Changed `DatasetApiKeyResource.resource_id_field` to use `app_id` 3. **Tenant-level key listing** (`controllers/console/datasets/datasets.py`) - Added `app_id.is_(None)` filter to ensure tenant-level endpoint only returns tenant-level keys 4. **Unit tests** (`tests/unit_tests/controllers/service_api/test_dataset_token_validation.py`) - Added 5 test cases covering permission validation scenarios ### Design Principles - **No database migration**: Reuse existing `app_id` field to store `dataset_id` - **Backward compatible**: Existing tenant-level keys continue to work unchanged - **Minimal invasion**: Only modify necessary code paths ## Screenshots N/A - This is a backend API change with no UI modifications. ## Checklist - [ ] This change requires a documentation update, included: [Dify Document](https://github.com/langgenius/dify-docs) - [x] I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!) - [x] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change. - [x] I've updated the documentation accordingly. - [x] I ran `make lint` and `make type-check` (backend) and `cd web && npx lint-staged` (frontend) to appease the lint gods ---
yindo added the pull-request label 2026-02-21 20:53:07 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#33346