Access-control on the item-level with Firebase [duplicate] - firebase

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.

Related

Firebase one to one chat with Angular

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.

Firebase security rules for friends list

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.

How to structure data in Firebase created by one user but accessible to users in a group?

So, let's say I have data like this:
{
"events" : {
"s0d980983s" :
{ creator: "bob#bob.com",
text: "Bob says 'My name is Robert'" },
"kjl34jl234j" :
{ creator: "fred#fred.com",
text: "Fred says 'My name is Fredrick'" }
}
"users" : {
"bob#bob.com" : { "paid": true },
"fred#fred.com" : { "paid": false }
}
}
I'm assuming this is the correct way to structure the data. When the data is created, I use the push() method to create a new key for the data, and then store the creator of the data inside it.
I'd like to make it so that:
I can allow anyone from a group of users to access certain data (and disallow others obviously).
The query is "optimized," meaning if I have thousands of records I am not iterating over all the data.
More concretely, for example, I want lizzie#lizzie.com to be able to see the s0d980983s.
I'm confused how to structure the data, and what my Firebase rules should look like.
Would it be something like this?
{ "events" : {
"s0d980983s" :
{ creator: "bob#bob.com",
viewers: { "bob#bob.com": true,
"lizzie#lizzie.com" : true },
text: "Bob says 'My name is Robert'" },
...
}
I don't understand how I can search for events that are viewable by a group of users. I don't believe Firebase supports some kind of wildcard that would make this code work, right?
var ref = firebase.database().ref( "events/*/viewers/lizzie#lizzie.com" ).on(...);
Do I also want to reference the events inside my users table? I'm not sure I understand how to flatten data (denormalize it) and keep references in both places to support a query like this. Should I expect to make multiple queries where I first retrieve a list of events stored in a user object and then retrieve them one by one using their key? But, how do I put that logic into my firebase rules?
{ "events" : {
"s0d980983s" :
{ creator: "bob#bob.com",
viewers: { "[insert bobs id]": true,
"[insert liz id]" : true
},
text: "Bob says 'My name is Robert'" },
...
}
Based on the above structure as you suggested, and if you are using firebase authentication to authenticate your user, you can add another 'read' or 'write' rule for checking whether that user is in the list of your 'viewers'. something like:
{
"rules": {
"users": {
"$uid": {
".write": "auth != null &&
root.child('users').child(auth.uid).child('viewers').child(auth.uid).val() ==
true"
}
}
}
}
This should help. setting firebase security rules at a location/node

NoSQL & Firebase: Storing & Iterating over many-to-many relationship

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.

Query firebase objects by user

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.

Resources