Sicherheitsregeln testen
Beim Erstellen Ihrer Anwendung möchten Sie eventuell den Zugriff auf Ihre Firestore-Datenbank sperren. Dazu benötigen Sie aber detailliertere Firestore-Sicherheitsregeln. Neben dem Prototyping und Testen der allgemeinen Funktionen und des Verhaltens Ihrer Anwendung mit dem Firestore-Emulator können Sie damit auch Einheitentests schreiben, die das Verhalten Ihrer Firestore-Sicherheitsregeln prüfen.
Kurzanleitung
Einige grundlegende Testfälle mit einfachen Regeln bieten die Kurzanleitung-Beispiel.
Informationen zu Firestore-Sicherheitsregeln
Implementieren Sie Firebase Authentication und die Firestore-Sicherheitsregeln für eine serverlose Authentifizierung, Autorisierung und Datenvalidierung, wenn Sie die Mobil- und Webclientbibliotheken verwenden.
Die Firestore-Sicherheitsregeln bestehen aus zwei Teilen:
- Eine
match
-Anweisung, mit der Dokumente in Ihrer Datenbank ermittelt werden. - Ein
allow
-Ausdruck, mit dem der Zugriff auf diese Dokumente gesteuert wird.
Firebase Authentication prüft die Anmeldedaten der Nutzer und bildet die Grundlage für nutzerbasierte und rollenbasierte Zugriffssysteme.
Jede Datenbankanfrage aus einer Mobil-/Webclientbibliothek von Firestore wird anhand Ihrer Sicherheitsregeln ausgewertet, bevor Daten gelesen oder geschrieben werden. Wenn der Zugriff auf einen der angegebenen Dokumentpfade durch die Regeln verweigert wird, schlägt die gesamte Anfrage fehl.
Weitere Informationen zu Firestore-Sicherheitsregeln finden Sie unter Erste Schritte mit Sicherheitsregeln.
Emulator installieren
Zum Installieren des Firestore-Emulators verwenden Sie die Firebase CLI und führen den folgenden Befehl aus:
firebase setup:emulators:firestore
Emulator starten
Beginnen Sie mit der Initialisierung eines Firebase-Projekts in Ihrem Arbeitsverzeichnis. Dies ist ein häufiger erster Schritt bei der Verwendung der Firebase CLI.
firebase init
Starten Sie den Emulator mit dem unten aufgeführten Befehl. Der Emulator wird ausgeführt, bis Sie den Prozess beenden:
firebase emulators:start --only firestore
Sie werden häufig den Emulator starten, eine Testsuite ausführen und dann den Emulator nach Beendigung der Tests herunterfahren. Dies können Sie ganz einfach mit dem Befehl emulators:exec
tun:
firebase emulators:exec --only firestore "./my-test-script.sh"
Nach dem Starten versucht der Emulator, auf einem Standardport (8080) zu laufen. Sie können den Port des Emulators ändern, indem Sie den Abschnitt "emulators"
in der firebase.json
-Datei ändern:
{ // ... "emulators": { "firestore": { "port": "YOUR_PORT" } } }
Vor dem Starten des Emulators
Für die Verwendung des Emulators ist Folgendes zu beachten:
- Der Emulator lädt zuerst die Regeln, die im Feld
firestore.rules
Ihrer Dateifirebase.json
angegeben sind. Er erwartet den Namen einer lokalen Datei mit Ihren Firestore-Sicherheitsregeln und wendet diese Regeln auf alle Projekte an. Wenn Sie den lokalen Dateipfad nicht angeben oder die MethodeloadFirestoreRules
wie unten beschrieben anwenden, behandelt der Emulator alle Projekte als solche mit offenen Regeln. - Während die meisten Firebase-SDKs direkt mit den Emulatoren arbeiten, unterstützt nur die
@firebase/rules-unit-testing
-Bibliothek das Mocking vonauth
in den Sicherheitsregeln. Das vereinfacht die Unittests. Darüber hinaus unterstützt die Bibliothek einige Emulator-spezifische Funktionen wie das Löschen aller Daten, wie unten aufgeführt. - Die Emulatoren akzeptieren auch Firebase-Auth-Produktionstoken, die über Client-SDKs bereitgestellt werden, und werten die Regeln entsprechend aus. Dadurch kann Ihre Anwendung bei Integrations- und manuellen Tests direkt mit den Emulatoren verbunden werden.
Lokale Unittests ausführen
Lokale Unittests mit dem v9 JavaScript SDK ausführen
Firebase bietet sowohl mit dem JavaScript-SDK der Version 9 als auch mit dem SDK der Version 8 eine Unittest-Bibliothek für Sicherheitsregeln an. Die Bibliotheks-APIs unterscheiden sich erheblich. Wir empfehlen die v9-Testbibliothek, die optimiert ist und weniger Einrichtungsschritte erfordert, um eine Verbindung zu Emulatoren herzustellen und daher versehentliche Verwendung von Produktionsressourcen zu vermeiden. Aus Gründen der Abwärtskompatibilität stellen wir weiterhin die v8-Testbibliothek zur Verfügung.
Verwenden Sie das Modul @firebase/rules-unit-testing
, um den Emulator anzusprechen, der lokal ausgeführt wird. Wenn Zeitüberschreitungen oder ECONNREFUSED
-Fehler auftreten, prüfen Sie, ob der Emulator tatsächlich ausgeführt wird.
Es wird dringend empfohlen, eine aktuelle Version von Node.js zu verwenden, damit Sie die async/await
-Notation verwenden können. Fast das gesamte Verhalten, das Sie testen möchten, umfasst asynchrone Funktionen. Das Testmodul ist für die Verwendung mit Promise-basiertem Code ausgelegt.
Die v9-Regeln Einheitentest Bibliothek kennt immer die Emulatoren und greift niemals auf Ihre Produktionsressourcen zu.
Sie importieren die Bibliothek mithilfe von modularen v9-Importanweisungen. Beispiel:
import {
assertFails,
assertSucceeds,
initializeTestEnvironment
} from "@firebase/rules-unit-testing"
// Use `const { … } = require("@firebase/rules-unit-testing")` if imports are not supported
// Or we suggest `const testing = require("@firebase/rules-unit-testing")` if necessary.
Nach dem Import umfasst das Implementieren von Unittest Folgendes:
RulesTestEnvironment
mit einem Aufruf voninitializeTestEnvironment
erstellen und konfigurieren- Einrichtung von Testdaten ohne Auslösung von Regeln, indem Sie eine praktische Methode verwenden, mit der Sie diese vorübergehend umgehen können:
RulesTestEnvironment.withSecurityRulesDisabled
. - Einrichten von Testsuite und Pro-Test vor/nach Hooks mit Aufrufen zur Bereinigung von Testdaten und -umgebungen, z. B.
RulesTestEnvironment.cleanup()
oderRulesTestEnvironment.clearFirestore()
. - Testfälle implementieren, die Authentifizierungsstatus mit
RulesTestEnvironment.authenticatedContext
undRulesTestEnvironment.unauthenticatedContext
nachahmen
Gängige Methoden und Dienstfunktionen
Weitere Informationen finden Sie unter Emulator-spezifische Testmethoden im v9 SDK.
initializeTestEnvironment() => RulesTestEnvironment
Diese Funktion initialisiert eine Testumgebung für Regel-Unittest. Rufen Sie diese Funktion zuerst zum Testen der Einrichtung auf. Für eine erfolgreiche Ausführung müssen Emulatoren ausgeführt werden.
Die Funktion akzeptiert ein optionales Objekt, das eine TestEnvironmentConfig
definiert, die aus einer Projekt-ID und Einstellungen für die Emulatorkonfiguration bestehen kann.
let testEnv = await initializeTestEnvironment({ projectId: "demo-project-1234", firestore: { rules: fs.readFileSync("firestore.rules", "utf8"), }, });
RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext
Diese Methode erstellt einen RulesTestContext
, der sich wie ein authentifizierter Authentifizierungsnutzer verhält. Anfragen, die über den zurückgegebenen Kontext erstellt werden, haben ein simuliertes Authentifizierungstoken. Optional können Sie ein Objekt übergeben, das benutzerdefinierte Anforderungen oder Überschreibungen für Authentifizierungstoken-Nutzlasten definiert.
Verwenden Sie das zurückgegebene Testkontextobjekt in Ihren Tests, um auf einen Emulator zuzugreifen
Instanzen konfiguriert, einschließlich der mit initializeTestEnvironment
konfigurierten Instanzen.
// Assuming a Firestore app and the Firestore emulator for this example import { setDoc } from "firebase/firestore"; const alice = testEnv.authenticatedContext("alice", { … }); // Use the Firestore instance associated with this context await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });
RulesTestEnvironment.unauthenticatedContext() => RulesTestContext
Diese Methode erstellt einen RulesTestContext
, der sich wie ein Client verhält, der nicht über Authentifizierung angemeldet ist. An Anfragen, die über den zurückgegebenen Kontext erstellt werden, werden keine Firebase Auth-Token angehängt.
Verwenden Sie das zurückgegebene Testkontextobjekt in Ihren Tests, um auf alle konfigurierten Emulatorinstanzen zuzugreifen, einschließlich der mit initializeTestEnvironment
konfigurierten.
// Assuming a Cloud Storage app and the Storage emulator for this example import { getStorage, ref, deleteObject } from "firebase/storage"; const alice = testEnv.unauthenticatedContext(); // Use the Cloud Storage instance associated with this context const desertRef = ref(alice.storage(), 'images/desert.jpg'); await assertSucceeds(deleteObject(desertRef));
RulesTestEnvironment.withSecurityRulesDisabled()
Führen Sie eine Testeinrichtungsfunktion mit einem Kontext aus, der sich so verhält, als seien die Sicherheitsregeln deaktiviert.
Diese Methode verwendet eine Callback-Funktion, die den Kontext verwendet, der Sicherheitsregeln umgeht, und die ein Promise zurückgibt. Der Kontext wird gelöscht, sobald das Promise aufgelöst oder abgelehnt wird.
RulesTestEnvironment.cleanup()
Diese Methode löscht alle RulesTestContexts
, die in der Testumgebung erstellt wurden, und bereinigt die zugrunde liegenden Ressourcen, was einen sauberen Beendigung ermöglicht.
Diese Methode ändert den Status der Emulatoren nicht. Verwenden Sie die Methode für spezifische Daten des Anwendungsemulators, um Daten zwischen Tests zurückzusetzen.
assertSucceeds(pr: Promise<any>)) => Promise<any>
Dies ist eine Dienstfunktion für Testläufe.
Die Funktion bestätigt, dass der bereitgestellte Promise, der einen Emulator-Vorgang verpackt, ohne Verstöße gegen Sicherheitsregeln aufgelöst wird.
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });
assertFails(pr: Promise<any>)) => Promise<any>
Dies ist eine Dienstfunktion für Testläufe.
Die Funktion bestätigt, dass der bereitgestellte Promise, der einen Emulator-Vorgang verpackt, mit einem Verstoß gegen Sicherheitsregeln abgelehnt wird.
await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });
Emulator-spezifische Methoden
Weitere Informationen finden Sie unter Allgemeine Testmethoden und Dienstfunktionen im v9 SDK.
RulesTestEnvironment.clearFirestore() => Promise<void>
Mit dieser Methode werden Daten in der Firestore-Datenbank gelöscht, die zu der für den Firestore-Emulator konfigurierten projectId
gehören.
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
Diese Methode ruft eine Firestore-Instanz für diesen Testkontext ab. Die zurückgegebene Firebase JS Client SDK-Instanz kann mit den Client SDK APIs verwendet werden (v9 modular oder v9 compat).
Evaluierungen von Regeln visualisieren
Mit dem Firestore-Emulator können Sie Clientanfragen in der Benutzeroberfläche der Emulator Suite visualisieren, einschließlich des Bewertungs-Tracings für Firebase-Sicherheitsregeln.
Öffnen Sie den Tab Firestore > Anfragen, um die detaillierte Bewertungssequenz für jede Anfrage aufzurufen.
Testberichte erstellen
Nachdem Sie eine Reihe von Tests ausgeführt haben, können Sie auf Berichte zur Testabdeckung zugreifen, die Aufschluss darüber geben, wie die einzelnen Sicherheitsregeln bewertet wurden.
Um die Berichte abzurufen, fragen Sie einen bereitgestellten Endpunkt im Emulator ab, während er ausgeführt wird. Verwenden Sie für eine browserfreundliche Version die folgende URL:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html
Dadurch werden Ihre Regeln in Ausdrücke und Teilausdrücke aufgeteilt. Bewegen Sie die Maus auf einen Ausdruck oder Teilausdruck, um weitere Informationen zu erhalten, einschließlich der Anzahl der Bewertungen und der zurückgegebenen Werte. Fügen Sie für die JSON-Rohversion dieser Daten die folgende URL in Ihre Abfrage ein:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage
Unterschiede zwischen Emulator und Produktion
- Sie müssen kein Firestore-Projekt explizit erstellen. Der Emulator legt automatisch jede Instanz an, auf die zugegriffen wird.
- Der Firestore-Emulator funktioniert nicht mit dem herkömmlichen Firebase-Authentifizierungsablauf.
Stattdessen wurde im Firebase Test SDK die Methode
initializeTestApp()
in der Bibliothekrules-unit-testing
bereitgestellt, was einauth
-Feld einnimmt. Das mit dieser Methode erstellte Firebase-Handle verhält sich so, als hätte es sich erfolgreich authentifiziert, unabhängig von der von Ihnen bereitgestellten Entität. Wenn Sienull
übergeben, verhält es sich wie ein nicht authentifizierter Nutzer (auth != null
-Regeln schlagen zum Beispiel fehl).
Bekannte Probleme beheben
Bei Verwendung des Firestore-Emulators treten möglicherweise die unten aufgeführten bekannten Probleme auf. Folgen Sie der Anleitung unten, um unerwartetes Verhalten zu korrigieren. Diese Hinweise wurden für die Bibliothek für Einheitentests für Sicherheitsregeln geschrieben. Die allgemeinen Ansätze gelten jedoch für jedes Firebase SDK.
Das Testverhalten ist inkonsistent
Wenn Ihre Tests hin und wieder erfolgreich sind und fehlschlagen, obwohl Sie die Tests selbst nicht geändert haben, müssen Sie gegebenenfalls überprüfen, ob sie in der richtigen Reihenfolge ablaufen.
Die meisten Interaktionen mit dem Emulator sind asynchron. Überprüfen Sie daher, ob der gesamte asynchrone Code ordnungsgemäß sequenziert ist. Sie können die Reihenfolge korrigieren, indem Sie Versprechen verketten oder die await
-Notation häufiger verwenden.
Überprüfen Sie insbesondere die folgenden asynchronen Vorgänge:
- Sicherheitsregeln festlegen, z. B. mit
initializeTestEnvironment
. - Lesen und Schreiben von Daten, z. B. mit
db.collection("users").doc("alice").get()
. - Operative Assertionen, einschließlich
assertSucceeds
undassertFails
.
Tests sind nur dann erfolgreich, wenn Sie den Emulator das erste Mal laden
Der Emulator ist zustandsorientiert. Alle in den Speicher geschriebenen Daten werden gespeichert, sodass die Daten bei jedem Herunterfahren des Emulators verloren gehen. Wenn Sie mehrere Tests mit derselben Projekt-ID durchführen, kann jeder Test Daten liefern, die nachfolgende Tests beeinflussen können. Sie können dieses Verhalten mit einer der folgenden Methoden umgehen:
- Verwenden Sie für jeden Test eindeutige Projekt-IDs. Wenn Sie sich für dieses Vorgehen entscheiden, muss
initializeTestEnvironment
bei jedem Test aufgerufen werden. Regeln werden automatisch nur für die Standardprojekt-ID geladen. - Strukturieren Sie Ihre Tests so um, dass sie nicht mit zuvor geschriebenen Daten interagieren. Verwenden Sie beispielsweise für jeden Test eine andere Sammlung.
- Löschen Sie alle während eines Tests geschriebenen Daten.
Die Testeinrichtung ist sehr kompliziert
Beim Einrichten Ihres Tests möchten Sie vielleicht die Daten so ändern, wie Ihre Firestore-Sicherheitsregeln letztendlich nicht zulassen. Wenn die Testeinrichtung durch Ihre Regeln sehr komplex wird, versuchen Sie, in den Einrichtungsschritten RulesTestEnvironment.withSecurityRulesDisabled
zu verwenden. So werden Lese- und Schreibvorgänge keine PERMISSION_DENIED
-Fehler auslösen.
Danach kann Ihr Test Vorgänge als authentifizierter oder nicht authentifizierten Nutzer mithilfe von RulesTestEnvironment.authenticatedContext
bzw. unauthenticatedContext
ausführen. Auf diese Weise können Sie prüfen, ob Ihre Firestore-Sicherheitsregeln verschiedene Fälle ordnungsgemäß zulassen / ablehnen.