Catch up on highlights from Firebase at Google I/O 2023. Learn more

Conditions d'écriture des règles de sécurité Cloud Firestore

Ce guide s'appuie sur le guide de structuration des règles de sécurité pour montrer comment ajouter des conditions à vos règles de sécurité Cloud Firestore. Si vous n'êtes pas familiarisé avec les bases des règles de sécurité Cloud Firestore, consultez le guide de démarrage .

Le bloc de construction principal des règles de sécurité Cloud Firestore est la condition. Une condition est une expression booléenne qui détermine si une opération particulière doit être autorisée ou refusée. Utilisez des règles de sécurité pour écrire des conditions qui vérifient l'authentification des utilisateurs, valident les données entrantes ou même accèdent à d'autres parties de votre base de données.

Authentification

L'un des modèles de règles de sécurité les plus courants consiste à contrôler l'accès en fonction de l'état d'authentification de l'utilisateur. Par exemple, votre application peut vouloir autoriser uniquement les utilisateurs connectés à écrire des données :

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

Un autre modèle courant consiste à s'assurer que les utilisateurs ne peuvent lire et écrire que leurs propres données :

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

Si votre application utilise Firebase Authentication ou Google Cloud Identity Platform , la variable request.auth contient les informations d'authentification pour le client demandant des données. Pour plus d'informations sur request.auth , consultez la documentation de référence .

La validation des données

De nombreuses applications stockent les informations de contrôle d'accès sous forme de champs sur les documents de la base de données. Les règles de sécurité Cloud Firestore peuvent autoriser ou refuser dynamiquement l'accès en fonction des données du document :

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

La variable resource fait référence au document demandé et resource.data est une carte de tous les champs et valeurs stockés dans le document. Pour plus d'informations sur la variable resource , consultez la documentation de référence .

Lors de l'écriture de données, vous souhaiterez peut-être comparer les données entrantes aux données existantes. Dans ce cas, si votre ensemble de règles autorise l'écriture en attente, la variable request.resource contient l'état futur du document. Pour les opérations update qui ne modifient qu'un sous-ensemble des champs du document, la variable request.resource contiendra l'état du document en attente après l'opération. Vous pouvez vérifier les valeurs des champs dans request.resource pour éviter les mises à jour de données indésirables ou incohérentes :

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

Accéder à d'autres documents

À l'aide des fonctions get() et exists() , vos règles de sécurité peuvent évaluer les requêtes entrantes par rapport à d'autres documents de la base de données. Les fonctions get() et exists() attendent toutes deux des chemins de document entièrement spécifiés. Lorsque vous utilisez des variables pour construire des chemins pour get() et exists() , vous devez explicitement échapper les variables à l'aide de la syntaxe $(variable) .

Dans l'exemple ci-dessous, la variable database est capturée par l'instruction match /databases/{database}/documents et utilisée pour former le chemin :

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

Pour les écritures, vous pouvez utiliser la fonction getAfter() pour accéder à l'état d'un document après la fin d'une transaction ou d'un lot d'écritures, mais avant la validation de la transaction ou du lot. Comme get() , la fonction getAfter() prend un chemin de document entièrement spécifié. Vous pouvez utiliser getAfter() pour définir des ensembles d'écritures qui doivent avoir lieu ensemble en tant que transaction ou lot.

Accéder aux limites d'appels

Le nombre d'appels d'accès aux documents par évaluation d'ensemble de règles est limité :

  • 10 pour les demandes de document unique et les demandes de requête.
  • 20 pour les lectures de plusieurs documents, les transactions et les écritures par lots. La limite précédente de 10 s'applique également à chaque opération.

    Par exemple, imaginez que vous créez une demande d'écriture par lot avec 3 opérations d'écriture et que vos règles de sécurité utilisent 2 appels d'accès aux documents pour valider chaque écriture. Dans ce cas, chaque écriture utilise 2 de ses 10 appels d'accès et la demande d'écriture groupée utilise 6 de ses 20 appels d'accès.

Le dépassement de l'une ou l'autre des limites entraîne une erreur d'autorisation refusée. Certains appels d'accès aux documents peuvent être mis en cache et les appels mis en cache ne sont pas pris en compte dans les limites.

Pour une explication détaillée de la manière dont ces limites affectent les transactions et les écritures par lots, consultez le guide de sécurisation des opérations atomiques .

Accéder aux appels et aux tarifs

L'utilisation de ces fonctions exécute une opération de lecture dans votre base de données, ce qui signifie que vous serez facturé pour la lecture des documents même si vos règles rejettent la demande. Voir Tarification de Cloud Firestore pour des informations de facturation plus spécifiques.

Fonctions personnalisées

Au fur et à mesure que vos règles de sécurité deviennent plus complexes, vous souhaiterez peut-être encapsuler des ensembles de conditions dans des fonctions que vous pourrez réutiliser dans votre ensemble de règles. Les règles de sécurité prennent en charge les fonctions personnalisées. La syntaxe des fonctions personnalisées ressemble un peu à JavaScript, mais les fonctions de règles de sécurité sont écrites dans un langage spécifique à un domaine qui présente des limitations importantes :

  • Les fonctions ne peuvent contenir qu'une seule instruction return . Ils ne peuvent contenir aucune logique supplémentaire. Par exemple, ils ne peuvent pas exécuter de boucles ou appeler des services externes.
  • Les fonctions peuvent accéder automatiquement aux fonctions et aux variables à partir de la portée dans laquelle elles sont définies. Par exemple, une fonction définie dans la portée du service cloud.firestore a accès à la variable resource et aux fonctions intégrées telles que get() et exists() .
  • Les fonctions peuvent appeler d'autres fonctions mais ne peuvent pas être récursives. La profondeur totale de la pile d'appels est limitée à 10.
  • Dans la version v2 des règles, les fonctions peuvent définir des variables à l'aide du mot clé let . Les fonctions peuvent avoir jusqu'à 10 liaisons let, mais doivent se terminer par une instruction return.

Une fonction est définie avec le mot-clé function et prend zéro ou plusieurs arguments. Par exemple, vous pouvez combiner les deux types de conditions utilisées dans les exemples ci-dessus en une seule fonction :

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

L'utilisation de fonctions dans vos règles de sécurité les rend plus maintenables à mesure que la complexité de vos règles augmente.

Les règles ne sont pas des filtres

Une fois que vous avez sécurisé vos données et commencé à écrire des requêtes, gardez à l'esprit que les règles de sécurité ne sont pas des filtres. Vous ne pouvez pas écrire une requête pour tous les documents d'une collection et vous attendre à ce que Cloud Firestore renvoie uniquement les documents auxquels le client actuel est autorisé à accéder.

Prenons par exemple la règle de sécurité suivante :

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

Refusé : cette règle rejette la requête suivante car le jeu de résultats peut inclure des documents dont visibility n'est pas public :

la toile
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

Autorisé : cette règle autorise la requête suivante, car la clause where("visibility", "==", "public") garantit que le jeu de résultats satisfait la condition de la règle :

la toile
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

Les règles de sécurité Cloud Firestore évaluent chaque requête par rapport à son résultat potentiel et échouent la requête si elle peut renvoyer un document que le client n'est pas autorisé à lire. Les requêtes doivent respecter les contraintes définies par vos règles de sécurité. Pour plus d'informations sur les règles de sécurité et les requêtes, consultez l'article interroger des données en toute sécurité .

Prochaines étapes