Firebase structure to share data between users on Android - android

So I'm using Firebase for my backend on an Android app I'm working on. Currently, my structure looks like this
-root
- users
- email
-email
-firstName
-lastName
-(user_email)AppName
- max
- workouts
- workoutName
- orderNumber
- some push key id
- exercise
- reps
- sets
I'm storing max and workouts under each individual user (user_email + AppName). I want to send workoutName to another user's workouts node. How would I go about this in my rules, and how would I do this programmatically? I also want the user that received the workout to only be able to read it. I was thinking about just copying the workoutName node from a user and inserting it into another user's workouts node.
My rules are currently
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
JSON
"testEmail#gmail?comWorkoutTracker" : {
"workouts" : {
"benchDay" : {
"-KyK9ZTc57vvI7QT_UjC" : {
"childCount" : "0",
"exercise" : "Bench",
"reps" : "4",
"sets" : "3",
"view" : "Exercise row",
"weight" : "2"
}
}
}
}

Related

Filter Firebase Realtime Database data based on authentication

I'm using Firebase Realtime Database as the datastore for an Android app, and right now I'm trying to implement some basic filtering based on user permissions.
Here's my database:
{
"admins" : {
`user1hash` : true
},
"clients" : {
"client1hash" : {
"owner": "user1hash",
"name" : "Client 1"
},
"client2hash" : {
"owner": "user1hash",
"name" : "Client 2"
},
"client3hash" : {
"owner": "user2hash",
"name" : "Client 3"
}
}
}
I followed the examples in the Query-based Rules section here https://firebase.google.com/docs/database/security/securing-data and defined my rules like this:
{
"rules": {
"clients": {
".indexOn": "owner",
".read": "auth.uid != null && (root.child('admins/' + auth.uid).val() == true || query.orderByChild == 'owner' && query.equalTo == auth.uid)",
".write": "auth.uid != null",
".validate": "newData.hasChildren(['owner']) && newData.child('owner').val() == auth.uid"
}
}
}
And this is my client code (Android):
FirebaseDatabase database = FirebaseDatabase.getInstance();
String authUser = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference clientsDatabase = database.getReference("clients").orderByChild("owner").equalTo(authUser).getRef();
clientsDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// handle success
}
#Override
public void onCancelled(DatabaseError databaseError) {
// handle error
}
});
So basically I just want to be able to fetch all clients, but filter the results according to the access rules of the current user. Access rules are pretty simple, an user can only see the clients where he is the owner, except for admin users who can see all clients. For example if this was run by user1 which is an admin he should see all three clients, but if this is run by user2 which is a regular user he should only see client 3.
This implementation is working for admins, but I get a permissions error for regular users.
I tried the rules simulator in Firebase console, but it doesn't provide any documentation on how to use queries. Anyway I tried adding the query as a regular URL query string like /clients?orderByChild=owner&equalTo=user2hash, but this returns an empty error on the simulator with no description of the cause. The error I'm getting on the Android side doesn't have any description either, just a generic permissions error.
The problem is in this statement:
DatabaseReference clientsDatabase =
database.getReference("clients")
.orderByChild("owner")
.equalTo(authUser)
.getRef();
Specifically that last line getRef(), which throws everything away that you've done to build the query. The above statement leaves clientsDatabase exactly the same as:
DatabaseReference clientsDatabase = database.getReference("clients");
Which explains quite well why the statement fails.
You need to keep the Query that you get back and attach your listener on that:
DatabaseReference clientsDatabase = database.getReference("clients");
Query query = clientsDatabase.orderByChild("owner").equalTo(authUser);
query.addListenerForSingleValueEvent(new ValueEventListener() { ...

problems query orderByValue() firebase database

I want to retrieve all persons (people) where city equal Paris from firebase database.
this is my query, is it correct ?
query = Databaseperson.child("persons").orderByValue().equalTo("Paris")
Data Structure:
-Persons
- KeyPerson1 : CKKJDJOCJJ
-name: Peter
-city: Paris
- KeyPerson2 : WGTHKJHHUH
-name: Oliver
-city: Paris
- KeyPerson3 : XVCDFBJJYF
-name: Anne
-city: New York
You must use roles for search. Like this;
{ "rules": { ".read": true, ".write": true, "persons": { ".indexOn": ["name","city"] } } }
after you can use like this:
query = Databaseperson.child("persons").orderByChild("name").equalTo("Paris")

How to set firebase read rule and read all siblings if one matches

I have set my firebase rules as follows:
{
"rules": {"users": {
"$uid": {
".read": "auth != null",
".write": "$uid === auth.uid"
}}
}
}
Which allows write only to the node with matching uid and read all child nodes of every uid. But I want it to be like if I query using a child node under uid, only the matching child and it's siblings can be read...
for example this is my json structure:
{
"users" : {
"AJkK4yZJsoseeefrJ7i6KIOUBDghtrhgthrtDi1" : {
"lat" : 20.5001,
"long" : 68.3755,
"number" : "9876543210",
"time" : 1499599788090
}
}
}
I want to query using the number, and set the read rule as can read lat long and time only where the number matches. How to write the rule?
update: my question now is, how to query the database using number and get other siblings if value of number matches in android? I have tried this but not working:}
friend = mDatabase.getReference("users");
friend.keepSynced(true);
Query z = LocationActivity.this.friend.orderByChild("number").equalTo("9876054321");
z.addListenerForSingleValueEvent((new ValueEventListener() {
long lastseen;
public void onDataChange(DataSnapshot dataSnapshot) {
try {
for (DataSnapshot zoneSnapshot: dataSnapshot.getChildren()) {
lastseen = (Long)zoneSnapshot.child("time").getValue();
friendLatitude = (Double) zoneSnapshot.child("lat").getValue();
friendLongitude = (Double) zoneSnapshot.child("long").getValue();
}
} catch (Exception e) {
}}
it returns value null, any help would be appreciated.
Firebase read permissions are enforced when you attach a listener. In order to query a node, you must have read permission on that node (as Bradley also explained). So in order to be able to query users, you must have read permission on /users. And since any user that has read permission to /users can also read any data under that, you cannot use security rules to filter what nodes a user has access to.
This is known as rules are not filters and is one of the common pitfalls for developers new to Firebase security model. I recommend that you read the documentation I linked already and some of the many questions/answer about the topic.
The simplest solution for your use-case seem to be to pull up the .read rule to users:
{
"rules": {
"users": {
".read": "auth != null",
"$uid": {
".write": "$uid === auth.uid"
}
}
}
}
}
In order to query every node, the user needs to have read permission for ALL nodes under the parent.
This is because security rules cannot be used to query data in Firebase.
Not exactly sure if this answers the question, but I found this post looking for the following solution with respect to Firebase Storage .. where I can control which sibling nodes are accessible:
match /uploadRoot/{userId}/{uploadCategory}/{allPaths=**} {
// allow read access for client upload/download
allow read, write:
if request.auth != null
&& request.auth.uid == userId
&& (
uploadCategory == 'userTextFiles'
|| uploadCategory == 'userImages'
);

Firebase database security rule to access only user generated data with Android

I want to create a chat support app model. I have only 1 agent and many requesting users asking queries. Each user can have multiple conversations with the agent (assume it like a whatsapp group having only 2 members, a user and that agent).
So I create a data structure like:
Conversation
-uId
-status
-conversationTitle
and for Messages,
Message
-conversationId
-message
-from //0 mean it from me to my friend and 1 for a message from my friend to me
-timestamp
Basic structure will be like:
Conversation1:{ "uId": "1", "status":0, "conversationTitle": "First conversation"},
Conversation2:{ "uId": "2", "status":0, "conversationTitle": "Second conversation"},
Conversation1:{ "uId": "1", "status":0, "conversationTitle": "Third conversation"}
and for messages:
Message1: {"conversationId": "Conversation1", "message": "Hello there 1!", "from":0, "timestamp":******** },
Message2: {"conversationId": "Conversation1", "message": "Hello there 2!", "from":0, "timestamp":******** },
Message3: {"conversationId": "Conversation2", "message": "Hello there 3!", "from":0, "timestamp":******** },
Message4: {"conversationId": "Conversation3", "message": "Hello there 4!", "from":0, "timestamp":******** },
As you can see Message 1 and 2 belong to Conversation 1 which in turn b belong to User 1 with uID 1.
I want all conversation that is happening with User 1 (uId: 1) and User 2 (uID: 2) must not be able to see conversations of 1.
I wrote security as:
"conversations":{
"$uid":{
".read": "data.child('uId').val() == auth.uid",
".write":"auth != null && newData.child('uId').val() == auth.uid"
}
}
and then in Android:
FirebaseDatabase database = FirebaseDatabase.getInstance();
Query conversationsRef = database.getReference("conversations");
FirebaseRecyclerAdapter<Conversation, ConversationHolder> binderData = new FirebaseRecyclerAdapter<Conversation, ConversationHolder>(Conversation.class, R.layout.item_butler_converastions, ConversationHolder.class, conversationsRef) {
#Override
public void populateViewHolder(ConversationHolder chatMessageViewHolder, Conversation chatMessage, int position) {
chatMessageViewHolder.setName(chatMessage.getTitle());
}
#Override
protected void onCancelled(DatabaseError error) {
super.onCancelled(error);
}
};
When I do this I get permission error. I want to retrieve all conversation belonging to User 1.
I also tried using:
Query conversationsRef = database.getReference("conversations").orderByChild("uId").equalTo(mAuth.getCurrentUser().getUid());
but didn't work. Thanks in advance.

As Firebase rules are not filters, how do we filter?

I am trying to have a set of chats that contains a list of participants, and be able to query per user, its list of chats where the user is a participant.
Because filtering is not possible in Firebase on the server side (I guess for scalability), I understood that I have to prepare all the data in a resource for each form of "filtering" I need, so I have the following structure right now:
Those are the rules I wrote to secure the data:
{
"rules": {
"users": {
"$user_id" : {
"chats" : {
".read":"auth != null && $user_id == auth.uid",
".write": "auth != null && !data.exists()"
}
}
},
"chats": {
"$chatId": {
".read":"auth != null && root.child('users').child(auth.uid).child('chats').child($chatId).exists()",
".write": "auth != null && !data.exists()"
}
}
}
}
The first rule (/users/&user_id/chats) is OK and works as intended. I am able to query by user_id and get a map of all the chat_ids the user should have access to.
The second rule fails because what I thought I had was the chat_id but what I really have is the id for the object containing an attribute having the chat_id as uid, and I can't get to a child without knowing its ID, as rules are not filters.
The only way I see I can protect a chat is to have a string for a list of participants, and then use contains() on that child of a chat to block access to it if the user is not a participant of that chat.
Is there another way? Is it the way to go?
Instead of pushing new key to set the chat's id in the "uid" key, you can directly set the chat's key as the child under /users/<uid>/chats with true as the value.
{
"users": {
"<uid>": {
"chats": {
"<chat_id>" : true,
"<chat_id2>" : true
}
}
}
}
With this structure, your firebase database rules for the read access should be true for the correct user.

Categories

Resources