12
votes

I am trying to base a security rule on a reference to another object.

I have a collection of users and collection of roles. A user object has a field called "role" that is a reference to a particular document in the roles collection.

users
  id
  name
  role <-- reference to particular role

roles
  id
  name
  isSuperUser

The goal here is to allow a user with a particular role (the role with isSuperUser == true) to edit any other role or it's sub-collections;

Here are my rules that I would have thought would have worked:

service cloud.firestore {
   match /databases/{database}/documents {
    match /users/{userId=**} {
     allow read, write: if request.auth.uid == userId;
   }
   match /roles/{roleId=**} {
      function isSuperUser() {
       return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role.isSuperuser == true;
      }
      allow read: if request.auth.uid != null;
      allow write: if isSuperUser();
   }
}

I have confirmed the following works, but it's not really that useful...

get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role != null;

If there is a better way to accomplish role base security, I am all ears.

The lack of any debugging tools makes this quite frustrating.

2
Basically I am looking for the same thing. I have a "fish" collection, that is owned by a user. Instead of storing the user id, I store a reference to the user. So now, I want to check whether fish.user.id is the same as auth.uid. Is that possible or should I store the userId only? - Jaap Weijland
had the idea of getting the data directly from the reference as in request.data.refField.data but apparently thats not supported - galki

2 Answers

1
votes

I know it's been a while since the original question but I've had a similar issue and I hope this could help you or others.

Your condition is: get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role.isSuperuser == true;

But role is a reference, which (apparently) means you need to get it as well. Try this:

get(get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role).data.isSuperuser == true;

0
votes

Have you tried to move the wildcard to a nested path?

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth.uid == userId;
      match /{role=**} {
        allow read: if request.auth.uid != null;
        allow write: if isSuperUser();
      }
    }
  }
}