I have a structure like this :
posts: {
group:{
postId: {
nbLikes: ..,
nbComments: ...,
updatedAt: ...,
text: ...
}
}
}
and I would like to allow the author to remove it, and everyone else to update everything except the text.
I have tried with this:
"posts": {
"$group": {
"$post": {
".write": "(data.exists() && !newData.exists() && data.child('userId').val() === auth.uid)
|| (data.exists() && newData.exists() && auth.uid != null)
|| (!data.exists() && newData.child('userId').val() === auth.uid)",
"text": {
".write": false
}
},
}
},
but unfortunately it does not work.
Moreover, when I try doing,
"posts": {
"$group": {
"$post": {
"text": {
".write": false
},
"nbLikes": {
".write": true
},
"nbComments": {
".write": true
}
....
},
}
},
it doesn't work either
Thanks for your help !
Related
I was writing security rules in my database but I do not understand why the validation does not pass it ..
I just want people to be able to type in "Extra" if the key says "nombre".
In case it's not the key "nombre", don't let it.
the rules:
"rules": {
"Users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid && root.child('Users').child(auth.uid).exists() === false || newData.hasChild('Extra')",
".validate": "newData.hasChildren(['nombre'])",
"Extra":{
}
}
}
}
Is the writing rule wrong? Does someone explain to me why?
TEST1 wrong
TEST2
You're writing to location /Users/$uid/Extra, so the nombre property ends up in /Users/$uid/Extra/nombre. To test the rule, you'll want to write to /Users/$uid.
If instead you want to allow the JSON like this:
Users: {
myUserId: {
Extra: {
nombre: "MT Designer"
}
}
}
Then your rules currently don't work, because you're validating that nombre exissts under myUserId. It should be:
{
"rules": {
"Users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid && root.child('Users').child(auth.uid).exists() === false || newData.hasChild('Extra')",
"Extra":{
".validate": "newData.hasChildren(['nombre'])",
}
}
}
}
}
Update: 2020-07-10
From the new screenshots it seems that yyou haven't applied the change from above yet, so I'd first recommend doing that. But if you want to reject other child nodes in Extra, you can do that by changing the rules to:
"Extra":{
"nombre": {
".validate": "data.isString()"
},
"$other": {
".validate": false
},
}
So this validates that the name is a string, and rejects all other child nodes.
From comments, apparently this is what OP ended up with:
"Users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid && root.child('Users').child(auth.uid).exists() === false || newData.hasChild('Extra')",
"$othernode": {
".validate": false
},
"Extra": {
"nombre": {
".validate": "newData.isString()"
},
"$other": {
".validate": false
},
}
}
},
Here is my data.
"users" : {
"user1": {
"1234": {
"role": "admin"
},
"1235": {
"role": "normal"
}
},
"user2": {
"1236": {
"role": "admin"
},
"1237": {
"role": "normal"
}
}
}
And here is rules for that.
"rules" {
"users": {
".read": "root.child('users').child('user1').child(auth.uid).child('role') === 'admin'"
}
}
But the rule doesn't work. I seem the auth.uid isn't gotten correctly.
Try this :-
{
"rules": {
"users": {
"user1": {
"$user_id": {
".read": "$user_id === auth.uid && root.child('users/user1/' + $user_id + '/role/').val() === 'admin' "
}
}
}
}
}
I'm clearly missing some fundamental aspect of firebase security, because this shouldn't work. I would expect it to throw a validation error when attempting to push invalid data. (Inserting a new node into /nodes)
Rules:
{
"rules": {
"nodes": {
".read": "auth !== null && auth.provider === 'google'",
".write": "auth !== null && auth.provider === 'google'",
"user": {
".validate": "newData.val() === auth.uid"
},
"ts": {
".validate": "newData.val() <= now && newData.val() >= (now-1000*60*60*24)"
}
}
}
}
Then in my console I try to intentionally insert invalid data:
ref.child('nodes').push({
'user': 'abc',
'ts': 123
}, function(err){console.log(err);});
Which logs null, and when I check my database it was inserted, no validation errors! I know I've got something fundamentally wrong, because a validation rule right after the .read and .write rows of the following disallows any writing. .validate": "newData.hasChildren(['user', 'ts'])",
{
"nodes" : {
"-KAgH0BLneWfGu8NymBo" : {
"ts" : 123,
"user" : "abc"
}
}
}
Whoops. Missing "$node_id"
{
"rules": {
"nodes": {
"$node_id":{
".read": "auth !== null && auth.provider === 'google'",
".write": "auth !== null && auth.provider === 'google'",
"user": {
".validate": "newData.val() === auth.uid"
},
"ts": {
".validate": "newData.val() <= now && newData.val() >= (now-1000*60*60*24)"
}
}
}
}
}
I'm trying to create a twitter clone to learn to use Firebase and I would love to get a suggestion about how to create the database structure. My biggest concern is related to followers and how to create a timeline when you are following for example 500 users. You would need to perform 500 queries and sort somehow for datetime.
{
"followers" : {
"cesar" : {
"followers" : {
"cesar2" : true
},
"following" : {
"cesar2" : true
}
},
"cesar2" : {
"followers" : {
"cesar" : true
},
"following" : {
"cesar" : true
}
}
},
"tweet" : {
"cesar" : [ null, {
"content" : "tweet 1"
} ]
},
"users" : {
"cesar" : {
"name" : "César",
"notifications" : true,
"username" : "cesar"
},
"cesar2" : {
"name" : "César2",
"notifications" : false,
"username" : "cesar2"
}
}
}
See Firefeed, Firebase's open-source Twitter clone. It includes a walkthrough of the data structure it uses, which boils down to using a fan-out approach when new messages are posted. Here's a copy of the rules used, which describes the underlying data structure:
{
"rules": {
// All data is readable by anyone.
".read": true,
"people": {
// A list of users with their names on the site.
"$userid": {
// Only the user can write their own entry into this list.
".write": "$userid ==auth.uid"
}
},
"users": {
"$userid": {
// The user is allowed to write everything in their bucket.
".write": "$userid ==auth.uid",
"following": {
// The following list should only contain actual ids from the "people" list.
"$followingid": {
".validate": "root.child('people').hasChild($followingid)"
}
},
"followers": {
// Anyone can add themself to to this user's followers list.
"$followerid": {
".write": "$followerid ==auth.uid"
}
},
"feed": {
"$sparkid": {
// User A can write in user B's feed, but only if A is following B, and only for sparks for which they are the author.
".write": "root.child('users/' + $userid + '/following').hasChild(auth.uid) && root.child('sparks/' + $sparkid + '/author').val() ==auth.uid"
}
}
}
},
"sparks": {
// A global list of sparks (the "firehose").
"$sparkid": {
// Modifying an existing spark is not allowed.
".write": "!data.exists()",
// Every spark should have an author and a body.
".validate": "newData.hasChildren(['author', 'content'])",
// A user can attribute a spark only to themselves.
"author": {
".validate": "newData.val() ==auth.uid"
},
"content": {
".validate": "newData.isString()"
}
}
},
"recent-users": {
// Users can add themselves to the list of users with recent activity.
"$userid": {
".write": "$userid ==auth.uid"
}
},
"recent-sparks": {
// Authors of sparks can add their sparks to this list.
"$sparkid": {
".write": "root.child('sparks/' + $sparkid + '/author').val() ==auth.uid"
}
},
"search": {
"firstName": {
"$searchKey": {
".write": "auth != null && (root.child('people/' +auth.uid + '/firstName').val() + '|' + root.child('people/' +auth.uid + '/lastName').val() + '|' +auth.uid) == $searchKey && newData.val() ==auth.uid"
}
},
"lastName": {
"$searchKey": {
".write": "auth != null && (root.child('people/' +auth.uid + '/lastName').val() + '|' + root.child('people/' +auth.uid + '/firstName').val() + '|' +auth.uid) == $searchKey && newData.val() ==auth.uid"
}
}
}
}
}
I have this set of rules:
{
"rules": {
"facebook_users": {
"$user": {
".read": "$user == auth.id",
".write": "$user == auth.id",
"userData": {
"maxProjects": {
".validate": false
},
"userProjects": {
".validate": ???
}
}
}
}
}
}
I'd like to let an user not being able to add a children to the userProjects array if userProjects.length > userData.maxProjects. The user is denied writing in the maxProjects already, due to the ".validate": false rule. How can I compare userProjects' length with maxProjects?
If it's not possible, what is a correct way to do this?
You can use the root special variable along with child() to do this comparison:
{
"rules": {
"facebook_users": {
"$user": {
".read": "$user == auth.id",
".write": "$user == auth.id",
"userData": {
"maxProjects": {
".validate": false
},
"userProjects": {
".validate": "data.val().length < root.child('facebook_users').child($user).child('userData/maxProjects').val()"
}
}
}
}
}
}
Have you read this?
parent()
Gets a RuleDataSnapshot for the parent location.
Return Value: RuleDataSnapshot - The RuleDataSnapshot for the parent location.