如何配置Firebase规则让同组织管理员读取其他用户文档?
我拥有users、files等多个集合,所有集合文档均包含email和organizationId字段。需求是允许同组织内的管理员用户读取files集合中属于其他用户的文档。
当前服务端Firestore调用代码
const db = getFirestoreInstance(); ... const filesSnapshot = await db.collection("files") .where("email", "==", email) .where("organizationId", "==", organizationId) .orderBy("timestamp", "desc") .limit(filesLimit) .get(); ... let myApp; if (!myApp){ myApp = initializeApp();} let fireStoreInstance; export const getFirestoreInstance = () => { if (!firestoreInstance){ firestoreInstance = getFirestore(myApp); } return firestoreInstance; }
当前Firebase规则代码
match /databases/{database}/documents { function isUserAdminInSameOrg(request, resource, database){ let userObj = get(/databases/$(database)/documents/users/$(request.auth.token.email)).data; return userObj.role == 'admin' //this line works && userObj.organizationId == resource.data.organizationId; //userObj.org has the correct value, resource.data.organizationId does not, despite resource.data.email working in the next function } //this function works function doesResourceBelongToUser(resource, request){ return resource.data.email == request.auth.token.email; } function Rules(collection, resource, request){ let collectionsAdminsCanRead = [ 'files' ]; return (collection in collectionsAdminsCanRead && (doesResourceBelongToUser(resource, request) || isUserAdminInSameOrg(request, resource, database))) || !(collection in collectionsAdminsCanRead ); } match /{collection}/{document} { allow read: if request.auth != null && Rules(collection, resource, request); } }
规则预期逻辑:验证当前认证用户在users集合中的文档,确认其角色为admin,且该用户的organizationId与目标集合(如files)文档的organizationId一致。
已知resource.data可用于访问待查询文档字段,且在doesResourceBelongToUser函数中通过resource.data.email验证用户归属的逻辑正常,但isUserAdminInSameOrg函数中resource.data.organizationId无法获取正确值。已经知晓Firebase规则并非过滤器,且在查询中添加了where条件,请问问题出在哪里?
1. 查询条件限制了管理员的访问范围
当前服务端查询同时添加了where("email", "==", email)和where("organizationId", "==", organizationId),这会导致即便是管理员,也只能查询到自己的文件——email条件直接过滤掉了其他用户的文档。Firebase规则会检查查询是否与规则逻辑兼容,当管理员尝试读取其他用户的文件时,查询本身已经排除了这些文档,规则验证时自然无法获取到对应文档的organizationId。
修复方式:
服务端需要根据用户角色动态调整查询条件:
- 普通用户:保留
email和organizationId两个过滤条件; - 管理员:仅保留
organizationId条件,去掉email过滤。
示例代码调整:
// 先从users集合获取当前用户角色 const userDoc = await db.collection("users").doc(email).get(); const userRole = userDoc.data()?.role; // 基础查询:按组织过滤 let query = db.collection("files").where("organizationId", "==", organizationId); // 普通用户额外添加邮箱过滤 if (userRole !== 'admin') { query = query.where("email", "==", email); } const filesSnapshot = await query.orderBy("timestamp", "desc").limit(filesLimit).get();
2. 规则中resource对象的场景限制
Firestore规则里的resource仅代表单个文档,当进行集合查询时,规则会校验"所有符合查询条件的文档都能通过规则验证"。当前查询的email条件导致管理员的查询只能匹配自己的文档,规则验证时不会触达其他用户的文档,所以resource.data.organizationId只能拿到当前用户文档的值,看起来像是"无法获取正确值"。
3. 规则逻辑优化建议
可以简化规则逻辑,直接针对files集合定义权限,同时确保规则与查询逻辑匹配:
match /databases/{database}/documents { // 获取当前用户的用户文档数据 function getUserData() { return get(/databases/$(database)/documents/users/$(request.auth.token.email)).data; } // 判断是否为文档所有者 function isOwner(resource) { return resource.data.email == request.auth.token.email; } // 判断是否为同组织管理员 function isOrgAdmin(resource) { let userData = getUserData(); return userData.role == 'admin' && userData.organizationId == resource.data.organizationId; } // 单独定义files集合的读取权限 match /files/{document} { allow read: if request.auth != null && (isOwner(resource) || isOrgAdmin(resource)); } // 其他集合的默认读取权限(可根据需求调整) match /{collection}/{document} { allow read: if request.auth != null; } }
内容的提问来源于stack exchange,提问作者David

