Check out what’s new from Firebase at Google I/O 2022. Learn more

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 zniuansowanych reguł bezpieczeństwa Cloud Firestore. Za pomocą emulatora Cloud Firestore oprócz tworzenia prototypów i testowania ogólnych funkcji i zachowania aplikacji możesz pisać testy jednostkowe, które sprawdzają zachowanie reguł zabezpieczeń Cloud Firestore.

Szybki start

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

Zrozumienie reguł bezpieczeństwa Cloud Firestore

Zaimplementuj uwierzytelnianie Firebase i reguły zabezpieczeń Cloud Firestore w celu bezserwerowego uwierzytelniania, autoryzacji i walidacji danych podczas korzystania z bibliotek klienta mobilnego i internetowego.

Zasady bezpieczeństwa Cloud Firestore obejmują dwie części:

  1. Oświadczenie match , które identyfikuje dokumenty w Twojej bazie danych.
  2. Wyrażenie allow , które kontroluje 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 odmawiają dostępu do dowolnej z określonych ścieżek dokumentów, całe żądanie koń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 Firebase CLI i uruchom poniższe polecenie:

firebase setup:emulators:firestore

Uruchom emulator

Zacznij od zainicjowania projektu Firebase w katalogu roboczym. Jest to typowy 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 uruchomieniu testów. Możesz to łatwo zrobić za pomocą polecenia emulators:exec :

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

Po uruchomieniu emulator spróbuje uruchomić się na domyślnym porcie (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 do pliku lokalnego lub użyjesz metody loadFirestoreRules , jak opisano poniżej, emulator potraktuje wszystkie projekty jako mające otwarte reguły.
  • Podczas gdy większość pakietów SDK Firebase działa bezpośrednio z emulatorami, tylko biblioteka @firebase/rules-unit-testing obsługuje 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, które wymieniono poniżej.
  • Emulatory będą również akceptować produkcyjne tokeny uwierzytelniania Firebase dostarczane za pośrednictwem pakietów Client SDK i odpowiednio oceniać reguły, co pozwala na połączenie Twojej aplikacji bezpośrednio z emulatorami w ramach testów integracyjnych i ręcznych.

Uruchom lokalne testy jednostkowe

Uruchom lokalne testy jednostkowe za pomocą v9 JavaScript SDK

Firebase dystrybuuje bibliotekę do testowania jednostek reguł bezpieczeństwa z pakietem SDK JavaScript w wersji 9 i pakietem SDK w wersji 8. Interfejsy API bibliotek znacznie się różnią. Zalecamy bibliotekę testową v9, która jest bardziej uproszczona i wymaga mniej konfiguracji, aby połączyć się z emulatorami, a tym samym bezpiecznie uniknąć przypadkowego użycia zasobów produkcyjnych. W celu zapewnienia kompatybilności wstecznej nadal udostępniamy bibliotekę testową v8 .

Użyj modułu @firebase/rules-unit-testing do interakcji z emulatorem działającym lokalnie. Jeśli wystąpią przekroczenia limitu czasu lub błędy ECONNREFUSED , sprawdź dwukrotnie, czy emulator faktycznie działa.

Zdecydowanie zalecamy korzystanie z najnowszej wersji Node.js, aby można było używać notacji async/await . Prawie wszystkie zachowania, które możesz chcieć przetestować, obejmują funkcje asynchroniczne, a moduł testowania został zaprojektowany do pracy z kodem opartym na obietnicy.

Biblioteka testów jednostkowych reguł v9 jest zawsze świadoma emulatorów i nigdy nie dotyka zasobów produkcyjnych.

Bibliotekę importujesz za pomocą modułowych instrukcji importu v9. Na przykład:

import {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment,
  RulesTestEnvironment,
} 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.

Implementacja testów jednostkowych po zaimportowaniu obejmuje:

  • Tworzenie i konfigurowanie RulesTestEnvironment z wywołaniem initializeTestEnvironment .
  • Konfigurowanie danych testowych bez wyzwalania Reguł przy użyciu wygodnej metody, która umożliwia tymczasowe ich pominięcie, RulesTestEnvironment.withSecurityRulesDisabled .
  • Konfigurowanie zestawu testów i dla każdego testu przed/po zaczepach z wywołaniami w celu oczyszczenia danych testowych i środowiska, takich jak RulesTestEnvironment.cleanup() lub RulesTestEnvironment.clearFirestore() .
  • Implementowanie przypadków testowych, które naśladują stany uwierzytelniania przy użyciu RulesTestEnvironment.authenticatedContext i RulesTestEnvironment.unauthenticatedContext .

Wspólne metody i funkcje użytkowe

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

initializeTestEnvironment() => RulesTestEnvironment

Ta funkcja inicjuje środowisko testowe do testowania jednostkowego reguł. Wywołaj tę funkcję najpierw, aby przeprowadzić konfigurację testową. 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 uwierzytelniania. Żądania utworzone za pomocą zwróconego kontekstu będą miały dołączony fałszywy token uwierzytelniania. Opcjonalnie przekaż obiekt definiujący oświadczenia niestandardowe lub zastąpienia dla ładunków tokenu uwierzytelniania.

Użyj zwróconego obiektu kontekstu testowego w testach, aby uzyskać dostęp do wszystkich skonfigurowanych wystąpień emulatora, w tym skonfigurowanych za pomocą 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 RulesTestContext , który zachowuje się jak klient, który nie jest zalogowany za pośrednictwem uwierzytelniania. Żądania utworzone za pomocą zwróconego kontekstu nie będą miały dołączonych tokenów uwierzytelniania Firebase.

Użyj zwróconego obiektu kontekstu testowego w testach, aby uzyskać dostęp do wszystkich skonfigurowanych wystąpień emulatora, w tym skonfigurowanych za pomocą 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ę zwrotną, która przyjmuje kontekst omijania reguł zabezpieczeń i zwraca obietnicę. Kontekst zostanie zniszczony, gdy obietnica zostanie rozwiązana/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 między testami, użyj metody czyszczenia danych specyficznej dla emulatora aplikacji.

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

Jest to funkcja narzędziowa przypadków testowych.

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

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

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

Jest to funkcja narzędziowa przypadków testowych.

Funkcja zapewnia, że ​​dostarczona Promise zawijająca operację emulatora zostanie odrzucona z naruszeniem reguł zabezpieczeń.

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

Metody specyficzne dla emulatora

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

RulesTestEnvironment.clearFirestore() => Promise<void>

Ta metoda usuwa dane w bazie danych Firestore, która należy do identyfikatora projectId skonfigurowanego dla emulatora Firestore.

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

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

Wizualizuj oceny reguł

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

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

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

Generuj raporty z testów

Po uruchomieniu zestawu testów możesz uzyskać dostęp do raportów dotyczących pokrycia testami, które pokazują, w jaki sposób oceniono każdą 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ć kursorem myszy, aby uzyskać więcej informacji, w tym liczbę zwróconych ocen i wartości. W przypadku nieprzetworzonej wersji tych danych w formacie JSON w zapytaniu uwzględnij 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żde wystąpienie, do którego uzyskuje się dostęp.
  2. Emulator Cloud Firestore nie działa z normalnym przepływem Uwierzytelniania Firebase. Zamiast tego w pakiecie Firebase Test SDK udostępniliśmy metodę initializeTestApp() w bibliotece rules-unit-testing , która przyjmuje pole auth . Dojście Firebase utworzone przy użyciu tej metody będzie zachowywać się tak, jakby zostało pomyślnie uwierzytelnione jako dowolna podana przez Ciebie jednostka. Jeśli przekażesz null , będzie on zachowywał się jak użytkownik nieuwierzytelniony (na przykład auth != null zakończą się niepowodzeniem).

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ć problemy z nieprawidłowym zachowaniem. Te uwagi zostały napisane z myślą o bibliotece testów jednostkowych reguł zabezpieczeń, ale ogólne podejście można zastosować do każdego pakietu Firebase SDK.

Zachowanie testowe jest niespójne

Jeśli twoje testy od czasu do czasu przechodzą i kończą się niepowodzeniem, nawet bez żadnych zmian w samych testach, może być konieczne sprawdzenie, czy są one prawidłowo zsekwencjonowane. Większość interakcji z emulatorem jest asynchroniczna, dlatego należy dokładnie sprawdzić, czy cały kod asynchroniczny jest prawidłowo zsekwencjonowany. Możesz naprawić kolejność, łą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 initializeTestEnvironment .
  • Odczytywanie i zapisywanie danych, na przykład za db.collection("users").doc("alice").get() .
  • Asercje operacyjne, w tym assertSucceeds i assertFails .

Testy przechodzą tylko przy pierwszym załadowaniu emulatora

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

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

Konfiguracja testu jest bardzo skomplikowana

Podczas konfigurowania testu możesz chcieć zmodyfikować dane w sposób, na który nie zezwalają reguły zabezpieczeń Cloud Firestore. Jeśli Twoje reguły utrudniają konfigurację testu, spróbuj użyć RulesTestEnvironment.withSecurityRulesDisabled w krokach konfiguracji, aby odczyty i zapisy nie powodowały błędów PERMISSION_DENIED .

Następnie test może wykonywać operacje jako uwierzytelniony lub nieuwierzytelniony użytkownik przy użyciu odpowiednio RulesTestEnvironment.authenticatedContext i unauthenticatedContext . Umożliwia to sprawdzenie, czy reguły zabezpieczeń Cloud Firestore poprawnie zezwalają/odrzucają różne przypadki.