0
votes

I've a firestore database and I now need to add a new collection.

Each entry of this collection should contain:

  1. Which userId is the owner(field admin)
  2. Which userId has been allowed to edit this element(field writer)
  3. Which userId has been allowed to only read(field reader).

I'm currently only at the first step, and already strugling:

I was hoping to be able to query my collection( /trips/) and get only the one that I'm allowed to access, but I get an error:

FirebaseError: Missing or insufficient permissions.

Here is my rules file:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
    match /users/{userId} {
        allow read, update, delete: if request.auth != null && request.auth.uid == userId;
        allow create: if request.auth != null;
    }
    match /trips/{trip} {
        allow read, update, delete: if request.auth != null && request.auth.uid == resource.data.admin;
        allow create: if request.auth != null;
    }
  }
}

So my questions:

  1. Is this the correct way of managing resource that must be acceeded by multiple people(meaning, I cannot just have the userId in the path since there are multiple users)
  2. How should I query only the documents list that I'm allowed to see?

Thank you very much for your help

2

2 Answers

1
votes

As you will read in the doc, "All match statements should point to documents, not collections".

With

service cloud.firestore {
  match /databases/{database}/documents {

    match /trips {
       // ....
    }
  }
}

you don't point to a document. You should use a wildcard to point to any document in the specified path, as follows:

service cloud.firestore {
  match /databases/{database}/documents {

    match /trips/{trip} {
       // ....
    }
  }
}

Therefore the following should correctly implement your requirements:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    match /trips/{trip} {
        allow read: if request.auth != null && 
        (request.auth.uid == resource.data.admin 
         || request.auth.uid == resource.data.writer
         || request.auth.uid == resource.data.reader
        );
        allow update: if request.auth != null && 
        (request.auth.uid == resource.data.admin 
         || request.auth.uid == resource.data.writer
        );
        allow create: if request.auth != null;
    }
  }
}

Then, for the two questions:

Is this the correct way of managing resource that must be acceeded by multiple people (meaning, I cannot just have the userId in the path since there are multiple users)

If the admin, writer and reader are specific for each document, yes this is the correct way. If those roles would be more global (e.g. all the trips to Europe can be edited by the same user), you could use a role based approach with Custom Claims.

How should I query only the documents list that I'm allowed to see?

It is important to note that rules are not filter. So your query for getting docs needs to be aligned with the rules. In your specific case, you could have an additional field of type Array which contains three values; the uids of the admin, writer and reader, and use the array-contains operator. Something like:

const user = firebase.auth().currentUser;
const query = db.collection("trips").where("authorizedReaders", "array-contains", user.uid);
0
votes
match /{document=**} {
      allow read, write: if false;
    }

You don't need the above code as it will apply to all routes of the database, because of the above line you are getting the below error as it does not allow you to read and write to the database

FirebaseError: Missing or insufficient permissions.

Now, if you want to assign privileges to users then you should add the Role field to users collections which would have a value such as Admin, Editor, Reader

Then, you can check in routes something like below

match /users/{userId}/trips/{tripId} {
        allow read, delete: if request.resource.data.role == "Admin";
        allow create, update: if request.resource.data.role == "Admin || request.resource.data.role == "Editor";
    }

If you want to know more about how to create a route check out this video for the best explanation