Search code examples
ruby-on-railspundit

Pundit keeps asking me to be logged in


I am building a simple website where people can upload their poems and writing new ones.

I am trying to use Pundit so that:

  • Everyone can see all the poems/poetries (in index)
  • Only logged in user can create a poetry
  • Only the user who create the poetry can updated OR delete it

I followed the official following documentations but I still need to login to perform any action in my controller.

I am not sure what I am doing wrong since I am following the docs and it seems a copy/paste type of work.

My code:

poetries_controller.rb

  class PoetriesController < ApplicationController
  before_action :set_poetry, only: [:show, :edit, :update, :destroy]

  def index
    @poetries = policy_scope(Poetry).order("RANDOM()").limit(30)
  end

  def show
  end

  def new
    @poetry = Poetry.new
    authorize @poetry
  end

  def create
    Poetry.create(poetry_params)
    authorize @poetry
    redirect_to poetries_path
  end

  def edit

  end

  def update
    @poetry.save
    redirect_to poetry_path(@poetry)
  end

  def destroy
    @poetry.destroy
    redirect_to poetries_path
  end

  private

  def poetry_params
    params.require(:poetry).permit(:title, :author, :body)
  end

  def set_poetry
    @poetry = Poetry.find(params[:id])
    authorize @poetry
  end
end

application_controller.rb

class ApplicationController < ActionController::Base
  include Pundit


  protect_from_forgery with: :exception
  before_action :authenticate_user!

  after_action :verify_authorized, :except => :index, unless: :devise_controller?
  after_action :verify_policy_scoped, :only => :index, unless: :devise_controller?

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private

  def user_not_authorized
    flash[:alert] = "Non sei autorizzato a eseguire questa azione"
    redirect_to(root_path)
  end

end

poetry_policy.rb

class PoetryPolicy < ApplicationPolicy

  class Scope < Scope
    def resolve
      scope.all
    end
  end


  def show?
    true  # Anyone can view a poetry
  end

  def create?
    true  # Anyone can create a poetry
  end

  def update?
    record.user == user  # Only poetry creator can update it
  end

   def destroy?
    record.user == user  # Only poetry creator can update it
  end
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.all
    end
  end
end

index.html.erb

  <div class="container">
    <div class="row">

      <% @poetries.each do | poetry| %>

        <div class="col-xs-12 col-sm-4">
          <% if policy(poetry).show? %>
            <%= link_to poetry_path(poetry) do %>
              <div class="card">
                <div class="card-description">
                  <h2> <%=poetry.title=%> </h2>
                  <% a = sanitize(poetry.body.truncate(170), tags: %w(br)) %></p>
                  <p> <%= a %></p>
                  <p><i><%=poetry.author=%><i></p>
                </div>
              </div>
            <% end %>
          <% end %>
        </div>
      <% end %>
      <!-- </div> -->
    </div>
  </div>

Solution

  • You call before_action :authenticate_user! in ApplicationController, that's why Devise doesn't allow you see poetries#index. It's not a Pundit problem at all.

    Move this callback from ApplicationController to needed controllers, where you really want to check authentication. And restrict it with particular actions.

    class PoetriesController < ApplicationController
      before_action :authenticate_user!, except: [:index, :show]
    end