如何在 Firebase FireStore 中实现此安全逻辑?

How do I achieve this security logic in Firebase FireStore?

下图显示了我需要为我的 firebase firestore 申请的安全规则的逻辑。

描述 我的应用程序允许用户身份验证,我手动激活客户帐户。激活后,他们可以将用户添加到他们的数据库集合中,以及对他们的数据库执行操作。我在下面用 'Organization' 说明了每个客户端,每个客户端都有多个用户,它们应该只能访问数据库的特定部分 collections/documents。 每个组织都有一个管理员用户,该用户对该特定组织的数据库集合具有完全访问权限。每个用户(来自每个组织)都可以访问节点 'Elements',以及他们自己的 UID 生成的文档和集合。

看来我需要自定义声明授权才能实现这一点。我想知道是否存在一些替代方案,比如在我的 fireStore 中添加一些特定的安全规则以使其工作,或者除了 firebase admin sdk 工具之外的任何其他替代方案,因为它很耗时而且我不是专业的后端开发人员。

其他详情 我用颤振。我的应用程序允许客户验证和创建他们的数据库集合。客户端添加具有不同角色的用户(作为团队成员)(这会影响 collection/document 他们可以访问的内容) 这个安全规则逻辑是我现在坚持的主要内容。

我非常感谢可以帮助我实现目标的建议和示例。

插图

我现在的 FireStore 安全规则

一个可能的解决方案: 每个组织都应该包含一个字符串列表(userIds),只有在这个列表中有userId的用户才能访问组织集合和文档。

数据库结构:

organisation_1:
  userIds (field containing list of user ids - []<String>): 
  adminId (field containing admin id - String):
  admin (collection):
  users (collection):
  elements (collection):
  premium (collection):

organisation_2:

安全规则

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    function isLoggedIn() {
      // only true if user is logged in
      return request.auth != null;
    }

    match /organisation/{organisationId} {
      function prefix() {
        return /databases/$(database)/documents/organisation/$(organisationId);
      }
      function isAdmin() {
        // only true if admin
        return isLoggedIn() && request.auth.uid == get(/$(prefix())).data.adminId;
      }
      function isUser() {
        // only true if user
        return isLoggedIn() && request.auth.uid in get(/$(prefix())).data.usersId;
      }
      function isDataOwner(dataId) {
        // only true if user is admin or userId is the document id.
        // this rule should allow each user access to their own UID-
        // generated docs and collections only
        return isLoggedIn() && (isAdmin() || dataId == request.auth.uid);
      }

      // since userIds list is organisation data, we should prevent any 
      // user from editing it (or only allow admin to edit it).
      // if you are using cloud function to update userIds list, set this 
      // to false. Cloud function does not need access.
      allow write: if isAdmin();
      allow read: if true;

      match /Elements/{elementsId=**} {
        // allow access to the entire Elements collection and 
        // subcollections if isAdmin or isUser.
        allow read, write: if isAdmin() || isUser();
      }
      match /settings/{userId} {
        // allow access only if document id is your userId
        allow read, write: if isDataOwner(userId);
      } 
      match /adminDocs/{docId} {
        // only allow admin
        allow read, write: if isAdmin();
      }
    }
  }
}

然后您可以使用云函数来使您的 userIds 列表保持最新。示例:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const db = admin.firestore();

exports.onCreate = functions.firestore
  .document("/organisation/{organisationId}/users/{userId}")
  .onCreate((_, context) => {
    const params = context.params;
    const organisationId = params.organisationId;
    const userId = params.userId;

    const data = {
      userIds: admin.firestore.FieldValue.arrayUnion(userId),
    };
    return db.doc(`/organisation/${organisationId}`)
      .set(data, { merge: true });
  });

exports.onDelete = functions.firestore
  .document("/organisation/{organisationId}/users/{userId}")
  onDelete((_, context) => {
    const params = context.params;
    const organisationId = params.organisationId;
    const userId = params.userId;

    const data = {
      userIds: admin.firestore.FieldValue.arrayRemove(userId),
    };
    return db.doc(`/organisation/${organisationId}`)
      .set(data, { merge: true });
  });

您可以通过在管理员创建新用户时将 userid 添加到 userId 列表来避免此云功能。但是云函数更干净(使用它)。

更新

$(database) 是您的 firestore 数据库的名称。

{database}(我的安全规则中的第 3 行)告诉规则将数据库的实际名称保存到 database 变量中。

prefix() returns 组织文档的路径。

如果用户尝试访问此路径中的文档 organisation/12345/users/67890,则 $(database)default 并且 prefix() returns /databases/default/documents/organisation/12345/

您可以前往 firestore docs 了解如何使用 $(database) 和路径 (prefix())。