0

This is what I get going on the recipe show page: Pundit::AuthorizationNotPerformedError

My Controller looks like that:

class RecipesController < ApplicationController
  skip_before_action :authenticate_user!, only: [:index, :show]
  def index
    if params[:query].present?
      @recipes = policy_scope(Recipe).search_by_title_and_description(params[:query]).order(created_at: :desc)
    else
      @recipes = policy_scope(Recipe).order(created_at: :desc)
    end
  end

  def show
    @recipe = Recipe.find(params[:id])
    @recipes = Recipe.first(5)
  end
end

My policy.rb:

class RecipePolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      scope.all
    end

    def index?
      false
    end

    def show?
      false
    end
  end
end

And this is the Error message when adding a 'authorize @recipe' to the show action: not allowed to show? this Recipe I need Pundit Authorization for the comments for each recipe but not for the recipe show-action itself. What do I do wrong? Thanks for helping !!

Witta
  • 15
  • 5
  • Your `show?` method has `false` hardcoded. Therefore no user is allowed to see any recipe under any circumstance. I suggest reading [how to define policy rules](https://github.com/varvet/pundit#policies) in the Pundit documentation. There are some great examples. – spickermann Feb 19 '21 at 10:08
  • @spickermann is it hardcoded in the policy.rb by writing - def show? false end - ? – Witta Feb 19 '21 at 10:15
  • Yes, when the `show?` method returns `false` then that means that the current user is not authorized to see the current `recipe`. You need to replace the `false` with code that makes sense in the context of your application and that returns `true` (only) when the current user is allowed to the see specific recipe. – spickermann Feb 19 '21 at 10:26

1 Answers1

2

authenticate_user! (which you haven't shown/explained to us, but is probably a method from devise or similar?) is presumably to do with signing in -- that's authentication, not authorization, and is therefore outside the scope of what Pundit tries to solve.

Authentication is all about checking "are you signed in?". If this check fails, then the server responds with a 401 status.

Authorization is about checking "are you allowed to perform this action (potentially as a guest)?". If this check fails, then the server responds with a 403 status

Now presumably, you have also added some code like this in your application:

class ApplicationController < ActionController::Base
  include Pundit
  after_action :verify_authorized, except: :index # !!!!!
end

This after_action check is a safety net; it's there to ensure that you never forget to authorize an endpoint -- as that would allow the action to be performable by any user, by default! The presence of this check is what's causing your error above.

So. With that explained, let's look how you can implement this.

  1. Should RecipesController#show be accessible by guests, or only by logged-in users?

If, and only if, the answer is "guests" then add this:

skip_before_action :authenticate_user!, only: :show
  1. Assuming you've already performed any necessary authentication, you want to let any user see any recipe. How can you implement that?

Option 1 (recommended):

class RecipePolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      scope.all
    end
  end ## WARNING!! NOTICE THAT THE `Scope` CLASS ENDS HERE!!!

  def show?
    true # !!!!
  end
end

class RecipesController < ApplicationController
  # ...
  def show
    @recipe = Recipe.find(params[:id])
    authorize(@recipe) # !!!
    # ...
  end
end

Option 2 (also works, but worse practice as it means you can't rely on unit tests for the policy class):

class RecipesController < ApplicationController
  def show
    skip_authorization # !!!
    @recipe = Recipe.find(params[:id])
    # ...
  end
end
Tom Lord
  • 27,404
  • 4
  • 50
  • 77
  • thank you very much for answering!! I tried both options but only option 2 works atm. Maybe because I have ' before_action :authenticate_user! ' in my ApplicationController ? I really appreciate your explanation, thanks again!! – Witta Feb 19 '21 at 13:07
  • @Witta Did you notice *"WARNING!! NOTICE THAT THE `Scope` CLASS ENDS HERE!!!"*? Your original policy isn't doing what you think it is, because you've defined methods in the wrong class. – Tom Lord Feb 19 '21 at 13:31
  • And as I said in my post, *authentication* and *authorization* are different things. They are independent. Don't mix them up. – Tom Lord Feb 19 '21 at 13:32