Alle PlaybooksFlutter Production App

Eine Supabase-Tabelle mit RLS hinzufügen

Neue Nutzerdaten in Supabase hinter RLS und pgTAP-Tests speichern und in der App über ein Repository lesen.

Wann du es brauchst

Die App speichert neue Nutzerdaten in Supabase, zum Beispiel Notizen, Favoriten oder Tagebucheinträge, und zeigt sie auf einem Screen an.

Regeln

  • Die App enthält den Publishable Key, daher trennt nur RLS die Zeilen eines Nutzers von denen anderer. Jede neue Tabelle aktiviert RLS in der Migration, die sie anlegt.
  • Migrationen sind append-only: Ändere nie eine bereits angewendete.
  • Jede neue Policy hat einen pgTAP-Test, der erlaubten Zugriff belegt und den Rest ablehnt.
  • Supabase wird nur aus einem Repository in data aufgerufen, hinter einer Schnittstelle in domain.

Schritte

  1. Lege supabase/migrations/<timestamp>_<name>.sql mit einem Zeitstempel nach dem letzten an, nach dem Vorbild von push_devices:

    create table public.notes (
      id uuid primary key default gen_random_uuid(),
      user_id uuid not null default auth.uid() references auth.users(id) on delete cascade,
      body text not null check (char_length(body) between 1 and 2000),
      created_at timestamptz not null default now()
    );
    
    create index notes_user_id_idx on public.notes (user_id);
    
    alter table public.notes enable row level security;
    
    create policy "notes_select_own" on public.notes for select
      to authenticated using ((select auth.uid()) = user_id);
    create policy "notes_insert_own" on public.notes for insert
      to authenticated with check ((select auth.uid()) = user_id);
    create policy "notes_delete_own" on public.notes for delete
      to authenticated using ((select auth.uid()) = user_id);
    
    revoke all on public.notes from anon;
    

    Füge nur die Operationen hinzu, die die App braucht: Hier können Nutzer eine Notiz nicht bearbeiten.

  2. Braucht ein Schreibvorgang Regeln, die eine Policy nicht ausdrücken kann, etwa einen Eigentümerwechsel oder die Prüfung anderer Tabellen, entziehe direkte Schreibrechte und biete eine security definer-Funktion mit set search_path = '' und einer Prüfung auf auth.uid() an, wie register_push_device.

  3. Lege supabase/tests/database/<name>_rls.test.sql nach dem Vorbild von push_devices_rls.test.sql an: zwei Nutzer in auth.users, set local role authenticated, request.jwt.claim.sub abwechselnd auf jeden Nutzer gesetzt. Prüfe, dass ein Nutzer nur seine eigenen Zeilen sieht und ändert, dass das Schreiben einer Zeile für jemand anderen mit 42501 fehlschlägt und dass anon nichts erhält.

  4. Führe lokal supabase db reset und supabase test db aus.

  5. Definiere in lib/features/<feature>/domain das Modell mit fromJson und eine abstract interface class für das Repository, wie ProfileRepository. Implementiere sie in data mit SupabaseClient, wie SupabaseProfileRepository: Ohne Sitzung wirf einen Fehler, statt die Abfrage auszuführen.

  6. Stelle das Repository in application über einen Provider bereit, der supabaseClientProvider liest, wie profileRepositoryProvider, und füge die Provider hinzu, die der Screen beobachtet. Für den Screen folge dem Playbook „Einen Screen mit Route und State hinzufügen“.

  7. Füge Dart-Tests für das Mapping des Modells und für die Provider mit einem Fake-Repository hinzu; die Tests nutzen kein Netzwerk.

  8. Trage Tabelle, Eigentümer und Regel in die Tabelle in DATABASE.md ein.

Fertig, wenn

  • die Prüfbefehle erfolgreich sind;
  • supabase test db einschließlich des neuen Tests erfolgreich ist;
  • die Tabelle RLS aktiviert hat und nur die Policies, die die App braucht;
  • DATABASE.md die Tabelle aufführt.