From d4ce5b6478f8496f8d016e185e3d399621968424 Mon Sep 17 00:00:00 2001 From: fahed Date: Wed, 8 Apr 2026 17:38:26 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20update=20access=20control=20spec=20?= =?UTF-8?q?=E2=80=94=20fail-closed=20on=20corrupted=20permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- ...026-04-08-museum-channel-access-control-design.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/specs/2026-04-08-museum-channel-access-control-design.md b/docs/superpowers/specs/2026-04-08-museum-channel-access-control-design.md index b6dd38b..df0b1fa 100644 --- a/docs/superpowers/specs/2026-04-08-museum-channel-access-control-design.md +++ b/docs/superpowers/specs/2026-04-08-museum-channel-access-control-design.md @@ -33,12 +33,12 @@ interface ParsedUser { id: number; name: string; role: 'admin' | 'viewer'; - allowedMuseums: string[]; // [] = unrestricted - allowedChannels: string[]; // [] = unrestricted + allowedMuseums: string[] | null; // [] = unrestricted, null = parse error (show nothing) + allowedChannels: string[] | null; // [] = unrestricted, null = parse error (show nothing) } ``` -**Convention:** empty array = full access. Existing users require no migration (missing fields parsed as `[]`). +**Convention:** `[]` = full access (admins), `string[]` = restricted to list, `null` = corrupted value (fail-closed: no data shown). Existing users require no migration (missing fields parsed as `[]`). ### NocoDB Users table @@ -54,7 +54,7 @@ Both default to `"[]"` (unrestricted). ### 1. `src/services/usersService.ts` -- Update `fetchUsers()` to parse `AllowedMuseums` and `AllowedChannels` JSON strings into `string[]` (with safe fallback to `[]` on parse error) +- Update `fetchUsers()` to parse `AllowedMuseums` and `AllowedChannels` JSON strings into `string[]`; on parse error return `null` (fail-closed — no data shown to the user) - Update `createUser()` to serialize `allowedMuseums`/`allowedChannels` arrays as JSON strings - **Add `updateUser(id, fields)`** — new function required (see Prerequisites below) @@ -127,6 +127,8 @@ const visibleChannels = allowedChannels.length > 0 **Layer 2 — Data filtered at base:** ```typescript const permissionFilteredData = useMemo(() => { + // null = corrupted stored value → show nothing (fail-closed) + if (allowedMuseums === null || allowedChannels === null) return []; let d = data; if (allowedMuseums.length > 0) d = d.filter(r => allowedMuseums.includes(r.museum_name)); @@ -167,7 +169,7 @@ Page reload | Admin user | `allowedMuseums: []`, `allowedChannels: []` — full access | | URL param references disallowed museum | Base filter removes it silently | | New museum added to data, not in user's list | Not visible to restricted user | -| JSON parse error on stored value | Falls back to `[]` (unrestricted, fail-open) | +| JSON parse error on stored value | `null` returned → no data shown (fail-closed) | | Page reload | Session restores access lists from server | ---