Reputation: 7039
I have the following database collection '/player_profiles/{userId}'
. I want to use the rule that only the request.auth.uid == userId
unless they are only updating the field matches which is an array. I tried this, but it denied permissions
match /player_profiles/{userId}{
allow write: if userId == request.auth.uid;
allow update: if (request.resource.data.keys().hasOnly(["matches"]) && request.auth != null);
}
And here is the Flutter code that runs the update to add in the item to the matches array:
await DatabaseProvider()
.db
.collection(PLAYER_COLLECTION)
.document(widget.userProfile.userId)
.updateData({
'matches': FieldValue.arrayUnion([profile.userId])
});
Im new to firestore rules but i thought this would work
Was able to get the behavior I wanted with this:
match /player_profiles/{userId}{
allow write: if request.auth.uid == userId || (request.auth != null && request.resource.data.diff(resource.data).affectedKeys().hasOnly(["matches"]));
}
Looking at @Doug Stevenson's answer though I can add in making sure they only are allowed to put in their own userId into other peoples profiles.
Upvotes: 1
Views: 234
Reputation: 317808
request.resource.data.keys()
always contains every field in the document being written, regardless of whether or not it's being updated. You should use the new MapDiff API instead to compare what's being written with what currently exists.
I think you will want to do something like this:
if
(request.resource.data.diff(request.resource.data).affectedKeys().hasOnly(["matches"]) &&
(request.resource.data.matches.toSet().difference(resource.data.matches) == [userId].toSet()) ||
request.auth == null;
I have not tested, but I think you get the idea. You will need to make liberal use of the linked API documentation for security rules to work with maps, sets, and lists effectively.
Upvotes: 3