Reputation: 7425
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
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):
a1
to read /users/a1
a1
to read /users/a3
a2
to read any document in /users
The above should deny:
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