一种可能的解决方案:
每个组织都应包含一个字符串列表(userIds),并且只有在此列表中具有 userId 的用户才能访问组织集合和文档。
数据库结构:
organisation_1:
userIds (field containing list of user ids - []<String>):
adminId (field containing admin id - String):
admin (collection):
users (collection):
elements (collection):
premium (collection):
organisation_2:
安全规则
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isLoggedIn() {
// only true if user is logged in
return request.auth != null;
}
match /organisation/{organisationId} {
function prefix() {
return /databases/$(database)/documents/organisation/$(organisationId);
}
function isAdmin() {
// only true if admin
return isLoggedIn() && request.auth.uid == get(/$(prefix())).data.adminId;
}
function isUser() {
// only true if user
return isLoggedIn() && request.auth.uid in get(/$(prefix())).data.usersId;
}
function isDataOwner(dataId) {
// only true if user is admin or userId is the document id.
// this rule should allow each user access to their own UID-
// generated docs and collections only
return isLoggedIn() && (isAdmin() || dataId == request.auth.uid);
}
// since userIds list is organisation data, we should prevent any
// user from editing it (or only allow admin to edit it).
// if you are using cloud function to update userIds list, set this
// to false. Cloud function does not need access.
allow write: if isAdmin();
allow read: if true;
match /Elements/{elementsId=**} {
// allow access to the entire Elements collection and
// subcollections if isAdmin or isUser.
allow read, write: if isAdmin() || isUser();
}
match /settings/{userId} {
// allow access only if document id is your userId
allow read, write: if isDataOwner(userId);
}
match /adminDocs/{docId} {
// only allow admin
allow read, write: if isAdmin();
}
}
}
}
然后您可以使用云功能来保持您的 userIds 列表是最新的。示例:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const db = admin.firestore();
exports.onCreate = functions.firestore
.document("/organisation/{organisationId}/users/{userId}")
.onCreate((_, context) => {
const params = context.params;
const organisationId = params.organisationId;
const userId = params.userId;
const data = {
userIds: admin.firestore.FieldValue.arrayUnion(userId),
};
return db.doc(`/organisation/${organisationId}`)
.set(data, { merge: true });
});
exports.onDelete = functions.firestore
.document("/organisation/{organisationId}/users/{userId}")
onDelete((_, context) => {
const params = context.params;
const organisationId = params.organisationId;
const userId = params.userId;
const data = {
userIds: admin.firestore.FieldValue.arrayRemove(userId),
};
return db.doc(`/organisation/${organisationId}`)
.set(data, { merge: true });
});
您可以通过在管理员创建新用户时简单地将用户 ID 添加到用户 ID 列表来避免此云功能。但是云功能更干净(使用它)。
更新
$(database) 是您的 Firestore 数据库的名称。
{database}(我的安全规则中的第 3 行)告诉规则将数据库的实际名称保存到 database 变量中。
prefix() 返回组织文档的路径。
如果用户试图在organisation/12345/users/67890这个路径中访问他的文档,那么$(database)就是default,prefix()返回/databases/default/documents/organisation/12345/
你可以去firestore docs查看$(database)和路径(prefix())的使用情况。