Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Security Rules Emberfire Multiple Access Levels Per Node

I have two Ember models: a items and comments. User will post an item, and other users will be able to comment on the items.

I cannot setup security rules within firebase that allow name and description to be writable by only the current user, but allow for comments to be written to by any logged in user.

Item

// app/models/item.js
export default DS.Model.extend({
  name: DS.attr('string'),
  description: DS.attr('string'),
  comments: DS.hasMany('comment'),
  user: DS.belongsTo('user')
})

Comment

// app/models/comment.js
export default DS.Model.extend({
  user: DS.belongsTo('user')
  body: DS.attr('string'),
  timestamp: DS.attr('string'),
  item: DS.belongsTo('user')
})

Saving a comment

// app/components/comment-form.js
const comment = this.get('comment');
const item = this.get('item');
// service that tracks the currently logged in user
const currentUser = this.get('sessionManager.user');
comment.set('timestamp', new Date());
comment.set('user', currentUser);

// setup both sides of the relationship
item.get('comments').pushObject(comment);
comment.set('item', item');

// save both
return Ember.RSVP.Promise.all([item.save(), user.save()]);

All this works fine. Today, I added security rules in firebase. I only want the currently logged in user to be able to edit an item, but allow any other user to add a comment to any item.

"items": {
    ".read": "auth !== null",
    "$item_id": {
        // only the currently logged in user can write to this node
        ".write": "root.child('users/'+data.child('user').val()+'/uid').val() === auth.uid",
        // allow any user to write comments
        "comments": {
            ".write": "auth !== null"
        }
    }
},

In the firebase simulator this works. As the user who owns the item I can write to /items/<item_id>/description. As a user who does not own the item, I can write to /items/<item_id>/comments/, but not to /items/<item_it>/description. However it fails within Ember using Emberfire.

My working theory is that when I add a new comment to an item I do not "own", then I call item.save() Emberfire tries to write to /items/<item_id>.

How can I setup Firebase security rules such that only the user who owns the item can update most of its properties, but any user can add a comment

Here is is in the firebase rules simulator:

Attempt to write to /items/<item_id>/comments with a user that does not own this item item, with data

{
    "cde": "comment_id"
}

will successfully write with the above rules.

However,

Attempt to write to /items/<item_id> with a user that does not own this item item, with data

{
    "comments": {
        "cde": "comment_id"
    }
}

fails.

like image 465
Nathanael Smith Avatar asked Jan 25 '17 22:01

Nathanael Smith


1 Answers

The problem is that in the .write rule you have used the data predefined variable and data refers to:

A RuleDataSnapshot representing the data as it existed before the attempted operation.

For new items, data.exists() will be false. To allow users to write new items, you should use newData, instead:

".write": "root.child('users/' + newData.child('user').val() + '/uid').val() === auth.uid"

And, if you want to allow for the deletion of items, you should test for the existence of newData and use data if it does not exist:

".write": "root.child('users/' + (newData.exists() ? newData.child('user').val() : data.child('user').val()) + '/uid').val() === auth.uid"
like image 72
cartant Avatar answered Sep 28 '22 01:09

cartant