Menulis Kondisi untuk Aturan Keamanan
Panduan ini dikembangkan dari panduan membuat struktur aturan keamanan untuk menunjukkan cara menambahkan kondisi ke Aturan Keamanan Firestore. Jika Anda belum menguasai dasar-dasar Aturan Keamanan Firestore, lihat panduan memulai.
Elemen penyusun utama Aturan Keamanan Firestore adalah kondisi. Kondisi adalah ekspresi boolean yang menentukan apakah operasi tertentu diizinkan atau ditolak. Gunakan aturan keamanan untuk menulis kondisi yang memeriksa autentikasi pengguna, memvalidasi data masuk, atau mengakses bagian lain dalam database Anda.
Autentikasi
Salah satu pola aturan keamanan yang paling umum adalah mengontrol akses berdasarkan status autentikasi pengguna. Misalnya, aplikasi Anda mungkin hanya ingin mengizinkan pengguna yang telah login untuk menulis data:
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;
}
}
}
Pola umum lainnya adalah memastikan bahwa pengguna hanya dapat membaca dan menulis data mereka sendiri:
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;
}
}
}
Jika aplikasi Anda menggunakan Firebase Authentication atau Google Cloud Identity Platform, variabel request.auth
akan berisi informasi autentikasi untuk klien yang meminta data.
Untuk informasi lebih lanjut tentang request.auth
, lihat dokumentasi referensi.
Validasi data
Banyak aplikasi yang menyimpan informasi kontrol akses sebagai kolom pada dokumen di database. Aturan Keamanan Firestore dapat mengizinkan atau menolak akses secara dinamis berdasarkan data dokumen:
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';
}
}
}
Variabel resource
mengacu pada dokumen yang diminta, dan resource.data
adalah peta dari semua kolom dan nilai yang tersimpan di dokumen tersebut. Untuk informasi lebih lanjut tentang variabel resource
, lihat
dokumentasi referensi.
Ketika menulis data, Anda mungkin ingin membandingkan data yang masuk dengan data yang sudah ada.
Dalam hal ini, jika kumpulan aturan Anda mengizinkan penulisan tertunda, variabel request.resource
akan memuat status waktu mendatang dari dokumen tersebut. Untuk operasi update
yang hanya mengubah sub-kumpulan kolom dokumen, variabel request.resource
akan memuat status dokumen tertunda setelah pengoperasian. Anda dapat memeriksa nilai kolom ini di request.resource
untuk mencegah update data yang tidak diinginkan atau tidak konsisten:
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;
}
}
}
Mengakses dokumen lain
Aturan keamanan Anda dapat memanfaatkan fungsi get()
dan exists()
untuk mengevaluasi permintaan yang masuk berdasarkan dokumen lain di database. Fungsi get()
dan
exists()
sama-sama memerlukan jalur dokumen yang ditentukan secara lengkap. Ketika menggunakan variabel untuk membuat jalur bagi get()
dan exists()
, Anda harus meng-escape variabel secara eksplisit menggunakan sintaksis $(variable)
.
Pada contoh di bawah , variabel database
ditangkap oleh pernyataan kecocokan
match /databases/{database}/documents
dan digunakan untuk membentuk jalur:
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;
}
}
}
Untuk penulisan, Anda dapat menggunakan fungsi getAfter()
untuk mengakses status
dokumen setelah sebuah transaksi atau batch penulisan selesai, namun sebelum
transaksi atau batch tersebut dijalankan. Seperti get()
, fungsi getAfter()
mengambil
jalur dokumen yang ditentukan secara lengkap. Anda dapat menggunakan getAfter()
untuk menentukan set penulisan
yang harus dilakukan bersama sebagai sebuah transaksi atau batch.
Batas panggilan akses
Ada batasan pada panggilan akses dokumen per kumpulan aturan:
- 10 untuk permintaan dokumen tunggal dan permintaan kueri.
-
20 untuk pembacaan, transaksi, dan penulisan batch multi-dokumen. Batas 10 sebelumnya juga berlaku untuk setiap operasi.
Misalnya, Anda membuat permintaan penulisan batch dengan 3 operasi penulisan dan aturan keamanan Anda menggunakan 2 panggilan akses dokumen untuk memvalidasi setiap penulisan. Dalam hal ini, setiap penulisan menggunakan 2 dari 10 panggilan aksesnya dan permintaan penulisan batch menggunakan 6 dari 20 panggilan aksesnya.
Melebihi salah satu batas akan menyebabkan error izin ditolak. Beberapa panggilan akses dokumen akan di-cache, dan panggilan yang di-cache tidak diperhitungkan batasnya.
Untuk penjelasan lengkap mengenai pengaruh batas ini terhadap transaksi dan penulisan batch, lihat panduan untuk mengamankan operasi atomik.
Panggilan akses dan harga
Penggunaan fungsi ini akan memicu operasi baca di database, yang berarti Anda akan ditagih untuk pembacaan dokumen meskipun aturan Anda menolak permintaan tersebut. Lihat Harga Firestore untuk mengetahui informasi penagihan yang lebih spesifik.
Fungsi kustom
Seiring aturan keamanan Anda bertambah kompleks, Anda mungkin ingin mengemas kumpulan kondisi ke dalam fungsi yang dapat digunakan kembali di semua kumpulan aturan Anda. Aturan keamanan mendukung fungsi kustom. Sintaksis untuk fungsi kustom mirip dengan JavaScript, tetapi fungsi aturan keamanan ditulis dalam bahasa khusus domain yang memiliki beberapa batasan penting:
- Fungsi hanya dapat berisi satu pernyataan
return
. Fungsi tidak boleh berisi logika lain apa pun. Misalnya, fungsi tidak boleh menjalankan loop atau memanggil layanan eksternal. - Fungsi dapat secara otomatis mengakses fungsi dan variabel dari cakupan tempat ditetapkannya. Misalnya, fungsi yang ditetapkan ke dalam cakupan
service cloud.firestore
memiliki akses ke variabelresource
dan memiliki fungsi bawaan sepertiget()
danexists()
. - Sebuah fungsi dapat memanggil fungsi lain namun tidak secara berulang. Total kedalaman stack panggilan dibatasi sampai 10.
- Pada aturan versi
v2
, fungsi dapat menentukan variabel menggunakan kata kuncilet
. Fungsi dapat memiliki hingga 10 binding let, tetapi harus diakhiri dengan pernyataan return.
Fungsi ditetapkan dengan kata kunci function
dan menerima nol argumen atau lebih. Misalnya, Anda mungkin ingin menggabungkan dua jenis kondisi yang digunakan dalam contoh di atas menjadi sebuah fungsi:
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();
}
}
}
Dengan menggunakan fungsi, aturan keamanan akan lebih mudah dipertahankan seiring aturan Anda bertambah kompleks.
Aturan bukanlah filter
Setelah Anda mengamankan data dan mulai menulis kueri, perlu diingat bahwa aturan keamanan bukanlah filter. Anda tidak dapat menulis kueri untuk semua dokumen dalam koleksi dan mengharapkan Firestore hanya menampilkan dokumen yang dapat diakses oleh klien saat ini.
Misalnya, perhatikan aturan keamanan berikut:
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';
}
}
}
Ditolak: Aturan ini menolak kueri berikut karena kumpulan hasilnya dapat
mencakup dokumen dengan visibility
yang bukan public
:
Web
db.collection("cities").get() .then(function(querySnapshot) { querySnapshot.forEach(function(doc) { console.log(doc.id, " => ", doc.data()); }); });
Diizinkan: Aturan ini mengizinkan kueri berikut karena klausa where("visibility", "==", "public")
menjamin bahwa kumpulan hasil tersebut memenuhi kondisi aturan:
Web
db.collection("cities").where("visibility", "==", "public").get() .then(function(querySnapshot) { querySnapshot.forEach(function(doc) { console.log(doc.id, " => ", doc.data()); }); });
Aturan keamanan Firestore mengevaluasi setiap kueri terhadap kemungkinan hasilnya dan menggagalkan permintaan jika dapat menampilkan dokumen yang tidak dapat dibaca oleh klien. Kueri harus mengikuti batasan yang ditetapkan oleh aturan keamanan Anda. Untuk informasi aturan keamanan dan kueri lebih lanjut, lihat membuat kueri data dengan aman.
Langkah berikutnya
- Pelajari bagaimana aturan keamanan memengaruhi kueri Anda.
- Pelajari cara membuat struktur aturan keamanan.
- Baca referensi aturan keamanan.