Dataset Permission Model#
Dify's dataset access control uses two coexisting layers that must stay in sync :
- Legacy resource-level enum β stored on every
Datasetrow; always evaluated - Enterprise RBAC β gated by the
RBAC_ENABLEDconfig flag; only active in enterprise deployments
Legacy Permission Enum#
PermissionEnum (in api/models/enums.py) defines three visibility levels:
| Value | Meaning |
|---|---|
only_me | Only the dataset maintainer can access |
all_team_members | All workspace members can access |
partial_members | Only explicitly listed members (via the DatasetPermission join table) can access |
DatasetPermissionEnum in api/models/dataset.py is a backward-compatible alias for PermissionEnum; new code should import PermissionEnum from models.enums directly .
Legacy Permission Enforcement#
DatasetService.check_dataset_permission() is the primary gate. It:
- Rejects cross-tenant access unconditionally
- Bypasses all further checks for the
OWNERrole - Enforces
ONLY_MEby comparingdataset.maintainerto the requesting user - Enforces
PARTIAL_TEAMby querying theDatasetPermissiontable for an explicit record
A second variant, check_dataset_operator_permission(), applies the same logic but is used for write/mutation paths.
Critical gap: Neither method contains an RBAC_ENABLED branch. When RBAC is active, these functions are still called and may block access that RBAC would allow β or, conversely, they may pass through access that RBAC should block .
RBAC-Aware Paths#
The following DatasetService methods do branch on RBAC_ENABLED:
get_datasets()β filters the dataset list differently depending on RBAC mode. With RBAC enabled andaccessible_dataset_idsprovided, it restricts to those IDs. Without RBAC, it applies the legacy SQL filter usingDatasetPermissionEnumvalues._can_manage_all_datasets()β checksRBAC_ENABLEDfirst, then callsRBACService.MyPermissions.get()to look for thedataset.create_and_managementpermission key.
The RBACService.DatasetPermissions.batch_get() and RBACService.MyPermissions.get() methods bridge legacy and RBAC: when RBAC is disabled, they fall back to _LEGACY_MY_PERMISSIONS, which maps tenant roles (OWNER/ADMIN/EDITOR, DATASET_OPERATOR, NORMAL) to dataset.acl.* permission keys .
RBAC β Legacy Sync Requirements#
Whenever Dataset.permission changes β or workspace membership changes β the RBAC access policy must be updated. The invariant:
all_team_membersβ RBAC whitelist must setautomatic_include_workspace_members=Trueand dispatchinitialize_created_app_rbac_access_taskto grant the default policy to all current members.partial_members/only_meβ RBAC whitelist must setautomatic_include_workspace_members=False.
Mutations that miss this call leave the two layers out of sync, causing access errors even for users who appear correctly permissioned in one layer.
Known Gaps and Fixes#
| Issue | Status |
|---|---|
| #39037 β Several code paths call only the legacy check, never updating RBAC state | Partially fixed: PR #40599 fixed CreateDatasetDocumentListApi.post; PR #41022 added if not RBAC_ENABLED guard to the dataset GET endpoint in datasets.py. Other call sites (e.g., 8 in datasets_document.py) still call check_dataset_permission without this guard . |
#38910 β RBAC all β workspace member sync missing | Fixed: PR #39735 added initialize_created_app_rbac_access_task dispatch in RBACDatasetWhitelistApi.put when automatic_include_workspace_members=True . |
#39226 β partial_members / only_me β RBAC sync missing | Open: RBAC whitelist config must be updated to automatic_include_workspace_members=False on legacy permission change . |
| #39379 β RBAC not initialized on several dataset creation paths | Partially open: RAG pipeline empty dataset and DSL import paths still skip RBAC initialization . |
Specific creation-path RBAC initialization status :
| Entry point | RBAC initialized? |
|---|---|
| Console empty dataset | β Yes |
| Public API create dataset | β Yes |
File upload (save_document_without_dataset_id) | β Yes (fixed PR #40599) |
| RAG pipeline empty dataset | β No |
| External knowledge base | β Yes (fixed PR #39930) |
| RAG pipeline DSL import | β No |
Key Files#
| File | Purpose |
|---|---|
api/models/enums.py | PermissionEnum definition |
api/models/dataset.py | DatasetPermissionEnum alias, Dataset model, DatasetPermission table |
api/services/dataset_service.py | check_dataset_permission, check_dataset_operator_permission, get_datasets, _can_manage_all_datasets |
api/services/enterprise/rbac_service.py | _LEGACY_MY_PERMISSIONS mapping, RBACService.DatasetPermissions, RBACService.MyPermissions |
api/configs/enterprise/__init__.py | RBAC_ENABLED flag |