Escreva condições para regras de segurança
Este guia baseia-se no guia estruturar regras de segurança para mostrar como adicionar condições às suas regras de segurança do Firestore. Se não estiver familiarizado com os princípios básicos das regras de segurança do Firestore, consulte o guia de introdução
A base das regras de segurança do Firestore é a condição. Uma condição é uma expressão booleana que determina se uma operação específica deve ser permitida ou recusada. Use regras de segurança para escrever condições que verificam a autenticação do utilizador, validam os dados recebidos ou até acedem a outras partes da sua base de dados.
Autenticação
Um dos padrões de regras de segurança mais comuns é o controlo do acesso com base no estado de autenticação do utilizador. Por exemplo, a sua app pode querer permitir que apenas os utilizadores com sessão iniciada escrevam dados:
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;
}
}
}
Outro padrão comum é garantir que os utilizadores só podem ler e escrever os seus próprios dados:
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;
}
}
}
Se a sua app usar o Firebase Authentication ou a Google Cloud Identity Platform, a variável request.auth
contém as informações de autenticação do cliente que pede dados.
Para mais informações sobre a request.auth
, consulte a documentação de referência.
Validação de dados
Muitas apps armazenam informações de controlo de acesso como campos em documentos na base de dados. As regras de segurança do Firestore podem permitir ou negar dinamicamente o acesso com base nos dados dos documentos:
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';
}
}
}
A variável resource
refere-se ao documento pedido e resource.data
é um mapa de todos os campos e valores armazenados no documento. Para mais
informações sobre a variável resource
, consulte a documentação
de referência.
Ao escrever dados, pode querer comparar os dados recebidos com os dados existentes.
Neste caso, se o conjunto de regras permitir a escrita pendente, a variável request.resource
contém o estado futuro do documento. Para operações update
que apenas
modificam um subconjunto dos campos do documento, a variável request.resource
contém o estado pendente do documento após a operação. Pode verificar os valores dos campos em request.resource
para evitar atualizações de dados indesejadas ou inconsistentes:
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;
}
}
}
Aceda a outros documentos
Com as funções get()
e exists()
, as suas regras de segurança podem avaliar
as solicitações recebidas em relação a outros documentos na base de dados. As funções get()
e exists()
esperam caminhos de documentos totalmente especificados. Quando usar variáveis para criar caminhos para get()
e exists()
, tem de usar explicitamente carateres de escape nas variáveis com a sintaxe $(variable)
.
No exemplo abaixo, a variável database
é capturada pela declaração match match /databases/{database}/documents
e usada para formar o caminho:
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;
}
}
}
Para escritas, pode usar a função getAfter()
para aceder ao estado de um documento após a conclusão de uma transação ou um lote de escritas, mas antes da confirmação da transação ou do lote. Tal como get()
, a função getAfter()
usa um caminho do documento totalmente especificado. Pode usar getAfter()
para definir conjuntos de gravações que têm de ocorrer em conjunto como uma transação ou um lote.
Limites de chamadas de acesso
Existe um limite de chamadas de acesso a documentos por avaliação do conjunto de regras:
- 10 para pedidos de documentos únicos e pedidos de consultas.
-
20 para leituras de vários documentos, transações e gravações em lote. O limite anterior de 10 também se aplica a cada operação.
Por exemplo, imagine que cria um pedido de gravação em lote com 3 operações de gravação e que as suas regras de segurança usam 2 chamadas de acesso a documentos para validar cada gravação. Neste caso, cada gravação usa 2 das suas 10 chamadas de acesso e o pedido de gravação em lote usa 6 das suas 20 chamadas de acesso.
Se exceder qualquer um dos limites, recebe um erro de acesso negado. Algumas chamadas de acesso a documentos podem ser colocadas em cache e as chamadas em cache não contam para os limites.
Para uma explicação detalhada de como estes limites afetam as transações e as gravações em lote, consulte o guia para proteger operações atómicas.
Aceda a chamadas e preços
A utilização destas funções executa uma operação de leitura na sua base de dados, o que significa que lhe é cobrada a leitura de documentos, mesmo que as suas regras rejeitem o pedido. Consulte os preços do Firestore para obter informações de faturação mais específicas.
Funções personalizadas
À medida que as regras de segurança se tornam mais complexas, pode querer incluir conjuntos de condições em funções que pode reutilizar no conjunto de regras. As regras de segurança suportam funções personalizadas. A sintaxe das funções personalizadas é um pouco semelhante à do JavaScript, mas as funções das regras de segurança são escritas numa linguagem específica do domínio que tem algumas limitações importantes:
- As funções só podem conter uma única declaração
return
. Não podem conter lógica adicional. Por exemplo, não podem executar ciclos nem chamar serviços externos. - As funções podem aceder automaticamente a funções e variáveis do âmbito
em que são definidas. Por exemplo, uma função definida no âmbito de
service cloud.firestore
tem acesso à variávelresource
e a funções integradas, comoget()
eexists()
. - As funções podem chamar outras funções, mas não podem ser recursivas. A profundidade total da pilha de chamadas está limitada a 10.
- Na versão
v2
das regras, as funções podem definir variáveis através da palavra-chavelet
. As funções podem ter até 10 associações let, mas têm de terminar com uma declaração de retorno.
Uma função é definida com a palavra-chave function
e usa zero ou mais argumentos. Por exemplo, pode querer combinar os dois tipos de condições usados nos exemplos acima numa única função:
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();
}
}
}
A utilização de funções nas regras de segurança torna-as mais fáceis de manter à medida que a complexidade das regras aumenta.
As regras não são filtros
Depois de proteger os seus dados e começar a escrever consultas, tenha em atenção que as regras de segurança não são filtros. Não pode escrever uma consulta para todos os documentos numa coleção e esperar que o Firestore devolva apenas os documentos aos quais o cliente atual tem autorização para aceder.
Por exemplo, considere a seguinte regra de segurança:
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';
}
}
}
Recusada: esta regra rejeita a seguinte consulta porque o conjunto de resultados pode incluir documentos em que visibility
não é public
:
Web
db.collection("cities").get() .then(function(querySnapshot) { querySnapshot.forEach(function(doc) { console.log(doc.id, " => ", doc.data()); }); });
Permitido: esta regra permite a seguinte consulta porque a cláusula where("visibility", "==", "public")
garante que o conjunto de resultados cumpre a condição da regra:
Web
db.collection("cities").where("visibility", "==", "public").get() .then(function(querySnapshot) { querySnapshot.forEach(function(doc) { console.log(doc.id, " => ", doc.data()); }); });
As regras de segurança do Firestore avaliam cada consulta em função do respetivo resultado potencial e rejeitam o pedido se puderem devolver um documento que o cliente não tem autorização para ler. As consultas têm de seguir as restrições definidas pelas suas regras de segurança. Para mais informações sobre regras de segurança e consultas, consulte o artigo sobre consultar dados de forma segura.
Passos seguintes
- Saiba como as regras de segurança afetam as suas consultas.
- Saiba como estruturar regras de segurança.
- Leia a referência das regras de segurança.