javascriptfirebasegoogle-cloud-firestorefirebase-security

How to write query for roles defined as Map object in angular and firestore?


I am using @angular/fire and I have a temples collection with below schema:

{
 name: 'Sri Chamundeshwari temple',
 address: 'Chamundi hills, Mysore',
 roles: {
  'owner@gmail.com': 'owner',
  'admin@gmail.com': 'admin',
  'member@gmail.com': 'member',
  'viewer@gmail.com': 'viewer'
 }
}

I want only the 4 users in the roles object to be able to access the temple document. Other users should be denied access.

I followed the guide in this link: https://firebase.google.com/docs/firestore/solutions/role-based-access and structured my security rules as below:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    
    match /temples/{temple} {
    
      function isSignedIn() {
        return request.auth != null;
      }
    
      function getRole(rsc) {
        return rsc.data.roles[request.auth.token.email];
      }

      function isOneOfRoles(rsc, array) {
        return isSignedIn() && getRole(rsc) in array;
      }

      allow read: if isOneOfRoles(resource, ['owner', 'admin', 'member', 'viewer']);
    }
  }
}

How should I write a suitable query to get all temple documents which is accessible to logged in user?

The guide does not describe anything about queries.

I tried the below queries and they are giving permission errors.

ERROR FirebaseError: Missing or insufficient permissions.

Let's assume there are 10 temples in the collection.

User A has a role as viewer in 3 temples.

I am expecting that the below queries should return only 3 temples accessible to user A.

const q = query(
     collectionGroup(this.fireStore, "temples"), 
     where(`roles.${this.auth.currentUser?.email}`, 'array-contains', ['owner', 'administrator', 'member', 'viewer'])
);
const q = query(
        collection(this.fireStore, "temples"),
        where(`roles.${this.auth.currentUser?.email}`, 'array-contains', ['owner', 'administrator', 'member', 'viewer'])
);

firestore db screenshot

Update: As per @Frank van Puffelen's answer, replaced the array-contains with in operator because in is the right one to use in this scenario and still receiving permission errors.


Solution

  • You're using the array-contains operator on the roles.${this.auth.currentUser?.email}, but that field isn't an array.

    To filter in whether a non-array field has one of a number of values, use the in operator.


    As discussed in the comments, you'll need to use FieldPath as you have a . in your actual field name (which Firestore will otherwise interpret as a separator).


    Roles array in security rules contains admin and in query contains administrator. Roles value in query can only be a sub-set of the roles array in security rules.