according to the docs functions cannot create variables but I need to check two fields for write permissions on my companies
collection. I have the documentId stored on my user
document as company_slug
So rule #1 for match /companies/{company} { allow write: ... }
is
get(/databases/$(database)/documents/users/$(request.auth.uid)).data.company_slug == company
That is fine for read access but I need to check the user.level
(1 for admin, 2-n for other levels) whether or not a write is OK.
so rule #2 would be
get(/databases/$(database)/documents/users/$(request.auth.uid)).data.level == 1
So the complete rule would be:
match /companies/{company} {
allow read: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.company_slug == company;
allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.company_slug == company && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.level == 1;
}
Which seems very unwieldy and would also "cost" me 2 reads and 1 write for every write request I send, wouldn't it?
Is there any way to optimize this as this seems very hard to maintain as the application grows.
You could use Custom Claims to implement you security needs.
You would assign two different Claims to each user :
companyId
and
userLevel
(1, 2, 3, etc)
and your rules would look like:
service cloud.firestore {
match /databases/{database}/documents {
function userIsLevel1() {
return (request.auth.uid != null) && (request.auth.token.userLevel == 1);
}
function isCorrectCompany(company) {
return request.auth.token.companyId == company;
}
match /companies/{company} {
allow read: if isCorrectCompany(company);
allow write: if isCorrectCompany(company) && userIsLevel1();
}
}
One key advantage of using Custom Claims is that you don't need to read one or more Firestore document(s) for each Security Rules verification.
You may watch this official Firebase video, starting at 17:49: https://www.youtube.com/watch?v=eW5MdE3ZcAw