Firebase database security rule to access only user generated data with Android - 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.

Related

Firebase Android: Having issues when my database grows

During testing I had about 30 quotes in my data base and about 6 users.
i now added rest of my quote.
Total is about 2000:
Problem:
It takes so long for the app to load, it crashes and goes to the page wait for the app or exit. Then I cut down to 200 quotes, but having issue still (might show one quote before crashing)
What i am doing?
I am referencing my quotes in my database and getting a random quote every time someone open the main page (Did not know how to get one quote a day)
Snippet of JSON for the quotes:
{
"Quotes": {
"1": {
"Name": "Abbey, Edward",
"Quote": "In social institutions, the whole is always less than the sum of its parts. There will never be a state as good as its people, or a church worthy of its congregation, or a university equal to its faculty and students."
},
"2": {
"Name": "Adams, George Matthew",
"Quote": "There is no such thing as a self-made man. We are made up of thousands of others. Everyone who has ever done a kind deed for us, or spoken one word of encouragement to us, has entered into the makeup of our character and our thoughts, as well as our success."
},
"3": {
"Name": "Albani, Emma",
"Quote": "I had always loved beautiful and artistic things, though before leav"
},
"4": {
"Name": "Borman, Frank",
"Quote": "Exploration is really the essence of the human spirit."
},
........ etc
my code:
private void showQuote(){
databaseReference = FirebaseDatabase.getInstance().getReference("Quotes");
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
int count = (int) dataSnapshot.getChildrenCount();
for(DataSnapshot data: dataSnapshot.getChildren()){
int rand = new Random().nextInt(count);
for (int i = 0; i < rand; i++) {
String authorName = data.child("Name").getValue().toString();
String quoteGiven = data.child("Quote").getValue().toString();
name.setText("- " + authorName);
quote.setText(quoteGiven);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(MainActivity.this, "Loading Quote failed", Toast.LENGTH_SHORT).show();
}
});
}
My Rules:
{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
},
"Quotes": {
".read": true,
".write": false
}
}
}
What i am asking for?
I want to be able either show a quote everyday, and if that not possible to randomly show a quote. If i continue to do the randomly give a quote, what can i do to make sure its not overloading the app and crashing it. I want to have a lot of quotes rather than delete and add new ones every so often.
1."It takes so long for the app to load"
do you have databaseRef.keepSynced(true);?
this will load all database, so will takes very long
You should only read what you want
or you can FirebaseDatabase.getInstance().setPersistenceEnabled(Base.dataPersistence);
it will retain data without repeating read long time
2.You should check your database,If the format is wrong then it will die
3.I have the easiest way, you just record size at your firebase datebase Root directory,
you just read size,and produce random int to get quote,Or make a random number formula first,can easily specify quote,no need to download all.

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() { ...

Firebase structure to share data between users on 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"
}
}
}
}

Android - Firebase Database Structure

I am developing an android application where older kids can pick up younger kids and walk to school. With the application the authenticated (email and password) younger kid can choose between three adresses to get picked up. As of right now my realtime database looks like this:
Should I make a new node named "Adresses" and have a structure like this below?
Adresses
Sherman Street
username: Jannie
Because I want to retrieve the name of the street and all the users that have chosen the adress in a listview
Your suggested method is good practice: you should try to flatten your data structure as much as possible.
I'd suggest using the user's ID for the membership of each address so it's easy to identify though. This way you can obtain a list of the members of "Sherman Street" from /addresses/Sherman Street and then match the keys listed within there to the users at /users/ with ease.
{
"users": {
"oXrJPVZsnMP3VKp9palSBfdnntk1": { ... },
"xQDx3ntavhV3c02KFRPS8lxYfC62": { ... }
},
"addresses": {
"Sherman Street": {
"members": {
"xQDx3ntavhV3c02KFRPS8lxYfC62": true
}
},
"Wallaby Way": {
"members": {
"oXrJPVZsnMP3VKp9palSBfdnntk1": true
}
}
}
}
You can also add backwards linking too by adding an address field to the user which matches the address name:
{
"users": {
"oXrJPVZsnMP3VKp9palSBfdnntk1": { ... },
"xQDx3ntavhV3c02KFRPS8lxYfC62": {
"username": "Jannie",
"address": "Sherman Street"
}
},
"addresses": {
"Sherman Street": { ... }
}
}
Using both together makes it easy to identify what users have selected which addresses, irrespective of which object you are currently handling within the app.
See the Firebase documentation on database structure for further details on structuring your data like this.

Firestore structure model

Coming from SQL background and watching tutorials, I am trying to do a model in Firestore to understand how things work. I basically wants model a situation where user has multiple lists and every list has his friends ( to display names of friends). Does the below make sense?
Users
"john#xyz.com"
-Name: John Smith
"celina#xyz.com"
- Name: Celina West
"dan#xyz.com"
- Name: Dan Nelson
Lists
"john#xyz.com"
List_Titles
"List 1"
- <AutoGenId>: Celina West
- <AutoGenId>: Dan Nelson
anything with "-" is a field, anything with bracket it Document and anything without prefixes is collection.
One issue I find here, is that lets say a user updates his/her name. Then I have to go not to only Users Collection but through every subcollection List to look for that person and update name. I thought about using email ID instead of name but then that goes against the "structure the nosql db as you view it" way. Plus then everytime I have to hit the Users tables in a seperate call for every Id to query the name.
Is my assumption correct?
Thanks
Snake, there is no perfect database structure. You need to model your database so you can query very easily later, when you need to do CRUD operations. In one of my tutorials, I have explained step by step how can we structure a Firestore database which holds users, lists and products.
Please see the below database structure that can help achieve what you want.
{
"users": {
"appfirstuser#gmail.com": {
"tokenId": "eGVzwv7Y...",
"userEmail": "appfirstuser#gmail.com",
"userName": "First User"
},
"appseconduser#gmail,com": {
"tokenId": "cc8Uhriu...",
"userEmail": "appseconduser#gmail.com",
"userName": "Second User"
}
},
"shoppingLists": {
"appfirstuser#gmail.com": {
"userShoppingLists": {
"3Oe37QdcHXSohL2dnNlX": {
"createdBy": "First User",
"date": "February 3, 2018 at 2:56:31 PM UTC+2",
"shoppingListId": "3Oe37QdcHXSohL2dnNlX",
"shoppingListName": "Pharmacy"
},
" WovuleVbTZdql68gXk84": {
"createdBy": "First User",
"date": "February 3, 2018 at 2:56:20 PM UTC+2",
"shoppingListId": "WovuleVbTZdql68gXk84",
"shoppingListName": "Grocery"
}
}
}
},
"products": {
"WovuleVbTZdql68gXk84": {
"shoppingListProducts": {
"8vinaHJyjG4JqFH33YE7": {
"productId": "8vinaHJyjG4JqFH33YE7",
"productName": "Milk"
},
"JALygtedMHWQcdEoSnPM": {
"productId": "JALygtedMHWQcdEoSnPM",
"productName": "Eggs"
},
"WFkJMWZSnhJU9iwGeoOi": {
"productId": "WFkJMWZSnhJU9iwGeoOi",
"productName": "Bacon"
}
}
}
}
}
Using a database structure that looks like this, you'll be abte to create, read, update and delete records very easily.

Categories

Resources