Search code examples
ruby-on-railsrubyinheritancecontrollerpundit

Pundit::NotAuthorizedError - ApplicationPolicy inheritance doesn't work


I am installing pundit on my app. My RestaurantPolicy file inherits from the ApplicationPolicy one (which by default gives access to none of the methods). When changing the methods in RestaurantPolicy (from false to true) it seems to have no effect as I still have access to none of the methods. BUT when changing falses to trues in the ApplicationPolicy file...I have the accesses ! Any idea why methods in the RestaurantPolicy file do not override the ApplicationPolicy's ones ? Thank youuu!!

application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :authenticate_user!
  include Pundit

  # Pundit: white-list approach.
  after_action :verify_authorized, except: :index, unless: :skip_pundit?
  after_action :verify_policy_scoped, only: :index, unless: :skip_pundit?

  private

  def skip_pundit?
    devise_controller? || params[:controller] =~ /(^(rails_)?admin)|(^pages$)/
  end
end

restaurants_controller.rb

class RestaurantsController < ApplicationController
  before_action :set_restaurant, only: [:show, :edit, :update, :destroy]

  def index
    @restaurants = Restaurant.all
    authorize @restaurants
  end

  def show
  end

  def new
    @restaurant = Restaurant.new
    authorize @restaurant
  end


  def edit
  end


  def create
    @restaurant = Restaurant.new(restaurant_params)
    @restaurant.user = current_user
# OU :     @restaurant = current_user.restaurants.build(restaurant_params)


    respond_to do |format|
      if @restaurant.save
        format.html { redirect_to @restaurant, notice: 'Restaurant was successfully created.' }
        format.json { render :show, status: :created, location: @restaurant }
      else
        format.html { render :new }
        format.json { render json: @restaurant.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @restaurant.update(restaurant_params)
        format.html { redirect_to @restaurant, notice: 'Restaurant was successfully updated.' }
        format.json { render :show, status: :ok, location: @restaurant }
      else
        format.html { render :edit }
        format.json { render json: @restaurant.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @restaurant.destroy
    respond_to do |format|
      format.html { redirect_to restaurants_url, notice: 'Restaurant was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

    def set_restaurant
      @restaurant = Restaurant.find(params[:id])
    end

    def restaurant_params
      params.require(:restaurant).permit(:name)
    end
end

restaurant.rb

class Restaurant < ApplicationRecord
  belongs_to :user
end

user.rb

class User < ApplicationRecord

devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :restaurants end

application_policy.rb

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def show?
    scope.where(:id => record.id).exists?
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  def scope
    Pundit.policy_scope!(user, record.class)
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      scope
    end
  end
end

restaurant_policy.rb

class RestaurantPolicy < ApplicationPolicy
  class Scope < Scope
    def index?
      true
    end

    def show?
      scope.where(:id => record.id).exists?
    end

    def create?
      true
    end

    def new?
      create?
    end

    def update?
      true
    end

    def edit?
      update?
    end

    def destroy?
      true
    end
  end
end

Solution

  • You must define your policy methods out of RestaurantPolicy::Scope, like so:

    class RestaurantPolicy < ApplicationPolicy
      class Scope < Scope
        def index?
          true
        end
      end
    
      # Notice we have closed the definition of Scope class.
      def show?
        scope.where(:id => record.id).exists?
      end
    
      def create?
        true
      end
    
      def new?
        create?
      end
    
      def update?
        true
      end
    
      def edit?
        update?
      end
    
      def destroy?
        true
      end
    end