Realtime Database Logo from firebase.google.com/products

Como funcionam as regras de segurança do Firebase na Realtime Database?

Parte 2 — Validações e Índices

Rosário Pereira Fernandes
Published in
4 min readMar 8, 2018

--

Depois de termos aprendido a definir regras para leitura e escrita de dados na nossa Realtime Database no artigo anterior, neste artigo vamos aprender como validar esses dados e como definir índices para ordenar os mesmos.

Tal como sabemos, as regras de segurança são uma árvore JSON composta por nós, chaves e valores (tal como qualquer outra árvore JSON), e já conhecemos 2 das 4 chaves que fazem parte das regras de segurança: “.read” e “.write”. Por isso, neste artigo vamos olhar para as outras 2 que faltavam: “.validate” e “.indexOn”.

Validação de dados (.validate)

A validação de dados é importante para garantir que tenhamos uma base de dados consistente.(Lazaro Anibal que o diga*). Afinal de contas, se a nossa base de dados é apenas uma árvore JSON, não podemos permitir que um mesmo campo tenha o valor de um tipo num nó e outro tipo noutro nó (sendo que ambos fazem parte da mesma lista):

“utilizadores”:{
“uid1”:{
“nome”:”Rosário”,
“visto”:”201802282030"
},
“uid2”:{
“nome”:”Paulo”,
“visto”:{
“dia”:”07/03/2017",
“hora”:”20h”
}
}
}

Nem permitir que na mesma lista exista um nó que tenha um campo (importante) e outro que não tenha:

"utilizadores":{
"uid1":{
"nome":"Rosário",
"idade":20,
"email":"rosariofernandes51@gmail.com",
"visto":"201802282030"
},
"uid2":{
"nome":"Paulo",
"idade":22,
"visto":"201803072000"
}
}

É para evitar as situações mostradas acima que existe a regra “.validate”. Podemos evitar o problema do exemplo 1 utilizando as regras a seguir:

"utilizadores":{
"$uid":{
".write":"auth!=null && $uid == auth.uid",
".read":"auth!=null && $uid == auth.uid",
".validate":"newData.child('visto').isString()"
}
}

Neste exemplo, newData.child(‘visto’).isString() vai garantir que o atributo visto que seja sempre uma String. Se não for enviado uma String, então o utilizador não será guardado na database.

E para evitar o problema do exemplo 2, podemos utilizar as regras:

"utilizadores":{
"$uid":{
".write":"auth!=null && $uid == auth.uid",
".read":"auth!=null && $uid == auth.uid", .validate":"newData.hasChildren(['nome','idade','visto','email'])"
}
}

Esta regra obriga que o nosso objeto utilizador tenha os 4 campos(nome, idade, visto e email) para que possa ser armazenado na database.

Índices (.indexOn)

Se você já utilizou o método orderByChild() quando criava uma Query para ler dados do Firebase, provavelmente já se deparou com o aviso(warning):

Using an unspecified index. Consider adding “.indexOn” … to your security and Firebase Database rules for better performance

Isso acontece porque, por padrão, o Firebase ordena os dados de acordo com as suas chaves. Se você quiser ordenar por um outro atributo/child, você deve especificar qual é esse atributo/child para que a Realtime Database possa ordenar os dados assim na própria base de dados, antes de enviar para a sua aplicação (Android, Web ou iOS). Isso porque ordenar os dados na aplicação pode ter custos elevados em termos de performance (uso de memória e bateria).

Por exemplo, se quisermos ordenar os nossos utilizadores pelo nome, utilizamos a regra .indexOn com o nome:

"utilizadores":{
"$uid":{
".write":"auth!=null && $uid == auth.uid",
".read":"auth!=null && $uid == auth.uid",
".indexOn":"nome"
}
}

Se houver uma outra tela na nossa aplicação onde precisamos de ordenar os utilizadores com base em um outro atributo (como o email por exemplo). Podemos especificar isso passando um array para o indexOn:

"utilizadores":{
"$uid":{
".write":"auth!=null && $uid == auth.uid",
".read":"auth!=null && $uid == auth.uid",
".indexOn": ["nome","email"]
}
}

Bónus: Regras baseadas em queries

A documentação especifica claramente que: “Regras não são filtros”. O que significa que não tem como criar regras para mostrar apenas um certo grupo de dados e outros não (filtragem básica).

Mas em Janeiro de 2018, o Firebase anunciou as regras baseadas em queries. Estas regras servem para limitar os tipos de queries que podem ser executadas na nossa base de dados. E de certa forma, fazem algo parecido com uma filtragem que antes não era possível fazer.

Por exemplo, supondo que temos 2 tipos de utilizadores na database. Queremos permitir que um dos utilizadores possa ler todo o nó mensagens, enquanto o outro só lê as primeiras 25 mensagens marcadas como activas.

Para tal, podemos limitar os tipos de queries que podem ser executados nesse nó:

{
"mensagens":{
".read":"query.orderByKey || (query.orderByChild == 'activa' && query.limitToFirst == 25)",
".write":"auth!=null"
}
}

Com estas regras, só podem ser executadas as seguintes queries na database:

getReference(“mensagens”).orderByKey(); //egetReference(“mensagens”).orderByChild(“activa”).limitToFirst(10);

Qualquer outra query resultará em PERMISSION_DENIED.

E esta foi a última regra que eu tinha para mostrar nesta série de Regras de Segurança da Realtime Database. Agora você pode manter a sua base de dados segura. No próximo artigo explicarei como utilizar o Simulador de Regras da Realtime Database.

Caso tenha alguma dúvida ou sugestão, pode me contactar pelo email rosariofernandes51@gmail.com ou pelo Telegram. Será um prazer conversar com você. 🙂

*- Lazaro Anibal é um amigo que teve um problema com as regras de validate e inspirou-me a escrever este post. Thanks dude 😃

--

--

Rosário Pereira Fernandes

Firebase DevRel Engineer at Google … Views and Opinions are my own.