19

The solution is in the end of the post. Check it out.

Решение проблемы в конце поста. Дочитайте.

just a simple question: whats wrong with this and why this is not working?

Trying to get access with user who has role 'admin' in users section to the /titles/{anyTitle} but still get

Missing or insufficient permissions.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow write: if false;
      allow read: if false;
    }
    function userCanWrite () {
      return get(/databases/{database}/documents/users/$(request.auth.uid)).data.role == "admin";
    }
    match /titles/{anyTitle=**} {
      allow read: if request.auth != null;
      allow write: if userCanWrite();
    }
  }
}

Here is my database structure

P.S.

I tried another rule from official documents get(/databases/{database}/documents/users/$(request.auth.uid‌​)).data.isAdmin == true;

and this is not working too

UPDATE: CORRECT WAY TO DO IT

Support helped me find the solution this is how you should do:

db structure:

users -> {{ userid }} -> { role: "admin" }

database rule settings:

get(usersPath/$(request.auth.uid)).role == "admin" || get(usersPath/$(request.auth.uid)).data.role == "admin";

numfin
  • 301
  • 1
  • 2
  • 6
  • he is dont care about it. Something wrong with get function. – numfin Oct 08 '17 at 09:03
  • 1
    (What `he` are you referring to?) – greybeard Oct 08 '17 at 09:07
  • firestore security rules. He is dont care about ' or " or `. Im trying to restructure my users collection now. But im really confused why get(/da.../users/$(request.auth.uid)).data.role is not really doc users/{uid}.role – numfin Oct 08 '17 at 09:13
  • `firestore security rules [don't] care about ' or " or \`` I was suggesting backticks as alternative markdown to make *Missing or insufficient permissions* stand out - `**` for **bold** works, too (obviously). – greybeard Oct 08 '17 at 09:17
  • my eng is soooo bad. Im not understand everything you say to me :) sorry about that. But i tried another rule from official documents now get(/databases/{database}/documents/users/$(request.auth.uid)).data.isAdmin == true; and this is not working too – numfin Oct 08 '17 at 09:23
  • *tried another rule from official documents now `get(/databases/{database}/documents/users/$(request.auth.uid‌​)).data.isAdmin == true`; and this is not working too* please edit additional information into your post - don't "hide" it in a comment. – greybeard Oct 08 '17 at 09:26
  • I'm facing this error too. Maybe it's a firestore error? – Joan Picornell Oct 09 '17 at 07:38
  • already answered – numfin Oct 12 '17 at 15:04
  • Man, this is literally exactly what I'm trying to do. Unfortunately, the proposed solution does nothing... Anyone else not seeing this work? – Andrew M. Jan 19 '18 at 04:50
  • It doesn't work for me. I always get true no matter if my path or data is existing. `get(/databases/$(database)/documents/crap).data.whatsoever == true ` – stackovermat Feb 19 '19 at 11:13
  • This doesn't work for me. 2021 and the get() function is still broken – user1447414 Feb 23 '21 at 21:35

5 Answers5

20

I contacted to the Firebase support to report that bug and they gave me a temporary solution on this. It seems that they are having a bug in their systems on the security rules side. They say that the documentation is ok, but for now we should workaround this way:

get(path).data.field == true || get(path).field == true;

Because the bug is that data object isn't populated, you should check both properties. There's no ETA for launching a solution on this bug, so I asked they if they could give me an advice when they solved this issue, so I'll keep this answer up-to-date with their information.

Joan Picornell
  • 405
  • 3
  • 6
  • Thanks, been fiddling with this issue for 2 days. Do you know if the same goes for the normal `resource` var. Does it become `resource.field` instead of `resource.data.field`? – DarkNeuron Oct 12 '17 at 12:46
  • @DarkNeuron Actually I don't know. I think this doesn't affect the resource var. If you try to access it with data and don't work, contact to google [firestore support](https://firebase.google.com/support/contact/bugs-features/) – Joan Picornell Oct 14 '17 at 01:06
  • 1
    @JoanPicornell any update on this one? I did I test today, and it seems like it works only with `get(path).data.field == true` – DauleDK Mar 03 '18 at 09:00
  • I posted : https://stackoverflow.com/questions/73668342/firestore-security-rule-that-checks-field-under-random-document-in-collection-to . Did not get that tip, but I think I tried both without luck. They should update their docs at least, they are minimal at best and incomplete and sometimes wrong even. Great product but it needs some more love and maintenance. – Vincent Gerris Oct 27 '22 at 21:36
5

So the way I've solved it is I've created another Collection Called admins

Then I've just added the uid of the user I needed there as such - Here is my database structure - https://i.stack.imgur.com/U2YnN.png

And here is the rules

service cloud.firestore {
  match /databases/{database}/documents {

    function isAdmin() {
      return exists(/databases/$(database)/documents/admins/$(request.auth.uid));
    }

    match /tasks/{anyTask} {
    allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null && isAdmin();
      allow delete: if request.auth != null && isAdmin();
    }
  }
}

You can view my full Open Source project here: https://github.com/metaburn/doocrate

Gal Bracha
  • 19,004
  • 11
  • 72
  • 86
5

You should use $(database) instead of {database} in your code:

get(/databases/{database}/documents/users/$(request.auth.uid)).data.role == "admin";
Myroslav
  • 896
  • 12
  • 21
  • That doesn't work for me. In this case I get the error `Function not found error: Name: [get].` – colin Oct 29 '19 at 03:35
  • @colin not sure why you're getting this error. Some answer may be found in this post https://stackoverflow.com/questions/55955938/function-not-found-error-name-get-in-firestore-security-rules-simulation – Myroslav Apr 22 '21 at 07:47
1

What worked for me was moving the userCanWrite function above my rules. It appears that the function has to be defined before any of the match rules that call it. Maddening :-)

Derrick Miller
  • 1,860
  • 3
  • 21
  • 37
  • I read this and thought, wrong. I remember the documentation talking about hoisting. Plus, I have other functions that work that are defined below the matches. But no matter what I could not get my "get()" function to work until I tried this. It also works to just bump your function definitions to the same bracket scope as the match statements. Anyway, thank you! – DjH Jun 21 '19 at 22:12
0

This is the Firestore rule I use to check if the user is admin.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {


match /{document=**} {
  allow read: if true;
  allow write: if userIsAdmin();
}


function userIsAdmin() {
    return getUserData().userRole == 'Admin';
}

function getUserData() {
    return get(/databases/$(database)/documents/User/$(request.auth.uid)).data;
}

  }
}
TSM
  • 1,225
  • 1
  • 9
  • 17