Reglas de seguridad en Cloud Firestore
Presentación
Firebase Auth
Cloud Firestore
Cloud Storage
Cloud Functions
Curso Firebase para Web se encuentra en desarrollo. Vuelve pronto para encontrar nuevas lecciones.
Enlaces a la documentación
- https://firebase.google.com/docs/firestore/security/get-started#testing_rules
- https://firebase.google.com/docs/auth/admin/custom-claims
- https://firebase.google.com/docs/firestore/security/rules-structure#version-2
- https://firebase.google.com/docs/firestore/security/rules-conditions
- La clave aquí es que vamos a definir una serie de reglas de acceso a cada nodo (colección o documento) de nuestra base de datos en Cloud Firestore.
- Todas las reglas consisten en bloques
match
, los cuales identifican documentos en tu base de datos y permiten expresiones que controlan el acceso a los mismos. - Vamos a examinar las reglas actuales y examinos cada parte.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if
request.time < timestamp.date(2021, 10, 2);
}
}
}
rules_version = '2'
nos indica que estamos usando la nueva versión de las reglas. Si has usado Realtime Database sabrás que las anteriores eran un objeto JSON.service cloud.firestore
define el servicio de la plataforma Firebase que estamos utilizando.match /databases/{database}/documents
define una variable o placeholder llamadodatabase
que apunta a la base de datos del proyecto actual.match /{document=**}
crea una nueva regla usando un recursive wildcard que indica todos los documentos en todas las colecciones y subcolecciones.allow read, write: if request.time < timestamp.date(2021, 10, 2)
permite lectura y escritura a cualquier usuario que se conecte antes del 10 del 2 de 2021.- Como ves hay diferentes partes que se combinan para crear las reglas de seguridad. Vamos a aprender un poco más sobre ellas.
Selectores de patrón
- Comienzan con la palabra clave
match
y siguen con un patrón que debe coincidir con la estructura de tu base de datos. - Todas las sentencias
match
debe apuntar a documentos, no colecciones. - Por ejemplo, si queremos alcanzar las subcolecciones
messages
de los documentos de la colecciónrooms
, podríamos utilizar:
match /rooms/{room}/messages/{message} {
// Selecciona todos los documentos de la colección messages
}
- También se pueden anidar los bloques
match
para hacerlo más legible o para indicar diferentes reglas de acceso ya que las reglas solo afectan al segmento al que se tiene acceso, no a posibles sub-colecciones (a no ser que se indique lo contrario).
match /rooms/{room} {
// Selecciona todos los documentos de la colección rooms
match /messages/{message} {
// Selecciona todos los documentos de la colección messages
}
}
- También podemos utilizar las variables y los recursive wildcards para seleccionar todos los documentos de posibles sub-colecciones anidadas.
match /users/{user} {
// Selecciona todos los documentos de la colección users
}
match /users/{user=**} {
// Selecciona todos los documentos de la colección users y los documentos de todas sus posibles sub-colecciones
}
// Selecciona todos los documentos de todas las sub-colecciones messages (collection group)
match /{path=**}/messages/{message} {
allow read, write: if request.auth != null;
}
Permisos
- Antes hemos visto como
allow read, write
permite al usuario ejercer los dos permisos esenciales: leer y escribir. Pero podemos ir más allá. - El permiso
read
se compone deget
, para leer un único documento ylist
para leer los documentos de una colección/sub-colección o hacer consultas sobre ella. Si otorgamos permisoread
estamos otorgando de forma implícitaget
ylist
. - El permiso
write
se compone decreate
para crear documentos en colecciones,update
para para actualizarlos ydelete
para eliminarlos. Igual que conread
, si otorgamos permisowrite
estamos concendiendocreate
,update
ydelete
.
Reglas básicas
- Teniendo en cuenta los selectores de patrón y los permisos, podemos comenzar a componer alguna relga muy básica.
// Nadie puede escribir en el documento con ID "juanwmedia" de la colección users
match /users/juanwmedia {
allow read;
allow write: if false;
}
// El secreto se mantiene secreto
match /users/juanwmedia/secrets/the_meaning_of_life {
allow read, write: if false;
}
- Sin embargo son reglas estáticas y poco útiles. En la mayoría de proyectos necesitamos algo más versátil. Como por ejemplo, condiciones.
Condiciones
- El mayoría de reglas debemos comprobar alguna condición o el valor de alguna variable. Para ello podemos usar la estructura de control
if
dentro de nuestras reglas.
match /users/{userID}/{docs=**} {
// Permite solo al usuario autentificado operar sobre su perfil
allow read, write: if request.auth.uid == userID;
}
match /rooms/adminRoom/{messages=**} {
// Acceder a la sala de administración en base a los custom claims
allow read, write: if request.auth.token.admin == true;
}
match /{path=**} {
// Contenido sólo para usuarios autentificados
allow read, write: if request.auth != null;
}
- Como ves estamos haciendo uso del objeto
request
que nos trae información sobre la petición que está siendo procesada por las reglas. La propiedad derequest
que más se suele utilizar esauth
, donde se nos indica un montón de información sobre la autentificación del usuario en la que podemos operar.
{
"auth": {
"uid": "my-unique-user-id",
"token": {
"some-custom-claim": true,
"email": "me@email.is",
"email_verified": false,
"phone_number": null,
"name": "Name",
}
},
...
}
- Otro objeto al que tenemos acceso es
resource
(no confundir conrequest.resource
), este hace referencia al documento que estamos intentando acceder. Enresource.data
tenemos un objeto con todos las propiedades y los valores. Teniendo en cuenta esto podemos comenzar a crear reglas de validación en base al contenido.
match /rooms/{room} {
// Permitir salas de conversación privadas
allow read: if resource.data.public == true;
}
- También podemos usar los dos
resources
(request.resource
yresource.data
) para comparar valores y validar en base a ello.
match /rooms/{room} {
// Permite actualizar sólo la descripcín, no el nombre
allow update: if request.resource.data.description is string &&
request.resource.data.description.size() > 10 &&
request.resource.data.description.size() < 160 &&
request.resource.data.name == resource.data.name;
}
Acceder a otros documentos
- Con los métodos
get()
yexists()
podemos evaluar las peticiones en base a la presencial de otros documentos en la base de datos.
match /rooms/{room} {
// Permitir eliminar salas de conversación si el usuario está autentificado y es administrador
allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
}
El primer headless CMS tanto para desarrolladores como para marketers. Pruébalo gratis.
¿Tienes alguna pregunta sobre esta lección de Curso Firebase para Web?
Resuelve todas tus dudas sobre Firebase en la Comunidad de Escuela Vue: un lugar donde participar, aprender y ayudar. ¡Te esperamos!.
Tras el registro (si no lo has hecho ya) serás redirigido/a al canal adecuado en la Comunidad.