I'm trying to write the rules for this node so only the user who is currently logged in can edit/update it with only the values "-5", "3" or "1" and the rest of the users can only edit/update it with "-3" or "3"
The data structure I have is:
/users/$uid/votes
the rules I have as of now are:
{
"rules": {
"users": {
"$uid": {
".read": true,
".write": "auth.uid === $uid",
"votes": {
".validate": "auth.uid === $uid && newData.isNumber() && (newData.val() === -5 || newData.val() === 3 || newData.val() === 1) || auth.uid !== $uid &&newData.isNumber() && (newData.val() === -3 || newData.val() === 3)"
}
}
}
}
}
is there any way to improve these rules? does "auth.uid !== $uid" make sure that the user that's not currently logged in can only update the values of this "votes" node by -3 or 3?
This is a slightly more readable way to accomplish the same (to me at least):
".validate": "(newData.isNumber() && (
newData.val() === 3 /* Everyone can set to 3 */
|| (auth.uid === $uid && newData.val() === -5 || newData.val() === 1) ||
|| (auth.uid !== $uid && newData.val() === -3)
)
But the rules let every authentication user write to the location for $uid, which seems odd. Are you sure you want just any user wrote to this user's votes?
If might be easier to help if you show the JSON you want to create, and the code for a write operation you want to allow (and disallow).
Related
here is my rules code, the code updates in unity every time the user gets +1 point. the rules need
to make it that points can only be updated by +1 (basically , currentPoints = newPoints +1).
in unity i would write it as:
if (currentPoints < newPoints) currentPoints++;
now how do i state my rules to stick to that so my code cant be hacked/edited by a hacker to give
them +1000 points at a time instead of +1 on the database.
{
"rules": {
"users": {
".read": "auth != null",
"$userId": {
".write": "$userId === auth.uid",
"points": {
".validate": "newData.isNumber()",
".write": "data.val() == null && newData.val() == 1 || newData.val() === data.val() + 1"},
"username": {
".validate": "newData.isString()"}
}
}
}
}
Looking at your current rules, you get caught out by cascading security rules:
{
"rules": {
"users": {
// any logged in user can read another user's data
".read": "auth != null",
"$userId": {
// a user can modify any of their own data
".write": "$userId === auth.uid",
"points": {
// asserts points is a number
".validate": "newData.isNumber()",
// this next rule is ignored when $userId === auth.uid - read/write rules cascade!
// if $userId !== auth.uid, ANYONE can write to this location as long as points is first set to 1 or is increased by 1
".write": "data.val() == null && newData.val() == 1 || newData.val() === data.val() + 1"
},
"username": {
// asserts username is a string
".validate": "newData.isString()"
}
}
}
}
}
With your current rules, as long as I am logged in, I can get the value of /users/someUserId/points, increase it by 1, and then set the new value to /users/someUserId/points. Arguably, the only person who should be able to increase a user's points is the user who owns that score.
To fix this, you need to remove the ".write" rule (which causes this bug) and move it's logic into ".validate":
{
"rules": {
"users": {
// any logged in user can read another user's data
".read": "auth != null",
"$userId": {
// a user can modify any of their own data
".write": "$userId === auth.uid",
"points": {
// asserts points is a number and is correctly increased by 1
".validate": "newData.isNumber() && ((data.val() == null && newData.val() == 1) || newData.val() === data.val() + 1)",
},
"username": {
// asserts username is a string
".validate": "newData.isString()"
}
}
}
}
}
Note: With the ".read": "auth != null" rule for /users, make sure that any private user data, e.g. emails, phone numbers, etc are not under /users - they should be moved into a different tree called /privateUserData (or similar).
I am trying to allow only write / update to my firebase table only if it has a child "A_Key". I managed to prevent "set" in the firebase rule simulator, but cannot prevent "update". The "update" simulation is always successful.
I've tried writing this to the table.
{
"objectId" : "XXaabb",
"value" : 135
}
Here are my rules
"MyTable": {
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid",
".validate": "newData.hasChild('A_Key') && newData.child('A_Key').val() === '123456'"
}
}
Simulated set denied. Which is what I want.
However
Simulated update allowed. This is not what I want.
My desired outcome is both should be denied.
If you want that on set there should be A_key in the data and when updating this data there should be again value of A_key then use the following rules:
"myTable": {
"$uid": {
".write": "(((data.val() == null && (auth.uid == $uid)) && newData.child('A_key').val() != null) || (((data.val() != null && newData.val() != null) && (auth.uid == $uid)) && data.child('A_key').val() != null))",
".read": "(auth.uid == $uid)"
}
}
Bolt Equivalent:
path /myTable/{uid} {
read() {isCurrentUser(uid)}
create() {isCurrentUser(uid) && this.A_key != null}
update() {isCurrentUser(uid) && prior(this.A_key) != null}
}
isCurrentUser(id) {
auth.uid == id
}
If you want that in update the value of A_key should not change then use this:
"myTable": {
"$uid": {
".write": "(((data.val() == null && (auth.uid == $uid)) && newData.child('A_key').val() != null) || (((data.val() != null && newData.val() != null) && (auth.uid == $uid)) && data.child('A_key').val() == newData.child('A_key').val()))",
".read": "(auth.uid == $uid)"
}
}
Equivalent Bolt:
path /myTable/{uid} {
read() {isCurrentUser(uid)}
create() {isCurrentUser(uid) && this.A_key != null}
update() {isCurrentUser(uid) && prior(this.A_key) == this.A_key}
}
isCurrentUser(id) {
auth.uid == id
}
https://github.com/FirebaseExtended/bolt/blob/master/docs/guide.md
I made one small change and now it works
newData.hasChild('A_Key')
should be
newData.child('A_Key').exists()
".write": "auth!=null && (
(
newData.val() != data.val() &&
newData.child('id').val() === auth.uid
)
|| !data.exists()
)"
I'm updating a list element with this schema {id: {id: id}} and comparing the id child to auth.uid which is the same. For some reason the line returns permission denied.
newData actually wraps all the object that is listening to, not just the "new data" that has changed. So I needed to look one level deeper:
"chats": {
"$chat": {
".write": "auth!=null && ((newData.val() != data.val() && newData.child('users').child(auth.uid).exists()) || !data.exists())"
}
}
I have a firebase database rule to block:
non-authenticated users
users trying to create data for some other user
I also want to prevent updating data.I need this code for that:
!data.exists() || !newData.exists()
This is currently how it looks:
"rules": {
"orders": {
"$order":{
".read": "auth != null && auth.token.admin === true",
".write": "auth != null && auth.uid === newData.child('userID').val()" // check the incoming data's userID value here if sender is same. or someone can send orders with other peoples ids. also anyone can send an order with id 123 but only if it doesnt exist. so they cannot update anyones order. is this safe enough?
},
".indexOn": "userID"
}
}
How am I supposed to include this "||" operator in my logical expression for the ".write" rules?
I am not able to delete data. Is it because Im checking the newData's child in the security rules and there is no newData when using ".remove()" in the app? If that is the case, how does write and delete rules in the same line work?
I got a weird issue that I could not use parantheses at first but it is now working. I created an || between two expressions
First expression is for write:
(auth != null && newData.exists() && auth.uid === newData.child('userID').val() && !data.exists())
Here "newData.exists()" in the expression tells us there is an incoming data ( write/update operation) and "!data.exists()" blocks updating data.
Second expression is for deleting rules:
(!newData.exists() && auth != null && auth.uid === data.child('userID').val()
Here "!newData.exists()" says that there is no new data so this is a delete operation, and the next part makes sure everyone can only delete their own data.
So now the code looks like this:
"rules": {
"orders": {
"$order":{
".read": "auth != null && auth.token.admin === true",
".write": "(auth != null && newData.exists() && auth.uid === newData.child('userID').val() && !data.exists()) || (!newData.exists() && auth != null && auth.uid === data.child('userID').val())"
},
".indexOn": "userID"
},
}
Documentation: https://www.firebase.com/docs/javascript/firebase/setpriority.html
Isn't there missing something important?
I am trying to update the priority of the group by some auth user. And i am able to do it only when i am authenticated as an owner of the group and i have no idea why and HOW TO GRANT ACCESS ONLY FOR UPDATING THE PRIORITY TO THE ALL AUTH USERS AS I DID WITH "membersCount".
// GROUPS
"groups": {
"$subCategoryId": {
"$groupId": {
// ONLY OWNER OF GROUP CAN UPDATE THE userId, name, hashTag, categoryId
//
// The first init has to have userId, name, categoryId
//
// Anyone can update the membersCount
".write": "newData.hasChildren(['userId','name','categoryId']) || !data.exists() || auth != null",
"userId": {
".validate": "auth.uid == newData.val()"
},
"name": {
".validate": "newData.val() != '' && newData.val().length < 30 && (data.parent().child('userId').val() == auth.uid || !data.parent().child('userId').exists())"
},
"hashTag": {
".validate": "(data.parent().child('userId').val() == auth.uid || !data.parent().child('userId').exists())"
},
"categoryId": {
".validate": "newData.val() != '' && root.child('categories').child(newData.val()).exists() && root.child('subcategories').child(newData.val()).child($subCategoryId).exists() && (data.parent().child('userId').val() == auth.uid || !data.parent().child('userId').exists())"
},
"membersCount": {
".write": "auth != null && newData.isNumber() && (data.val()+1 == newData.val() || data.val()-1 == newData.val() )"
}
}
}
},
When i tried update({ '.priority': 2}) i got:
Uncaught Error: update() does not currently support updating .priority.
The error message has nothing to do with security rules in this case. You can't update a priority using the update() command. You can do it by using setPriority(2).
If you want to reference priorities in your security rules, you can do that with getPriority.