-- ==================================================================== -- 0073_anon_rechte_minimal — anon nur noch dort, wo die Karte liest -- ==================================================================== -- Sicherheitsplan M22 (24.10.2026). Supabase vergibt über seine -- Default-Privileges jeder neuen Tabelle volle Rechte an anon (inkl. -- TRUNCATE, das RLS nicht erfasst). RLS hat das bisher abgefangen; jetzt -- werden die Rechte selbst abgebaut. Öffentlich gelesen werden nur -- employees, persons und site_settings — dort gelten weiter die -- Spalten-Grants aus 0014/0045 ff. (ohne Geheimnis-Spalten). -- -- Bewusst KEIN "revoke ... on all tables in schema public" und KEIN -- "alter default privileges": Auf gemeinsam genutzten Datenbanken (etwa -- supabase.stwhas.de) liegen fremde Tabellen derselben Eigentümerrolle im -- Schema public; deren Rechte dürfen sich nicht ändern. Deshalb eine -- ausdrückliche Liste der TeamVis-Tabellen. Neue Tabellen nehmen anon die -- Rechte in ihrer eigenen Migration (siehe 0060, 0072 und CLAUDE.md). -- -- Ebenfalls entfernt: die Policies admin_write und public_read auf employees (ALL für -- authenticated, true). Sie stammt aus keiner TeamVis-Migration — TeamVis -- nutzt keine Supabase-Auth —, lag aber auf einer Bestandsinstanz und hätte -- jedem Inhaber eines authenticated-JWT Schreibzugriff gegeben. do $$ declare t text; begin foreach t in array array[ 'admin_invites', 'admin_users', 'appointment_requests', 'bookings', 'calendar_connections', 'card_leads', 'card_view_log', 'change_log', 'compliance_role_bindings', 'deputies', 'external_parties', 'job_description_blocks', 'job_description_versions', 'job_descriptions', 'locations', 'magic_link_tokens', 'org_units', 'organigram_versions', 'portal_api_tokens', 'position_assignments', 'positions', 'push_preferences', 'push_subscriptions', 'reception_kiosks', 'reception_visit_audit', 'service_relations', 'teamvis_schema_migrations', 'trusted_access_log', 'visitor_preregistrations', 'wallet_pass_registrations', 'webhook_deliveries', 'webhook_subscriptions' ] loop if to_regclass('public.' || t) is not null then execute format('revoke all on table public.%I from anon', t); end if; end loop; end $$; drop policy if exists admin_write on public.employees; -- authenticated: dieselben engen Rechte wie anon. TeamVis nutzt keine -- Supabase-Auth; auf gemeinsam genutzten Datenbanken kann es aber Inhaber -- eines authenticated-JWT geben. Übrige Tabellen: keine Rechte; die drei -- Kartentabellen: genau die Lesespalten, die anon hat. do $$ declare t text; cols text; begin foreach t in array array[ 'admin_invites', 'admin_users', 'appointment_requests', 'bookings', 'calendar_connections', 'card_leads', 'card_view_log', 'change_log', 'compliance_role_bindings', 'deputies', 'external_parties', 'job_description_blocks', 'job_description_versions', 'job_descriptions', 'locations', 'magic_link_tokens', 'org_units', 'organigram_versions', 'portal_api_tokens', 'position_assignments', 'positions', 'push_preferences', 'push_subscriptions', 'reception_kiosks', 'reception_visit_audit', 'service_relations', 'teamvis_schema_migrations', 'trusted_access_log', 'visitor_preregistrations', 'wallet_pass_registrations', 'webhook_deliveries', 'webhook_subscriptions' ] loop if to_regclass('public.' || t) is not null then execute format('revoke all on table public.%I from authenticated', t); end if; end loop; foreach t in array array['employees', 'persons', 'site_settings'] loop if to_regclass('public.' || t) is null then continue; end if; select string_agg(quote_ident(column_name), ', ' order by column_name) into cols from information_schema.column_privileges where table_schema = 'public' and table_name = t and grantee = 'anon' and privilege_type = 'SELECT'; execute format('revoke all on table public.%I from authenticated', t); if cols is not null then execute format('grant select (%s) on public.%I to authenticated', cols, t); end if; end loop; end $$; -- Ebenfalls nicht aus TeamVis: Policy public_read (Rolle public, aktive -- Karten). Für anon gilt public_read_active_employees (0001/0014). drop policy if exists public_read on public.employees; do $$ begin if to_regclass('public.teamvis_schema_migrations') is not null then alter table public.teamvis_schema_migrations enable row level security; revoke all on table public.teamvis_schema_migrations from anon; end if; end $$;