4

I'm trying to centralize authentification in pundit policies instead of having it in my controllers. It works well but I lost some flexibility in customizing redirection and flash message.

How could I transfer the information about which authentification didn't pass to the Pundit::NotAuthorizedError rescuing function ? One action can have 2 steps of authentification: 1. user.paid? 2. user.is_allowed_to_update? and I want custom message and redirection for each case.

The exception.query solution is not working cause it only allow to customize flash and redirection for each action and not within one action.

Below is a more detailed explanation of the situation

WITHOUT PUNDIT
Comment_Controller
def update
    if user.didnt_pay?
        flash[:message] = nice_message
        redirect_to payment_page_path
    elsif user.is_not_allowed_to_perform_action
        flash[:message] = less_nice_message
        redirect_to dashboard_path
    end
end

And now

WITH PUNDIT
Comment_Controller
def update
    authorize @comment
end

Comment_policy
def update?
    user.paid? && user_is_allowed_to_perform_action
end

ApplicationController
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def user_not_authorized
    flash[:message] = one_message_for_all_error # THIS IS WHAT I WANT TO CUSTOMIZE
    redirect_to one_path_for_all_error # THIS IS WHAT I WANT TO CUSTOMIZE
end

2 Answers2

3

A possibility for customizing this error messages it to set the expected message in the Policy and later getting it from the controller. How to do so?

The exception object you get as an argument in the controller

class CommentsController < ApplicationController

  def user_not_authorized(exception)
  end
end

comes with a policy attribute which links you to the offending policy. So, lets say that in your policy you want to set a particular message when some clause is not fulfilled:

class AnimalPolicy < ApplicationPolicy
  attr_accessor :error_message

  def new?
    if !authorization_clause
      @error_message = "Something terrible happened"
      false
    else
      true
    end
  end
end

Hence, in your controller, you would have to set this error_message into your flash or wherever you want it to be:

class CommentsController < ApplicationController

  def user_not_authorized(exception)
    flash[:error] = exception.policy.try(:error_message) || "Default error message"

    redirect_to root_path 
  end
end

It's a bit clumsy solution, but it worked for me

finiteautomata
  • 3,753
  • 4
  • 31
  • 41
0

In my solution I propose two methods, one when the user has a good answer the other when the answer is not favorable ... pundit has a method (user_not_authorized) which allows to manage that one could duplicate and Adapt to your suggestions

 def update

    if user.didnt_pay?

       authorize @comment
       user_congratulation

    elsif user.is_not_allowed_to_perform_action

        user_not_authorized 

     end

end

in ApplicationController

past this rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

and after You'll create two private methods in your controller called

user_not_authorized and user_congratulation

   private

     def user_not_authorized
        flash[:alert] = "less_nice_message"
        redirect_to dashboard_path
     end


     def user_congratulation
         flash[:alert] = "nice_message"
         redirect_to payment_page_path
     end

   end

for more information visit this link https://github.com/elabs/pundit#rescuing-a-denied-authorization-in-rails

Although this post is old, I thought fit to answer because I was also in need of a good answer, which was not the case! I hope to have helped