-- 004_rls_policies.sql -- RLS-policies på nodes og edges, basert på node_access-matrisen. -- -- Maskinrommet kobler til som superuser (sidelinja) for skriveoperasjoner. -- For tunge lesespørringer (søk, statistikk, graf-traversering) brukes -- SET LOCAL ROLE synops_reader, som er underlagt RLS. -- -- Ref: docs/retninger/bruker_ikke_workspace.md (RLS-policy-spesifikasjon) BEGIN; -- ============================================================================= -- Applikasjonsrolle for RLS-filtrerte spørringer -- ============================================================================= DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'synops_reader') THEN CREATE ROLE synops_reader NOLOGIN; END IF; END $$; -- Gi lesetilgang til alle relevante tabeller GRANT SELECT ON nodes, edges, node_access, auth_identities TO synops_reader; -- Tillat synops_reader å lese sekvenser (for eventuelle funksjoner) GRANT USAGE ON SCHEMA public TO synops_reader; -- Sidelinja (superuser) kan bytte til synops_reader GRANT synops_reader TO sidelinja; -- ============================================================================= -- current_node_id() — leser brukerens node_id fra sesjonsvariabel -- ============================================================================= CREATE OR REPLACE FUNCTION current_node_id() RETURNS UUID AS $$ BEGIN RETURN current_setting('app.current_node_id', true)::UUID; EXCEPTION WHEN OTHERS THEN RETURN NULL; END; $$ LANGUAGE plpgsql STABLE; -- synops_reader må kunne kalle funksjonen GRANT EXECUTE ON FUNCTION current_node_id() TO synops_reader; -- ============================================================================= -- RLS på nodes -- ============================================================================= ALTER TABLE nodes ENABLE ROW LEVEL SECURITY; -- Policy: bruker kan se noder de har opprettet, har tilgang til, eller som er -- discoverable/readable/open. -- -- Tre sjekker (ref: bruker_ikke_workspace.md): -- 1. Egne noder (created_by) — instant -- 2. Eksplisitt tilgang via node_access — indeksert lookup -- 3. Offentlig synlige noder (discoverable+) — kolonne-sjekk CREATE POLICY node_select ON nodes FOR SELECT TO synops_reader USING ( created_by = current_node_id() OR id IN ( SELECT object_id FROM node_access WHERE subject_id = current_node_id() ) OR visibility >= 'discoverable' ); -- ============================================================================= -- RLS på edges -- ============================================================================= ALTER TABLE edges ENABLE ROW LEVEL SECURITY; -- Policy: bruker kan se edges der de har tilgang til minst én av endepunktene. -- System-edges (alias etc.) er alltid skjult med mindre brukeren er source. CREATE POLICY edge_select ON edges FOR SELECT TO synops_reader USING ( -- Ikke vis system-edges til andre enn eieren (NOT system OR source_id = current_node_id()) AND ( -- Bruker opprettet edgen created_by = current_node_id() -- Bruker er source eller target OR source_id = current_node_id() OR target_id = current_node_id() -- Bruker har tilgang til source- eller target-noden OR source_id IN ( SELECT object_id FROM node_access WHERE subject_id = current_node_id() ) OR target_id IN ( SELECT object_id FROM node_access WHERE subject_id = current_node_id() ) ) ); -- ============================================================================= -- RLS på node_access (brukere kan bare se sine egne tilganger) -- ============================================================================= ALTER TABLE node_access ENABLE ROW LEVEL SECURITY; CREATE POLICY na_select ON node_access FOR SELECT TO synops_reader USING (subject_id = current_node_id()); -- ============================================================================= -- Indeks for RLS-ytelse -- ============================================================================= -- node_access brukes i subquery med subject_id — allerede indeksert (idx_na_subject). -- Legg til indeks på object_id for edge-policyen (reverse lookup). CREATE INDEX IF NOT EXISTS idx_na_object ON node_access (object_id); COMMIT;