Menguji Aturan Keamanan Cloud Firestore

Saat membangun aplikasi, Anda mungkin ingin mengunci akses ke database Cloud Firestore. Namun, sebelum meluncurkan aplikasi, Anda memerlukan Cloud Firestore Security Rules yang lebih mendetail. Dengan emulator Cloud Firestore, selain membuat prototipe serta menguji fitur dan perilaku umum aplikasi, Anda dapat menulis pengujian unit yang memeriksa perilaku Cloud Firestore Security Rules.

Panduan memulai

Untuk beberapa kasus pengujian dasar dengan aturan sederhana, cobalah contoh panduan memulai.

Memahami Cloud Firestore Security Rules

Implementasikan Firebase Authentication dan Cloud Firestore Security Rules untuk autentikasi, otorisasi, dan validasi data tanpa server saat Anda menggunakan library klien seluler dan web.

Cloud Firestore Security Rules menyertakan dua bagian:

  1. Pernyataan match yang mengidentifikasi dokumen dalam database Anda.
  2. Ekspresi allow yang mengontrol akses ke dokumen tersebut.

Firebase Authentication memverifikasi kredensial pengguna dan memberikan landasan untuk sistem akses berbasis pengguna dan peran.

Sebelum membaca atau menulis data, setiap permintaan database dari library klien seluler/web Cloud Firestore dievaluasi terhadap aturan keamanan Anda. Jika aturan tersebut menolak akses ke salah satu jalur dokumen yang ditentukan, seluruh permintaan akan gagal.

Pelajari lebih lanjut Cloud Firestore Security Rules di Memulai Cloud Firestore Security Rules.

Menginstal emulator

Untuk menginstal emulator Cloud Firestore, gunakan Firebase CLI dan jalankan perintah di bawah ini:

firebase setup:emulators:firestore

Menjalankan emulator

Mulai dengan menginisialisasi project Firebase di direktori kerja Anda. Ini adalah langkah pertama yang umum saat menggunakan Firebase CLI.

firebase init

Mulai emulator menggunakan perintah berikut. Emulator akan berjalan hingga Anda menghentikan prosesnya:

firebase emulators:start --only firestore

Dalam sebagian besar kasus, sebaiknya Anda memulai emulator, menjalankan serangkaian pengujian, lalu menghentikan emulator setelah pengujian berjalan. Anda dapat melakukannya dengan mudah menggunakan perintah emulators:exec:

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

Ketika dimulai, emulator akan mencoba berjalan pada port default (8080). Anda dapat mengubah port emulator dengan mengubah bagian "emulators" pada file firebase.json Anda:

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

Sebelum menjalankan emulator

Sebelum Anda mulai menggunakan emulator, perhatikan hal-hal berikut:

  • Pertama, emulator akan memuat aturan yang ditentukan dalam kolom firestore.rules file firebase.json Anda. Emulator mengharapkan nama file lokal yang berisi Cloud Firestore Security Rules dan menerapkan aturan tersebut untuk semua project. Jika Anda tidak memberikan jalur file lokal atau menggunakan metode loadFirestoreRules seperti yang dijelaskan di bawah, emulator akan memperlakukan semua project dengan aturan terbuka.
  • Meskipun sebagian besar Firebase SDK bekerja dengan emulator secara langsung, hanya library @firebase/rules-unit-testing yang mendukung tiruan auth di Aturan Keamanan, sehingga pengujian unit menjadi lebih mudah. Selain itu, library ini mendukung beberapa fitur khusus emulator seperti menghapus semua data, seperti yang tercantum di bawah ini.
  • Emulator juga akan menerima token Firebase Auth produksi yang disediakan melalui SDK Klien dan mengevaluasi aturan yang sesuai, agar aplikasi Anda dapat terhubung langsung ke emulator dalam pengujian manual dan integrasi.

Menjalankan pengujian unit lokal

Menjalankan pengujian unit lokal dengan JavaScript SDK v9

Firebase mendistribusikan library pengujian unit Aturan Keamanan dengan JavaScript SDK versi 9 dan SDK versi 8. API library sangat berbeda. Kami merekomendasikan library pengujian v9, yang lebih sederhana dan memerlukan lebih sedikit penyiapan untuk terhubung ke emulator sehingga dapat dengan aman menghindari penggunaan resource produksi secara tidak sengaja. Untuk kompatibilitas mundur, kami terus menyediakan library pengujian v8.

Gunakan modul @firebase/rules-unit-testing untuk berinteraksi dengan emulator yang berjalan secara lokal. Jika Anda menemukan error ECONNREFUSED atau waktu tunggu, periksa kembali apakah emulator sedang berjalan atau tidak.

Kami sangat menyarankan penggunaan Node.js versi terbaru agar Anda dapat menggunakan notasi async/await. Hampir semua perilaku yang mungkin ingin Anda uji melibatkan fungsi asinkron, dan modul pengujian dirancang untuk digunakan dengan kode berbasis Promise.

Library Pengujian Unit Aturan v9 selalu mengetahui keberadaan emulator dan tidak pernah menyentuh resource produksi Anda.

Anda mengimpor library menggunakan pernyataan impor modular v9. Contoh:

import {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment
} from "@firebase/rules-unit-testing"

// Use `const { … } = require("@firebase/rules-unit-testing")` if imports are not supported
// Or we suggest `const testing = require("@firebase/rules-unit-testing")` if necessary.

Setelah diimpor, penerapan pengujian unit melibatkan:

  • Membuat dan mengonfigurasi RulesTestEnvironment dengan panggilan ke initializeTestEnvironment.
  • Menyiapkan data pengujian tanpa memicu Aturan, menggunakan metode praktis yang memungkinkan Anda mengabaikannya untuk sementara, RulesTestEnvironment.withSecurityRulesDisabled.
  • Menyiapkan rangkaian pengujian dan per-pengujian sebelum/setelah hook dengan panggilan untuk membersihkan data dan lingkungan pengujian, seperti RulesTestEnvironment.cleanup() atau RulesTestEnvironment.clearFirestore().
  • Menerapkan kasus pengujian yang meniru status autentikasi menggunakan RulesTestEnvironment.authenticatedContext dan RulesTestEnvironment.unauthenticatedContext.

Metode dan fungsi utilitas yang umum

Lihat juga metode pengujian khusus emulator di SDK v9.

initializeTestEnvironment() => RulesTestEnvironment

Fungsi ini menginisialisasi lingkungan pengujian untuk pengujian unit aturan. Panggil fungsi ini terlebih dahulu untuk penyiapan pengujian. Emulator harus berjalan agar eksekusi dapat berhasil dilakukan.

Fungsi ini menerima objek opsional yang menentukan TestEnvironmentConfig, yang dapat terdiri dari project ID dan setelan konfigurasi emulator.

let testEnv = await initializeTestEnvironment({
  projectId: "demo-project-1234",
  firestore: {
    rules: fs.readFileSync("firestore.rules", "utf8"),
  },
});

RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext

Metode ini membuat RulesTestContext, yang berperilaku seperti pengguna Authentication yang terautentikasi. Permintaan yang dibuat melalui konteks yang ditampilkan akan memiliki token Authentication tiruan yang dilampirkan. Secara opsional, teruskan objek yang menentukan klaim kustom atau penggantian untuk payload token Authentication.

Gunakan objek konteks pengujian yang ditampilkan dalam pengujian Anda untuk mengakses instance emulator yang dikonfigurasi, termasuk yang dikonfigurasi dengan initializeTestEnvironment.

// Assuming a Firestore app and the Firestore emulator for this example
import { setDoc } from "firebase/firestore";

const alice = testEnv.authenticatedContext("alice", { … });
// Use the Firestore instance associated with this context
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

Metode ini membuat RulesTestContext, yang berperilaku seperti klien yang tidak login melalui Authentication. Permintaan yang dibuat melalui konteks yang ditampilkan tidak akan dilampiri token Firebase Auth.

Gunakan objek konteks pengujian yang ditampilkan dalam pengujian Anda untuk mengakses instance emulator yang dikonfigurasi, termasuk yang dikonfigurasi dengan initializeTestEnvironment.

// Assuming a Cloud Storage app and the Storage emulator for this example
import { getStorage, ref, deleteObject } from "firebase/storage";

const alice = testEnv.unauthenticatedContext();

// Use the Cloud Storage instance associated with this context
const desertRef = ref(alice.storage(), 'images/desert.jpg');
await assertSucceeds(deleteObject(desertRef));

RulesTestEnvironment.withSecurityRulesDisabled()

Menjalankan fungsi penyiapan pengujian dengan konteks yang berperilaku seolah-olah Aturan Keamanan dinonaktifkan.

Metode ini menggunakan fungsi callback yang mengambil konteks pengabaian Aturan Keamanan dan menampilkan promise. Konteks akan dimusnahkan setelah promise me-resolve/menolak.

RulesTestEnvironment.cleanup()

Metode ini menghapus semua RulesTestContexts yang dibuat di lingkungan pengujian dan membersihkan resource yang mendasarinya, sehingga memungkinkan keluar secara bersih.

Metode ini tidak mengubah status emulator dengan cara apa pun. Untuk mereset data di antara pengujian, gunakan metode penghapusan data khusus emulator aplikasi.

assertSucceeds(pr: Promise<any>)) => Promise<any>

Ini adalah fungsi utilitas kasus pengujian.

Fungsi ini menegaskan bahwa Promise yang disediakan dan menggabungkan operasi emulator akan diselesaikan tanpa pelanggaran Aturan Keamanan.

await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

assertFails(pr: Promise<any>)) => Promise<any>

Ini adalah fungsi utilitas kasus pengujian.

Fungsi ini menegaskan bahwa Promise yang disediakan dan menggabungkan operasi emulator akan ditolak dengan pelanggaran Aturan Keamanan.

await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });

Metode khusus emulator

Lihat juga metode pengujian dan fungsi utilitas umum di SDK v9.

RulesTestEnvironment.clearFirestore() => Promise<void>

Metode ini menghapus data dalam database Firestore yang termasuk dalam projectId yang dikonfigurasi untuk emulator Firestore.

RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;

Metode tersebut mendapatkan instance Firestore untuk konteks pengujian ini. Instance Firebase JS Client SDK yang ditampilkan dapat digunakan dengan API SDK klien (v9 modular atau v9 compat).

Memvisualisasikan evaluasi aturan

Emulator Cloud Firestore memungkinkan Anda memvisualisasikan permintaan klien di UI Emulator Suite, termasuk pelacakan evaluasi untuk Aturan Keamanan Firebase.

Buka tab Firestore > Requests guna melihat urutan evaluasi mendetail untuk setiap permintaan.

Pemantauan Permintaan Emulator Firestore yang menampilkan evaluasi Aturan Keamanan

Menghasilkan laporan pengujian

Setelah menjalankan serangkaian pengujian, Anda dapat mengakses laporan cakupan pengujian yang menunjukkan evaluasi dari setiap aturan keamanan Anda.

Untuk mendapatkan laporan tersebut, buat kueri untuk endpoint yang terekspos pada emulator selagi emulator berjalan. Untuk versi yang mudah digunakan di browser, gunakan URL berikut:

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

Proses ini memecah aturan Anda menjadi beberapa ekspresi dan subekspresi, dan Anda dapat mengarahkan mouse ke ekspresi/subekspresi tersebut untuk melihat informasinya lebih lanjut, termasuk jumlah evaluasi dan nilai yang ditampilkan. Untuk versi JSON mentah data ini, sertakan URL berikut dalam kueri Anda:

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

Perbedaan antara emulator dan produksi

  1. Anda tidak harus membuat project Cloud Firestore secara eksplisit. Emulator secara otomatis membuat setiap instance yang diakses.
  2. Emulator Cloud Firestore tidak berfungsi dengan alur Firebase Authentication yang normal. Sebagai gantinya, di Firebase Test SDK, kami telah menyediakan metode initializeTestApp() di library rules-unit-testing, yang menggunakan kolom auth. Handle Firebase yang dibuat menggunakan metode ini akan berperilaku seolah-olah telah berhasil diautentikasi sebagai entity apa pun yang Anda berikan. Jika Anda meneruskan null, objek tersebut akan berperilaku sebagai pengguna yang tidak diautentikasi (misalnya, aturan auth != null akan gagal).

Memecahkan masalah umum

Saat menggunakan emulator Cloud Firestore, Anda mungkin mengalami masalah umum di bawah ini. Ikuti panduan berikut untuk memecahkan masalah perilaku tidak teratur yang Anda alami. Catatan ini ditulis dengan mempertimbangkan library pengujian unit Aturan Keamanan, tetapi pendekatan umumnya berlaku untuk Firebase SDK apa pun.

Perilaku pengujian tidak konsisten

Jika pengujian Anda sesekali berhasil dan sesekali gagal, bahkan tanpa perubahan apa pun pada pengujian itu sendiri, Anda mungkin perlu memastikan apakah pengujian tersebut telah diurutkan dengan benar atau belum. Sebagian besar interaksi dengan emulator bersifat asinkron, jadi periksa kembali apakah semua kode asinkron telah diurutkan dengan benar. Anda dapat memperbaiki urutan tersebut dengan menggabungkan promise atau menggunakan banyak notasi await.

Secara khusus, tinjau operasi asinkron berikut:

  • Menyetel aturan keamanan, dengan, misalnya, initializeTestEnvironment.
  • Membaca dan menulis data, dengan, misalnya, db.collection("users").doc("alice").get().
  • Pernyataan operasional, termasuk assertSucceeds dan assertFails.

Pengujian hanya berhasil saat pertama kali Anda memuat emulator

Emulator bersifat stateful. Emulator menyimpan semua data yang dituliskan kepadanya ke dalam memori, sehingga data apa pun akan hilang setiap kali emulator nonaktif. Jika Anda menjalankan beberapa pengujian terhadap project ID yang sama, setiap pengujian dapat menghasilkan data yang mungkin memengaruhi pengujian berikutnya. Anda dapat menggunakan salah satu metode berikut untuk mengabaikan perilaku ini:

  • Menggunakan project ID unik untuk setiap pengujian. Perhatikan bahwa jika Anda memilih untuk melakukan hal ini, Anda harus memanggil initializeTestEnvironment sebagai bagian dari setiap pengujian; aturan hanya dimuat secara otomatis untuk project ID default.
  • Mengubah struktur pengujian Anda agar tidak berinteraksi dengan data yang ditulis sebelumnya (misalnya, gunakan koleksi yang berbeda untuk setiap pengujian).
  • Menghapus semua data yang ditulis selama pengujian.

Penyiapan pengujian sangat rumit

Saat menyiapkan pengujian, Anda mungkin ingin mengubah data dengan cara yang sebenarnya tidak diizinkan oleh Cloud Firestore Security Rules Anda. Jika aturan mempersulit penyiapan pengujian, coba gunakan RulesTestEnvironment.withSecurityRulesDisabled dalam langkah penyiapan, sehingga operasi baca dan tulis tidak akan memicu error PERMISSION_DENIED.

Setelah itu, pengujian Anda dapat menjalankan operasi sebagai pengguna terautentikasi atau tidak terautentikasi dengan menggunakan RulesTestEnvironment.authenticatedContext dan unauthenticatedContext. Hal ini memungkinkan Anda memvalidasi bahwa Cloud Firestore Security Rules mengizinkan/menolak kasus yang berbeda dengan benar.