I'm working on a contact-app for android with firebase and I want to read values (here: the users' profile) from a user in my database that matches a specified email.
I tried to design the query like shown in this answer: https://stackoverflow.com/a/39025129/8739722 but the problem is that I don't know how to apply this to my database-structure since I have another child (profile) in my user-structure.
My Nodes look like this:
My Rules look like this:
{
"rules": {
"users": {
"$uid": {
"profile": {
".read": "auth.uid != null",
".write": "auth.uid == $uid",
},
//...
},
}
I tried to query like this:
String email = "email_address_1";
Query q_1 = ref.child("users").child("profile").orderByChild("email").equalTo(email);
...resulting in a 'permission denied' error.
I also considered following queries but they have invalid syntax or try to perform multiple orderByChild-calls which is not allowed:
String email = "email_address_1";
Query q_2 = ref.child("users").orderByChild("profile").child("email").equalTo(email);
Query q_3 = ref.child("users").orderByChild("profile").orderByChild("email").equalTo(email);
How can I get the profile-information of the user with the matching email?
Firebase Realtime Database queries filter and order on a single property that is under a fixed path/name under each child node of the location where you run the query. In your case the property is under profile/email, so your query becomes:
ref.child("users").orderByChild("profile/email").equalTo(email);
But note that your rules will reject this query, since they request to read data from /users and you don't grant anyone permission to read data from there. Firebase security rules are enforced when you attach a listener, and can't be used to filter data. To learn a lot more about this, read the documentation section called rules are not filters and the many questions mentioning that same phrase.
To quickly test the above query, you can temporarily grant every access to /users. Just be sure to change it back before sharing your app with any users.
To properly secure the data you'll need to verify in your security rules that the user is only trying to read their own data. You can do this with query based security rules. In your case they'd look something like this:
"users": {
".read": "auth.email != null &&
query.orderByChild == 'profile/email' &&
query.equalTo == auth.token.email"
}
Related
Am using the firebase database as my storage. While signing up am storing the username for validating the uniqueness of the username. I declared firebase security rules as follow,
{
"rules": {
".read": true,
".write": true
}
}
And my database structure is as follow,
Users
o**kcarw***27p**enV**UcB3***
Email: "testing#mail.com"
UserName: "GokulNew"
But if I declare as above am getting a firebase warning as "Your project's Realtime Database "Project" has insecure rules ".
So later I changed the security rules as follow,
{
"rules": {
".read": "auth !=null",
".write": "auth !=null"
}
}
If I declare as above the security warning is not showing but it is not allowing me to signup due to the security rules. So again I changed the rule as follow,
{
"rules": {
".read": "auth !=null",
".write": "auth !=null",
"Users":{
".read": true,
".write": true
}
}
}
After this, it is allowed me to signup but a security warning is showing. How can I avoid the security warning and at the same time I need to validate the username while signup.
And below is the Java code for signup
Query usernameQuery = FirebaseDatabase.getInstance().getReference().child("Users").
orderByChild("UserName").equalTo(upperCaseChar(username));
usernameQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (snapshot.getChildrenCount() > 0) {
usernameLayout.setError("Username is already taken");
vibrator.vibrate(VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE));
dialog.dismiss();
Toast.makeText(RegisterActivity.this, "Username is already taken, Choose new one", Toast.LENGTH_SHORT).show();
} else {
//sign-up and storing the data to firebase
}
}
}
Hope my requirement is clear and thanks in advance,
Your rules should follow the principle of least privilege, meaning they allow exactly what your code needs and nothing more. Since the code you shared performs a query on /Users, you want to secure for that query, which would look something like this:
...
"Users": {
".read": "query.orderByChild == 'UserName' &&
query.equalTo.length > 0"
}
...
This only allows (if it works, because I didn't test the length check) reads on /Users that query on UserNames and specify an equalTo value, so that matches your code.
I'd recommend considering another data structure for this though, as your current code may not work when multiple users try to claim the same name around the same time. To make that work, you should introduce a top-level node where you use the username as the key and then use a transaction to write there, and in security rules ensure a value can only be written if non exists yet.
For more on this, also see these previous questions about unique usernames.
I'm new to firebase and I want to do some rules and I don't know how to do it well. If someone can help me and explain to me how they work. That would be nice! So let's start!
This is my realtime database: Image of Database
And what I want to do is that only registered users in the app can read messages and user information. Also only that you cannot delete/edit a message if you aren't the user that sent the message. The same for user information if you aren't the user you can't modify anything only read. Sorry for my English.
How can I do that? With Firebase Rules.
My actual rules are:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
I've tried with internet examples and StackOverflow examples but I couldn't have it done. So I left it like this.
So,
First: In rules you have three standard parameters: "read","write" and "validate". You can build you rules to suit you database schema. Let`s do it:
{
"rules":{
"Messages":{
// we will ad rules here leter
},
"Users":{
// we will ad rules here leter
},
".write":false,
".read":false
}
}
Now we can set diffrent rules for "Message" node, and different for "Users" node. Last parameters are for root, we dont want user to set new node or read them, we want only to allow them read/write in upper two nodes.
When user will be trying to read from Message nodes, rules will check "Root/Message" and look for rules there, rules in "Users" block wont affect this operation.
Rules working cascade, this mean that if user find ruleswhich allow him to write, write operactions will be accepted by database, even if lower will be rules with forbit this.
In firebase rules you can use plenty of functions. First let`s protect "Users" node.
to allow write only for authenticate user we will add something like this:
"Users":{
$uid:{
".write": auith.uid == $uid,
".read": auth != null,
},
".write": auth != null
}
With $ sign we can create variable, when users try to write/read in Root/Users/1234, $uid will take value 1234. With "auith.uid == $uid" we only allow user to write in their own nodes
. We should also set write rules lower (in "Users" node) to allow users to add new child to this node.
For "Message" rules we will use hasChild() method, something like this:
"Message":{
$messageUID:{
".write": data.child('uId').val() == auth.uid,
".validate": newData.hasChild('message') && newData.hasChild('name')..
},
".write":root.child("Users").haschild(auth.uid),
".read":root.child("Users").haschild(auth.uid)
}
Here we`re setting that only user whose uid is in uId property can write in $messageUID node. ".validate" rules are checking if data after operations will having message child and name child (you can add more child ).
Rule ".write":root.child("Users").haschild(auth.uid) will only allow to write for user whose id exist in Users node.
All together we have:
{
"rules":{
"Messages":{
"$messageUID":{
".write": "data.child('uId').val() == auth.uid",
".validate": "newData.hasChild('message') &&
newData.hasChild('name')"
},
".write": "root.child('Users').hasChild(auth.uid)",
".read":"root.child('Users').hasChild(auth.uid)",
},
"Users":{
"$uid":{
".write": "auth.uid == $uid",
".read": "auth != null",
},
".write": "auth != null"
},
".write":false,
".read":false
}
}
I wasn't tested this rules so maybe you need to change something, but now you should better understand how to use rules and what you can do with them. Ask if something will be wrong
My firebase database has a child which is name is Messages and it has children user_id and also it has children which are title and timestamp such as:
my firebase database
Messages
user_uid_1
Title
timestamp
user_uid_2
user_uid_3
...
I want to add a addChildEventListener for Messages to determine if there are any change in Messages. this is very easy with this code:
myRef.child("Messages").addChildEventListener(new ChildEventListener()...
how about firebase rules. if I made it public it works. but if I do like that child listener NOT working what should I add to my rules
{
"rules": {
"Messages":{
"$uid":{
".read": "data.child('timestamp').val() > (now - 18000000)",
".write": "auth != null"
}
},
"$other":{
".read": true,
".write": "auth != null"
}
}
}
I want to read only messages from the last 30 minutes can be read so that I've added ".read": "data.child('timestamp').val() > (now - 18000000)",.
I think I should add something between "Messages" and "$uid" to addchild listener correctly.
thnks!
Firebase security rules are enforced when you attach the listener. If you're allowing reads on /Messages/$uid, then a listener on /Messages will be rejected since you have no read permission on that level.
This is known as rules are not filters in the Firebase documentation, and means that you cannot use security rules to filter data. It has been covered quite a bit here on Stack Overflow already, so I suggest you check out some of the previous questions. The oldest answer I can find is this one.
Recently found out the database rules can be set to validate whether the database already have the same data but currently it just accepted same data to insert but how to validate and prevent same input of username and email?
{
"rules": {
".read": true,
".write": true,
"username":{
".validate": "!root.child('username').child(newData.val()).exists()"
},
"email":{
".validate": "!root.child('email').child(newData.val()).exists()"
}
}
}
The root child is created by email authentication uid and the rest will be under the same nodes.
How to prevent user enter same username and email?
Your rules validate if a single property /username exists and has the same value as the new data you're writing. Your use-case seems different: you want to ensure a unique user name across all users.
You cannot ensure a unique value across many multiple nodes in Firebase's security rules. You instead will need to store the user names as keys in a separate collection, i.e.
usernames
"rexyou0831": "ik3sf...."
The above data structure indicates that user ik3sf... claimed name rexyou0831. With such a structure in place, user names are guaranteed to be unique since keys must by definition be unique in a collection. You can ensure that users can only write new names or delete their own name with:
{
"rules": {
"usernames": {
"$name": {
".write": "!data.exists() || newData.val() === data.val()"
}
}
}
}
To enforce uniqueness of the email addresses too, you will need to create a similar collection for those.
For more explanation read one of the previous questions covering this same topic:
Firebase android : make username unique
Enforcing unique usernames with Firebase simplelogin
Firebase Database Rules for Unique Usernames
How do you prevent duplicate user properties in Firebase?
I've been stuck on this problem for many hours now, so any help is appreciated. Trying to make an Android app with Firebase for user authentication (simple-login email and password) along with unique usernames. I have my sign-up screen fields laid out on a single screen e.g.:
"Enter Username"
"Enter Email Address"
"Enter Password"
I'm very confused as how how to query the database to check if the username exists or not without reading a database snapshot and without attempting to write the username to the database (because this is happening while the user is in state auth == null, so while on the sign-up page before the user has created his account I want to inform the user whether his username is taken or not).
I mean, this feels like it should be very simple, just a simple query to Firebase with a string and just getting Firebase to return True or False, but after hours of googling I could find nothing.
The reason I don't want to use a snapshot to do this is because I do not want to expose all my user's names and their UIDs to the public by setting "read" to true (I followed this guide so my security rules are set up just like this, along with my database structure Firebase android : make username unique).
Here are my rules, and they work currently (I don't like the fact that the read is set to True which is why I'm asking the question though):
{
"rules": {
"usernames": {
".read": true,
"$username": {
".write": "auth !== null && !data.exists()"
}
},
"users": {
"$uid": {
".write": "auth !== null && auth.uid === $uid && !data.exists()",
".read": "auth !== null && auth.provider === 'password' && auth.uid === $uid",
"username": {
".validate": "(!root.child('users').child(newData.val()).exists() || root.child('usernames').child(newData.val()).val() == $uid)"
}
}
}
}
}
And this is my data:
{
"usernames" : {
"abcd" : "some-user-uid"
},
"users" : {
"\"some-user-uid\"" : {
"username" : "abcd"
}
}
}
Thanks!
There is unfortunately, no way to test whether the data exists without actually downloading it via the SDK. Data structures are going to be supreme here (recommended reading: NoSQL Data Structures and you shouldn't be afraid to denormalize a bit of data when optimization and scale are critical.
Generally speaking, you should keep your data well structured so payloads are small and fetch it. If you're fetching something that can't wait for the bytes to be fetched (e.g. games, strange one-off admin ops on very large data sets, et al) then here are a few reasonable approaches to simulate this:
Fetching a list of keys via the REST API
Using the attribute shallow=true in a call to the REST API will prevent loading of a large data set and return only the keys at that path. Note that if you store a million records, they still have to be loaded into memory on the server (slow) and you still have to fetch a million strings (expensive).
So one way to check the existence of data at a path, without actually downloading the data, would be to make a call to the path, such as https://<YOUR-FIREBASE-APP>.firebaseio.com/foo.json?shallow=true, and check whether any keys are returned.
Creating a denormalized index you can query instead
If you really need to squeeze some extra performance and speed out of your Firebase Database (hint: you don't need this unless you're running millions of queries per minute and probably only for gaming logic and similar), you can dual-write your records (i.e. denormalize) as follows:
/foo/data/$id/... data goes here...
/foo/index/$id/true (just a boolean value)
To dual write, you would use the update command, and a write similar to the following (Android SDK sample):
public void addRecord(Map<String, Object> data) {
DatabaseReference db = FirebaseDatabase.getInstance().getReference();
// create a new record id (a key)
String key = db.child("foo").push().getKey();
// construct the update map
Map<String, Object> dualUpdates = new HashMap<>();
dualUpdates.put("/data/" + key, /* data here */);
dualUpdates.put("/index/" + key, true);
// save the new record and the index at the same time
db.child("foo").updateChildren(dualUpdates);
}
Now to determine if a record exists, without actually downloading the data, I can simply query against /foo/index/$id and try DataSnapshot.exists(), at the cost of downloading a single boolean.