Firestore rules - List collections returning only

Viewed 84

I know that the answer to this question is: Firebase rules is not a filter system! But I've this case (for a chat app with rooms) and I've a problem with security.

Rooms collection

  • ...info about room...
  • participants: [id1, id2, id3]
  • Messages subcollections

When the user open his rooms view, he should see only the rooms when his id is within "participants" array. I can perform it with a query, and all works fine, but ...

Is there a way to deny the list operation on all collection to avoid that user could see all rooms of all users? I know that I can use a function to restrict the access, but I'm wondering if there are some ways to do it with firestore rules.

My security rules:

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

function authed() {
  return request.auth.token.firebase.sign_in_provider == 'password' && request.auth != null && request.auth.token != null && request.auth.token.email != null;
}

function dataExists() {
  return resource != null && resource.data != null;
}

function isConversationVisible() {
  return dataExists() && resource.data.visible == true
}

function userInConversation() {
  let email = request.auth.token.email;
  return dataExists() && email in resource.data.participants;
}

function userInConversationUsingGet(convId) {
  let email = request.auth.token.email;
  let conv = get(/databases/$(database)/documents/conversations/$(convId));
  return email in conv.data.participants;
}

match /conversations/{convId} {
  allow create, delete, update: if false;
  allow list, get: if authed() && isConversationVisible() && userInConversation();

  match /messages/{messageId} {
    allow create, read, list: if userInConversationUsingGet(convId);
    allow delete, update: if false;
  }
}

} }

And my java code to test it:

NOT WORKS
sChatQuery = sChatCollection
                .whereArrayContains("participants", "myemail@me.com")
                .whereEqualTo("type", type)
                .whereEqualTo("visible", true)
                .orderBy("timestamp", Query.Direction.DESCENDING);

NOT WORKS
FirebaseFirestore.getInstance()
                .collection("conversations")
                .get(Source.SERVER)

Both snippets returns PERMISSION_DENIED.

An example of my firestore:

conversations
-- 61a8ec6c99791eb2f7ab5c69
---- participants ["myemail@me.com", "mail1@me.com", "mail2@me.com"]
-- 61a8ec6c99777sjskny77ahw
---- participants ["mail1@me.com", "mail2@me.com"]
-- 61a8ec6c9977718881666618
---- participants ["myemail@me.com"]

UPDATE I suppose that the query fails because "myemail@me.com" is not within all documents (reading official docs). But I'm wondering, how can I deny that a user perform a query passing arbitrary email address? I'm thinking a rule that evaluates the email value passed on whereArrayContains is equal to request.auth.token.email. But I don't found a way to retrieve the parameter passed on query from firestore rules.

1 Answers

I'm not sure if I understand you correctly, but if you want to disallow anyone listing all collection, you can take away the list permission and only allow get operations. For more on this, see the Firebase documentation on allowing granular operations.

If you want to allow a user to get a limit list, you'll need to encode the condition you have in your query in your security rules too. So you'd have to check whether the request.auth.uid is present in the participants array of the documents being requested.

Note: I haven't tested this specific scenario myself, but the rules engine is often surprisingly capable in statically determining that the code is requesting data that it is authorized to do.


Update

In the code you shared, you're trying to read the entire collection:

FirebaseFirestore.getInstance().collection("conversations")

Firebase's security rules don't filter data, but instead merely ensure that the code is only requesting documents that it is authorized for. So you will have to filter in a query and then check for that condition in your rules too.

Related