Testa le regole di sicurezza di Cloud Firestore

Durante la creazione dell'app, potresti voler bloccare l'accesso al database Cloud Firestore. Tuttavia, prima del lancio, è necessario un approccio più approfonditoCloud Firestore Security Rules. Con l'emulatore Cloud Firestore, oltre a eseguire la prototipazione e testare le funzionalità e il comportamento generali dell'app, puoi scrivere test delle unità che controllano il comportamento di Cloud Firestore Security Rules.

Guida rapida

Per alcuni casi di test di base con regole semplici, prova l'esempio di avvio rapido.

Informazioni su Cloud Firestore Security Rules

Implementa Firebase Authentication e Cloud Firestore Security Rules per l'autenticazione, l'autorizzazione e la convalida dei dati serverless quando utilizzi le librerie client web e per dispositivi mobili.

Cloud Firestore Security Rules sono costituiti da due parti:

  1. Un'istruzione match che identifica i documenti nel database.
  2. Un'espressione allow che controlla l'accesso a questi documenti.

Firebase Authentication verifica le credenziali degli utenti e fornisce le basi per i sistemi di accesso basati sugli utenti e sui ruoli.

Ogni richiesta al database da una Cloud Firestorelibreria client mobile/web viene valutata in base alle tue regole di sicurezza prima di leggere o scrivere dati. Se le regole negano l'accesso a uno dei percorsi dei documenti specificati, l'intera richiesta non va a buon fine.

Scopri di più su Cloud Firestore Security Rules in Iniziare a utilizzare Cloud Firestore Security Rules.

Installa l'emulatore

Per installare l'emulatore Cloud Firestore, utilizza l'interfaccia a riga di comando Firebase esegui il comando riportato di seguito:

firebase setup:emulators:firestore

Esegui l'emulatore

Inizia inizializzando un progetto Firebase nella tua directory di lavoro. Si tratta di un primo passaggio comune quando si utilizza l'interfaccia a riga di comando di Firebase.

firebase init

Avvia l'emulatore utilizzando il seguente comando. L'emulatore verrà eseguito fino a quando non interrompi il processo:

firebase emulators:start --only firestore

In molti casi vuoi avviare l'emulatore, eseguire una suite di test e quindi arrestare l'emulatore dopo l'esecuzione dei test. Puoi farlo facilmente utilizzando il comando emulators:exec:

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

All'avvio, l'emulatore tenterà di funzionare su una porta predefinita (8080). Puoi cambiare la porta dell'emulatore modificando la sezione "emulators" del file firebase.json:

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

Prima di eseguire l'emulatore

Prima di iniziare a utilizzare l'emulatore, tieni presente quanto segue:

  • L'emulatore caricherà inizialmente le regole specificate nel campo firestore.rules del file firebase.json. Aspetta il nome di un file locale contenente il tuo Cloud Firestore Security Rules e applica queste regole a tutti i progetti. Se non fornisci il percorso del file locale o utilizzi il metodo loadFirestoreRules come descritto di seguito, l'emulatore considera tutti i progetti come con regole aperte.
  • Mentre la maggior parte degli SDK Firebase funziona direttamente con gli emulatori, solo la libreria @firebase/rules-unit-testing supporta la simulazione di auth nelle regole di sicurezza, semplificando così i test delle unità. Inoltre, la libreria supporta alcune funzionalità specifiche dell'emulatore, come l'eliminazione di tutti i dati, come elencato di seguito.
  • Gli emulatori accetteranno anche i token di produzione di Firebase Authentication forniti tramite gli SDK client e valuteranno le regole di conseguenza, il che consente di collegare la tua applicazione direttamente agli emulatori nei test di integrazione e manuali.

Esecuzione dei test delle unità locali

Eseguire test locali delle unità con l'SDK JavaScript 9

Firebase distribuisce una libreria di test di unità delle regole di sicurezza sia con l'SDK JavaScript di versione 9 sia con l'SDK di versione 8. Le API della libreria sono molto diverse. Consigliamo la libreria di test v9, che è più snella e richiede meno configurazione per connettersi agli emulatori e quindi evitare in modo sicuro l'uso accidentale delle risorse di produzione. Per la compatibilità con le versioni precedenti, continuiamo a mettere a disposizione la libreria di test v8.

Utilizza il modulo @firebase/rules-unit-testing per interagire con l'emulatore eseguito localmente. Se si verificano timeout o errori ECONNREFUSED, verifica che l'emulatore sia effettivamente in esecuzione.

Ti consigliamo vivamente di utilizzare una versione recente di Node.js in modo da poter utilizzare la notazione async/await. Quasi tutti i comportamenti che potresti voler testare richiedono funzioni asincrone e il modulo di test è progettato per funzionare con il codice basato su promesse.

La libreria di test delle unità delle regole 9 è sempre consapevole degli emulatori e non tocca mai le risorse di produzione.

Importa la libreria utilizzando le istruzioni di importazione modulari della versione 9. Ad esempio:

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.

Una volta importati, l'implementazione dei test di unità prevede:

  • Creazione e configurazione di un RulesTestEnvironment con una chiamata a initializeTestEnvironment.
  • Configurazione dei dati di test senza attivare le regole, utilizzando un metodo di comodità che consente di ignorarle temporaneamenteRulesTestEnvironment.withSecurityRulesDisabled.
  • Configurazione di suite di test e hook prima/dopo per test con chiamate per pulizia dei dati e dell'ambiente di test, ad esempio RulesTestEnvironment.cleanup() o RulesTestEnvironment.clearFirestore().
  • Implementazione di casi di test che simulano gli stati di autenticazione utilizzando RulesTestEnvironment.authenticatedContext e RulesTestEnvironment.unauthenticatedContext.

Metodi comuni e funzioni di utilità

Consulta anche i metodi di test specifici dell'emulatore nell'SDK v9.

initializeTestEnvironment() => RulesTestEnvironment

Questa funzione inizializza un ambiente di test per i test delle unità delle regole. Chiama prima questa funzione per la configurazione del test. Per un'esecuzione corretta è necessario che gli emulatori siano in esecuzione.

La funzione accetta un oggetto facoltativo che definisce un valore TestEnvironmentConfig, che può essere composto da un ID progetto e dalle impostazioni di configurazione dell'emulatore.

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

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

Questo metodo crea un RulesTestContext, che si comporta come un utente autenticato di Authentication. Alle richieste create tramite il contesto restituito verrà allegato un token di autenticazione simulato. Se vuoi, puoi passare un oggetto che definisce rivendicazioni personalizzate o override per i payload dei token di autenticazione.

Utilizza l'oggetto contesto test restituito nei test per accedere a eventuali istanze di emulatore configurate, incluse quelle configurate con 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

Questo metodo crea un RulesTestContext, che si comporta come un client che non ha eseguito l'accesso tramite autenticazione. Le richieste create tramite il contesto restituito non avranno token Firebase Auth allegati.

Utilizza l'oggetto contesto test restituito nei test per accedere a eventuali istanze di emulatore configurate, incluse quelle configurate con 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()

Esegui una funzione di configurazione del test con un contesto che si comporta come se le Regole di sicurezza fossero disabilitate.

Questo metodo accetta una funzione di callback, che prende il contesto di Security-Rules-bypassing e restituisce una promessa. Il contesto verrà distrutto una volta che la promessa viene risolta/rifiutata.

RulesTestEnvironment.cleanup()

Questo metodo distrugge tutti i RulesTestContexts creati nell'ambiente di test e pulizia delle risorse sottostanti, consentendo un'uscita pulita.

Questo metodo non modifica in alcun modo lo stato degli emulatori. Per reimpostare i dati tra un test e l'altro, utilizza il metodo di cancellazione dei dati specifico dell'emulatore dell'applicazione.

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

Questa è una funzione di utilità per i casi di test.

La funzione afferma che la Promise fornita che racchiude un'operazione dell'emulatore verrà risolta senza violazioni delle regole di sicurezza.

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

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

Questa è una funzione di utilità per i casi di test.

La funzione afferma che la Promise fornita che racchiude un'operazione dell'emulatore verrà rifiutata con una violazione delle regole di sicurezza.

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

Metodi specifici dell'emulatore

Consulta anche i metodi di test e le funzioni di utilità comuni nell'SDK versione 9.

RulesTestEnvironment.clearFirestore() => Promise<void>

Questo metodo cancella i dati nel database Firestore che appartiene all'projectId configurato per l'emulatore Firestore.

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

Questo metodo recupera un'istanza Firestore per questo contesto di test. L'istanza dell'SDK client Firebase JS restituita può essere utilizzata con le API SDK client (v9 modulare o v9 compat).

Visualizzare le valutazioni delle regole

L'emulatore Cloud Firestore ti consente di visualizzare le richieste del client nell'interfaccia utente di Emulator Suite, incluso il monitoraggio della valutazione per le regole di sicurezza di Firebase.

Apri la scheda Firestore > Richieste per visualizzare la sequenza di valutazione dettagliata per ogni richiesta.

Monitoraggio delle richieste dell&#39;emulatore Firestore che mostra le valutazioni delle regole di sicurezza

Genera report sui test

Dopo aver eseguito una suite di test, puoi accedere ai report di copertura dei test che mostrano il modo in cui è stata valutata ciascuna regola di sicurezza.

Per ottenere i report, esegui una query su un endpoint esposto sull'emulatore mentre è in esecuzione. Per una versione adatta ai browser, utilizza il seguente URL:

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

In questo modo, le regole vengono suddivise in espressioni e sottoespressioni su cui puoi eseguire il passaggio del mouse per visualizzare ulteriori informazioni, tra cui il numero di valutazioni e i valori restituiti. Per la versione JSON non elaborata di questi dati, includi il seguente URL nella query:

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

Differenze tra l'emulatore e la produzione

  1. Non è necessario creare esplicitamente un progetto Cloud Firestore. L'emulatore crea automaticamente qualsiasi istanza a cui viene eseguito l'accesso.
  2. L'emulatore Cloud Firestore non funziona con il normale flusso Firebase Authentication. Nell'SDK Firebase Test, invece, abbiamo fornito il metodo initializeTestApp() nella libreria rules-unit-testing, che accetta un campo auth. L'handle Firebase creato con questo metodo si comporterà come se fosse stato autenticato correttamente come qualsiasi entità da te fornita. Se superi l'istruzione null, questo si comporterà come un utente non autenticato (ad esempio, le regole di auth != null non andranno a buon fine).

Risolvere i problemi noti

Mentre utilizzi l'emulatore Cloud Firestore, potresti riscontrare i seguenti problemi noti. Segui le indicazioni riportate di seguito per risolvere eventuali comportamenti irregolari che stai riscontrando. Queste note sono scritte tenendo presente la libreria di test di unità delle Regole di sicurezza, ma gli approcci generali sono applicabili a qualsiasi SDK Firebase.

Il comportamento del test non è coerente

Se i test a volte passano e a volte falliscono, anche senza modifiche ai test stessi, potrebbe essere necessario verificare che siano sequenziati correttamente. La maggior parte delle interazioni con l'emulatore è asincrona, quindi verifica che tutto il codice asincrono sia sequenziato correttamente. Puoi correggere la sequenzialità concatenando le promesse o utilizzando liberamente la notazione await.

In particolare, esamina le seguenti operazioni asincrone:

  • Impostazione delle regole di sicurezza, ad esempio con initializeTestEnvironment.
  • Lettura e scrittura di dati, ad esempio con db.collection("users").doc("alice").get().
  • Affermazioni operative, tra cui assertSucceeds e assertFails.

I test passano solo la prima volta che carichi l'emulatore

L'emulatore è stateful. Memorizza tutti i dati scritti in memoria, pertanto tutti i dati andranno persi ogni volta che l'emulatore si arresta. Se stai eseguendo più test sullo stesso ID progetto, ogni test può produrre dati che potrebbero influenzare i test successivi. Per bypassare questo comportamento, puoi utilizzare uno dei seguenti metodi:

  • Utilizza ID progetto univoci per ogni test. Tieni presente che se scegli questa opzione, dovrai chiamare initializeTestEnvironment come parte di ogni test; le regole vengono caricate automaticamente solo per l'ID progetto predefinito.
  • Ristruttura i test in modo che non interagiscano con i dati scritti in precedenza (ad esempio, utilizza una raccolta diversa per ogni test).
  • Elimina tutti i dati scritti durante un test.

La configurazione del test è molto complicata

Quando configuri il test, potresti voler modificare i dati in un modo che Cloud Firestore Security Rules non consente. Se le regole rendono complessa la configurazione di test, prova a utilizzare RulesTestEnvironment.withSecurityRulesDisabled nei passaggi di configurazione, in modo che le letture e le scritture non generino errori di tipo PERMISSION_DENIED.

Dopodiché, il test può eseguire operazioni come utente autenticato o non autenticato utilizzando rispettivamente RulesTestEnvironment.authenticatedContext e unauthenticatedContext. In questo modo puoi verificare che Cloud Firestore Security Rules consenta/neghi correttamente diverse richieste.