Testa le regole di sicurezza di Cloud Firestore

Durante la creazione dell'app, ti consigliamo di 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 creare prototipi e a testare le funzionalità e il comportamento generali della tua app, puoi scrivere test di unità che controllano il comportamento del tuo 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 include due elementi:

  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 di 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, è consigliabile avviare l'emulatore, eseguire una suite di test e poi spegnerlo al termine dei test. Puoi farlo facilmente utilizzando il comando emulators:exec:

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

Una volta avviato, 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.
  • Anche se la maggior parte degli SDK Firebase funziona direttamente con gli emulatori, solo la libreria @firebase/rules-unit-testing supporta il auth simulato nelle regole di sicurezza, semplificando notevolmente i test di 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.

Esegui test locali delle unità

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 per 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 dell'ambiente e dei dati 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 e funzioni di utilità comuni

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 innanzitutto 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 TestEnvironmentConfig, che può essere costituito da un ID progetto e da 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 cliente che non ha eseguito l'accesso tramite l'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 accetta il contesto di aggiramento delle regole di sicurezza e restituisce una promessa. Il contesto verrà distrutto una volta che la promessa viene risolta / rifiutata.

RulesTestEnvironment.cleanup()

Questo metodo elimina 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 per l'emulatore

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

RulesTestEnvironment.clearFirestore() => Promise<void>

Questo metodo cancella i dati nel database Firestore appartenenti al 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 compatibile).

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.

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

Generare report di test

Dopo aver eseguito una suite di test, puoi accedere ai report sulla copertura dei test che mostrano in che modo è stata valutata ciascuna delle tue regole 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 rules-unit-testing libreria, che accetta un campo auth. L'handle Firebase creato con questo metodo si comporterà come se avesse eseguito l'autenticazione come qualsiasi entità fornita. Se passi null, il valore si comporterà come un utente non autenticato (ad esempio, le regole auth != null non andranno a buon fine).

Risolvere i problemi noti

Durante l'utilizzo dell'emulatore Cloud Firestore, potresti riscontrare i seguenti problemi noti. Segui le indicazioni riportate di seguito per risolvere eventuali comportamenti irregolari riscontrati. 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 superano e a volte non superano, 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 esegui più test sullo stesso ID progetto, ogni test può produrre dati che potrebbero influire sui test successivi. Per aggirare questo comportamento, puoi utilizzare uno dei seguenti metodi:

  • Utilizza ID progetto univoci per ogni test. Tieni presente che, se scegli di farlo, dovrai chiamare initializeTestEnvironment nell'ambito 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 consenta. Se le regole rendono complessa la configurazione del test, prova a utilizzare RulesTestEnvironment.withSecurityRulesDisabled nei passaggi di configurazione in modo che le letture e le scritture non attivino errori 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.