Prueba reglas de seguridad

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

Guía de inicio rápido

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

Comprende las reglas de seguridad de Firestore

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

Las reglas de seguridad de Firestore incluyen dos elementos:

  1. Una declaración match que identifica los documentos en la 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 Firestore para dispositivos móviles o la Web se comparan con las reglas de seguridad antes de 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 Firestore en Comienza a usar las reglas de seguridad de Firestore.

Instala el emulador

Para instalar el emulador de Firestore, usa Firebase CLI y ejecuta el siguiente comando:

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:

  • Al principio, el emulador cargará las reglas especificadas en el campo firestore.rules del archivo firebase.json. Este espera el nombre de un archivo local que contenga las reglas de seguridad de Firestore, que aplicará 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 a todos los proyectos como si tuvieran reglas abiertas.
  • Si bien la mayoría de los SDK de Firebase funcionan con los emuladores de forma directa, solo la biblioteca @firebase/rules-unit-testing admite la simulación de auth en las reglas de seguridad, lo que facilita las pruebas de unidades. Además, la biblioteca admite algunas funciones específicas del emulador, como borrar todos los datos, según se detalla a continuación.
  • Los emuladores también aceptarán tokens de producción de Firebase Auth proporcionados a través de SDK cliente y evaluarán las reglas en consecuencia, lo que te permite conectar tu aplicación directamente a los emuladores en pruebas de integración y manuales.

Ejecuta pruebas locales

initializeTestApp({ projectId: string, auth: Object }) => 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 para crear una app autenticada como un usuario específico que puedes emplear durante las pruebas.

firebase.initializeTestApp({
  projectId: "my-test-project",
  auth: { uid: "alice", email: "alice@example.com" }
});

initializeAdminApp({ projectId: string }) => FirebaseApp

Este método muestra una app de administración de Firebase inicializada, que omite las reglas de seguridad cuando realiza operaciones de lectura y escritura. 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: string, rules: Object }) => Promise

Este método envía reglas a una base de datos que se ejecuta localmente. Requiere un objeto que especifique las reglas como 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 se cumple correctamente si la entrada se rechaza. Úsalo para confirmar si fallan las operaciones de lectura o escritura de 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 se cumple correctamente si la entrada es correcta o se rechaza si la entrada se rechaza. Úsalo para confirmar si se completan correctamente las operaciones de lectura o escritura de una base de datos.

firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
    

clearFirestoreData({ projectId: string }) => Promise

Este método borra todos los datos asociados a un proyecto determinado de la instancia de Firestore 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 Firestore de manera explícita. El emulador crea de forma automática cualquier instancia a la que se acceda.
  2. El emulador de Firestore no funciona con el flujo normal de Firebase Authentication. En su lugar, en el SDK de prueba de Firebase, proporcionamos el método initializeTestApp() en la biblioteca rules-unit-testing, 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, fallarán las reglas auth != null).

Soluciona problemas conocidos

Mientras usas el emulador de Firestore, podrías tener los siguientes problemas conocidos. Sigue las recomendaciones de más abajo para solucionar cualquier problema de comportamiento irregular que experimentes. Estas notas se escribieron para el SDK de prueba de Firebase, pero los enfoques generales pueden aplicarse a cualquier SDK de Firebase.

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:

  • Establecer reglas de seguridad (por ejemplo, con firebase.loadFirestoreRules)
  • Leer y escribir datos (por ejemplo, con db.collection("users").doc("alice").get())
  • Usar aserciones operacionales, como 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. Ten en cuenta que, si decides hacerlo, deberás llamar a loadFirestoreRules como parte de cada prueba. Las reglas se cargarán automáticamente solo para el ID de proyecto predeterminado.
  • 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 las reglas de seguridad de 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.