Getting started with Ruby on RailsRoutingActiveRecordViewsActiveRecord MigrationsRails Best PracticesNaming ConventionsActionCableActiveModelUser Authentication in RailsActiveRecord AssociationsActiveRecord ValidationsActiveRecord Query InterfaceActionMailerRails generate commandsConfigurationI18n - InternationalizationUsing GoogleMaps with RailsFile UploadsCachingActionControllerConfigurationSafe ConstantizeRails 5Authorization with CanCanMongoidGemsChange default timezoneAsset PipelineUpgrading RailsActiveRecord LockingDebuggingConfigure Angular with RailsRails loggerPrawn PDFRails APIDeploying a Rails app on HerokuActiveSupportForm HelpersActiveRecord TransactionsRSpec and Ruby on RailsDecorator patternElasticsearchReact with Rails using react-rails gemRails Cookbook - Advanced rails recipes/learnings and coding techniquesMultipurpose ActiveRecord columnsClass OrganizationShallow RoutingModel states: AASMRails 5 API AutheticationTesting Rails ApplicationsActive JobsRails frameworks over the yearsAdd Admin PanelNested form in Ruby on RailsFactory GirlImport whole CSV files from specific folderTools for Ruby on Rails code optimization and cleanupActiveJobActive Model SerializersRails Engine - Modular RailsSingle Table InheritanceActiveRecord TransactionsTurbolinksFriendly IDSecurely storing authentication keysAuthenticate Api using DeviseIntegrating React.js with Rails Using HyperloopChange a default Rails application enviornmentReserved WordsRails -EnginesAdding an Amazon RDS to your rails applicationPayment feature in railsRails on docker

Authorization with CanCan

Other topics

Remarks:

Before using CanCan don't forget to create Users either by devise gem or manually. To get maximum functionality of CanCan do create an Admin user.

Getting started with CanCan

CanCan is a a popular authorization library for Ruby on Rails which restricts user access to specific resources. The latest gem (CanCanCan) is a continuation of the dead project CanCan.

Permissions are defined in the Ability class and can be used from controllers, views, helpers, or any other place in the code.

To add authorization support to an app, add the CanCanCan gem to the Gemfile:

gem 'cancancan'

Then define the ability class:

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
  end
end

Then check authorization using load_and_authorize_resource to load authorized models into the controller:

class ArticlesController < ApplicationController
  load_and_authorize_resource

  def show
    # @article is already loaded and authorized
  end
end

authorize! to check authorization or raise an exception

def show
  @article = Article.find(params[:id])
  authorize! :read, @article
end

can? to check if an object is authorized against a particular action anywhere in the controllers, views, or helpers

<% if can? :update, @article %>
  <%= link_to "Edit", edit_article_path(@article) %>
<% end %>

Note: This assumes the signed user is provided by the current_user method.

Defining abilities

Abilities are defined in the Ability class using can and cannot methods. Consider the following commented example for basic reference:

class Ability
  include CanCan::Ability

  def initialize(user)
    # for any visitor or user
    can :read, Article

    if user
      if user.admin?
        # admins can do any action on any model or action
        can :manage, :all
      else
        # regular users can read all content
        can :read, :all
        # and edit, update and destroy their own user only
        can [:edit, :destroy], User, id: user_id
        # but cannot read hidden articles
        cannot :read, Article, hidden: true
      end
    else
      # only unlogged visitors can visit a sign_up page:
      can :read, :sign_up
    end
  end
end

Handling large number of abilities

Once the number of abilities definitions start to grow in number, it becomes more and more difficult to handle the Ability file.

The first strategy to handle these issue is to move abilities into meaningful methods, as per this example:

class Ability
  include CanCan::Ability

  def initialize(user)
    anyone_abilities 

    if user
      if user.admin?
        admin_abilities
      else
        authenticated_abilities
      end
    else
      guest_abilities
    end
  end

  private
  
  def anyone_abilities
    # define abilities for everyone, both logged users and visitors
  end

  def guest_abilities
    # define abilities for visitors only
  end

  def authenticated_abilities
    # define abilities for logged users only
  end

  def admin_abilities
    # define abilities for admins only
  end
end

Once this class grow large enough, you can try breaking it into different classes to handle the different responsibilities like this:

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    self.merge Abilities::Everyone.new(user)

    if user
      if user.admin?
        self.merge Abilities::Admin.new(user)
      else
        self.merge Abilities::Authenticated.new(user)
      end
    else
      self.merge Abilities::Guest.new(user)
    end
  end
end

and then define those classes as:

# app/models/abilities/guest.rb
module Abilities
  class Guest
    include CanCan::Ability

    def initialize(user)
      # Abilities for anonymous visitors only
    end
  end
end

and so on with Abilities::Authenticated, Abilities::Admin or any other else.

Quickly test an ability

If you'd like to quickly test if an ability class is giving the correct permissions, you can initialize an ability in the console or on another context with the rails environment loaded, just pass an user instance to test against:

test_ability = Ability.new(User.first)
test_ability.can?(:show, Post) #=> true
other_ability = Ability.new(RestrictedUser.first)
other_ability.cannot?(:show, Post) #=> true

More information: https://github.com/ryanb/cancan/wiki/Testing-Abilities

Contributors

Topic Id: 3021

Example Ids: 10259,10260,10261,19034

This site is not affiliated with any of the contributors.