Skip to content

Commit 0a09e55

Browse files
committed
test(perms): pin canAction behaviour against Directus's access: 'full' quirk
Critical-review finding that turned into a regression test instead of a behaviour change. Real `/permissions/me` payloads from Directus 11 set `access: 'full'` even when `fields` is an explicit allow-list — e.g. a restricted user's read entry on `issue_37_test`: { "access": "full", "fields": ["id", "title", "translations"] } (no `thumbnail`). The natural-language reading of `'full'` suggests unrestricted access, but in practice `fields` remains the authoritative gate; Directus uses `access` as a coarser hint that does not override the whitelist. Short-circuiting on `access === 'full'` would let denied fields through and silently re-introduce the original Bug A-D class. - Add a comment on `canAction` documenting why we deliberately don't trust `access` for the field check. - Add a regression test mirroring the real payload shape so a future contributor's "obvious cleanup" cannot remove this guard unnoticed. 181/181 tests, type-check, lint, prettier all green.
1 parent 8c89269 commit 0a09e55

2 files changed

Lines changed: 16 additions & 0 deletions

File tree

src/composables/usePermissions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ export function usePermissions() {
4040

4141
if (!field) return true;
4242

43+
// `entry.fields` is the authoritative field-level whitelist. `access` is
44+
// a coarser hint that Directus also reports as `'full'` for permissions
45+
// whose `fields` is an explicit allow-list — so do NOT short-circuit on
46+
// `access === 'full'` here, that would let denied fields through.
4347
const fields = entry.fields ?? [];
4448
if (fields.includes('*')) return true;
4549
return fields.includes(field);

tests/unit/composables/usePermissions.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ describe('usePermissions.canRead', () => {
3535
expect(canRead('issue_37_test', 'anything')).toBe(true);
3636
});
3737

38+
// Directus reports `access: 'full'` even when the field-level whitelist is
39+
// a restricted subset — `fields` stays the authoritative gate.
40+
it('honours the field whitelist even when access is reported as full', () => {
41+
mockPermissions.value.issue_37_test.read = {
42+
access: 'full',
43+
fields: ['id', 'title'],
44+
};
45+
const { canRead } = usePermissions();
46+
expect(canRead('issue_37_test', 'title')).toBe(true);
47+
expect(canRead('issue_37_test', 'thumbnail')).toBe(false);
48+
});
49+
3850
it('returns false when collection has access "none"', () => {
3951
mockPermissions.value.issue_37_test.read.access = 'none';
4052
const { canRead } = usePermissions();

0 commit comments

Comments
 (0)