En esta página, se amplían los conceptos mencionados en los artículos Estructura reglas de seguridad y Escribe condiciones para las reglas de seguridad para explicar cómo las consultas interactúan con Cloud Firestore Security Rules. Además, se examina con más detalle la manera en que las reglas de seguridad afectan a las consultas que puedes escribir y se describe cómo garantizar que las consultas usen las mismas restricciones que tus reglas de seguridad. En esta página, también se explica cómo escribir reglas de seguridad para permitir o rechazar consultas según las propiedades de una consulta, como limit
y orderBy
.
Las reglas no son filtros
Cuando escribas consultas para recuperar documentos, ten en cuenta que las reglas de seguridad no son filtros: las consultas son todo o nada. Para ahorrar tiempo y recursos, Cloud Firestore evalúa una consulta en comparación con su conjunto de resultados potenciales en lugar de con los valores de campo reales para todos tus documentos. Si una consulta llegara a mostrar documentos que el cliente no tiene permiso para leer, fallará toda la solicitud.
Consultas y reglas de seguridad
Tal como se muestra en los siguientes ejemplos, debes escribir las consultas de modo que se adapten a las restricciones de las reglas de seguridad.
Protege y consulta documentos en función del auth.uid
En el siguiente ejemplo, se muestra cómo escribir una consulta para recuperar documentos protegidos por una regla de seguridad. Supongamos que tienes una base de datos que contiene una colección de documentos story
:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
Además de los campos title
y content
, cada documento almacena los campos author
y published
para usarlos a fin de controlar el acceso. En estos ejemplos, se supone que la app usa Firebase Authentication para configurar el campo author
con el UID del usuario que creó el documento. Firebase Authentication también propaga la variable request.auth
de las reglas de seguridad.
En la siguiente regla de seguridad, se usan las variables request.auth
y resource.data
para restringir el acceso de lectura y escritura de cada story
, de modo que solo el autor pueda leer y escribir los documentos:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Only the authenticated user who authored the document can read or write
allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Supongamos que la app incluye una página que muestra a los usuarios una lista de documentos story
que ellos mismos crearon. Es de esperar que puedas usar la siguiente consulta para propagar el contenido de la página. Sin embargo, esta consulta fallará porque no incluye las mismas restricciones que tus reglas de seguridad:
No válida: Las restricciones de la consulta no coinciden con las restricciones de las reglas de seguridad.
// This query will fail
db.collection("stories").get()
No se logra realizar la consulta incluso si el usuario actual es el autor de cada documento story
. Este comportamiento se debe a que, cuando
Cloud Firestore aplica las reglas de seguridad, evalúa la consulta
según su conjunto de resultados posibles, no según las propiedades reales de
los documentos de tu base de datos. Si una consulta llegara a incluir documentos
que infringen las reglas de seguridad, esta fallará.
Por el contrario, la siguiente consulta se ejecuta de forma correcta porque incluye la misma restricción del campo author
que las reglas de seguridad:
Válida: Las restricciones de la consulta coinciden con las restricciones de las reglas de seguridad.
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
Protege y consulta documentos según el valor de un campo
Para demostrar en más profundidad la interacción entre consultas y reglas, las siguientes reglas de seguridad amplían el acceso de lectura de la colección stories
para que cualquier usuario pueda leer los documentos story
cuyo campo published
esté configurado como true
.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Anyone can read a published story; only story authors can read unpublished stories
allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
// Only story authors can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
La consulta para las páginas publicadas debe incluir las mismas restricciones que las reglas de seguridad:
db.collection("stories").where("published", "==", true).get()
La restricción de consulta .where("published", "==", true)
garantiza que resource.data.published
sea true
para cualquier resultado. Por lo tanto, esta consulta cumple con las reglas de seguridad y tiene permitido leer los datos.
OR
consultas
Cuando se evalúa una consulta lógica OR
(or
, in
o array-contains-any
)
según un conjunto de reglas, Cloud Firestore evalúa cada valor de comparación
por separado. Cada valor de comparación debe cumplir con las restricciones de las reglas de seguridad. Veamos
la siguiente regla
como ejemplo:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
No válida: La consulta no garantiza que x > 5
en todos los documentos posibles.
// These queries will fail
query(db.collection("mydocuments"),
or(where("x", "==", 1),
where("x", "==", 6)
)
)
query(db.collection("mydocuments"),
where("x", "in", [1, 3, 6, 42, 99])
)
Válida: La consulta garantiza que x > 5
en todos los documentos posibles.
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
Evalúa restricciones en las consultas
Las reglas de seguridad también pueden aceptar o rechazar consultas según sus restricciones.
La variable request.query
contiene las propiedades limit
, offset
y orderBy
de una consulta. Por ejemplo, las reglas de seguridad pueden rechazar cualquier consulta que no limite la cantidad máxima de documentos recuperados a un intervalo determinado:
allow list: if request.query.limit <= 10;
En el siguiente conjunto de reglas, se muestra cómo escribir reglas de seguridad que evalúen las restricciones impuestas a las consultas. En este ejemplo, se amplía el conjunto de reglas stories
con los siguientes cambios:
- El conjunto de reglas desglosa la regla read en reglas
get
ylist
. - La regla
get
restringe la recuperación de documentos individuales a documentos públicos o documentos que haya creado el usuario. - La regla
list
aplica las mismas restricciones queget
, pero en las consultas. También verifica el límite de consultas y, luego, rechaza cualquier consulta sin límite o con uno superior a 10. - El conjunto de reglas define una función
authorOrPublished()
para evitar la duplicación de código.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Returns `true` if the requested story is 'published'
// or the user authored the story
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
// Deny any query not limited to 10 or fewer documents
// Anyone can query published stories
// Authors can query their unpublished stories
allow list: if request.query.limit <= 10 &&
authorOrPublished();
// Anyone can retrieve a published story
// Only a story's author can retrieve an unpublished story
allow get: if authorOrPublished();
// Only a story's author can write to a story
allow write: if request.auth.uid == resource.data.author;
}
}
}
Consultas de grupos de colecciones y reglas de seguridad
De forma predeterminada, las consultas se enfocan a una sola colección y solo obtienen resultados de ella. Con las consultas de grupos de colecciones, puedes obtener resultados de un grupo de colecciones que contenga todas las colecciones que tengan el mismo ID. En esta sección, se describe cómo proteger las consultas de grupos de colecciones con reglas de seguridad.
Protege y consulta documentos según grupos de colecciones
En las reglas de seguridad, debes permitir explícitamente las consultas de grupos de colecciones. Para ello, escribe una regla específica para estos grupos:
- Asegúrate de que
rules_version = '2';
sea la primera línea del conjunto de reglas. Las consultas de grupos de colecciones requieren el nuevo comodín recursivo{name=**}
correspondiente a la versión 2 de las reglas de seguridad. - Escribe una regla para el grupo de colecciones con
match /{path=**}/[COLLECTION_ID]/{doc}
.
Por ejemplo, considera un foro organizado en documentos forum
que contienen subcolecciones de posts
:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
En esta aplicación, permitimos que los propietarios puedan editar sus publicaciones y que los usuarios autenticados puedan leerlas:
service cloud.firestore {
match /databases/{database}/documents {
match /forums/{forumid}/posts/{post} {
// Only authenticated users can read
allow read: if request.auth != null;
// Only the post author can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Cualquier usuario autenticado puede recuperar las publicaciones de cualquier foro:
db.collection("forums/technology/posts").get()
¿Pero qué sucede si deseas mostrar al usuario actual las publicaciones que realizó en todos los foros?
Puedes usar una consulta de grupo de colecciones para obtener resultados de todas las colecciones de posts
:
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
En las reglas de seguridad, debes permitir esta consulta con una regla read o list para el grupo de colecciones posts
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Authenticated users can query the posts collection group
// Applies to collection queries, collection group queries, and
// single document retrievals
match /{path=**}/posts/{post} {
allow read: if request.auth != null;
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Sin embargo, ten en cuenta que estas reglas se aplicarán a todas las colecciones que tengan el ID posts
, independientemente de la jerarquía. Por ejemplo, estas reglas se aplican a todas estas colecciones de posts
:
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
Protege consultas de grupos de colecciones según un campo
Al igual que las consultas de una sola colección, las consultas de grupos de colecciones también deben cumplir con las restricciones establecidas en las reglas de seguridad. Por ejemplo, podemos agregar un campo published
a cada publicación del foro, como hicimos en el ejemplo anterior de stories
:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
Luego, podemos escribir reglas para el grupo de colecciones de posts
según el estado
published
y el author
de la publicación:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Returns `true` if the requested post is 'published'
// or the user authored the post
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
match /{path=**}/posts/{post} {
// Anyone can query published posts
// Authors can query their unpublished posts
allow list: if authorOrPublished();
// Anyone can retrieve a published post
// Authors can retrieve an unpublished post
allow get: if authorOrPublished();
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth.uid == resource.data.author;
}
}
}
Con estas reglas, los clientes web, de Apple y Android pueden realizar las siguientes consultas:
Cualquiera puede obtener las publicaciones de un foro:
db.collection("forums/technology/posts").where('published', '==', true).get()
Cualquier usuario puede recuperar las publicaciones que realizó un autor en todos los foros:
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
Los autores pueden obtener todo lo que publicaron, o no, en todos los foros:
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
Protege y consulta documentos en función de un grupo de colecciones y la ruta del documento
En algunos casos, es posible que desees restringir las consultas de grupos de colecciones en función de la ruta del documento. Para crear estas restricciones, puedes usar las mismas técnicas para proteger y consultar documentos en función de un campo.
Imagina una aplicación que realiza un seguimiento de las transacciones de cada usuario entre varios intercambios de acciones y criptomonedas:
/users/{userid}/exchange/{exchangeid}/transactions/{transaction}
{
amount: 100,
exchange: 'some_exchange_name',
timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
user: "some_auth_id",
}
Observa el campo user
. Si bien conocemos al propietario del documento transaction
gracias a su ruta, duplicamos esta información en cada documento transaction
porque nos permite realizar estas dos acciones:
Escribir consultas de grupos de colecciones que estén restringidas a documentos que incluyan un
/users/{userid}
específico en su ruta: Por ejemplo:var user = firebase.auth().currentUser; // Return current user's last five transactions across all exchanges db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
Aplicar esta restricción a todas las consultas del grupo de colecciones de
transactions
para que un usuario no pueda recuperar los documentos detransaction
de otro usuario
Aplicamos esta restricción a nuestras reglas de seguridad y, además, incluimos la validación de datos para el campo user
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{path=**}/transactions/{transaction} {
// Authenticated users can retrieve only their own transactions
allow read: if resource.data.user == request.auth.uid;
}
match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
// Authenticated users can write to their own transactions subcollections
// Writes must populate the user field with the correct auth id
allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
}
}
}
Próximos pasos
- Para obtener un ejemplo más detallado de control de acceso basado en funciones, consulta cómo proteger el acceso a datos para usuarios y grupos.
- Consulta la referencia de las reglas de seguridad.