Wzorce RLS w PostgreSQL dla aplikacji multi-tenant
Poznaj RLS w PostgreSQL przez praktyczne wzorce izolacji tenantów i reguły ról — tak, by dostęp był egzekwowany w bazie danych, a nie tylko w aplikacji.

Dlaczego egzekwowanie dostępu w bazie danych ma znaczenie w aplikacjach biznesowych
Aplikacje biznesowe zwykle mają reguły typu „użytkownicy mogą widzieć tylko rekordy swojej firmy” oraz „tylko menedżerowie mogą zatwierdzać zwroty”. Wiele zespołów egzekwuje te reguły w UI lub API i zakłada, że to wystarczy. Problem polega na tym, że każda dodatkowa ścieżka do bazy danych to nowe ryzyko wycieku danych: narzędzie administracyjne, job w tle, zapytanie analityczne, zapomniany endpoint albo błąd pomijający jedną z kontroli.
Izolacja tenantów oznacza, że jeden klient (tenant) nigdy nie może odczytać ani zmienić danych innego klienta, nawet przypadkowo. Dostęp oparty na rolach oznacza, że osoby w tym samym tenancie mają różne uprawnienia, np. agent vs menedżer vs dział finansów. Łatwo opisać te reguły, ale trudno utrzymać ich pełną spójność, gdy są zakodowane w wielu miejscach.
Row-Level Security (RLS) w PostgreSQL to funkcja bazy danych, która pozwala bazie decydować, które wiersze zapytanie może zobaczyć lub zmienić. Zamiast polegać na tym, że każde zapytanie w aplikacji pamięta właściwy WHERE, baza danych stosuje polityki automatycznie.
RLS nie jest magiczną tarczą na wszystko. Nie zaprojektuje schematu, nie zastąpi uwierzytelniania ani nie ochroni przed kimś, kto już ma potężną rolę w bazie (np. superuser). Nie zapobiegnie też błędom logicznym typu „ktoś może zaktualizować wiersz, którego nie może wybrać”, jeśli nie napiszesz polityk dla odczytu i zapisu.
Co zyskujesz, to solidna siatka bezpieczeństwa:
- Jeden zestaw reguł dla wszystkich ścieżek, które trafiają do bazy danych
- Mniej „upsów” po wdrożeniu nowej funkcji
- Czytelniejsze audyty, bo reguły są widoczne w SQL
- Lepszą ochronę, gdy błąd w API się zdarzy
Jest drobny koszt konfiguracji. Potrzebujesz spójnego sposobu przekazywania „kto to robi” i „który tenant” do bazy oraz utrzymywania polityk w miarę rozwoju aplikacji. Zysk jest jednak duży, zwłaszcza w SaaS i narzędziach wewnętrznych, gdzie dane klientów są wrażliwe.
Podstawy Row-Level Security bez żargonu
Row-Level Security (RLS) automatycznie filtruje, które wiersze zapytanie może zobaczyć lub zmienić. Zamiast polegać na każdym ekranie, endpointzie czy raporcie, baza danych stosuje reguły za Ciebie.
W PostgreSQL piszesz polityki sprawdzane przy każdym SELECT, INSERT, UPDATE i DELETE. Jeśli polityka mówi „ten użytkownik może widzieć tylko wiersze dla tenanta A”, to nawet zapomniana strona administracyjna, nowe zapytanie czy szybka poprawka nadal dostaną te same zabezpieczenia.
RLS różni się od GRANT/REVOKE. GRANT decyduje, czy rola może mieć dostęp do tabeli w ogóle (lub do wybranych kolumn). RLS decyduje, które wiersze w tej tabeli są dozwolone. W praktyce często używasz obu: GRANT, by ograniczyć, kto wchodzi w kontakt z tabelą, i RLS, by ograniczyć, co mogą zobaczyć.
RLS dobrze sprawdza się też w realnym, złożonym środowisku. Widoki zwykle respektują RLS, bo dostęp do tabeli nadal uruchamia politykę. Joiny i podzapytania nadal są filtrowane, więc użytkownik nie może „dołączyć się” do cudzych danych. Polityka obowiązuje niezależnie od klienta uruchamiającego zapytanie: kod aplikacji, konsola SQL, job w tle czy narzędzie raportujące.
RLS jest dobrym wyborem, gdy potrzebujesz silnej izolacji tenantów, gdy istnieje wiele sposobów zapytań tych samych danych lub gdy wiele ról współdzieli tabele (typowe dla SaaS i narzędzi wewnętrznych). Może być zbyt rozbudowany dla malutkich aplikacji z jednym zaufanym backendem albo dla danych, które nie są wrażliwe i nigdy nie wychodzą poza jedną kontrolowaną usługę. Gdy masz więcej niż jeden punkt wejścia (narzędzia admina, eksporty, BI, skrypty), RLS zwykle się opłaca.
Zacznij od mapowania tenantów, ról i własności danych
Zanim napiszesz pierwszą politykę, ustal, kto co posiada. RLS działa najlepiej, gdy model danych już odzwierciedla tenantów, role i własność.
Zacznij od tenantów. W większości aplikacji SaaS najprostsza reguła to: każda współdzielona tabela zawierająca dane klienta ma tenant_id. To obejmuje oczywiste tabele jak faktury, ale także często zapominane: załączniki, komentarze, logi audytu czy joby w tle.
Następnie nazwij role, których ludzie rzeczywiście używają. Utrzymaj mały, zrozumiały zbiór: owner, manager, agent, read-only. To role biznesowe, które później odwzorujesz na sprawdzenia w politykach (to nie to samo co role bazy danych).
Potem zdecyduj, jak są własnością są rekordy. Niektóre tabele należą do jednego użytkownika (np. prywatna notatka), inne do zespołu (np. wspólna skrzynka). Mieszanie tych dwóch modeli bez planu prowadzi do trudnych do zrozumienia polityk, które łatwo obejść.
Prosty sposób dokumentowania reguł to odpowiedź na te same pytania dla każdej tabeli:
- Jaka jest granica tenanta (która kolumna to wymusza)?
- Kto może czytać wiersze (według roli i własności)?
- Kto może tworzyć i aktualizować wiersze (i na jakich warunkach)?
- Kto może usuwać wiersze (zwykle najsurowsza reguła)?
- Jakie wyjątki są dozwolone (wsparcie, automatyzacja, eksporty)?
Przykład: „Invoices” może pozwalać menedżerom oglądać wszystkie faktury tenanta, agentom oglądać faktury przypisanych klientów, a użytkownikom tylko do odczytu — tylko przeglądać bez edycji. Zdecyduj z wyprzedzeniem, które reguły muszą być ścisłe (izolacja tenantów, usuwanie) i które mogą być bardziej elastyczne (dodatkowa widoczność dla menedżerów). Jeśli budujesz w narzędziu no-code jak AppMaster, to mapowanie pomaga utrzymać zgodność między UI a regułami bazy.
Wzorce projektowe dla tabel multi-tenant
RLS dla multi-tenant działa najlepiej, gdy tabele mają przewidywalny kształt. Jeśli każda tabela przechowuje tenant w inny sposób, polityki stają się łamigłówką. Spójna struktura ułatwia czytanie, testowanie i utrzymanie polityk.
Zacznij od wyboru jednego identyfikatora tenanta i używaj go wszędzie. UUID są popularne, bo trudno je zgadnąć i łatwo wygenerować w wielu systemach. Liczby całkowite też są OK, szczególnie w aplikacjach wewnętrznych. Slugi (np. "acme") są przyjazne dla ludzi, ale mogą się zmieniać, więc traktuj je jako pole wyświetlające, nie jako klucz podstawowy.
Dla danych scoped do tenanta dodaj kolumnę tenant_id do każdej tabeli należącej do tenanta i ustaw NOT NULL kiedy to możliwe. Jeśli wiersz może istnieć bez tenanta, to zwykle jest zapach problemu — często mieszasz dane globalne i tenantowe w jednej tabeli, co utrudnia i osłabia polityki RLS.
Indeksowanie jest proste, ale ważne. Większość zapytań w aplikacji SaaS filtruje najpierw po tenantcie, potem po polu biznesowym jak status czy data. Dobrym domyślnym indeksem jest indeks na tenant_id, a dla tabel o dużym ruchu indeks złożony jak (tenant_id, created_at) lub (tenant_id, status) w zależności od najczęstszych filtrów.
Zdecyduj wcześnie, które tabele są globalne, a które scoped do tenantów. Typowe tabele globalne to kraje, kody walutowe czy definicje planów. Tabele scoped to tenantów to klienci, faktury, tickety i wszystko, co tenant posiada.
Jeśli chcesz zestawu reguł łatwych do utrzymania, trzymaj je wąsko:
- Tabele scoped do tenantów:
tenant_id NOT NULL, RLS włączone, polityki zawsze sprawdzajątenant_id. - Tabele referencyjne globalne: brak
tenant_id, brak polityk tenantowych, tylko do odczytu dla większości ról. - Tabele współdzielone, ale kontrolowane: osobne tabele dla różnych konceptów (unikaj mieszania wierszy globalnych i tenantowych).
Jeśli budujesz w narzędziu takim jak AppMaster, ta spójność zwróci się w modelu danych. Gdy tenant_id stanie się standardowym polem, możesz powielać te same wzorce w modułach bez niespodzianek.
Krok po kroku: utwórz swoją pierwszą politykę tenantową
Dobrym pierwszym krokiem z RLS jest pojedyncza tabela, którą będą czytać tylko wiersze z bieżącego tenanta. Chodzi o prostotę: nawet jeśli ktoś zapomni WHERE w API, baza odmówi zwrócenia wierszy z innych tenantów.
Zacznij od tabeli z kolumną tenant_id:
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
Po włączeniu RLS domyślne zachowanie często zaskakuje: jeśli rola podlega RLS i nie istnieje pasująca polityka, SELECT nie zwróci żadnych wierszy (a zapisy się nie powiodą). To jest pożądane na początek.
Dodaj minimalną politykę do odczytu. Ten przykład zakłada, że aplikacja ustawia zmienną sesji jak app.tenant_id po zalogowaniu:
CREATE POLICY invoices_tenant_read
ON invoices
FOR SELECT
USING (tenant_id = current_setting('app.tenant_id')::uuid);
Następnie dodaj reguły zapisu. W RLS USING kontroluje, które istniejące wiersze możesz dotknąć, a WITH CHECK kontroluje, jakie nowe wartości możesz zapisać.
CREATE POLICY invoices_tenant_insert
ON invoices
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY invoices_tenant_update
ON invoices
FOR UPDATE
USING (tenant_id = current_setting('app.tenant_id')::uuid)
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY invoices_tenant_delete
ON invoices
FOR DELETE
USING (tenant_id = current_setting('app.tenant_id')::uuid);
Polityki są domyślnie PERMISSIVE, co oznacza, że dowolna polityka może pozwolić na dostęp. Wybierz RESTRICTIVE, gdy chcesz, aby wszystkie reguły musiały być spełnione (przydatne przy drugim strażniku typu „tylko aktywne konto”).
Trzymaj polityki małe i skoncentrowane na roli. Zamiast jednej gigantycznej reguły z wieloma OR, twórz osobne polityki dla każdej grupy odbiorców (np. invoices_tenant_read_app_user i invoices_tenant_read_support_agent). Łatwiej je testować, przeglądać i bezpieczniej modyfikować później.
Bezpieczne przekazywanie kontekstu tenant i użytkownika
Aby RLS działał, baza musi wiedzieć „kto wywołuje” i „do którego tenanta należy”. Polityki RLS mogą porównywać wiersze tylko do wartości dostępnych w czasie zapytania, więc musisz przekazać ten kontekst do sesji.
Częsty wzorzec to ustawianie zmiennych sesyjnych po uwierzytelnieniu, a potem odczytywanie ich w politykach przez current_setting(). Aplikacja potwierdza tożsamość (np. walidując JWT), a następnie zapisuje identyfikatory tenant i użytkownika oraz rolę w połączeniu z bazą.
-- Uruchamiaj raz na żądanie (lub na transakcję)
SELECT set_config('app.tenant_id', '3f2a0c3e-9c7b-4d3f-9c5c-3c5e9c5d1a11', true);
SELECT set_config('app.user_id', '8d9c6b1a-6b6d-4e32-9c0d-2bfe6f6c1111', true);
SELECT set_config('app.role', 'support_agent', true);
-- W polityce
-- kolumna tenant_id jest UUID
USING (tenant_id = current_setting('app.tenant_id', true)::uuid);
Użycie trzeciego argumentu true czyni ustawienie „lokalnym” dla bieżącej transakcji. To ma znaczenie przy pooling'u połączeń: połączenie z puli może być ponownie użyte przez inne żądanie, więc nie chcesz, by kontekst poprzedniego żądania pozostał.
Wypełnianie kontekstu z roszczeń JWT
Jeśli API używa JWT, traktuj claims jako wejście, nie jako prawdę. Zweryfikuj podpis i datę wygaśnięcia tokena, a potem skopiuj tylko potrzebne pola (tenant_id, user_id, role) do ustawień sesji. Unikaj pozwalania klientom na bezpośrednie przesyłanie tych wartości w nagłówkach czy parametrach zapytania.
Brakujący lub nieprawidłowy kontekst: domyślnie odmów
Projektuj polityki tak, aby brakujące ustawienia skutkowały brakiem wierszy.
Używaj current_setting('app.tenant_id', true), żeby brakujące wartości zwracały NULL. Rzutuj do właściwego typu (np. ::uuid), aby nieprawidłowe formaty zawiodły szybko. I przerywaj żądanie, jeśli kontekst tenant/user nie może zostać ustawiony, zamiast zgadywać domyślną wartość.
To utrzymuje kontrolę dostępu spójną, nawet gdy zapytanie omija UI lub dodany zostanie nowy endpoint.
Praktyczne wzorce ról, które da się utrzymać
Najprostszy sposób, żeby polityki RLS były czytelne, to oddzielić tożsamość od uprawnień. Solidna baza to tabela users plus memberships, która łączy użytkownika z tenantem i rolą (lub kilkoma rolami). Wtedy polityki mogą prostym pytaniem odpowiadać: „Czy bieżący użytkownik ma odpowiednie członkostwo dla tego wiersza?”
Trzymaj nazwy ról powiązane z rzeczywistymi czynnościami, a nie z tytułami stanowisk. invoice_viewer i invoice_approver zwykle starzeją się lepiej niż „manager”, bo polityka może być napisana prostymi słowami.
Oto kilka wzorców ról, które pozostają proste przy rozroście aplikacji:
- Tylko właściciel: wiersz ma
created_by_user_id(lubowner_user_id) i sprawdzenie dopasowuje dokładnie tę wartość. - Tylko zespół: wiersz ma
team_id, a polityka sprawdza, czy użytkownik jest członkiem tego zespołu w ramach tego samego tenanta. - Tylko zatwierdzone: odczyty dozwolone tylko gdy
status = 'approved', a zapisy ograniczone do zatwierdzających. - Reguły mieszane: zacznij rygorystycznie, potem dodawaj małe wyjątki (np. „support może czytać, ale tylko w obrębie tenanta”).
Administratorzy między tenantami to częste źródło kłopotów. Obsługuj ich jawnie, a nie jako ukryty skrót superusera. Stwórz osobną koncepcję jak platform_admin (globalną) i wymuś celowe sprawdzenie w polityce. Lepiej trzymać dostęp między tenantami domyślnie tylko do odczytu i wymagać wyższych uprawnień do zapisu.
Dokumentacja ma większe znaczenie, niż się wydaje. Dodaj krótki komentarz nad każdą polityką wyjaśniający intencję, nie tylko SQL. „Approvers mogą zmieniać status. Viewers tylko czytają zatwierdzone faktury.” Po pół roku taka notka pomaga bezpiecznie modyfikować polityki.
Jeśli budujesz w narzędziu no-code jak AppMaster, te wzorce nadal mają zastosowanie. UI i API mogą się zmieniać szybko, ale reguły bazy pozostaną stabilne, bo opierają się na memberships i jasnym znaczeniu ról.
Scenariusz przykładowy: prosty SaaS z fakturami i wsparciem
Wyobraź sobie mały SaaS obsługujący kilka firm. Każda firma to tenant. Aplikacja ma faktury (pieniądze) i zgłoszenia wsparcia (codzienna pomoc). Użytkownicy mogą być agentami, menedżerami lub supportem.
Model danych (upraszczając): każda faktura i zgłoszenie ma tenant_id. Tickety mają też assignee_user_id. Aplikacja ustawia bieżącego tenanta i użytkownika w sesji bazy zaraz po zalogowaniu.
Oto jak RLS zmienia codzienne ryzyko.
Użytkownik z Tenanta A otwiera ekran faktur i próbuje zgadnąć ID faktury z Tenanta B (albo UI wysyła je przez pomyłkę). Zapytanie się wykona, ale baza zwróci zero wierszy, bo polityka wymaga invoice.tenant_id = current_tenant_id. Nie ma wycieku typu „access denied”, tylko pusty wynik.
W obrębie jednego tenanta role dodatkowo zawężają dostęp. Menedżer może widzieć wszystkie faktury i zgłoszenia dla swojego tenanta. Agent widzi tylko tickety przypisane do niego, może mieć też swoje szkice. W tym miejscu zespoły często popełniają błędy w API, zwłaszcza gdy filtry są opcjonalne.
Support to szczególny przypadek. Mogą potrzebować widoku faktur, żeby pomagać klientom, ale nie powinni zmieniać wrażliwych pól jak amount, bank_account czy tax_id. Praktyczny wzorzec to:
- Daj
SELECTna faktury dla roli support (wciąż scoped do tenanta). - Pozwól na
UPDATEtylko przez „bezpieczną” ścieżkę (np. widok pokazujący edytowalne kolumny albo restrykcyjna polityka odrzucająca zmiany chronionych pól).
Teraz scenariusz „przypadkowy błąd w API”: endpoint zapomniał zastosować filtr tenant w czasie refaktoryzacji. Bez RLS może to wyciec cross-tenant. Z RLS baza odmówi zwrócenia wierszy spoza bieżącego tenanta, więc błąd da efekt zepsutego ekranu zamiast wycieku danych.
Jeśli budujesz taki SaaS w AppMaster, nadal warto mieć te reguły w bazie. Kontrole w UI pomagają, ale to reguły bazy trzymają, gdy coś przycieknie.
Typowe błędy i jak ich unikać
RLS jest potężny, ale drobne przeoczenia mogą sprawić, że „bezpieczne” stanie się „zaskakujące”. Najwięcej problemów pojawia się, gdy dodasz nową tabelę, zmienisz rolę lub ktoś testuje z niewłaściwym użytkownikiem bazy.
Częste potknięcie to zapomnienie włączenia RLS na nowej tabeli. Możesz napisać polityki dla kluczowych tabel, potem dodać notes czy attachments i wysłać je z pełnym dostępem. Zrób regułę: nowa tabela = RLS włączone + przynajmniej jedna polityka.
Inną pułapką są niespójne polityki między akcjami. Polityka pozwalająca INSERT ale blokująca SELECT może powodować, że dane „znikają” zaraz po dodaniu. I odwrotnie: użytkownicy mogą czytać wiersze, których nie mogą utworzyć, i zaczną obchodzić to w UI. Myśl w przepływach: „utwórz potem obejrzyj”, „zaktualizuj potem otwórz ponownie”, „usuń potem listuj”.
Uważaj na SECURITY DEFINER funkcje. Uruchamiają się z uprawnieniami właściciela funkcji i mogą ominąć RLS, jeśli nie będziesz ostrożny. Jeśli ich używasz, trzymaj je małe, waliduj wejścia i unikaj dynamicznego SQL, chyba że naprawdę potrzebujesz.
Unikaj też polegania jedynie na filtrowaniu po stronie aplikacji, pozostawiając otwarty dostęp w bazie. Nawet dobrze zbudowane API z czasem zyska nowe endpointy, joby w tle i skrypty. Jeśli rola bazy może wszystko odczytać, prędzej czy później coś pójdzie nie tak.
Aby wykrywać problemy wcześnie, trzymaj kontrole praktyczne:
- Testuj używając tej samej roli DB, której używa produkcyjna aplikacja, a nie osobistego użytkownika admina.
- Dodaj jeden test negatywny na tabelę: użytkownik z innego tenanta powinien widzieć zero wierszy.
- Potwierdź, że każda tabela obsługuje oczekiwane akcje:
SELECT,INSERT,UPDATE,DELETE. - Przejrzyj użycie
SECURITY DEFINERi udokumentuj powód jego zastosowania. - Dołącz „RLS włączone?” do checklisty przeglądów kodu i migracji.
Przykład: jeśli agent wsparcia tworzy notatkę do faktury, ale potem nie może jej odczytać, zwykle to INSERT bez odpowiadającej SELECT polityki (lub brak ustawienia kontekstu tenant dla tej sesji).
Szybka lista kontrolna do walidacji konfiguracji RLS
RLS może wydawać się poprawny w przeglądzie, a i tak zawieść w rzeczywistości. Walidacja to mniej czytanie polityk, a więcej próbowania ich łamać realnymi kontami i zapytaniami. Testuj tak, jak używa tego Twoja aplikacja, nie jak byś chciał, żeby działało.
Stwórz mały zestaw tożsamości testowych. Użyj przynajmniej dwóch tenantów (Tenant A i Tenant B). Dla każdego dodaj zwykłego użytkownika i admina/menedżera. Jeśli wspierasz role typu „support agent” lub „read-only”, dodaj i je.
Następnie przetestuj RLS zestawem powtarzalnych sprawdzeń:
- Wykonaj główne operacje dla każdej roli: listowanie wierszy, pobranie pojedynczego wiersza po id, insert, update i delete. Dla każdej operacji spróbuj przypadków „dozwolone” i „powinno być zablokowane”.
- Udowodnij granice tenantów: jako Tenant A spróbuj odczytać lub zmodyfikować dane Tenant B znając istniejące id. Powinieneś otrzymać zero wierszy lub błąd uprawnień, nigdy fragmentaryczny wynik.
- Testuj joiny pod kątem wycieków: dołącz chronione tabele do innych (w tym tabel lookup). Potwierdź, że join nie przyciągnie powiązanych wierszy z innego tenanta przez klucz obcy lub widok.
- Sprawdź, że brak lub złe ustawienia kontekstu skutkują odmową dostępu: wyczyść kontekst tenant/user i spróbuj ponownie. „Brak kontekstu” powinien zakończyć się zamknięciem dostępu. Spróbuj też nieprawidłowego tenant id.
- Potwierdź podstawową wydajność: obejrzyj plany zapytań i upewnij się, że indeksy wspierają wzorzec filtrowania po
tenant_idi tym, po czym sortujesz lub wyszukujesz.
Jeśli którykolwiek test zaskakuje, napraw politykę lub sposób ustawiania kontekstu najpierw. Nie łataj tego w UI lub API i nie polegaj na przypuszczeniach, że reguły bazy „jakoś” będą trzymać.
Następne kroki: wdrażaj bezpiecznie i utrzymuj spójność
Traktuj RLS jak system bezpieczeństwa: wprowadzaj ostrożnie, weryfikuj często i utrzymuj reguły proste, by zespół ich przestrzegał.
Zacznij mało. Wybierz tabele, gdzie wyciek byłby najbardziej dotkliwy (płatności, faktury, dane HR, wiadomości klientów) i włącz RLS tam najpierw. Wczesne sukcesy są lepsze niż masowy rollout, którego nikt do końca nie rozumie.
Praktyczna kolejność wdrożenia często wygląda tak:
- Najpierw tabele „owned” (wiersze należą jasno do jednego tenanta)
- Tabele z danymi osobowymi (PII)
- Tabele współdzielone, ale filtrowane przez tenant (raporty, analityka)
- Tabele łączące i przypadki brzegowe (relacje wiele-do-wielu)
- Pozostałe, gdy podstawy są stabilne
Uczyń testowanie obowiązkowym. Testy automatyczne powinny uruchamiać te same zapytania dla różnych tenantów i ról i potwierdzać oczekiwane zmiany. Uwzględnij testy „powinno być dozwolone” i „powinno być odrzucone”, bo najdroższe błędy to te, które cicho dają zbyt szerokie uprawnienia.
Miej jedno, jasne miejsce w flow żądania, gdzie kontekst sesji jest ustawiany przed jakimkolwiek zapytaniem. Tenant id, user id i rola powinny być zastosowane raz, wcześnie i nigdy nie zgadywane później. Jeśli ustawisz kontekst w środku transakcji, prędzej czy później wykona się zapytanie z brakującymi lub przeterminowanymi wartościami.
Gdy budujesz z AppMaster, planuj spójność między generowanymi API a politykami PostgreSQL. Ustandaryzuj sposób przekazywania kontekstu tenant/rola do bazy (np. te same zmienne sesji dla każdego endpointu), żeby polityki zachowywały się wszędzie tak samo. Jeśli używasz AppMaster na appmaster.io, RLS wciąż jest wart traktowania jako ostateczny autorytet izolacji tenantów, nawet jeśli również ograniczasz dostęp w UI.
Wreszcie, obserwuj, co się nie udaje. Niepowodzenia autoryzacji to cenne sygnały, szczególnie zaraz po wdrożeniu. Monitoruj odmowy i badaj, czy to atak, uszkodzony klient czy polityka zbyt restrykcyjna.
Krótka lista nawyków, które utrzymają RLS zdrowym:
- Domyślne odmów, wyjątki dodawane celowo
- Jasne nazwy polityk (tabela + akcja + odbiorca)
- Zmiany polityk przeglądane jak zmiany w kodzie
- Logowanie i przegląd odmów podczas początkowego rollout
- Mały zestaw testów dodawany do każdej nowej tabeli z RLS


