Test delle regole di sicurezza

Durante la creazione dell'app, potresti bloccare l'accesso al tuo databaseFirestore. Tuttavia, prima del lancio avrai bisogno di regole di sicurezza firewall più specifiche. Con l'emulatore Firestore, oltre a prototipare e testare le funzionalità e comportamenti generali dell'app, puoi scrivere test di unità che verificano il comportamento delle regole di sicurezza di Firestore.

Guida rapida

Per alcuni scenari di test di base con regole semplici, prova l'esempio di guida rapida.

Comprendi le regole di sicurezza di Firestore

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

Le regole di sicurezza di Firestore includono due elementi:

  1. 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 le basi per sistemi di accesso basati sugli utenti e sui ruoli.

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

Scopri di più sulle regole di sicurezza Firestore nella Guida introduttiva alle regole di sicurezza Firestore.

Installa l'emulatore

Per installare l'emulatore 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 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 finché non completerai la procedura:

firebase emulators:start --only firestore

In molti casi vuoi avviare l'emulatore, eseguire una suite di test e poi 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 eseguire l'esecuzione su una porta predefinita (8080). Per cambiare la porta dell'emulatore, modifica 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. Prevede il nome di un file locale contenente le regole di sicurezza Firestore e le applica a tutti i progetti. Se non fornisci il percorso del file locale o se utilizzi il metodo loadFirestoreRules come descritto di seguito, l'emulatore considera tutti i progetti come 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, rendendo molto più semplice il 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 token Firebase Auth di produzione forniti tramite gli SDK client e valutano le regole di conseguenza, consentendo la connessione della tua applicazione direttamente agli emulatori nei test di integrazione e manuali.

Test dei test locali delle unità

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

Firebase distribuisce una libreria di test delle unità di Regole di sicurezza con il suo SDK JavaScript versione 9 e il suo SDK versione 8. Le API delle librerie sono molto diverse. Consigliamo la libreria di test v9, che è più semplice e richiede meno configurazione per il collegamento agli emulatori, evitando così l'uso 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 eseguito localmente. Se vengono visualizzati 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 tutto il comportamento che potresti voler testare comprende funzioni asincrone e il modulo di test è progettato per funzionare con codice basato su promise.

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

Puoi importare la libreria utilizzando istruzioni di importazione modulari v9. Ad 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 eseguita l'importazione, l'implementazione dei test delle unità comporta:

  • Creazione e configurazione di un RulesTestEnvironment con chiamata a initializeTestEnvironment.
  • Configura i dati di test senza attivare le regole, utilizzando un metodo di convenienza che ti consente di ignorarle temporaneamente, RulesTestEnvironment.withSecurityRulesDisabled.
  • Configurazione di una suite di test e di test prima/dopo con cicli di chiamata per pulire i dati e l'ambiente di test, come RulesTestEnvironment.cleanup() o RulesTestEnvironment.clearFirestore().
  • Implementazione di scenari 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 il test dell'unità regole. Chiama prima questa funzione 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 elemento 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 Authentication autenticato. Alle richieste create tramite il contesto restituito verrà allegato un token di autenticazione fittizio. Facoltativamente, passa un oggetto che definisce le attestazioni personalizzate o le sostituzioni dei payload del token di autenticazione.

Utilizza l'oggetto di contesto restituito nei test per accedere a qualsiasi istanza di 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 eseguito l'accesso tramite Authentication. Alle richieste create tramite il contesto restituito non verranno associati i token di Firebase Auth.

Utilizza l'oggetto di contesto restituito nei test per accedere a qualsiasi istanza di 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 test con un contesto che si comporti come se le regole di sicurezza fossero state disattivate.

Questo metodo prevede una funzione di callback, in cui il contesto delle regole di sicurezza viene ignorato e viene restituita una promessa. Il contesto verrà eliminato una volta che la promessa verrà 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 un test e l'altro, utilizza il metodo chiaro e chiaro specifico dell'applicazione.

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

Questa è una funzione di utilità di test.

La funzione afferma che l'offerta Promise che esegue 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à di test.

La funzione afferma che l'offerta Promise che esegue 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 all'projectId configurato per l'emulatore Firestore.

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

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

Visualizza le valutazioni delle regole

L'emulatore Firestore consente di visualizzare le richieste del client nell'interfaccia utente di Emulator Suite, compreso il tracciamento di valutazione per le regole di sicurezza Firebase.

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

Monitor 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 rapporti sulla copertura di test che mostrano come è stata valutata ciascuna delle tue regole di sicurezza.

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

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

Suddivide le regole in espressioni e sottoespressioni che puoi spostare con il mouse per ottenere ulteriori informazioni, tra cui il numero di valutazioni e valori restituiti. Per la versione JSON non elaborata di questi dati, includi il seguente URL nella tua query:

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

Differenze tra emulatore e produzione

  1. Non devi creare esplicitamente un progetto Firestore. L'emulatore crea automaticamente qualsiasi istanza a cui si accede.
  2. L'emulatore Firestore non funziona con il normale flusso di autenticazione Firebase. Nell'SDK Firebase Test abbiamo invece fornito il metodo initializeTestApp() nella libreria rules-unit-testing, che utilizza un campo auth. L'handle di Firebase creato con questo metodo si comporterà come se fosse stato autenticato correttamente come qualsiasi altra entità tu abbia fornito. Se trasmetti in null, 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 Firestore, potresti riscontrare i seguenti problemi noti. Segui le indicazioni riportate di seguito per risolvere eventuali comportamenti irregolari. Queste note sono scritte in base alla libreria di test dell'unità di regole di sicurezza, ma gli approcci generali sono applicabili a qualsiasi SDK Firebase.

Il comportamento dei test è incoerente

Se a volte i test hanno esito positivo e non superato, anche senza modifiche, è possibile che tu debba verificare che siano in sequenza correttamente. La maggior parte delle interazioni con l'emulatore è asincrona, pertanto verifica che tutto il codice asincrono sia in sequenza corretta. Puoi correggere la sequenza seguendo le promesse o utilizzando la notazione await in modo liberale.

In particolare, rivedi le seguenti operazioni asincrone:

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

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

L'emulatore è stateful. Archivia tutti i dati scritti in memoria, quindi tutti i dati andranno persi ogni volta che l'emulatore si spegne. Se stai eseguendo più test sullo stesso ID progetto, ogni test può produrre dati che potrebbero influire sui test successivi. Puoi utilizzare uno dei seguenti metodi per aggirare 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.
  • Riorganizza i test in modo che non interagiscano con i dati scritti in precedenza (ad esempio, utilizza una raccolta diversa per ogni test).
  • Eliminare tutti i dati scritti durante un test.

La configurazione del test è molto complicata

Quando configuri il test, ti consigliamo di modificare i dati in modo che le regole di sicurezza di firewall non consentano effettivamente di farlo. Se le regole rendono la configurazione di test complessa, prova a utilizzare RulesTestEnvironment.withSecurityRulesDisabled nei passaggi di configurazione, quindi le operazioni di lettura e scrittura non attiveranno errori PERMISSION_DENIED.

Successivamente, il test può eseguire le operazioni in qualità di utente autenticato o non autenticato utilizzando RulesTestEnvironment.authenticatedContext e unauthenticatedContext. In questo modo puoi verificare che le regole di sicurezza di Firestore consentano / negano correttamente le diverse richieste.