3. Authorization vs Authentication
Today we’re talking about authorization
• Authentication is about who you are
• Authorization is about what you can do
• To work, authorization requires authentication (need
to know who you are to determine what you can do)
4. Rails Authorization: A Retrospective
• 10 years ago, in the beginning of The Enlightenment,
authorization was a “roll your own” thing
• 4 years ago, CanCan shook the Rails world to the
core
• A year ago, Pundit arrives on the scene
5. Pundit
The world’s foremost authority
• Written by Elabs
• Simple, uses pure Ruby classes
• Popular and well maintained
• Second most popular Rails authorization gem
6. Pundit Setup
• Include in Gemfile
• Optionally run generator
• Include in ApplicationController
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery
after_action :verify_authorized, :except => :index
after_action :verify_policy_scoped, :only => :index
…
rails g pundit:install
gem "pundit"
7. Policies
• Authorization in Pundit is controlled by Policy classes
• Policies go in app/policies
• Policies are PORC
9. Pundit In Controllers
def update
@post = Post.find(params[:id])
authorize @post
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
10. Pundit In Views
<% if policy(@post).update? %>
<%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
11. Pundit Scopes
class PostPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.where(:published => true)
end
end
end
…
12. Pundit Scopes In Controllers
def index
@posts = policy_scope(Post)
end
13. Pundit Scopes In Views
<% policy_scope(@user.posts).each do |post| %>
<p>
<% link_to post.title, post_path(post) %>
</p>
<% end %>
14. Pundit Strong Parameters 1/2
class PostPolicy < ApplicationPolicy
…
def permitted_attributes
if user.admin? || user.owner_of?(post)
[:title, :body, :tag_list]
else
[:tag_list]
end
end
…
16. Pundit with an API 1/4
custom Pundit users
class APIController < ActionController::Base
…
!
private
!
def pundit_user
current_device
end
…
17. Pundit with an API 2/4
custom Pundit users
class APIController < ActionController::Base
…
Context = Struct.new(:app, :device, :app_instance, :user)
…
private
!
def pundit_user
Context.new(
current_app, current_device,
current_app_instance, current_user
)
end
…
18. Pundit with an API 3/4
custom Pundit users
AppInstancePolicy < ApplicationPolicy
…
def update?
app_instance == current.app_instance
end
…
19. Pundit with an API 4/4
class DeliveriesController < APIController
…
def cancel
@delivery = Delivery.find(params[:id])
authorize @delivery
@delivery.cancel
!
respond_with @delivery
end
…
20. Pundit Is PORC
You can:
• Encapsulate a set of permissions into an included module
• Use alias_method to make permissions behave the same
• Inherit from a base set of permissions
• Use metaprogramming