Authorization with Pundit
I want share my experience in the group from our Monday meetup yesterday. Let’s refresh the memory:
“do authorization with pundit gem” – Homework here on github issue #12
https://rubygems.org/gems/pundit
We went to this page and bundle it to our Gemfile
gem 'pundit', '~> 1.0.1'
And open up these links on that page.
Homepage: https://github.com/elabs/pundit
Documentation: http://www.rubydoc.info/gems/pundit/1.0.1
Previously we have done the authorization in this way writing on the products_controller
and views/products/
# app/controllers/products_controller.rb
if @product.user == current_user
and then also implemented after that the same feature in this way
# app/models/product.rb
def user_owns?(u)
self.user == u
end
def user_can_change?(u)
self.user_owns?(u)
end
# app/controllers/products_controller.rb
if @product.user_owns?(current_user)
What about pundit now? We were going to implement the feature with pundit. By documentation, we did following process. (After adding gem 'pundit'
to Gemfile and running bundle install
on console.)
class ApplicationController < ActionController::Base
include Pundit
...
$ rails g pundit:install
That generates a file application_policy.rb
and the documentation example code looks like this.
# app/policies/application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
...
def update?
false
end
...
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def update?
user.admin? or not record.published?
end
end
And of course this code was not working on our code, to solve the issue we tried following code and it worked (override update?
method)
# app/policies/application_policy.rb
class ApplicationPolicy
attr_reader :user
def initialize(user)
@user = user
end
...
def update?
false
end
...
# app/policies/post_policy.rb
class ProductPolicy < ApplicationPolicy
attr_reader :user, :product
def initialize(user, product)
super(user)
@product = product
end
def update?
user == product.user
end
end
(By the way, following code works also when I tried just now)
# app/policies/application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, :record)
@user = user
@record = record
end
...
def update?
false
end
...
# app/policies/post_policy.rb
class ProductPolicy < ApplicationPolicy
# attr_reader :user, :product
# def initialize(user, product)
# super(user)
# @product = product
# end
def update?
user == record.user
end
end
However we changed the controller & views based using methods pundit gives us.
# app/controllers/products_controller.rb
before_action :set_product, only: [:show, :edit, :update, :destroy]
...
def update
# here runs set_product as before_action
authorize @product
...
def edit
authorize @product
end
...
private
def set_product
@product = Product.find(params[:id])
end
And then by the way (this was my case) it didn’t work! couldn’t find why! After my panicking, Sean found I had those lines from the last week:
# app/controllers/products_controller.rb
before_action :authorize_user, only: [:edit, :update, :destroy]
...
def authorize_user
redirect_to products_url, alert: "not your product!" unless @product.user_can_change?(current_user)
After this, when we commented them out, and applied it in views and controllers, it worked as expected.
# app/controllers/products_controller.rb
...
def edit
authorize @product
end
def update
authorize @product
end
...
<!-- app/views/products/index.html.erb -->
<table class="table">
<% @products.each do |product| %>
<tbody>
<% if policy(product).update? %>
<td><%= link_to 'Edit', edit_product_path(product), class: "btn btn-sm btn-default" %></td>
<td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-sm btn-danger" %></td>
<% end %>
In addition, the authorize
method simply gives Pundit::NotAuthorizedError
error, when it’s not authorized (not the flash message we implemented the last time)
# app/controllers/products_controller.rb
before_action :authorize_user, only: [:edit, :update, :destroy]
...
def authorize_user
redirect_to products_url, alert: "not your product!" unless policy(@product).update?
end
However, Sako showed us his code and it’s different from mine (ours) you can check his repo
https://github.com/LARailsLearners/sakoh_app
Let’s review other members’ code. Simply open their repo on our organization page. And let’s share resource links helped you solve problems.
https://github.com/RailsApps/rails-devise-pundit
We found this github repo link (from pundit documentation) as example app with devise and pundit.
you can check repo of my new_app, what we worked yesterday :) by the way.
https://github.com/LARailsLearners/kangkyu_new_app
Thank you for coming, Sako, Jen, Sean, James, Eddie, Kristiana, Kang-Kyu, and anybody missing.