Cette page a été traduite par l'API Cloud Translation.
Switch to English

Tester les règles de sécurité

Lors du développement de votre application, vous voudrez peut-être verrouiller l'accès à votre base de données Firestore. Toutefois, avant le lancement, vous aurez besoin de règles de sécurité Firestore plus nuancées. Avec l'émulateur Firestore, vous pouvez écrire des tests unitaires qui vérifient le comportement de vos règles de sécurité Firestore.

Démarrage rapide

Pour quelques scénarios de test basiques avec des règles simples, essayez le Guide de démarrage rapide avec JavaScript ou le Guide de démarrage rapide avec PostScript.

Comprendre les règles de sécurité Firestore

Installez Firebase Authentication et les règles de sécurité Firestore pour l'authentification, l'autorisation et la validation des données sans serveur lorsque vous utilisez les bibliothèques clientes mobiles et Web.

Les règles de sécurité de Firestore comprennent deux éléments :

  1. Une instruction match qui identifie les documents dans votre base de données.
  2. Une expression allow qui contrôle l'accès à ces documents.

Firebase Authentication vérifie les identifiants des utilisateurs et constitue la base des systèmes d'accès basés sur l'utilisateur et sur les rôles.

Chaque requête de base de données provenant d'une bibliothèque cliente mobile/Web Firestore est évaluée par rapport à vos règles de sécurité avant toute lecture ou écriture de données. Si les règles refusent l'accès à l'un des chemins de document spécifiés, l'intégralité de la requête échoue.

Obtenez plus d'informations sur les règles de sécurité Firestore en consultant la section Premiers pas avec les règles de sécurité Firestore.

Installer l'émulateur

Pour installer l'émulateur Firestore, utilisez la CLI Firebase et exécutez la commande ci-dessous :

firebase setup:emulators:firestore

Exécuter l'émulateur

Démarrez l'émulateur à l'aide de la commande suivante. L'émulateur s'exécute jusqu'à ce que vous arrêtiez le processus :

firebase emulators:start --only firestore

Dans la plupart des cas, vous souhaitez démarrer l'émulateur, exécuter une suite de tests, puis arrêter l'émulateur après l'exécution des tests. Pour ce faire, utilisez la commande emulators:exec :

firebase emulators:exec --only firestore "./my-test-script.sh"

Au démarrage, l'émulateur tente de s'exécuter sur un port par défaut (8080). Vous pouvez modifier le port de l'émulateur en modifiant la section "emulators" de votre fichier firebase.json :

{
  // ...
  "emulators": {
    "firestore": {
      "port": "YOUR_PORT"
    }
  }
}

Avant d'exécuter l'émulateur

Avant de commencer à utiliser l'émulateur, gardez à l'esprit les points suivants :

  • L'émulateur charge initialement les règles spécifiées dans le champ firestore.rules de votre fichier firebase.json. Il attend le nom d'un fichier local contenant vos règles de sécurité Firestore et applique ces règles à tous les projets. Si vous ne fournissez pas le chemin d'accès au fichier local ou utilisez la méthode loadFirestoreRules comme décrit ci-dessous, l'émulateur traite tous les projets comme ayant des règles ouvertes.
  • Bien que la plupart des SDK Firebase fonctionnent directement avec les émulateurs, seule la bibliothèque @firebase/rules-unit-testing accepte une simulation d'option auth dans les règles de sécurité, ce qui facilite considérablement les tests unitaires. En outre, la bibliothèque est compatible avec quelques fonctionnalités spécifiques à l'émulateur, telles que la suppression de toutes les données, comme indiqué ci-dessous.
  • Les émulateurs acceptent également les jetons d'authentification Firebase de production fournis par les SDK du client et évaluent les règles en conséquence, ce qui permet de connecter directement votre application aux émulateurs dans les tests d'intégration et les tests manuels.

Exécuter des tests locaux

initializeTestApp({ projectId: string, auth: Object }) => FirebaseApp

Cette méthode renvoie une application Firebase initialisée correspondant à l'ID du projet et à la variable d'authentification spécifiés dans les options. Utilisez-la pour créer une application authentifiée en tant qu'utilisateur spécifique, à utiliser dans les tests.

firebase.initializeTestApp({
  projectId: "my-test-project",
  auth: { uid: "alice", email: "alice@example.com" }
});

initializeAdminApp({ projectId: string }) => FirebaseApp

Cette méthode renvoie une application Firebase d'administration initialisée. Cette application contourne les règles de sécurité lors de la lecture et de l'écriture. Utilisez-la pour créer une application authentifiée en tant qu'administrateur, afin de définir l'état des tests.

firebase.initializeAdminApp({ projectId: "my-test-project" });
    

apps() => [FirebaseApp] Cette méthode renvoie toutes les applications de test et d'administration actuellement initialisées. Utilisez-la pour nettoyer les applications pendant ou après les tests.

Promise.all(firebase.apps().map(app => app.delete()))

loadFirestoreRules({ projectId: string, rules: Object }) => Promise

Cette méthode envoie des règles à une base de données exécutée localement. Elle prend un objet qui spécifie les règles sous forme de chaîne. Utilisez cette méthode pour définir les règles de votre base de données.

firebase.loadFirestoreRules({
  projectId: "my-test-project",
  rules: fs.readFileSync("/path/to/firestore.rules", "utf8")
});
    

assertFails(pr: Promise) => Promise

Cette méthode renvoie une promesse qui est refusée si l'entrée réussit ou qui réussit si l'entrée est rejetée. Utilisez cette méthode pour indiquer si la lecture ou l'écriture d'une base de données échoue.

firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
    

assertSucceeds(pr: Promise) => Promise

Cette méthode renvoie une promesse qui réussit si l'entrée réussit et qui est rejetée si l'entrée est rejetée. Utilisez cette méthode pour indiquer si la lecture ou l'écriture de la base de données a réussi.

firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
    

clearFirestoreData({ projectId: string }) => Promise

Cette méthode efface toutes les données associées à un projet particulier dans l'instance Firestore exécutée localement. Utilisez cette méthode pour effectuer un nettoyage après les tests.

firebase.clearFirestoreData({
  projectId: "my-test-project"
});
   

Générer des rapports de test

Après avoir exécuté une suite de tests, vous pouvez accéder à des rapports sur la couverture des tests qui montrent comment chacune de vos règles de sécurité a été évaluée.

Pour obtenir les rapports, interrogez un point de terminaison exposé sur l'émulateur pendant son exécution. Pour une version adaptée aux navigateurs, utilisez l'URL suivante :

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html

Cette opération casse vos règles en expressions et sous-expressions que vous pouvez survoler avec la souris pour obtenir plus d'informations, y compris le nombre d'évaluations et les valeurs renvoyées. Pour la version JSON brute de ces données, incluez l'URL suivante dans votre requête :

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage

Différences entre l'émulateur et la production

  1. Vous n'avez pas besoin de créer explicitement un projet Firestore. L'émulateur crée automatiquement une instance à laquelle vous accédez.
  2. L'émulateur Firestore ne fonctionne pas avec le flux Firebase Authentication standard. Au lieu de cela, dans le SDK Firebase Test, nous avons fourni la méthode initializeTestApp() dans la bibliothèque rules-unit-testing, qui accepte un champ auth. Le handle Firebase créé à l'aide de cette méthode se comportera comme s'il était authentifié comme n'importe quelle entité que vous fournissez. Si vous transmettez la valeur null, il se comporte comme un utilisateur non authentifié (les règles auth != null échouent, par exemple).

Résoudre les problèmes connus

Lorsque vous utilisez l'émulateur Firestore, vous pouvez rencontrer les problèmes connus suivants. Suivez les conseils ci-dessous pour résoudre tout problème de comportement anormal. Ces notes sont rédigées avec le SDK Firebase Test à l'esprit, mais les approches générales s'appliquent à tous les SDK Firebase.

Comportement de test incohérent

Si vos tests réussissent et échouent occasionnellement, même si vous n'avez pas modifié les tests eux-mêmes, vous devrez peut-être vérifier qu'ils sont correctement séquencés. La plupart des interactions avec l'émulateur sont asynchrones. Par conséquent, vérifiez que tout le code asynchrone est correctement séquencé. Vous pouvez corriger le séquencement en enchaînant les promesses ou en utilisant généreusement la notation await.

Examinez notamment les opérations asynchrones suivantes :

  • Définition de règles de sécurité, avec par exemple firebase.loadFirestoreRules
  • Lecture et écriture de données, avec par exemple db.collection("users").doc("alice").get()
  • Assertions opérationnelles, y compris firebase.assertSucceeds et firebase.assertFails

Les tests ne s'affichent que la première fois que vous chargez l'émulateur.

L'émulateur est un programme avec état. Il stocke toutes les données écrites en mémoire, entraînant la perte de toutes les données à chaque arrêt de l'émulateur. Si vous exécutez plusieurs tests sur le même ID de projet, chaque test peut produire des données susceptibles d'influencer les tests ultérieurs. Pour contourner ce problème, vous pouvez utiliser l'une des méthodes suivantes :

  • Utilisez des ID de projet uniques pour chaque test. Notez que si vous optez pour cette méthode, vous devez appeler loadFirestoreRules dans le cadre de chaque test. Les règles ne sont chargées automatiquement que pour l'ID de projet par défaut.
  • Restructurez vos tests afin qu'ils n'interagissent pas avec les données précédemment écrites (par exemple, utilisez une collection différente pour chaque test).
  • Supprimez toutes les données écrites lors d'un test.

Configuration du test très complexe

Vous pouvez tester des scénarios que vos règles de sécurité Firestore n'autorisent pas. Par exemple, il est difficile de tester si des utilisateurs non authentifiés peuvent modifier des données, car vous ne pouvez pas modifier les données en tant qu'utilisateur non authentifié.

Si vos règles rendent la configuration de test complexe, essayez d'utiliser un client autorisé par l'administrateur pour contourner les règles. Vous pouvez le faire avec firebase.initializeAdminApp. Les lectures et les écritures effectuées par les clients autorisés par l'administrateur contournent les règles et ne déclenchent pas d'erreurs PERMISSION_DENIED.