Firebase Firestore: custom admin access

后端 未结 2 2009
攒了一身酷
攒了一身酷 2021-02-14 05:49

In Firebase Firestore, I\'m trying to allow only (custom-assigned) admins to write/update/delete resources, and for that I\'ve got these security rules:

service          


        
相关标签:
2条回答
  • 2021-02-14 06:31

    Some points i noticed

    match /resources is pointing to a collection, that rules has no effect on its documents. here i am quoting from the doc

    Rules for collections don't apply to documents within that collection. It's unusual (and probably an error) to have a security rule that is written at the collection level instead of the document level.

    so you don't have to write rules for collections

    Then in the rules allow write, update, delete: you can say either allow write: or specifically allow create, update, delete: any of the three options or combine them.

    try this

    service cloud.firestore {
        match /databases/{database}/documents {
          match /resources/{resource} {
    
            function isAdmin() {
                return get(/databases/$(database)/documents/users/$(request.auth.uid)).isAdmin ||
                get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin;
            }
    
            allow read;
            allow create, update, delete: if isAdmin();
        }
      }
    }
    
    0 讨论(0)
  • 2021-02-14 06:38

    While writing my question, I made it work! I made two mistakes, both of which could have been avoided if I read the docs properly.

    Firstly, all calls to the service-defined function get needs to prefix the path with /databases/$(database)/documents/. So that this rule:

    allow write: if get(/users/$(request.auth.uid)).isAdmin;
    

    becomes this:

    allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).isAdmin;
    

    It's long, I know, but that's how it is. I'm not sure why Firestore isn't able to do that by itself, though, seeing as that same path prefix will stay the same across all calls to get, but perhaps this is for some future feature that isn't ready yet, like cross-database querying or something.

    Second, the get function will return a resource, which in turn you'll need to call .data on to get the actual data that it contains. Thus, instead of doing this:

    get(/path/to/user/).isAdmin
    

    you'll need to do this:

    get(/path/to/user/).data.isAdmin
    

    Now I just wish I was able to extract that logic into a user-defined function:

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

    But doing so results in a PERMISSION_DENIED again, and without knowing what's actually going on in the function, I'm not sure if I'll spend more time trying to figure this out now.

    UPDATE: @Hareesh pointed out that functions must be defined within the scope of a matcher, so it's possible to put the function in the default top-level matcher like this:

    service cloud.firestore {
      match /databases/{database}/documents {
        function isAdmin() {
          return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin == true;
        }
    
        // ...
      }
    }
    
    0 讨论(0)
提交回复
热议问题