high priority medium complexity documentation pending documentor Tier 6

Acceptance Criteria

Document contains a complete table inventory: every Supabase table listed with its RLS status (enabled/disabled), applicable policies by name, and which roles each policy covers
Claims section documents: which JWT claims are injected per role (org_admin, coordinator, peer_mentor, super_admin), the claim names, value types, and the injection mechanism (AdminRlsGuard set_claim RPC)
Recursive subtree resolution strategy is explained with a diagram or pseudocode showing how org hierarchy traversal works for multi-level organisations (relevant for NHF's 12 landsforeninger / 9 regioner / 1400 lokallag structure)
GDPR section explicitly states: which tables contain personal data, how RLS enforces data isolation per organisation, and that no cross-org data leakage is architecturally possible via the API layer
Bufdir audit trail section documents: which activity records are immutable, how deletion is prevented or logged, and how export functionality produces auditable output
Document is reviewed and signed off by at least one developer who implemented the RLS policies
Document is stored in the repository under `docs/security/admin-portal-security-contract.md`
QA team confirms the document is sufficient to derive their security test matrix without additional clarification

Technical Requirements

frameworks
Markdown (GitHub-flavoured)
apis
Supabase RLS policy introspection (pg_policies view)
Supabase Auth JWT claims
data models
All tables with RLS: users, peer_mentors, activities, certifications, organisations, user_roles, user_stories, phases
performance requirements
Document must be renderable in GitHub/GitLab without plugins
security requirements
Document must not contain real credentials, real UUIDs, or production environment details
GDPR section must reference the applicable Norwegian GDPR regulation (personopplysningsloven)

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Structure the document in these sections: (1) Overview and scope; (2) Role taxonomy and JWT claims map (table: role → claim name → claim value → injection point); (3) Table RLS inventory (table: table name → RLS enabled → policy names → roles covered → personal data: yes/no); (4) Recursive organisation subtree resolution (explain the WITH RECURSIVE CTE or equivalent used to resolve org membership for multi-level hierarchies); (5) HLF-specific extensions (certification-gated visibility); (6) GDPR data isolation guarantees; (7) Bufdir audit trail requirements; (8) Known limitations and future work. Extract the table inventory programmatically: `SELECT tablename, policyname, roles FROM pg_policies WHERE schemaname = 'public'` — paste the output and annotate it. This document is the foundation for Bufdir grant reporting and must survive project handovers.

Testing Requirements

This is a documentation task; testing is a review process. Acceptance testing: (1) a QA engineer not involved in implementation reads the document and can independently construct a security test matrix covering all RLS policies; (2) a compliance reviewer confirms the GDPR isolation guarantees section is legally accurate for Norwegian data protection requirements; (3) a developer cross-checks the table inventory against `supabase/migrations/` and confirms no table with personal data is omitted; (4) Bufdir audit trail section is reviewed against the grant agreement requirements for Bufdir-funded organisations (NHF, Blindeforbundet, HLF).

Component
Admin Row-Level Security Guard
service high
Epic Risks (3)
high impact medium prob security

Missing RLS policies on one or more tables (e.g., a newly added join table or a Supabase view) could expose cross-org data to org_admin queries, creating a GDPR-reportable data breach.

Mitigation & Contingency

Mitigation: Enumerate all tables and views accessed by admin queries before writing any policy. Create an automated test that attempts a cross-org query for each table from an org_admin JWT and asserts an empty result set.

Contingency: If a gap is discovered post-deployment, immediately disable the affected query surface and deploy a hotfix policy before re-enabling. Log the incident and notify DPO if any cross-org data was returned.

high impact medium prob technical

The recursive CTE for NHF's deeply nested org tree (up to 5 levels, 1,400 local chapters) may exceed the 2-second dashboard load target when resolving large subtrees on every request.

Mitigation & Contingency

Mitigation: Benchmark the recursive CTE against a synthetic NHF-scale dataset during development. Introduce a short-TTL server-side cache for subtree resolution results. Index the parent_id column on the organisations table.

Contingency: If CTE performance remains insufficient, materialise the org subtree as a precomputed closure table updated on org structure changes, and switch the RLS guard to query the closure table instead.

high impact low prob security

Incorrect JWT claim injection in AdminRlsGuard (e.g., wrong claim key name or missing refresh on org switch) could silently apply the wrong org scope, causing org_admin to see a different organisation's data without an explicit error.

Mitigation & Contingency

Mitigation: Write unit tests for the guard that verify the injected claim value against the authenticated user's org_id for every admin route. Add a server-side assertion that the claim matches the user's database record before executing any query.

Contingency: Roll back the guard to a deny-all fallback, invalidate active admin sessions, and re-issue corrected JWTs. Audit query logs to identify any sessions that received incorrect scope.