-
Notifications
You must be signed in to change notification settings - Fork 0
Security rules
To protect our data from unallowed access or even modification we need to write solid security rules. Now before we dive into it make sure to understand the basic concept of them.
So I would say we are good to go. To begin let's take a look at our data structure.
{
"events" : {
"-KvM7BE84diJN5ixyoOG" : {
"location" : {
"lat" : 37.743172,
"lng" : -122.441297
},
"uid" : "id",
"maxPrecision" : 7,
"minPrecision" : 5,
"title" : "Travelers Meetup",
"uid" :
}
},
"events-geo" : {
"9q8yt" : {
"-KvM7BE84diJN5ixyoOG" : {
"lat" : 37.743172,
"lng" : -122.441297
}
},
"9q8yty" : {
"-KvM7BE84diJN5ixyoOG" : {
"lat" : 37.743172,
"lng" : -122.441297
}
},
"9q8ytyb" : {
"-KvM7BE84diJN5ixyoOG" : {
"lat" : 37.743172,
"lng" : -122.441297
}
}
}
}
As you can see there are two primary nodes. One is the /events
node and the other is the /events-geo
node. If this structure looks unfamiliar to you, check out the advanced usage guide. (coming soon)
So the goal is it to make everything readable to everyone, but each event just writable by the user it belongs to. Sounds more complicated than it actually is.
As a first step, we are going to protect the nodes. Everyone should be able to read the events, but just the owner should be able to update them. New ones can be created by authenticated users. This can be done like this:
{
"rules" : {
"events" : {
"$event_id" : {
".read" : true,
".write" : "auth != null && (data.child('uid').val() === auth.uid || !data.exists())"
}
},
"events-geo" : {
"$geohash" : {
".read" : true,
".write" : "auth != null && !data.exists()",
"$event_id" : {
".read" : true,
".write" : "root.child('events').child($event_id).child('uid').val() === auth.uid"
}
}
}
}
}
To make use of this concept the event must contain the userid. So make sure you add it to the entry before you call the createEntry
method of gof.
As the second and already last step we need to add some property validation.
{
"rules" : {
"events" : {
"$event_id" : {
".read" : true,
".write" : "auth != null && (data.child('uid').val() === auth.uid || !data.exists())",
".validate": "newData.hasChildren(['location', 'title'])
&& newData.child('location').hasChildren(['lat', 'lng'])
&& newData.child('location').child('lat').isNumber()
&& newData.child('location').child('lat').val() >= -90
&& newData.child('location').child('lat').val() <= 90
&& newData.child('location').child('lng').isNumber()
&& newData.child('location').child('lng').val() >= -180
&& newData.child('location').child('lng').val() <= 180
&& newData.child('title').isString()"
}
},
"events-geo" : {
"$geohash" : {
".read" : true,
".write" : "auth != null && !data.exists()",
"$event_id" : {
".read" : true,
".write" : "root.child('events').child($event_id).child('uid').val() === auth.uid",
".validate": "newData.hasChildren(['lat', 'lng'])
&& newData.child('lat').isNumber()
&& newData.child('lat').val() >= -90
&& newData.child('lat').val() <= 90
&& newData.child('lng').isNumber()
&& newData.child('lng').val() >= -180
&& newData.child('lng').val() <= 180"
}
}
}
}
}
With this setup we are good to go.
Notice: Generally I would recommend you to use the write action of gof on the server. (support added soon) Since this gives you much more control over the data.