Firebase Security & Rules, How can I let users delete their own data? - firebase

My data in firebase looks like this, in my web app everyone who accesses it gets authenticated anonymously via firebase, and their UID is stored with every post the user creates:
"-KF5N2V_dKD1dMHebUqc" : {
"note" : "Hello everybody",
"pos" : {
"lat" : 40.3628851,
"lng" : -74.0493175
},
"time" : 1460395853884,
"uid" : "f8cf7863-5607-4e2b-97d7-6a121261466c"
},
"-KHwyP-tnWNOA3nxzEm4" : {
"note" : "hi",
"pos" : {
"lat" : 37.0947156,
"lng" : -121.0179501
},
"time" : 1463459362615,
"uid" : "f8cf7863-5607-4e2b-97d7-6a121261466c"
}
I want my firebase rules setup so that only anonymous users can delete their through own posts.
So far i was only able to come up with this after reading the firebase documentation:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"$msg": {
".validate": "newData.hasChildren(['note','time','uid','pos'])
&& newData.child('note').isString() && newData.child('time').isNumber()
&& newData.child('uid').isString() && newData.child('note').isString()
&& newData.child('pos/lat').isNumber() && newData.child('pos/lng').isNumber()"
}
}
}

You'll need to move the .write permission down and tie it to the data:
{
"rules": {
".read": "auth != null",
"$msg": {
".write": "!data.exists() || (!newData.exists() && data.child('uid').val() === auth.uid)"
".validate": "..."
}
}
}
It's a bit of mix-and-match from these two sections of the Firebase documentation:
https://www.firebase.com/docs/security/guide/securing-data.html#section-data-variables
https://www.firebase.com/docs/security/guide/user-security.html

Related

Firebase rules and validations for update existing data

I have a data structure as following:
{
"users" : {
"31J59dq1clZ3inHMzoopiigsEg63" : [ {
"name" : "Lanchana",
"timestamp" : 1516916245423,
"uid" : "31J59dq1clZ3inHMzoopiigsEg63",
"userEmail" : "*****#gmail.com",
"total-stars" : 123
} ]
}
}
and fire rules as following:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid && auth != null",
".write": "!data.exists() || data.child('uid').val() ==
auth.uid && auth != null",
}
}
}
}
I can add new data successfully but I cannot make any updates to the existing one. for ex: If I want to update only total-stars of a particular user based on the uid, how can I specify write and validation rules?
Your data structure seems to have a layer 0 that is missing from the rules. If that layer indeed exists in your actual JSON, you'll want to have it in your rules too:
{
"rules": {
"users": {
"$uid": {
"$postid": {
".read": "$uid === auth.uid && auth != null",
".write": "!data.exists() || data.child('uid').val() ==
auth.uid && auth != null",
}
}
}
}
I called the layer $postid here. You may want to use a name that better reflects what the layer represents.

Firebase: allow all users to create but only owners to edit while allowing users to "join" rooms

In my app I have public groups that can be created by anyone but only edited (edit group metadata) by the creator/owner.
Also a group can be joined by any authenticated user (and left by users who already joined).
I have the following test data:
"public_groups" : {
"group1" : {
"metadata" : {
"name" : "swag2017",
"numUsers" : 2,
"owner" : "user1",
"price" : 500
},
"users" : {
"user1" : "",
"user2" : ""
}
}
}
I have the following set of rules:
"public_groups": {
".read": "auth !== null",
//Either group id is new or writer is the owner of the group
"$groupId": {
".write": "auth !== null && (!data.exists() || data.child('metadata').child('owner').val() === auth.uid)",
"metadata": {
".validate": "data.child('owner').val() == auth.uid"
},
"users": {
"$userId": {
".validate": "auth !== null && (!data.exists() || $userId == auth.uid)"
}
}
}
},
Everything works properly, except for new users are not allowed to join the group (since it's editable only by owners).
It's blocked by the rule
".write": "auth !== null && (!data.exists() || data.child('metadata').child('owner').val() === auth.uid)"
Is it possible to leave all the restrictions while allowing new users to join the group? Or should I restructure my data?
Probably restructure. IMO you should pull your group membership records out of your public_groups nodes and make a top level group_membership node with a child node for each group that contains memberships for that group.

Firebase Security rule

I have data in firebase in following format -
"requests" : {
"-KpPjt5jQZHBalQRxKSK" : {
"email" : "pariksheet#agsft.com",
"itemId" : "-KmazkKp5wavdHOczlDS",
"quantity" : 1,
"status" : "new"
},
"-KpZsw3KHE9oD1CIFQ4R" : {
"email" : "pbarapatre#gmail.com",
"itemId" : "-Kmb-ZXfao7VdfenhfYj",
"quantity" : 1,
"status" : "new"
}
}
Every request contains
"email" <- user's email id who has initiated the request.
"itemId" <- id of requested item
"quantity" <- item quantity
"status" <- "new" | "approved" | decline.
I am struggling to write Firebase rule which would:
allow authenticated user to access/read only requests which are raised by him/her.
allow admin user to read/update all requests.
My current rule is as follows :
{
"rules": {
".read": false,
".write": false,
"items" : {
".read": "auth != null",
".write": "root.child('roles').child(auth.uid).val() === 'admin'"
},
"requests": {
".write": "root.child('roles').child(auth.uid).val() === 'admin'", /*Only Admins can update request*/
"$rId": {
".read": "data.child('email').val() == auth.email || root.child('roles').child(auth.uid).val() === 'admin'"/*Request owner and admin can only read the particular request*/
}
}
}
}
I have maintained separate node roles which has
{
"uid" : "role"
}
I am using AngularFire2 to query Firebase in my app.
Sample code to retrieve requests with given status as below
const queryList$ = this.db.list('/requests', {
query: {
orderByChild: 'status',
equalTo: status
}
})
Thanks
Pari
I suggest you make the following changes:
In the root of the database create a new object admins
"admins": {
"<ADMIN_1_UID>": "true",
"<ADMIN_2_UID>": "true"
}
Then make changes to your security rules like this:
"rules": {
"admins": {
".read": false,
".write": false /*This ensures that only from firebase console you can add data to this object*/
},
"requests": {
".read": "root.child('admins').child(auth.uid).val()",
".write": "root.child('admins').child(auth.uid).val()", /*Only Admins can read/update all requests*/
"$rId": {
".read": "data.child('email').val() == auth.email"/*Request owner can read only the particular request*/
}
}
}

Firebase data structure and security rules

Im having trouble with the security rules for firebase and Im not 100% where I am going wrong. I am thinking that maybe I have my data structure wrong:
{
"users": {
"uid": {
"displayName": "Name";
}
},
"modules": {
"id": {
"title": "buttons",
"uid": "(user id string)"
},
"id": {
"title": "navbars",
"uid": "(user id string)"
}
},
"snippets": {
"id = moduleID": {
"id (of snippet)": "(id string)" {
"uid (user ID)": "(string)",
"body": {
"css": "(some code)",
"html": "(Some code)",
"name": "(string)",
"description": "(string)"
}
}
}
}
Everything in the app works fine, but when I started to add security rules I got access denied errors. Im just wondering if I have the data structure correct in the first place or is the security rules completely wrong?
Security rules:
{
"rules": {
"users": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
},
"snippets": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
},
"modules": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
}
}
Any advice would be greatly appreciated.
It seems the rules are malformed based on the data structure.
The rules have $uid's in each node but your data doesn't match that. Users has uid but modules has id and snippets has id = moduleID.
$uid is a variable that holds the node name so it can be referenced inside { } so you should (for readability) rename that variable in the other two nodes to something that makes more sense inside each {}. Like in modules, have it $module_id.
However. I think the jest of this is you want to limit reading snippets and modules to authenticated users. To do that, you can reference the users node.
a .read rule would be something like this
"modules": {
"$module_id": {
".read": "auth != null && root.child('users/' + auth.id).exists()
}
So your modules node can be read by a user that is auth'd and their uid also appears in the users/ node
Are you using the Firebase Bolt compiler for rules? I had to write some complex rules and doing it by hand gets confusing very quickly.
Below is what it would looks like. Very easy to make changes, compile and try them out.
//current logged in user
isUser(uid) = auth != null && auth.uid == uid;
//does this module id exist
hasValidModule(module_id) = root['modules'][module_id] != null;
//dont let anyone read or write to top node
path / {
read() = false;
write() = false;
}
path /users/$user_id
{
write() = isUser($user_id);
read() = isUser($user_id);
}
path /snippets/$module_id/$snipit_id/$user_id
{
write() = isUser($user_id) && hasValidModule($module_id);
read() = isUser($user_id);
}
path /modules/$user_id
{
write() = isUser($user_id);
read() = isUser($user_id);
}
Here's the json it spits out:
{
"rules": {
"users": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id"
}
},
"snippets": {
"$module_id": {
"$snipit_id": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id && newData.parent().parent().parent().parent().child('modules').child($module_id).val() != null"
}
}
}
},
"modules": {
"$user_id": {
".read": "auth != null && auth.uid == $user_id",
".write": "auth != null && auth.uid == $user_id"
}
}
}
}
There's some info on the Firebase blog but the doc that really helped me is this
https://github.com/firebase/bolt/blob/master/docs/language.md

Firebase using auth.uid as key and adding rules to it and its children

I have a data structure that adds user's data to their unique id such as follows.
"users" :
{
"user_id":
{
"name":"John Doe",
"email":"email#example.com",
"account":"limited",
"avatar" : "this will be a base64 data string"
}
}
I want to deny users from listing other users and I also want logged in users to access their data based on their "user_id" which is gotten from auth.uid
I had tried some rules:
{
"rules" :
{
"users" :
{
".read" : "false",
".write" : "auth != null && !data.exists() && newData.exists() ",
".validate" : "newData.child('user_id').hasChildren(['name', 'email', 'account','avatar'])",
"user_id" :
{
".read" : "auth.uid === user_id",
".write" : "false",
"avatar" :
{
".write" : "!data.exists() && newData.exists() && auth.uid === user_id",
".read" : "auth.uid === user_id"
}
}
}
}
}
Now keeping in mind that "user_id" can be anything and it changes per user, how can I implement that? Do you have other suggestions on a way I can work this out?
You need to take a close look at the Firebase documentation found here: https://www.firebase.com/docs/security/guide/user-security.html
You need to make use of the wildcard path to represent each user like this:
{
"rules": {
"users": {
"$user_id": { //this is the WILDCARD path
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "$user_id === auth.uid"
}
}
}
}
Wildcard paths explanation: https://www.firebase.com/docs/security/api/rule/path.html
Finally, I wouldn't recommend storing the email in this way because it will be available anyway via simpleLogin.

Resources