简体   繁体   中英

How to rescue by passing a block in Ruby?

I have defined my own method authorize_user in one of my controllers, as:

    def authorize_user
      if !((current_user.has_role? :admin, @operator) || (current_user.has_role? :super_admin))
        raise CanCan::AccessDenied
      end
    end

I want to rescue from the CanCan exception (or any other exception for that matter). I have used Rolify in my app. How do I rescue and redirect to the root_url of my app with a custom message?

I have tried the following options, but none of them worked:

Try 1:

rescue CanCan::AccessDenied do |exception|
    redirect_to root_url, :alert => exception.message
end

Error in this case: syntax error, unexpected keyword_do, expecting '('

Try 2:

rescue CanCan::AccessDenied
  redirect_to root_url, :alert => "Unauthorized Access"

Error in this case: Render and/or redirect were called multiple times in this action

How do I solve this issue?


This is my controller code:

class CabsController < ApplicationController
  before_action :set_cab, only: [:show, :edit, :update, :destroy]
  before_action :authenticate_user!
  after_action :authorize_user

 # Some basic CRUD actions

 private

    def set_cab
      @cab = Cab.find(params[:id])
      @operator = Operator.find(params[:operator_id])
    end


    def cab_params
      params.require(:cab).permit(:category, :number)
    end

    def authorize_user
      if !((current_user.has_role? :admin, @operator) || (current_user.has_role? :super_admin))
        raise CanCan::AccessDenied
      end
    end
end

I think you could try the rescue_from method.

For example, your ApplicationController , would look like this:

class ApplicationController < ActionController::Base
  rescue_from CanCan::AccessDenied, with: :not_authorized

  #other stuff      

  private
  def not_authorized
    redirect_to root_url, alert: "Unauthorized Access"
  end
end

Since the question was updated with more code, here is additional information:

Some suggestions:

  • Make the :authorize_user a before_action as well. That way you don't need to worry about code running in the action even when the user was not allowed to do stuff.
  • You might also need to add the same :only option as for the :set_cab since you use the @operator instance variable.
  • Last, a personal code style preference is that I would have changed the if ! to unless to increase reading flow.

尝试redirect_to(...) and return

Agreeing with Jakob W I would like to point, that authorization (and authentication) MUST be performed only before action. What is the purpose of any authorization and exception raising when DB transaction, reading/writing to filesystem etc have been already done?

And using before_action has no problem with Render and/or redirect were called multiple times in this action - there will be only one redirect - in exception handling before controller method call.

So, I recommend next code (updated Jakob W's sample):

class CabsController < ApplicationController
  #...
  before_action :authorize_user

  private
    #...
    def authorize_user
      if !((current_user.has_role? :admin, @operator) || (current_user.has_role? :super_admin))
        raise CanCan::AccessDenied
      end
    end
end


class ApplicationController < ActionController::Base
  rescue_from CanCan::AccessDenied, with: :not_authorized

  #other stuff      

  private
  def not_authorized
    redirect_to(request.referrer || root_path), alert: "Unauthorized Access"
  end
end

Could I recommend another authorization gem? I think this one is flexible and easy to use - pundit ( https://github.com/elabs/pundit ). Page on github has some useful tips on authorization.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM