1
votes

I currently have security rules that allow a user to read only from a collection named "153".

In Android, I access Firestore and call .collection("153").get()... on the Firestore instance to retrieve data from that collection.

If, instead, the Android code reads .collection("999").get..., the permission is denied, as it should be.

Security Rules:

service cloud.firestore {
  match /databases/{database}/documents {
    // FULL ADMIN ACCESS
    match /{document=**} {
      allow read, write: if request.auth.uid == 'XYZ1234XYEDFC';
    }
    // 153 READ
    match /153/{x} {
        allow read: if request.auth.uid == 'XYZ12234XTZ';
    }
    // 999 READ
    match /999/{x} {
        allow read: if request.auth.uid == 'ABC1234ABC';
    }
  }
}

What is the method to allow an app user, who is signed in via Firestore, to only read a collection they have access to in Security Rules without hardcoding the collection?

Edit:

I have an existing database in Firestore. The database has many top-level collections with documents in each. Each app user will have access to one or many of the collections.

Right now, I can only see how to call upon a specific collection by giving the collection path. Can I get all collections that a UID has access to, programmatically?

2
Could you edit the question to illustrate better when your last sentence means? Are you working with an existing database structure? Please also show your existing security rules.Doug Stevenson
I have edited and added my security rulesMcGuile

2 Answers

1
votes

You can use a document within Firestore to configure Firestore rules. Here's an example - you'll likely have to modify it to meet your specific needs, based on your actual collections.

Firstly, you're going to need a way to identify which collections should have this per-user authentication. Let's assume, for simplicity, that the collection name starts with an "X". Your pattern may differ. But you do absolutely need a way to distinguish them from other collections that shouldn't have special protection, otherwise you won't be able to store other types of data.

Second, define a new collection called "config", and create a document in there called "permissions". This document can contain fields for each of the collections to protect. The value of these fields will be the UID that can access all documents in the collection. Here: UID1 can access Xcollection1, and UID2 can access Xcollection2:

/config/permissions
  Xcollection1: UID1
  Xcollection2: UID2

Now, add this rule:

match /{coll}/{document=**} {
  allow read, write: if coll[0] == "X" && get(/config/permissions).data[coll] == request.auth.uid
}

What this rule does is firstly check to see if the collection being accessed (wildcard coll) should have per-user protection (it begins with "X"), and secondly check the contents of the /config/permissions document to see if the user's UID (request.auth.uid) has been assigned access to the collection.

You may want something different or more sophisticated, but the idea is that you can use a document in Firestore to configure Firestore rules using the get() function.

0
votes

I also had the same problem and now I found a alternate way to solve this. First of all make a spinner which contains the name of collections[make sure collection names and spinner item names should be same] and user will be able to select a collection they belong to[considering user know which collection they have to choose].Considering the above security rule, programmatically you can implement it by passing the spinner value: i.putExtra("any keyword",variable_ name);

In onCreate:

 ArrayAdapter<CharSequence> adapter1=ArrayAdapter.createFromResource(LoginActivity.this,R.array.Company, android.R.layout.simple_spinner_item);
    adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter1);
    spinner.setOnItemSelectedListener(this);

In login method:

firebaseAuth.signInWithEmailAndPassword(email, password)
                    .addOnCompleteListener(LoginActivity.this, new OnCompleteListener<AuthResult>() {

                        @Override
                        public void onComplete(@NonNull Task<AuthResult> task) {


                            if (task.isSuccessful()) {
                                pd.setVisibility(View.GONE);
                                Toast.makeText(LoginActivity.this, "Welcome", Toast.LENGTH_SHORT).show();
                                String text1 = spinner.getSelectedItem().toString();
                                Intent i= new Intent( getApplicationContext(),MainActivity.class);
                                i.putExtra("spin",text1);
                                startActivity(i);
                            }

In MainActivity :

Intent intent =getIntent();
    String a = intent.getStringExtra("spin"); //'a' stores the value of collection
    switch(a){
        case "153":
            showData1(a); //This method will retrieve data in that particular collection with a parameter used 
            break;
        case "999":
            showData1(a);
            break;
    }

In showData:

private void showData1(String col) {
    pd.getProgress();
    db.collection(String.valueOf(col))
            .get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {.......

That's the method I found out to allow a particular user to access a particular collection.