Is there an efficient way to implement security rules on a list of nodes in Firebase, to prevent many network connections and queries?
I'm doing an application with a user list, and each user has a list of posts that they want to share with people in their friends list (basically a bit like a social network).
One way is to have a "/users" table where each user has a list of friends "/users/xxx/friends" under their user node, and a list of their own posts "/users/xxx/posts". To query the recent posts of all their friends requires many Firebase queries, one to each of the user's friends' "posts" node.
{
"users" : {
"FKOZlhz05xrVvKGl2" : {
"name" : "John Smith",
"friends" : {
"Tz7Ztv546lOaowrq2" : true
}
"posts" : {
"-Kc-n235d5" : {
"text" : "This is a status update"
}
},
"Tz7Zt546jlOaowrq2" : {
"name" : "Helen test",
"friends" : {
"FKOZlhz05xrVvKGl2" : true
},
"posts" : {
"-Kc-neow05" : {
"text" : "Another status update"
},
"-Kc-ab3243" : {
"text" : "Feeling confused"
}
}
}
}
This requires many simultaneous queries if someone wants to query "posts" for everyone in their friends list, because Firebase wouldn't allow someone to query the whole "users" table and filter out any that are not in the friends list.
Another option is to have a separate "/posts" list for all users which can be queried in one action. However in this case one also can't implement security rules to filter out only posts for your friends, because Firebase doesn't allow filtering the "posts" list based on security rules.
The only way I can see of looking up the "posts" list for only people in your friends list, is to fire off multiple simultaneous queries for each friend.
Related
I am bit familiar with NoSQL and Firebase Realtime Database and also I know that it is not best solution to solve tasks where relational database should be more appropriate. I want to verify about structure of simple many to many relationship that I have.
I have events and users. I want to use Firebase for storing information about users participating in events, later I will need to
Get list of users for event knowing it's id and city
Get list of events for users knowing it's id and city
add or delete information about user attending to event
I would like to have first tree of events ids divided by cities.
events {
'city1' : {
event_id_1 : {'user_1', 'user_2'},
event_id_2 : {'user_3', 'user_4'}.
}
'city2' : {
event_id_3 : {'user_5', 'user_6'},
event_id_4 : {'user_7', 'user_7'}.
}
}
And second tree for users
users {
'user1' : {
'city1' : {event_id_1, event_id_2},
'city2' : {event_id_3, event_id_4},
'city3' : {event_id_3, event_id_4}
},
'user2' : {
'city1' : {event_id_1, event_id_2},
'city2' : {event_id_3, event_id_4},
'city3' : {event_id_3, event_id_4}
},
'user3' : {
'city1' : {event_id_1, event_id_2},
'city2' : {event_id_3, event_id_4},
'city3' : {event_id_3, event_id_4}
},
}
Would it be easy and fast to use and maintain?
Your structure looks pretty OK to me given the requirements listed. Most importantly: you store the data in both directions already, which is the biggest hurdle for many developers new to NoSQL data modeling.
A few notes about your data model, though most are on the level of typos:
Be sure to store the data as maps, not arrays. So event_id_1 : {'user_1': true, 'user_2': true }
If there is a many-to-many relationship between users and events, I'd usually have four top-level lists: users and events (for the primary information about each), and then userEvents and eventUsers (for connections between the two).
Adding a user to an event can be done with a single multi-location update, e.g.:
ref.update({
'/userEvents/userId1/eventId1': true,
'/eventUsers/eventId1/userId1': true
});
Unregistering them is a matter of doing the same with null as the value (which deletes the existing key):
ref.update({
'/userEvents/userId1/eventId1': null,
'/eventUsers/eventId1/userId1': null
});
Also see my answer here: Many to Many relationship in Firebase
You can do this:
List of user
Users
useruid
name:userx
email:userx#gmail.com
useruid
name:usery
email:usery#gmail.com
Events
eventid
useruid
name:userx
location: city1
eventname: party
eventid2
useruid1
name:usery
location: city2
eventname: Boring Party
Get list of users for event knowing it's id and city:
DatabaseReference ref=FirebaseDatabase.getInstance().getReference().child("Events").child(eventid);
ref.orderByChild("location").equalTo(city1);
//retrieve users using a listener
Get list of events for users knowing it's id and city:
DatabaseReference ref=FirebaseDatabase.getInstance().getReference().child("Events");
Query q=ref.orderByChild("location").equalTo(city1);
using a listener this can give you the events that are in location:city1
I would like to make a one to one chat. Each user can contact another user.
Json structure would be :
{
"messages" :
"user1UID_user2UID" : {
auto generated ID : {
"text" : "hello",
"timestamp" : 192564646546,
"name" : "user1"
},
auto generated ID : {
"text" : "hi",
"timestamp" : 192564646554,
"name" : "user2"
}
}
}
When user1 connects to the app, he can see the list of every conversation of which he is a part.
Let's say he had initiated a conversation with user 2, and user 3 has a conversation with him too.
So we would have the following children :
user1UID_user2UID
user3UID_user1UID
How can I retrieve all the conversations User1 is involved in to ?
constructor(db: AngularFireDatabase) {
this.messages= db.list('/messages/' + user1UID + "_" + user2UID); //but I don't know user2UID at this moment
}
Can I make a Regex or do I have to store the conversation key (somewhere) every time it concerns him ?
Or I'm completely wrong and I do not look at the problem the right way?
The key naming schema you use for the chat rooms is a variant of my answer here: http://stackoverflow.com/questions/33540479/best-way-to-manage-chat-channels-in-firebase. It's a variant, since you don't seem to order the UIDs lexicographically, which I recommend.
All my proposed algorithm does is generate a reproducible, unique, idempotent key for a chat room between specific users. And while those are very important properties for a data model, they don't magically solve other use-cases.
As often the case in NoSQL data modeling, you'll have to model the data to fit with the use-cases you want. So if your app requires that you show a list of chat rooms for each user, then you should include in your data model a list of chat rooms for each user:
userChatRooms
user1UID
user1UID_user2UID
user1UID_user3UID
user2UID
user1UID_user2UID
user1UID_user3UID
user3UID
user1UID_user3UID
Now getting a list of the chat rooms for a user is as easy as reading /userChatRooms/$uid.
I have 2 main nodes in my database: Users and Projects.
Each user can be assigned to multiple projects, in different roles.
After reading about how to structure many to many relationship, I ended up that it should look like this:
users : {
user1 : {
name : blah,
email : a#a.com,
projects : {
projects1key : true,
projects2key : true
}
}
}
projects : {
project1key : {
name : blahserve,
category : blahbers,
providers : {
user1 : true,
user7 : true
}
}
}
What I couldn't figure out is how I can assign every user a role in each project.
What's the new database structure should look like if I need to add role (string) to each user within a project?
Correct me if I'm wrong: When I assign user to a project, I need to create 2 new nodes: a projectkey node in my user node, and a userkey node in my projectkeynode. Is that right?
Update
Since the answer given here is correct but not sure it will fit my use case, this is my use case:
(1) Iterating over a list of all project users
(2) Iterating over a list of all member project
(3) Check for project role when the user access a project and give give him permission depends on his role
(4) The "Project -> users" page allow you to add a new user to existing project. the user role is picked in the same form together with the user. the user must have a role in a project.
Or maybe just simply you could add a different node all together:-
What's the new database structure should look like if I need to add role (string) to each user within a project?
users : {
user1 : {
name : blah,
email : a#a.com,
}
},
projects : {
projectKey1 : {
name : blahserve,
category : blahbers,
}
},
projectsLists:{
user1 :{
projectKey1 : true,
projectKey2 : true
}
},
projectsRoles :{
projectKey1 : {
user1 : provider,
user2 : editor,
....
}
}
Correct me if I'm wrong: When I assign user to a project, I need to create 2 new nodes: a projectkey node in my user node, and a userkey node in my projectkeynode. Is that right?
Always prefer the flatter DB structure. So using this structure you only gotta append or update your user's role once.
This question already has an answer here:
Granting access to Firebase locations to a group of users
(1 answer)
Closed 7 years ago.
Let's say you have a firebase database with news articles to show in a webapp. On the top level we use Firebase auth to make sure that the user has access to the news-articles in the first place.
But then the different articles (items in the news-database) should be visible for different people. For example should users I put in a group called "management" have access to view some articles that the rest of the users cannot.
How can I achieve this using Firebase? (Web-version)
Sample database:
{
"newsfeed" : {
"1001" : {
"body" : "Lots of text here for the body of the article.",
"header" : "This is an open article",
"leadParagraph" : "With a lead paragraph and lots of content",
"permissions" : {
"All" : false,
"Group11" : true,
"Management" : true
}
},
"1002" : {
"Permissions" : {
"All" : true
},
"body" : "Content here",
"header" : "Testarticle for everyone",
"leadParagraph" : "Everyone can read this one"
}
}
}
So the question is not about how to limit a users access to the whole of "newsfeed", but rather how to edit access individually on the item level. In the example above all users can read item 1002, whilst only users that are tagged as members of the groups "Management" and/or "Group11" should be able to read item 1001.
Is this possible with rules dynamically? If I have to create a new rule for every item, is there a limit to the number of rules?
If you haven't already, I would read the Security Quickstart guide. It does a great job of explaining how Firebase security rules work.
In the case of your problem, I would suggest having a list of groups, where each group has the uids of the users in that group. For example:
{
"groups" : {
"Group11" : {
"<uid1>": true,
"<uid2>": true
},
"Management" : {
"<uid1>": true,
"<uid2>": true
}
}
}
If a user is a member of a group, add their uid to the group's list of members. Then in your rules, you can make the .read check whether or not the user who is trying to read the data has their uid in any of the allowed groups.
I have a data structure that looks like:
{
"farms" : {
"-J3saHfe8_2GRqn3jOTc" : {
"stockunits" : {
"-J4CAeRI-IKKXVuNILCv" : {
"name" : "Chickens",
"totalStockUnits" : 115
}
},
"owner" : 3,
"name" : "smith"
},
"-J3sbbqsGabs5cLaj0zP" : {
"owner" : 2,
"name" : "brown"
}
}
}
When I query "/farms" I would like to get a list of the farms for which the authenticated user is the owner. I would also like to prevent users from viewing farms that they are not the owner for.
My rules currently look like this:
{
"rules": {
".write": false,
".read": false,
"farms": {
"$farm": {
".read": "data.child('owner').val() == auth.id"
I get that by putting the .read at this level I am granting read access to individual farm when accessed via /farms/:farmid. And if I move the ".read" up to the "farms" level this results in granting read access to the entire list.
So I think I need to restructure my data but aren't sure how best to go about this? I don't want to add the farms object below the user object as I will be wanting to add a number of "editor" users to any given farm as well.
Thanks for your help
You'll need to denormalize your data and include references to individual farm objects under each user's profile. There's a full example of how to do this kind of denormalization in this blog post: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html
In that example, the code snippets show organizing comments under posts, you can use a similar technique to map farms to users.