As I know, Pundit and CanCanCan are the 2 best approaches to adding AUTHORIZATION into a Rails app.
AUTHORIZATION - allow users to perform different actions / see different content based on their roles / other conditions.
I personally just prefer Pundit.
1. Basic installation & usage:
# Gemfile gem "pundit"
# console bundle rails g pundit:install rails g pundit:policy post rails g pundit:policy user
# app/controllers/application_controller.rb include Pundit rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized private def user_not_authorized flash[:alert] = "You are not authorized to perform this action." redirect_to(request.referrer || root_path) end
# app/policies/post_policy.rb class PostPolicy < ApplicationPolicy def index? true # false - nobody has access end def show? @user.has_any_role? :admin, :newuser || @record.user == @user # index? # @user.has_role? :admin end end
# app/controllers/posts_controller.rb def index @posts = Post.order(created_at: :desc) authorize @posts end def show authorize @post end
2. Pundit policy scopes
This allows you to let users with different authorizations to see different scopes of items.
Below example - admins can see all posts, other users can see posts that have content not blank.
# app/policies/post_policy.rb class PostPolicy < ApplicationPolicy class Scope < Scope def resolve if @user.has_role? :admin scope.all else scope.where.not(content: "") end end end
# app/controllers/posts_controller.rb def index @posts = policy_scope(Post).order(created_at: :desc) authorize @posts end
In the above case
@record = selected post
3. View validation
Allow users different authorizations to see content in a view:
<b>current user can see particular users show page?</b> <%= policy(@user).show? %> <b>current user can see users index?</b> <%= policy(User).index? %> <b>current user can edit a user?</b> <%= policy(User).edit? %> <%= link_to 'Edit user roles', edit_user_path(user) if policy(User).edit? %>
4. Advanced controller setup
Instead of adding
authorize @posts or
authorize @post to each controller action,
just list the actions that you want to authorize either in a before_action:
# app/controllers/posts_controller.rb before_action :authorize_valuations, only: %i[edit update destroy] # after_action :authorize_valuations, except: %i[create report_quotes] def authorize_valuations authorize(@valuations || @valuation) end