How to fetch all resources from Firebase database with nested security rules? - firebase

In firebase, is it possible to configure a rule so that when a parent resource is fetched, it only returns child items that the user as access to?
I have a data structure like so:
{
application_access_request: {
app_id_1: {
access_key_1: {
email: "abc#b.com"
},
},
app_id_2: {
access_key_2: {
email: xyz#c.com
},
}
}
}
And then I have rules like so:
{
"application_access_request": {
"$appId": {
"$accessId": {
".read": "data.child($accessId).child('email').val() === auth.token.email",
},
}
},
}
As a user logged in with email abc#b.com,
When I request all resources from application_access_request/,
Then I want app_id_1 and it's children to be accessable / returned to the user,
Is it possible to allow reading of all application_access_request but only return apps that the auth'd user has access to?

No, security rules cannot be used to selectively return information (see rules are not filters). You may, however, be able to use querying to solve this use case. For example, if your data was structured:
{
application_access_request: {
app1: {
access_key: "user#example.com"
},
app2: {
access_key: "user2#example.com"
}
}
}
You can use query-based rules to limit querying:
"application_access_request": {
".read": "auth.uid != null && auth.token.email_verified &&
query.orderByChild == 'access_key' &&
query.equalTo == auth.token.email"
}

Related

Firebase Rules: Allow user to read data only if user is included in a child node

I am attempting to rewrite my database rules to only allow members of a specific 'collection' to access that collection only if that member is included in the teams list. Referring to the image attached below, this is what my rule currently looks like:
{
"rules": {
"collection": {
"$collection_id": {
"$teams" : {
".read": "data.child('id').val() === auth.uid"
}
}
}
}
}
However this doesn't seem to work and I believe it's incomplete. How can I update my rules to match this design structure? If any change in my structure is necessary to support this, do pitch that and I will attempt to update the current production data.
You could reach into the teams node like this:
{
"rules": {
"collection": {
"$collection_id": {
".read": "data.child('teams').child(auth.uid).child('id').val() === auth.uid"
}
}
}
}
Right now you have unnecessary redundancy in the teams node. The id of the child node is repeated in its own child id. You can simplify your database and rules if you simply set teams/{teamId} = true in the database, your rule could look like this instead to allow only users listed under teams to read the entire collection:
{
"rules": {
"collection": {
"$collection_id": {
".read": "data.child(auth.uid).val() === true"
}
}
}
}

How to structure data with Firebase to allow data to be user specific and also public?

We are building a platform using Firebase Realtime Database and I'm having a bit of a struggle to find the best way to structure our data for private and public access.
Today we have
database: {
items: {
$userUid: {
$itemUid: {
poster_link: "..."
format: "..."
title: "..."
}
}
}
}
All our items are stored under each user in order to make it fast and secure to load.
Our rules are set up like this
{
"rules": {
"items": {
"$userId": {
"$itemId": {
".read": "auth !== null,
".write": "auth !== null"
}
}
}
}
}
So only an authorised user can read and write the data. I could create something like this to allow items to be public if the value is true:
".read": "auth !== null || data.child('public').val() == true"
But this will still be under $userUid
So I was wondering if you have any suggestion on how to structure this example to allow items to be under a user and also seen publicly, not necessary under this user, a bit like Dropbox does when you share something.
You chosen data structure does not take advantage of the flat data principles of Firebase. This will make it very difficult for you to query items of multiple users. For example, how do you get all the public items without drilling into each user?
Similarly, a boolean called public is also not good because you can't extend it to other ACL scenarios. Much better is an ACL object that can be extended in the future.
For example:
items: {
itemsUid: {
[...],
userId: ...,
ACL: { public: true }
}
}
Now you can write the rule:
auth !== null && (root.child(items/ACL/public).exsists() || data.userId === auth.UID)
If in three months you add a concept of friends that can see you posts or followers that can see you items you can simply add friends: true, followers: true to the ACL object and adjust the rule.
You can structure like this
database: {
items: {
$itemUid: {
poster_link: "..."
format: "..."
title: "..."
user: "userid"
}
}
}
now set the rules as
{
"rules": {
"items": {
"$itemId": {
".read": "auth !== null || data.child('public').val() == true,
".write": "auth !== null"
}
}
}
}

firebase security rule for nested data

I have the following data structure
{
"users": {
"uid": {
"priv": {
"eid": "eid"
}
}
}
}
{
"entity": {
"eid": {
"priv": {},
"pub": {}
}
}
}
Now my requirement is - any user can create an entity if it does not have one entity_id in the "priv" child.
Once an user create an entity, it gets the entity_id under it's priv field.
And the same user can modify the same entity which he has under it's priv field.
One entity can have multiple users!
I have tried with the following rule but it allows any one to modify any entity if it does not have an enity_id(i.e. eid) in priv field.
"entity":{
".write": "root.child('users/'+auth.uid+'/priv/eid').val() == null",
"$eid":{
".read": "root.child('users/'+auth.uid+'/priv/eid').val() == $eid",
".write": "!data.exists() || root.child('users/'+auth.uid+'/priv/eid').val() == $eid"
}
}
How should I re-write the rules?

Getting .read and .write Security Rules To Work For Groups

I am​ having difficulty writing the security rules for​ building a team based collaboration platform.
​​When a user registers they should be able to create a team and invite users to that team.
Projects should be owned by the team​.​
​Only users in that team should be able to view ​that project.​
​Users should only see the teams they are a member of​.
How do I write​ .read​ security rules so the ​users only see info from teams they're in?
I should only get two teams listed because I belong to them github:8272012​.​
​Current Security Rules: ​
{
"rules": {
".read": true,
"users": {
"$user": {
//can add a message if authenticated
".write": "auth.uid === $user"
}
},
"teams": {
"$team": {
"users": {
// can write to the users list only if ADMINISTRATOR
"$user": {
".write":"newData.parent().child(auth.uid).val() === 99"
}
}
}
},
"projects": {
"$team": {
"$project": {
//can add a message if they are a MEMBER
".write": "(!data.exists() && newData.exists() && root.child('teams/' + $team + '/users/' + auth.uid).val() >= 10)"
}
}
}
}
}
I should only get two teams listed because I belong to them github:8272012.
The following security rules would give read and write access for a project only to users who are in that project's team (assuming you add a /projects node for each user to indicate which projects that user has access to):
"rules": {
"projects": {
"$project": {
".read": "root.child('users').child(auth.uid).child('projects').val().child($project).exists()" ,
".write": "root.child('users').child(auth.uid).child('projects').val().child($project).exists()"
}
}
}
I can't see what data you're storing for each project, but if you store a reference to the project's team you could also use that in your security rules.

Firebase Security Rules

I'm new to Firebase and really struggling with the security rules.
I'm trying to store data for anonymous user sessions that can only be accessed by that user but as soon as I tighten up the security rules I get permission denied and can't work out why.
The structure I have is:
/userSessions/$user/user_id
The application pushes to user sessions and writes the user_id as returned by the anonymous login.
I have the following security rules:
{
"rules": {
".read": true,
"userSessions": {
"$user": {
".write": "newData.child('user_id').val() == auth.id",
"user_id": {
".validate": "newData.isString()"
}
}
}
}
}
I'm sure I'm doing something silly, but can't work out how to use the security simulator either so don't know how to go about troubleshooting this
Any help greatly appreciated
EDIT -
Problem occurs when trying to create a new entry under userSessions after authenticating anonymously.
Code:
var userSessionsRef = new Firebase('https://xxxxxx.firebaseio.com/userSessions');
var userSession;
var auth = new FirebaseSimpleLogin( userSessionsRef, function(error,user) {
if (error) {
console.log(error);
} else if (user) {
userSessionsRef.child(user.id).set({
user_id: user.id,
provider: user.provider,
created: Firebase.ServerValue.TIMESTAMP
});
userSession = new Firebase('https://xxxxx.firebaseio.com/userSessions/'+user.id);
userSession.onDisconnect().remove();
}
The set() operation you've provided runs great; no errors; there must be another issue at play here that you haven't captured in your repro (did you enable anonymous auth in Forge?). Here's a fiddle running on your security rules, copied line-for-line (just moved down one child path): http://jsfiddle.net/katowulf/wxn5J/
On a related note, your current security rules will not allow the userSession.onDisconnect().remove(); op, since records can only be written if newData() contains a user_id value (i.e. it can't be null). A better way to structure these security rules would be as follows:
{
"rules": {
".read": true,
"userSessions": {
"$user": {
".write": "$user === auth.id", // delete allowed
"user_id": {
".validate": "newData.isString() && newData.val() === $user"
}
}
}
}
}

Resources