Firebase Security Permissions - android

I'm having trouble with a user who is trying to access someone else's email. I'm getting E/ContactsFragment.java: Finding email failed. DatabaseError: Permission denied. It's also worth noting that the email gets checked before going into the database and also the email that I inputted is the same as the one in the database.
Basically, what I'm trying to do is for user1 to find the user2's email. From there, I will do something with the email.
{
"rules": {
"users":{
".indexOn": ["email"],
"$uid":{
".read": "auth !== null",
"email":{
".write": "auth !== null && $uid === auth.uid",
".validate": "newData.isString()"
}
}
}
}
}
Here's my data structure:
Here's my code that is trying to access the email:
final String TAG = "ContactsFragment.java";
FirebaseDatabase.getInstance().getReference().child("users").orderByChild("email").equalTo(email).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// some code
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG,"Finding email failed. " + databaseError);
}
});
How can I fix it? Am I missing something important?

Currently, your rules are only granting read access to children under the /users node, but not the /users node itself, which will deny them from querying the list.
If you move your .read rule outside of the $uid child, it will allow users to query the the /users list node:
{
"rules": {
"users":{
".indexOn": ["email"],
".read": "auth !== null",
"$uid":{
"email":{
".write": "auth !== null && $uid === auth.uid",
".validate": "newData.isString()"
}
}
}
}
}
Queries are attached to the list node rather than child nodes under the list, so the .read permission needs to be granted on the list itself. For example, your query is being attached to the /users node:
FirebaseDatabase.getInstance().getReference().child("users").orderByChild("email").equalTo(email)[...]
This is because rules are applied in an atomic manner, and therefore a .read rule at a deep path will not allow access to a shallower path.

Related

Firebase permission denied when attempting to load only records tagged as "public"

My rules look like this:
{
"rules": {
"userShaders": {
"$uid": {
"shaders": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid || data.child('public').val() == true"
}
}
}
}
}
My data looks like this:
userShaders
8v4N3yLlXAU1cpRiaDj5kkDQaKn1
shaders
myShaderName
attr_1: ""
attr_2: ""
public: false
I am not logged in as this user and am attempting to query their data. I expect to succesfully get back 0 records because they have none marked as public.
However, I'm getting a permission denied error.
My query looks like this:
val shaderRef = db
.getReference("userShaders/${uid}/shaders")
.orderByChild("public")
.equalTo(true);
shaderRef.get().addOnSuccessListener {
// do something with the data
}
To summarize, I am trying to query for only public data, while simultaneously making a rule that non-public data is impossible to fetch by other users. I want to write a query such that I may receive 0 or many records back, but never a permission denied error.
The most important thing to realize is that Firebase security rules don't filer data. Instead they merely check that you're only requesting data you are authorized for.
Your code already queries only for the public data, so you need to validate that query in your rules with something like:
"shaders": {
".read": "query.orderByChild === 'public' &&
query.equalTo === true"
}
Even with that you will still need separate read operations for the shared that this user created and all public shaders, as there's no way to express that OR condition in a single query.
Well, I ended up just separating my data into separate public and private sections in Firebase like this:
"userShaders": {
"$uid": {
"shaders": {
"public": {
".write": "auth.uid === $uid",
".read": "true"
},
"private": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
},
}
}
}
It's more work, because when I update the isPrivate boolean I have to make sure to delete it from the other section, but it does get the job done.
Open to other answers if there's another way to do this.

how can I read and write as an admin even if the rules set to false in Firebase Realtime Database

As an admin I am trying to get the users data so that I could 'read' and 'write' even through the rules of the firebase 'read','write' set to be false.
users
|
"uid"
|-firstname: "xyz"
|-lastname: "xyz"
|-email: "xyz#gmail.com"
Here, is my rules
"users": {
"$uid": {
".read": "auth != null && $uid === auth.uid",
".write": "auth != null && $uid === auth.uid"
}
}
when I am retrieving users data from firebase using FirebaseRecyclerOptions, users data display only when I set rules read and write to true but I want even if the rules set to be false through admin I could read and write. Is this possible
FirebaseRecyclerOptions<Model> options =
new FirebaseRecyclerOptions.Builder<Model>()
.setQuery(FirebaseDatabase.getInstance().getReference("Users"), Model.class)
.build();
Add a boolean to your user object and call it admin, set it false for all the users except you.
Now copy that to your firebasee rules:
"users": {
".read":
"root.child('users').child($uid).child('Admin').val()",
".write":"
"root.child('users').child($uid).child('Admin').val()"
,"$uid": {
".read": "auth != null && $uid === auth.uid",
".write": "auth != null && $uid === auth.uid"
}
}
}
}
Just like the isActivated boolean I have in my Users you can create a boolean called isAdmin.Add it to your admin class , give it set and get methods in your class.When you upload it to firebase you can read and write this data.
Then let your firebase rules to do the job.
It depends on what "as an admin" means to you. If the application administrator is only you for example, you could simply hard-code your own UID in the security rules:
"users": {
".read": "auth.uid === 'yourOwnUid'"
"$uid": {
".read": "auth != null && ($uid === auth.uid",
".write": "auth != null && $uid === auth.uid"
}
}
This grants the one user access to all of /users, while everyone else can only read their own child node under that path.
Hard-coding the UID like this works great during development, as you're likely the only administrator. Later on when closer to release, you'll want to store a list of administrator UIDs in the database:
"Admins": {
"yourOwnUid": true,
"otherUid": true
}
You can then check in your rules whether the current user's UID exists in this path with:
"users": {
".read": "root.child('Admins').child(auth.uid).exists()"
...
}

Write and Read in Firebase Android

I am using Firebase Realtime database with the below rules
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
My code to write in database is
mDatabase = FirebaseDatabase.getInstance().getReference("OWNER_DATA");
String id = mDatabase.push().getKey();
OwnerSignup ownerSignup = new OwnerSignup(id, name, email, phoneNumber);
mDatabase.child("MY_DATA").setValue(ownerSignup);
If I change the rules to
read:"false"
write:"false"
I can push the data but with the above rules I cannot insert values in the data.
Please help me to resolve this issue.
and how can I call this with API in Postman where my API is
"https://rent-c-246c0.firebaseio.com/"
I tried to do this in postman with the below given API
https://rent-c-246c0.firebaseio.com/OWNER_DATA.json
here I want to pass my auth.uid token
If you use the following rules:
read:"false" and write:"false"
It means you cannot retrieve nor add data to the database.
If you use:
{
"rules": {
"some_path": {
"$uid": {
// Allow only authenticated content owners access to their data
".read": "request.auth.uid == uid"
".write": "request.auth.uid == uid"
}
}
}
}
Then these rules restrict access to the authenticated owner of the content only. The data is only readable and writable by one user, and the data path contains the user's ID.
https://firebase.google.com/docs/rules/basics#content-owner_only_access

Firebase returns permission denied on random setValue() child data

I'm testing Firebase Rules to add more security to the data entry in the Firebase database. I would like to ensure that:
Only Firebase Authenticated "users" can read/write their own account information into "accounts"
only Firebase Authenticated "users" can read/write their own character data into "messages".
For this purpose I have written the following Rules:
{
"rules": {
"account": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid"
}
},
"messages": {
"$message_id": {
".read": "auth !== null",
".write": "auth.uid === newData.child('_uid').val() &&
root.child('account').hasChild(auth.uid) === true"
},
}
}
}
The above rules look to be correct, however I get "failed: DatabaseError: Permission denied" on (3) of the "messages" child nodes that I am pushing in for a logged-in user account.
Here is a code snippet of my push to firebase method:
public void saveToFirebase(){
String echo = "";
String _uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference mCurrentRecord = mFirebaseGame.push();
Log.d(LOG, "User is " + _uid + "\nNew record ID: " + mCurrentRecord.getKey());
gameCharacter.map.put("_uid", _uid);
try{
for(String keys : gameCharacter.map.keySet()){
echo += keys + ":" + gameCharacter.map.get(keys) + "\n";
mCurrentRecord.child(keys).setValue(gameCharacter.map.get(keys));
}
}
catch(Exception e){
error = e.toString();
}
}
And here is the permission-denied error that I am getting:
I cannot follow why I would get permission denied errors only for the child nodes "txtStr, txtCharName and txtEner", while the rest have been successfully pushed to the database.
I hope you can point out if I miss anything in my Firebase Rules structure or anything. Thank you.
The correct answer to this question is as #Frank van Puffelen pointed out, by inserting ALL child node values at once by calling mCurrentRecord.setValue(gameCharacter.map), and not to iterate putting data to setValue() separately.
This is because of the "messages" Rule, which checks for a group of bulk-inserted child nodes in newData.
"messages": {
"$message_id": {
".read": "auth !== null",
".write": "auth.uid === newData.child('_uid').val() &&
root.child('account').hasChild(auth.uid) === true"
},
}

W/SyncTree: Listen at /Products failed: DatabaseError: Permission denied?

I am developing a rudimentary delivery app using android studio and I am using Firebase as the database. I need to save the user info and also products that all the users can select from.
So basically each user has access to only their details but each user can access details from all the Products.
Here are my rules for the database:
{
"rules": {
"users": {
"$uid": {
".read": "auth != null",
".write": "auth != null"
}
},"Products": {
"$uid": {
".read": true,
".write": true
}
}
}
}
And the database structure:
pat-2017-42536
Products
1
Description: "Product description"
Name: "Name of product"
Price: "Price of product"
2
Description: "Product description"
Name: "Name of product"
Price: "Price of product"
etc...
I tried changing the permissions to true as some previous questions suggested but it still keeps giving me this error:
W/SyncTree: Listen at /Products failed: DatabaseError: Permission denied
This is in my onCreate in Andoid Studio:
databaseProducts = FirebaseDatabase.getInstance().getReference("Products");
databaseProducts.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
showData(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Here is the method that is supposed to fetch the product details:
private void showData(DataSnapshot dataSnapshot) {
for(DataSnapshot productSnapShot: dataSnapshot.getChildren()){
Product product = productSnapShot.getValue(Product.class);
product.setName(productSnapShot.child("item").getValue(Product.class).getName());
product.setDescription(productSnapShot.child("item").getValue(Product.class).getDescription());
product.setPrice(productSnapShot.child("item").getValue(Product.class).getPrice());
ArrayList<String> array = new ArrayList<>();
array.add(product.getName());
array.add(product.getDescription());
array.add("R" + product.getPrice() + ".00");
Log.d(TAG,"showData: name " + product.getName());
Log.d(TAG,"showData: description " + product.getDescription());
Log.d(TAG,"showData: price " + product.getPrice());
ArrayAdapter adapter = new ArrayAdapter(CustomerActivity.this,android.R.layout.simple_expandable_list_item_1,array);
listViewProducts.setAdapter(adapter);
}
}
It is probably a simple error but I can't seem to find it. Any help?
The problem is that you try to read at /Products while you allow read at /Products/Something. Enable read at Products node.
Your rules should be something like
{
"rules": {
"users": {
"$uid": {
".read": "auth != null",
".write": "auth != null"
}
},
"Products": {
".read": true,
".write": true
}
}
}
I don't think the above answer Birendra provided will do what you want it to. I believe that that will allow any authenticated user to read and write in any other specific users id. This is the same as the default rules, but just specific to the users id. He is right about the fact that you have it set up so that it is looking for /Products/$uid and so if you want anyone at all (whether authenticated or not) to both read and write to your products list then that would be the rules--but I can't imagine that you want your products list to be modifiable by everyone. From what I understand you want users to be able to read and write their own account info, but everybody can access the products. I'm not sure if you only want authenticated users to be able to view the products or not, so I'll include the rules for both. Remember, your read and write rules do NOT have to be the same for a particular node. So if you wanted to have every authenticated user be able to see the information of other users but only that particular user can write to their own profile, and if you want your "products" to be viewable by the general public (authentication not required), but you obviously don't want the public messing with your products you could have rules like this:
{
"rules": {
"users": {
".read": "auth != null", //all authenticated users can read the information from the "users" node.
"$uid": {
".write": "$uid === auth.uid" //only a user with an authenticated uid that matches the uid of the child node will be able to write to their node--no need for a read rules because it is already covered in the parent node
}
},
"Products": {
".read": true,
".write": false //you don't want just anybody to write to this node--probably even better since this wouldn't allow anyone to modify the products is to set it up with an admin access where a specific uid is the only one that has access--such as ".write": "auth.uid === 'dJrGShfgfd2'"
}
}
}
If you want only authenticated users to be able read the products, but not modify the products, then you would want:
{
"rules": {
"users": {
".read": "auth != null",
"$uid": {
".write": "$uid === auth.uid"
}
},
"Products": {
".read": "auth != null",
".write": false
}
}
}
Finally, if authenticated users can add or modify products, then you would want:
{
"rules": {
"users": {
".read": "auth != null",
"$uid": {
".write": "$uid === auth.uid"
}
},
"Products": {
".read": "auth != null",
".write": "auth != null"
}
}
}
Make the rules to be as shown below
Make sure that you have chosen REALTIMEDATABASE.
{
security rules. */
"rules": {
".read": true,
".write": true
}
}
There will be two DATA bases
1) cloud firestore
2) Real Time data Base
Choose the real time database and change the rules as the above mentioned one.
The above rule makes it visible to all the people.

Categories

Resources