Firebase queryEqual(toValue:) not working - ios

I have a database with the following structure
"users":{
"user1234" {
"username": "user1234"
}
}
I am trying to do a query to find a user with username "user1234". When I use queryEqual(toValue:) I don't get any matches but when using queryStarting(atValue: ) I do get the user. I have confirmed that the username is actually "user1234". What am I doing wrong?
let query1 = databaseRef.child("users").queryOrdered(byChild:"username").queryStarting(atValue: "user1234").queryLimited(toFirst: 1)
query1.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
//snapshot contains the user with username "user1234"
})
However the following does not work
let query2 = databaseRef.child("users").queryOrdered(byChild:"username").queryEqual(toValue: "user1234")
query2.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
//snapshot DOES NOT contain the user with username "user1234"
})
My security rules are
"rules": {
".read": "true",
".write": "true",
"users": {
"$uid": {
".indexOn": ["username"],
},
},
},

You're facing this issue because query goes only one level deep, so you need to flatten your data or you have to figure out a different approach.
Here you can find more detailed answer.

Related

Attempting to get a key where value is equal to in Realtime Database

I have the following structure:
attendanceOptions:
uid: "specificemail#email.com",
uid: "anotherEmail#email.com",
I'm attempting to get the key and value where value is...
In example get where anotherEmail#email.com
Here is my attempt:
security rules:
{
"rules": {
".read": true,
".write": true,
"organization": {
"$uid": {
"attendanceOptions": {
".indexOn": ["$uid"]
},
"members": {
".indexOn": ["mStatus"]
}
}
}
}
}
The error I get is
Error saving rules - Line 9: Invalid key: $uid. Index must be either .value or declared on a valid path
Obviously I'm doing something wrong. How can I accomplish this?
If you want to define an index on attendanceOptions for the value of each child node, you can do that with:
"attendanceOptions": {
".indexOn": [".value"]
},
Also see the Firebase documentation on indexing by value.

How can I write the rule for the Firebase data structure below?

I want to write a rule in Firebase to allow only the user to be able to write to the database when the disable is set to false.
The structure of my database is as follows:
{
"Users": {
"UID": {
"details": {
"random key generated by push": {
"disable": true,
},
}
To read a value from another node inside your security rules, you have to know the exact path to that node. So unless you know the value of "random key generated by push", there is no way to read that from the rules on one of the nodes above it.
This usually means that you should change your data structure to have the disable flag at a known location under the UID node. For example:
{
"Users": {
"UID": {
"disable": true,
"details": {
"random key generated by push": {
},
}
Now you can allow access to UID node like this:
{
"rules": {
"Users": {
"$uid": {
".read": "data.child('disable').val() === true"
}
}
}
}

How can I limit Firebase Realtime db items to "users" who exist in an object

I have a realtime db all setup and working. The data structure is very simple:
Item
some: info
some: other info
Item 2
some: info
some: other info
My rules are also super simple:
{
"rules": {
".read":"auth.uid != null",
".write":"auth.uid != null"
}
}
The issue (obviously) is that while I am forcing a user to be authenticated, that's all I am requiring and any user can access all the items in the db.
What I want is a way to limit a user to an item.
something like:
Item1
some: info
some: other info
user_1: auth.uid
user_2: auth.uid2
Item2
some: info
some: other info
user_1: auth.uid3
user_2: auth.uid4
I can store that data but I am not sure how to structure my rules to limit that.
My actual json looks like:
{
"annotations": {
"8df0309f-dc62-821e-dd65-f0ad46396937": {
"author": "1OXVKN3Y5Z-11",
"xfdf": "LONG STRING"
}
},
"complete": false,
"created_at": "2020-09-01T17:52:25.653Z",
"field_values": {
"field_name": {
"name": "copy",
"value": "TEsting",
"widgetID": "e61e3abf-7cdd-7d07-daec-6c3d3a55d667"
}
},
"stamp_count": 0
}
What I plan to implement is:
{
"annotations": {
"8df0309f-dc62-821e-dd65-f0ad46396937": {
"author": "1OXVKN3Y5Z-11",
"xfdf": "LONG STRING"
}
},
"complete": false,
"created_at": "2020-09-01T17:52:25.653Z",
"field_values": {
"field_name": {
"name": "copy",
"value": "TEsting",
"widgetID": "e61e3abf-7cdd-7d07-daec-6c3d3a55d667"
}
},
"stamp_count": 0,
"users": [ "CFX4I0PTM9-11", "CFX4I0PTM9-7"]
}
One I implement that json structure, how can I setup rules to support?
From reading your question and the comment thread I think your requirement is:
Allow a user to access an item if their UID is associated with that item.
In that case, you'll first need to ensure that the UIDs are in keys, as you can't search across multiple values, as your proposed users array would require. So you'd end up with:
"items": {
"item1": {
...
"users": {
"CFX4I0PTM9-11": true,
"CFX4I0PTM9-7": true
}
}
}
Now with this structure, you can ensure a user can only update items where their UID is in the users map with rules like this:
{
"rules": {
"items": {
"$itemid": {
".write": "data.child('users').child(auth.uid).exists()"
}
}
}
}
For reading the specific item you could use a similar rule. That will allow the user to read an item once they know its complete path, and when their UID is in the users map.
But you won't be able to query this structure, as you can only index on named properties. For more on this, and the alternative data structure to still implement you use-case, see Firebase query if child of child contains a value

How prevent username from duplicate signUp in Firebase? [duplicate]

This question already has answers here:
Firebase android : make username unique
(4 answers)
Closed 6 years ago.
I want to prevent duplicate username while signUp form what user entered. I have created email login like below, firtly createUser and then auth it setValue with user dictionary.
But I stack with Firebase security settings and how to check handle this situation.
REF_BASE.createUser(email, password: pwd, withValueCompletionBlock: { ..
REF_BASE.authUser(email, password: pwd, withCompletionBlock: { ..
REF_USERS.childByAppendingPath(uid).setValue(user)
This handling always response error.
REF_USERS.childByAppendingPath(uid).childByAppendingPath("username").setValue(user["username"]) { (error: NSError!, result: Firebase!) in
if error != nil {
print(error.localizedDescription)
}
else {
self.REF_USERS.childByAppendingPath(uid).setValue(user)
}
}
This is my security rules, How Can I fix everything to be prevented from duplicate user?
"users": {
".read": "auth !== null",
"$user": {
"username": {
".read": "auth !== null",
".write": "newData.val() === auth.uid && !data.exists()"
}
}
},
Update:
From Frank's answer, but I don't know what I need to do while signUp new user for swift in this situation.
app : {
users: {
"some-user-uid": {
email: "test#test.com"
username: "myname"
}
},
usernames: {
"myname": "some-user-uid"
}
}
Need I add same user uid at the same time for two different nodes? If someone explain it in Swift. It would be great.
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.provider === 'password'",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
}
}
}
I couldn't add new user informations to usernames parent node. It's needed another security rules?
If you are trying to prevent duplicate Firebase usernames, that can be done right at the createUser stage.
Assume a dialog is presented to new user asking to create an account: this will get their desired userName and password.
Send that to createUser and if there's an error (because it already exists) notify them and ask for a different userName:
myRootRef.createUser(email, password: pw, withValueCompletionBlock: { error, result in
if error != nil {
self.errMsgField.stringValue = "email/username in use, try again"
} else {
let uid = result["uid"] as! String //the uid of the new user
print("user created as \(uid)")
self.authUserWithAuthData( email, password: pw ) //auth the user
}
})
After the user is created you would probably add their info the /users node as well.

Firebase queryOrderedByChild not ordering by name in Swift

I have in the security & rules option this:
{
"rules": {
".read": true,
".write": true,
"groups": {
".indexOn": "name"
}
}
}
And my JSON structure is like this:
{
"groups": {
"-KAti17inT7GbHEgbzzS": {
"author": "ruben",
"name": "C"
},
"-KAti36BQGO8sRmfufkE": {
"author": "ruben",
"name": "D"
},
"-KAti2CVAHtJllQm_m-W": {
"author": "ruben",
"name": "A"
}
}
As you can see, it is not ordered by "name", as is it supposed to be.
What I'm doing wrong?
That .indexOn instruction in the security rules only tells the database to create an index on the name property. It doesn't automatically re-order the data.
In addition: the order of keys in a JSON object is undefined.
To get a list of your groups by name, you have to execute a Firebase query. From the Firebase documentation on queries:
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByChild("height").observeEventType(.ChildAdded, withBlock: { snapshot in
if let height = snapshot.value["height"] as? Double {
println("\(snapshot.key) was \(height) meters tall")
}
})
If you adapt this snippet to your data structure, it will print the groups in the correct order.

Resources