w461
w461

Reputation: 2678

How to restrict access to a document and all of its SUBCOLLECTIONS in FireStore based on a user group/role? (without group name in each document)

I found this nice answer to my question, here.

match /leagues/{league}/{document=**} {
    allow read, write: if request.auth.uid ==
        get(/databases/$(database)/documents/leagues/$(league)).data.creator
}

However, I do not get it to work. My implementation looks like this:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{user} {
        allow read, write: if false;
    }
    match /employees/{user} {
      allow read, write: if false;
    }
 //   match /companies/{document=**} {
 //     allow read, write: if false;
 //   }
    function isSignedIn() {
      return request.auth != null;
    }
    function getEmployeeData() {
      return get(/databases/$(database)/documents/employees/$(request.auth.uid)).data
    }
    // check if the current user has access to specific company
    function accessCompany(companyId) {
      return isSignedIn() && getEmployeeData()['companyId'] == companyId;
    }
  }
  
  match /users/{user} {
    allow get: if true;
    allow list, create: if false;
    allow update, delete: if request.auth.uid == user;
  }

  match /employees/{user} {
    allow get: if request.auth.uid == user || hasRole('admin');
    allow list: if hasRole('admin');
    allow update: if hasRole('admin');
    allow create, delete: if false;
  }
  
  match /companies/{company}/{document=**} {      // <<<<<<<<<<<<<< IMPLEMENTATION HERE
    allow read, write: if getEmployeeData()['companyId'] == 
      get(/databases/$(database)/documents/companies/$(company)).data.id;
        }
}

So with request.auth.uid I lookup the document id of the company document in the employees collection. Then I compare this with the id of the requested company (sub) document companies/$(company)).data.id.

I also tried

  match /companies/{company}/{document=**} {
    allow read, write: if getEmployeeData()['companyId'] == company;

Does anyone see my mistake or misunderstanding?

Upvotes: 0

Views: 226

Answers (2)

LeadDreamer
LeadDreamer

Reputation: 3499

The function needs to be "anywhere upchain" from the match statement. I use a very deep database structure, with convenience functions at many levels. In your case, you could put the function like so:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
      function getEmployeeData() {
        return get(/databases/$(database)/documents/employees/$(request.auth.uid)).data;
      }

and it would be available to all of your other match statements.

Upvotes: 1

w461
w461

Reputation: 2678

My blueprint for this was quite misleading. The functions need to be located within the calling match statement. Now it works

match /companies/{company}/{document=**} {
      function getEmployeeData() {
        return get(/databases/$(database)/documents/employees/$(request.auth.uid)).data;
      }
      allow read, write: if getEmployeeData()['companyId'] == company;
}

Upvotes: 0

Related Questions