Sulman Azhar
Sulman Azhar

Reputation: 1079

Firestore rules to read only one collection and rest of the database restricted

So I have a project where I have some predefined rules already set which includes allow database read if user is authenticated and then allow some seperate stuff which i don't know much but when i integrated stripe in my firebase then stripe asked me to add those in my firestore rules.

Now i want to allow everyone to read from one specific collection and its one subcollection but i am not being able to do that

Before my rules were this

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read: if
          request.auth != null;
    }
  }
  match /databases/{database}/documents {
    match /users/{uid} {
      allow read: if request.auth.uid == uid;

      match /checkout_sessions/{id} {
        allow read, write: if request.auth.uid == uid;
      }
      match /subscriptions/{id} {
        allow read: if request.auth.uid == uid;
      }
    }

    match /products/{id} {
      allow read: if true;

      match /prices/{id} {
        allow read: if true;
      }

      match /tax_rates/{id} {
        allow read: if true;
      }
    }
  }
}

After they are these

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if
          request.auth != null;
    }
     match /organization/{document=**} {
      allow read: if true;     
    }
  }
  match /databases/{database}/documents {
    match /users/{uid} {
      allow read: if request.auth.uid == uid;

      match /checkout_sessions/{id} {
        allow read, write: if request.auth.uid == uid;
      }
      match /subscriptions/{id} {
        allow read: if request.auth.uid == uid;
      }
    }

    match /products/{id} {
      allow read: if true;

      match /prices/{id} {
        allow read: if true;
      }

      match /tax_rates/{id} {
        allow read: if true;
      }
    }
  }
}

But none of them are allowing users to read the organization collection publically

This is my database with some collections and i only want organization + organization's subcollection campaign to be publicly available.

enter image description here

Upvotes: 6

Views: 1706

Answers (1)

Priyashree Bhadra
Priyashree Bhadra

Reputation: 3597

Your rules :

rules_version = '2'; 
service cloud.firestore { 
match /databases/{database}/documents { 
match /{document=**} { 
allow read, write: if request.auth != null;
 } 
match /organization/{document=**} { 
allow read: if true; 
} } 
match /databases/{database}/documents { 
match /users/{uid} { 
allow read: if request.auth.uid == uid; 
match /checkout_sessions/{id} {
allow read, write: if request.auth.uid == uid; } 
match /subscriptions/{id} { 
allow read: if request.auth.uid == uid; 
} 
} 
match /products/{id} { 
allow read: if true; 
match /prices/{id} { 
allow read: if true; 
} 
match /tax_rates/{id} { 
allow read: if true; 
} } } }

Rules modified that works :

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
     match /organization/{document=**} {
      allow read: if true;     
    }
   
    match /users/{uid} {
      allow read: if request.auth.uid == "priya";
        }
      match /checkout_sessions/{id} {
        allow read : if request.auth.uid == "priya";
        allow write: if request.auth.uid == "priya";
      }
      match /subscriptions/{id} {
        allow read : if true;
      }
      match /products/{id} {
      allow read: if true;
    }
      match /prices/{id} {
        allow read: if true;
      }
      match /tax_rates/{id} {
        allow read: if true;
      }
    }
  }

Explanation :

  1. I have removed the :

    match /{document=**} { 
    allow read, write: if request.auth != null; } 
    

    match /{document=**} matches all documents in the entire database. The wildcard there actually "gobbles up" the entire path of the document, for the purpose of further matches. You also nested match /organization/{document=**} underneath it, which doesn't actually have any meaning (as you can't nest more documents under an outermost document). My rules work because I am matching the organization collection at the top level, not nested under anything else.

    Source :

    https://stackoverflow.com/a/55719394/15803365 https://firebase.google.com/docs/rules/basics#default_rules_locked_mode

  2. I have changed :

        match /organization/{document=**} { 
            allow read: if true;
    
    
    
    To 
    
        match /organization/{org}/campaign/{document=**} {
              allow read: if true;     
            }
    
    As you specified, you only want organization + organization's
    subcollection campaign to be publicly available.  I have designed a
    Firestore security rule that allows only documents and
    subcollections inside your campaign sub collection to have public
    read access. If you try to give documents under organization
    collection public read access, it will be denied. As the public read
    access only applies to documents/subcollections under campaign sub
    collection which is under organization collection, If you want any
    collections/documents inside organization to have public read
    access, you can change this to 
    
    match /organization/{document=**}{ 
        allow read: if true;  }
    

    Source :

    https://firebase.google.com/docs/firestore/security/rules-structure#recursive_wildcards

  3. match /databases/{database}/documents {

    This was a duplicate/ repeat of the first
    /databases/{database}/documents and it pretty much means matching to
    the default database we have and it's where all our Firestore rules
    should be inside.  Creating another /databases/{database}/documents
    is not correct and does not make sense.
    

    Source :

    https://firebase.google.com/docs/firestore/security/rules-structure#overlapping_match_statements

You should test your rules, using the Firestore Simulator. The above rules were checked and modified following this documentation and this video

How to test the rules?

Open your Firebase Console. Go to Firestore Database, Click on Rules tab as highlighted in this screenshot. Click on Rule Playground and here you can simulate and test your rules.

If you are checking for public read access, change the simulation type to ‘get’, specify the exact location path for which you want to check the rule in Location field for eg. subscriptions/{id} for your match /subscription/{id} rule. Set Authentication to off. Run the simulator by clicking on Run button and you will get a green/red message specifying your rule was tested successfully/denied respectively.

enter image description here

If you are checking for authenticated read access, change the simulation type to ‘get’, specify the exact location path for which you want to check the rule in Location field .For eg. users/{uid} for your match /users/{uid} rule. Set Authentication to on. Specify the Firebase uid, email, name, phone number with some random values. Run the simulator by clicking on Run button and you will get a green/red message specifying your rule was tested successfully/denied respectively. Here you have to keep in mind that request.auth.uid should be equal to uid. As we are testing it on a simulator, we hard code the values in uid, as request.auth.uid is already set when we give value to Firebase uid. When in production, you can set uid in your application.

enter image description here

If you are checking for authenticated write access, change the simulation type to ’create’, specify the exact location path for which you want to check the rule in Location field .For eg. checkout_sessions/{id} for your match /checkout_sessions/{id} rule. Set Authentication to on. Specify the Firebase uid, email, name, phone number with some random values. Run the simulator by clicking on Run button and you will get a green/red message specifying your rule was tested successfully/denied respectively. Here you have to keep in mind that request.auth.uid should be equal to uid. As we are testing it on a simulator, we hard code the values in uid, as request.auth.uid is already set when we give value to Firebase uid. When in production, you can set uid in your application.

enter image description here

Upvotes: 4

Related Questions