Tallmaris
Tallmaris

Reputation: 7590

Authorize with multiple action in CanCan

I am trying to understand a bit better the capabilities of CanCan when it comes to authorization. Imagine this controller action:

def update
  if can? :action, Model or can? :resolve, Model or can? :authorize, AnotherModel
    # My Code here
    respond_with @model
  else
    raise CanCan::AccessDenied.new(nil, :update, Model)
  end
end

I got to this point while trying to find a solution to the above using authorize!. As far as I can see (also looking at the signature) authorize! only accepts one permission (action) and one subject, with an optional message, like this:

def authorize!(action, subject, *args)
  # code
end

Is there a way which I may be overlooking to instruct authorize to check for multiple actions? Putting two authorize one after the other will act as an AND condition between permissions, what I would like is it to work like an OR condition, basically similar to the custom code above (which has the problem of raising the AuthorizationNotPerformed in CanCan, avoidable with skip_authorize_resource which is not something I would really like to do).

Upvotes: 3

Views: 2706

Answers (2)

Tallmaris
Tallmaris

Reputation: 7590

In the end I added this rather nice solution to the ability class:

def multi_authorize!(*actions, message_hash)
    message = nil
    if message_hash.kind_of?(Hash) && message_hash.has_key?(:message)
      message = message_hash[:message]
    end
    auth = false
    actions.each do |act|
      auth = auth || can?(act[0], act[1])
    end
    if !auth
      message ||= unauthorized_message(actions[0][0], actions[0][1])
      raise CanCan::AccessDenied.new(message, actions[0][0], actions[0][1])
    end
end

Included an helper for the Controllers:

module CanCanAddition
  def multi_authorize!(*args)
    @_authorized = true
    current_ability.multi_authorize!(*args)
  end
end

if defined? ActionController::Base
  ActionController::Base.class_eval do
    include ApplicationHelper::CanCanAddition
  end
end

Which I call like this:

  def create
    multi_authorize! [:create, Model1], [:authorize, Model2], :message => "You are not authorized to perform this action!"
    # other code...
  end

WARNING: Due to the code in the ability class, you must provide a message or the last pair of authorization will not be passed in the *args. I'll take some time to overcome this but the idea of the solution I think fits nice with.

Upvotes: 3

spas
spas

Reputation: 1934

You can create an custom action and create as many or-conditions as you like.

can :my_update_action, Project do |project|
  can?(:read, ModelX) || can?(:read, ModelY) || ... 
end

Upvotes: 3

Related Questions