firebase - 基于角色的访问的 Firestore 安全规则
问题描述
我正在尝试在我的 Firestore 数据库中实施基于角色的安全性,但无法获取做出访问决策所需的必要数据。
我users
在地图下有一个包含组成员身份的集合,该集合roles
具有两个级别的组权限:“成员”和“管理员”
示例用户文档:
{
name: "User1",
roles: { group1: "admin", group2: "member"... }
}
我想让user
文档的所有者能够:
- 更新
roles
地图以外的所有字段
另外,我希望作为组管理员的用户能够:
- 修改其他用户的
roles
地图,但仅限于他具有管理员角色的组
示例单元测试:
it("[users] group admin can update user's roles if admin of destination group", async () => {
const admin = adminApp({ uid: "admin" });
const alice = authedApp({ uid: "alice" });
// Create user document for alice and make 'admin' of group1
await firebase.assertSucceeds(
admin.collection("users")
.doc("alice")
.set({ name: "Alice", roles: { group1: "admin" } })
);
// Create user document for bob but without any group membership
await firebase.assertSucceeds(
admin.collection("users")
.doc("bob")
.set({ name: "Bob" })
);
// Alice should be able to grant bob membership to group1
// as she is an 'admin' of group1
await firebase.assertSucceeds(
alice.collection("users")
.doc("bob")
.update({ 'roles.group1': "member" })
);
// Alice should NOT be able to grant bob membership to group2
// as she is not an 'admin' of group2
await firebase.assertFails(
alice.collection("users")
.doc("bob")
.update({ 'roles.group2': "member" })
);
});
到目前为止我的规则:
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function isOwner(rsc) {
return isSignedIn() && request.auth.uid == rsc.id;
}
function notUpdating(field) {
return !(field in request.resource.data)
|| resource.data[field] == request.resource.data[field]
}
function requestorIsAdminOfGroup(groupId) {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles[groupId] == "admin"
}
function adminUpdateUsersRole() {
// Get document before update
let before = resource.data;
// Get potential document after update
let after = getAfter(request.path).data;
// Compare to find top level document keys with changes
let affectedKeys = after.diff(before).affectedKeys();
// Find keys in roles that have changes (groupIds)
let affectedGroups = after.roles.diff(before.roles).affectedKeys();
// What I'm trying to do here:
// - Ensure that only the 'roles' field is changing by checking
// affected key name and size
// - Ensure that a single groupId is being modified and that the
// requesting user is an 'admin' of that group
//
// Problem: how do I get the key name of the modified key from the 'roles' map
// in order to pass to requestorIsAdminOfGroup?
return affectedKeys.size() == 1 && affectedKeys.hasOnly(["roles"]) &&
affectedGroups.size() == 1 && requestorIsAdminOfGroup(groupId)
}
match /users/{userId} {
allow update: if (isOwner(resource) && notUpdating("roles")) || adminUpdateUsersRole();
}
}
我的问题在上面的评论中突出显示adminUpdateUsersRole()
如何在roles
映射中获取表示 groupId 的更改键,以验证请求者是该组的管理员?
如果这是不可能的,我也愿意接受改变users
集合结构的建议,同时实现使组管理员能够管理组成员身份的相同目标。
解决方案
推荐阅读
- http - 如何在响应标头中获取资源的 crc32?
- html - 如何使用角度材料下拉查看每个从 sql server 中选择的“公司名称”
- mysql - MYSQL 按名称和最大 id 排序
- python - Python中的多元回归
- amazon-web-services - AWS Glue Crawlers - 如何处理可能仅包含字符串的大型 CSV 目录结构
- python-3.x - 提取最后 24 小时的日志并清理它们 python 3.x
- javascript - 无法让 Javascript 在 LAMP 服务器上运行
- reactjs - useReducer - 如何判断状态何时更新
- c++ - 是否有可以与 boost::uniform_int 相媲美的 Go 函数?
- excel - 写入 Excel 时无法打印背景颜色