Firestore 安全访问角色系统

Tho*_*man 2 firebase firebase-security firebase-authentication google-cloud-firestore

我知道有很多关于使用安全规则在 firestore 上创建角色系统的线程,但我仍然无法做到这一点。我希望能够拥有三种不同的角色,以及三种不同的访问级别。

  • 管理员:可以读取和写入组织内的任何内容
  • 经理:可以阅读组织内的任何内容,但不能写,除非是他们自己的文件
  • 员工:可以读取和更新自己的数据

我找到了谷歌关于此主题的文档(https://firebase.google.com/docs/firestore/solutions/role-based-access),但我不希望每个用户和机构都有每个经理或管理员,因为那是太多的重复数据。此外,角色应适用于组织内的任何地方,每个子集合不应有所不同。

我的数据库:

organisations{
    organisation1{
    <data about organisation>
       establishments{
           establishment1{
              <data about establishment>
           }
           establishment2{
               <data about establishment>
           }
       }
       people{
           user1{
               <data about user>
               userId: <UID from authentication>
               accountType: <Administrator, Manager or Employee>
           }
           user2{
               <data about user>
               userId: <UID from authentication>
               accountType: <Administrator, Manager or Employee>
           }
       }
    }
    organisation2{
    <data about organisation>
       establishments{
           establishment1{
              <data about establishment>
           }
           establishment2{
               <data about establishment>
           }
       }
       people{
           user1{
               <data about user>
               userId: <UID from authentication>
               accountType: <Administrator, Manager or Employee>
               documents{
                   some documents
               }
           }
           user2{
               <data about user>
               userId: <UID from authentication>
               accountType: <Administrator, Manager or Employee>
           }
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

我知道这很多,但它可能会帮助别人更好地理解我的问题。管理员(帐户类型为管理员的人员)应该能够在组织(组织 1 或组织 2)内的任何位置进行读写操作。经理应该只能读取所有内容,但不能写入,除非它是他们自己的数据(因此他们可以读取任何内容,但具有与员工相同的写入权限,他们只能写入文档以及这些文档的子集合中的任何位置request.auth.uid作为userId字段,例如organization2中的user1应该能够写入user1的文档,以及任何子集合中的每个文档)

我目前有以下内容,我想我需要得到这样的东西:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
  //Access levels:
  //Medewerker: Can only see their own user document and all subcollections
  //Beheerder: Can see any document within the organisation
  //Administrator: Can see and do everything

    function getAccountType(orgId, userId) {
      return get(/databases/$(database)/documents/organisations/$(orgId)/people/$(userId)).data.accountType;
    }
    function isAdmin(orgID){
      return getAccountType(orgID, request.auth.uid) == "Administrator";
    }
    function isBeheerder(orgID){
      return getAccountType(orgID, request.auth.uid) == "Beheerder";
    }
    function isOwnData(userId){
      return request.auth.uid == userId;
    }
    function authenticated(){
      return request.auth != null;
    }
    match /organisations/{orgID} {
      //Administrator rule goes here
      allow write: if authenticated() && (isAdmin(resource.data.organisationId));
      
      //Beheerder rule goes here
      allow read: if authenticated() && (isAdmin(resource.data.organisationId) || isBeheerder(resource.data.organisationId));

      match /people/{userID} {
        //Medewerker rule goes here
        allow read: if authenticated() && (isAdmin(resource.data.organisationId) || isBeheerder(resource.data.organisationId) || isOwnData(resource.data.userId));
        allow update: if authenticated() && (isAdmin(resource.data.organisationId) || isOwnData(resource.data.userId));
        allow create, delete: if authenticated() && (isAdmin(resource.data.organisationId));
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我现在拥有的代码不会给出错误,但它的行为并不符合我的预期。当我使用管理员帐户登录时,我首先只能看到我自己的用户记录。如果我使用 Beheerder 或 Medewerker 帐户登录,我根本得不到任何记录,甚至连我自己的记录也没有。另外,如果我将帐户更改回管理员,我将无法恢复自己的记录。有什么方法可以解决这个问题/我的代码有什么问题吗?另外,我是否需要在每个文档中提供组织 ID,或者是否有解决方法?我当前的代码确实有这个。感谢您的帮助!

Hug*_*sse 5

由于这个问题比较大,这里有两点:

\n

1. 具有 3 级访问角色的示例项目(针对此问题制作)

\n

完整的开源示例在这里,如果需要,请随意克隆/分叉和贡献。

\n

Firestore模型

\n
    \n
  • 组织(集合)\n
      \n
    • 组织1(文档)\n
        \n
      • 机构名称(字段)
      • \n
      • 创建于(字段)
      • \n
      • 机构(集合)\n
          \n
        • 机构 1(doc)
        • \n
        • 机构2(doc)
        • \n
        \n
      • \n
      • 人民(集合)\n
          \n
        • 用户身份 (文档)\n
            \n
          • uid:用户 ID(字段)
          • \n
          • 帐户类型:“管理员”|| “主持人”|| “苦工”(田野)
          • \n
          • 文档(集合)\n
              \n
            • 文档ID (doc)
            • \n
            \n
          • \n
          \n
        • \n
        \n
      • \n
      \n
    • \n
    \n
  • \n
\n

访问级别详细信息:

\n

行政

\n
    \n
  • 阅读:他所在的整个组织(包括机构、人民文件、组织)
  • \n
  • 写:他所在的整个组织(包括机构、人民文件、组织)
  • \n
\n

主持人

\n
    \n
  • 阅读:他所在的整个组织(包括机构、人民文件、组织)
  • \n
  • 写:只有他自己的文件(organizations/orgId/peoples/userId/documents/*
  • \n
\n

日工

\n
    \n
  • 阅读:只有他自己的文件(organizations/orgId/peoples/userId/documents/*),而不是组织或机构
  • \n
  • 写:只有他自己的文件(organizations/orgId/peoples/userId/documents/*
  • \n
\n

firestore.rules(解释)

\n
function getAccountType(orgId, userId) {\n    return get(/databases/$(database)/documents/organizations/$(orgId)/peoples/$(userId)).data.accountType;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这会查询参数中给定组织的参数中给定用户的角色。将其包含在参数中可以轻松地将其与request.data或 一起使用id匹配规则中的

\n
function isAdmin(orgID){\n    return getAccountType(orgID, request.auth.uid) == "admin";\n}\n
Run Code Online (Sandbox Code Playgroud)\n

检查当前登录用户是否request.auth.uid具有参数中给定组织的“管理员”角色。

\n
match /organizations/{orgID} {\n    allow read: if authenticated() && (isAdmin(orgID) || isModerator(orgID));\n
Run Code Online (Sandbox Code Playgroud)\n

仅当用户登录并且是管理员或版主时才允许读取。

\n

完整规则可用在此处查看

\n
\n

2. 一般指导:

\n

遵守干净的规则:

\n
isAdmin()\nisManager()\nisPeon()\n
Run Code Online (Sandbox Code Playgroud)\n

您稍后可以轻松使用和阅读:

\n
match /reminders/{reminderId} {\n    allow read: if authenticated() && (isAdmin(resource.data.organizationId) || isManager(...) || isPeon(...));\n    allow write: if authenticated() && (isAdmin(request.resource.data.organizationId) || isManager(...));\n    allow delete: if authenticated() && isAdmin(resource.data.organizationId);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

一些firestore.rules例子:这个另一个

\n

为您的规则设置单元测试(\xe2\x9a\xa0\xef\xb8\x8f 很重要),因此更容易对其进行迭代并检查一切是否按预期工作。\n此处的测试示例,使用以下命令运行它jest

\n