How to fix security rules for newData on set function - firebase

Im working on an ionic app, i would like to send data to my firebase realtime-database as an object and compare the data with something, however on the server side which is my security rules for the newData, the write rule seems to be incorrect.
This is my sending code
var firebaseRef = this.afd.database.ref();
firebaseRef.child('Download/01/1').set({password:2431,name:'john'});
my database node should be written here
Download: {
01 : {
1: { // the data will be written here
}
}
}
and this is my security rules
"Download": {
"$id": {
".write": "newData.child('password').val() === 4321"
}
}
As you can see, i have purposely written the password on my set function '2431' in order to be process as incorrect during the validation of write rules, however the write rule still proceeds in writing the database even though the password it receive is incorrect, can you advise what should correct in my code? Thanks

Your rules and query don't match. You're writing to "Download/01/1", and password is set at "Download/01/1/password". However, your rules are set at "Download/XX", and newData.child('password') is referring to "Download/XX/password" (note that the "1" is missing in the path). Perhaps you meant something like:
"Download": {
"$id": {
"$i": {
".write": "newData.child('password').val() === 4321"
}
}
}
Or maybe:
"Download": {
"$id": {
".write": "newData.child('1').child('password').val() === 4321"
}
}

Related

Firebase Database Rules: can't write to database

I tried to set some important write persmissions but I can't solve my problem. I got told that, if I add a write-rule to room, then I overwrite my room/$roomID/ingame rule.
What I'm trying to do is
Creating a room by auth users.
Set/update ingame of a room only by the creator of the room. (That works)
Rules:
{
"rules": {
".read": true,
"user": {
".indexOn": "displayname"
},
"room": {
"$roomID": {
"ingame":{
".write": "data.parent().child('creatorUid').val() == auth.uid"
}
}
}
}
}
How I call to create a new room:
let user = firebase.auth().currentUser
dbRoomRef.push().then((room) => {
room.set({
creatorUid: user.uid,
ingame: false,
})
}).catch(function(err) {
console.log(err.message)
}
)
Error message (as expected):
FIREBASE WARNING: set at /room/-L572bnuRv0_vntko-Bd failed: permission_denied
Thank you.
The error messages says that you're trying to write /room/-L572bnuRv0_vntko-Bd and have no permission to write there. That is correct, since your rules only give permission to write to /room/-L572bnuRv0_vntko-Bd/ingame.
If creatorUid is already set when you create the room, you don't have to include it in your write statement and can just do:
room.child("ingame").set(false);
If you're trying to allow everyone to create a new room (or write to an existing room) as long as they are the owner, you need to set your rules one level higher:
"room": {
"$roomID": {
".write": "newData.child('creatorUid').val() == auth.uid"
}
}

How to check for new documents in a different collection for security rules?

So in realtime DB, I could check for new data to be written in various paths via newData().parent().parent()...parent() in the security rules when I'm doing fanout writes.
e.g.
const fanout = {
'users/user_a': {
username: 'foobar'
},
'usernames': {
'foobar': 'user_a'
}
};
firebase.update(fanout);
And a security something like this:
"rules": {
"users": {
$user_id: {
"username": {
".validate": "newData.parent().parent().parent().child('usernames').child(newData.val()).val() == auth.uid;
}
}
},
"usernames": {
"$username": {
".validate": "newData.parent().parent().child('users').child(auth.uid).child('username').val() == $username"
}
}
}
If was wondering if there's a way to do this in Firestore's security rules? I've seen the exists() function but it's only for the existing documents, not something that's about to be written.
Unfortunately there isn't currently an equivalent ability in Cloud Firestore Rules. You can use request.resource.data to examine the data being written in a request, but a batch write is treated as separate requests in Rules and there's no way to examine the net effect of all the writes like newData did in the Realtime Database. We're looking at adding this to Cloud Firestore in the future though.
If it's helpful, this specific use case could perhaps be solved without fanout, since you could do a usersRef.where('username', '==' 'foobar').get() query to get the user with a particular username.

Ho to get all fields from firebase except one? [duplicate]

I have a collection of signatures where each signature has a few properties: public: fullname, city and then email.
I want to keep the email property private and I've been struggling with writing the correct rules to only return fullname and city. Here is what my rules.json looks like so far:
{
"rules": {
"signatures": {
"$signatureID": {
"public": {
".read": true
},
"email": {
".read": false
}
}
}
}
}
When I go to the /signatures end point, I would like to receive an array of signatures with the public data and not receive the email addresses.
So far I haven't had any luck getting this to work the way I want it to. Am I doing something wrong? Should I structure my data differently?
With respect to security rules, Firebase operations are all-or-nothing.
As a result, attempting to load all of the data at /signatures will fail because your client does not have permission to read all of the data at that location, though you do have permission to read some of the data there. Similarly, writing to a location behaves the same way, and full permission is required before your operation will continue.
To handle this use case, consider restructuring your data like this:
{
"rules": {
".read": false,
".write": false,
"signatures-public": {
".read": true,
"$signatureID": {
// ... public data here
}
},
"signatures-private": {
"$signatureID": {
// ... private data here
}
}
}
}

Firebase validate with newData

I'm working thru the excellent example on how to structure data as shown by Kato in this post:
Firebase user account security with firebaseSimpleLogin
I'm not having any luck getting validate to work properly.
The data structure is :
accepted_invites
game1
desc "fun game"
invites
game1
uuidAAA true
uuidBBB true
here's a screen shot:
Firebase data
If I try and write the following
ref.child("accepted_invites").child("game1").child("userTwo").child("uuidBBB").setValue(true);
it will make an entry in accepted_invites with this rule :
".validate": "root.child('invites/'+$game_id+'/uuidBBB').exists()"
but not
".validate": "root.child('invites/'+$game_id+'/'+newData.val()).exists()"
I tried using the simulator but I'm getting
Type Error: + only operates on numbers and strings.
Here's the complete rules as I have them:
{
"rules": {
".write" : true,
".read" : true,
"accepted_invites": {
"$game_id": {
"$user_id": {
//This validate rule fails
//".validate": "root.child('invites/'+$game_id+'/'+newData.val()).exists()"
//This one works
".validate": "root.child('invites/'+$game_id+'/uuidBBB').exists()"
}
}
}
}
}
The newData keyword is a special attribute referring to all incoming data, but it is not permitted in path expressions because it may not be a string. i.e., it could very well be an object.
If you're interested in using some portion of that data within the path, I would recommend just including another validation rule at a deeper path, such as:
{
"rules": {
".write" : true,
".read" : true,
"accepted_invites": {
"$game_id": {
"$accepting_user_id": {
"$sending_user_id": {
".validate": "root.child('invites').child($game_id).child($sending_user_id).exists()"
}
}
}
}
}
}

Firebase Security Rules

I'm new to Firebase and really struggling with the security rules.
I'm trying to store data for anonymous user sessions that can only be accessed by that user but as soon as I tighten up the security rules I get permission denied and can't work out why.
The structure I have is:
/userSessions/$user/user_id
The application pushes to user sessions and writes the user_id as returned by the anonymous login.
I have the following security rules:
{
"rules": {
".read": true,
"userSessions": {
"$user": {
".write": "newData.child('user_id').val() == auth.id",
"user_id": {
".validate": "newData.isString()"
}
}
}
}
}
I'm sure I'm doing something silly, but can't work out how to use the security simulator either so don't know how to go about troubleshooting this
Any help greatly appreciated
EDIT -
Problem occurs when trying to create a new entry under userSessions after authenticating anonymously.
Code:
var userSessionsRef = new Firebase('https://xxxxxx.firebaseio.com/userSessions');
var userSession;
var auth = new FirebaseSimpleLogin( userSessionsRef, function(error,user) {
if (error) {
console.log(error);
} else if (user) {
userSessionsRef.child(user.id).set({
user_id: user.id,
provider: user.provider,
created: Firebase.ServerValue.TIMESTAMP
});
userSession = new Firebase('https://xxxxx.firebaseio.com/userSessions/'+user.id);
userSession.onDisconnect().remove();
}
The set() operation you've provided runs great; no errors; there must be another issue at play here that you haven't captured in your repro (did you enable anonymous auth in Forge?). Here's a fiddle running on your security rules, copied line-for-line (just moved down one child path): http://jsfiddle.net/katowulf/wxn5J/
On a related note, your current security rules will not allow the userSession.onDisconnect().remove(); op, since records can only be written if newData() contains a user_id value (i.e. it can't be null). A better way to structure these security rules would be as follows:
{
"rules": {
".read": true,
"userSessions": {
"$user": {
".write": "$user === auth.id", // delete allowed
"user_id": {
".validate": "newData.isString() && newData.val() === $user"
}
}
}
}
}

Resources