2

So, I've implemented some permissions between my users and the objects the users modify.. and I would like to lessen the coupling between the views/controllers with the models (calling said permissions). To do that, I had an idea: Implementing some of the permission functionality in the before_save / before_create / before_destroy callbacks. But since the permissions are tied to users (current_user.can_do_whatever?), I didn't know what to do.

This idea may even increase coupling, as current_user is specifically controller-level.

The reason why I initially wanted to do this is: All over my controllers, I'm having to check if a user has the ability to save / create / destroy. So, why not just return false upon save / create / destroy like rails' .save already does, and add an error to the model object and return false, just like rails' validations?

Idk, is this good or bad? is there a better way to do this?

NullVoxPopuli
  • 61,906
  • 73
  • 206
  • 352

2 Answers2

4

Let the controller check the user's privileges. To have the model handle authorization logic would lead to more coupling (just in different places, and there'd still be coupling to the controller to get the current user). Checking privileges isn't really internal logic to a model.

Simile: Imagine if it was a file's responsibility to check whether you can read/write to it, instead of having the OS (which is the mother of all controllers, really) handle the access.

If you want cleaner controllers you can (for instance) make some generalized before_filters that restrict access to CRUD actions based on the current user.

Flambino
  • 18,507
  • 2
  • 39
  • 58
  • ok, cool. thanks for the explanation! you've probably saved a few hours of my time =D – NullVoxPopuli Jun 12 '12 at 22:37
  • No problem. I agree that'd it be nice if the model could just "raise" a validation error, but I think you'd get into some funky, hard-to-maintain code pretty quickly. A quick litmus test might be: Can you use the model by itself in the Rails console? If yes, then it's nicely decoupled. If no, then the model is probably too tied up in stuff that doesn't directly concern it. – Flambino Jun 12 '12 at 22:49
  • ah, that's a good way to think about it. I think I came up with a different solution though that still reduces code repetition. For my controllers, I think I'm going to Make an abstract controller, implement methods that get the permission boolean from the user, and then in my actual controllers call super and pass a block, so my current code still gets executed. This will work for 4 of my controllers. Passing large blocks of core seems weird, but it works. idk. =D – NullVoxPopuli Jun 12 '12 at 23:03
2

Flambino gave you the party line :) but let me add my 2 cents. It is certainly possible to consider access control logic as part of models.

Model validations is a way of checking the manipulating user's rights on crud actions. A downside of this is exactly the fact that access logic usually generalizes across models, therefore we like to abstract them out like in a neat cancan ability file.

You can actually have your cake and eat it by making your manipulation itself a polymorphic association on models. This is how most audit/version control systems are implemented. https://github.com/collectiveidea/audited You could extend it and put your access control logic in audit class validations, so it is in one place. Audited gem also offers a cunning way to pass the current user to the model level by using an observer as around filter on a controller. https://github.com/collectiveidea/audited/blob/master/lib/audited/sweeper.rb but there are other methods too https://github.com/bokmann/sentient_user often considered hacky.

Caveats

  • In either case you will have to guard against situations when models are manipulated outside the request cycle (in a background routine, cronjob, console) when current_user is ill-defined.

  • You usually do not want to treat access violations with validation errors but use standard http status code responses.

  • Third, in a web app setting, you usually manipulate objects via forms and typically you can already controll access to say an update form in fact usually coupled with the same access rights as update itself (cancan even have general alias for new/create and edit/update) so if the latter is handled with validations, this generalized access logic will be lost. Not to mention that read-access logic is impossible to handle with model validations.

hope this helps

Viktor Trón
  • 8,774
  • 4
  • 45
  • 48