1

I'm trying to build a similar like the role-based access (https://firebase.google.com/docs/firestore/solutions/role-based-access) within my firestore database. But the roles like I defined them are not working. I'm always getting the error with

Missing or insufficient permissions.

What I have so far is the following in my firestore rules:

service cloud.firestore {
  match /databases/{database}/documents {
  
    function isSignedIn() {
      return request.auth != null;
    }

    function getRole(rsc) {
      return rsc.data.roles[request.auth.uid];
    }

    function isOneOfRoles(rsc, array) {
      return isSignedIn() && (getRole(rsc) in array) || isSignedIn() && rsc.data.openWorld == true;
    }
   
    match /users/{user} {
      allow read: if isSignedIn() && request.auth.uid == resource.data.uid;
    }
 
    // Match any document in the 'worlds' collection
    match /worlds/{world} {
      allow read: if isOneOfRoles(resource, ['owner', 'writer', 'commenter', 'reader']);
    }
  }
}

My document structure looks like: ROOT/worlds/{WORLDID}/... and every document in there like the following:

{
  name: "Open World",
  desc: "", 
  openWorld: true,
  roles: {
    DzpqsN6QjmZoCoM0eymWJ17VKbG3: "owner"
  }
}

I'm using this with an Angular Frontedn and Angularfire with the following code which is wrapped into a service:

getWorlds(userId): Observable<any> {
  return this.afs.collection('worlds').snapshotChanges().map(actions => {
      return actions.map(a => {
        const data = a.payload.doc.data();
        const id = a.payload.doc.id;
        return { id, ...data };
      });
    }).catch((e: any) => Observable.throw(this.errorHandler(e)));
}

Can anybody see where there might be an error in there or is there a general bug with firestore at this point? Thanks!

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
StS
  • 283
  • 4
  • 10

1 Answers1

1

Edit following your comment:

Your query fails "because it does not include the same constraints as your security rules". See https://firebase.google.com/docs/firestore/security/rules-query#secure_and_query_documents_based_on_authuid.

In other words, you cannot rely only on the security rule to filter the documents the user can read and you have to build your query accordingly. However, this will not be possible with your current data structure since Firestore does not support the logical OR queries (e.g. the following will not be possible owner OR writer, owner OR openWorld, etc.).


I've thoroughly tested your rules and they seem to work correctly for all the cases, i.e. with the different roles, in the case openWorld = true and no correct role is declared, in the case openWorld = false, etc. Are you sure your user is correctly signed in? Did you wait enough time for the rules to "propagate"?

Here is the JavaScript code used for testing

var docRef = firestoredb.collection("worlds").doc("x9fwf0Wi528OBlGYIZz1");

firebase.auth().signInWithEmailAndPassword("xxxx@xxx.com", "xxxxx")
    .then(function(userCredential) {

   docRef.get().then(function(doc) {
      if (doc.exists) {
          console.log("Document data:", doc.data());

      } else {
          // doc.data() will be undefined in this case
          console.log("No such document!");
      }

   }).catch(function(error) {
      console.log("Error getting document:", error);
   });

});

Finally, here is a suggestion: I would maybe refactor your rules to separate the case isOneOfRoles and the case isOpenWorld, like e.g.:

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

    function isOpenWorld(rsc) {
      return isSignedIn() && rsc.data.openWorld == true;
    }

   .....

    // Match any document in the 'worlds' collection
    match /worlds/{world} {
      allow read: if isOneOfRoles(resource, ['owner', 'writer', 'commenter', 'reader']) || isOpenWorld(resource);
    }
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
  • @AndréKool You might be right, I don't really know... The OP asks "Can anybody see where there might be an error in there or is there a general bug with firestore at this point?". If you think I should remove it, I have no problem to do so. – Renaud Tarnec May 14 '18 at 08:20
  • I will leave that decision to OP. They can best determine the usefullnes of your post. – André Kool May 14 '18 at 08:24
  • Yes, I waited enough time (more than 10min) for my rules to apply. When I'm testing my code just with the isSignedIn() it is working. Because of that, I think that the error is somewhere in the getRole() function. Does it maybe come from my call to the collection itself (I inserted it into the original question)? I want to query all entries, the user is permitted to, not one specific. – StS May 14 '18 at 09:09
  • OK, thanks! I understood what's the problem. But how can I do this in my frontend script. I have to write a where condition, that checks if the current user is part of my .roles set. Something like the following: ref.where("roles", "contains", user.uid) – StS May 14 '18 at 09:50
  • Look at the first answer of this SO post: https://stackoverflow.com/questions/46568142/google-firestore-query-on-substring-of-a-property-value-text-search. But this method would oblige you to keep four objects, one for each role and do several queries. I think you will have to change your data model anyhow. – Renaud Tarnec May 14 '18 at 10:01