Rails custom validation involving current_user - ruby-on-rails

I have been developing a rails app in which we allow different users to submit different values for an attribute of a model.
For example all users can submit values greater than 10 while administrators are allowed to submit one-digit values as well.
As we know models do not have access to the current_user helper, therefore you can not write a custom validator with ease.
What's the best practice when implementing such a feature?

This is a perfect use case for validation contexts. You define the contexts in your model, but then your controller specifies which context to use.
Here is a nice article by Justin Weiss about them—although he doesn't mention that they solve problems not solved well any other way, per-user validation rules being the perfect example, since your model doesn't have access to the current user, and your controller isn't designed to specify validation rules:
https://www.justinweiss.com/articles/a-lightweight-way-to-handle-different-validation-situations/

It's always advised to keep logic like current_user outside your model.
But given your requirement,
Well, as our controller knows current_user then it should be the controller that tells your model about it.
So in your model add an attr_accessor for current_user
and in your controller do something like
#model_obj.current_user = current_user
Now all this being said. I'd propose using an alternative to model validation. Because in real what you are trying to do is giving users permissions based on their role. You'd be better of using a gem like Pundit for it.

This is probably a case where you enforce the validation in the controller. Strictly speaking this is not about model validation, but about user authorisation. Pundit is a nice gem for authorisation or you can roll your own.

Related

Different Model callbacks depending on whether for API or in-app

I want to add a mobile app to my web app that has some differences in the User model. Specifically when a User signs up in the web app, the model generates an activation digest which is emailed to them to click on. The callback is:
# app/models/user.rb
before_create :create_activation_digest
I will use a different Users_controller for the api that has an api namespace. When a new user registers from the mobile app I don't want to create the activation digest, but instead generate an access token.
# app/models/user.rb
before_create :generate_access_token
So what is the best way to run the first if it is an in-app signup and the second if it is a mobile app signup. Do I call the method from the controller instead of a model callback? Or is there a better way in the model?
From my experience, avoid model callbacks at all costs. They are difficult to skip, a bit difficult to test, difficult to customize depending on context.
I would consider creating user factories classes and calling them in your controllers, you can easily pass context and do any other customization needed.
Edit since I haven't really given you a way to do that.
If you really want to achieve that with callbacks, probably the only way to do it is to define a new attribute in your user model (not backed by a database field), e.g.
attr_accessor :created_from_api
setting that attribute in your controller and then customizing your model callbacks, e.g.
before_create :create_activation_digest, unless: :created_from_api
This should work, but if I were in your place, I really would move away from callbacks.
It seems to me that creating and activation digest and generating an access token do not belong in the User model - that seems like too much responsibility for User.
Personally, I would say the same thing for the controllers.
So, I would suggest that you use a service object.

What is the most elegant way to access current_user from the models? or why is it a bad idea?

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?
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 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

Was mass assignment really the culprit in Homakov's GitHub hack?

Many commentators (e.g. ZDNet) have suggested that the weakness in GitHub's case was that the model Homakov discovered was vulnerable had mass assignment enabled for its attributes.
However, I think the problem was not this, but was rather a failure to use a before_filter (or suchlike) in the controller to ensure that any given row in the table he updated could only be updated by an admin or by the user with the ID listed in that row. If such a filter had been in place in the controller, then the table would have been secured from attack even if the model's attributes were mass assignable.
Am I correct?
Yes, I think you're right.
In such situations, you can also use authorization gem, for example: cancan, declarative authorization, heimdallr...
The problem is not that you can use. The problem is how to help do not forget to use it in certain situations. For example cancan have the following method: check_authorization It helps to check authorization for all actions.
About that says Homyakov. Add the protected attribute would be the easiest way. Maybe vulnerability has been discovered just because solution to restrict the authorization was different. In some situations, adding of protected attributes leads to a loss of flexibility in your api.
As far as I understand this issue has nothing to do with before_filters checking for admins.
An attribute existed, in the exemple you cite the user_id on the public_keys model, that was not supposed to be exposed to mass assignment.
The before_filter can work as another "layer" of security as .slice can be also used to ensure that you only get the attributes you supposed to get can also work.
On a last note: I think admin != god, meaning, I find no use to a Github admin having rights to perform the action Homakov was able to perform exploiting this issue.

Role based security mechanism for attributes in Rails

I'm looking for a plugin that provides a role based authorization mechanism for securing read/write access to attributes. I'm picturing something along the lines of declarative_authorization for white listing attributes of model objects. I've spent some time looking around but have come up short, does anyone know of anything?
EDIT: I'm using declarative_authorization to control which users have access to what actions in the controller, but I need something similar that provides access control to the attributes of each model object. I'm trying to prevent information leakage through the web API or users from crafting malicious posts. I can do this through the mass_assignment_authorizer but I was hoping some plugin did this already.
CanCan Branch 2.0
https://github.com/ryanb/cancan/tree/2.0
Edit:
The continuation of CanCan is called CanCanCan.
See https://github.com/bryanrite/cancancan
What about creating a model / controller pair for each controller, and then allowing each role only to access methods in its controller(s)? Then you can make a before_filter in each controller that makes sure each user has permission to use methods in that controller.

Restful model ownership validations

I know there are role based authorization gems/plugins for rails to determine if a user can do things based on which role their in. However, is there a best-practice approach to hiding actions from users based on ownership? ie: the show/edit/destroy methods for a user should only be available if they're being performed on the currently logged in user id. Hopefully that makes sense, but I've written some methods to protect against non-owners accessing methods and its become a bit bloated and ugly.
Look at cancan's implementation.
I can recommend on restful_authentication( https://github.com/technoweenie/restful-authentication ). quick tutorial http://railscasts.com/episodes/67-restful-authentication

Resources