Firebase Realtime Database rules for specific node of structure - swift

Before creating a new user I want to check if creating username property already exists in Firebase Database.
Checking function is:
let databaseRef = Database.database().reference()
databaseRef.child("users").queryOrdered(byChild: "username").queryEqual(toValue: loginRegisterTextField.text).observeSingleEvent(of: .value, with: { (snapshot: DataSnapshot) in
if snapshot.exists() {
print("Login exists")
} else {
print("Login does not exist")
}
})
JSON is:
Rules are for node users:
{
"rules": {
"users" : {
".read": "auth != null",
"$uid" : {
".write": "auth != null && auth.uid == $uid",
}
},
Is it possible to write a rules to check existing of username without a new uid?

There is no way to check for a specific value across a JSON branch in security rules. This has been covered quite a few times before, so I recommend checking some of these search results.
But you can make your query on /users more secure, by only allowing that specific query, and not allowing people to read all of /users. To secure the query you could some something like:
{
"rules": {
"users" : {
".read": "auth != null &&
query.orderByChild == 'username' &&
query.equalTo !== null",
...
This is the first time I've used query.equalTo !== null, so there may be some small mistakes in that part, but the flow should be clear.

Related

Firebase rules failed: permission_denied

In my app I have vc1 that pushes on vc2. Inside vc2 I pull some data from Firebase.
The problem is when I set my rules to the below values, when vc2 gets pushed on I keep getting failed: permission_denied
{
"rules": {
"sneakers": {
".read": "auth != null || auth.uid === null",
".write": "auth.uid != null"
},
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
If I go back to vc1, change the values to the below, then I get no problems and I get access
{
"rules": {
".read": "auth.uid != null",
".write": "auth.uid != null"
}
}
But here's the thing, once I set them back to the first values, go back to vc1 then push on vc2 I can then still get access to the database. If I delete the app and relaunch it the process repeats itself.
I want to use the first values because I want to keep the data at the user's node safe.
Why won't do I keep getting permission_denied with my first values, make a change to the rules, then after I change them back I no longer get permission denied? Where am I going wrong in my rules
vc2:
let root = Database.database().reference()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
root?.observeSingleEvent(of: .value, with: {
(snapshot) in
// sneakers node might not exist
if snapshot.hasChild("sneakers/nike)"){
....
}
)}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
root?.removeAllObservers()
}
The database layout:
root
|
#-sneakers // this node might not exist
| |
| nike
|
#-users
| |
| uid
|
#-jackets...
You're trying to read from the root of your database (root?.observeSingleEvent(...), but you only grant access to individual users and sneakers nodes. Since you're trying to read more than you have access to, Firebase rejects your observer.
It's hard to say why sometimes you don't get this error, but most likely it's a caching issue. Either your new rules haven't been applied yet (which should be rare), or you're reading from the cache of your iOS device (where the data was stored when you still did have access).
Either way, you should make sure you only read data you have access to. So:
root?.child("sneakers/nike")?.observeSingleEvent(of: .value, with: {
(snapshot) in
// sneakers node might not exist
if snapshot.exists() {
....
}
)}

Unable to Compare database timestamp to firebase 'now' security rule

I am having an issue comparing the time interval that I have saved in my firebase db to the 'now' firebase security rule. The rule I am writing is intended to prevent users from reading if a post is after a certain time.
This is how I am saving the timestamp in my database:
"time": NSDate().timeIntervalSince1970
This is the security rule I am writing that should prevent a read if after a certain time:
"follower-feed": {
// ".read": "auth !== null",
".write": "auth !== null",
"$userID": {
"$postID": {
".read": "auth !== null && data.child('time').val() >= ((now / 1000) - 86400)",}}},
And this is the database schema I am using to store posts:
-follower-feed: {
user_uid_1: {
post_uid_1: {
"time": 1515435031.16646
post_uid_2: {
"time": 1515435091.22323
I would like to note that I am already accounting for the fact that 'now' is in milliseconds and dividing it by 1,000 should set my two numbers to the same time value of seconds. I have stifled all about the firebase documentation but nothing is helping me solve this. When I run the simulator test to determine if the requested read will pass, it says that it will pass. However, in my app no data is being read.
This is the code that attempts to read the data from firebase:
var followingPosts = [Post]()
func loadUserFeed(_ update: #escaping () -> Void) {
userFeedHandle = CURRENT_USER_FEED_REF.observe(DataEventType.value, with: {(snapshot) in
self.followingPosts.removeAll()
for child in snapshot.children.allObjects as! [DataSnapshot] {
let post = Post(postID: child.key, postData: child.value as! Dictionary<String, Any>)
self.followingPosts.append(post)
self.followingPosts.sort(by: { Double(truncating: $0.time) > Double(truncating: $1.time)})
update()
}
if snapshot.childrenCount == 0 {
update()
}
})
}
It appears that CURRENT_USER_FEED_REF is the location containing a given user's posts, i.e. follower-feed/$userID, and that you were expecting that the security rule for post age would act a filter, allowing the user's recent posts to be returned by the query and old posts to be excluded. But security rules are not filters. For any location, you'll either be able to read all of the data (including its children), or none of it. You have no rule allowing read at follower-feed/$userID, so a query at that location will fail.
See this answer from a Firebase team-member for an idea on how to implement what you want, or search for "firebase rules are not filters" to see other related questions and answers.

Swift Firebase query returns null when filtering uid

I tried to retrieve all the entries that belong to a user by comparing their uid with the uid attribute of the entry.
ref = FIRDatabase.database().reference()
self.ref?.child("Entry").queryOrdered(byChild: "uid").queryEqual(toValue: uid).observeSingleEvent(of: .value, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
print(snapshot)
Which returns
Snap (Entry) <null>
Currently I have this data structure:
Entry
KgZkrnjFYXG9zKzfgh5
date: "Mar 31, 2017"
text: "test 123"
uid: "vKSJm500zCQmy2TTFUgv8FepknF2"
wordcount: "6"
Users
vKSJm500zCQmy2TTFUgv8FepknF2
Email: "test7#test.com"
Provider: "email"
Following these rules :
"Users": {
"$uid": {
".write": "!data.exists() || auth.uid === $uid",
".read": "auth !== null"
}
},
"Entry":{
".read" : "auth != null",
".write" : "auth != null",
".indexOn" : "uid"
}
Im a little confused because when I try to print out the query by itself, I get
let query = self.ref?.child("Entry").queryOrdered(byChild: "uid").queryEqual(toValue: uid)
print("/n")
print(query)
Optional((/Entry {
ep = "Optional(\"vKSJm500zCQmy2TTFUgv8FepknF2\")";
i = uid;
sp = "Optional(\"vKSJm500zCQmy2TTFUgv8FepknF2\")";
}))
I'm not sure what that means. Should I change the query to go through the autoIDs before I order it by uid?
The problem wasn't the firebase query, it was how I stored the UID.
When I query'd through my database, I was using
Optional(\"vKSJm500zCQmy2TTFUgv8FepknF2\")
instead of
vKSJm500zCQmy2TTFUgv8FepknF2
A quick change on the optional value fixed it.

Firebase Swift - Slow response - Rules index [g]

My app is sending and retrieving large amount of data from Firebase every second, performing multiple functions.
I'm trying to understand the Firebase rules. Currently I have the default rules set up.
{
"rules": {
".read": "auth != null",
".write": "auth != null",
".indexOn": ["g"]
}
}
However, in my debugger it is saying...
[FirebaseDatabase] Using an unspecified index. Consider adding ".indexOn": "g" at /gameUserCoordinates to your security rules for better performance
Should I be creating something extra in my FireBase rules for gameUserCoordinates?
It is set up FirebaseRoot -> gameUserCoordinates -> UID -> GeoFireCoordinates
My user has to sign in and be authenticated to use the app.
Thanks, any help would be much appreciated.
UPDATE
var dbRef: FIRDatabaseReference {return FIRDatabase.database().reference()}
var gameUserCoordinatesRef: FIRDatabaseReference {return dbRef.child("gameUserCoordinates")}
func geoFireUploadUserCoordinates(latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
let userKey = FIRAuth.auth()?.currentUser?.uid
let geoFireUser = GeoFire(firebaseRef: gameUserCoordinatesRef)
geoFireUser?.setLocation(CLLocation(latitude: latitude, longitude: longitude), forKey: userKey)

Swift, Firebase. Check if a users, childs value exists

In my game you, first have to login, then you have to choose a team name, which gets stored in my firebase database, under the players UID, and when the player has entered his team name, I want to check if it is already taken, or the player is good to go.
let rootRef = FIRDatabase.database().reference()
rootRef.queryOrderedByChild("teamName").queryEqualToValue("Bob fc").observeEventType(.Value, withBlock: { snapshot in
if (snapshot.value is NSNull) {
print("Name is not in use")
} else {
print("Name is in use")
}
})
My data tree:
{
"users" : {
"pbXvXYOKmJQqwSQZ9IlBykG7x1P2" : {
"teamName" : "Bob fc"
}
}
}
My database rules:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
The problem is that it doesn't print anything, what am I doing wrong here?
You are querying your root ref. You should query the /users node instead
let rootRef = FIRDatabase.database().reference()
let usersRef = rootRef.childByAppendingPath("users")
usersRef.queryOrderedBy....
You can shorten that up but I used the verbose model for clarity.
As a side note, with Firebase 3.x, the default is to only allow authenticated users to read and write. This is accomplished through Rules in the Realtime Database section.
If you want to test your code without authenticating, change your Rules to this
{
"rules": {
".read": true,
".write": true
}
}
PLEASE NOTE: This opens up your data to ANYONE that wants to read it, but if you are just learning or testing an app it makes it a bit more convenient.