4
votes

I've got Firebase data structured a little something like this:

+ tasks
  + task_id
    + title
    + user
    + ...
+ users
  + ...

The from the frontend (using AngularJS and AngularFire (plus the built in SimpleLogin)) - when a logged in user creates a task, the user's uid (eg. simplelogin:2) is placed into the user part of the task being created.

When a user is logged in, they can view only their tasks by using the:

$firebase( ref.orderByChild('user').equalTo(User.uid) );

The next step is to secure the data, which is where I'm struggling. I've tried:

"tasks": {
   ".indexOn": ["user", "order"],
   ".write": "auth != null",
   ".read": "auth != null && data.child('user').val() == auth.uid",
     "$task": {
        "title": {
           ".validate": "newData.isString() && newData.val().length <= 1000"
        },
        "completed": {
        },
        "time_added": {
            ".validate": "newData.val() <= now"
        },
        "order": {
        },
        "user": {
            ".validate": "newData.val() != null && newData.val() == auth.uid"
        },
        "$other": {
           ".validate": false
        }
     }
  }

and also:

"tasks": {
     "$task": {
       ".indexOn": ["user", "order"],
       ".write": "auth != null",
       ".read": "auth != null && data.child('user').val() == auth.uid",

        "title": {
           ".validate": "newData.isString() && newData.val().length <= 1000"
        },
        "completed": {
        },
        "time_added": {
            ".validate": "newData.val() <= now"
        },
        "order": {
        },
        "user": {
            ".validate": "newData.val() != null && newData.val() == auth.uid"
        },
        "$other": {
           ".validate": false
        }
     }
  }

However I get:

Error: permission_denied: Client doesn't have permission to access the desired data.

Where am I going wrong?

1
You cannot use security rules as a filter. This includes usage with queries. Since the query must, in essence, read tasks/ to get the children which match, it is failing because read access is not allowed at the parent (i.e. iterative) level. - Kato
Hi @Kato - what's your recommendation in this situation where all tasks should only be accessible to the user/s (1 or more) in the 'user' field. Or am I structuring data incorrectly? - mcnamee
Check out the structuring data guide. There is a section titled creating data that scales that covers indices. Some variant of that would be necessary here. - Kato
@mcnamme I'm facing the same problem at the moment. Did you find your solution, yet? - Steffen

1 Answers

4
votes

The tasks collection doesn't have a user child. Each specific task has a user child. So you need to move your read rule one level down:

"tasks": {
    "$task_id" {
        ".read": "auth != null && data.child('user').val() == auth.uid",
    }

Also see the examples on: https://www.firebase.com/docs/security/guide/user-security.html