devdanetra
devdanetra

Reputation: 13

Firebase rules acting very strange

Hello to everyone reading this.

I am coding a flutter app for an hospital, that has this db structure.

Database Model

I am having an issue fetching sessions data, exactly the following document.

Document that i am trying to request

Using the following method to get the lastSession a therapist made, using his therapistUID as the filtering field.

Future<Session> getLastSession() async {
 Query query;
 query = Firestore.instance
     .collection("sessions")
     .where("therapistUID",
         isEqualTo: this.uid)
     .orderBy("date", descending: true)
     .limit(1); //this.uid = auth uid of current therapist.
 try {

  QuerySnapshot querySnapshot = await query.getDocuments(); //exception thrown here

  if (querySnapshot.documents.isEmpty) {
    throw Exception("Empty query");
  } else {
    lastSession = Session.fromDocument(querySnapshot.documents[0]);
    return lastSession;
  }
} catch (e) {
  throw Exception("cannot get data from database");
}}

with the following rules

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

match /patients/{document=**} {
  allow read,write,list: if checkPatientAccess(resource.data);
}

match /therapists/{document=**} {
  allow read,write,list: if checkOwnership();
}

match /sessions/{document=**} { 
    allow read, write,list: if checkPatientAccess(get(/databases/$(database)/documents/patients/$(resource.data.patientUID)).data);
}

match /devices/{document=**} {
    allow read, write,list: if false;
}

match /clinics/{document=**} {
    allow read, write,list: if false;
  }}}

function checkOwnership(){
  return resource.id == request.auth.uid;
}

function checkPatientAccess(patient){
  return request.auth.uid in patient.therapistUIDs;
}

Code is throwing this exception

Exception

Does anyone know why is it rejecting the query? keep in mind query is only one document, and as well there is only one document in the database that could fit those filters. Using testlab with same parameters works.

Upvotes: 0

Views: 82

Answers (2)

Frank van Puffelen
Frank van Puffelen

Reputation: 598648

Firebase security rules do not on their own filter data, as that would not scale. This becomes clear when we look at:

match /sessions/{document=**} { 
    allow read, write,list: if checkPatientAccess(get(/databases/$(database)/documents/patients/$(resource.data.patientUID)).data);
}
function checkPatientAccess(patient){
  return request.auth.uid in patient.therapistUIDs;
}

In order to secure your read operation, these rules would have to load each document and check the therapistUIDs value in there. This would be an O(n) operation, while Firestore is guaranteed to return results on O(1). For this reason, such security rules don't work.

Your rules do work for reading a single document, but not for the list operation.

If you can come with with a query that returns the data that you want, you may be able to secure that query. But since Firestore doesn't support any type of join in queries, you'd need to replicate the data you want to filter on from the patient document into each session document in order to make this work.


As discussed in the comments: Since your query ensures all documents have the same patientUID, the get() call in your rules is guaranteed to always get the same document, and thus the rules engine can guarantee that it will never return an authorized document for the query.

Pretty nifty actually.

Upvotes: 1

Doug Stevenson
Doug Stevenson

Reputation: 317322

It doesn't matter how many documents you request - Firestore security rules will not act as a filter on those documents. Please read and understand this documentation. It won't let you conditionally check something for each document to determine if it can be read. Your rules are trying to express that something must exist in a matching patient document for each session read, but that's not allowed. It simply will not scale the way that Firestore requires, and would be extremely costly for queries with large result sets.

Upvotes: 1

Related Questions