Escribe condiciones para las reglas de seguridad de Cloud Firestore

Esta guía complementa la guía de estructuración de reglas de seguridad y explica cómo agregar condiciones a las reglas de seguridad de Cloud Firestore. Si no conoces los conceptos básicos de las reglas de seguridad de Cloud Firestore, consulta la guía de introducción.

El elemento principal de las reglas de seguridad de Cloud Firestore es la condición. Una condición es una expresión booleana que determina si se debe permitir o rechazar una operación en particular. Usa las reglas de seguridad para escribir condiciones que verifiquen la autenticación del usuario, validen datos entrantes o incluso accedan a otras partes de la base de datos.

Autenticación

Uno de los patrones de reglas de seguridad más comunes es controlar el acceso a partir del estado de autenticación del usuario. Por ejemplo, es posible que tu app permita que escriban datos solo los usuarios que acceden:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to access documents in the "cities" collection
    // only if they are authenticated.
    match /cities/{city} {
      allow read, write: if request.auth != null;
    }
  }
}

Otro patrón común es asegurarse de que los usuarios solo puedan leer y escribir sus propios datos:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the user
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /users/{userId} {
      allow read, update, delete: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null;
    }
  }
}

Si la app usa Firebase Authentication o Google Cloud Identity Platform, la variable request.auth contiene la información de autenticación del cliente que solicita datos. Para obtener más información sobre request.auth, consulta la documentación de referencia.

Validación de datos

Muchas apps almacenan información de control de acceso como campos en documentos dentro de una base de datos. Las reglas de seguridad de Cloud Firestore pueden permitir o rechazar el acceso de forma dinámica según los datos del documento:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

La variable resource hace referencia al documento solicitado, y resource.data es un mapa de todos los campos y los valores almacenados en el documento. Para obtener más información sobre la variable resource, consulta la documentación de referencia.

Cuando escribas datos, te recomendamos comparar los datos entrantes con los existentes. En este caso, si el conjunto de reglas permite la escritura pendiente, la variable request.resource contendrá el estado futuro del documento. Para las operaciones update en las que solo se modifica un subconjunto de los campos del documento, la variable request.resource contendrá el estado del documento pendiente después de la operación. Puedes verificar los valores de los campos de request.resource a fin de evitar actualizaciones de datos no deseadas o incoherentes. Para ello, usa este código:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure all cities have a positive population and
    // the name is not changed
    match /cities/{city} {
      allow update: if request.resource.data.population > 0
                    && request.resource.data.name == resource.data.name;
    }
  }
}

Accede a otros documentos

Con las funciones get() y exists(), las reglas de seguridad pueden evaluar las solicitudes entrantes en comparación con otros documentos de la base de datos. Las funciones get() y exists() usan rutas de acceso a documentos especificadas por completo. Cuando usas variables a fin de construir rutas de acceso para get() y exists(), debes escapar las variables de forma explícita con la sintaxis $(variable).

En el siguiente ejemplo, la sentencia de coincidencia match /databases/{database}/documents capta la variable database, la cual se usa para formar la ruta de acceso:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before
      // allowing any writes to the 'cities' collection
      allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid))

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
    }
  }
}

En el caso de las operaciones de escritura, puedes usar la función getAfter() con el fin de acceder al estado de un documento después de que se complete una transacción o un lote de operaciones de escritura, pero antes de que se confirmen estos procesos. Al igual que get(), la función getAfter() usa una ruta de acceso a documentos especificada por completo. Puedes usar getAfter() para definir conjuntos de operaciones de escritura que se deben llevar a cabo juntas como una transacción o un lote.

Límites de llamadas de acceso

Existen los siguientes límites en las llamadas de acceso a documentos por evaluación de conjunto de reglas:

  • 10 para las solicitudes de un solo documento o las solicitudes de consulta.
  • 20 para las lecturas de varios documentos, transacciones y escrituras en lotes. El límite anterior de 10 también aplica a cada operación.

    Por ejemplo, imagina que creas una solicitud de escritura en lotes con 3 operaciones de escritura y que tus reglas de seguridad usan 2 llamadas de acceso a documentos para validar cada escritura. En este caso, cada escritura usa 2 de las 10 llamadas de acceso y la solicitud de escritura en lotes usa 6 de las 20 llamadas de acceso.

Si se excede alguno de esos límites, se obtiene un error de permiso denegado. Algunas llamadas de acceso a documentos se pueden almacenar en caché y estas llamadas no cuentan para los límites.

Para obtener una explicación detallada de cómo influyen los límites en las transacciones y las operaciones de escritura en lotes, consulta la guía para proteger las operaciones atómicas.

Llamadas de acceso y precios

El uso de estas funciones ejecuta una operación de lectura en la base de datos, lo que significa que se te cobrará por leer documentos, incluso si las reglas rechazaron la solicitud. Consulta Precios de Cloud Firestore para obtener datos de facturación más específicos.

Funciones personalizadas

A medida que tus reglas de seguridad se vuelvan más complejas, te recomendamos agrupar conjuntos de condiciones en funciones que puedas volver a usar en tu conjunto de reglas. Las reglas de seguridad admiten funciones personalizadas. La sintaxis de las funciones personalizadas es similar a la de JavaScript, pero las funciones de reglas de seguridad se escriben en un lenguaje específico del dominio, que tiene algunas limitaciones importantes:

  • Las funciones solo pueden incluir una sola sentencia return. No pueden contener lógica adicional. Por ejemplo, no pueden ejecutar bucles ni llamar a servicios externos.
  • Las funciones pueden acceder automáticamente a funciones y variables desde el permiso en el que se definen. Por ejemplo, una función definida en el permiso service cloud.firestore tiene acceso a la variable resource y a las funciones integradas, como get() y exists().
  • Las funciones pueden llamar a otras funciones, pero no hacer referencia a sí mismas. La profundidad total de la pila de llamadas se limita a 10.
  • En la versión de reglas v2, las funciones pueden definir variables con la palabra clave let. Las funciones pueden tener hasta 10 vinculaciones let, pero deben terminar con una sentencia return.

Una función se define con la palabra clave function y usa cero o más argumentos. Por ejemplo, puedes combinar los dos tipos de condiciones usados en los ejemplos anteriores en una única función:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

Usar funciones en las reglas de seguridad facilita su mantenimiento a medida que aumenta su complejidad.

Las reglas no son filtros

Cuando protejas tus datos y comiences a escribir consultas, ten en cuenta que las reglas de seguridad no son filtros. No puedes escribir una consulta para todos los documentos de una colección y esperar que Cloud Firestore muestre solo los documentos a los que el cliente actual puede acceder.

Por ejemplo, analicemos la siguiente regla de seguridad:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

Denegada: Esta regla rechaza la siguiente consulta porque el conjunto de resultados puede incluir documentos en los que visibility no es public:

Web
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

Permitida: Esta regla permite la siguiente consulta porque la cláusula where("visibility", "==", "public") garantiza que el conjunto de resultados satisface la condición de la regla:

Web
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

Las reglas de seguridad de Cloud Firestore evalúan cada consulta según su posible resultado y hacen fallar la solicitud si es posible que muestre un documento que el cliente no tiene permiso para leer. Las consultas deben seguir las restricciones establecidas por sus reglas de seguridad. Para obtener más información sobre las reglas de seguridad y las consultas, ve a consulta datos de forma segura.

Pasos siguientes