Firebase Firestore Security Rules Variable not use

2019-08-02 18:19发布

问题:

Writing rules for Firestore it seems that custom variables are not working. Did anyone know why or have seen similar behaviour? Using the below I got access denied although the uid is in the array of admin.

service cloud.firestore {
      match /databases/{database}/documents {
        match /conferences/{confid} {
          allow read,write: if request.auth.uid in get(/databases/$(database)/documents/conferences/$(confid)).data.admin;
        }
      }
    }

Simulator is giving the below error:

Function [get] called with path to nonexistent resource: /databases/%28default%29/documents/conferences/%7Bconfid%7D

Also testing this on a real devices I got access denied.

If however I use the ID of the document like below it works and access is granted.

service cloud.firestore {
  match /databases/{database}/documents {
    match /conferences/{confid} {
      allow read,write: if request.auth.uid in get(/databases/$(database)/documents/conferences/ySWLb8NSTj9sur6n2CbS).data.admin;
    }
  }
}

Obviously I can't hardcode this for each and every ID.

UPDATE

Apart from logging the case with support I have done some further testing. On the below the simulator is now granting access.

service cloud.firestore {
  match /databases/{database}/documents {
    match /conferences/{confID}{
      allow read, write: if request.auth.uid in get(/databases/$(database)/documents/conferences/$(confID)/permissions/permission).data.users;
        }
  }
}

For reference I use the below to query from my web-application:

db.collection("conferences")
  .get()
  .then(query => {
    console.log("SUCCESS!!!")
    query.forEach(function(doc) {
      // doc.data() is never undefined for query doc snapshots
      console.log(doc.id, " => ", doc.data());
    });
  }).catch((e) => {
    console.log(e)
  })

This is the log from the browser:

FirebaseError: Missing or insufficient permissions. at new FirestoreError (webpack-internal:///./node_modules/@firebase/firestore/dist/index.cjs.js:352:28) at JsonProtoSerializer.fromRpcStatus (webpack-internal:///./node_modules/@firebase/firestore/dist/index.cjs.js:5649:16) at JsonProtoSerializer.fromWatchChange (webpack-internal:///./node_modules/@firebase/firestore/dist/index.cjs.js:6146:44) at PersistentListenStream.onMessage (webpack-internal:///./node_modules/@firebase/firestore/dist/index.cjs.js:14350:43) at eval (webpack-internal:///./node_modules/@firebase/firestore/dist/index.cjs.js:14279:30) at eval (webpack-internal:///./node_modules/@firebase/firestore/dist/index.cjs.js:14319:28) at eval (webpack-internal:///./node_modules/@firebase/firestore/dist/index.cjs.js:7411:20)

I am using the latest Firebase package 5.8.3.

If I change the above rule to something simple like below it got access as long as I am logged in with a user:

service cloud.firestore {
  match /databases/{database}/documents {
    match /conferences/{confID}{
      allow read, write: if request.auth.uid != null
        }
  }
}

This even confuses me more. Is this because the rule is more complex and it takes too long to get this verified and gives back access denied?

Update-2

Quickly tested this in a mobile app via Flutter. Same result. Access denied with this ruleset.

service cloud.firestore {
  match /databases/{database}/documents {
    match /conferences/{confID}{
      allow read, write: if request.auth.uid in get(/databases/$(database)/documents/conferences/$(confID)/permissions/permission).data.users;
        }
  }
}

回答1:

I think my problem was the query's don't match security rules. If you would only access a single specific document it would work but if you query multiple documents in a collection you got blocked by the security rules. I had two options. Restructure my data so that a single document will hold all the data I need or redesign security rules to match query's. In the end I have attached to each document an indentifier like the UID to make sure query's match the security rules.



回答2:

One solution would be to put the users with permissions into an array in the conference document instead, so request.resource.data.permissions

So, instead of this:

get(/databases/$(database)/documents/conferences/$(confID)/permissions/permission).data.users

use this:

request.resource.data.permissions

This wouldn't solve the get() problem, but it would eliminate the need for a get() call, which could save you 15% or more on your quota.