1

I'm trying to create a simple todo or blog system based on React + ReactFire.

And after a hour of reading firebase tutorial confused about configuring firebase security rules.

Code for saving element :

this.props.itemsStore.push({
    text : this.state.text,
    done : false,
    user : this.props.user.uid
})

Everything ok, but how i can get all records what owns only but authorized user?

This rules doesn't works :

  "rules": {
    "items" : {
         ".write" : "auth !== null",
         "$item" : {
            ".read": "data.child('user').val() == auth.uid"
         }
    }
  }

Seems to there no way to get all records only for one user, with security rules, instead of this, i should use something like filter. But again, i don't know how to filter elements in ReactFire, and in manuals no information.

As example how does it work in Parse https://i.stack.imgur.com/l9iXM.png

  • The question is unclear; are you asking how to read all of the records 'owned' by a user? If so, there is no ownership of data in Firebase. If you are asking how to allow data to be read by a certain authenticated user, that's a different question. The rules in the question are for an 'items' node but your data doesn't have an 'items' node. Can you clarify the code, rules and question? – Jay Dec 27 '15 at 14:02
  • Thank you for answer. I think both variants: read all of the records 'owned' by a user OR allow data to be read by a certain authenticated user, is very very close to each other. In result, i'd like to have a list of topics, for main page of my site. Where topics only for user who created them. – Maksim Rukomoynikov Dec 27 '15 at 14:35
  • All codebase here https://github.com/Rukomoynikov/firebaseReact Main method for getting list of topics in src/app.jsx. Could you help me understand, how i can get all topics owned by authorized user? Thank you very much) componentWillMount : function(){ this.fb = new Firebase(rootUrl + 'items/'); this.getAuthData(); this.bindAsObject(this.fb, 'items'); this.fb.on('value', this.handleDataLoaded); }, – Maksim Rukomoynikov Dec 27 '15 at 14:43

2 Answers2

0

The Firebase security model has two common pitfalls:

  1. permissions cascade: once you've granted a read or write permission on a specific level, you cannot take this permission away at a lower level

  2. rules are not filters: (this is essentially a consequence of the previous pitfall) you cannot use security rules to return a different subset of children for specific users. Either a user has access to a node, or they don't have access to it.

You seem to be falling for that second pitfall. While the user can access each specific message that they are the user for, they cannot query the higher-level items node since they don't have read access to it.

If you want to secure a list of messages/todos for a specific user, you will need to store that data for that specific user.

items_per_user
    $uid
        $itemid: true

This is quite common in NoSQL database and is often called denormalizing. See this article called "denormalization is normal" on the Firebase web site. It's a bit outdated as far as the Firebase API goes, but the architectural principles on denormalizing still apply.

To then show the items for a user, you'd do:

ref.child('items_per_user')
   .child(ref.getAuth().uid)
   .on('child_added', function(snapshot) {
       ref.child('items')
          .child(itemId.key())
          .once('value', function(itemSnapshot) {
               console.log(itemSnapshot.val());
          });
   })

Many developer new to Firebase think that the inner loop will be too slow to load their data. But Firebase is very efficient when it comes to handling multiple requests, since it only opens a connection once per client and pipelines all the requests in the inner loop.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
0

Keep in mind, Rules are not filters. They allow access to nodes based on criteria.

Here's an example simple structure where users 0 and 1 have stored text data within their node.

Data Structure

ToDo
  a_user_id_0
    text: "some text"
    done: yes
  a_user_id_1
    text: "another text"
    done: no

Rules

In this example rule, users can only read/write from nodes that belong to them within the ToDo node, so the path $user_id would be equal to their auth.id. It assumes the users has authenticated as well.

"ToDo": {
   "$user_id": {
      ".read": "auth != null && $user_id == auth.uid",
      ".write": "auth != null && $user_id == auth.uid"
   }
 }

If user_0 was auth'd and attempted to read/write data from a_user_id_1 node, it would fail.

Jay
  • 34,438
  • 18
  • 52
  • 81
  • In another words, i have to keep todos\blogpost in users table ? – Maksim Rukomoynikov Dec 27 '15 at 16:23
  • In my example, the user_id is the parent node and the auth'd user only has access to the data within that node. You could optionally store your data in a ToDo parent node with children nodes that are Record_0, Record_1, Record_2. And then within each Record child node store children of /text /user_id etc. You would modify the Rules to enforce that Record 0/user_id = auth.id. Based on your questions though, I think you may be trying to leverage rules as a type of filter, which you shouldn't do. You filter Firebase data via Query's or Events so I would look at that first. – Jay Dec 27 '15 at 16:34
  • Oh - and if you are, for example, wanting to read in and display ToDo's for a particular user, you could store their user_id with the toDo and then query for all ToDo's for that user_id (no Rules are needed to do that). There are a ton of posts about doing that here on StackExchange and some examples in the Firebase Getting Started guide as well. – Jay Dec 27 '15 at 16:36