Testa le regole di sicurezza di Cloud Firestore

Durante la creazione dell'app, potresti voler bloccare l'accesso al tuo Cloud Firestore database. Tuttavia, prima del lancio, avrai bisogno di regole di sicurezza di Cloud Firestore più sfumate Cloud Firestore Security Rules. Con l'emulatore Cloud Firestore, oltre a creare prototipi 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 della guida rapida.

Informazioni su Cloud Firestore Security Rules

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

Cloud Firestore Security Rules includono 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 su utenti e ruoli.

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

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

Installare l'emulatore

Per installare l'emulatore Cloud Firestore, utilizza la CLI Firebase ed esegui il comando riportato di seguito:

firebase setup:emulators:firestore

Eseguire l'emulatore

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

firebase init

Avvia l'emulatore utilizzando il comando seguente. L'emulatore verrà eseguito finché non interrompi il processo:

firebase emulators:start --only firestore

In molti casi, ti consigliamo di avviare l'emulatore, eseguire una suite di test e poi arrestarlo dopo l'esecuzione dei test. Puoi farlo facilmente utilizzando il comando emulators:exec:

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

Quando viene avviato, l'emulatore tenta di essere eseguito su una porta predefinita (8080). Puoi modificare la porta dell'emulatore modificando la sezione "emulators" del tuo firebase.json file:

{
  // ...
  "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. Si aspetta il nome di un file locale contenente le tue Cloud Firestore Security Rules e applica quelle 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 se avessero regole aperte.
  • Sebbene la maggior parte degli SDK Firebase funzioni direttamente con gli emulatori, solo la libreria @firebase/rules-unit-testing supporta la simulazione di auth nelle regole di sicurezza, semplificando notevolmente i test delle unità. Inoltre, la libreria supporta alcune funzionalità specifiche dell'emulatore, come la cancellazione di tutti i dati, come indicato di seguito.
  • Gli emulatori accettano anche i token Firebase Authentication di produzione forniti tramite gli SDK client e valutano le regole di conseguenza, il che consente di collegare l'applicazione direttamente agli emulatori nei test di integrazione e manuali.

Eseguire test delle unità locali

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

Firebase distribuisce una libreria di test delle unità delle regole di sicurezza sia con l'SDK JavaScript versione 9 sia con l'SDK versione 8. Le API della libreria sono significativamente diverse. Ti consigliamo di utilizzare la libreria di test v9, che è più snella e richiede meno configurazione per la connessione agli emulatori, evitando così l'utilizzo accidentale delle risorse di produzione. Per la compatibilità con le versioni precedenti, continuiamo a rendere disponibile la libreria di test v8.

Utilizza il modulo @firebase/rules-unit-testing per interagire con l'emulatore in esecuzione in locale. Se ricevi errori di timeout o ECONNREFUSED, controlla 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 tutto il comportamento che potresti voler testare coinvolge funzioni asincrone e il modulo di test è progettato per funzionare con il codice basato su Promise.

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

Importa la libreria utilizzando le istruzioni di importazione modulare v9. 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, i test delle unità comportano:

  • Creazione e configurazione di un RulesTestEnvironment con una chiamata a initializeTestEnvironment.
  • Configurazione dei dati di test senza attivare le regole, utilizzando un metodo pratico che consente di ignorarle temporaneamente, RulesTestEnvironment.withSecurityRulesDisabled.
  • Configurazione della suite di test e dei hook before/after per test con chiamate per liberare spazio nei dati e nell'ambiente di test, come 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

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

initializeTestEnvironment() => RulesTestEnvironment

Questa funzione inizializza un ambiente di test per il test delle unità delle regole. Chiama questa funzione per prima per la configurazione del test. Per l'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 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 di autenticazione autenticato. Le richieste create tramite il contesto restituito avranno un token di autenticazione simulato allegato. Facoltativamente, passa un oggetto che definisce rivendicazioni personalizzate o override per i payload dei token di autenticazione.

Utilizza l'oggetto del contesto di test restituito nei test per accedere a tutte le istanze dell'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().doc('/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

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

Utilizza l'oggetto del contesto di test restituito nei test per accedere a tutte le istanze dell'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 disattivate.

Questo metodo accetta una funzione di callback, che accetta il contesto di bypass delle regole di sicurezza e restituisce una promessa. Il contesto verrà eliminato una volta che la promessa viene risolta / rifiutata.

RulesTestEnvironment.cleanup()

Questo metodo elimina tutti i RulesTestContexts creati nell'ambiente di test e pulisce le risorse sottostanti, consentendo un'uscita pulita.

Questo metodo non modifica in alcun modo lo stato degli emulatori. Per reimpostare i dati tra i test, 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 assicura che la promessa fornita che racchiude un'operazione dell'emulatore venga 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 assicura che la promessa fornita che racchiude un'operazione dell'emulatore venga rifiutata con una violazione delle regole di sicurezza.

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

Metodi specifici dell'emulatore

Vedi 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 ottiene un'istanza di Firestore per questo contesto di test. L'istanza dell'SDK Firebase JS Client restituita può essere utilizzata con le API dell'SDK client (v9 modulare o v9 compat).

Visualizzare le valutazioni delle regole

L'emulatore Cloud Firestore consente di visualizzare le richieste client nell' interfaccia utente di Emulator Suite, inclusa la traccia di 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

Generare report di test

Dopo aver eseguito una suite di test, puoi accedere ai report sulla copertura dei test che mostrano come è stata valutata ciascuna delle tue regole di sicurezza.

Per ottenere i report, esegui una query su un endpoint esposto sull'emulatore durante l'esecuzione. Per una versione adatta al browser, utilizza il seguente URL:

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

Le regole vengono suddivise in espressioni e sottoespressioni su cui puoi passare il 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 devi creare esplicitamente un progetto Cloud Firestore. L'emulatore crea automaticamente qualsiasi istanza a cui si accede.
  2. L'Cloud Firestore emulator non funziona con il normale flusso Firebase Authentication. Invece, nell'SDK Firebase Test, abbiamo fornito il metodo initializeTestApp() nella libreria rules-unit-testing, che accetta un campo auth. L'handle Firebase creato utilizzando questo metodo si comporterà come se avesse eseguito l'autenticazione correttamente come qualsiasi entità fornita. Se passi null, si comporterà come un utente non autenticato (le regole auth != null non andranno a buon fine, ad esempio).

Risolvere i problemi noti

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

Il comportamento dei test è incoerente

Se i test a volte vanno a buon fine e a volte no, anche senza modifiche ai test stessi, potrebbe essere necessario verificare che siano sequenziati correttamente. La maggior parte delle interazioni con l'emulatore è asincrona, quindi controlla che tutto il codice asincrono sia sequenziato correttamente. Puoi correggere il sequenziamento concatenando le promesse o utilizzando la notazione await in modo esteso.

In particolare, esamina le seguenti operazioni asincrone:

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

I test vanno a buon fine solo la prima volta che carichi l'emulatore

L'emulatore è stateful. Memorizza tutti i dati scritti in memoria, quindi tutti i dati vengono persi quando l'emulatore viene arrestato. Se esegui più test sullo stesso ID progetto, ogni test può produrre dati che potrebbero influenzare i test successivi. Puoi utilizzare uno dei seguenti metodi per ignorare questo comportamento:

  • Utilizza ID progetto univoci per ogni test. Tieni presente che, se scegli di farlo, 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 i tuoi Cloud Firestore Security Rules non consentono. 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 le tue Cloud Firestore Security Rules consentano / neghino correttamente i diversi casi.