Catch up on highlights from Firebase at Google I/O 2023. Learn more

Kiểm tra Quy tắc bảo mật Cloud Firestore của bạn

Khi bạn đang xây dựng ứng dụng của mình, bạn có thể muốn khóa quyền truy cập vào cơ sở dữ liệu Cloud Firestore của mình. Tuy nhiên, trước khi khởi chạy, bạn sẽ cần các Quy tắc bảo mật của Cloud Firestore chi tiết hơn. Với trình giả lập Cloud Firestore, ngoài việc tạo nguyên mẫu và thử nghiệm các tính năng cũng như hành vi chung của ứng dụng, bạn có thể viết các bài kiểm tra đơn vị để kiểm tra hành vi của Quy tắc bảo mật Cloud Firestore của bạn.

Bắt đầu nhanh

Đối với một vài trường hợp thử nghiệm cơ bản với các quy tắc đơn giản, hãy thử mẫu khởi động nhanh .

Hiểu các quy tắc bảo mật của Cloud Firestore

Triển khai Xác thực FirebaseQuy tắc bảo mật Cloud Firestore để xác thực, ủy quyền và xác thực dữ liệu phi máy chủ khi bạn sử dụng thư viện ứng dụng web và thiết bị di động.

Quy tắc bảo mật của Cloud Firestore bao gồm hai phần:

  1. Một câu lệnh match xác định các tài liệu trong cơ sở dữ liệu của bạn.
  2. Một biểu thức allow phép kiểm soát quyền truy cập vào các tài liệu đó.

Xác thực Firebase xác minh thông tin đăng nhập của người dùng và cung cấp nền tảng cho các hệ thống truy cập dựa trên vai trò và dựa trên người dùng.

Mọi yêu cầu cơ sở dữ liệu từ thư viện máy khách web/di động Cloud Firestore đều được đánh giá dựa trên các quy tắc bảo mật của bạn trước khi đọc hoặc ghi bất kỳ dữ liệu nào. Nếu các quy tắc từ chối quyền truy cập vào bất kỳ đường dẫn tài liệu nào được chỉ định, toàn bộ yêu cầu sẽ không thành công.

Tìm hiểu thêm về Quy tắc bảo mật của Cloud Firestore trong Bắt đầu với Quy tắc bảo mật của Cloud Firestore .

Cài đặt trình giả lập

Để cài đặt trình giả lập Cloud Firestore, hãy sử dụng Firebase CLI và chạy lệnh bên dưới:

firebase setup:emulators:firestore

Chạy trình giả lập

Bắt đầu bằng cách khởi tạo dự án Firebase trong thư mục làm việc của bạn. Đây là bước đầu tiên phổ biến khi sử dụng Firebase CLI .

firebase init

Khởi động trình giả lập bằng lệnh sau. Trình giả lập sẽ chạy cho đến khi bạn tắt tiến trình:

firebase emulators:start --only firestore

Trong nhiều trường hợp, bạn muốn khởi động trình giả lập, hãy chạy bộ thử nghiệm và sau đó tắt trình giả lập sau khi chạy thử nghiệm. Bạn có thể làm điều này một cách dễ dàng bằng cách sử dụng lệnh emulators:exec :

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

Khi bắt đầu, trình giả lập sẽ cố gắng chạy trên một cổng mặc định (8080). Bạn có thể thay đổi cổng trình giả lập bằng cách sửa đổi phần "emulators" trong tệp firebase.json của mình:

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

Trước khi bạn chạy trình giả lập

Trước khi bạn bắt đầu sử dụng trình giả lập, hãy ghi nhớ những điều sau:

  • Ban đầu, trình giả lập sẽ tải các quy tắc được chỉ định trong trường firestore.rules của tệp firebase.json của bạn. Nó mong đợi tên của tệp cục bộ chứa Quy tắc bảo mật Cloud Firestore của bạn và áp dụng các quy tắc đó cho tất cả các dự án. Nếu bạn không cung cấp đường dẫn tệp cục bộ hoặc sử dụng phương thức loadFirestoreRules như được mô tả bên dưới, trình giả lập sẽ coi tất cả các dự án là có quy tắc mở.
  • Mặc dù hầu hết các SDK Firebase hoạt động trực tiếp với trình giả lập, nhưng chỉ có thư viện @firebase/rules-unit-testing hỗ trợ mô phỏng auth trong Quy tắc bảo mật, giúp kiểm tra đơn vị dễ dàng hơn nhiều. Ngoài ra, thư viện hỗ trợ một số tính năng dành riêng cho trình giả lập như xóa tất cả dữ liệu, như được liệt kê bên dưới.
  • Trình giả lập cũng sẽ chấp nhận mã thông báo Xác thực Firebase sản xuất được cung cấp thông qua SDK ứng dụng khách và đánh giá các quy tắc tương ứng, cho phép kết nối trực tiếp ứng dụng của bạn với trình giả lập trong các thử nghiệm tích hợp và thủ công.

Chạy thử nghiệm đơn vị cục bộ

Chạy thử nghiệm đơn vị cục bộ với SDK JavaScript v9

Firebase phân phối thư viện thử nghiệm đơn vị Quy tắc bảo mật với cả SDK JavaScript phiên bản 9 và SDK phiên bản 8. Các API thư viện khác nhau đáng kể. Chúng tôi khuyên dùng thư viện thử nghiệm v9, thư viện này được sắp xếp hợp lý hơn và yêu cầu ít thiết lập hơn để kết nối với trình giả lập và do đó tránh sử dụng ngẫu nhiên tài nguyên sản xuất một cách an toàn. Để tương thích ngược, chúng tôi tiếp tục cung cấp thư viện thử nghiệm v8 .

Sử dụng mô- @firebase/rules-unit-testing để tương tác với trình mô phỏng chạy cục bộ. Nếu bạn gặp lỗi hết thời gian chờ hoặc ECONNREFUSED , hãy kiểm tra kỹ xem trình mô phỏng có thực sự chạy hay không.

Chúng tôi thực sự khuyên bạn nên sử dụng phiên bản Node.js gần đây để bạn có thể sử dụng ký hiệu async/await . Hầu như tất cả các hành vi mà bạn có thể muốn kiểm tra đều liên quan đến các chức năng không đồng bộ và mô-đun kiểm tra được thiết kế để hoạt động với mã dựa trên Lời hứa.

Thư viện Kiểm tra đơn vị quy tắc v9 luôn biết về trình giả lập và không bao giờ động đến tài nguyên sản xuất của bạn.

Bạn nhập thư viện bằng cách sử dụng câu lệnh nhập mô-đun v9. Ví dụ:

import {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment,
  RulesTestEnvironment,
} 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.

Sau khi được nhập, việc triển khai các bài kiểm tra đơn vị bao gồm:

  • Tạo và định cấu hình RulesTestEnvironment bằng lệnh gọi initializeTestEnvironment .
  • Thiết lập dữ liệu thử nghiệm mà không cần kích hoạt Quy tắc, sử dụng phương pháp thuận tiện cho phép bạn tạm thời bỏ qua chúng, RulesTestEnvironment.withSecurityRulesDisabled .
  • Thiết lập bộ thử nghiệm và móc nối trước/sau mỗi thử nghiệm với các lệnh gọi để dọn dẹp môi trường và dữ liệu thử nghiệm, như RulesTestEnvironment.cleanup() hoặc RulesTestEnvironment.clearFirestore() .
  • Triển khai các trường hợp thử nghiệm bắt chước các trạng thái xác thực bằng cách sử dụng RulesTestEnvironment.authenticatedContextRulesTestEnvironment.unauthenticatedContext .

Các phương thức phổ biến và các hàm tiện ích

Ngoài ra, hãy xem các phương pháp thử nghiệm dành riêng cho trình mô phỏng trong SDK v9 .

initializeTestEnvironment() => RulesTestEnvironment

Hàm này khởi tạo một môi trường thử nghiệm để thử nghiệm đơn vị quy tắc. Gọi chức năng này đầu tiên để thiết lập thử nghiệm. Thực hiện thành công yêu cầu trình giả lập đang chạy.

Hàm chấp nhận một đối tượng tùy chọn xác định TestEnvironmentConfig , có thể bao gồm ID dự án và cài đặt cấu hình trình mô phỏng.

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

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

Phương thức này tạo một RulesTestContext , hoạt động giống như một người dùng Xác thực đã được xác thực. Các yêu cầu được tạo thông qua ngữ cảnh được trả về sẽ có mã thông báo Xác thực giả được đính kèm. Theo tùy chọn, chuyển một đối tượng xác định các xác nhận quyền sở hữu hoặc ghi đè tùy chỉnh cho tải trọng mã thông báo Xác thực.

Sử dụng đối tượng ngữ cảnh thử nghiệm được trả về trong các thử nghiệm của bạn để truy cập vào bất kỳ phiên bản trình giả lập nào được định cấu hình, bao gồm cả những phiên bản được định cấu hình với 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

Phương thức này tạo một RulesTestContext , hoạt động giống như một ứng dụng khách chưa đăng nhập qua Xác thực. Các yêu cầu được tạo thông qua ngữ cảnh được trả về sẽ không đính kèm mã xác thực Firebase.

Sử dụng đối tượng ngữ cảnh thử nghiệm được trả về trong các thử nghiệm của bạn để truy cập vào bất kỳ phiên bản trình giả lập nào được định cấu hình, bao gồm cả những phiên bản được định cấu hình với 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()

Chạy chức năng thiết lập thử nghiệm với ngữ cảnh hoạt động như thể Quy tắc bảo mật đã bị tắt.

Phương thức này nhận một chức năng gọi lại, lấy ngữ cảnh bỏ qua quy tắc bảo mật và trả về một lời hứa. Bối cảnh sẽ bị hủy sau khi lời hứa giải quyết/từ chối.

RulesTestEnvironment.cleanup()

Phương pháp này hủy tất cả RulesTestContexts được tạo trong môi trường thử nghiệm và dọn sạch các tài nguyên cơ bản, cho phép thoát sạch.

Phương pháp này không thay đổi trạng thái của trình giả lập theo bất kỳ cách nào. Để đặt lại dữ liệu giữa các lần kiểm tra, hãy sử dụng phương pháp xóa dữ liệu dành riêng cho trình giả lập ứng dụng.

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

Đây là một chức năng tiện ích trường hợp thử nghiệm.

Chức năng khẳng định rằng Promise được cung cấp gói một hoạt động giả lập sẽ được giải quyết mà không vi phạm Quy tắc bảo mật.

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

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

Đây là một chức năng tiện ích trường hợp thử nghiệm.

Hàm khẳng định rằng Promise được cung cấp bao bọc một hoạt động giả lập sẽ bị từ chối do vi phạm Quy tắc bảo mật.

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

Phương pháp dành riêng cho trình giả lập

Ngoài ra, hãy xem các phương pháp thử nghiệm phổ biến và chức năng tiện ích trong SDK v9 .

RulesTestEnvironment.clearFirestore() => Promise<void>

Phương pháp này xóa dữ liệu trong cơ sở dữ liệu Firestore thuộc về projectId được định cấu hình cho trình giả lập Firestore.

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

Phương thức này lấy một phiên bản Firestore cho ngữ cảnh thử nghiệm này. Phiên bản SDK ứng dụng khách Firebase JS được trả lại có thể được sử dụng với API SDK ứng dụng khách (mô-đun v9 hoặc tương thích v9).

Trực quan hóa đánh giá quy tắc

Trình giả lập Cloud Firestore cho phép bạn trực quan hóa các yêu cầu của ứng dụng khách trong Giao diện người dùng Bộ giả lập, bao gồm theo dõi đánh giá cho Quy tắc bảo mật của Firebase.

Mở tab Firestore > Yêu cầu để xem trình tự đánh giá chi tiết cho từng yêu cầu.

Màn hình yêu cầu trình giả lập Firestore hiển thị đánh giá Quy tắc bảo mật

Tạo báo cáo thử nghiệm

Sau khi chạy một bộ kiểm tra, bạn có thể truy cập báo cáo phạm vi kiểm tra cho biết cách đánh giá từng quy tắc bảo mật của bạn.

Để nhận báo cáo, hãy truy vấn điểm cuối được hiển thị trên trình giả lập trong khi trình giả lập đang chạy. Đối với phiên bản thân thiện với trình duyệt, hãy sử dụng URL sau:

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

Điều này chia các quy tắc của bạn thành các biểu thức và biểu thức con mà bạn có thể di chuột qua để biết thêm thông tin, bao gồm số lượng đánh giá và giá trị được trả về. Đối với phiên bản JSON thô của dữ liệu này, hãy bao gồm URL sau trong truy vấn của bạn:

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

Sự khác biệt giữa trình giả lập và sản xuất

  1. Bạn không cần phải tạo dự án Cloud Firestore một cách rõ ràng. Trình giả lập tự động tạo bất kỳ phiên bản nào được truy cập.
  2. Trình giả lập Cloud Firestore không hoạt động với luồng Xác thực Firebase thông thường. Thay vào đó, trong SDK thử nghiệm Firebase, chúng tôi đã cung cấp phương thức initializeTestApp() trong thư viện rules-unit-testing , có trường auth . Bộ điều khiển Firebase được tạo bằng phương pháp này sẽ hoạt động như thể nó đã được xác thực thành công như bất kỳ thực thể nào bạn cung cấp. Nếu bạn chuyển vào null , nó sẽ hoạt động như một người dùng chưa được xác thực ( ví dụ: quy tắc auth != null sẽ không thành công).

Khắc phục sự cố đã biết

Khi sử dụng trình giả lập Cloud Firestore, bạn có thể gặp phải các sự cố đã biết sau đây. Làm theo hướng dẫn bên dưới để khắc phục mọi hành vi bất thường mà bạn đang gặp phải. Những ghi chú này được viết với thư viện thử nghiệm đơn vị Quy tắc bảo mật, nhưng các phương pháp chung có thể áp dụng cho bất kỳ SDK Firebase nào.

Hành vi kiểm thử không nhất quán

Nếu các bài kiểm tra của bạn thỉnh thoảng đạt và không đạt, ngay cả khi không có bất kỳ thay đổi nào đối với bản thân các bài kiểm tra, bạn có thể cần xác minh rằng chúng được sắp xếp đúng trình tự. Hầu hết các tương tác với trình giả lập là không đồng bộ, vì vậy hãy kiểm tra kỹ xem tất cả mã không đồng bộ có được sắp xếp đúng trình tự hay không. Bạn có thể sửa trình tự bằng cách xâu chuỗi các lời hứa hoặc sử dụng ký hiệu await một cách tự do.

Cụ thể, hãy xem xét các hoạt động không đồng bộ sau:

  • Đặt quy tắc bảo mật, chẳng hạn như initializeTestEnvironment .
  • Đọc và ghi dữ liệu, chẳng hạn với db.collection("users").doc("alice").get() .
  • Xác nhận hoạt động, bao gồm assertSucceedsassertFails .

Các bài kiểm tra chỉ vượt qua lần đầu tiên bạn tải trình giả lập

Trình giả lập có trạng thái. Nó lưu trữ tất cả dữ liệu được ghi vào bộ nhớ, vì vậy mọi dữ liệu sẽ bị mất bất cứ khi nào trình giả lập tắt. Nếu bạn đang chạy nhiều thử nghiệm đối với cùng một id dự án, thì mỗi thử nghiệm có thể tạo ra dữ liệu có thể ảnh hưởng đến các thử nghiệm tiếp theo. Bạn có thể sử dụng bất kỳ phương pháp nào sau đây để bỏ qua hành vi này:

  • Sử dụng ID dự án duy nhất cho mỗi thử nghiệm. Lưu ý rằng nếu bạn chọn thực hiện điều này, bạn sẽ cần gọi initializeTestEnvironment như một phần của mỗi bài kiểm tra; các quy tắc chỉ được tải tự động cho ID dự án mặc định.
  • Tái cấu trúc các bài kiểm tra của bạn để chúng không tương tác với dữ liệu đã viết trước đó (ví dụ: sử dụng một bộ sưu tập khác cho mỗi bài kiểm tra).
  • Xóa tất cả dữ liệu được ghi trong quá trình kiểm tra.

Thiết lập thử nghiệm rất phức tạp

Khi thiết lập thử nghiệm của mình, bạn có thể muốn sửa đổi dữ liệu theo cách mà Quy tắc bảo mật Cloud Firestore của bạn không thực sự cho phép. Nếu các quy tắc của bạn đang làm cho quá trình thiết lập thử nghiệm trở nên phức tạp, hãy thử sử dụng RulesTestEnvironment.withSecurityRulesDisabled trong các bước thiết lập của bạn để việc đọc và ghi sẽ không gây ra lỗi PERMISSION_DENIED .

Sau đó, thử nghiệm của bạn có thể thực hiện các hoạt động với tư cách là người dùng được xác thực hoặc chưa được xác thực bằng cách sử dụng RulesTestEnvironment.authenticatedContextunauthenticatedContext tương ứng. Điều này cho phép bạn xác thực rằng Quy tắc bảo mật Cloud Firestore của bạn cho phép/từ chối các trường hợp khác nhau một cách chính xác.