1
votes

I am using firbase database and I am trying to set up rules to limit write access to users who's uid matches the userId node of the object they are trying to write and still allow write to create a new object such as a new post if it doesn't exist. So new posts are allowed and editing your own post is allowed, but write is denied if post exists, but doesn't belong to user.

Something like:

"posts": {
  ".write": "auth !== null && posts.post.userId.val() === auth.uid
}

or maybe something like

//where posts.post represents objectRef.objectUpdating
"rules":{
  ".write": " auth !== null && posts.post.userId.val() === auth.uid
}

posts.postid object is updated like this:

firebase.database().ref('posts').child(id).update(updates)

Here is a sample of how posts look. Threads and forums follow same structure.

  "posts": {
    "-KsjWehQ--apjDBwSBCZ": {
      "edited": {
        "at": 1504037546,
        "by": "ALXhxjwgY9PinwNGHpfai6OWyDu2"
      },
      "publishedAt": 1504035908,
      "text": "some post text",
      "threadId": "-KsjWehQ--apjDBwSBCY",
      "userId": "ALXhxjwgY9PinwNGHpfai6OWyDu2"
    },
    ...{post2},
    ...{etc}
  }

Hope that makes sense, any help is appreciated.

EDIT: I got it so when the update or create method is called it checks to see if data.userId is the same as the auth.uid. Updating existing data now works.

The create method is not working though, I am not sure how to allow update() to create new posts or threads like before without updating if userId does not match auth.uid.

Rules below:

{
  "rules": {
    ".read": true,
    "users": {
      ".indexOn": ["usernameLower", "email"],
      "$user": {
        ".write": "auth !== null && $user === auth.uid"
      }
    },
    "forums": {
      ".write": false
    },
    "categories": {
      ".write":false
    },
    "$resource": {
      "$child": {
        ".write": "(auth !== null && auth.uid === data.child('userId').val()) || data.val() === null"
      }
    }
  }
}
1

1 Answers

0
votes

I managed to get it working. Using posts as an example it would be:

"posts":{
  "$post":{
      ".write": "(auth !== null && auth.uid === data.child('userId').val()) || (!data.exists() && auth !== null)"
   }
}

If there is a better way to do it or if I am being redundant or overlooking something don't hesitate to comment and let me know, I am still new to firebase and any input is appreciated.

For Reference I have included my rules below. One thing I am unclear of is whether I have to set ".write" to false below my other write rules to prevent any other data from being written or if that is handled by default.

{
  "rules": {
    ".read": true,
    "users": {
      ".indexOn": ["usernameLower", "email"],
      "$user": {
        ".write": "auth !== null && $user === auth.uid"
      }
    },
    "categories": {
      ".write": false
    },
    "forums": {
      "$forum":{
        "lastPostId": {
          ".write": "auth !== null"
        },
        "threads": {
          ".write": "auth !== null"
        }
      }
    },
    "posts": {
      "$post": {
        ".write": "(auth !== null && auth.uid === data.child('userId').val()) || (!data.exists() && auth !== null)"
      }
    },
    "threads": {
      "$thread": {
        "$child":{
          ".write": "auth !== null"
        },
        ".write": "(auth !== null && auth.uid === data.child('userId').val()) || (!data.exists() && auth !== null)"
      }
    },
    "$resource": {
      "$child": {
        "$child2": {
          ".write": "auth !== null"
        },
        "userId": {
          ".write": "!data.exists()"
        }
      }
    }
  }
}

For reference these are the updates being passed to update() for creating a new post in the database:

const updates = {}
        updates[`posts/${postId}`] = post
        updates[`threads/${post.threadId}/posts/${postId}`] = postId
        updates[`threads/${post.threadId}/contributors/${post.userId}`] = post.userId
        updates[`users/${post.userId}/posts/${postId}`] = postId

firebase.database().ref().update(updates)