Skip to content

Clarify polices in the roles and users endpoints are directus_access records #520

@wmasman

Description

@wmasman

Describe the Bug

In Directus 11.x (tested on 11.1.1, containerized/Fly.io), when a Role is linked to a Policy via the directus_access table, the Directus API and Permission Engine incorrectly use the id (primary key) of the directus_access record as the Policy UUID, ignoring the actual policy foreign key column.

This causes "Ghost Policies" where the API reports the Role is linked to a non-existent Policy UUID (which is actually the Access record's UUID), resulting in 403 Forbidden errors even when the database link is correct.

To Reproduce

Create a Role (e.g., "Test Role").
Create a Policy (e.g., "Test Policy") with some permissions.
Link them via SQL (simulating what the UI does, or a migration):
INSERT INTO directus_access (id, role, policy, sort)
VALUES (
gen_random_uuid(), -- Generates a random UUID, e.g., '5fd3eefd...'
'',
'', -- The actual policy, e.g., 'a1111111...'
1
);
Restart Directus (to flush cache).
Query the Role via API: GET /roles/
Observe: The policies array contains '5fd3eefd...' (the Access ID), NOT 'a1111111...' (the Policy ID).
Result: Permissions defined in "Test Policy" are not applied.
Expected Behavior
Directus should read the policy column from directus_access to determine which Policy is linked to the Role. The id of the access record should be irrelevant to the permission logic.

Root Cause Analysis
It appears the internal logic for hydrating Role policies maps the id field of the junction table object to the policy ID, instead of mapping the policy field.

Workaround
Force the directus_access.id to be identical to the directus_access.policy UUID.

INSERT INTO directus_access (id, role, policy, sort)
VALUES (
'', -- ID MUST MATCH POLICY ID
'',
'',
1
);
When id === policy, the bug is masked because reading the wrong column still yields the correct UUID.

Environment
Directus Version: 11.x

Deployment: Docker / Fly.io

Directus Version

11.12.0

Hosting Strategy

Self-Hosted (Docker Image)

Database

PostgreSQL (Neon)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions