Ad

Declaring A Function In Firestore Rules

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 and 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 bock.

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 amke 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 is 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.

Ad

Answer

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'));
Ad
source: stackoverflow.com
Ad