I am using firebase database in my app. I have set the rules to allow normal users to create new childs in the node but I found that it's refused.
this is the writing rules of the node :
".write":"auth.uid == \"DFhNb28506Y345CpJ3Ye7DQNn713\" || ((newData.exists() && !data.exists()) || auth.token.email == data.child(\"userEmail\").val())",
I think that newData.exists() && !data.exists() should allow users to write in the database but this doesn't happened
this is the rules of the users node :
"users":{
".write":"auth.uid == \"DFhNb28506Y345CpJ3Ye7DQNn713\" || ((newData.exists() && data.child(\"userEmail\").val() != null) || auth.token.email == data.child(\"userEmail\").val())",
".read": "auth != null"
}
The database strucutre is like that :
-users
-user1
-userName, userEmail ....
-user2
-userName, userEmail .....
when a new user sign up in the app he should be allowed to push his data in the database
this is the database structre :
Ok, I think you're creating extra validation steps that aren't needed.
First
With ".read": "auth != null" on your users root, each user is able to access other user's data, so we should address the access for each user individually.
Second
If you just want to allow users that are authenticated to write and read its own contents, you can remove these extra ((newData.exists() && !data.exists()) and auth.token.email == data.child(\"userEmail\").val()) steps.
Tip: this ((newData.exists() && !data.exists()) comparison means exactly: Write here if you're sending anything but there should be nothing written in this requested "path". You should reflect on the need of this, as I don't know your exact use cases.
Also, I would guess the hardcoded UID you're requesting is of an Admin you've created - I wouldn't recommend this, please read more about user roles on this answer.
To clarify, I think your rules structure should be something like this:
{
"rules": {
".write":"auth.uid == \"DFhNb28506Y345CpJ3Ye7DQNn713\",
".read": "auth.uid == \"DFhNb28506Y345CpJ3Ye7DQNn713\",
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
Related
My Chat and Friends node are separate of course, so would it be possible to refer to the friend node when writing rules for my Chat.
my current chat rules are as follows:
"Chats": {
"$uid_1":{
"$uid_2":{
".read": "auth.uid == $uid_1 || auth.uid == $uid_2",
".write": "auth.uid == $uid_1 || auth.uid == $uid_2 "
}
}
},
Friends structure in database
- Friends
-uid1
-uid2
- username
-uid2
-uid1
- username
when users are no longer friends, how do I make sure that messages do not go through? I have rules inside my android application but was told that I should write it in the security rules too.
You can check data anywhere in the database, by starting from the root variable.
So if you want to only allow uid2 to write to uid1's messages if their UID is stored under uid1's friend list, you can check:
".write": "(auth.uid == $uid_1 || auth.uid == $uid_2)
&& root.child('Friends').child($uid_1).child($uid_2).exists()"
You can try out these rules:
{
"rules": {
"chats": {
"$uid": {
"$uid2": {
".read": "auth.uid === $uid || auth.uid === $uid2",
".write": "auth.uid === $uid && root.child('friends').child($uid).child($uid2).exists()"
}
}
}
}
}
This will still keep READ access for both the users but they cannot write new messages.
Though your db structure looks a bit difficult to store messages. chats -> uid 1 -> uid2 won't be same always I believe or at least it will be difficult for you to maintain the order like which user remains 1st and so.
I have put a rule as
(root.child('users').child(auth.uid).child('role/cafe').val() === true)
in my realtime database rules.
My user is
key {
uid: xxxx
name: xxxx
role:{
cafe: true
user: false
}
}
if auth.uid is equal to key, the rule works, however how can I modify the above rule to look for uid inside data
It is unclear what the value of $key is expected to be. So I'm going to assume it's just some random string that isn't used in the rules.
For each security rule, the current data of the node is accessible using the predefined variable data. ".write" and ".validate" rules also have access to the to-be-written data as newData. These are both RuleDataSnapshot objects.
Assuming that a user making changes must be the user given by the uid property, the following rules can be used.
"rules": {
"users": {
"$key": {
".read": "auth != null && data.child('uid').val() == auth.uid",
".write": "auth != null && ((data.exists() && data.child('uid').val() == auth.uid) || (!data.exists() && newData.child('uid').val() == auth.uid)"
}
}
}
The above rules use a fail-fast approach. If a user is not logged in, the check aborts. Otherwise, the user's ID is matched against the existing data at the given node. If the data doesn't yet exist, the newly updated data must also match the current user's ID.
In case the "cafe" role is important, the following rules also require that the "cafe" is set to true to allow read/write operations.
"rules": {
"users": {
"$key": {
".read": "auth != null && data.child('uid').val() == auth.uid && data.child('role/cafe').val() == true",
".write": "auth != null && ((data.exists() && data.child('uid').val() == auth.uid && data.child('role/cafe').val() == true) || (!data.exists() && newData.child('uid').val() == auth.uid && newData.child('role/cafe') == true))"
}
}
}
Note: If $key is storing user info/data, I highly recommend using the user's ID as the key as security rules cannot perform queries like "does userid have admin role?" without structuring your data to allow it. If $key is meant to be a username, instead use a username-to-userID map as it will prevent future problems. One such example is if a user wants to change their username, you can remove and link the new username to the user without ever having to move all their data.
You can use a wild card. In your rules
"users":{
"$key":{
".read" : (root.child('users'+$key).child('role/cafe').val() === true) && (root.child('users'+$key).child('uid').val() === auth.uid ),
".write" : (root.child('users'+$key).child('role/cafe').val() === true ) &&(root.child('users'+$key).child('uid').val() === auth.uid )
}
},
The first condition checks whether the café value for the user is true the second checks whether the uid is the same
Lemme introduce my problem, I would like to build a product like advisory service, that user send their question (post), and then admin give him an advice.
So this is my schema:
About security, I prefer user can get their own questions only, but admin can see all of them.
{
"rules": {
"profiles": {
"$uid": {
".read": "auth != null && (auth.uid == $uid || root.child('profiles').child(auth.uid).child('role').val() == 'admin')",
".write": "auth != null && (auth.uid == $uid || root.child('profiles').child(auth.uid).child('role').val() == 'admin')"
}
},
"posts": {
"$uid": {
".read": "auth != null && (auth.uid == $uid || root.child('profiles').child(auth.uid).child('role').val() == 'admin' || root.child('profiles').child(auth.uid).child('role').val() == 'admin')",
".write": "auth != null && (auth.uid == $uid || root.child('profiles').child(auth.uid).child('role').val() == 'user')"
},
".read": "auth != null && (root.child('profiles').child(auth.uid).child('role').val() == 'admin' || root.child('profiles').child(auth.uid).child('role').val() == 'mod')"
},
".read": false,
".write": false
}
}
From user-view, it looks quite good because I can get quickly posts belong to that user. However, from admin-view, it really bad:
I don't how to purely get all user's posts. I have to get all users, then get posts of user later.
I can not get exactly trigger event by using $watch. When an user add new post, I got "child_changed" event (of user by id), not "child_added" (of post by id)
I'd like to query all questions that not response from admin yet, but dont know how.
A trade-off, I've stored all post in plain array e.g:
posts: {
<post_id>: {
uid: <user_id>,
....
}
}
But then I've got problem with access rule, can not restrict users to access their own data only. Without read-rule, user can bruce-force user-id to get data.
Would you please give me a hint. Thank you in advance!
P/S: I'm working on Firebase w/ angular JS, no backend server
I'm working on a Firebase rule configuration to control read/write access to my database. I had more rules written originally, but I've pared things down during troubleshooting. Here is my current rule configuration:
{
"rules": {
"developers": {
"$dev": {
".write": "!data.exists() && auth != null",
".read": "auth.devBucket === $dev",
"$proj": {
".read": "auth.devBucket === $proj",
"shared": {
".write": "!data.exists() || (auth.devBucket === $dev && auth.projBucket === $proj)"
}
}
}
}
}
}
What I'm trying to do is allow users of the Firebase to create a $dev node, $proj node, and shared node as long as they don't already exist and the user is authenticated. Then, I want to allow a user to have free write access within the shared node as long as their auth token's devBucket matches the $dev node they're writing within and their auth token's projBucket matches the $proj node they're writing within. I'm using the Firebase custom auth system for Android and I've loaded my tokens with these devBucket and projBucket variables. Authentication is definitely working according the my logcat, but I'm definitely getting permission denied errors with my current rules. I've been pouring over the Firebase Rule documentation and questions here for days and I am still puzzled as to the nuances of how their rule system works.
According to the documentation rules carry through to lower levels of nesting in the JSON, I'm just having trouble understanding how I can write a rules that allows a node and it's children to be created once, but also allows any number of children to be written or overwritten under shared if you're properly authenticated.
Does anyone have any idea how I could write rules to accomplish what I'm trying to do?
EDIT: I think it's also worth mentioning that I'm getting permission denied errors when I try to point listeners to my nodes too.
I figured out a configuration that worked for me.
{
"rules": {
"developers": {
".write": "!data.exists() || auth != null",
".read": "auth != null",
"$dev": {
".write": "!data.exists() || (auth != null && auth.devBucket == $dev)",
".read": "auth != null && auth.devBucket == $dev",
"$proj": {
".write": "!data.exists() || (auth != null && auth.projBucket == $proj)",
".read": "auth != null && auth.projBucket == $proj"
}
}
}
}
}
Before in my app I was creating accounts successfully until I put some rules
{
"rules": {
"users": {
"$uid": {
// grants write access to the owner of this user
// account whose uid must exactly match the key ($uid)
".write": "auth !== null && auth.uid === $uid",
// grants read access to any user who is logged in
// with an email and password
".read": "auth !== null && auth.provider === 'password'"
}
}
}
}
then, the accounts are created because I see those accounts in the dashboard. But once I try to create, I am getting this kind of errors
FIREBASE WARNING: set at /users/simplelogin:32 failed: permission_denied
Since you want the user to be able to create their own node initially, you'll need to explicitly allow the case where the location is new.
Based on the documentation on the auth parameter:
".write": "!data.exists() || auth.uid === $uid""
Note: I'm not entirely sure this will work, but felt it'd be more readable as in answer-format than as a "try this" comment.