6

I want to implement role based restrictions in my application. I have the user role information in one collection documents. Now I want to write rules to restrict different Write, Update operation on other collections of the database.

Since I am using Firestore database with React-Native I only pass the respective collection's document info while inserting/updating. So how can I pass the user's role information along with this so that my rule gets authenticated and that data doesn't go into other collection.

One example depicting above scenario:

/collection1/document1
{
   prop1: value1,
   prop2: value2,
   role: "WRITE"
}

/collection1/document2
{
   prop1: value1,
   prop2: value2,
   role: "READ"
}

Now consider that current logged in user is document2.

I have another collection:

/collection2/doc1
{
   userRef: document1, //this is id of document1 from collection1
   ...
}

I want to configure firestore rule for collection2 that if request has come from user with role="WRITE" then only allow it to isert/update documents.

Have read many articles and ways but any of them does not satisfy this use-case.

Any help would be appreciated.

Hriday Modi
  • 2,001
  • 15
  • 22
  • "Now consider that current logged in user is `document2`". I'm having a hard time understanding what this means. How does the logged in user relate to the document? It might help if you give your collections and documents meaningful names for the use-case you're asking about. – Frank van Puffelen Oct 21 '18 at 23:06
  • @FrankvanPuffelen Consider that logged in user is being represented by document2 which has role of "READ". I want to make sure that this user only gets to read the documents within my app. Now your doubt could be why is user represented by document2? Here i would like to say that consider this user belongs to one client for which it has READ access. The same user can also have access to second client for which it has WRITE access. Hence storing role information in firestore `/users` collection doesn't work. Hope this clears your doubt. – Hriday Modi Oct 22 '18 at 20:12
  • It's a little hard to follow the question but I would advise watching this video, even though its for angular, it's still firebase/firestore and it tackles how to conceptually introduce role-based auth into a firestore application. https://www.youtube.com/watch?v=1PEdd2rtG30 – Isuru Fonseka Oct 29 '18 at 11:21
  • @IsuruFonseka In video they are using firestore's "/users" collection to store Role information. While as mention above in my comment, my use-case is different. In my case user can have different role on different client, i.e. `user1` can have ADMIN access on client1 while same user can have READ access on client2. Hence here Role is based on User+Client and not only on User, so solution provided in video won't work. – Hriday Modi Oct 30 '18 at 20:47

1 Answers1

3

To make the rules easier to read, you can create a function that gets the user role, then use in the conditions. This is how the firestore.rules file would look like :

service cloud.firestore {
  match /databases/{database}/documents {
    match /collection2/{doc} {
     allow read: if getUserRole() === 'READ' || getUserRole() === 'WRITE';
     allow create: if getUserRole() === 'WRITE';
     allow update: if getUserRole() === 'WRITE';
    }
    function getUserRole() {
        return get(/databases/$(database)/documents/collection1/$(request.auth.uid)).data.role;
    }
  }
}
ibenjelloun
  • 7,425
  • 2
  • 29
  • 53
  • Thanks for the answer. This is the correct solution but my main question is, In `getUserRole` function I want to user some other parameter rather that `request.auth.uid`. The question is how can I pass this other parameters in `request.auth`? – Hriday Modi Oct 30 '18 at 21:05
  • You mean parameters from the client side ? – ibenjelloun Oct 30 '18 at 21:57
  • 1
    Claims-based auth should do the trick for you, check this out. https://firebase.google.com/docs/auth/admin/custom-claims – Isuru Fonseka Oct 31 '18 at 13:52
  • @ibenjelloun Yes, parameters from client side. So in `getUserRole` method we are doing `/databases/$(database)/documents/collection1/$(request.auth.uid)`, here instead of `request.auth.uid` I want to pass documentId of collection1 object in `request.auth` object. How can I pass that? – Hriday Modi Oct 31 '18 at 17:13
  • If you let the user inject something in the rules with parameters, it will make the app weak and easy to hack. Keep in mind that every request coming from the browser can be modified by the user. User1 can ask to change data as user2 if he can send that as a request parameter. If you want to make a controller with parameters, you can use the cloud functions, you will have the possibility to specify the request and response prototype as you want. – ibenjelloun Oct 31 '18 at 18:30
  • I agree @ibenjelloun. I will think about cloud functions. – Hriday Modi Nov 01 '18 at 09:42