In my code I'm updating a user's "decks" list by adding one item:
const deckId = 'fake-deck-id';
await updateDoc(doc(db, 'users', userId), {
decks: arrayUnion(deckId),
});
However, this is giving a permission-denied
error. Here are my firestore rules:
// Allow updates only to the "decks" and "votes" lists
allow update: if request.auth != null &&
request.auth.uid == userId &&
(
(
// Allow updates to "decks" using array operations
request.resource.data.keys().hasOnly(['decks']) &&
request.resource.data.decks is list &&
request.resource.data.decks.size() == resource.data.decks.size() + 1
) ||
(
// Allow updates to "votes" using array operations
request.resource.data.keys().hasOnly(['votes']) &&
request.resource.data.votes is list &&
request.resource.data.votes.size() == resource.data.votes.size() + 1
)
);
I can't figure out why this isn't working. Does anyone have any ideas?
I think you may be misunderstanding how request.resource
works. The request.resource
contains the entire document as it will exist after the write operation (if that operation is allowed).
So this line in your rules:
request.resource.data.keys().hasOnly(['decks']) &&
This says that the document after it has been updated may only contain a decks
field. If there are any other fields in the document after the update, the update is rejected.
If you only want the decks
field to be updated, you'll need to use affectedKeys
function. There are some good examples of this in the Firebase documentation on restricting what fields can be updated too. For example, here's how it allows only certain fields to be updated:
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Allow a client to update only these 6 fields in a document
allow update: if (request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}