Skip to content

fogle/can_do

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Can Do

Can Do is a 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

Testing

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.

Comparison

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.

About

DSL-based permission rules for easy authorization logic in Rails

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages