Trước hết, bạn cần phải hiểu được 2 khái niệm cơ bản về Cloud Firestore như sau:

  • collections: ứng với CSDL quan hệ thì nó là tables
  • document: ứng với CSDL quan hệ thì nó là row
  • field: giống với CSDL quan hệ

Chuẩn bị

Có 3 thứ bạn cần chuẩn bị là: danh sách user2 collections là postsusers

Danh sách users
Danh sách users (Xem ảnh gốc)

Posts Collection
Posts Collection (Xem ảnh gốc)

Users Collection
Users Collection (Xem ảnh gốc)

Sử dụng Rules Playground

Để hỗ trợ cho việc test các rules được đơn giản và nhanh chóng, và không làm thay đổi đến dữ liệu hiện tại thì chúng ta sẽ sử dụng Rules Playground. Truy cập thông qua https://console.firebase.google.com

Rules Playground
Rules Playground (Xem ảnh gốc)

Có 3 trường dữ liệu cần lưu ý như sau:

  • Simulation type: phương thức request gồm: get, create, update, delete
  • Location: đường dẫn tới document hoặc collection cần kiểm tra rule. VD: /posts/BklzUCx35BLnkYs5ru2g
  • Authenticated: lựa chọn loại request là đã xác thực hay chưa xác thực

firestore-rule-6.png
firestore-rule-6.png (Xem ảnh gốc)

Cài đặt Rules cơ bản

1/ Vô hiệu hóa toàn bộ truy cập vào database

Nghĩa là user không thể thực hiện hành động đọc (read), ghi (write) dữ liệu vào database.

Các khai báo bên dưới được hiểu như sau:

  • service firebase.storage: chỉ định rules chỉ được áp dụng cho dịch vụ storage
  • match /{document=**}: khai báo này biểu thị rằng toàn bộ rule sẽ được áp dụng cho toàn bộ document
  • allow read, write: if false: không cho phép đọc, ghi dữ liệu
rules_version = '2';
service firebase.storage { 
  match /{document=**} {
    allow read, write: if false
  }
}

2/ Cho phép bất kì ai đọc và ghi dữ liệu

Chuyển cài đặt if false sang if true, có nghĩa là toàn bộ document đều có thể đọc và ghi. Kể cả với những user chưa được xác thực

Cài đặt này chỉ phù hợp khi phát triển hoặc kiểm thử

match /{document=**} {
  allow read, write: if true
}

3/ Phân quyền cho user đã đăng nhập

Nếu bạn chỉ muốn cho những user đã đăng nhập được quyền write dữ liệu vào database

match /posts/{postId} {
   allow write: if request.auth != null;
}

Lưu ý: user đã đăng nhập ở đây là những user đăng nhập sử dụng Firebase Authentication

4/ Phân quyền cho 1 user cụ thể

Ví dụ, bạn muốn phân quyền ghi dữ liệu trong collection users cho chính người sở hữu dữ liệu ấy, hay nói cách khác là có uid trùng với userId

match /users/{userId} {
   allow write: if request.auth.uid != userId;
}

5/ Truy cập vào dữ liệu của document

Tiếp theo, bạn có thể tạo ra rule dựa vào dữ liệu trong document bằng cách sử dụng biến resource.data

match /posts/{postId} {
  allow read: if resource.data.isDraft == false
}

Biến resource.data đại diện cho toàn bộ dữ liệu trong document. Giả sử bạn có 1 document như sau

// posts/{postId}
{
  name: 'Post no 1';
  isDraft: false;
  content: "...";
  author: "James"
}

Lúc này, bạn có thể sử dụng resource.data.name, resource.data.content, .v.v

Cài đặt Rules nâng cao

6/ Sử dụng function để rút gọn rule

Thử hình dung rằng, khi số lượng điều kiện của bạn ngày càng tăng lên và điều kiện cũng phức tạp hơn gây khó khăn trong việc đọc hiểu.

Nếu vẫn sử dụng cách cài đặt điều kiện như trên thì không tốt chút nào. Để giải quyết vấn đề này, chúng ta sẽ sử dụng function

match /posts/{postId} {
  function isSignedIn() { // tạo function
    return request.auth != null;
  }

  allow read: if isSignedIn(); // gọi function - khá giống javascript
}

Trông ngắn gọn và tường minh hơn đúng k 🌝

7/ Truy cập đến dữ liệu từ collection khác

Trường hợp tiếp theo, nếu bạn muốn truy cập đến dữ liệu từ 1 collection khác thì sao. Xem xét ví dụ: chỉ cho phép tác giả hoặc admin sửa hoặc xóa bài viết của họ tạo ra

match /post/{postId} {
   allow update, delete: if isAuthor() || isAdmin();

   function isAuthor() {
      return resource.data.ownerId == request.auth.uid;
   }
   function isAdmin() {
      return get(/databases/$(database)/documents/
          user/$(request.auth.uid)).data.role == "ADMIN";
   }
}

Hàm get() được sử dụng để giải quyết trường hợp này. Ở ví dụ trên, hàm này được giải thích như sau:

  • get : sử dụng để truy xuất các collection khác
  • /databases/$(database)/documents/user/$(request.auth.uid): đường dẫn đến document thuộc collection cần lấy dữ liệu.
  • get(</path/to/document>).data: chứa kết quả trả về của hàm get

8/ Kiểm tra dữ liệu có tồn tại hay không

Muốn kiểm tra xem 1 document có tồn tại hay không thì sử dụng hàm exists, các sử dụng khá đơn giản như dưới

exists(/databases/$(database)/documents/collectionName/$(documentId))

9/ Phân quyền theo nhiều cấp dữ liệu

Cuối cùng, bạn còn có thể phân quyền cho từng document bên trong collection

  match /databases/{database}/documents {
    match /post/{postId} {
      allow read, write: if <condition>;

      match /children {
        allow read: if <condition>;
      }
    }
  }

Tài liệu tham khảo

Nếu bạn cần tham khảo thêm về các method khác ngoài write, read thì có thể tham khảo 1 số nguồn dưới đây

Kết

Với 9 cài đặt trên, hi vọng các bạn có thể nắm được những quy tắc cơ bản khi cài đặt rules cho Cloud Firestore.

Nguồn tham khảo:
https://khreniak.medium.com/cloud-firestore-security-rules-basics-fac6b6bea18e
https://firebase.google.com/docs/rules