Przetestuj swoje reguły bezpieczeństwa Cloud Firestore

Podczas tworzenia aplikacji możesz zablokować dostęp do bazy danych Cloud Firestore. Jednak przed uruchomieniem będziesz potrzebować bardziej dopracowanych reguł bezpieczeństwa Cloud Firestore. Dzięki emulatorowi Cloud Firestore, oprócz prototypowania i testowania ogólnych funkcji i zachowania aplikacji, możesz pisać testy jednostkowe, które sprawdzają zachowanie reguł bezpieczeństwa Cloud Firestore.

Szybki start

Aby zapoznać się z kilkoma podstawowymi przypadkami testowymi z prostymi regułami, wypróbuj przykład szybkiego startu .

Zapoznaj się z zasadami bezpieczeństwa Cloud Firestore

Zaimplementuj reguły uwierzytelniania Firebase i reguły bezpieczeństwa Cloud Firestore w celu bezserwerowego uwierzytelniania, autoryzacji i sprawdzania poprawności danych podczas korzystania z bibliotek klientów mobilnych i internetowych.

Reguły bezpieczeństwa Cloud Firestore obejmują dwa elementy:

  1. Instrukcja match identyfikująca dokumenty w bazie danych.
  2. Wyrażenie allow kontrolujące dostęp do tych dokumentów.

Uwierzytelnianie Firebase weryfikuje poświadczenia użytkowników i stanowi podstawę systemów dostępu opartych na użytkownikach i rolach.

Każde żądanie bazy danych z biblioteki klienta mobilnego/internetowego Cloud Firestore jest oceniane pod kątem reguł bezpieczeństwa przed odczytaniem lub zapisaniem jakichkolwiek danych. Jeśli reguły odmówią dostępu do którejkolwiek z określonych ścieżek dokumentów, całe żądanie zakończy się niepowodzeniem.

Dowiedz się więcej o regułach bezpieczeństwa Cloud Firestore w artykule Pierwsze kroki z regułami bezpieczeństwa Cloud Firestore .

Zainstaluj emulator

Aby zainstalować emulator Cloud Firestore, użyj interfejsu CLI Firebase i uruchom poniższe polecenie:

firebase setup:emulators:firestore

Uruchom emulator

Rozpocznij od zainicjowania projektu Firebase w swoim katalogu roboczym. Jest to częsty pierwszy krok podczas korzystania z interfejsu wiersza polecenia Firebase .

firebase init

Uruchom emulator za pomocą następującego polecenia. Emulator będzie działał, dopóki nie zabijesz procesu:

firebase emulators:start --only firestore

W wielu przypadkach chcesz uruchomić emulator, uruchomić zestaw testów, a następnie zamknąć emulator po przeprowadzeniu testów. Możesz to łatwo zrobić za pomocą polecenia emulators:exec :

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

Po uruchomieniu emulator podejmie próbę uruchomienia na porcie domyślnym (8080). Możesz zmienić port emulatora, modyfikując sekcję "emulators" w pliku firebase.json :

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

Zanim uruchomisz emulator

Zanim zaczniesz korzystać z emulatora, pamiętaj o następujących kwestiach:

  • Emulator początkowo załaduje reguły określone w polu firestore.rules pliku firebase.json . Oczekuje nazwy pliku lokalnego zawierającego reguły bezpieczeństwa Cloud Firestore i stosuje te reguły do ​​wszystkich projektów. Jeśli nie podasz ścieżki pliku lokalnego lub użyjesz metody loadFirestoreRules zgodnie z poniższym opisem, emulator traktuje wszystkie projekty jako posiadające otwarte reguły.
  • Chociaż większość zestawów SDK Firebase współpracuje bezpośrednio z emulatorami, tylko biblioteka @firebase/rules-unit-testing obsługuje szydercze auth w regułach bezpieczeństwa, co znacznie ułatwia testy jednostkowe. Ponadto biblioteka obsługuje kilka funkcji specyficznych dla emulatora, takich jak czyszczenie wszystkich danych, jak wymieniono poniżej.
  • Emulatory akceptują również produkcyjne tokeny Firebase Auth dostarczane przez zestawy SDK klienta i odpowiednio oceniają reguły, co umożliwia bezpośrednie połączenie aplikacji z emulatorami w testach integracyjnych i ręcznych.

Uruchom lokalne testy jednostkowe

Uruchom testy jednostek lokalnych za pomocą zestawu SDK języka JavaScript w wersji 9

Firebase dystrybuuje bibliotekę testów jednostkowych reguł bezpieczeństwa wraz z pakietem SDK JavaScript w wersji 9 i pakietem SDK w wersji 8. Interfejsy API bibliotek znacznie się od siebie różnią. Polecamy bibliotekę testową v9, która jest bardziej usprawniona i wymaga mniej konfiguracji, aby połączyć się z emulatorami, a tym samym bezpiecznie uniknąć przypadkowego użycia zasobów produkcyjnych. Aby zapewnić kompatybilność wsteczną, nadal udostępniamy bibliotekę testową v8 .

Użyj modułu @firebase/rules-unit-testing aby wejść w interakcję z emulatorem działającym lokalnie. Jeśli wystąpią przekroczenia limitu czasu lub błędy ECONNREFUSED , sprawdź dokładnie, czy emulator rzeczywiście działa.

Zdecydowanie zalecamy korzystanie z najnowszej wersji Node.js, aby móc używać notacji async/await . Prawie wszystkie zachowania, które warto przetestować, obejmują funkcje asynchroniczne, a moduł testowy został zaprojektowany do pracy z kodem opartym na obietnicach.

Biblioteka do testowania jednostek reguł w wersji 9 zawsze wie o emulatorach i nigdy nie wpływa na zasoby produkcyjne.

Bibliotekę importujesz za pomocą instrukcji importu modułowego w wersji 9. Na przykład:

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.

Po zaimportowaniu wdrożenie testów jednostkowych obejmuje:

  • Tworzenie i konfigurowanie RulesTestEnvironment z wywołaniem initializeTestEnvironment .
  • Konfigurowanie danych testowych bez wyzwalania reguł przy użyciu wygodnej metody umożliwiającej tymczasowe ich ominięcie, RulesTestEnvironment.withSecurityRulesDisabled .
  • Konfigurowanie zestawu testów i zaczepów przed/po każdym teście z wywołaniami do czyszczenia danych testowych i środowiska, np RulesTestEnvironment.cleanup() lub RulesTestEnvironment.clearFirestore() .
  • Implementowanie przypadków testowych naśladujących stany uwierzytelniania przy użyciu RulesTestEnvironment.authenticatedContext i RulesTestEnvironment.unauthenticatedContext .

Typowe metody i funkcje użytkowe

Zobacz także metody testowania specyficzne dla emulatora w zestawie SDK v9 .

initializeTestEnvironment() => RulesTestEnvironment

Ta funkcja inicjuje środowisko testowe do testowania jednostkowego reguł. Najpierw wywołaj tę funkcję w celu konfiguracji testu. Pomyślne wykonanie wymaga uruchomienia emulatorów.

Funkcja akceptuje opcjonalny obiekt definiujący TestEnvironmentConfig , który może składać się z identyfikatora projektu i ustawień konfiguracyjnych emulatora.

let testEnv = await initializeTestEnvironment({
  projectId: "demo-project-1234",
  firestore: {
    rules: fs.readFileSync("firestore.rules", "utf8"),
  },
});

RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext

Ta metoda tworzy RulesTestContext , który zachowuje się jak uwierzytelniony użytkownik uwierzytelniający. Żądania utworzone za pośrednictwem zwróconego kontekstu będą miały dołączony próbny token uwierzytelniający. Opcjonalnie przekaż obiekt definiujący niestandardowe oświadczenia lub zastąpienia ładunków tokenu uwierzytelniania.

Użyj zwróconego obiektu kontekstu testowego w swoich testach, aby uzyskać dostęp do dowolnych skonfigurowanych instancji emulatora, w tym tych skonfigurowanych z initializeTestEnvironment .

// 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

Ta metoda tworzy obiekt RulesTestContext , który zachowuje się jak klient niezalogowany za pośrednictwem uwierzytelniania. Żądania utworzone za pośrednictwem zwróconego kontekstu nie będą miały dołączonych tokenów uwierzytelniania Firebase.

Użyj zwróconego obiektu kontekstu testowego w swoich testach, aby uzyskać dostęp do dowolnych skonfigurowanych instancji emulatora, w tym tych skonfigurowanych z initializeTestEnvironment .

// 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()

Uruchom funkcję konfiguracji testu z kontekstem, który zachowuje się tak, jakby reguły zabezpieczeń były wyłączone.

Ta metoda przyjmuje funkcję wywołania zwrotnego, która pobiera kontekst omijania reguł zabezpieczeń i zwraca obietnicę. Kontekst zostanie zniszczony, gdy obietnica zostanie rozpatrzona/odrzucona.

RulesTestEnvironment.cleanup()

Ta metoda niszczy wszystkie RulesTestContexts utworzone w środowisku testowym i czyści podstawowe zasoby, umożliwiając czyste wyjście.

Ta metoda w żaden sposób nie zmienia stanu emulatorów. Aby zresetować dane pomiędzy testami, użyj metody czyszczenia danych specyficznej dla emulatora aplikacji.

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

Jest to funkcja użyteczności przypadku testowego.

Funkcja zapewnia, że ​​dostarczona obietnica otaczająca operację emulatora zostanie rozwiązana bez naruszenia reguł bezpieczeństwa.

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

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

Jest to funkcja użyteczności przypadku testowego.

Funkcja zapewnia, że ​​dostarczona obietnica otaczająca operację emulatora zostanie odrzucona z powodu naruszenia reguł bezpieczeństwa.

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

Metody specyficzne dla emulatora

Zobacz także typowe metody testowania i funkcje narzędziowe w zestawie SDK v9 .

RulesTestEnvironment.clearFirestore() => Promise<void>

Ta metoda czyści dane w bazie danych Firestore należące do projectId skonfigurowanego dla emulatora Firestore.

RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;

Ta metoda pobiera instancję Firestore dla tego kontekstu testowego. Zwróconej instancji pakietu SDK klienta Firebase JS można używać z interfejsami API pakietu SDK klienta (wersja modułowa lub kompatybilna z wersją 9).

Wizualizuj oceny reguł

Emulator Cloud Firestore umożliwia wizualizację żądań klientów w interfejsie użytkownika pakietu Emulator Suite, w tym śledzenie oceny dla reguł bezpieczeństwa Firebase.

Otwórz kartę Firestore > Żądania , aby wyświetlić szczegółową sekwencję oceny każdego żądania.

Monitor żądań emulatora Firestore pokazujący oceny reguł bezpieczeństwa

Generuj raporty z testów

Po przeprowadzeniu zestawu testów możesz uzyskać dostęp do raportów zasięgu testów, które pokazują, jak została oceniona każda z Twoich reguł bezpieczeństwa.

Aby uzyskać raporty, wykonaj zapytanie do odsłoniętego punktu końcowego w emulatorze, gdy jest on uruchomiony. Aby uzyskać wersję przyjazną dla przeglądarki, użyj następującego adresu URL:

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

Spowoduje to rozbicie reguł na wyrażenia i podwyrażenia, na które można najechać myszką, aby uzyskać więcej informacji, w tym liczbę ocen i zwróconych wartości. Aby uzyskać surową wersję tych danych w formacie JSON, w zapytaniu umieść następujący adres URL:

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

Różnice między emulatorem a produkcją

  1. Nie musisz jawnie tworzyć projektu Cloud Firestore. Emulator automatycznie tworzy każdą instancję, do której uzyskuje się dostęp.
  2. Emulator Cloud Firestore nie działa z normalnym przepływem uwierzytelniania Firebase. Zamiast tego w zestawie SDK Firebase Test udostępniliśmy metodę initializeTestApp() w bibliotece rules-unit-testing , która przyjmuje pole auth . Uchwyt Firebase utworzony przy użyciu tej metody będzie zachowywał się tak, jakby pomyślnie uwierzytelnił się jako dowolny podany obiekt. Jeśli przekażesz null , będzie on zachowywał się jak nieuwierzytelniony użytkownik (na przykład reguły auth != null nie powiodą się).

Rozwiązywanie znanych problemów

Podczas korzystania z emulatora Cloud Firestore możesz napotkać następujące znane problemy. Postępuj zgodnie z poniższymi wskazówkami, aby rozwiązać wszelkie nietypowe zachowania, których doświadczasz. Te uwagi zostały napisane z myślą o bibliotece testów jednostkowych reguł bezpieczeństwa, ale ogólne podejście ma zastosowanie do dowolnego zestawu SDK Firebase.

Zachowanie testowe jest niespójne

Jeśli Twoje testy czasami kończą się pomyślnie lub niepowodzeniem, nawet bez żadnych zmian w samych testach, może być konieczne sprawdzenie, czy ich kolejność jest prawidłowa. Większość interakcji z emulatorem ma charakter asynchroniczny, dlatego sprawdź dokładnie, czy cały kod asynchroniczny ma prawidłową sekwencję. Możesz naprawić sekwencję, łącząc obietnice lub swobodnie używając notacji await .

W szczególności przejrzyj następujące operacje asynchroniczne:

  • Ustawianie reguł bezpieczeństwa, na przykład za initializeTestEnvironment .
  • Odczyt i zapis danych, na przykład za db.collection("users").doc("alice").get() .
  • Asercje operacyjne, w assertSucceeds assertFails .

Testy przechodzą pomyślnie tylko przy pierwszym załadowaniu emulatora

Emulator jest stanowy. Przechowuje wszystkie zapisane w pamięci dane, więc wszelkie dane są tracone po wyłączeniu emulatora. Jeśli uruchamiasz wiele testów dla tego samego identyfikatora projektu, każdy test może wygenerować dane, które mogą mieć wpływ na kolejne testy. Aby ominąć to zachowanie, możesz użyć dowolnej z następujących metod:

  • Dla każdego testu używaj unikalnych identyfikatorów projektów. Pamiętaj, że jeśli zdecydujesz się to zrobić, będziesz musiał wywołać initializeTestEnvironment w ramach każdego testu; reguły są ładowane automatycznie tylko dla domyślnego identyfikatora projektu.
  • Zrestrukturyzuj testy, aby nie wchodziły w interakcję z wcześniej zapisanymi danymi (na przykład użyj innej kolekcji dla każdego testu).
  • Usuń wszystkie dane zapisane podczas testu.

Konfiguracja testu jest bardzo skomplikowana

Konfigurując test, możesz chcieć zmodyfikować dane w sposób, na który nie pozwalają reguły bezpieczeństwa Cloud Firestore. Jeśli reguły komplikują konfigurację testu, spróbuj użyć pliku RulesTestEnvironment.withSecurityRulesDisabled na etapach konfiguracji, aby odczyty i zapisy nie powodowały błędów PERMISSION_DENIED .

Następnie test może wykonywać operacje jako użytkownik uwierzytelniony lub nieuwierzytelniony, używając odpowiednio RulesTestEnvironment.authenticatedContext i unauthenticatedContext . Pozwala to sprawdzić, czy reguły bezpieczeństwa Cloud Firestore poprawnie dopuszczają/odrzucają różne przypadki.