Firestore security rule that checks field under random document in collection to allow update in other collection

Viewed 83

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:

conversations

users

Any help is greatly appreciated, thank you!

0 Answers
Related