带角色的Firestore查询?

时间:2018-06-25 10:22:38

标签: javascript firebase firebase-realtime-database google-cloud-firestore

the documentation中,您可以通过将字符串与另一个字符串值进行比较来进行查询,例如:

citiesRef.where('name', '>=', 'San Francisco');
citiesRef.where('state', '>=', 'CA').where('state', '<=', 'IN');

然后,此roles part in the documentation显示了如何在Firestore文档中应用角色。但是,它没有显示如何查询它。.但是如上面的示例所示,它应如下所示工作:

citiesRef.where(`roles.${user.uid}`, '>', '');

因此,以上查询应返回所有值大于空字符串的文档,对吧?

在我的代码中,我拥有包含一个文档的组织集合:

{
  "name": "MyAmazingCompany",
  "roles": {
    "my-user-uid": "owner"
  }
}

如果我尝试查询具有以下角色的组织:

organizationsRef.where(`roles.${user.uid}`, '>', '');

我将在浏览器控制台中获得Uncaught Error in onSnapshot: Error: Missing or insufficient permissions.(使用firebase npm软件包版本5.1.0,并尝试使用5.0.3)。

只是为了确保我有权访问该文档,下面的查询经过测试,它可以正常工作并返回一个组织。

organizationsRef.where(`roles.${user.uid}`, '==', 'owner');

那怎么了?

还有人声称它应该可以工作:Firestore select where is not null

这是我的规则:

service cloud.firestore {
  match /databases/{database}/documents {
    function isSignedIn() {
      return request.auth != null;
    }

    function getRole(rsc) {
      // Read from the "roles" map in the resource (rsc).
      return rsc.data.roles[request.auth.uid];
    }

    function isOneOfRoles(rsc, array) {
      // Determine if the user is one of an array of roles
      return isSignedIn() && (getRole(rsc) in array);
    }

    match /organizations/{organizationId} {
      allow read: if isOneOfRoles(resource, ['owner']);
      allow write: if isOneOfRoles(resource, ['owner']);
    }
  }
}

就像我说的那样,如果我比较角色是否是所有者,这是可行的,但是如果用户的uid存在于角色数组中,无论她具有什么角色,我都想获得结果。

2 个答案:

答案 0 :(得分:0)

规则的一个问题可能是您如何检查角色图。如果您尝试访问不存在的映射密钥,则会引发异常。

function getRole(rsc) {
  // Read from the "roles" map in the resource (rsc).
  return rsc.data.roles[request.auth.uid]; // this will throw an exception if the uid isn't in the map
}

在规则语言中,地图具有get("key", "default-value")方法,可帮助您安全地执行自己的工作。因此,这可能会有所帮助:

function getRole(rsc) {
  // Read from the "roles" map in the resource (rsc).
  return rsc.data.roles.get(request.auth.uid, null);
}

function isOneOfRoles(rsc, array) {
  // Determine if the user is one of an array of roles
  return isSignedIn() && getRole(rsc) != null && (getRole(rsc) in array);
}

我尚未在firebase中运行您的代码以验证是否没有其他问题,但这至少是您可能遇到的一个问题。

答案 1 :(得分:0)

我还发现文档的这一部分令人困惑(或者缺少非常重要的细节)。 Firestore安全性要求您遵循有关数据结构的非常特定的设计模式。这是因为,Firebase安全规则语言在表达规则时的功能比可以对数据运行的简单查询和复合查询表达式要少。

当试图保护数据库安全时,我逐渐不得不放弃原始的数据抽象思想,并从安全语言所施加的限制重新开始。我开始采用以下模式,而不是Firestore文档(https://firebase.google.com/docs/firestore/solutions/role-based-access)中建议的角色表示。您的用例采用的代码。

{
  "name": "MyAmazingCompany",
  "roles": {
    "anyRole": ["user-id-1", "user-id-2", "user-id-3", "user-id-4"],
    "owners": ["user-id-1"],
    "editors": ["user-id-2"],
    "readers": ["user-id-3", "user-id-4"],
  }
}

因此,实质上,角色对象的属性是不同的角色级别,并且属于给定角色的用户以id数组的形式存储在角色属性下。还有一个名为anyRole的附加元角色,它用作便利集合,使查询更容易。

Firebase安全规则如下:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
        match /organizations/{organization} {
    
      function hasReadPermission(res) {
        let anyRole = request.auth.uid in res.data.accessors.anyRole;
        return anyRole;
      }
      
      function hasWritePermission(res) {
        let owner = request.auth.uid in res.data.accessors.owners;
        let editor = request.auth.uid in res.data.accessors.editors;
        return owner || editor;
      }

      allow read: if hasReadPermission(resource);
      allow write: if hasWritePermission(resource);
    }
  }
}

最后,使用array-contains上的roles.anyRole来查询数据非常简单

获取用户有权访问的所有文档:

organizationsRef.where(`roles.anyRole`, 'array-contains', user.uid);

获取用户拥有的所有文档:

organizationsRef.where(`roles.owner`, 'array-contains', user.uid);