I'm trying to prevent my home view from loading posts that are flagged by the user.
I have a data structure as follows:
"reportedPosts" : {
"HtnULzU0lnZKYva2M2Wepl6N8wE3" : {
"reported" : true,
},
"boX6rtJ98haWVxNoXfSq21maCVU2" : {
"reported" : true,
},
},
"posts" : {
"HtnULzU0lnZKYva2M2Wepl6N8wE3" : {
"details" : "abc",
},
"boX6rtJ98haWVxNoXfSq21maCVU2" : {
"details" : "abc",
},
"jSMSkY9rHtdNkXoLrsFmCAXdY9n2" : {
"details" : "abc",
},
"jnFhJbgCjZeJFx0hspObqoskQej2" : {
"details" : "abc",
},
"r6KPesUr1qORfIJke07SloZHeNW2" : {
"details" : "abc",
}
}
My Security Rules:
"posts": {
".indexOn": "datestamp",
"$post_id" : {
// only load posts not reported
".read": "auth != null && !root.child('reportedPosts').hasChild($post_id)",
".write": "auth != null"
}
},
"reportedPosts" : {
".read": "auth != null",
".write": "auth != null"
}
However I'm getting
Listener at /dev/posts failed: permission_denied
Can someone help me shed some light?
This is a common stumbling block when designing Firebase databases. As the docs say, rules are not filters so if you make some children entities readable and others not readable, you'll no longer be able to fetch /posts as you'd expect.
A simple solution: When a post is reported, move it from /posts to /reportedPosts so that it not longer exists under /posts. Then after you've reviewed the post you can move it back under /posts if the post doesn't violate any rules. That simplifies your rules and allows users to fetch a list of posts without seeing reported posts.
Related
I am coding a microblogging site on Firebase, to which I am very new. I am storing user information (e.g. introduction, profile pictures) and posts the users write like below structure:
{
"posts" : {
"postid" : {
"category": "xx",
"content": "xx",
"uid":"xx"
}
},
"users" : {
"uid" : {
"intro" : "xx",
"nickname" : "xx",
"profile_picture" : "xx"
}
}
}
I'd like make following rules:
Any signed-in users will be able to post, but only will be able to edit their own posts afterwards
For user info, they will only be able to post/edit their own.
I set the database security rules like below.
"users": {
".read": true,
"$uid": {
".write": "auth.uid===$uid"
},
},
"posts": {
".read": true,
".write": "auth!==null",
},
Here are a couple of issues:
a. This rule will enable any bad user to edit any other post that is not his/her own.
b. When trying to write to users node, there is no existing UID child node at the moment and the users are denied permission to write.
How can I change the database rules to solve the two issues? My preference is doing so without rewriting the front-end code or change the database structure...would love to seek some advice here!
To only allow users to write their own posts (or create new posts with their own UID), you can check the value of the uid field in write operations:
"posts": {
".read": true,
".write": "auth !== null && auth.uid === newData.child('uid').val",
},
I've the follwing database:
actions : [ {
"added" : 1535293085383,
"countdown" : 9999999999,
"item" : 1,
"type" : "a"
}, {
"added" : 1535293085383,
"countdown" : 999999999,
"extra" : "bb",
"item" : "2",
"type" : "b"
}, {
"added" : 1635293085383,
"countdown" : 1,
"item" : 3,
"type" : "c"
}]
I want to any logged user be able to read all data, but ONLY WRITE THE COUNTDOWN NODE.
My idea is everytime users read the data decrement that value, but they are not allowed to update any other node
there are the rules i wrote
{
"rules":{
".read":false,
".write":false,
"actions":{
".indexOn":[
"added"
],
".read": "auth != null",
"countdown":{
".write" : "auth != null"
}
}
}
it is denying read from unauthenticated users
it is allowing to read from authenticated users
it is dennying to write from authenticated users EVEN in the countdown node
how can i fix it
You're missing a level in your security rules. Right now you allow writing to /actions/countdown. But you want to allow writing to /actions/*/countdown. To capture that requirement, use a $ variable in your rules:
{
"rules":{
".read":false,
".write":false,
"actions":{
".indexOn": [ "added" ],
".read": "auth != null",
"$actionid": {
"countdown":{
".write" : "auth != null"
}
}
}
}
Now because of the $actionid the countdown/.write rule under there applies to each child node of /actions.
I'm creating an application which lets users create items and then allow other users to subscribe to those items. I'm struggling to craft a rule that will prevent users from subscribing more than once to an item.
Here is an example of my data structure (anonymized, hence the "OMITTED" values):
{
"OMITTED" : {
"name" : "Second",
"body" : "this is another",
"userName" : "Some User",
"userId" : "OMITTED",
"created" : 1385602708464,
"subscribers" : {
"OMITTED" : {
"userName" : "Some User",
"userId" : "OMITTED"
}
}
}
}
Here are my Firebase rules at present:
{
"rules": {
".read": true,
".write": "auth != null",
"items": {
"$item": {
".write": "!data.exists()",
".validate": "newData.hasChildren(['name', 'body', 'userId', 'userName']) && newData.child('userId').val() == auth.id",
"subscribers": {
"$sub": {
".validate": "newData.hasChildren(['userId', 'userName']) && newData.child('userId').val() != data.child('userId').val()"
}
}
}
}
}
}
How can I prevent users from subscribing more than once? What is the rule I need to prevent duplicate users within the subscribers list based on userId?
Since security rules can't iterate a list of records to find the one containing a certain bit of data, the trick here is to store the records by an ID which allows for easy access. There is a great article on denormalization which offers some good insights into this practice.
In this case, if your use case allows, you may simply want to switch your data structure so that records are stored by the user's id, rather than storing the ID as a value in the record, like so:
/users/user_id/items/item_id/subscribers/user_id/
In fact, as you'll see in denormalization, you may even benefit from splitting things out even farther, depending on the exact size of your data and how you'll be reading it later:
/users/user_id
/items/user_id/item_id
/subscribers/item_id/user_id
In either of these formats, you can now prevent duplicates and lock down security rather nicely with something like this:
{
"users": {
"$user_id": { ".write": "auth.id === $user_id" }
},
"subscribers": {
"$subscriber_id": { ".write": "auth.id === $subscriber_id" }
}
}
I'm really scratching my head here, and I've been reading as much as I can on many other cases. I'm new to Firebase, but got some understanding of it. I was wanting to restrict certain records of the database to certain users. So here's my JSON:
"accounts" : {
"13asdf313" : {
"dog" : "bacon",
"email" : "luis#fakeemail.com",
"first" : "luis",
"last" : "xxxx"
},
"HlELrrGDbiMKgxxxx" : {
"name" : "Luis"
},
"anthony" : {
"email" : "anthony#fakeemail.com",
"last" : "xxxx",
"name" : "anthony"
},
"jpSq6UzX0mcAvExxxx" : {
"name" : "anthony"
}
}
Here are the rules set up based on what I've been reading:
{
"rules": {
"accounts":{
"$uid":{
".read": "auth.uid == $uid",
".write": "auth.uid == $uid"
}
},
}
}
In the simulator, I used the bottom condition (I even put /accounts in the location field). I used the UID: HlELrrGDbiMKgxxxx, copied straight from Firebase account list.
This is always coming up as failed
What am I doing wrong?
You have to insert accounts/HlELrrGDbiMKgxxxx into the Location field.
Otherwise you are trying to access the whole database (standard location is root, which covers the whole database). Your rule is just set for the child accounts/$uid and to access it the user id from authentication and the child location in the database must match.
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*/
}
}
}