14

In Firebase I have a users "node", which looks like:

users: {
  someUid: {
    username: 'someUsername'
    activeConversations: {},
    profile_picture: ''
    ... lots of other children
  },
  ...
},
anotherNode: {

},
... hundreds of other nodes

My rules right now:

{
  "rules": {
    ".read": true,
    ".write": true,
    "users": {
      ".indexOn": [
        "username"
      ]
    },
    "friendRequests": {
      ".indexOn": [
        "timeSent"
      ]
    }
  }
}

What I want to do is restrict child's access in the users "node" only to the client who owns the child. So for instance, the someUid child should only be writeable by the client with uid someUid. Other "node" like anotherNode can be writeable / readable by any logged-in client. Also, any logged-in client should be able to write on profile_picture and activeConversations in the users doc.

How can I achieve that without having to put a read/write rule on every single node?

Thank you

Dan P.
  • 1,707
  • 4
  • 29
  • 57

3 Answers3

7

I think @Bradley Mackey was nearly there but just needed a small tweak.

{
  "rules": {
    "users": {
      ".indexOn": ["username"],
      // wildcard, matches any node under 'users'
      "$someUid": {
        "$other" : {
          ".read": "($other == 'profile_picture' || $other == 'activeConversations') || auth.uid == $someUid", 
          ".write": "($other == 'profile_picture' || $other == 'activeConversations') || auth.uid == $someUid", 
        }
      }
    },
    "$anythingelse": {
      ".read": "auth != null",
      ".write": "auth != null",
    }
  }
}

The ".validate": field ensures that a field matches a certain format. The read and write here should give everyone read and write access if the field is profile_picture or activeConversations, and give the user access to everything else.

EDIT:

I added in another rule that would allow read-write access to any signed in user to any of the non-users nodes.

  • hi Adam. Thanks. What about the other nodes beside `users` however? By default there's no read/write access if not set. The other nodes at the same level of `users` should have read/write access. – Dan P. May 20 '17 at 16:53
  • Edited now to hopefully do what you want it to. – Adam Brocklehurst May 20 '17 at 17:00
  • Good answer. Needs a comma before `"$anythingelse"` – Bob Snyder May 21 '17 at 21:39
  • Thanks Bob, added that in – Adam Brocklehurst May 21 '17 at 23:10
  • I tried it and it doesn't work, I am not sure where the pitfall is right now and I'm investigating if it comes from my own data, for instance if fields other than `profile_picture` and `activeConversations` need a write/read access. Will get back here as soon as I delve more into it. – Dan P. May 22 '17 at 16:52
  • this does not work, if you have other nodes example if you have a node called `location` it wont work – Peter Haddad Dec 25 '17 at 11:05
4

Having ".read": true, ".write": true at the root of your database is a very bad idea. Literally anyone could just wipe your entire database or insert any data at any time. This is because .read and .write rules cascade, so if you have them evaluating to true at some node, the person has permission for all children of that node as well.

Luckily more secure rules, like the rules you are proposing, are easy to implement. I'm assuming that someUid is equal to the users auth.uid you can do:

{
  "rules": {
    "users": {
      ".indexOn": ["username"],
      // wildcard, matches any node under 'users'
      "$someUid": {
        ".read": "auth != null",
        ".write":"auth != null",
        "$other" : {
          ".validate": "($other == 'profile_picture' || $other == 'activeConversations') || auth.uid == $someUid" // only that own user can change their username
        }
      }
    }
  }
}

I've omitted your friendRequests node, because I'm not sure what security rules you'd like for that, but I hope this is enough to get you started.

The Firebase docs cover this stuff very well.

Bradley Mackey
  • 6,777
  • 5
  • 31
  • 45
  • Thanks for that Bradley. How can I keep the write access to any authenticated user for `activeConversations` and `profile_picture`? – Dan P. May 16 '17 at 11:39
  • Also for `friendRequests` and other nodes, we can keep to `write: true` and `read: true`. If we put it in global scope will it cascade down for all nodes (and when setting it for example in `users` then it would override those)? – Dan P. May 16 '17 at 11:41
  • @DanP. see my updated answer, also don't set them to `true` unless you very explicitly need it. It's very bad practice, at least check for `auth != null`. – Bradley Mackey May 16 '17 at 11:53
  • Thanks. I actually have like 50 children besides username, profile_picture etc. Is it possible to set the default to `auth != null && auth.uid == $someUid`, and set `profile_picture` and `activeConversations` to `auth != null`? (basically switch the rule you wrote under `username` with the rule in `$other`) – Dan P. May 16 '17 at 11:58
  • 1
    @DanP. yes you can easily do that! – Bradley Mackey May 16 '17 at 11:59
  • Hey @Bradley Mackey, I updated the post with what my rules look like now. Does it look like something could be wrong? I added some questions within the code. – Dan P. May 16 '17 at 13:24
  • One error I'm getting: `FIREBASE WARNING: set at /users/WKLiK2XPdpaYzJtedEZkte0K9qz1 failed: permission_denied` However I am logged-in as this user. – Dan P. May 16 '17 at 13:40
  • @DanP. See my updated answer, I've also changed it so it will fully work with the structure you described in a previous comment, so just copy and paste what I've done. – Bradley Mackey May 16 '17 at 13:51
  • Using that now and the problem is that I can't write to other nodes, for example I can't write inside `friendRequests` – Dan P. May 16 '17 at 14:04
  • Just to make sure we're on the same page, every node should have read and write access for any logged-in user. The only node that needs that different (for the write part only) is the `users` node, which its children should be writeable only by the logged-in user EXCEPT for its children `profile_picture` and `activeConversations`, which should both be available for read/write by any logged-in user. – Dan P. May 16 '17 at 14:10
  • Hi Brad, any idea about this? Still haven't gotten around this :/ – Dan P. May 18 '17 at 08:58
  • Hi sorry, use pastebin or similar so I can have a look at your code. – Bradley Mackey May 22 '17 at 13:11
  • I am not sure where the pitfall is right now and I'm investigating if it comes from my own data, for instance if fields other than `profile_picture` and `activeConversations` need a write/read access. Will get back here as soon as I delve more into it. – Dan P. May 22 '17 at 16:53
0

As per the firebase documentation, you can restrict the access permission at user level and data level

For example,

    // These rules grant access to a node matching the authenticated
    // user's ID from the Firebase auth token
    {
      "rules": {
        "users": {
          "$uid": {
            ".read": "$uid === auth.uid",
            ".write": "$uid === auth.uid"
          }
        }
      }
    }

Also you can restrict the read write permission in data level,

{
  "rules": {
    "messages": {
      "$message": {
        // only messages from the last ten minutes can be read
        ".read": "data.child('timestamp').val() > (now - 600000)",

        // new messages must have a string content and a number timestamp
        ".validate": "newData.hasChildren(['content', 'timestamp']) && newData.child('content').isString() && newData.child('timestamp').isNumber()"
      }
    }
  }
}

Go through these firebase official documentation, https://firebase.google.com/docs/database/security/quickstart https://firebase.google.com/docs/database/security/user-security https://firebase.google.com/docs/database/security/securing-data

Mohammed Safeer
  • 20,751
  • 8
  • 75
  • 78