2023-05-30 - Authorization with Pundit
main topics
Pundit checklist
- install gem
rails generate pundit:install
- paste pundit boilerplate in
application_controller.rb
rails generate pundit:policy ModelName
(singular)- edit
app/policies/collective_policy.rb
- show? / new? / create?
- edit? / update? / destroy?
- the index is defined in the
Scope#resolve
(usuallyscope.all
)
- add
authorize @controller
in the controller's actions (probably except index) Controller#index
usingpolicy_scope(Controller)
Steps explained
bundle add pundit
rails generate pundit:install
# creates the 'app/policies/applicatoin_policy.rb'
stuff to paste in app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :authenticate_user!
include Pundit::Authorization
# Pundit: allow-list approach
after_action :verify_authorized, except: :index, unless: :skip_pundit?
after_action :verify_policy_scoped, only: :index, unless: :skip_pundit?
# Uncomment when you *really understand* Pundit!
# rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
# def user_not_authorized
# flash[:alert] = "You are not authorized to perform this action."
# redirect_to(root_path)
# end
private
def skip_pundit?
devise_controller? || params[:controller] =~ /(^(rails_)?admin)|(^pages$)/
end
end
Run rails server
and try to acces an "#index" action.
You should see Pundit::PolicyScopingNotPerformedError
(if you see uninitialized constant ApplicatoinController::Pundit
, you need to bundle && rails s
).
rails generate pundit:policy collective
# generates 'app/policies/collective_policy.rb'
app/policies/collective_policy.rb
class CollectivePolicy < ApplicationPolicy
class Scope < Scope
end
def show?
true
end
end
In the Controller, add the authorize
line. Example
def show
authorize @restaurant
end
Do the same for new
and create
.
For edit
, update
and destroy
, only allow the person who created the resource.
app/policies/restaurant_policy.rb
def edit?
update?
end
def update?
record.user == user
# in my model it was collective.owner
end
def destroy?
record.user == user
end