Eaedev
Eaedev

Reputation: 319

Firestore retrieve multiple documents security rules

I have a recyclerview in my app which with a get, it retrieves all the documents in a collection. Each document contains the uid of the person who created it. I would like to implement a security rule in which you retrieve only the documents matches the uid of the owner. It looks like quite trivial, but I am not able to make it working.

Database has a structure like this

cars
|
|-documentA
|-documentB
|-documentC

an each document:

documentA
|
|
|-fieldA: aaa
|-fieldB: bbb
|-userId: aklsjdaosjfpasf

Security rules at firestore

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{cars=**}/{carId}  {
    allow read: if request.auth.uid == resource.data.userId;
    allow write: if request.auth.uid != null;
  }
  }
}

EDIT query:

query = firestore.collection("cars").limit(10L)

Firestore Adapter for make queries for the recyclerview

public abstract class FirestoreAdapter<VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH> {

    private static final String TAG = "Firestore Adapter";

    private Query mQuery;
    private ListenerRegistration mRegistration;

    private ArrayList<DocumentSnapshot> mSnapshots = new ArrayList<>();

    public FirestoreAdapter(Query query) {
        mQuery = query;
    }

    public void startListening() {
        // TODO(developer): Implement
    }

    public void stopListening() {
        if (mRegistration != null) {
            mRegistration.remove();
            mRegistration = null;
        }

        mSnapshots.clear();
        notifyDataSetChanged();
    }

    public void setQuery(Query query) {
        // Stop listening
        stopListening();

        // Clear existing data
        mSnapshots.clear();
        notifyDataSetChanged();

        // Listen to new query
        mQuery = query;
        startListening();
    }

    @Override
    public int getItemCount() {
        return mSnapshots.size();
    }

    protected DocumentSnapshot getSnapshot(int index) {
        return mSnapshots.get(index);
    }

    protected void onError(FirebaseFirestoreException e) {};

    protected void onDataChanged() {}
}

Upvotes: 1

Views: 373

Answers (2)

SteapStepper69
SteapStepper69

Reputation: 1295

Based on the comments, I think this might be the solution. According to the OP, all users should be able to register a car, but only the user who created a car should be able to see it.

The correct permissions in this case would look like this:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /cars/{carId}  {
      allow read, write: if request.auth.uid == resource.data.userId;
      allow create: if request.auth.uid != null;
    }
  }
}

For rules pre version 2:

There is also a problem with the permission URL, you can use the following syntax to match all documents in a collection: /cars/{documemts=**}/, but your path /{cars=**}/{carId} is invalid, thereofore the rule will never be enforced for your query.

The path /cars/{carId} will refer to any document in the collection 'cars', storing the name of the document as 'carId'.

Upvotes: 1

Renaud Tarnec
Renaud Tarnec

Reputation: 83068

You have to note that security rules are not filters, as explained in the documentation:

Once you secure your data and begin to write queries, keep in mind that security rules are not filters. You cannot write a query for all the documents in a collection and expect Cloud Firestore to return only the documents that the current client has permission to access.

With

query = firestore.collection("cars").limit(10L)

you are querying independently of the userId, hence the problem.

You need to adapt your query with whereEqualTo() as follows:

//get the value of uid
query = firestore.collection("cars").whereEqualTo("userId", uid).limit(10L)

Upvotes: 2

Related Questions