I have a firebase data where the structure of the data is as follows:
{
"users": {
"123456": {
"Name": "ABC"
"uid": "123456"
}
}
"Orders":{
"987654": {
"Customer ID": "123456"
"Other Details": *****
}
}
}
Where users are the list of users in my database. And Orders are orders placed by each of the users. The user id and order id shown are dummy. They are generated by push key. Now the orders can be read by the user if the "Customer ID" is same as the userid. For which I have the following Firebase rule:
"Orders":{
".indexOn": ["Customer ID"],
"$Orders_id": {
".write": "((root.child('Orders/'+$Orders_id+'/Customer ID').val() === auth.uid && auth != null) ||(auth != null && !data.exists())",
".read": "root.child('Orders/'+$Orders_id+'/Customer ID').val() === auth.uid && auth != null "
}
}
However, I get the message "Permission Denied". The first write of the data was flawless. Can someone please guide.
Are you sure read / write about right node from the coding.
you can access to read/write in this node => Orders/{Orders_id}
you can't access to read/write in this node => Orders
Related
What should be the firebase rules for comment on post which is similar to facebook.
There are two things:
first, only authenticated user can comment.
Second, only the user who has commented can delete the comment. The user who has commented his id is saved in username.
I strongly suggest using Firebase Bolt for writing/compiling Firebase Database Security rules. Data structure can get big and complicated. Using Bolt language you'll be able to easily write complex access and structure rules that can be re-used for other db patterns.
Your rules would look something like this:
path /comment/{postUid}/{commentUid} is Comment {
read() { true }
write() { isAuthor(this) || isAuthor(prior(this)) }
}
type Comment {
text : String,
username : String
}
isAuthor(value) { auth != null && value.username == auth.uid }
Pay attention to isAuthor(prior(this)) call. This is the way to make sure only author can delete a comment. prior function returns data as it was saved before current event (create, update or delete).
After using firebase-bolt tool to compile rules to JSON format you'll get:
{
"rules": {
"comment": {
"$postUid": {
"$commentUid": {
".validate": "newData.hasChildren(['text', 'username'])",
"text": {
".validate": "newData.isString()"
},
"username": {
".validate": "newData.isString()"
},
"$other": {
".validate": "false"
},
".read": "true",
".write": "auth != null && newData.child('username').val() == auth.uid || auth != null && data.child('username').val() == auth.uid"
}
}
}
}
}
How can I add new rule to Firebase Realtime Database so that users can read data where recid equal to user ID?
I have a massage table with this structure:
"messages" : {
"-KyyjeMOtc7fWAsOiuiP" : {
"recid" : "FL5hyQJrsHWRQsRtiLe1PxkyRnk1",
"senderid" : "6K6pQHaCishDlCb0Y9AaN3zI22n1",
"text" : "hi"
},
"-KyykczCNpsSL6a1t8vt" : {
"recid" : "FL5hyQJrsHWRQsRtiLe1PxkyRnk1",
"senderid" : "6K6pQHaCishDlCb0Y9AaN3zI22n1",
"text" : "test"
},
}
I want a rule that when data is added to the database, only the user whose uid is equal to recid can see the data.
To achieve this, you can create user-based security rules for your database, something similar to:
{
"rules": {
"messages": {
"$messageId": {
".read": "auth.uid == data.child('recid').val()",
".write": "auth !== null"
}
}
}
}
In this example, $messageId uses a $location variable that will match any key under your messages list. Then, we grant read access only if the current user's auth.uid matches the recid child value.
I want to stop the signup process if the username exists. I don't want to create Email\Password (Authentication) and other info if the username exists
database rules:
{
"rules": {
"users": {
".read": "(auth != null)",
".indexOn": ["username"],
"$uid": {
"$username":{
".write": "(auth != null) && !data.exists() || newData.exists()"
}
}
}
}
}
I have added !data.exists() in .write , but still it duplicate usernames.
And then I added ".validate" to make username atleast 3 characters, to see if the signup process gonna fail if its longer, but it created email\password and other stuff but didn't wrote any data in database root\users\
After registered new user with email\password I got this error, Which means username couldn't be more than 3 characters. How to stop the signup process and show alert message at this situation?
[Firebase/Database][I-RDB03812] setValue: or removeValue: at /users/jt94ARqOoodtU3kYp3W1MtlUZ4m1 failed: permission_denied
Please help me if you could fix the problem.
Your simplified rules are:
{
"rules": {
".write": "(auth != null)",
"users": {
"$uid": {
"username": {
".write": "auth != null && (!data.exists() || !newData.exists())",
So you first grant any authenticated user write access on the root of the data and subsequently say that a user can only write if there is not data a the current location.
That won't work: once you grant permissions at a certain level, you cannot take that permission away on a lower level.
The Firebase documentation describes this as Read and Write rules cascade:
Child rules can only grant additional privileges to what parent nodes have already declared. They cannot revoke a read or write privilege.
I'm trying to create a website that uses Firebase's databases to store user information. I want to use unique usernames. I have two indexes, one for users and another for usernames.
My database is structured like this:
users {
$uid {
username: "username1",
gender: "xyz"
email: "xyz"
}
},
usernames {
"username1": $uid"
}
The users claim a username with their $uid.
These are my rules:
{
"rules": {
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.uid === $uid",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
}
}
},
"usernames" : {
".write": "!data.exists() && auth!= null",
".validate": "newData.val() == auth.uid" <---- I can't get this too work
}
}
}
When setting username under $uid it checks the usernames index so username can only be written with a username not in use or one that has it's own $uid.
I only want data in which the value is the authenticated users uid and the key is the username. I can't quite get this to work. I suspect that I am using newData().val() incorrectly. My validate statement is failing.
I'd like to avoid using custom tokens, but I'm open to any suggestions. Thanks in advance.
Sorry if this explanation is too drawn out, this is my second post on StackOverflow.
Edit #2
I did some research from what I can tell and all I can find in docs talks about the need to use .child() before .val() but I need .child to take a variable instead of a set username.
The accepted answer will cause problems as soon as you want to allow people to change their usernames.
By slightly changing your database structure you can easily manage usernames in firebase. You can also allow users to change their username.
Database:
usernames: {
username1: {
owner: "userId1"
},
username2: {
owner: "userId2"
},
...
}
The following rules will:
stop any user changing/deleting another user's username
let a new user create a username
let a user change their username by deleting their old one and creating a new one
(split on newlines only for readability)
"usernames": {
"$username": {
".read": "true",
".write": "auth != null &&
(
\\ Allow user to delete only their own username
(data.exists() && data.child('owner').val() === auth.uid && !newData.child('owner').exists())
||
\\ Allow user to create a new username if the username is not taken
(!data.exists() && newData.child('owner').val() === auth.uid)
)"
}
}
This update will create a user:
const toAdd = {}
toAdd[`usernames/${username}`] = { owner: userId };
firebase.database().ref().set(toAdd)
This update will change a users username:
const update = {}
update[`usernames/${previousUsername.toLowerCase()}`] = null;
update[`usernames/${newUsername.toLowerCase()}`] = { owner: userId };
firebase.database().ref().update(update)
In your code you should also query the database to check if a username exists before sending the update.
Why I avoided the username: userId approach:
You say
Edit #2 I did some research from what I can tell and all I can find in docs talks about the need to use .child() before .val() but I need .child to take a variable instead of a set username.
I believe this is correct. For your usernames setup:
usernames {
"username1": userId1,
"username2": userId2,
...
}
This answer describes a method which sends the key information in the request:
ref.update({
_username: "username3",
username3: "userId3",
})
Then you can check if the username is in the database with a rule like
data.child(newData.child('_username').val()).exists()
However in your usernames setup, you would then overwrite the other usernames with this update. To avoid that you would need to set the new data at the path of the username, usernames/username3 = userId3. But you cannot set it like this as you are then back at the problem of not having the key to reference in the rule.
You would need to create a nonsensical structure something like:
usernames: {
username1: {
_username: "username1",
userId: "userId1"
}
}
So I chose the simpler, but unfortunately a slightly less beautiful username database setup I describe at the start.
Sorry if i'm late but i ran into a similar problem, i changed my usernames rule to the following which did the trick:
"usernames" : {
"$username": {
".write": "!data.exists() && auth!= null && newData.val() == auth.uid"
}
},
}
I have a field within Firebase called 'pending_members' which contains a list of members pending permission to be granted by an 'owner', as such, 'pending_members' requires the following rules:
The current user can only add themselves (uid)
The current user can remove only themselves (uid) from the list
The 'owner' can remove any member from the list
Only the 'owner' can read the list
I've tried various security rules but seem to miss many corner cases, for example, a user is given write access because the data contains their uid but they can then submit someone else's uid along with this.
Can anyone suggest appropriate rules for this situation? Many thanks
"pending_members" : {
".write" : "auth !== null &&
// The user is authenticated AND
(newData.child(auth.uid).exists() ||
// The new data contains either the current user's id OR
(!newData.exists() &&
// There's no new data (a delete operation) AND
data === auth.uid))",
// The old data is the current user's id
"$member" : {
".validate" : "newData.isString()",
"$other": { ".write": false, ".read": false }
}
}
Edit:
Structure Example:
users ->
personal_data ->
email (user email address)
first_name (user first name)
last_name (user last name)
networks_index ->
networks ->
members (list of uids of users linked to the network)
owner (uid of the owner/primary user)
pending_members (list of uids of users wishing to link to the network)
Data Example (image)
Complicated structure you have but i will give it a try:
Keep in mind standard value for read and write is false.
{
"rules": {
"networks": {
"$networkid": {
//Give read and write access to the owner of the network
".read": "auth != null && "root.child('networks').child($networkid).child('owner').val() == auth.uid",
".write": "auth != null && "root.child('networks').child($networkid).child('owner').val() == auth.uid",
"pending_members": {
"$uid": {
//Give members write access to their own node inside pending_members
".write": "auth != null && auth.uid == $uid",
//Use validate to check if the value is a bool or emty(removal)
".validate": newData.isBoolean() || !newData.exists()
}
}
}
}
I have only concentrated on the pending_members here, I hope that is enough and it is clear enough. If it doesn't work i suggest testing each rule seperatly to see which one is causing a ploblem so I (or someone else) can help fix it.