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

Firebase Firestore: custom admin access

在 Firebase Firestore 中,我试图只允许 (custom-assigned) 管理员访问 write/update/delete 资源,为此我有这些安全规则:

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 collection:

中标记为管理员的用户登录

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

但是,由于某些原因(更新: 原因已确定;请参阅答案),我遇到了 PERMISSION_DENIED 错误在绝对确定我已使用管理员用户登录后写入 resources collection 时。

也许可以查看来自 Firestore 的请求日志?然后我可以看看 request.auth.uid 看起来像什么,以将其与我的 collection 和规则相匹配。

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

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

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

变成这样:

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

它很长,我知道,但就是这样。不过,我不确定为什么 Firestore 不能自己做到这一点,因为相同的路径前缀在所有对 get 的调用中都将保持不变,但这也许是为了某些未来的功能还没准备好,比如跨库查询什么的。

第二个get函数将return一个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 必须在匹配器的范围内定义函数,因此可以将函数放在默认的顶级匹配器中像这样:

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

    // ...
  }
}

我注意到的几点

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

Rules for collections don't apply to documents within that collection. It's unusual (and probably an error) to have a security rule that is written at the collection level instead of the document level.

因此您不必为集合编写规则

然后在规则 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();
    }
  }
}