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 variabel resource dan memiliki fungsi bawaan seperti get() dan exists().
  • 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 kunci let. 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