-- Phase A: paliad.can_see_akte(akte_id) — single source of truth for -- office-scoped Akten visibility (design §2). -- -- OBSOLETED by migration 018 (data model v2): can_see_akte() is dropped and -- replaced by paliad.can_see_project(project_id) with team-based visibility -- (direct + inherited up the project tree). The effective shape lives in 018; -- this file is kept only so a fresh database can replay the migration history. -- -- A user can see an Akte iff ANY of: -- - the Akte is flagged firm_wide_visible -- - the Akte's owning_office matches the user's office -- - the user's uuid appears in the Akte's collaborators array -- - the user has role = 'admin' -- -- Used by every RLS policy (migration 007) and mirrored at the application -- layer (Phase B AkteService.ListVisibleForUser) for defense in depth. -- -- SECURITY DEFINER so the function can read paliad.users regardless of the -- caller's own RLS grants. The function itself enforces the identity check -- by keying off auth.uid(). CREATE OR REPLACE FUNCTION paliad.can_see_akte(_akte_id uuid) RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path = paliad, public AS $$ SELECT EXISTS ( SELECT 1 FROM paliad.akten a LEFT JOIN paliad.users u ON u.id = auth.uid() WHERE a.id = _akte_id AND ( a.firm_wide_visible OR (u.office IS NOT NULL AND a.owning_office = u.office) OR auth.uid() = ANY (a.collaborators) OR (u.role = 'admin') ) ); $$; COMMENT ON FUNCTION paliad.can_see_akte(uuid) IS 'Office-scoped visibility predicate for paliad.akten. Called from RLS policies.';