1
votes

I have a users collection where I store user data such as name, email etc and another collection for blocked users. I want to allow a user to read its own document and the document of the users that he/she has not blocked. I have implemented security rules but somehow a user cannot even read its own document. Can someone help?

Users Collection

users { // name of collection
   a1 { // this is firebase id serving as document name
     name: "abc",
     email: [email protected]
   }
   a2 { // this is firebase id serving as document name
     name: "efg",
     email: [email protected]
   }
   a3 { // this is firebase id serving as document name
     name: "hij",
     email: [email protected]
   }
   a4 { // this is firebase id serving as document name
     name: "klm",
     email: [email protected]
   }
}

Blocked Collection

blocked { // name of the collection
    a1 { // name of the document
        a2 : 1, // this means a1 has blocked a2
        a4 : 1  // this means a1 has blocked a4
    }
}

Security Rules

service cloud.firestore {
    match /databases/{database}/documents {
        match /users/{userId} {
            allow read: if request.auth.uid != null
            && get(/databases/$(database)/documents/blocked/$(request.auth.uid)).userId != 1;
        }

        match /blocked/{fid} { // blocked collection/fid == owner firebase id
            allow read: if request.auth.uid == fid;
        }
    }
}

Code

DocumentReference docref = Firestore.instance.collection("users").document(user.uid);
return docref.get();
1
Please edit the question with the code that doesn't work the way you expect with these rules in place. Rules must always have some matching code to go along with them in order to be meaningful. It will also be helpful to see the exact contents of the documents in use for a particular query. In other words, please provide an mcve. stackoverflow.com/help/minimal-reproducible-exampleDoug Stevenson
Thanks @DougStevenson, I can't even get my own document's reference. Please see code aboveRobin Chander
You're using get() incorrectly in the rules. It returns a Resource object that has a data property where all the field values live. Note that accessing a property that doesn't exist is an error and will cause the rule to always reject. I also suggest using boolean values instead of numbers to indicate users that are blocked. firebase.google.com/docs/reference/rules/rules.firestore#.getDoug Stevenson
@RobinChander More generally, think about how you want to test the rules -- including both allow and deny cases, and what queries you will make -- the single get() you mentioned could be solved with just wide open read rules too. I do believe my answer should get you what you described in this post (the primary problem being what Doug described), but it may not actually be what you need.robsiemb
thanks guys. I realized that I am indeed using get incorrectly. Also I was trying to use the rules incorrectly to filter.Robin Chander

1 Answers

2
votes

You need to handle the userId variable differently in this case. The way you have written it, the rule is looking for a userId property in the user's blocked document's Resource object, not the value of the userId variable. You probably also want to provide a reasonable default to the get call.

Likewise, since the get() call returns a Resource you need to use the data member to get at the Map of properties.

You may want to additionally check that the user's blocked document actually exists, otherwise your query will fail if it does not.

service cloud.firestore {
    match /databases/{database}/documents {
        match /users/{userId} {
            allow read: if request.auth.uid != null
                && (!exists(/databases/$(database)/documents/blocked/$(request.auth.uid)) ||
                    get(/databases/$(database)/documents/blocked/$(request.auth.uid)).data.get(userId,0) != 1);
        }

        match /blocked/{fid} { // blocked collection/fid == owner firebase id
            allow read: if request.auth.uid == fid;
        }
    }
}

The above should allow (given the /blocked you defined in your question):

  • user a1 to read /users/a1
  • user a1 to read /users/a3
  • user a2 to read any document in /users

The above should deny:

  • all writes
  • user a1 to read /users/a2

I have only done some loose validation of the above in the simulator. I strongly recommend you write extensive tests for any rules you end up using.

Finally, keep in mind that security rules are not filters, so it really depends on how you are using this -- queries where any potentially matching document might be denied will deny the whole query. The way you have described the problem (using the rule for a user to be denied access to the documents of other users that they have blocked) makes it sound like you are trying to filter, not trying to protect the data.