Testa le regole di sicurezza di Cloud Firestore

Mentre crei la tua app, potresti voler bloccare l'accesso al tuo database Cloud Firestore. Tuttavia, prima del lancio, avrai bisogno di regole di sicurezza di Cloud Firestore più dettagliate. Con l'emulatore Cloud Firestore, oltre a creare prototipi e testare le funzionalità e il comportamento generali della tua app, puoi scrivere test unitari che controllano il comportamento delle regole di sicurezza Cloud Firestore.

Avvio rapido

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

Comprendere le regole di sicurezza di Cloud Firestore

Implementa l'autenticazione Firebase e le regole di sicurezza Cloud Firestore per l'autenticazione, l'autorizzazione e la convalida dei dati serverless quando utilizzi le librerie client mobile e web.

Le regole di sicurezza di Cloud Firestore includono due parti:

  1. Una dichiarazione match che identifica i documenti nel database.
  2. Un'espressione allow che controlla l'accesso a tali documenti.

Firebase Authentication verifica le credenziali degli utenti e fornisce le basi per sistemi di accesso basati sull'utente e sul ruolo.

Ogni richiesta di database da una libreria client mobile/web di Cloud Firestore viene valutata rispetto alle regole di sicurezza prima di leggere o scrivere qualsiasi dato. Se le regole negano l'accesso a uno qualsiasi dei percorsi dei documenti specificati, l'intera richiesta avrà esito negativo.

Scopri di più sulle regole di sicurezza di Cloud Firestore in Inizia con le regole di sicurezza di Cloud Firestore .

Installa l'emulatore

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

firebase setup:emulators:firestore

Esegui l'emulatore

Inizia inizializzando un progetto Firebase nella tua directory di lavoro. Questo è un primo passaggio comune quando si utilizza la CLI Firebase .

firebase init

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

firebase emulators:start --only firestore

In molti casi si desidera avviare l'emulatore, eseguire una suite di test e quindi spegnere l'emulatore una volta eseguiti i test. Puoi farlo facilmente usando 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 tuo file firebase.json :

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

Prima di eseguire l'emulatore

Prima di iniziare a utilizzare l'emulatore, tenere 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 regole di sicurezza di Cloud Firestore e applica tali regole a tutti i progetti. Se non fornisci il percorso del file locale o non utilizzi il metodo loadFirestoreRules come descritto di seguito, l'emulatore considera tutti i progetti come aventi regole aperte.
  • Sebbene la maggior parte degli SDK Firebase funzionino direttamente con gli emulatori, solo la libreria @firebase/rules-unit-testing supporta auth simulata nelle regole di sicurezza, rendendo i test unitari molto più semplici. Inoltre, la libreria supporta alcune funzionalità specifiche dell'emulatore come la cancellazione di tutti i dati, come elencato di seguito.
  • Gli emulatori accetteranno anche i token di autenticazione Firebase di produzione forniti tramite gli SDK client e valuteranno le regole di conseguenza, il che consente di connettere l'applicazione direttamente agli emulatori nell'integrazione e nei test manuali.

Esegui test delle unità locali

Esegui unit test locali con l'SDK JavaScript v9

Firebase distribuisce una libreria di unit test delle regole di sicurezza sia con l'SDK JavaScript della versione 9 che con l'SDK della versione 8. Le API della libreria sono significativamente diverse. Consigliamo la libreria di test v9, che è più snella e richiede meno configurazioni per connettersi agli emulatori e quindi evitare in modo sicuro l'uso accidentale delle risorse di produzione. Per 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 eseguito localmente. Se ricevi timeout o errori ECONNREFUSED , ricontrolla 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 tutto il comportamento che potresti voler testare coinvolge funzioni asincrone e il modulo di test è progettato per funzionare con codice basato su Promise.

La libreria v9 Rules Unit Testing è sempre a conoscenza degli emulatori e non tocca mai le tue risorse di produzione.

Importi la libreria utilizzando le istruzioni di importazione modulare v9. Per 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 unitari comporta:

  • Creazione e configurazione di RulesTestEnvironment con una chiamata a initializeTestEnvironment .
  • Impostazione dei dati di test senza attivare le regole, utilizzando un metodo pratico che consente di ignorarli temporaneamente, RulesTestEnvironment.withSecurityRulesDisabled .
  • Impostazione della suite di test e hook prima/dopo per-test con chiamate per pulire i dati e l'ambiente di test, come RulesTestEnvironment.cleanup() o RulesTestEnvironment.clearFirestore() .
  • Implementazione di casi di test che imitano 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 lo unit test delle regole. Chiamare prima questa funzione per l'impostazione 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 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 fittizio allegato. Facoltativamente, passare un oggetto che definisce attestazioni personalizzate o sostituzioni per i payload del token di autenticazione.

Utilizza l'oggetto del contesto di test restituito nei test per accedere a qualsiasi istanza dell'emulatore configurata, 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 effettuato l'accesso tramite l'autenticazione. Le richieste create tramite il contesto restituito non avranno token di autenticazione Firebase allegati.

Utilizza l'oggetto del contesto di test restituito nei test per accedere a qualsiasi istanza dell'emulatore configurata, 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 di prova 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 bypass delle regole di sicurezza e restituisce una promessa. Il contesto verrà distrutto una volta che la promessa si risolve/rifiuta.

RulesTestEnvironment.cleanup()

Questo metodo distrugge tutti RulesTestContexts creati nell'ambiente di test e ripulisce le 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, utilizzare il metodo di cancellazione dei dati specifico dell'emulatore dell'applicazione.

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

Questa è una funzione di utilità del caso di test.

La funzione asserisce 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à del caso di test.

La funzione asserisce 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 comuni e le funzioni di utilità nell'SDK v9 .

RulesTestEnvironment.clearFirestore() => Promise<void>

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

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

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

Visualizza le valutazioni delle regole

L'emulatore Cloud Firestore ti consente di visualizzare le richieste dei client nell'interfaccia utente di Emulator Suite, inclusa la traccia della valutazione per le regole di sicurezza Firebase.

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

Monitoraggio delle richieste dell'emulatore Firestore che mostra le valutazioni delle regole di sicurezza

Genera rapporti di prova

Dopo aver eseguito una serie 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 mentre è in esecuzione. Per una versione ottimizzata per il browser, utilizzare il seguente URL:

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

Ciò suddivide le regole in espressioni e sottoespressioni su cui puoi passare il mouse per ulteriori informazioni, incluso il numero di valutazioni e 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 si accede.
  2. L'emulatore Cloud Firestore non funziona con il normale flusso di autenticazione Firebase. Invece, nell'SDK di test Firebase, 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 fosse stato autenticato con successo come qualsiasi entità fornita. Se passi null , si comporterà come un utente non autenticato ( le regole auth != null falliranno, 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 riscontrati. Queste note sono scritte tenendo presente la libreria di unit test delle regole di sicurezza, ma gli approcci generali sono applicabili a qualsiasi SDK Firebase.

Il comportamento del test è incoerente

Se i tuoi test occasionalmente superano e falliscono, anche senza alcuna modifica ai test stessi, potrebbe essere necessario verificare che siano sequenziati correttamente. La maggior parte delle interazioni con l'emulatore sono asincrone, quindi ricontrolla che tutto il codice asincrono sia sequenziato correttamente. È possibile correggere la sequenza concatenando le promesse o utilizzando liberamente la notazione await .

In particolare, esaminare le seguenti operazioni asincrone:

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

I test vengono superati solo la prima volta che si carica l'emulatore

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

  • Utilizza ID progetto univoci per ciascun test. Tieni presente che se scegli di farlo dovrai chiamare initializeTestEnvironment come parte di ciascun test; le regole vengono caricate automaticamente solo per l'ID progetto predefinito.
  • Ristruttura i tuoi 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 le regole di sicurezza di Cloud Firestore non consentono effettivamente. Se le tue 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 .

Successivamente, il test può eseguire operazioni come utente autenticato o non autenticato utilizzando rispettivamente RulesTestEnvironment.authenticatedContext e unauthenticatedContext . Ciò ti consente di verificare che le regole di sicurezza di Cloud Firestore consentano/negano correttamente casi diversi.