Testen Sie Ihre Cloud Firestore-Sicherheitsregeln

Während Sie Ihre App erstellen, möchten Sie möglicherweise den Zugriff auf Ihre Cloud Firestore-Datenbank sperren. Vor dem Start benötigen Sie jedoch differenziertere Cloud Firestore-Sicherheitsregeln. Mit dem Cloud Firestore-Emulator können Sie nicht nur Prototypen erstellen und die allgemeinen Funktionen und das Verhalten Ihrer App testen, sondern auch Komponententests schreiben, die das Verhalten Ihrer Cloud Firestore-Sicherheitsregeln überprüfen.

Schnellstart

Probieren Sie für einige grundlegende Testfälle mit einfachen Regeln das Schnellstartbeispiel aus.

Verstehen Sie die Sicherheitsregeln von Cloud Firestore

Implementieren Sie Firebase-Authentifizierung und Cloud Firestore-Sicherheitsregeln für serverlose Authentifizierung, Autorisierung und Datenvalidierung, wenn Sie die Mobil- und Web-Client-Bibliotheken verwenden.

Die Cloud Firestore-Sicherheitsregeln bestehen aus zwei Teilen:

  1. Eine match Anweisung, die Dokumente in Ihrer Datenbank identifiziert.
  2. Ein allow , der den Zugriff auf diese Dokumente steuert.

Die Firebase-Authentifizierung überprüft die Anmeldeinformationen der Benutzer und bildet die Grundlage für benutzerbasierte und rollenbasierte Zugriffssysteme.

Jede Datenbankanforderung von einer mobilen/Web-Client-Bibliothek von Cloud Firestore wird anhand Ihrer Sicherheitsregeln bewertet, bevor Daten gelesen oder geschrieben werden. Wenn die Regeln den Zugriff auf einen der angegebenen Dokumentpfade verweigern, schlägt die gesamte Anfrage fehl.

Weitere Informationen zu Cloud Firestore-Sicherheitsregeln finden Sie unter Erste Schritte mit Cloud Firestore-Sicherheitsregeln .

Installieren Sie den Emulator

Um den Cloud Firestore-Emulator zu installieren, verwenden Sie die Firebase-CLI und führen Sie den folgenden Befehl aus:

firebase setup:emulators:firestore

Führen Sie den Emulator aus

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 folgenden Befehl. Der Emulator läuft, bis Sie den Prozess abbrechen:

firebase emulators:start --only firestore

In vielen Fällen möchten Sie den Emulator starten, eine Testsuite ausführen und den Emulator dann herunterfahren, nachdem die Tests ausgeführt wurden. Sie können dies ganz einfach mit dem Befehl emulators:exec tun:

firebase emulators:exec --only firestore "./my-test-script.sh"

Beim Start versucht der Emulator, auf einem Standardport (8080) zu laufen. Sie können den Emulator-Port ändern, indem Sie den Abschnitt "emulators" Ihrer firebase.json Datei ändern:

{
  // ...
  "emulators": {
    "firestore": {
      "port": "YOUR_PORT"
    }
  }
}

Bevor Sie den Emulator ausführen

Beachten Sie Folgendes, bevor Sie den Emulator verwenden:

  • Der Emulator lädt zunächst die im Feld firestore.rules Ihrer Datei firebase.json angegebenen Regeln. Es erwartet den Namen einer lokalen Datei, die Ihre Cloud Firestore-Sicherheitsregeln enthält, und wendet diese Regeln auf alle Projekte an. Wenn Sie den lokalen Dateipfad nicht angeben oder die Methode loadFirestoreRules nicht wie unten beschrieben verwenden, behandelt der Emulator alle Projekte so, als hätten sie offene Regeln.
  • Während die meisten Firebase-SDKs direkt mit den Emulatoren arbeiten, unterstützt nur die @firebase/rules-unit-testing Bibliothek die Mock- auth in Sicherheitsregeln, was Unit-Tests erheblich vereinfacht. Darüber hinaus unterstützt die Bibliothek einige emulatorspezifische Funktionen wie das Löschen aller Daten, wie unten aufgeführt.
  • Die Emulatoren akzeptieren auch über Client-SDKs bereitgestellte Firebase-Auth-Token für die Produktion und werten die Regeln entsprechend aus, sodass Sie Ihre Anwendung bei Integrations- und manuellen Tests direkt mit den Emulatoren verbinden können.

Führen Sie lokale Unit-Tests durch

Führen Sie lokale Unit-Tests mit dem v9 JavaScript SDK aus

Firebase vertreibt eine Unit-Testing-Bibliothek für Sicherheitsregeln sowohl mit seinem JavaScript-SDK der Version 9 als auch mit seinem SDK der Version 8. Die Bibliotheks-APIs unterscheiden sich erheblich. Wir empfehlen die v9-Testbibliothek, die schlanker ist und weniger Setup für die Verbindung mit Emulatoren erfordert und so eine versehentliche Nutzung von Produktionsressourcen sicher vermeidet. 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 mit dem Emulator zu interagieren, der lokal ausgeführt wird. Wenn Zeitüberschreitungen oder ECONNREFUSED Fehler auftreten, überprüfen Sie noch einmal, ob der Emulator tatsächlich ausgeführt wird.

Wir empfehlen dringend, eine aktuelle Version von Node.js zu verwenden, damit Sie async/await Notation verwenden können. Fast das gesamte Verhalten, das Sie testen möchten, betrifft asynchrone Funktionen, und das Testmodul ist für die Arbeit mit Promise-basiertem Code konzipiert.

Die v9 Rules Unit Testing-Bibliothek kennt die Emulatoren immer und berührt niemals Ihre Produktionsressourcen.

Sie importieren die Bibliothek mit v9 modularen Importanweisungen. Zum 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 die Implementierung von Unit-Tests Folgendes:

  • Erstellen und Konfigurieren einer RulesTestEnvironment mit einem Aufruf von initializeTestEnvironment .
  • Einrichten von Testdaten ohne Auslösen von Regeln mithilfe einer praktischen Methode, mit der Sie diese vorübergehend umgehen können: RulesTestEnvironment.withSecurityRulesDisabled .
  • Einrichten von Testsuite- und Test-Vorher/Nachher-Hooks mit Aufrufen zum Bereinigen von Testdaten und -umgebung, wie RulesTestEnvironment.cleanup() oder RulesTestEnvironment.clearFirestore() .
  • Implementieren von Testfällen, die Authentifizierungszustände nachahmen, mithilfe von RulesTestEnvironment.authenticatedContext und RulesTestEnvironment.unauthenticatedContext .

Gängige Methoden und Hilfsfunktionen

Siehe auch emulatorspezifische Testmethoden im v9 SDK .

initializeTestEnvironment() => RulesTestEnvironment

Diese Funktion initialisiert eine Testumgebung für Regeleinheitentests. Rufen Sie diese Funktion zuerst für den Testaufbau 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 Emulatorkonfigurationseinstellungen 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 Authentifizierungsbenutzer verhält. An Anfragen, die über den zurückgegebenen Kontext erstellt werden, ist ein Schein-Authentifizierungstoken angehängt. Übergeben Sie optional ein Objekt, das benutzerdefinierte Ansprüche oder Überschreibungen für Authentifizierungstoken-Nutzlasten definiert.

Verwenden Sie das zurückgegebene Testkontextobjekt in Ihren Tests, um auf alle konfigurierten Emulatorinstanzen zuzugreifen, einschließlich der mit initializeTestEnvironment konfigurierten.

// 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 die Authentifizierung angemeldet ist. An Anfragen, die über den zurückgegebenen Kontext erstellt werden, sind keine Firebase-Auth-Tokens 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 Test-Setup-Funktion mit einem Kontext aus, der sich so verhält, als wären die Sicherheitsregeln deaktiviert.

Diese Methode verwendet eine Rückruffunktion, die den Kontext zur Umgehung von Sicherheitsregeln übernimmt und ein Versprechen zurückgibt. Der Kontext wird zerstört, sobald das Versprechen gelöst/ablehnt wird.

RulesTestEnvironment.cleanup()

Diese Methode zerstört alle in der Testumgebung erstellten RulesTestContexts und bereinigt die zugrunde liegenden Ressourcen, sodass ein sauberer Exit möglich ist.

Diese Methode ändert in keiner Weise den Status von Emulatoren. Um Daten zwischen Tests zurückzusetzen, verwenden Sie die anwendungsemulatorspezifische Methode zum Löschen von Daten.

assertSucceeds(pr: Promise<any>)) => Promise<any>

Dies ist eine Dienstprogrammfunktion für Testfälle.

Die Funktion stellt sicher, dass das bereitgestellte Promise, das einen Emulatorvorgang umschließt, ohne Verstöße gegen Sicherheitsregeln aufgelöst wird.

await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

assertFails(pr: Promise<any>)) => Promise<any>

Dies ist eine Dienstprogrammfunktion für Testfälle.

Die Funktion stellt sicher, dass das bereitgestellte Promise, das einen Emulatorvorgang umschließt, mit einem Verstoß gegen die Sicherheitsregeln abgelehnt wird.

await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });

Emulatorspezifische Methoden

Siehe auch allgemeine Testmethoden und Dienstprogrammfunktionen im v9 SDK .

RulesTestEnvironment.clearFirestore() => Promise<void>

Diese Methode löscht Daten in der Firestore-Datenbank, die zur 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 (v9 modular oder v9 kompatibel) verwendet werden.

Visualisieren Sie Regelauswertungen

Mit dem Cloud Firestore-Emulator können Sie Clientanfragen in der Emulator Suite-Benutzeroberfläche visualisieren, einschließlich der Auswertungsverfolgung für Firebase-Sicherheitsregeln.

Öffnen Sie die Registerkarte Firestore > Anfragen , um die detaillierte Bewertungssequenz für jede Anfrage anzuzeigen.

Firestore-Emulator-Anforderungsmonitor mit Auswertungen der Sicherheitsregeln

Testberichte erstellen

Nachdem Sie eine Reihe von Tests ausgeführt haben, können Sie auf Testabdeckungsberichte zugreifen, die zeigen, wie jede Ihrer Sicherheitsregeln bewertet wurde.

Um die Berichte zu erhalten, fragen Sie einen verfügbar gemachten Endpunkt auf dem Emulator ab, während dieser ausgeführt wird. Für eine browserfreundliche Version verwenden Sie die folgende URL:

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html

Dadurch werden Ihre Regeln in Ausdrücke und Unterausdrücke unterteilt, über die Sie mit der Maus fahren können, um weitere Informationen zu erhalten, einschließlich der Anzahl der Auswertungen und zurückgegebenen Werte. Für die JSON-Rohversion dieser Daten fügen Sie die folgende URL in Ihre Abfrage ein:

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage

Unterschiede zwischen Emulator und Produktion

  1. Sie müssen nicht explizit ein Cloud Firestore-Projekt erstellen. Der Emulator erstellt automatisch jede Instanz, auf die zugegriffen wird.
  2. Der Cloud Firestore-Emulator funktioniert nicht mit dem normalen Firebase-Authentifizierungsablauf. Stattdessen haben wir im Firebase Test SDK die Methode initializeTestApp() in der Bibliothek rules-unit-testing bereitgestellt, die ein auth benötigt. Das mit dieser Methode erstellte Firebase-Handle verhält sich so, als ob es sich erfolgreich als die von Ihnen angegebene Entität authentifiziert hätte. Wenn Sie null übergeben, verhält es sich wie ein nicht authentifizierter Benutzer (z. B. schlagen auth != null Regeln fehl).

Beheben Sie bekannte Probleme

Wenn Sie den Cloud Firestore-Emulator verwenden, können die folgenden bekannten Probleme auftreten. Befolgen Sie die nachstehenden Anleitungen, um etwaiges unregelmäßiges Verhalten zu beheben. Diese Hinweise wurden unter Berücksichtigung der Unit-Testing-Bibliothek für Sicherheitsregeln geschrieben, die allgemeinen Ansätze gelten jedoch für jedes Firebase SDK.

Das Testverhalten ist inkonsistent

Wenn Ihre Tests gelegentlich bestanden und nicht bestanden werden, auch wenn an den Tests selbst keine Änderungen vorgenommen wurden, müssen Sie möglicherweise überprüfen, ob sie ordnungsgemäß sequenziert sind. Die meisten Interaktionen mit dem Emulator erfolgen asynchron. Überprüfen Sie daher noch einmal, ob der gesamte asynchrone Code ordnungsgemäß sequenziert ist. Sie können die Reihenfolge korrigieren, indem Sie Versprechen entweder verketten oder await Notation großzügig verwenden.

Überprüfen Sie insbesondere die folgenden asynchronen Vorgänge:

  • Festlegen von Sicherheitsregeln, beispielsweise mit initializeTestEnvironment .
  • Lesen und Schreiben von Daten, beispielsweise mit db.collection("users").doc("alice").get() .
  • Operative Behauptungen, einschließlich assertSucceeds und assertFails .

Tests bestehen nur beim ersten Laden des Emulators

Der Emulator ist zustandsbehaftet. Es speichert alle darauf geschriebenen Daten im Speicher, sodass alle Daten verloren gehen, wenn der Emulator heruntergefahren wird. Wenn Sie mehrere Tests mit derselben Projekt-ID ausführen, kann jeder Test Daten erzeugen, die sich auf nachfolgende Tests auswirken können. Sie können dieses Verhalten mit einer der folgenden Methoden umgehen:

  • Verwenden Sie für jeden Test eindeutige Projekt-IDs. Beachten Sie, dass Sie in diesem Fall initializeTestEnvironment als Teil jedes Tests aufrufen müssen. Regeln werden nur für die Standardprojekt-ID automatisch geladen.
  • Strukturieren Sie Ihre Tests neu, damit 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.

Der Testaufbau ist sehr kompliziert

Beim Einrichten Ihres Tests möchten Sie möglicherweise Daten auf eine Weise ändern, die Ihre Cloud Firestore-Sicherheitsregeln eigentlich nicht zulassen. Wenn Ihre Regeln die Testeinrichtung komplex machen, versuchen Sie, RulesTestEnvironment.withSecurityRulesDisabled in Ihren Einrichtungsschritten zu verwenden, damit Lese- und Schreibvorgänge keine PERMISSION_DENIED -Fehler auslösen.

Danach kann Ihr Test Vorgänge als authentifizierter oder nicht authentifizierter Benutzer mithilfe von RulesTestEnvironment.authenticatedContext bzw. unauthenticatedContext ausführen. Dadurch können Sie überprüfen, ob Ihre Cloud Firestore-Sicherheitsregeln verschiedene Fälle korrekt zulassen/ablehnen.