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

Khi đ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 Cloud Firestore phức tạp hơn. Với trình mô phỏng Cloud Firestore, ngoài việc tạo mẫu và thử nghiệm các tính năng và 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 mình.

Bắt đầu nhanh

Đối với một số 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 bắt đầu nhanh .

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

Triển khai Quy tắc xác thực Firebasebảo mật Cloud Firestore để xác thực, ủy quyền và xác thực dữ liệu không có máy chủ khi bạn sử dụng thư viện máy khách 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. Tuyên bố match xác định 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á theo 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 thì 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 mô phỏng

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 mô phỏng bằng lệnh sau. Trình mô phỏng sẽ chạy cho đến khi bạn kết thúc 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 mô phỏng, chạy bộ thử nghiệm rồi tắt trình mô phỏng sau khi chạy thử nghiệm. Bạn có thể thực hiện việc 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 khởi động trình giả lập sẽ cố gắng chạy trên cổng mặc định (8080). Bạn có thể thay đổi cổng trình mô phỏng bằng cách sửa đổi phần "emulators" trong tệp firebase.json :

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

Trước khi bạn chạy trình mô phỏng

Trước khi bạn bắt đầu sử dụng trình mô phỏng, hãy ghi nhớ những điều sau:

  • Trình mô phỏng ban đầu 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 thì trình mô phỏng sẽ coi tất cả các dự án là có quy tắc mở.
  • Mặc dù hầu hết SDK Firebase hoạt động trực tiếp với trình mô phỏng, 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 việc kiểm tra đơn vị dễ dàng hơn nhiều. Ngoài ra, thư viện còn hỗ trợ một số tính năng dành riêng cho trình mô phỏng như xóa tất cả dữ liệu, như được liệt kê bên dưới.
  • Trình mô phỏng cũng sẽ chấp nhận mã thông báo Firebase Auth sản xuất được cung cấp thông qua SDK khách hàng 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 mô phỏng trong quá trình tích hợp và kiểm tra 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 có sự khác biệt đá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 mô phỏng, nhờ đó tránh được việc vô tình sử dụng 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ô-đun @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ự đang 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 để có thể sử dụng ký hiệu async/await . Hầu như tất cả hành vi mà bạn có thể muốn kiểm tra đều liên quan đến các hàm không đồng bộ và mô-đun kiểm tra được thiết kế để hoạt động với mã dựa trên Promise.

Thư viện Kiểm tra đơn vị quy tắc v9 luôn nhận thức được trình mô phỏng 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â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 thực hiện 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 kích hoạt Quy tắc, sử dụng phương pháp tiện lợi cho phép bạn tạm thời bỏ qua chúng, RulesTestEnvironment.withSecurityRulesDisabled .
  • Thiết lập bộ kiểm tra và các móc nối trước/sau mỗi bài kiểm tra bằng các lệnh gọi để dọn sạch dữ liệu và môi trường kiểm tra, như RulesTestEnvironment.cleanup() hoặc RulesTestEnvironment.clearFirestore() .
  • Triển khai các trường hợp thử nghiệm bắt chướ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 và hàm tiện ích phổ biến

Đồng thời 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ôi trường thử nghiệm để thử nghiệm đơn vị quy tắc. Gọi hàm này trước để thiết lập thử nghiệm. Việc thực thi thành công đòi hỏi phải chạy trình mô phỏng.

Hàm này 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 ra 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ó đính kèm mã thông báo Xác thực giả. Theo tùy chọn, chuyển một đối tượng xác định xác nhận quyền sở hữu tùy chỉnh hoặc ghi đè 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 thử nghiệm của bạn để truy cập vào bất kỳ phiên bản trình mô phỏng nào đã được định cấu hình, bao gồm cả những phiên bản được định cấu hình bằng 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 ra RulesTestContext , hoạt động giống như một ứng dụng khách không đăng nhập thông 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 được đính kèm mã thông báo Xác thực Firebase.

Sử dụng đối tượng ngữ cảnh thử nghiệm được trả về trong thử nghiệm của bạn để truy cập vào bất kỳ phiên bản trình mô phỏng nào đã được định cấu hình, bao gồm cả những phiên bản được định cấu hình bằng 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 hàm gọi lại, lấy bối 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 khi lời hứa được giải quyết/từ chối.

RulesTestEnvironment.cleanup()

Phương pháp này phá 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 hoàn toàn.

Phương pháp này không thay đổi trạng thái của trình giả lập dưới bất kỳ hình thức 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 mô phỏng ứ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.

Hàm này xác nhận rằng Promise được cung cấp gói một thao tác mô phỏng 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 xác nhận rằng Promise được cung cấp bao bọc một hoạt động mô phỏng sẽ bị từ chối do vi phạm Quy tắc bảo mật.

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

Các phương pháp dành riêng cho trình mô phỏng

Đồng thời xem các phương pháp thử nghiệm phổ biến và các chức năng tiện ích trong SDK v9 .

RulesTestEnvironment.clearFirestore() => Promise<void>

Phương thức này xóa dữ liệu trong cơ sở dữ liệu Firestore thuộc projectId được định cấu hình cho trình mô phỏng Firestore.

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

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

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

Trình mô phỏng Cloud Firestore cho phép bạn trực quan hóa các yêu cầu của khách hàng trong Giao diện người dùng Bộ mô phỏng, bao gồm cả việc 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 mô phỏng 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ộ thử nghiệm, bạn có thể truy cập các báo cáo phạm vi thử nghiệm để 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 hiển thị trên trình mô phỏng trong khi trình mô phỏng đang chạy. Để có 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 đưa URL sau vào 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 mô phỏng 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 mô phỏng tự động tạo bất kỳ phiên bản nào được truy cập.
  2. Trình mô phỏng 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 , trong đó có trường auth . Trình xử lý 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 với 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ụ: các 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 mô phỏng Cloud Firestore, bạn có thể gặp phải các sự cố đã biết sau đây. Hã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. Các ghi chú này được viết có lưu ý đến 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 mọi SDK Firebase.

Hành vi kiểm tra 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 thành công, ngay cả khi không có bất kỳ thay đổi nào đối với các bài kiểm tra, thì bạn có thể cần phải xác minh rằng chúng được sắp xếp theo trình tự chính xác. Hầu hết các tương tác với trình mô phỏng đều 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 theo trình tự chính xác hay không. Bạn có thể sửa trình tự bằng cách kết nố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 lại 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, ví dụ: với db.collection("users").doc("alice").get() .
  • Các 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 mô phỏng

Trình mô phỏng có trạng thái. Nó lưu trữ tất cả dữ liệu được ghi vào bộ nhớ, do đó mọi dữ liệu sẽ bị mất bất cứ khi nào trình mô phỏng tắt. Nếu bạn đang chạy nhiều thử nghiệm trên 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 việc này, bạn sẽ cần gọi initializeTestEnvironment như một phần của mỗi bài kiểm tra; quy tắc chỉ được tải tự động cho ID dự án mặc định.
  • Cơ cấu lại các bài kiểm tra của bạn để chúng không tương tác với dữ liệu đã ghi trước đó (ví dụ: sử dụng một bộ sưu tập khác nhau 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, 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 quy tắc của bạn khiến 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 thao tác 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.