I am working on a project that was created without any security and I am trying to lock things down to acceptable levels. It's an Angular/Ionic app using AngularFire.
Issues started when locking down users writes to the user itself, because the application contains a chat function. When a chat message is created, the application code uses a batch write to update both involved users, of which the latter obbviously fails. The batch write code looks like:
const batch = this.db.firestore.batch();
const sellerUserDoc = this.db.firestore.collection(this.collectionId()).doc(this.toUserDocId(sellerExtendedUser));
batch.set(sellerUserDoc, this.createExtendedUserOfferConversationUpdateItem(sellerExtendedUser, offerConversation), { merge: true });
const buyerUserDoc = this.db.firestore.collection(this.collectionId()).doc(this.toUserDocId(buyerExtendedUser));
batch.set(buyerUserDoc, this.createExtendedUserOfferConversationUpdateItem(buyerExtendedUser, offerConversation), { merge: true });
return batch.commit();
My question is about how to add a security rule / modify query or other code that allows another user to be written to, if a conversation exist between them.
Currently a conversation is created in the conversations collection, with a random ID, which contains both user ids.
Then there is the users collection, which has a map of conversation, that currently does not get populated with the ID because of the rules/query.
I have spend hours watching videos, posts here, and trying thigns out without succes. I think my main problem is that when a user update gets done, I am unable to get the conversation ID ( and I got the message that security rules are not filters :) ).
This is an example of an attempt with non working findings commented out( I am using the emulator on a dev machine):
match /users/{document=**} {
allow write: if request.auth.uid == resource.data.id || isConvPartner();
allow read,update: if request.auth.uid == resource.data.id || getConversations(request.auth.uid)
allow create: if request.auth != null;
}
function isConvPartner() {
let isBuyer = get(/databases/staging-v1-conversations/documents).data.buyerId == request.auth.uid;
let isSeller = get(/databases/staging-v1-conversations/documents/$(resource.data.id)).data.sellerId == request.auth.uid;
return isBuyer || isSeller
}
function getConversations(uid){
let conversationsForuser = get(/databases/staging-v1-conversations/documents).data.values.buyerId.hasAny(uid);
match /{prefix=**}/staging-v1-conversations/{docId} {
allow read,write,update: if request.auth.uid == resource.data.buyerId
|| request.auth.uid == resource.data.sellerId
|| request.data.id == resource.data.id
|| get(/databases/staging-v1-conversations/documents/$(docId)).data.buyerId == request.auth.uid
|| get(/databases/staging-v1-conversations/documents/$(docId)).data.sellerId == request.auth.uid
|| resource == null
|| request.auth != null;
allow create: if request.resource.data.buyerId == request.auth.uid || request.resource.data.sellerId == request.auth.uid;
function recipeData() {
return get(/databases/$(database)/documents/$(docId)).data.buyerId == request.auth.id
}
}
return conversationsForuser
}
I thought about modifying the way data is written, but I cannot really think of a way to write this data to another user then either - the rule has to allow it. Is this the correct approach and how should the security rules look like precisely?
Some pictures from the emulator to show the database structure:
Any help is greatly appreciated, thank you!

