Como gravar condições para regras de segurança

Este guia foi desenvolvido com base no guia sobre como estruturar regras de segurança. O objetivo dele é mostrar como adicionar condições às suas regras de segurança do Cloud Firestore. Se você não estiver familiarizado com os princípios básicos das regras de segurança do Cloud Firestore, consulte o guia de primeiros passos.

O elemento principal das regras de segurança do Cloud Firestore é a condição. Uma condição é uma expressão booleana que determina se uma operação específica deve ser permitida ou negada. Use regras de segurança para criar condições que verifiquem a autenticação do usuário, validem dados recebidos ou acessem outras partes do seu banco de dados.

Autenticação

Um dos padrões de regras de segurança mais comuns é o controle do acesso com base no estado de autenticação do usuário. Por exemplo, seu aplicativo pode permitir que apenas usuários conectados gravem 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.uid != null;
    }
  }
}

Outro padrão comum é garantir que os usuários possam ler e gravar apenas os próprios dados:

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

Se o aplicativo usar o Firebase Authentication, a variável request.auth conterá as informações de autenticação para o cliente que solicita dados. Para mais informações sobre request.auth, consulte a documentação de referência.

Validação de dados

Muitos aplicativos armazenam informações de controle de acesso como campos em documentos no banco de dados. As regras de segurança do Cloud Firestore podem permitir ou negar acesso dinamicamente com base nos dados do 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';
    }
  }
}

A variável resource refere-se ao documento solicitado, 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 gravar dados, pode ser necessário comparar os dados recebidos aos dados existentes. Nesse caso, se o conjunto de regras permitir a gravação pendente, a variável request.resource conterá o estado futuro do documento. Para operações update que apenas modificam um subconjunto dos campos do documento, a variável request.resource conterá o estado do documento pendente após a operação. É possível verificar os valores do campo 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;
    }
  }
}

Acesso a outros documentos

Por meio das funções get() e exists(), suas regras de segurança podem avaliar solicitações recebidas em relação a outros documentos no banco de dados. As funções get() e exists() esperam caminhos de documentos totalmente especificados. Ao usar variáveis para construir caminhos para as funções get() e exists(), será necessário escapá-las usando a sintaxe $(variable).

No exemplo abaixo, a variável database é capturada pela instrução de correspondência 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 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 gravações, é possível usar a função getAfter() para acessar o estado de um documento após a conclusão de uma transação ou lote de gravações, mas antes da confirmação da transação ou do lote. Como get(), a função getAfter() pega um caminho de documento totalmente especificado. Você pode usar getAfter() para definir conjuntos de gravações que precisam ocorrer juntos como uma transação ou lote.

Funções personalizadas

À medida que suas regras de segurança se tornam mais complexas, recomendamos reunir conjuntos de condições em funções que você pode reutilizar no seu conjunto de regras. As regras de segurança dão suporte a funções personalizadas. A sintaxe para funções personalizadas é um pouco parecida com JavaScript, mas as funções de regras de segurança são escritas em uma linguagem de programação específica de domínio com algumas limitações importantes:

  • As funções podem conter apenas uma única instrução return. Elas não podem conter nenhuma lógica adicional. Por exemplo, as funções não podem criar variáveis intermediárias, executar loops ou chamar serviços externos.
  • As funções podem acessar automaticamente funções e variáveis do escopo em que são definidas. Por exemplo, uma função definida no escopo service cloud.firestore tem acesso à variável resource e a funções integradas, como get() e exists().
  • As funções podem chamar outras funções, mas podem não ser executadas novamente. A profundidade total da pilha de chamadas é limitada a 10.

Uma função é definida com a palavra-chave function e leva zero ou mais argumentos. Por exemplo, pode ser necessário combinar os dois tipos de condições usadas nos exemplos acima em uma ú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();
    }
  }
}

O uso de funções em suas regras de segurança as torna mais fáceis de atualizar à medida que a complexidade delas aumenta. Para mais informações sobre as funções definidas pelo desenvolvedor, consulte a documentação de referência.

Próximas etapas

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.