I am trying to set a rule on a node in firebaseDatabase, but I get error
Error saving rules - Line 85: Key names can't contain ".", "#", "$",
"/", "[", or "]" (unbound names start with "$")
As I understand auth.uid is a global variable for the current logged in user. How can I fix this?
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"notifications/auth.uid": {
".indexOn":["createdAt"]
}
}
}
If you're trying to store notifications for each user under their auth.uid and allow querying those by defining an index, then you're looking for these rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"notifications": {
"$uid": {
".indexOn":["createdAt"]
}
}
}
}
The $uid here is a wildcard, and applies to each node under notifications. To learn more about this, see Using $ Variables to Capture Path Segments.
This is the issue
"notifications/auth.uid"
as it's treating everything inside the quotes as a string so the period is causing the error and paths cannot include a period character. Also, it will not resolve the auth.uid as it's just a string, not the variable you want. You could do something like
root.child('notifications').child(auth.uid) {...
or even
root.child( 'notifications/' + auth.uid ) {...
Related
I need to allow only 2 specific uid for write access and I can't publish with my current rule:
{
"rules": {
".read": "auth != null",
".write": "auth != null && auth.uid === 'abc123' || '123abc'",
}
}
It keeps complain "rule regulation may not contain '|' operator" and
"right operand for '||' must be boolean", which ever i used.
How should I change my rule to achieve what I want?
Any help will be much appreciated.
The word after || is actually not a boolean, it is just a string. Please, check my fixed code.
{
"rules": {
".read": "auth != null",
".write": "auth != null && (auth.uid === 'abc123' || auth.uid === '123abc')",
}
}
I am using GeoFire to store location in Firebase realtime database. I have 2 tables table-a and table-b. For both of these tables, I would like to enable indexing. On saving the below rules, Firebase prompts the following error:
Cannot have multiple default rules ('$table-a' and '$table-b').
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"$table-a": {
".indexOn":"g"
},
"$table-b": {
".indexOn":"g"
}
}
}
Any ideas on what I might be missing here?
I think you should do as follows:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"table-a": {
".indexOn":"g"
},
"table-b": {
".indexOn":"g"
}
}
}
As explained here, the $location syntax is "used to reference the key of a $location that was used earlier in a rule structure"
This is my DB structure
"tasks"
"$taskId"
...
"user": "firebase user id"
I have already written a rule ".read": data.child('user').val() === auth.uid" under $taskId. When I try to access a single task, this rule is taking effect.
Will this also guarantee that if I write a query like firebase.database().ref('/tasks').orderByChild('status').limitToFirst(1) I'll only get tasks that have user id field as auth.uid. Or should I also write a .read clause under tasks
There are several aspects to be answered in your question:
1/ At which level should you write the security rules?
If you write only at the task level like just follows, you will not be able to query the entire set of tasks.
You can test it by doing the following:
Rules:
{
"rules": {
"tasks": {
"$taskID": {
".read": "auth != null",
".write": "auth != null"
}
}
}
}
JS:
var db = firebase.database();
var ref = db.ref('tasks');
firebase.auth().signInWithEmailAndPassword("....", "....")
.then(function(userCredential) {
ref.once('value').then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val());
});
});
});
This will fail with "Error: permission_denied at /tasks: Client doesn't have permission to access the desired data."
If you change var ref = db.ref('tasks'); to var ref = db.ref('tasks/123456'); (123456 being an existing task id) you will get a result.
If you change your rules to the following, the two previous queries will work.
{
"rules": {
"tasks": {
".read": "auth != null",
".write": "auth != null"
}
}
}
2/ How should you do to only get tasks that have user id field as auth.uid?
The first point to note is that "Rules are not Filters", as detailed here: https://firebase.google.com/docs/database/security/securing-data#rules_are_not_filters
So if you implement security rules as follows:
{
"rules": {
"tasks": {
"$taskId": {
".read": "auth != null && data.child('user').val() === auth.uid",
".write": "auth != null"
}
}
}
}
You will need to write a query that includes the same restriction on the user uid, like the following:
var db = firebase.database();
firebase.auth().signInWithEmailAndPassword("....", "....")
.then(function(userCredential) {
var ref = db.ref('tasks').orderByChild('user').equalTo(userCredential.user.uid);
ref.once('value').then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val());
});
});
});
But this query will not work, again, because "Error: permission_denied at /tasks: Client doesn't have permission to access the desired data."
You cannot do the following neither, since "Shallower security rules override rules at deeper paths.":
{
"rules": {
"tasks": {
".read": "auth != null",
".write": "auth != null"
"$taskId": {
".read": "auth != null && data.child('user').val() === auth.uid",
".write": "auth != null"
}
}
}
}
One solution is to use Query-based Rules (see the doc here) and write your rules as follows:
{
"rules": {
"tasks": {
".read": "auth != null &&
query.orderByChild == 'user' &&
query.equalTo == auth.uid",
".write": "auth != null"
}
}
}
However, as you have probably noticed, this will prevent you to order your query (and filter it) by something else than the user (e.g. by status), since "You can only use one order-by method at a time."
The solution would therefore be to create a second data structure in parallel to your existing structure, where you add the user as a top node, like
"tasks"
"$taskId"
...
"user": "firebase user id"
"tasksByUser"
"$userId"
"$taskId"
...
You would use the update() method to write to the two data structures simultaneously. See the doc here.
I gave .read: true under tasks and it is considering the rules written under the individual task objects before returning the results.
So I'm delving into Firebase security rules and as far as I understand, rules that are specified higher up in the tree cascade further down into the tree.
So I'm wondering if there's a way to make a case work where I basically have a /bands subtree that I want writeable by anyone, however there are admins and members subtrees where I want only writeable, based on special conditions.
So far, this is kinda what I have going on:
{
"rules": {
".read": "auth != null",
"bands": {
"$bandId": {
".write": "auth !== null",
"$bandId": {
".write": "auth !== null && data.child('creator_id').val() === auth.uid"
}
}
}
}
}
When I go to test "writing", in the Firebase simulator, something like /bands/-KnLeIHM4zCspwBZjZP9 where the creator_id does NOT match the specified auth.uid I have provided, I still get a simulator write success, due to the /bands tree-level having the write access.
Is there any clever way to do allow anyone to "push" to /bands but then when it gets down to the actual /bands/$bandId level, it starts looking at these various conditions? Or am I going to have to rework my data and separate out the trees into even more trees? I have other instances where this kind of thing is necessary, but this is the most succinct version I am working with that I need to solve.
Any thoughts? Thanks in advance :)
{
"rules": {
"bands": {
".read": "auth != null",
".write": "!data.exists() && auth != null",
"$bandId": {
".write": "data.child('creator_id').val() === auth.uid"
}
}
}
}
".write": "!data.exists() && auth != null" will only allow authenticated users to write to paths within bands if they don't exist (creating new content).
I've got a data structure like this:
How can I access /Restaurant/-KK37k6g5cYYippEHpZ3/User/-KK37k6g5cYYippEHpZ4/id's value within the firebase security rules? The two push keys should be wildcards. I need something like this:
"Restaurant": {
"$id": {
".read": "auth.uid != null",
".write": "data.child($id).child('User').child($anotherWildcard).child('id').val() === auth.uid"
}
}
Not sure if I fully understood what you are asking for but here goes my thoughts.
The first problem in your rule is that you are specifying child($id) but you already are inside the $id. it is implicit in your data that you are referring to $id.
To resolve your main problem you wont need another wildcard. You can just use hasChild to verify if the auth.uid is inside restaurant/user.
"Restaurant": {
"$id": {
".read": "auth.uid != null",
".write": "auth.uid != null && data.child('User').hasChild(auth.uid)"
}
}