Demesne

Die Autorisierungs-Engine, auf der Fóir läuft. Zugriffsregeln werden einmal in einer Spec-Datei geschrieben, und Demesne kompiliert sie zu Postgres Row-Level Security, sodass die Datenbank sie auf jede Abfrage anwendet. Wir haben es für Fóir gebaut und unter der Apache-2.0-Lizenz veröffentlicht.

Was es leistet

Eine Demesne-Spec beschreibt vier Dinge: die Form Ihrer Multi-Tenancy, die Arten von Nutzern und Zugangsdaten, die handeln, die Tabellen, auf denen sie handeln, und die Relationen, die sie verbinden, etwa Eigentümerschaft, Rollen, Freigaben und Gruppenmitgliedschaft.

Aus dieser Datei erzeugt der Compiler die Row-Level-Security-Policies und die Datenbankfunktionen, die diese Policies aufrufen. Er erzeugt außerdem eine Berechtigungs-Map für Go und TypeScript, für die Fragen, bei denen es nicht um Zeilen geht („darf dieser Nutzer veröffentlichen?“), und die Claims, die eine Sitzung tragen muss. Wird die Spec geändert und neu erzeugt, ziehen Datenbank und Anwendungscode gemeinsam nach.

Warum die Regeln in der Datenbank liegen

Autorisierung sitzt meist im Anwendungscode: in einem Service, den man aufruft, oder in Prüfungen, die über die Request-Handler verstreut sind. So oder so schützt sie nur die Pfade, die daran denken zu fragen. Ein Hintergrundjob, ein neuer Endpunkt oder eine Abfrage, die in einer Datenbankkonsole getippt wird, umgeht sie.

Demesne legt die Entscheidung auf die Daten. Dieselbe Policy filtert jede Abfrage, gleich welcher Code sie gesendet hat, sodass ein Pfad, der die Prüfung vergisst, nur die Zeilen zurückbekommt, die sein Aufrufer sehen durfte.

Warum wir einen Compiler geschrieben haben

Row-Level Security ist der richtige Ort für eine Regel, aber unhandlich, wenn man sie von Hand schreibt. Ein Multi-Tenant-Produkt braucht Eigentümerschaft, Rollen, verschachtelte Gruppen und Freigaben, wiederholt über jede Tabelle, und diese Muster lassen sich leicht auf subtile Weise falsch machen.

Die Engines, die das Modell von Demesne teilen, nämlich Googles Zanzibar und die Open-Source-Projekte, die ihm folgen, laufen als eigener Service. Ihre Anwendung muss diesen Service für jede Entscheidung aufrufen und seine Kopie der Beziehungen mit Ihren Daten synchron halten. Wir wollten das Modell ohne den Service, deshalb lesen die Regeln von Demesne Ihre vorhandenen Tabellen in derselben Transaktion wie die Abfrage.

Wie die Ausgabe geprüft wird

Der Build von Demesne vergleicht jede erzeugte Datei Byte für Byte mit einer eingecheckten Kopie. Jede Berechtigungsprüfung auf Anwendungsseite trägt die Bedingung der passenden Policy wortgetreu in sich, sodass der Code nicht von der Datenbank abdriften kann, ohne dass der Build fehlschlägt. Gegen ein echtes Postgres ausgeführt, installiert die Testsuite die erzeugten Funktionen und bestätigt Fall für Fall, dass das SQL mit den Go- und TypeScript-Versionen übereinstimmt.

Bei einer Datenbank, die bereits im Einsatz ist, meldet demesne diff jeden Unterschied zwischen der Spec und den Policies, die live sind.

Warum es Open Source ist

Die Komponente, die entscheidet, wer Ihre Daten lesen darf, sollte die sein, die Sie prüfen können. Jede Plattform kann behaupten, ihre Tenants seien isoliert. Bei Demesne können Sie den Compiler lesen, der unsere Policies schreibt, die Tests, die er bestehen muss, und das SQL, das er erzeugt, und Sie können das alles selbst ausführen.

Nichts an Demesne ist spezifisch für Fóir. Es funktioniert mit jeder Postgres-Datenbank und liest die Sitzung, die Ihr eigener Anmeldeanbieter ausstellt, und die Anleitung im Repository führt durch die Einführung in einem bestehenden Schema. Ein Team ohne Interesse an unserer Plattform kann es nutzen, und das ist die beste Probe, die wir kennen, ob das Modell trägt.

Was es auf Fóir bedeutet

Das Zugriffsmodell von Fóir ist eine einzige Demesne-Spec, die zum Zeitpunkt der Erstellung etwa hundert Tabellen regelt. Ein Test listet jede Tabelle unseres Schemas auf und schlägt bei jeder fehl, die ohne dokumentierte Entscheidung darüber hinzugekommen ist, wie sie geschützt wird.

Die Regeln, die Sie für Ihr eigenes Projekt festlegen, etwa wer einen Datensatz besitzt und welche Rollen ein Modell lesen dürfen, sind Daten, die diese Policies lesen, und wirken daher ebenfalls innerhalb der Datenbank. In der Datenbank erzwungen beschreibt, wie die Policies erzeugt, erzwungen und getestet werden.

Es bleibt bei Ihnen

Ein Postgres-Export Ihres Projekts enthält Row-Level-Security-Policies, die nachbilden, welche Zeilen jeder Ihrer Nutzer lesen konnte, bereit zur Anwendung auf die Datenbank, die Ihnen dann gehört. Wenn Sie das ganze Modell auf Ihrer eigenen Infrastruktur haben möchten, steht die Engine unter einer Lizenz zur Verfügung, die das erlaubt.

Wo es das falsche Werkzeug ist

Demesne gibt es nur für Postgres. Es ist eine Bibliothek und ein Kommandozeilenwerkzeug, ohne gehosteten Service, den man aufruft. Jede Regel muss sich als SQL-Bedingung ausdrücken lassen, und Fragen, die in die andere Richtung laufen („wer kann diesen Datensatz sehen?“), werden konservativ beantwortet. Das Repository enthält einen Vergleich mit Zanzibar, Ory Keto, OpenFGA, Cerbos und Oso, der sagt, wo jeweils die andere Lösung die bessere Wahl ist.

Selbst nachlesen

Die Spec-Sprache, das erzeugte SQL und die Testsuite liegen im Repository.