Quantcast
Channel: Recent Questions - Stack Overflow
Viewing all articles
Browse latest Browse all 11661

Firestore model and security room for multiusers "open" chat app

$
0
0

I'am creating a chat app with the following requirements. Each chat is related to an event in real life.

  • anyone can join/leave a chat which are multi-users chats (provided he knows the chat Id)
  • anyone can post messages after joining
  • anyone can indicate if he is attending/waiting/rejecting the related event =>attendance
  • User can only list chats they have an attendance to.

The idea is: if someone gave me the ID of a chat I can join him and afterwards it will be in my list but I cannot list all chats

Option 1This solution seems a bit dirty but the model would be (not all fields)

- /chat(collection)--- creator_id--- members [Array of userId]--- attendance [Array of user attendances Objects]- /messages (sub collection)--- sender_id

Rules:

match /chat/{chatId} {     allow create : request.resource.data.creator_id == request.auth.uid;     allow read : if request.auth != null     allow list : if request.auth.uid in resource.data.members     allow update : editingOnlyAllowedFields(["members", "attendance"]);     match /messages/{messageId} {         // Use same as chat for get/list         allow create : request.resource.data.sender_id == request.auth.uid;         allow update : resource.data.sender_id == request.auth.uid;}}function editingOnlyAllowedFields(allowedFields) {    let editedKeys = request.resource.data.diff(resource.data).affectedKeys();    return editedKeys.hasOnly(allowedFields);}// Taken from https://stackoverflow.com/a/69140909/3979236

This makes it easy front side to get chats the user is member of (userId in-array members) but this also allows anyone to remove users or change attendance which is problematic.

Option 2Model:

- /chat(collection)--- creator_id--- /attendance (sub collection)----- userId (== document Id)----- status--- /messages (sub collection)----- sender_id

Rules:

function isChatMember(chatId) {    return request.auth != null && exists(/databases/$(database)/documents/chats/$(chatId)/attendance/$(request.auth.uid)) }match /chat/{chatId} {    allow get if request.auth != null    allow list if isChatMember(chatId) // But this makes a Read on the subcollection. Another option is to have a Cloud Function creating an array member on chat when /attendance/userId is created    allow write if request.resource.data.creator_id == request.auth.uid    match /attendance/{userId} {        allow write if userId == request.auth.uid        // Use same as chat for get/list    match /messages/{messageId} {         // Use same as chat for get/list         allow create : request.resource.data.sender_id == request.auth.uid;         allow update : resource.data.sender_id == request.auth.uid;}}

This solution seems to works but "Rules are not filters" and I think we cannot from the front app make a request to get chats for which attendance.userId exists. Said otherwise we cannot use a where filter on a subcollection.

Solution 1? : Cloud Function to maintain the array of member directly on the chat objectSolution 2? : compound query on attendance to get the chatIds, then request with the IDs (but there might be a lot).


Viewing all articles
Browse latest Browse all 11661

Trending Articles