Ir a la consola

Prueba tus reglas de seguridad de Cloud Firestore

Recomendamos que bloquees el acceso a la base de datos de Cloud Firestore a medida que compiles tu app. Sin embargo, antes de que la inicies, deberás contar con reglas de seguridad de Cloud Firestore más avanzadas. Con el emulador de Cloud Firestore, puedes escribir pruebas de unidades que verifiquen el comportamiento de tus reglas de seguridad de Cloud Firestore.

Guía de inicio rápido

Si quieres consultar algunos casos de prueba básicos con reglas simples, consulta la guía de inicio rápido de JavaScript o la guía de inicio rápido de TypeScript.

Comprende las reglas de seguridad de Cloud Firestore

Implementa Firebase Authentication y las reglas de seguridad de Cloud Firestore para la autenticación, autorización y validación de datos sin servidores cuando uses las bibliotecas cliente para dispositivos móviles y la Web.

Las reglas de seguridad de Cloud Firestore incluyen dos partes:

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

Firebase Authentication verifica las credenciales de los usuarios y proporciona una base para los sistemas de acceso basados en usuarios y funciones.

Todas las solicitudes que se envíen a la base de datos desde una biblioteca cliente de Cloud Firestore web o para dispositivos móviles se comparan con tus reglas de seguridad antes de poder leer o escribir datos. Si las reglas rechazan el acceso a alguna de las rutas de los documentos especificados, falla la solicitud completa.

Obtén más información sobre las reglas de seguridad de Cloud Firestore en Cómo comenzar con las reglas de seguridad de Cloud Firestore.

Instala el emulador

Para instalar el emulador de Cloud Firestore, usa Firebase CLI y ejecuta el comando que aparece a continuación:

firebase setup:emulators:firestore

Ejecuta el emulador

Inicia el emulador con el siguiente comando. El emulador se ejecutará hasta que finalices el proceso:

firebase emulators:start --only firestore

En muchos casos, es recomendable que inicies el emulador, ejecutes una prueba y, luego, cierres el emulador después de que hayas ejecutado las pruebas. Puedes hacerlo de manera sencilla con el comando emulators:exec, como en el ejemplo:

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

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

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

Antes de que ejecutes el emulador

Antes de comenzar a usar el emulador, ten en cuenta lo siguiente:

  • El único SDK que actualmente admite el emulador es el SDK de Node.js. Proporcionamos el módulo @firebase/testing para facilitar la interacción con el emulador.
  • Inicialmente, el emulador cargará las reglas especificadas en el campo firestore.rules de tu archivo firebase.json. Este espera el nombre de un archivo local con tus reglas de seguridad de Cloud Firestore y las aplica a todos los proyectos. Si no proporcionas la ruta de un archivo local o no usas el método loadFirestoreRules como se describe a continuación, el emulador trata todos los proyectos como si tuvieran reglas abiertas.

Ejecuta pruebas locales

Usa el módulo @firebase/testing para interactuar con el emulador que se ejecuta de manera local. Si recibes errores ECONNREFUSED o tiempos de espera, vuelve a verificar que se esté ejecutando el emulador.

Recomendamos usar una versión reciente de Node.js, de modo que puedas utilizar la notación async/await. Casi todo comportamiento que quieras probar involucra funciones asíncronas. El módulo de pruebas está diseñado para funcionar con un código basado en promesas.

El módulo muestra los siguientes métodos:

  • initializeTestApp({ projectId: <name>, auth: <auth> }) => FirebaseApp

    Este método muestra una app de Firebase inicializada correspondiente al ID del proyecto y la variable de auth especificada en las opciones. Úsalo a fin de crear una app autenticada como un usuario específico para usarla durante las pruebas.

     firebase.initializeTestApp({
       projectId: "my-test-project",
       auth: { uid: "alice", email: "alice@example.com" }
     });
    
  • initializeAdminApp({ projectId: <name> }) => FirebaseApp

    Este método muestra una app de administración de Firebase inicializada. La app elude las reglas de seguridad cuando realiza lecturas o escrituras. Usa el método a fin de crear una app autenticada como un administrador para configurar el estado de las pruebas.

    firebase.initializeAdminApp({ projectId: "my-test-project" });
    
  • apps() => [FirebaseApp] Este método muestra todas las apps de administración y pruebas inicializadas actualmente. Úsalo para limpiar las apps entre pruebas o después de ellas.

     Promise.all(firebase.apps().map(app => app.delete()))
    
  • loadFirestoreRules({ projectId: <name>, rules: <rules> }) => Promise

    Este método envía reglas a una base de datos que se ejecuta localmente. Recibe un objeto que especifica las reglas como una string. Usa este método para configurar las reglas de tu base de datos.

     firebase.loadFirestoreRules({
       projectId: "my-test-project",
       rules: fs.readFileSync("/path/to/firestore.rules", "utf8")
     });
    
  • assertFails(pr: Promise) => Promise

    Este método muestra una promesa que se rechaza si la entrada es correcta o que se cumple si se rechaza la entrada. Úsalo para declarar si no se puede leer ni escribir una base de datos:

    firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
    
  • assertSucceeds(pr: Promise) => Promise

    Este método muestra una promesa que tiene éxito si se logra realizar la entrada y que se rechaza si la entrada es rechazada. Úsalo para declarar si una base de datos puede leer o escribir:

    firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
    
  • clearFirestoreData({ projectId: <name> }) => Promise

    Este método borra todos los datos asociados a un proyecto determinado de la instancia de Firebase que se ejecuta de manera local. Usa este método para realizar la limpieza después de las pruebas.

    firebase.clearFirestoreData({
     projectId: "my-test-project"
    });
    

Genera informes de prueba

Luego de ejecutar un conjunto de pruebas, puedes acceder a los informes de cobertura de pruebas que muestran cómo se evaluaron las reglas de seguridad.

Para obtenerlos, consulta un extremo expuesto en el emulador mientras se ejecuta. Usa la siguiente URL para una versión compatible con el navegador:

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

Esto divide tus reglas en expresiones y subexpresiones sobre las que puedes desplazar el mouse para obtener más información, como la cantidad de evaluaciones y los valores mostrados. Si quieres acceder a la versión JSON sin procesar de los datos, incluye la siguiente URL en la consulta:

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

Diferencias entre el emulador y la producción

  1. No tienes que crear un proyecto de Cloud Firestore de manera explícita. El emulador crea automáticamente cualquier instancia a la que se acceda.
  2. El emulador de Cloud Firestore no funciona con el flujo normal de Firebase Authentication. En su lugar, proporcionamos el método initializeTestApp() en el módulo de prueba, que incluye un campo auth. El controlador de Firebase creado con este método se comportará como si se hubiera autenticado correctamente como cualquier entidad proporcionada. Si pasas null, se comportará como un usuario no autenticado (por ejemplo, las reglas auth != null no funcionarán).

Soluciona problemas conocidos

Mientras usas el emulador de Cloud Firestore, podrías tener los siguientes problemas conocidos. Sigue las recomendaciones de más abajo para solucionar cualquier problema de comportamiento irregular que experimentes.

El comportamiento de prueba es incoherente

Si tus pruebas pasan y fallan ocasionalmente, incluso sin ningún cambio en ellas, es posible que debas verificar si están secuenciadas de manera correcta. La mayoría de las interacciones con el emulador son asíncronas, por lo tanto, vuelve a verificar que todo el código asíncrono esté secuenciado de forma correcta. Puedes solucionar problemas de secuencia mediante cadenas de promesas o con el uso libre de la notación await.

En particular, revisa las siguientes operaciones asíncronas:

  • Configuración de reglas de seguridad con firebase.loadFirestoreRules, por ejemplo.
  • Lectura y escritura de datos con db.collection("users").doc("alice").get(), por ejemplo.
  • Declaraciones operacionales que incluyen firebase.assertSucceeds y firebase.assertFails.

Las pruebas solo pasan la primera vez que cargas el emulador

El emulador es con estado. Almacena en la memoria todos los datos que se escriban en él, por lo que se pierden todos los datos cuando se apaga. Si estás ejecutando varias pruebas con el mismo ID de proyecto, cada prueba puede producir datos que podrían influir en las pruebas posteriores. Para evitar este comportamiento, puedes usar cualquiera de los siguientes métodos:

  • Usa ID de proyecto únicos para cada prueba.
  • Reestructura tus pruebas, de modo que no interactúen con datos escritos previamente (por ejemplo, usa una colección diferente para cada prueba).
  • Borra todos los datos escritos durante una prueba.

La configuración de la prueba es muy complicada

Te recomendamos probar situaciones que tus reglas de seguridad de Cloud Firestore no permitan. Por ejemplo, es difícil probar si los usuarios no autenticados pueden editar datos, ya que no puedes editar datos si no estás autenticado.

Si tus reglas hacen que la configuración de la prueba sea compleja, intenta usar un cliente autorizado por el administrador para omitirlas. Puedes hacerlo con firebase.initializeAdminApp. Las operaciones de lectura y escritura de los clientes autorizados por el administrador omiten las reglas y no activan errores de PERMISSION_DENIED.