In Meteor, when should I prefer a method over a deny rule?

It seems to me that allow/deny rules should be favoured, as their goal is more explicit, and one knows where to look for them.

However, in the Discover Meteor book, preventing duplicate insertions (“duplicate” being defined as adding a document whose url property is already defined in some other document of the same collection) is said to have to be defined through a method (and left as an exercise to the reader, chapter 8.3).

I think I am able to implement this check in a way that I find much clearer:

Posts.deny({
    update: function(userId, post, fieldNames, modifier) {
        return Posts.findOne({ url: modifier.$set.url, _id: { $ne: post._id } });
    }
});

(N.B. if you know the example, yes, I voluntarily left out the “only a subset of the attributes is modified” check from the question to be more specific.)

I understand that there are other update operators than $set in Mongo, but they look typed and I don't feel like leaving a security hole open.

So: are there any flaws in my deny rule? Independently, should I favour a method? What would I gain from it? What would I lose?

Solution 1

Normally I try to avoid subjective answers, but this is a really important debate. First I'd recommend reading Meteor Methods vs Client-Side Operations from the Discover Meteor blog. Note that at Edthena we exclusively use methods for reasons which should become evident.

Methods

pro

  • Methods can correctly enforce schema and validation rules of arbitrary complexity without the need of an outside library. Side note - check is an excellent tool for validating the structure of your inputs.

  • Each method is a single source of truth in your application. If you create a 'posts.insert' method, you can easily ensure it is the only way in your app to insert posts.

con

  • Methods require an imperative style, and they tend to be verbose in relation to the number of validations required for an operation.

Client-side Operations

pro

  • allow/deny has a simple declarative style.

con

  • Validating schema and permissions on an update operation is infinitely hard. If you need to enforce a schema you'll need to use an outside library like collection2. This reason alone should give you pause.

  • Modifications can be spread all over your application. Therefore, it may be tricky to identify why a particular database operation happened.


Summary

In my opinion, allow/deny is more aesthetically pleasing, however it's fundamental weakness is in enforcing permissions (particularly on updates). I would recommend client-side operations in cases where:

  • Your codebase is relatively small - so it's easy to grep for all instances where a particular modifier occurs.

  • You don't have many developers - so you don't need to all agree that there is one and only one way to insert into X collection.

  • You have simple permission rules - e.g. only the owner of a document can modify any aspect of it.

In my opinion, using client-side operations is a reasonable choice when building an MVP, but I'd switch to methods for all other situations.


update 2/22/15

Sashko Stubailo created a proposal to replace allow/deny with insert/update/remove methods.

update 6/1/16

The meteor guide takes the position that allow/deny should always be avoided.