Firebase RTDB rule prevents "set" but still allows "update" - firebase

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()

Related

hi, my realtime database rules are not making sence, i want the int to only be allowed to go up by 1, not down, nor up more than 1

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).

vote system firebase security rules

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).

Firebase Database Rules: Apply an update only if newData has a child id with user id

".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())"
}
}

Firebase data structure and security rules

Im having trouble with the security rules for firebase and Im not 100% where I am going wrong. I am thinking that maybe I have my data structure wrong:
{
"users": {
"uid": {
"displayName": "Name";
}
},
"modules": {
"id": {
"title": "buttons",
"uid": "(user id string)"
},
"id": {
"title": "navbars",
"uid": "(user id string)"
}
},
"snippets": {
"id = moduleID": {
"id (of snippet)": "(id string)" {
"uid (user ID)": "(string)",
"body": {
"css": "(some code)",
"html": "(Some code)",
"name": "(string)",
"description": "(string)"
}
}
}
}
Everything in the app works fine, but when I started to add security rules I got access denied errors. Im just wondering if I have the data structure correct in the first place or is the security rules completely wrong?
Security rules:
{
"rules": {
"users": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
},
"snippets": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
},
"modules": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
}
}
Any advice would be greatly appreciated.
It seems the rules are malformed based on the data structure.
The rules have $uid's in each node but your data doesn't match that. Users has uid but modules has id and snippets has id = moduleID.
$uid is a variable that holds the node name so it can be referenced inside { } so you should (for readability) rename that variable in the other two nodes to something that makes more sense inside each {}. Like in modules, have it $module_id.
However. I think the jest of this is you want to limit reading snippets and modules to authenticated users. To do that, you can reference the users node.
a .read rule would be something like this
"modules": {
"$module_id": {
".read": "auth != null && root.child('users/' + auth.id).exists()
}
So your modules node can be read by a user that is auth'd and their uid also appears in the users/ node
Are you using the Firebase Bolt compiler for rules? I had to write some complex rules and doing it by hand gets confusing very quickly.
Below is what it would looks like. Very easy to make changes, compile and try them out.
//current logged in user
isUser(uid) = auth != null && auth.uid == uid;
//does this module id exist
hasValidModule(module_id) = root['modules'][module_id] != null;
//dont let anyone read or write to top node
path / {
read() = false;
write() = false;
}
path /users/$user_id
{
write() = isUser($user_id);
read() = isUser($user_id);
}
path /snippets/$module_id/$snipit_id/$user_id
{
write() = isUser($user_id) && hasValidModule($module_id);
read() = isUser($user_id);
}
path /modules/$user_id
{
write() = isUser($user_id);
read() = isUser($user_id);
}
Here's the json it spits out:
{
"rules": {
"users": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id"
}
},
"snippets": {
"$module_id": {
"$snipit_id": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id && newData.parent().parent().parent().parent().child('modules').child($module_id).val() != null"
}
}
}
},
"modules": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id"
}
}
}
}
There's some info on the Firebase blog but the doc that really helped me is this
https://github.com/firebase/bolt/blob/master/docs/language.md

firebase setPriority permisions_denied

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.

Resources