Check if email exists in user list using AngularFire2 - firebase

I want to check if there exists a user that have email = someemail#gmai.com in the Firebase database.
I use this function and is download 50MB of data in my PC after 10 minute I get a response that exists. There is any way that I can get that less than 5 second I don't want to wait 10 minute.
in PHP is like the
SELECT FROM ALL USERS WERE EMAIL = 'george#gmail.com'
This is the database:
"users" : {
user: {
"email" : "george#gmail.com",
"custom_id" : 534253,
"description" : "some small description"
},
1754: {
"email" : "natassa#gmail.com", <---- i want to find if this email from 5000 users exist in database and if exist i want to return true
"custom_id" : 110571,
"description" : "some small description"
},
1755: {
"email" : "george#gmail.com",
"custom_id" : dsgfsdfds,
"description" : "some small description"
},
}
This function give me response after 10 min:
check_if_email_exist(email: string) {
this.users = this.db.list('/user', { query: {
orderByChild: 'email',
equalTo: x,
limitToLast: 10,
}} ) as FirebaseListObservable<Product[]>;
console.log(this.users);
return this.users;
}

OMG I find the problem thanx to https://stackoverflow.com/users/209103/frank-van-puffelen is say something about index and when I look in my chrome console I find warning that says something about index and when I look in the database rules I understand what is missing
I add this inside database rules
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"needs": {
".read": "true",
".write": "auth != null",
".indexOn": "name"
},
"brands": {
".read": "true",
".write": "auth != null",
".indexOn": "name"
},
"products": {
".read": "true",
".write": "auth != null",
".indexOn": "custom_id" <========== This line you want
}
}
}

Related

What is the best practice to write the rules of Firebase in a situation like this?

Initial approach/problem
The user will be able to write to his own user node. Then the user has to be able to write as many buildings he/she wants and as many depts as he/she wants (there are also rooms but I will leave that aside for now for clarity's sake). The user should be able to read (and write) his own user node, buildings and departments but not the other users' node, buildings and departments.
Basically:
User > user's Building > building's Department (TOTAL read and write permissions)
User > Another User's Stuff (NO Permissions at all)
Here is the database strucure:
{
"buildings" : {
"-L9Bc9aazn3mNiW1elJk" : {
"address" : "",
"comments" : "",
"hasDepts" : {
"-L9FwBmYEnkZQzdFJ4lU" : true
},
"name" : "J house",
"ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
}
},
"depts" : {
"-L9FwBmYEnkZQzdFJ4lU" : {
"comments" : "",
"inBuilding" : "-L9Bc9aazn3mNiW1elJk",
"name" : "Dep 1"
},
},
"users" : {
"6hwNde08Wuaa9bfReR28niSbOsF3" : {
"isAdmin" : {
"-L9Bc9aazn3mNiW1elJk" : true,
}
}
}
Initial approach
Each users node has a child node isAdmin that contains all push keys of the buildings this user has created. Using the same logic, the buildings' node contains a hasDepts node with all the push keys from the depts that the user has created in that building.
If you can help I would really appreciate. Anybody out there?
I am using vue.js to write to firebase like this:
addBuilding: function () {
let userId = firebase.auth().currentUser.uid;
let buildingKey = buildingsRef.push().key;
this.newBuilding.ownerID = userId;
buildingsRef.child(buildingKey).set(this.newBuilding);
usersRef.child(userId).child('isAdmin').child(buildingKey).set(true);
}
Closest solution so far / by #André Kool (partially using the initial approach)
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
},
},
"buildings": {
"$pushKey" : {
".read": "root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid",
".write": "!data.exists() || root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid"
}
},
"depts": {
"$pushKey": {
".read": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid",
".write": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid"
}
},
}
With #André Kool's attempt the simulator allows it to read/write to the buildings/$pushKey node. However while it shows the node on the frontend as soon as it it is created, when we refresh the browser or add a new building the node disappears from the frontend (remains on the database). Firebase it's also not allowing to write to the depts node. Any clues and possible solutions?
UPDATE: another approach.
1) making sure both the buildings and depts nodes both have the ownerId child node, like this:
{
"buildings" : {
"-L9HIbKu5fIe8rfoePgi" : {
"address" : "",
"comments" : "",
"hasDepts" : {
"-L9HIdScisDItysCnMlm" : true
},
"name" : "building 1",
"ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
}
},
"depts" : {
"-L9HIdScisDItysCnMlm" : {
"comments" : "",
"inBuilding" : "-L9HIbKu5fIe8rfoePgi",
"name" : "dep 1",
"ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
}
},
"users" : {
"6hwNde08Wuaa9bfReR28niSbOsF3" : {
"isAdmin" : {
"-L9HIbKu5fIe8rfoePgi" : true
},
"name" : "João Alves Marrucho",
"userEmail" : "joaomarrucho#hotmail.com"
}
}
}
2) Use the ownerId to authorise read and writing on all buildings and depts:
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
},
},
"buildings": {
"$id": {
".read": "data.child('ownerID').val() == auth.uid" ,
".write": "data.child('ownerID').val() == auth.uid"
}
},
"depts": {
"$id": {
".read": "data.child('ownerID').val() == auth.uid" ,
".write": "data.child('ownerID').val() == auth.uid"
}
},
}
3) New problem with this approach!
With the rules above, using the simulator, it seems to me like firebase is not allowing the user to read/write to the buildings node but it's allowing to read/write to buildings/$pushKey node. Does firebase needs the user to be able read/write to both parent (buildings) and child node (buildings/$pushKey)?. And if so how can you prevent the user from deleting (.set) the whole buildings node?
I am asking this because if we add ".read": true, ".write": true before the $wildcards it writes the intended database structure while rendering the next rules on the cascade completely useless... So that's no good, but at least it kind of hints where part of the solution may reside.
"users": {
".read": true, <<<<<<
".write": true, <<<<<<
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
},
}, // but is should also be able to write to his own buildings
"buildings": {
".read": true, <<<<<<
".write": true, <<<<<<
"$id": {
".read": "data.child('ownerID').val() == auth.uid" ,
".write": "data.child('ownerID').val() == auth.uid"
}
},
"depts": {
".read": true, <<<<<<
".write": true, <<<<<<
"$id": {
".read": "data.child('ownerID').val() == auth.uid" ,
".write": "data.child('ownerID').val() == auth.uid"
}
},
}
(Other thoughts)
I don't see why the user shouldn't be able to read/write to the buildings parent node (provided the rules prevent him from deleting the whole buildings node and only grant him full access to the buildings/$pushKeys he creates). Is that complicated? I may be getting this wrong, but doesn't firebase need to scan the buildings node before it knows which one belongs to the user?
If firebase rules can't resolve this issue like this, that means that in theory, every firebase app in which the user needs to read and write its own content to the realtime database, which is often the case, needs to store the information under a users.uid node to make it available to him/her. That seems to go against the "keep your database as flat as possible" firebase general instruction, and it also doesn't play well with the fact that any function with database references that have children in firebase require that data.snapshots nightmarish iteration.
Moreover if you can't design a shallow structure, and the user id will constantly change, how would you go about writing the database references in the Apps firebase configuration: const buildingsRef = db.ref('users/'+ userId!!!!+'/buildings'); : >How to constrain read/write rules to the users that create the nodes while keeping this structure?
There must be an good way to do this without bending over backwards.
?
Here are the rules for each part of your datastructure:
For the users node I removed the more global read and write rules because if they are false they are redundant because that is the default state. And if they are true they will override these rules because rules cascade. To allow users to only read/write their own data:
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
Next is the buildings node. Here you can read if you are the owner (ownerID === your uid) and write if there is no data or you are the owner:
"buildings" : {
"$pushKey" : {
".read": "root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid",
".write": "!data.exists() || root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid"
}
}
And last is the departments node where it gets a little tricky because a user can should only read/write departments in buildings he owns. So we have to check if the user has a building with the pushkey of the department. Here I check if the building with the value of inBuilding in the department has ownerID that is your uid (mind spinning complicated):
"depts" : {
"$pushKey" : {
".read": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid",
".write": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid"
}
}
Another option is changing your datastructure to something like this where you store buildings and departments under the user id:
{
"buildings" : {
"$uid" : {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid",
"$buildingid" : {
}
}
},
"depts" : {
"$uid" : {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid",
"$deptid" : {
}
}
}
}

What is the proper way of storing timestamps in Firebase if we don't want them to be editable?

Suppose I have a messages node in my database with this structure:
"messages": {
"$messageId": {
"text": "Hello there!",
"created_by": "$userId",
"created_at": 1501749790029
}
}
and this rule:
"messages": {
".read": "auth != null",
"$messageId": {
".write": "auth != null",
// required fields
".validate": "newData.hasChildren(['text', 'created_by', 'created_at'])"
}
}
Seems pretty standard. But my problem is, this structure and rule allows any user to alter the value of created_at to any value, right? The property created_at should be a timestamp of when the message is pushed and should not be editable.
Am I correct if I re-structure my database like this:
"messages": {
"$messageId": {
"text": "Hello there!",
"created_by": "$userId"
}
},
"created_at": {
"$messageId": 1501749790029
}
Basically, I will move created_at to a separate node so it cannot be edited by the user. I will then set up an event trigger via Cloud Functions that will auto-push the timestamp at created_at when a new message is pushed to messages.
The way to do this is to allow setting the created_at value only if there wasn't a previous value, here's the doc
"messages": {
".read": "auth != null",
"$messageId": {
".write": "auth != null",
// required fields
".validate": "newData.hasChildren(['text', 'created_by', 'created_at'])"
"created_at": {
".write": "!data.exists()"
}
}
}
Also, I think you may want to validate the owner of the message to prevent users from posting as other people adding this:
"created_by": {
".validate": "newData.val() == auth.uid"
}
After further researching, I found out I can make use of .validate and the now variable on Firebase rules to prevent invalid timestamps:
"messages": {
".read": "auth != null",
"$messageId": {
".write": "auth != null",
".validate": "newData.child('created_at').val() == now && newData.hasChildren(['text', 'created_by', 'created_at'])"
}
}
This is less complicated than the one I thought of doing. Amazing.

How to write .indexOn for Nested values in firebase?

I have wrote on below rules for Get the value form chats with key createdAt But i cant able to get the values from Firebase DB. Rules are Below
"chats": {
".read" : true,
".write" : "auth !== null",
"messages" : {
".indexOn": ["userName", "createdAt", "userId"]
}
},
chats/messages.json?orderBy="createdAt"&startAt=1485920224250&print=pretty
You're missing a level in your rules:
"chats": {
".read" : true,
".write" : "auth !== null",
"$chatId": {
"messages" : {
".indexOn": ["userName", "createdAt", "userId"]
}
}
},

Firebase - Get items I have read access to from endpoint

TLDR: How can I set up the DB access rules so that I can read all the items I have authorization to from a given endpoint?
I have a similar set of data in my Firebase database:
"employees" : [ {
"first_name" : "Constance",
"last_name" : "Smith",
"createdBy: "vdBoGzI2i9f12er6ZcPjG9AiTip2"
}, {
"first_name" : "Agatha",
"last_name" : "Carlton",
"createdBy: "Tpg1mFR99meDV2QGT44pU6y7s1T2"
},
...
}
I also have a list of application users:
"users" : {
"Tpg1mFR99meDV2QGT44pU6y7s1T2" : {
"name" : "Alex Lund",
"isAdmin": true
},
"vdBoGzI2i9f12er6ZcPjG9AiTip2" : {
"name" : "David Peterson",
"isAdmin": false
},
...
},
Basic users will have access just to the employees they created; the admin will be able to read everything.
{
"rules": {
".write": "auth != null",
"employees": {
"$employee": {
".read": "root.child('users').child(auth.uid).child('isAdmin').val() === true || data.child('createdBy').val() === auth.uid"
}
},
}
With this rules, an admin will be able to read ref.child('/employees/0'), but won't have access to ref.child('employees').
How can I get all the employees I have read access to? Is running a query the only solution?
With the rules as you have them now, a query on /users won't work. Since you don't have read permission on /employees any listener on that location will immediately be rejected.
You probably want the rules to be like this:
{
"rules": {
".write": "auth != null",
"employees": {
".read": "root.child('users').child(auth.uid).child('isAdmin').val() === true"
"$employee": {
".read": "data.child('createdBy').val() === auth.uid"
}
},
}
With these rules administrators can read (and thus query) /users, while regular users can only access children that they created.
This is a common pitfall when it comes to Firebase Database security rules and is typically referred to as "rules are not filters". See the relevant section in the documentation, this original Q&A on it and any of the questions in this list.

How to allow users to write new content but not update/delete existing content

So I have this application where anonymous users are allowed to write but not read a specific path. They are posting data to a moderated message board kind of thing.
But with my current security rules, they are allowed to overwrite existing data as well. How can I disallow updates and allow only new posts.
My current security rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"inbox" : {
".write": true,
},
"moderated" : {
".read": true,
},
}
}
Use data.exists() to determine if the object they're trying to write already exists:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"inbox" : {
"$post" : {
".write": "!data.exists()",
}
},
"moderated" : {
".read": true,
},
}
}

Resources