I am trying to prevent unwanted fields to be added to my user objects.
--> A user can have a phone and a username (but doesn't have to --> newData.hasChildren(['phone', 'username']) doesn't work here).
I first tried this:
"users": {
"$uid": {
"$other": {
".validate": "['phone', 'username'].indexOf($other) > -1"
}
}
}
I get an error because of the array :(
So then I thought about doing something like this but it's really not great if I have many potential fields
"$other": {
".validate": "$other === 'phone' || $other === 'username'"
}
Finally, I created a node in my Firebase called 'rules' and did this:
"rules": {
"users": {
"fields": {
"phone": true,
"username": true
}
}
}
And then my new validation rule in my user object is:
"$other": {
".validate": "root.child('rules/users/fields/'+$other).val() === true"
}
My question is: Is this a correct way to restrict field names ?
Thank you very much for your answer :) I am pretty new to Firebase but I am having a lot of fun trying it out!
To restrict an object in your Firebase to only have the specified keys, try using one additional wildcard child that will match any attributes not already specified, and reject the write if it contains one of these unmatched attributes:
"rules": {
"users": {
"$userid": {
".validate": "newData.hasChildren(['phone', 'username'])",
"phone": {
".validate": "newData.isNumber()"
},
"username": {
".validate": "newData.isString()"
},
"$other": {
".validate": false
}
}
}
}
Related
I'm building a private note app in Firebase realtime database, Android environment,
and now, I'd like to add authentificaion features with google sign-in in my app.
Until now, I tried several Rules in 'Rules playground', but it's not working well.
DB is like following:
{
"items" : {
"-MwtOrIBmhaoiGtSjzRl" : {
"key" : "-MwtOrIBmhaoiGtSjzRl",
"modifiedAt" : "2022-02-27 04:19:15.782677",
"string" : "test11"
},
"-MwvRBvCJ3hVJS3Qx1M3" : {
"key" : "-MwvRBvCJ3hVJS3Qx1M3",
"modifiedAt" : "2022-02-27 13:48:43.081140",
"string" : "Test2"
}
}
}
And the rules are like following:
{
"rules":{
"items": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid",
},
".indexOn": "modifiedAt"
}
}
}
In Rules playground, I tried 'get' simulation and the result is
'Simulated read denied'
{
"auth": {
"uid": "e41ac05f-6c93-40c8-add6-90bccf8ab80d",
"token": {
"sub": "e41ac05f-6c93-40c8-add6-90bccf8ab80d",
"firebase": {
"sign_in_provider": "google.com"
},
"email": "",
"email_verified": false,
"phone_number": "",
"name": ""
}
},
"resource": {
"key": "value"
},
"path": "/item",
"method": "get",
"time": "2022-02-27T14:44:13.766Z",
"isAdmin": false
}
Can I get some help?
One strange thing is that the follwing rules are not working well also.
{
"rules":{
"items": {
"$uid": {
".read": true,
".write": true,
},
".indexOn": "modifiedAt"
}
}
}
The key values you have under items are not Firebase Auth UIDs. They are push IDs randomly generated from your client app code. A push ID has nothing to do with the identity of the person who added that data.
To be clear, this value: "MwtOrIBmhaoiGtSjzRl" is not a UID. And it doesn't match the UID you're specifying here: "e41ac05f-6c93-40c8-add6-90bccf8ab80d".
If you want to use the user's UID as the key of the data to add to the database, don't use push(). You should instead build a path to the data using the user's UID in your code using setValue() as shown in the documentation.
My database:
I want to set firebase rules where user can update existing tags value but not create new tags
If you only want the user to be able to update existing properties, and not create any new properties, that'd be:
{
"rules": {
"$uid": {
".write": "auth.uid === $uid"
"$property": {
".validate": "data.exists()"
}
}
}
}
So this rule validates that a property can only be written if there's already data for that property in the database.
If you only want the user to be able to write a name and address, and not any other properties, that'd be:
{
"rules": {
"$uid": {
".write": "auth.uid === $uid"
"name": {
".validate": true
},
"address": {
".validate": true
},
"$other": {
".validate": false
}
}
}
}
In the above rules we allow writing the named name and address properties, and reject any other properties with a wildcard capture rule.
This question already has an answer here:
Firebase: How to structure public/private user data
(1 answer)
Closed 4 years ago.
I can't figure out how to filter data using Firebase database. I've read that rules can't be used for filters. But then how?
I'd like a datastructure somewhat like the one below. i.e. a list of posts created by different users due for a specified time (user-id is not included in the layout below as I'm not sure where to put it)
posts: {
"-LKwbZsfy55d24kwX4t1" : {
when: {
from: "2019-01-01 10:00",
to: "2019-01-01 11:00"
content: {
text: "Hello"
}
},
"-LKwbZsfy55d24kwX4t2" : {
when: {
from: "2019-01-02 10:00",
to: "2019-01-02 11:00"
content: {
text: "Another hello"
}
}
}
I would like everyone to be able to read all posts so my sync path is '/posts'
BUT only the user that created the post should be able to see the 'content'. So I somehow need to say that posts has ".read" : true, and content has ".read": $uid == auth.uid (which is not possible since access cannot be revoked by a child path)
If your current data structure makes it impossible to secure the data to your needs, considered restructuring it so that security rules become possible. In other words, don't nest protected data under public data. Put protected data in its own top-level child.
"posts-public": {
"-LKwbZsfy55d24kwX4t1": {
// public data here
}
},
"posts-private": {
"-LKwbZsfy55d24kwX4t1": {
// private data here
}
}
Now you can write security rules to protect them independently from each other.
".read": "true", gives everyone to read data
And it should be looks like this (just for example):
"posts": {
".read": "true",
"$postId": {
".read": "true",
".validate": "root.child('posts/'+$postId).exists()",
"$contentId": {
".read": "auth !=null",
".write": "auth != null",
".validate": "(newData.hasChildren(['content']))",
"content": {
".validate": "newData.val().length > 0"
},
"user": {
".validate": "newData.hasChildren(['id', 'name', 'avatar'])"
}
}
}
},
"privatePost": {
"$uid1": {
"$uid2": {
".read": "auth != null && ($uid1 === auth.uid || $uid2 === auth.uid)",
"$postId": {
".write": "auth != null",
".validate": "(newData.hasChildren(['content']))",
"content": {
".validate": "newData.val().length > 0"
},
"user": {
".validate": "newData.hasChildren(['id', 'name', 'avatar'])"
}
}
}
}
So far not having any luck with Firebase Security rules.
I have this
{
"rules": {
"users": {
"$user_id": {
".read": true,
".write": "auth !== null && auth.uid === $user_id",
"profile": {
".validate": "newData.hasChildren(['first_name', 'last_name'])"
}
}
}
}
}
I send data and for the profile and one of them is blank... it lets it write any way. I wind up with data like so...
{
"users" : {
"simplelogin:25" : {
"profile" : {
"first_name" : "John",
"last_name" : ""
}
},
"simplelogin:26" : {
"profile" : {
"first_name" : "Bob",
"last_name" : ""
}
}
}
}
Any help on how to make the above rules work? Cant seem to get it to validate correctly.
Your validation rule is:
".validate": "newData.hasChildren(['first_name', 'last_name'])"
So the new data is valid if it has first_name and last_name properties.
You're sending this object over:
"profile" : {
"first_name" : "John",
"last_name" : ""
}
This object has a first_name and a last_name property, so it is valid according to your rule.
What you seem to want is that the properties don't only exist, but also are strings and have a minimum length. If that is indeed your requirement, you can write it into your validation rules:
"profile": {
".validate": "newData.hasChildren(['first_name', 'last_name'])",
"first_name": {
".validate": "newData.isString() && newData.val().length >= 10"
},
"last_name": {
".validate": "newData.isString() && newData.val().length >= 10"
}
}
The first .validate ensures that a profile has (at least) first_name and last_name properties. The other .validate rules ensure that they are of the correct type and minimum length.
Please consider the follow rule:
"list" : {
"$item" : {
".validate": "newData.hasChildren(['field1', 'field2'])",
"field1" : {
".validate": newData.isString()
},
"field2" : {
".validate": newData.isString()
},
"$other" : {
".validate": false
}
}
}
}
It should only allow you to build a list of objects of a certain type. However, there's nothing to prevent the following:
{
"list" : "i've been hacked"
}
Only thing I've been able to come up with is to add this to the "list" rules:
".validate": "!newData.isString() && !newData.isNumber() && !newData.isBoolean()"
.. which is a bit clunky. Is there a better to enforce this or is my whole approach just wrong-headed?
You could just add .write: false to the list itself, and only enable it on the $item. This way, you can't write to list without validating $item rule.
To elaborate on #cwehrung's answer:
"list": {
".write": false,
"$item": {
".validate": "newData.hasChildren(['field1', 'field2'])",
"field1": {
".write": "newData.isString()"
},
"field2": {
".write": "newData.isString()"
}
}
}
Another option:
"list": {
".validate": "newData.hasChildren()",
...
}