I’m working with a project (in Flutter) that has a concept of “groups” and I’d like user Alice to be able to add user Bob to their group, change their status, and remove them. For my application, I’d like this to happen both in a document representing the group and on Bob’s user document.
For concreteness (initially, before any changes are made):
groups/group1
- "owner" : "alice"
- "members" : {}
users/bob
- "groups": {}
I have security rules set up in Firestore to control who can add people to groups, so only Alice can add people to group1
as they are the owner. This code works (run as Alice):
final batch = FirebaseFirestore.instance.batch();
batch.update(groupRef, {'members.bob': 'member'});
batch.update(bobRef, {'groups.group1': 'member'});
batch.commit();
At this point the documents look like:
groups/group1
- "owner" : "alice"
- "members" : {"bob": "member"}
users/bob
- "groups": {"group1":"member"}
If I then update Bob to be an administrator, that works too:
final batch = FirebaseFirestore.instance.batch();
batch.update(groupRef, {'members.bob': 'admin'}); // changed to admin
batch.update(bobRef, {'groups.group1': 'admin'}); // changed to admin
batch.commit();
Documents:
groups/group1
- "owner" : "alice"
- "members" : {"bob": "admin"}
users/bob
- "groups": {"group1":"admin"}
However, when Alice tries to remove Bob from the group, it fails:
final batch = FirebaseFirestore.instance.batch();
batch.update(groupRef, {'members': {} }); // works individually
batch.update(bobRef, {'groups.group1': FieldValue.delete()}); // fails individually
batch.commit();
The result is:
error: [cloud_firestore/permission-denied] Missing or insufficient permissions.
I’m getting a Firestore permissions error, but I don’t understand why, provided that the previous two updates are permitted. Is there a difference in what permissions need to be granted to delete a single field value?
For reference, my firestore rules for Bob’s document are like this:
match /users/{name} {
function allowGroupUpdate() {
let singleGroup = request.resource.data.groups
.diff(resource.data.groups).affectedKeys().size() == 1;
let group = request.resource.data.groups.keys()[0]; // should be only one
let groupDoc = get(/databases/$(database)/documents/groups/$(group));
return singleKitchen && (isOwner(groupDoc));
}
allow read: if isPublic(resource) || name == request.auth.token.name;
allow update: if allowProfileUpdate() || allowGroupUpdate();
// delete is never allowed for clients
...
Is there something else I need to allow for this action to be permitted?