Reputation: 13907
Rails form validation is designed to go in the model most easily. But I need to make sure the current user has the required privileges to submit a post and the current_user
variable is only accessible in the controller and view.
I found this answer in a similar question:
You could define a
:user_gold
virtual attribute forBook
, set it in the controller where you have access tocurrent_user
and then incorporate that into yourBook
validation.`
How can I set this up with my post and user controller so that the current_user
variable is accessible in the model?
This whole thing is wrong from an application design perspective as @Deefour's answer pointed out. I changed it so my view doesn't render the form unless the condition is true.
Upvotes: 1
Views: 3997
Reputation: 18070
I agree with Ismael - this is normally done in the controller. It's not an attribute of the model, it's a permission issue and related to the controller business logic.
If you don't need all the power of a gem like CanCan, you can role your own.
class BooksController < ApplicationController
before_filter :gold_required, :only => :create
def create
book = Book.new
book.save!
end
# Can be application controller
private
def gold_required
return current_user && current_user.is_gold?
end
end
You may want to put the filter on the 'new' method as well.
Upvotes: 0
Reputation: 35360
The "similar question" is saying you can do something like this
class YourModel < ActiveRecord::Base
attr_accessor :current_user
# ...
end
and then in your controller action you can do something like
@your_model = YourModel.find(params[:id])
@your_model.current_user = current_user
@your_model.assign_attributes(params[:your_model])
if @your_model.valid?
# ...
You can then use self.current_user
within YourModel
's validation methods.
Note I don't think this is what you should be doing though, as I don't consider this "validation" as much as "authorization". An unauthorized user shouldn't even be able to get the part of your action where such an update to a YourModel
instance could be saved.
As for doing the authorization with Pundit as requested, you'd have a file in app/policies/your_model.rb
class YourModelPolicy < Struct.new(:user, :your_model)
def update?
user.some_privilege == true # change this to suit your needs, checking the "required privileges" you mention
end
end
Include Pundit in your ApplicationController
class ApplicationController < ActionController::Base
include Pundit
# ...
end
Then, in your controller action you can do simply
def update
@your_model = YourModel.find(params[:id])
authorize @your_model
# ...
The authorize
method will call YourModelPolicy
's update?
method (it calls the method matching your action + ?
by default) and if a falsy value is returned a 403 error will result.
Upvotes: 4
Reputation: 16720
Authorization shouldn't be done in models. Models have already many responsibilities don't you think?
That's a controller thing, and actually you can have the logic in other place using some gem like cancan and in your controller you would do something like:
authorize! :create, Post
Upvotes: 2
Reputation: 1705
You can define a "virtual attribute" in your model like this:
class Book < ActiveRecord::Base
attr_accessor :current_user
end
Its value can be set directly in your controller like this:
class BooksController < ApplicationController
def create
book = Book.new
book.current_user = current_user
book.save!
end
end
And inside your model's validation routine, you can access it like any other ActiveRecord field:
def validate_user_permission
errors[:current_user] = "user does not have permission" unless current_user.is_gold?
end
I can't remember if this is the case with ActiveRecord, but you might be able to set virtual attributes via the mass-assignment methods like create
, update
, and new
in the controller:
def create
Book.create!(current_user: current_user)
end
In order to do that, you would probably have to add the following line to your model to enable mass-assignment of that virtual attribute:
attr_accessible :current_user
Upvotes: 1