My app stores the user's role (subscriber, editor or admin) as a "roles" map within their user doc in the users collection of Firestore. The role map looks like this:
// Roles map for a subscriber
{
subscriber: true
}
// Roles map for a editor
{
editor: true
}
// Roles map for administrator
{
administrator: true
}
I can also put multiple (or brand new) role fields within the user doc's role map as needed.
I want users to be able to update their profile without being able to change the role map. So for security rules I have tried ensuring this by checking that the user is the owner, and that the before and after roles are all the same. But I always get a permission error when trying to update. Here are the rules:
match /users/{userUid} {
allow update: if
(
isOwner(userUid) &&
(
(request.resource.data.roles.administrator == resource.data.roles.administrator) &&
(request.resource.data.roles.editor == resource.data.roles.editor) &&
(request.resource.data.roles.subscriber == resource.data.roles.subscriber)
)
);
}
The first isOwner(user)
condition looks like this:
function isOwner(uid) {
return (isSignedIn() && (request.auth.uid == uid));
}
I am confident this part is working because when I run it with only that, it works.
I suspect my issue may be that in cases where one of the roles fields (e.g. subscriber) doesn't exist before or after the write, it fails the equality check. So I also tried adding a condition to allow if the existing object doesn't have that field, but it still doesn't work:
match /users/{userUid} {
allow update: if
(
isOwner(userUid) &&
(
(!request.resource.data.roles.administrator || request.resource.data.roles.administrator == resource.data.roles.administrator) &&
(!request.resource.data.roles.editor || request.resource.data.roles.editor == resource.data.roles.editor) &&
(!request.resource.data.roles.subscriber || request.resource.data.roles.subscriber == resource.data.roles.subscriber)
)
);
}
Thanks in advance for any help.
UPDATE 1
I found a simpler solution using writeFields
which works, but writeFields is deprecated so this is not a long-term solution:
allow update: if isOwner(userUid) && !('roles' in request.writeFields)
Again, writeFields is deprecated so should not be used.
UPDATE 2
This solution ended up working for me, replacing role with roles to match my case: https://stackoverflow.com/a/48214390/4407512