Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
DSL-based permission rules for easy authorization logic in Rails
Ruby
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib
spec
README
can_do-0.1.0.gem
can_do.gemspec

README

Can Do

DSL-based permission rules for Rails

Example:

    class Permission
      def self.define_rules
        CanDo.setup do
          can :index, User do
            rule("You must be logged in.") {User.current}
            rule("You must have an active account to do this.") {User.current.active?}
          end
      
          can :show, User do
            cascade :index          #inherit the logged in and active rules from :index
            rule("You may not view others' accounts if they are private.") do |user| 
              !user.private? || user == User.current || User.current.admin?
            end
          end
      
          can :update, User do
            cascade :index          #inherit the logged in and active rules from :index
            rule("You may not update others' accounts.") {|user| user == User.current || User.current.admin?}
          end
      
          can :delete, User do
            cascade :update
          end
      
          can :create, UserInterest do
            # You may create an interest if you have permission to update that user.
            cascade :update, {|interest| interest.user} 
          end
        end
      end
    end

application.rb: 
    ActionDispatch::Callbacks.to_prepare do
      Permission.define_rules       #allows rules to be reloaded when classes are reloaded
    end

users_controller.rb:
    def index
      require_permission! :index, User  #this will raise a CanDo::PermissionError if permission is denied
      ...
    end
  
    def show
      @user = User.find(params[:id])
      require_permission! :show, user   #this will raise a CanDo::PermissionError if permission is denied
      ...
    end
  
application_controller.rb:
    rescue_from CanDo::PermissionError do |error|
      render :text => "Permission denied: #{error.message}"
    end
    
    before_filter :initialize_current_user
    
    def initialize_current_user
      User.current = your_code_goes_here
    end
    
user.rb
    def self.current=(value)
      Thread.current["User.current"] = value
    end

    def self.current
      Thread.current["User.current"]
    end

users/index.haml
    %ul
      - @users.each do |user|
        - can?(:show, user) do
          %li
            = link_to user.name, user_path(user)
            - if can?(:update, user) do
              = link_to "Edit", edit_user_path(user)
    - can?(:create, User) do
      = link_to "Add User", new_user_path

    
To test your permission logic, simply call CanDo.reason(:verb, object) and test that the reason is what you expect. Make sure to test all rules inherited from cascades as well. Without this, it's easy for cascades to introduce unintended consequences.
          

Special thanks to cancan, upon which Can Do is loosely based. Important differences:
  * For large permission sets, cancan slows down dramatically. Can Do uses hash-based lookups, which dramatically reduces performance overhead.
  * Can Do is far more expressive, allowing user-friendly explanations for failures.
  * Can Do has explicit support for cascading rules to reduce repetition.
Something went wrong with that request. Please try again.