teecup/test_isolation.sql

214 lines
9.7 KiB
PL/PgSQL

-- =====================================================================
-- TeeCup — isolasjonstest (oppførsel, ikke bare skjema)
-- =====================================================================
-- Beviser at RLS faktisk hindrer én organisasjon i å se en annens data,
-- sett fra runtime-rollen teecup_app (som IKKE er superuser/BYPASSRLS).
--
-- Kjør som admin/superbruker mot en scratch- eller test-database der
-- 001_initial_schema.sql, 002_roles_and_grants.sql og
-- 003_scoring_and_blinddraw.sql allerede er kjørt (003 kreves for
-- match_hole_result/lineup_lock, testet her i tillegg til grunnskjemaet):
-- psql -d teecup_scratch -f test_isolation.sql
--
-- Alt kjøres i én transaksjon som RULLES TILBAKE til slutt — ingen testdata
-- blir liggende igjen. Skriptet stopper med feil hvis isolasjonen svikter.
-- =====================================================================
\set ON_ERROR_STOP on
BEGIN;
-- --- Seed to organisasjoner + én turnering hver (som privilegert rolle) ---
-- (Superbruker omgår RLS, så innsettingen går uhindret her — det er meningen.)
INSERT INTO organization (id, name) VALUES
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Org A'),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Org B');
INSERT INTO tournament (organization_id, name) VALUES
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'A-Cup'),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'B-Cup');
-- --- Seed nok struktur til å teste match_hole_result og lineup_lock ---
-- (ADR-012/013, migrasjon 003). Fortsatt privilegert rolle, RLS omgås her.
INSERT INTO course (id, organization_id, name) VALUES
('c1111111-1111-1111-1111-111111111111', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'A-banen'),
('c2222222-2222-2222-2222-222222222222', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'B-banen');
INSERT INTO team (id, organization_id, tournament_id, name)
SELECT 'de111111-1111-1111-1111-111111111111', t.organization_id, t.id, 'A rødt'
FROM tournament t WHERE t.organization_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
INSERT INTO team (id, organization_id, tournament_id, name)
SELECT 'de222222-2222-2222-2222-222222222222', t.organization_id, t.id, 'A blått'
FROM tournament t WHERE t.organization_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
INSERT INTO team (id, organization_id, tournament_id, name)
SELECT 'df111111-1111-1111-1111-111111111111', t.organization_id, t.id, 'B rødt'
FROM tournament t WHERE t.organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
INSERT INTO team (id, organization_id, tournament_id, name)
SELECT 'df222222-2222-2222-2222-222222222222', t.organization_id, t.id, 'B blått'
FROM tournament t WHERE t.organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
INSERT INTO session (id, organization_id, tournament_id, sequence, format, course_id)
SELECT 'e5111111-1111-1111-1111-111111111111', t.organization_id, t.id, 1, 'singles',
'c1111111-1111-1111-1111-111111111111'
FROM tournament t WHERE t.organization_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
INSERT INTO session (id, organization_id, tournament_id, sequence, format, course_id)
SELECT 'e5222222-2222-2222-2222-222222222222', t.organization_id, t.id, 1, 'singles',
'c2222222-2222-2222-2222-222222222222'
FROM tournament t WHERE t.organization_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
INSERT INTO match (id, organization_id, session_id, sequence, team_a_id, team_b_id) VALUES
('fa111111-1111-1111-1111-111111111111', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
'e5111111-1111-1111-1111-111111111111', 1,
'de111111-1111-1111-1111-111111111111', 'de222222-2222-2222-2222-222222222222'),
('fa222222-2222-2222-2222-222222222222', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'e5222222-2222-2222-2222-222222222222', 1,
'df111111-1111-1111-1111-111111111111', 'df222222-2222-2222-2222-222222222222');
INSERT INTO match_hole_result (organization_id, match_id, hole_number, winning_side) VALUES
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'fa111111-1111-1111-1111-111111111111', 1, 'a'),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'fa222222-2222-2222-2222-222222222222', 1, 'b');
INSERT INTO lineup_lock (organization_id, session_id, team_id) VALUES
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'e5111111-1111-1111-1111-111111111111',
'de111111-1111-1111-1111-111111111111'),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'e5222222-2222-2222-2222-222222222222',
'df111111-1111-1111-1111-111111111111');
-- --- Bytt til runtime-rollen: nå SKAL RLS gjelde ---------------------
SET ROLE teecup_app;
SELECT set_config('app.current_org', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', false);
-- Test 1: Ser bare Org A sine turneringer (1, ikke 2).
DO $$
DECLARE n int;
BEGIN
SELECT count(*) INTO n FROM tournament;
IF n <> 1 THEN
RAISE EXCEPTION 'ISOLASJON FEILET (lesing): ser % turneringer, forventet 1', n;
END IF;
RAISE NOTICE 'OK Test 1: ser kun egen organisasjons turneringer (%).', n;
END $$;
-- Test 2: Ser bare sin egen organisasjonsrad.
DO $$
DECLARE n int;
BEGIN
SELECT count(*) INTO n FROM organization;
IF n <> 1 THEN
RAISE EXCEPTION 'ISOLASJON FEILET (organization): ser % rader, forventet 1', n;
END IF;
RAISE NOTICE 'OK Test 2: ser kun egen organisasjonsrad (%).', n;
END $$;
-- Test 3: Kan IKKE sette inn data merket med en annen organisasjon.
-- WITH CHECK skal blokkere det (SQLSTATE 42501).
DO $$
BEGIN
BEGIN
INSERT INTO tournament (organization_id, name)
VALUES ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'kryss-org-forsøk');
-- Kommer vi hit, ble det tillatt -> isolasjonen er brutt.
RAISE EXCEPTION 'ISOLASJON FEILET (skriving): fikk sette inn data for annen org';
EXCEPTION
WHEN insufficient_privilege THEN
RAISE NOTICE 'OK Test 3: WITH CHECK blokkerte kryss-org innsetting.';
END;
END $$;
-- Test 4: Kontrollprøve — bytter kontekst til Org B og ser Bs turnering isteden.
SELECT set_config('app.current_org', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', false);
DO $$
DECLARE nm text;
BEGIN
SELECT name INTO nm FROM tournament;
IF nm <> 'B-Cup' THEN
RAISE EXCEPTION 'ISOLASJON FEILET (kontekstbytte): forventet B-Cup, fikk %', nm;
END IF;
RAISE NOTICE 'OK Test 4: kontekstbytte gir riktig organisasjons data (%).', nm;
END $$;
-- --- Tilbake til Org A-kontekst for de nye tabellene (ADR-012/013) ----
SELECT set_config('app.current_org', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', false);
-- Test 5: match_hole_result — ser bare Org A sin rad (1, ikke 2).
DO $$
DECLARE n int;
BEGIN
SELECT count(*) INTO n FROM match_hole_result;
IF n <> 1 THEN
RAISE EXCEPTION 'ISOLASJON FEILET (match_hole_result lesing): ser % rader, forventet 1', n;
END IF;
RAISE NOTICE 'OK Test 5: match_hole_result — ser kun egen organisasjons rad (%).', n;
END $$;
-- Test 6: match_hole_result — kan IKKE sette inn en rad merket med annen org,
-- selv om match_id-en (fra org B) i seg selv er FK-gyldig.
DO $$
BEGIN
BEGIN
INSERT INTO match_hole_result (organization_id, match_id, hole_number, winning_side)
VALUES ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'fa222222-2222-2222-2222-222222222222', 2, 'b');
RAISE EXCEPTION 'ISOLASJON FEILET (match_hole_result skriving): fikk sette inn data for annen org';
EXCEPTION
WHEN insufficient_privilege THEN
RAISE NOTICE 'OK Test 6: match_hole_result — WITH CHECK blokkerte kryss-org innsetting.';
END;
END $$;
-- Test 7: lineup_lock — ser bare Org A sin rad (1, ikke 2).
DO $$
DECLARE n int;
BEGIN
SELECT count(*) INTO n FROM lineup_lock;
IF n <> 1 THEN
RAISE EXCEPTION 'ISOLASJON FEILET (lineup_lock lesing): ser % rader, forventet 1', n;
END IF;
RAISE NOTICE 'OK Test 7: lineup_lock — ser kun egen organisasjons rad (%).', n;
END $$;
-- Test 8: lineup_lock — kan IKKE sette inn en rad merket med annen org,
-- selv om session_id/team_id (fra org B) i seg selv er FK-gyldige.
DO $$
BEGIN
BEGIN
INSERT INTO lineup_lock (organization_id, session_id, team_id)
VALUES ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'e5222222-2222-2222-2222-222222222222',
'df222222-2222-2222-2222-222222222222');
RAISE EXCEPTION 'ISOLASJON FEILET (lineup_lock skriving): fikk sette inn data for annen org';
EXCEPTION
WHEN insufficient_privilege THEN
RAISE NOTICE 'OK Test 8: lineup_lock — WITH CHECK blokkerte kryss-org innsetting.';
END;
END $$;
-- Test 9: kontekstbytte til Org B gir Org Bs rader for begge nye tabeller.
SELECT set_config('app.current_org', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', false);
DO $$
DECLARE ws team_side;
DECLARE tid uuid;
BEGIN
SELECT winning_side INTO ws FROM match_hole_result;
IF ws <> 'b' THEN
RAISE EXCEPTION 'ISOLASJON FEILET (match_hole_result kontekstbytte): forventet b, fikk %', ws;
END IF;
RAISE NOTICE 'OK Test 9a: match_hole_result — kontekstbytte gir riktig organisasjons data (%).', ws;
SELECT team_id INTO tid FROM lineup_lock;
IF tid <> 'df111111-1111-1111-1111-111111111111' THEN
RAISE EXCEPTION 'ISOLASJON FEILET (lineup_lock kontekstbytte): forventet Org Bs lag, fikk %', tid;
END IF;
RAISE NOTICE 'OK Test 9b: lineup_lock — kontekstbytte gir riktig organisasjons data (%).', tid;
END $$;
RESET ROLE;
RESET app.current_org;
ROLLBACK; -- ingen testdata blir liggende igjen
\echo '============================================'
\echo ' Alle 9 isolasjonstester bestått (se OK-linjer)'
\echo ' Dekker: tournament/organization (001) samt'
\echo ' match_hole_result/lineup_lock (003, ADR-012/013)'
\echo '============================================'