Firebase 将于 5 月 10 日重返 Google I/O 大会!查看讲座

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 Cloud Firestore più sfumate. Con l'emulatore di Cloud Firestore, oltre a creare prototipi e testare le funzionalità e il comportamento generali della tua app , puoi scrivere unit test che controllano il comportamento delle regole di sicurezza di Cloud Firestore.

Avvio rapido

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

Informazioni sulle regole di sicurezza di Cloud Firestore

Implementa l'autenticazione Firebase e le regole di sicurezza di Cloud Firestore per l'autenticazione, l'autorizzazione e la convalida dei dati senza server quando utilizzi le librerie client Web e per dispositivi mobili.

Le regole di sicurezza di Cloud Firestore includono due parti:

  1. Un'istruzione 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 la base per i sistemi di accesso basati su utenti e ruoli.

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

Scopri di più sulle regole di sicurezza di Cloud Firestore in Introduzione alle regole di sicurezza di Cloud Firestore .

Installa l'emulatore

Per installare l'emulatore Cloud Firestore, utilizza l' interfaccia a riga di comando di 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 l'interfaccia a riga di comando di Firebase .

firebase init

Avviare l'emulatore utilizzando il seguente comando. L'emulatore verrà eseguito fino a quando non si interrompe il processo:

firebase emulators:start --only firestore

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

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

All'avvio, l'emulatore tenterà di essere eseguito 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, tieni presente quanto segue:

  • L'emulatore caricherà inizialmente le regole specificate nel campo firestore.rules del tuo 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 usi 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 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 Firebase Auth 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.

Eseguire unit test 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 versione 9 che con l'SDK versione 8. Le API della libreria sono significativamente 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 compatibilità con le versioni precedenti, continuiamo a rendere disponibile la libreria di test v8 .

Usa il modulo @firebase/rules-unit-testing per interagire con l'emulatore che viene 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 il 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 modulari v9. Per esempio:

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.

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 ignorarle temporaneamente, RulesTestEnvironment.withSecurityRulesDisabled .
  • Impostazione della suite di test e degli hook prima/dopo per test con chiamate per ripulire 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à

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

initializeTestEnvironment() => RulesTestEnvironment

Questa funzione inizializza un ambiente di test per il test unitario delle regole. Chiamare prima questa funzione per la configurazione del test. L'esecuzione corretta richiede 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 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 Authentication autenticato. Le richieste create tramite il contesto restituito avranno un token di autenticazione fittizio allegato. Facoltativamente, passa un oggetto che definisce attestazioni o sostituzioni personalizzate per i payload del token di autenticazione.

Utilizza l'oggetto del contesto di test restituito nei tuoi 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 è connesso tramite Authentication. Le richieste create tramite il contesto restituito non avranno i token Firebase Auth collegati.

Utilizza l'oggetto del contesto di test restituito nei tuoi 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 impostazione 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 esclusione 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 i test, 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 l'operazione di wrapping di Promise fornita da un 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 l'operazione di wrapping di Promise fornita da un emulatore verrà 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 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 dell'SDK client (v9 modular o v9 compat).

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 di valutazione per le regole di sicurezza Firebase.

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

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

Genera rapporti di prova

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

Per ottenere i report, eseguire una query su un endpoint esposto nell'emulatore mentre è in esecuzione. Per una versione compatibile con il browser, utilizzare 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 è possibile passare il mouse per ulteriori informazioni, incluso 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 si accede.
  2. L'emulatore Cloud Firestore non funziona con il normale flusso di autenticazione Firebase. 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 fosse stato autenticato correttamente come qualsiasi entità tu fornisca. Se passi null , si comporterà come un utente non autenticato ( auth != null falliranno, per esempio).

Risolvi 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 che stai riscontrando. Queste note sono state scritte pensando alla 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 test occasionalmente vengono superati e falliti, anche senza alcuna modifica ai test stessi, potrebbe essere necessario verificare che siano correttamente sequenziati. La maggior parte delle interazioni con l'emulatore sono asincrone, quindi ricontrolla che tutto il codice asincrono sia correttamente sequenziato. Puoi correggere la sequenza concatenando le promesse o usando liberamente la notazione await .

In particolare, esamina le seguenti operazioni asincrone:

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

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

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

  • Utilizza ID progetto univoci per ogni test. Nota che se scegli di farlo dovrai chiamare initializeTestEnvironment come parte di ogni test; le regole vengono caricate automaticamente solo per l'ID progetto predefinito.
  • Riorganizza i tuoi test in modo che non interagiscano con i dati scritti in precedenza (ad esempio, usa una raccolta diversa per ogni test).
  • Cancella tutti i dati scritti durante un test.

La configurazione del test è molto complicata

Quando imposti 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 tuo 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/neghino correttamente i diversi casi.