Pruebe sus reglas de seguridad de Cloud Firestore

Mientras crea su aplicación, es posible que desee bloquear el acceso a su base de datos de Cloud Firestore. Sin embargo, antes del lanzamiento, necesitará reglas de seguridad de Cloud Firestore más matizadas. Con el emulador de Cloud Firestore, además de crear prototipos y probar las características y el comportamiento generales de su aplicación, puede escribir pruebas unitarias que verifiquen el comportamiento de sus reglas de seguridad de Cloud Firestore.

Inicio rápido

Para ver algunos casos de prueba básicos con reglas simples, pruebe el ejemplo de inicio rápido .

Comprender las reglas de seguridad de Cloud Firestore

Implemente las reglas de seguridad de Firebase Authentication y Cloud Firestore para la autenticación, autorización y validación de datos sin servidor cuando utilice las bibliotecas de clientes web y móviles.

Las reglas de seguridad de Cloud Firestore incluyen dos partes:

  1. Una declaración match que identifica los documentos en su base de datos.
  2. Una expresión allow que controla el acceso a esos documentos.

Firebase Authentication verifica las credenciales de los usuarios y proporciona la base para sistemas de acceso basados ​​en usuarios y roles.

Cada solicitud de base de datos de una biblioteca de cliente web/móvil de Cloud Firestore se evalúa según sus reglas de seguridad antes de leer o escribir cualquier dato. Si las reglas niegan el acceso a cualquiera de las rutas de documentos especificadas, toda la solicitud falla.

Obtenga más información sobre las reglas de seguridad de Cloud Firestore en Introducción a las reglas de seguridad de Cloud Firestore .

Instalar el emulador

Para instalar el emulador de Cloud Firestore, use Firebase CLI y ejecute el siguiente comando:

firebase setup:emulators:firestore

Ejecute el emulador

Comience inicializando un proyecto de Firebase en su directorio de trabajo. Este es un primer paso común cuando se utiliza Firebase CLI .

firebase init

Inicie el emulador usando el siguiente comando. El emulador se ejecutará hasta que finalices el proceso:

firebase emulators:start --only firestore

En muchos casos, desea iniciar el emulador, ejecutar un conjunto de pruebas y luego apagar el emulador después de ejecutar las pruebas. Puedes hacer esto fácilmente usando el comando emulators:exec :

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

Cuando se inicie, el emulador intentará ejecutarse en un puerto predeterminado (8080). Puedes cambiar el puerto del emulador modificando la sección "emulators" de tu archivo firebase.json :

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

Antes de ejecutar el emulador

Antes de comenzar a utilizar el emulador, tenga en cuenta lo siguiente:

  • El emulador cargará inicialmente las reglas especificadas en el campo firestore.rules de su archivo firebase.json . Espera el nombre de un archivo local que contiene sus reglas de seguridad de Cloud Firestore y aplica esas reglas a todos los proyectos. Si no proporciona la ruta del archivo local o utiliza el método loadFirestoreRules como se describe a continuación, el emulador trata todos los proyectos como si tuvieran reglas abiertas.
  • Si bien la mayoría de los SDK de Firebase funcionan directamente con los emuladores, solo la biblioteca @firebase/rules-unit-testing admite auth simulada en las reglas de seguridad, lo que facilita mucho las pruebas unitarias. Además, la biblioteca admite algunas funciones específicas del emulador, como borrar todos los datos, como se enumera a continuación.
  • Los emuladores también aceptarán tokens de Firebase Auth de producción proporcionados a través de los SDK del cliente y evaluarán las reglas en consecuencia, lo que permite conectar su aplicación directamente a los emuladores en pruebas de integración y manuales.

Ejecute pruebas de unidades locales

Ejecute pruebas de unidades locales con el SDK de JavaScript v9

Firebase distribuye una biblioteca de pruebas unitarias de reglas de seguridad con su SDK de JavaScript versión 9 y su SDK versión 8. Las API de la biblioteca son significativamente diferentes. Recomendamos la biblioteca de pruebas v9, que es más optimizada y requiere menos configuración para conectarse a emuladores y así evitar de forma segura el uso accidental de recursos de producción. Para lograr compatibilidad con versiones anteriores, seguimos poniendo a disposición la biblioteca de pruebas v8 .

Utilice el módulo @firebase/rules-unit-testing para interactuar con el emulador que se ejecuta localmente. Si obtiene tiempos de espera o errores ECONNREFUSED , verifique que el emulador realmente se esté ejecutando.

Recomendamos encarecidamente utilizar una versión reciente de Node.js para poder utilizar la notación async/await . Casi todo el comportamiento que quizás desee probar involucra funciones asincrónicas y el módulo de prueba está diseñado para funcionar con código basado en Promise.

La biblioteca de pruebas unitarias de reglas v9 siempre está al tanto de los emuladores y nunca toca sus recursos de producción.

Importa la biblioteca utilizando declaraciones de importación modulares v9. Por ejemplo:

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 vez importadas, la implementación de pruebas unitarias implica:

  • Crear y configurar un RulesTestEnvironment con una llamada a initializeTestEnvironment .
  • Configurar datos de prueba sin activar reglas, utilizando un método conveniente que le permite omitirlas temporalmente, RulesTestEnvironment.withSecurityRulesDisabled .
  • Configurar un conjunto de pruebas y enlaces antes/después de cada prueba con llamadas para limpiar los datos y el entorno de la prueba, como RulesTestEnvironment.cleanup() o RulesTestEnvironment.clearFirestore() .
  • Implementar casos de prueba que imitan los estados de autenticación usando RulesTestEnvironment.authenticatedContext y RulesTestEnvironment.unauthenticatedContext .

Métodos comunes y funciones de utilidad.

Consulte también los métodos de prueba específicos del emulador en el SDK v9 .

initializeTestEnvironment() => RulesTestEnvironment

Esta función inicializa un entorno de prueba para pruebas unitarias de reglas. Llame a esta función primero para la configuración de la prueba. La ejecución exitosa requiere que se estén ejecutando emuladores.

La función acepta un objeto opcional que define TestEnvironmentConfig , que puede consistir en un ID de proyecto y ajustes de configuración del emulador.

let testEnv = await initializeTestEnvironment({
  projectId: "demo-project-1234",
  firestore: {
    rules: fs.readFileSync("firestore.rules", "utf8"),
  },
});

RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext

Este método crea un RulesTestContext , que se comporta como un usuario de autenticación autenticado. Las solicitudes creadas a través del contexto devuelto tendrán adjunto un token de autenticación simulado. Opcionalmente, pase un objeto que defina notificaciones personalizadas o anulaciones para las cargas útiles del token de autenticación.

Utilice el objeto de contexto de prueba devuelto en sus pruebas para acceder a cualquier instancia del emulador configurada, incluidas aquellas configuradas 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

Este método crea un RulesTestContext , que se comporta como un cliente que no ha iniciado sesión mediante autenticación. Las solicitudes creadas a través del contexto devuelto no tendrán tokens de autenticación de Firebase adjuntos.

Utilice el objeto de contexto de prueba devuelto en sus pruebas para acceder a cualquier instancia del emulador configurada, incluidas aquellas configuradas 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()

Ejecute una función de configuración de prueba con un contexto que se comporte como si las reglas de seguridad estuvieran deshabilitadas.

Este método toma una función de devolución de llamada, que toma el contexto de omisión de reglas de seguridad y devuelve una promesa. El contexto se destruirá una vez que la promesa se resuelva/rechace.

RulesTestEnvironment.cleanup()

Este método destruye todos los RulesTestContexts creados en el entorno de prueba y limpia los recursos subyacentes, lo que permite una salida limpia.

Este método no cambia el estado de los emuladores de ninguna manera. Para restablecer datos entre pruebas, utilice el método de borrar datos específico del emulador de aplicaciones.

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

Esta es una función de utilidad de caso de prueba.

La función afirma que la Promesa proporcionada que envuelve una operación del emulador se resolverá sin violaciones de las reglas de seguridad.

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

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

Esta es una función de utilidad de caso de prueba.

La función afirma que la Promesa proporcionada que envuelve una operación del emulador será rechazada con una violación de las Reglas de Seguridad.

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

Métodos específicos del emulador

Consulte también los métodos de prueba comunes y las funciones de utilidad en el SDK v9 .

RulesTestEnvironment.clearFirestore() => Promise<void>

Este método borra los datos de la base de datos de Firestore que pertenecen al projectId configurado para el emulador de Firestore.

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

Este método obtiene una instancia de Firestore para este contexto de prueba. La instancia del SDK del cliente Firebase JS devuelta se puede usar con las API del SDK del cliente (v9 modular o compatible con v9).

Visualizar evaluaciones de reglas

El emulador de Cloud Firestore te permite visualizar las solicitudes de los clientes en la interfaz de usuario de Emulator Suite, incluido el seguimiento de la evaluación de las reglas de seguridad de Firebase.

Abra la pestaña Firestore > Solicitudes para ver la secuencia de evaluación detallada de cada solicitud.

Monitor de solicitudes del emulador de Firestore que muestra evaluaciones de reglas de seguridad

Generar informes de prueba

Después de ejecutar un conjunto de pruebas, puede acceder a informes de cobertura de pruebas que muestran cómo se evaluó cada una de sus reglas de seguridad.

Para obtener los informes, consulte un punto final expuesto en el emulador mientras se está ejecutando. Para obtener una versión compatible con el navegador, utilice la siguiente URL:

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

Esto divide sus reglas en expresiones y subexpresiones sobre las que puede pasar el mouse para obtener más información, incluida la cantidad de evaluaciones y valores devueltos. Para la versión JSON sin formato de estos datos, incluya la siguiente URL en su consulta:

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

Diferencias entre el emulador y la producción.

  1. No es necesario crear explícitamente un proyecto de Cloud Firestore. El emulador crea automáticamente cualquier instancia a la que se accede.
  2. El emulador de Cloud Firestore no funciona con el flujo normal de autenticación de Firebase. En cambio, en el SDK de Firebase Test, proporcionamos el método initializeTestApp() en la biblioteca rules-unit-testing , que toma un campo auth . El identificador de Firebase creado con este método se comportará como si se hubiera autenticado correctamente como cualquier entidad que proporcione. Si pasa null , se comportará como un usuario no autenticado (las reglas auth != null fallarán, por ejemplo).

Solucionar problemas conocidos

Al utilizar el emulador de Cloud Firestore, es posible que se encuentre con los siguientes problemas conocidos. Siga las instrucciones a continuación para solucionar cualquier comportamiento irregular que esté experimentando. Estas notas están escritas teniendo en cuenta la biblioteca de pruebas unitarias de Reglas de seguridad, pero los enfoques generales son aplicables a cualquier SDK de Firebase.

El comportamiento de la prueba es inconsistente

Si sus pruebas ocasionalmente pasan y fallan, incluso sin realizar cambios en las pruebas mismas, es posible que deba verificar que estén secuenciadas correctamente. La mayoría de las interacciones con el emulador son asíncronas, así que verifique que todo el código asíncrono esté secuenciado correctamente. Puede arreglar la secuencia encadenando promesas o usando la notación await generosamente.

En particular, revise las siguientes operaciones asíncronas:

  • Establecer reglas de seguridad, por ejemplo, con initializeTestEnvironment .
  • Leer y escribir datos, por ejemplo, con db.collection("users").doc("alice").get() .
  • Afirmaciones operativas, incluidas assertSucceeds y assertFails .

Las pruebas solo pasan la primera vez que cargas el emulador.

El emulador tiene estado. Almacena todos los datos escritos en la memoria, por lo que todos los datos se pierden cada vez que se apaga el emulador. Si ejecuta varias pruebas con el mismo ID de proyecto, cada prueba puede producir datos que podrían influir en las pruebas posteriores. Puede utilizar cualquiera de los siguientes métodos para evitar este comportamiento:

  • Utilice ID de proyecto únicos para cada prueba. Tenga en cuenta que si decide hacer esto, deberá llamar initializeTestEnvironment como parte de cada prueba; Las reglas solo se cargan automáticamente para el ID de proyecto predeterminado.
  • Reestructura tus pruebas para que no interactúen con datos escritos previamente (por ejemplo, usa una colección diferente para cada prueba).
  • Eliminar todos los datos escritos durante una prueba.

La configuración de la prueba es muy complicada.

Al configurar su prueba, es posible que desee modificar los datos de una manera que las reglas de seguridad de Cloud Firestore en realidad no lo permitan. Si sus reglas hacen que la configuración de la prueba sea compleja, intente usar RulesTestEnvironment.withSecurityRulesDisabled en sus pasos de configuración, de modo que las lecturas y escrituras no generen errores PERMISSION_DENIED .

Después de eso, su prueba puede realizar operaciones como usuario autenticado o no autenticado usando RulesTestEnvironment.authenticatedContext y unauthenticatedContext respectivamente. Esto le permite validar que sus reglas de seguridad de Cloud Firestore permitan/nieguen diferentes casos correctamente.