Firebase - secure location to be targeted with query - firebase

Hello guys,
I started to work on securing my Firebase. I have a location containing a list of events :
events:
$event1: {
name: test
...
}
$event2: {
name: test2
...
}
In my app, I let the user add events using their name. For that, after the user validates his input (eventName), I'm looking if that name exists using
ref.child("events").orderByChild("name").equalTo(eventName)
And if so, I will add it to the list of user's events. My issue is with security. If I define security rules like so :
"events": {
"$eventId": {
".read": "auth != null"
}
}
Then it fails - it's completely expected since "events" location, which is targeted by the Firebase reference, has no "read" rule. The problem is, I don't want to open the "events" location for read by all connected users, since events are private and must be shared directly by their name.
So, since this name must be unique, I could use it as the event object key in place of the Firebase generated key (I currently use .push() when a new event is created to add it to the list, Firebase generates me the key), but I really like the way it is now. So I ask before changing : is there another way to reach what I'm aiming to do ?
Thanks a lot ahead.

The reason things don't work as expected here is that security rules are not filters. Rules are applied in an atomic manner. That means that a read or write operation is failed immediately if there isn't a rule at that location or at a parent location that grants access. Even if every child path is accessible, reading at the parent location will fail completely.
So, you would only be able to read from the /events/ node if you can read every child of that node. So if you are not logged in or you are not logged in with the correct user, the read will fail.
To accomplish what you want, you should add an /events/ node to each user in your /users/ node which just list the eventId for each event they are a part of. Then you don't need you event query and can instead just easily get a users events by doing a read at /users/$uid/events/. Then you can look up the event details from the /events/$eventId/ node and avoid any reads at the top-level /events/ node which is disallowed by security rules.

Related

Security Rules: Storing unique usernames in one document

On my app I am trying to make it so that users have to have a unique username.
My current method is to have a Social Collection with one document called Usernames. That document will store the users userID as the key for the field and then their username for the value.
I am struggling to write the correct security rules for this. I would like it so that:
All signed-in users can get this document
Users can only update their own data in the document, formatted as [theirUserId: theirUsername]
There can be no duplicate usernames, e.g.
userIdA: "foo"
userIdB: "foo"
At the moment the only point that I can't get to work is checking to see whether a username is already taken.
Another solution I have thought of is to reverse the fields (username: userID). But I can't figure out a way how to write the security rules for this method either.
Current Rules
// Usernames
match /Social/Usernames {
allow get: if request.auth.uid != null;
allow update: if isUserNameAvailable();
}
// Functions
function isUserNameAvailable() {
// This line works
return (request.writeFields.hasOnly([request.auth.uid]))
// This one doesn't
&& !(resource.data.values().hasAny([request.writeFields[request.auth.uid]]));
}
Firestore Data Structure
Any help is greatly appreciated, many thanks!

Firestore auth rule to test reference value in list

I'm trying to create a Firestore auth rule that checks the current user against a list of team members in a team document. The members are stored as document references so I've been trying things like this:
match /teams/{document=**} {
allow read: if path("/users/" + request.auth.uid) in resource.data.members;
}
But when I try and access the team document I get told there is an Auth failure.
Each team member has their own document in /users using their UID as a key. So a user might be /users/12345678 and the teams document might have:
/teams/team1 {
members: [/users/12345678, ....]
}
Where the members are Reference types.
So far I've not been able to figure this out as Firestore does not seem to have the concept of a document reference type in it's auth rules.
Any suggestions?
Security rules do have a concept of a reference, and it's represented as a Path type object. When a document reference is read by security rules, you have to treat it like a Path. And that Path will be fully qualified like this:
/databases/$(database)/documents/collection/documentId
Where $(database) comes from your usual top-level database wildcard match.
So, your rule might be implemented like this:
match /teams/{document=**} {
allow read: if /databases/$(database)/documents/collection/users/$(request.auth.uid) in resource.data.members;
}
Note that in security rules, you can build a path simply by starting with a /, and use $(foo) for interpolating variables as path components.

Firebase - Database : How to restrict access to sub-levels of other trees according to users

I am a bit stuck on my project.
I have a list of users which include the events allowed.
I have a list of events with users allowed.
I want to allow a user to get only the list of their events in "users".
And of course, I want the json sent to only include the the content of their events.
See the Database
See the Rules
Thank you for your help
If I understand correctly, you want to allow a user access (read/write ?) to a specific part of the your database/events.
According to the Firebase Secure Your Data docs, you can specify who gets what.
If you want the current user to be able to see the data in an event, I think this rule should work :
{
"rules": {
"events": {// allows read to /events
"$eventID": {
".read": "data.child('users').child(auth.uid).exists()",
".write": false
}
}
}
}
If you want the user to be able to edit the information as well, copy the rule from .read to .write.
As for sending the JSON of this event specifically, you need to do it with a db query to that specific node in the DB.

Removing firebase-generated id of user saved in database

When I save a user in my firebase db I do it via this path:
/allUsers/serviceUsers/${usersUID}
However, firebase adds another apparent UID to this UID.
Is there anyway to prevent this or tell firebase to not do it?
The first ID after serviceUsers is the ID I care about. The second one is generated by firebase and is making working with these objects in the app more painful. I would like that the user object be directly nested under the ID after serviceUsers. How can I achieve this?
Try to call :
firebase.database().ref("/allUsers/serviceUsers/${uid}").setValue(user)
Instead of :
firebase.database().ref("/allUsers/serviceUsers/${uid}").pus‌​h(user)
The push function generate an automatic id.
Why not just use a regular POST to
/allUsers/serviceUsers/
and return the ID? The path parameter should only be used on an update, read, or delete, if you are adhering to REST.
https://en.wikipedia.org/wiki/Representational_state_transfer#Applied_to_Web_services

Firebase JSON Security and Arrays

We'd like to use Firepad in our (mostly non-Firebase hosted) project, but we're having some troubles figuring out the best way to approach the problem.
Basically, we have many users, and each user can be a member of many groups. These "groups" each have their own Firepad which users can edit. We already have a deeply developed database structure using MySQL and don't really want to migrate our user data into Firebase right now, so we figured we'd get more creative.
We don't want users being able to edit the Firepads of groups they do not belong to. As such, as part of our authentication token, we figured we'd try sending along the user ID and the list of groups they belong to. Then, using the Firebase JSON security system, we could verify that the Firepad currently being edited is in the list of groups the user belongs to.
The problem is, the JSON system doesn't seem to accept many commands. There's no indexOf, and I can't call hasChild on the auth variable.
How can we ensure that users can only edit the Firepads of groups they belong to, without migrating all of our data to Firebase? (Or maintaining two copies of the database - one on MySQL and one on Firebase)
The trick here is to use an object instead of an array to store the groups (a tad awkward, I know. We'll try to make this easier / more intuitive). So in your auth token, you'd store something like:
{ userid: 'blah', groups: { 'group1': true, 'group2': true, ... } }
And then in your security rules you could have something like:
{
...
"$group": {
".read": "auth.groups[$group] == true",
".write": "auth.groups[$group] == true"
}
}
And then a user will have read/write access to /groups/<group> only if <group> is in their auth token.

Resources