7

I'm trying to create an application which allows users to collaborate on lists. Every user needs to be invited in order to be able to work on the list.

I structured my data like that (loosely based on this blog post). Also this structure can be changed if needed.

list
  list_1:
    users:
      owner:
        owner@company.com: true
      shared:
        user@company.com: true
        user2@company.com: true
    id
    name
    items:
      item_1:
        id:
        name:
      ...

What I'm trying to achieve: Everyone should be able to create lists. They creator then becomes the owner of the created list. Only the owner and users in the "shared" document should be able to read and write to this list.

I guess that the permission settings should look something like this. But this isn't working:

service cloud.firestore {
  match /databases/{database}/documents {
    match /lists/{listId}/{anything=**} {
        allow read, write: if !exists(resource.data.users.owner) ||
                               resource.data.users.owner == request.auth.token.email ||
                               request.auth.token.email in resource.data.users.shared
    }
  }
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Marcel Bochtler
  • 1,291
  • 1
  • 14
  • 23

1 Answers1

4

I was able to figure it out.

I changed the data structure to this:

list
  list_1
    owner: owner@company.com
    writeAccess: [user1@company.com, user2@company.com]
    id
    name
    items:
      item_1:
        id:
        name:
      ...

Then the database rules like this are working:

service cloud.firestore {
  match /databases/{database}/documents {
    match /lists/{listId} {
        // Allow RW on lists for owner, shared user or for everyone if it's a new list
      allow read, write: if resource.data.owner == request.auth.token.email ||
                            request.auth.token.email in resource.data.writeAccess ||
                            !exists(/databases/$(database)/documents/lists/$(listId))
    }
    match /lists/{listId}/items/{itemId} {
        // Allow RW on item for owner or shared user of parent list
        allow read, write: if get(/databases/$(database)/documents/lists/$(listId)).data.owner == request.auth.token.email ||
                              request.auth.token.email in get(/databases/$(database)/documents/lists/$(listId)).data.writeAccess ||
                             !exists(/databases/$(database)/documents/lists/$(listId)) // Needed for new lists. Because lists and items are created in a batch
    }
  }
}
Marcel Bochtler
  • 1,291
  • 1
  • 14
  • 23
  • If you use a write-access list like this for sharing, how do you query all lists for one user then? Here they say it's impossible to get all posts with category cats if you use an array: https://firebase.google.com/docs/firestore/solutions/arrays. How did you solve that? – The Oddler Nov 05 '17 at 08:01
  • @TheOddler You are right, querying all lists of one user is not possible with this solution. To make this possible you can store the user's id as a map key. This is also the suggestion in the link you posted. Unfortunately dots are not allowed as a map key. So email addresses won't work. Also there is a known problem with permissions based on maps. See: https://stackoverflow.com/q/46674372/2837489 – Marcel Bochtler Nov 05 '17 at 11:40
  • Thanks for the answer! So that explains why it isn't working. I was using the uid in the map though, but still no cake. Thanks for the link! For now I'll hardcode my allowed testers, and keep an eye out for updates. – The Oddler Nov 05 '17 at 17:28
  • 1
    @MarcelBochtler do you have a link to the docs regarding 'dots are not allowed as a map key'? – ralphinator80 Mar 13 '18 at 17:18
  • @MarcelBochtler It is actually possible to use email addresses as a key, by using `new firebase.firestore.FieldPath()` to build the path in the `where()` query. https://firebase.google.com/docs/reference/js/firebase.firestore.FieldPath – aristidesfl Mar 25 '19 at 23:36
  • What if they change their email address? Use uid instead. – SacWebDeveloper Aug 02 '19 at 01:29