SlideShare a Scribd company logo
Pundit
simple Rails authorization
Ruby::AZ - July 15, 2014
Bruce White
Software Ops developer
bruce@softwareops.com
We Build Mobile App Systems
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)
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
Pundit
The world��s foremost authority
• Written by Elabs
• Simple, uses pure Ruby classes
• Popular and well maintained
• Second most popular Rails authorization gem
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"
Policies
• Authorization in Pundit is controlled by Policy classes
• Policies go in app/policies
• Policies are PORC
Policy Example
class PostPolicy < ApplicationPolicy
…
def update?
user.admin? or not post.published?
end
…
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
Pundit In Views
<% if policy(@post).update? %>
<%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
Pundit Scopes
class PostPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.where(:published => true)
end
end
end
…
Pundit Scopes In Controllers
def index
@posts = policy_scope(Post)
end
Pundit Scopes In Views
<% policy_scope(@user.posts).each do |post| %>
<p>
<% link_to post.title, post_path(post) %>
</p>
<% end %>
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
…
Pundit Strong Parameters 2/2
class PostsController < ApplicationController
…
private
!
def post_params
params.require(:post).permit(
*policy(@post || Post).permitted_attributes
)
end
…
Pundit with an API 1/4

custom Pundit users
class APIController < ActionController::Base
…
!
private
!
def pundit_user
current_device
end
…
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
…
Pundit with an API 3/4

custom Pundit users
AppInstancePolicy < ApplicationPolicy
…
def update?
app_instance == current.app_instance
end
…
Pundit with an API 4/4
class DeliveriesController < APIController
…
def cancel
@delivery = Delivery.find(params[:id])
authorize @delivery
@delivery.cancel
!
respond_with @delivery
end
…
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
Questions?

More Related Content

Pundit

  • 1. Pundit simple Rails authorization Ruby::AZ - July 15, 2014 Bruce White Software Ops developer bruce@softwareops.com
  • 2. We Build Mobile App Systems
  • 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
  • 8. Policy Example class PostPolicy < ApplicationPolicy … def update? user.admin? or not post.published? end …
  • 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 …
  • 15. Pundit Strong Parameters 2/2 class PostsController < ApplicationController … private ! def post_params params.require(:post).permit( *policy(@post || Post).permitted_attributes ) 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