I want to make public only username node under the user's child for register function. So this value must be able for the non-register users to register. Here is my firebase database structure. How can I do that?
Here are my rules
{
"rules": {
"Homeland": {
".indexOn": ["username","email","bakiyetl","yarismada","yarismadabb","splashmesaj","uygulama1tut","uygulama2tut","uygulama3tut","uygulama4tut","uygulama5tut","uygulama6tut","uygulama7tut","uygulama8tut","uygulama9tut","uygulama10tut"]
},
"gunluksifreler": {
".read": true, // <-- allows every person
".write": true
},
".read": "auth !== null", // <-- allows read if logged in
".write": "auth !== null" // <-- allows write if logged in
}
}
Search username in database.
Query cmyquery = refbir.child("Homeland").orderByChild("username").equalTo(usergivenname);
cmyquery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
//user name already taken
}else if(sonlandirgorev!=1){
//You can use this username
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Rule 1: To be able to read a location or run a query on a location, you must be able to read the data on that location.
Knowing that, let's look at:
Query cmyquery = refbir.child("Homeland").orderByChild("username")
This code requires that the user can read the Homeland node.
This is referred to in the documentation as rules are not filters.
Rule 2: One you can read or write data at a certain level in the JSON, that permission can't be taken away at a lower level.
Given this rule and the above: one a user can read and query Homeland they can read all data below it. There is no way to hide part of that data.
This is referred to in the documentation as read and write rules cascade.
The common solution is to split the data by who needs access to it. So if you want only the username property values to be available to everyone, create a top-level usernames node with the same keys under it, and then just the value of that user's user name. On that new usernames node you can then grant more liberal access than on the Homeland node.
Related
I have developed an app using firebase realtime database. In the app, if e-g user 1 clicks on the profile of user 2 then the data of user 2 would be changed. User 1 can only change the data of other users if he clicks on their profiles. I have written some rules to secure my database but these rules won't allow user 1 to change the data of other users if he clicks on their profile. You help will be highly appreciated. Thank You
Below is my code:
Firebase Rules
Database Structure
Java Code which will change user data if his profile is clicked
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dRef.child(modelClass.get(position).userID).child("showGigCount").setValue(modelClass.get(position).getShowGigCount()-1); // this will decrease showGigCount for clicked users
dRef.child(modelClass.get(position).getUserID()).child("clicksOnProfile").setValue(modelClass.get(position).getClicksOnProfile()+1); // this will increase clicksOnProfile for clicked users
dRef.child(modelClass.get(position).getUserID()).child("lifeTimeClicksOnProfile").setValue(modelClass.get(position).getLifeTimeClicksOnProfile()+1); // this will increase clicksOnProfile for clicked users
It sounds like you want to allow certain properties to be modified by all users, which you can do with:
...
"$user_id": {
".write": "auth != null && $user_id === auth.uid",
"showGigCount": {
".write": "auth != null"
},
"clicksOnProfile": {
".write": "auth != null"
},
"lifeTimeClicksOnProfile": {
".write": "auth != null"
},
}
...
The added rules give permission to write the lower level properties, while your original write rules on $user_id still rejects writing other properties of the user.
I want to allow access to user name so other users can search uses by user name.
I have a search feature in my app that allows users to search other users by user name
Firebase Rules
{
"rules": {
"users": {
"$uid": {
// Allow only authenticated content owners access to their data
".read": "auth.uid != null",
".write": "auth.uid != null"
},
},
"chat": {
"messages": {
".write": true,
".read": true
}
},
"app-settings": {
".write": true,
".read": true
}
}
}
Here is JSON of users
{
"app-settings" : {
"app-Available" : true,
"version" : "4"
},
"users" : {
"uid" : {
"blocked" : false,
"email" : "gamatiaihab#gmail.com",
"profilePhotoUrl" : "https://lh3.googleusercontent.com/a-/AOh14Gi6eXrdLfZTQH0B7GvoTxhqBHoVFUUTibK3QKfrfA=s96-c",
"uid" : "uid",
"userName" : "gamatiaihab"
}
}
}
In my app, I have a feature that allows the user to change their user name, with this code I'm trying to validate the user name checking if it's takin by other user or not, I normally access the single user information by child(uid) this works if firebase rules are only configured for authenticated users, but in order to validate the user name I need to not order the by child(uid) because this will return only single user which is the current user, I need to order by child("users").orderBy("userName") and this returns PERMISSION DENIED because i didn't pass child(uid)
private void validateUserName(final String chatUserName){
mEditChatUserNamePr.setVisibility(View.VISIBLE);
mDb = FirebaseDatabase.getInstance().getReference().child("users");
mDb.orderByChild("userName")
.equalTo(chatUserName)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null){
mEditChatUserNamePr.setVisibility(View.GONE);
mChatUserNameInput.setError("User name not available");
}else {
updateUserName(chatUserName);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
A user either has access to a complete node, or they don't have access to the node at all. There is no way to give them access to only part of each child node.
But even if there was, your code would have a problem: another user might claim the name between the time your query runs, and you write the new user name to the database. And while this problem may seem unlikely in your testing, in a successful app this type of problems (known as a race condition) is guaranteed to come back and bite you.
The idiomatic (and only guaranteed) way to implement uniqueness is to use the thing that must be unique as the keys in a list in the database. So in your case that'd mean:
Creating a list of usernames, with each key being a username, and each value being the UID of the user with that username.
Using a transaction to ensure no two users are updating the same node at the same time.
Using security rules to ensure that nobody can overwrite a username that was claimed by another user already.
Instead of repeating more here, let me point you to some previous questions where this was covered in more detail:
Firebase android : make username unique
How do you prevent duplicate user properties in Firebase?
unique property in Firebase
Firebase security rules to check unique value of a child #AskFirebase
I am working on a project and I followed few tutorials in order to learn and build the app. But all of them, they change the Firebase read and write rules to true which is not safe. for example they change
{
"rules": {
".read": false,
".write": false
}
}
to
{
"rules": {
".read": true,
".write": true
}
}
This gives access to anyone to read and write the server data which is not safe in any way. And hence I turned this to false and now I am unable to register the user to Firebase it is giving an error saying 'Permission Denied. So what would I have to do in order to get the permission now.
Previously I was using this code to register the user to Firebase which is not working now.
mFirebaseAuth.createUserWithEmailAndPassword(editEmail.getText().toString(), editPass.getText().toString()).addOnSuccessListener(new OnSuccessListener<AuthResult>() {
#Override
public void onSuccess(AuthResult authResult) {
//Saving User to Database
User user = new User();
user.setEmail(editEmail.getText().toString());
user.setName(editName.getText().toString());
user.setPassword(editPass.getText().toString());
user.setPhone(editPhone.getText().toString());
users.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).setValue(user).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
waitingdialog.dismiss();
Snackbar.make(rootLayout, "Registration Successful", Snackbar.LENGTH_SHORT).show();
return;
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
waitingdialog.dismiss();
Snackbar.make(rootLayout, "Registration Failed" + e.getMessage(), Snackbar.LENGTH_LONG).show();
return;
}
});
}
});
There are different rules for in the Firebase for this reason and the registration of the user to Database depends on those rules for instance there are four rules given by Firebase
as Default
The default rules require Authentication. They allow full read and write access to authenticated users of your app only. They are useful if you want data open to all users of your app but don't want it open to the world
// These rules require authentication
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
as Public
During development, you can use the public rules in place of the default rules to set your files publicly readable and writable. This can be useful for prototyping, as you can get started without setting up Authentication. This level of access means anyone can read or write to your database. You should configure more secure rules before launching your app.
// These rules give anyone, even people who are not users of your app,
// read and write access to your database
{
"rules": {
".read": true,
".write": true
}
}
as User
Here's an example of a rule that gives each authenticated user a personal node at /users/$user_id where $user_id is the ID of the user obtained through Authentication. This is a common scenario for any apps that have data private to a user.
// These rules grant access to a node matching the authenticated
// user's ID from the Firebase auth token
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
as Private
Private rules disable read and write access to your database by users. With these rules, you can only access the database through the Firebase console.
// These rules don't allow anyone read or write access to your database
{
"rules": {
".read": false,
".write": false
}
}
For registering the user to Database while read and write permissions as false will only give permission to you to edit and read the data from the Firebase Console.
I'm working on a small app to practice my JAVA/Firebase skills, and I have come into a roadblock. I do admit that I'm not very familiar with Firebase, and rules associated with the database portion. But I have tried looking at other SO posts and searching through documentation.
Problem:
Users create an account (through Firebase Authentication - E-mail/Password). Then they are able to create a "character" and provide this "character" with a name. So I need the "charName" to be unique. And obviously the authenticated ID is also unique already. So I need the app to tell users if the name is already taken or if it isn't, then to go ahead with adding it to the database.
Here are simplified snippits of my code:
btnCreate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final String charName = "MyCharactersName";
final int charID = 123;
mFirebaseDatabase.child("characters").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (!(snapshot.child(charName).exists())) {
Character newCharacter = new Character(charID, charName);
mFirebaseDatabase.child("characters").child(getNewCharID()).setValue(newCharacter); // add to database
Snackbar.make(findViewById(R.id.view_root), "Success", BaseTransientBottomBar.LENGTH_INDEFINITE).show();
} else {
Snackbar.make(findViewById(R.id.view_root), "Error creating a character.", BaseTransientBottomBar.LENGTH_INDEFINITE).show();
}
} else {
Snackbar.make(findViewById(R.id.view_root), "That character name is already taken.", BaseTransientBottomBar.LENGTH_INDEFINITE).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Snackbar.make(findViewById(R.id.view_root), "Error - Did not connect with Database", BaseTransientBottomBar.LENGTH_INDEFINITE).show();
}
});
}
});
Currently: The app creates a new character on the database, but you can add duplicate characters. (the charID is unique by the way, I hardcoded it in the snippit above... but it is timeStamp + 4 random digits).
So that obviously happens with the default database Rules. And in the examples that I did read, it looks like I might have to modify those?
My Database structure is as such:
App / characters / charID / charName
I tried to adapt some code from this SO post: How do you prevent duplicate user properties in Firebase?
and this is what I wrote, but it doesn't work and as I admitted before, I'm not familiar with rules, so I'm not sure what I did/did wrong. haha.
{
"rules" : {
"characters" : {
"$charID" : {
".validate": "root.child('charName_lookup/'+newData.val()).val() === auth.uid"
}
},
"charName_lookup" : {
"$charName" : {
".write" : "!data.exists()",
".validate": "newData.val() === auth.uid"
}
}
}
}
If you have any questions/clarifications please let me know. I will be stepping away from the computer periodically but I will check back promptly (I hope!)
Basically, the userName or the email address should be the name of your node. Regarding rules, you can use wildcards. If you create a userName George, to verify if exists you only need to put a reference on users node and use exists() method.
Please take a look at Frank van Puffelen's explanation from this video. Even if you'll need to remodel a bit your database, remember that this is the best practice when we are talking about duplicates.
I have integrated firebase auth with my android app. Lets say a user has a mail abc#abc.com. I want to add some extra information to the user like the name of the user, occupation and address. How can i connect the user auth table with my android app to do that?
Do i need to write any APIs for that?
First, create a users directory in db. Then, using user's unique id you get from authn process, store the user info under users/{userid}.
To achieve this, you need to get into the details of Firebase database. See here: https://firebase.google.com/docs/database/android/save-data
You do not need to write any custom code to do this. Firebase already has features you can use.
The first thing you'd need to do is ensure that users have access to only the data they store. To do this, go to Database/Rules and change your rules to this:
{
"rules": {
"my_app_user": {
"$uid": {
".write": "auth != null && auth.uid == $uid",
".read": "auth != null && auth.uid == $uid"
}
}
}
}
Then, to save the new details in a Firebase database, do this:
MyAppUser user = new MyAppUser();
user.setAddressTwo("address_two");
user.setPhoneOne("phone_one");
...
mDatabaseReference.child("my_app_user").child(firebaseUser.getUid()).setValue(user).
addOnCompleteListener(DetailsCaptureActivity.this,
new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
...
The name of the child my_app_user must match both in your code and the Firebase rules else it won't persist.
If everything goes as is supposed to, you should see the details in your database:
You have to create another database table say "user". On successful signin, signup for first time you have to create a new row in user table.
public static void writeNewUser(DatabaseReference databaseReference, String userId, String name, String email, int accountType) {
User user = new User(name, email, accountType);
databaseReference.child("users").child(userId).setValue(user);
}
You may refer https://github.com/firebase/quickstart-android/tree/61f8eb53020e38b1fdc5aaeddac2379b25240f3b/database