I read through the docs, and I'm not sure of the difference between the write-rule and validation-rule section. Is this code redundant? Any point of using one or the other, or both?
Specifically:
"validates" to say "user must be logged in, and the value written must be the uid.
"write" permission says you can only write to the $user_id section if the value matches your uid.
{
"rules": {
"users": {
".validate": "auth != null && newData.val() === auth.uid",
"$user_id": {
".write": "$user_id === auth.uid"
}
}
}
}
The only difference is that .validate doesn't propagate to its children.
Answering to your question, in your example you could use just ".write".
".write": "auth != null && $user_id === auth.uid"
Technically there might not be (except propagation) any difference but in my opinion there is a difference.
For example Security is the primary concern of the .write while data integrity is the primary concern of the .validate.
For example:
Consider we are writing to /books/{bookKey}/name path.
Is user allowed to write to this path is .write's concern. Checks like user logged in can be performed here.
Is user is entering the right name for the book? Is .validate's concern. For example name should be more than 3 characters long, must start with characters...
P.S.
Also considering this issue with multi-line, it is good to have two separate methods for more readability.
Related
I have a question for my own sanity. Below is one portion of my firebase rules
{
"rules": {
".read": "auth != null",
".write" : false,
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid",
"tokens": {
".write": "(newData.val() < data.val())"
}
}
},
...
If I understand correctly the rules state that:
ALL users must be auth'ed in order to read ANY node
ALL user can NOT write to any nodes
Specific to the User node:
In order to read from your own data, you need to be auth'ed and you can only read your own node
In order to write to your own user data, you must be auth'ed and you can only write to your own node
The user/token node can only be decremented and never increased by any user
Can someone confirm my assumptions/understandings reading Firebase security rules documentation.
Also does anyone have any good articles or helpful tips on using the simulator!?
An important concept with the security rules is that read/write rules "cascade" down the tree. This is discussed briefly in the documentation. That means that as you read your rules from top to bottom, the first rule that grants access takes precedence over any rules specified below it on children of that location.
Addressing each of your items:
ALL users must be auth'ed in order to read ANY node (YES)
ALL user can NOT write to any nodes (non-auth'ed users can NOT write to any nodes)
Specific to the User node:
In order to read from your own data, you need to be auth'ed and you can only read your own node (YES)
In order to write to your own user data, you must be auth'ed and you can only write to your own node (YES)
The user/token node can only be decremented and never increased by any user (see below)
In your current rules, the check for smaller token is not effective because the prior rule granting write access to an auth'ed user overrides it. You also need to address the case where there is no existing token value. My suggestion for fixing that is to use a .validate rule. The documentation recommends:
Used once a .write rule has granted access, to ensure that the data
being written conforms to a specific schema.
{
"rules": {
".read": "auth != null",
".write": false,
"users": {
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid",
"tokens": {
".validate": "!data.exists() || (newData.val() < data.val())"
}
}
}
}
}
As for the Simulator, I don't know of any user guide, but have managed to learn how to use it by experimentation. It's a very effective tool for understand the rules.
Here are a few cases of using the Simulator:
When you open the Simulator, Authenticated is off, which simulates a non-authenticated user. To simulate a read, click on the read button, enter a location: e.g. /users/xyz/tokens, and click on Run. You will see a red X on the lines of the rules that forbid that operation. To simulate an authenticated read, click on the Authenticated button and, for convenience, enter a simple user UID, like "Frank". Now enter location /users/Frank/tokens, click on Run and observe that the read succeeds.
You can do similar tests for writing, entering a location, auth settings and value.
I am little bit confused about setting permissions in Rules section of my Firebase database.
I am working on an app (which is a Book actually) and the app must be updated by only one person with this email address: someone#gmail.com. Therefore the rest of people, either authenticated or not, must not be able to modify the contents, but they are allowed to read.
If you look at the Firebase Security Rules API, you'll see that the user's email address (if there is one) is made available via auth.token.email.
So to grant write access to the entire database to the user with the someone#gmail.com email address and read access to everyone else, you could define rules like this:
{
"rules": {
".read": true,
".write": "auth !== null && auth.token.email === 'someone#gmail.com'"
}
}
Said rules would grant read access to everyone. If you wanted to grant read access only to authenticated users, you could use:
{
"rules": {
".read": "auth !== null",
".write": "auth !== null && auth.token.email === 'someone#gmail.com'"
}
}
EDIT: Figured it out. Multi-location updates work not like updates as far as they seem to overwrite pre-existing values.
I have a hard time figuring out security rules for multi location updates. Until now I had this code
return this.userProfileRef.child(firebase.auth().currentUser.uid).update(profileData);
to update a user profile in fb, where profileData is an object containing some fields.
when I try to do the same operation, but written in a way that I can add more write operations (multi-location update), I get a validation error.
var updateData = {};
updateData['users/' + firebase.auth().currentUser.uid] = profileData;
return firebase.database().ref().update(updateData);
my security rules are
{
"rules": {
".write": "true",
".read": "true",
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "!data.exists() || ( data.exists() && auth.uid === $uid )",
"profileStatus": {
".validate": "!data.exists() || (newData.parent().hasChildren(['firstName', 'dateOfBirth', 'gender', 'lookingForGender', 'lookingForAgeMin', 'lookingForAgeMax', 'lookingForRadius']) && data.val() === 'incomplete' && newData.val() === 'awaitingVerification')"
}
}
}
}
Validation fails in profileStatus, when I do the multi-location update as written above, but passes when I do a 'normal' update.
Can someone help out and tell me what I'm missing here. Does fb handle multi-location updates differently when it comes to security rules?
Thanks.
Okay, to answer my own question. It seems that multi-loation update works more like a multi-lcoation set, so it overwrites existing values which caused my validation fail.
I'm still confused by this beviour… following the firebase blog here I thought, it would wor like an update.
In my example, i have a pre-existing key called dateOfBirth. This key gets deletd when I run the multi-update
I use Firebase. Here is what one of my nodes looks like:
--Users
----RandomUID1
----RandomUID2
----RandomUID3
For Rules, this is what I have:
--"users" : {
----".read": "auth != null && !root.child('blockedUsers').hasChild(auth.uid)",
----".write": "auth != null && !root.child('blockedUsers').hasChild(auth.uid)"
--}
What I would like is for reading from the users node to stay the same, but I would like for writing to only be allowed on children of the users node. The reason for this is it would be catastrophic if I accidentally wrote code one night that deleted the entire users node. Is it possible to make a rule so that writing on the node itself is not allowed but writing to child nodes is allowed?
Is there a way to secure myself from such an accident that might occur because of human error?
You can use variables, Firebase variable rules
.At the users node i didn't add a write rule, which doesn't allow for data modification.
"users":{
".read": "auth != null && !root.child('blockedUsers').hasChild(auth.uid)",
"$uid": { //where $uid is child
".read": "auth!==null && auth.uid == $uid",
".write": ""auth!==null && auth.uid === $uid"
}
}
I've been looking on the docs but I couldn't figure out how to prevent duplicated entries if the email exist on a record. There are my current 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'"
}
}
}
}
And my record format is:
Thank you very much
Unfortunately you cannot do this type of query in firebase due to it's distributed nature. In general, arrays are extremely tricky and you can read about their limitations in the context of Firebase here.
The way I see it you have two options, you can index your users "array" by the email itself, or you can keep a completely separate object holding all the emails in the system to check against when you make an insert. My suggestion would be the first, set the user object to users/<email>.