Reputation: 7455
Here is a problem I am facing now with the Firestore security rules.
First of all here is an example of data structure I have in my firestore database:
userProfiles/userId/userData
companies/companyId/companyData
Looks pretty simple. Each userData
includes an array named companies
which includes all companyIds which this user has access to.
Now I need to write rules to allow read companyData only if companyId is in particular user info companies list.
Here are the rules which work for me:
service cloud.firestore {
match /databases/{database}/documents {
match /companies/{companyId} {
allow read: if companyId in get(/databases/$(database)/documents/userProfiles/$(request.auth.uid)).data.companies
}
}
}
Taking the fact that I am going to have much more rules, I would like to make them more readable and comfortable to reuse. According to this official guide I can create custom functions and according to this article they can be common and declared outside of the main rules block.
I refactored my rules to look like this and it also worked for me:
service cloud.firestore {
match /databases/{database}/documents {
match /companies/{companyId} {
allow read: if companyId in getUserCompanies()
}
function getUserCompanies() {
return get(/databases/$(database)/documents/userProfiles/$(request.auth.uid)).data.companies
}
}
}
But now I would like to move function outside of the rules block to make it even more clear:
service cloud.firestore {
match /databases/{database}/documents {
match /companies/{companyId} {
allow read: if companyId in getUserCompanies()
}
}
}
function getUserCompanies() {
return get(/databases/$(database)/documents/userProfiles/$(request.auth.uid)).data.companies
}
And that doesn't work. There are no any errors, I just receive the regular Read denied
message from the simulator.
So the questions are: is it possible to move function outside as I did it in my example? Are there any obvious mistakes I've done here? is there better way to make my rules set even more clear?
P.S. I also tried to pass some parameters to that function, including user and company ids - no luck.
Upvotes: 7
Views: 8453
Reputation: 598765
Functions can be defined on any level in your rules file. But they only have access to variables that are defined in the scope where you define them. Anything else you have to pass in as a variable.
So this (useless) function works when defined globally:
function isTrue() {
return true;
}
But this one won't, because it doesn't have access to request
:
function isAdmin() {
return (request.auth.token.email_verified &&
request.auth.token.email.matches(".*@google.com"));
}
What I sometimes do is add a parameter to the function definition:
function isAdmin(request) {
return (request.auth.token.email_verified &&
request.auth.token.email.matches(".*@google.com"));
}
and then pass the variable in to the call:
allow update: if isAdmin(request) ||
(request.auth.uid == uid && isUnmodified(request, resource, 'name'));
Upvotes: 18
Reputation: 3744
It’s not possible, rules only work on the scope they are defined, they mentioned very subtly here
Upvotes: 2