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();
}
}
}
在 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();
}
}
}