Robin Chander
Robin Chander

Reputation: 7425

Firestore security rules - permission denied

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();

Upvotes: 2

Views: 1953

Answers (1)

robsiemb
robsiemb

Reputation: 6374

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.

Upvotes: 2

Related Questions