我有一个user
集合,其中有两个字段memberOf
和managerOf
(即组织的;都是文档ID的数组)。
我想限制经理仅列出属于他们所管理的组织的用户。
在JS中,将是这样的:
const memberOf = [1, 2, 3, 4, 5]
const managerOf = [6, 7, 1, 9, 0]
console.log(memberOf.some(el => managerOf.includes(el))) // ? returns true
这是我到目前为止所拥有的:
function isSignedIn() {
return request.auth.uid != null
}
function isAdmin() {
return isSignedIn() && 'admin' in request.auth.token && request.auth.token.admin
}
match /users/{userId} {
allow get: if isSignedIn() && (request.auth.uid == userId || isAdmin());
allow list: if isAdmin() || ???; // ? how can I express the above condition?
allow write: if isAdmin();
}
这就是查询:
const unsubscribe = db.collection('users')
.where('memberOf', 'array-contains', organisationId)
.orderBy('email', 'asc')
.onSnapshot(snap => {
console.log(`Received query snapshot of size ${snap.size}`)
var docs = []
snap.forEach(doc => docs.push({ ...doc.data(), id: doc.id }))
actions.setMembers(docs)
}, error => console.error(error))
首先,我想在安全规则中使用请求中的organisationId
,但由于它不是写操作(https://firebase.google.com/docs/reference/rules/rules.firestore.Request#resource),所以不可用
我想到了:
function hasMemberManagerRelationship(userId) {
return isSignedIn() && get(/databases/$(database)/documents/users/$(userId)).data.memberOf in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.managerOf
}
match /users/{userId} {
allow get: if isSignedIn() && (request.auth.uid == userId || isAdmin());
allow list: if isAdmin() || hasMemberManagerRelationship(userId);
allow write: if isAdmin();
}
或
function hasMemberManagerRelationship(userId) {
return isSignedIn() && get(/databases/$(database)/documents/users/$(userId)).data.memberOf.toSet().hasAny(get(/databases/$(database)/documents/users/$(request.auth.uid)).data.managerOf.toSet())
}
(https://firebase.google.com/docs/reference/rules/rules.Set#hasAny)
但是它不起作用,并且出现错误FirebaseError: Null value error. for 'list' @ L27
。而且最重要的是,这可能会产生很多额外的读取操作(未优化按结算方式)。
我可以做以下事情:
allow list: if isAdmin() || (isManagerOf('jJXLKq7p9wWSNLsHcVIn') && 'jJXLKq7p9wWSNLsHcVIn' in resource.data.memberOf);
其中jJXLKq7p9wWSNLsHcVIn
是组织的ID(并在查询中使用),但我不知道如何从请求“上下文”中检索ID。
任何帮助将不胜感激!
答案 0 :(得分:1)
好的。首先,感谢@Doug Stevenson在另一篇文章中提到debug()
!我不知道它的存在,而且它震撼了!
调试日志中debug(resource.data.memberOf)
的结果是:
constraint_value {
simple_constraints {
comparator: LIST_CONTAINS
value {
string_value: "jJXLKq7p9wWSNLsHcVIn"
}
}
}
LIST_CONTAINS
强迫我看一下List:https://firebase.google.com/docs/reference/rules/rules.List#hasAny
(实际上,它确实存在,但在我的情况下?无效)toSet()
不适用于列表,但是列表已经具有hasAny()
功能。
最后,此规则有效:
function hasMemberManagerRelationship() {
return isSignedIn() && resource.data.memberOf.hasAny(getUser(request.auth.uid).data.managerOf)
}
现在,我只是想知道getUser(request.auth.uid).data.managerOf
是否以某种方式被缓存(读取多个用户条目需要1次读取)还是每次都重新运行(100个用户,额外读取100次)。
对此有何想法?
我衷心希望这是第一种情况^^
答案 1 :(得分:0)
我测试了一些规则,这些规则与您使用Firestore“ Rules Playground”的尝试非常相似,并且似乎可以正常工作。
您不需要get()
当前的userId
,因为您已经在resource
对象中拥有了它。
我不确定它会产生更多的读取操作,因为我们仅将get()
用于request.auth.uid
。
function getUser(userId) {
return get(/databases/$(database)/documents/users/$(userId));
}
function isSignedIn() {
return request.auth.uid != null;
}
function isAdmin() {
return isSignedIn() && 'admin' in request.auth.token && request.auth.token.admin;
}
function hasMemberManagerRelationship() {
return isSignedIn() && resource.data.memberOf.toSet().hasAny(getUser(request.auth.uid).data.managerOf.toSet());
}
match /users/{userId} {
allow read: if isAdmin() || hasMemberManagerRelationship();
}
您在哪里得到FirebaseError: Null value error
?