Condiciones de escritura de reglas de seguridad

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 aspectos 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 tu 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.uid != 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 the 'author_id' field
    // of the document
    match /users/{user} {
      allow read, update, delete: if request.auth.uid == resource.data.author_id;
      allow create: if request.auth.uid != null;
    }
  }
}

Si tu app usa Firebase Authentication, 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 tu conjunto de reglas permite la escritura pendiente, la variable request.resource contiene el estado futuro del documento. Para las operaciones update que solo modifican un subconjunto de los campos del documento, la variable request.resource contendrá el estado del documento pendiente antes de la operación de escritura. Puedes verificar los valores de los campos en request.resource para evitar actualizaciones de datos no deseadas o incoherentes:

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;
    }
  }
}

Acceso 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() esperan rutas de documentos especificadas por completo. Cuando usas variables para construir rutas para get() y exists(), debes escapar las variables de forma explícita con la sintaxis $(variable).

En el siguiente ejemplo, la declaración de coincidencia match /databases/{database}/documents captura 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 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 get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
    }
  }
}

Para las escrituras, 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 escrituras, pero antes de que se confirme la transacción o el lote. Al igual que get(), la función getAfter() usa una ruta de acceso de documentos especificada por completo. Puedes usar getAfter() para definir conjuntos de escrituras que se deben llevar a cabo juntas como una transacción o un lote.

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 y tiene algunas limitaciones importantes:

  • Las funciones solo pueden incluir una única declaración return. No pueden contener lógica adicional. Por ejemplo, no pueden crear variables intermedias, ejecutar bucles ni llamar a servicios externos.
  • Las funciones pueden acceder automáticamente a funciones y variables desde el alcance en el que se definen. Por ejemplo, una función definida dentro del alcance 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 hacen referencia a sí mismas. La profundidad total de la pila de llamadas se limita a 10.

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. Para obtener más información sobre las funciones definidas por el programador, consulta la documentación de referencia.

Próximos pasos

Enviar comentarios sobre…

¿Necesitas ayuda? Visita nuestra página de asistencia.