Firebase Firestore:自定义管理员访问权限

时间:2017-11-07 09:31:37

标签: firebase admin access rules google-cloud-firestore

在Firebase Firestore中,我尝试仅允许(自定义分配的)管理员编写/更新/删除资源,为此我已经获得了以下安全规则:

service cloud.firestore {
  match /databases/{database}/documents {
    match /resources {
      allow read;
      allow write, update, delete: if get(/users/$(request.auth.uid).isAdmin);
    }
    match /resources/{resource} {
      allow read;
      allow write, update, delete: if get(/users/$(request.auth.uid).isAdmin);
    }
  }
}

我正在使用 users 集合中标记为管理员的用户登录:

users collection has a single admin

NfwIQAjfNdS85yDvd5yPVDyMTUj2 是从身份验证窗格获取的UID:

The user exists

但是,出于某种原因(确定更新:原因;请参阅答案),我在写入资源时出现 PERMISSION_DENIED 错误

也许可以从Firestore查看请求日志?然后我可以看看request.auth.uid看起来与我的收藏和规则相匹配的内容。

2 个答案:

答案 0 :(得分:12)

在写我的问题时,我做到了!我犯了两个错误,如果我正确阅读文档,这两个错误都可以避免。

首先,对service-defined function get的所有调用都需要在路径前加上 / databases / $(database)/ documents / 。所以这个规则:

allow write: if get(/users/$(request.auth.uid)).isAdmin;

成为这个:

allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).isAdmin;

很长一段时间,我知道,但那是怎么回事。我不确定为什么Firestore本身无法做到这一点,因为看到同一路径前缀在get的所有调用中都保持不变,但这可能是为了将来尚未准备好的功能,如跨数据库查询等。

第二次get函数将返回resource,而您又需要调用.data来获取实际数据它包含。因此,而不是这样做:

get(/path/to/user/).isAdmin

你需要这样做:

get(/path/to/user/).data.isAdmin

现在我只希望能够将该逻辑提取到user-defined function

function isAdmin() {
  return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin;
}

但这样做又会导致 PERMISSION_DENIED ,并且在不知道函数中实际发生了什么的情况下,我不确定我是否会花更多时间试图解决这个问题。

更新: @Hareesh pointed out必须在匹配器的范围内定义函数,因此可以将函数放在默认的顶级匹配器中这样:

service cloud.firestore {
  match /databases/{database}/documents {
    function isAdmin() {
      return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin == true;
    }

    // ...
  }
}

答案 1 :(得分:3)

我注意到了一些观点

match /resources指向一个集合,规则对其文档没有影响。在这里,我引用了doc

  

收藏规则不适用于该收藏品中的文件。拥有一个在集合级别而不是文档级别编写的安全规则是不寻常的(也可能是错误的)。

所以你不必为集合编写规则

然后在规则allow write, update, delete:中,您可以说出allow write:或具体allow create, update, delete:这三个选项中的任何一个或将它们合并。

试试这个

service cloud.firestore {
    match /databases/{database}/documents {
      match /resources/{resource} {

        function isAdmin() {
            return get(/databases/$(database)/documents/users/$(request.auth.uid)).isAdmin ||
            get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin;
        }

        allow read;
        allow create, update, delete: if isAdmin();
    }
  }
}