Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Permissions framework for Ruby objects

branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 autotest
Octocat-spinner-32 features
Octocat-spinner-32 lib
Octocat-spinner-32 spec
Octocat-spinner-32 .gitignore
Octocat-spinner-32 .rspec
Octocat-spinner-32 Gemfile
Octocat-spinner-32 Gemfile.lock
Octocat-spinner-32 LICENSE.txt
Octocat-spinner-32 README.mdown
Octocat-spinner-32 Rakefile
README.mdown

Cannibal

A simple permissions system for declaring and querying permissions between Ruby objects.

Background

Cannibal is based around defining interactions between Actors and Subjects.

An Actor participates in your system as an agent that "does" things. Cannibal lets you declare what each Actor can do, and Actors are queried to determine whether or not they are permitted to perform a particular action.

You can turn any class into an Actor by including Cannibal::Actor within it.

A Subject participates in your system as something that is acted upon. You declare for each Subject the conditions under which each Actor may or may not interact with it.

You can turn any class into a Subject by including Cannibal::Actor within it.

If for example you are using Cannibal in a Rails application, an example of an Actor might be a User model. An example of a Subject might be a Task model. You may want all Users in the system to be able to view all of the available Tasks, but you may want to restrict tasks to only be editable by their creators.

Permissions can be set at various levels, starting at the Class level and getting finer grained right down to the field or method level of your models.

In addition, you can specify static permissions (no User may modify a Task) or dynamic permissions that are evaluated at query time (ie. a specific user may or may not be able to modify a specific task, based on whatever rules you put forth).

Sample Scenarios

Class-level permissions:

class User
  include Cannibal::Actor
end

class Thing
  include Cannibal::Subject
  allow User, :edit
end

@user = User.new
@thing = Thing.new

puts "Yay!" if @user.can? :edit, @thing

Actor object-level permissions:

class User
  include Cannibal::Actor
  attr_accessor :role
end

class Thing
  include Cannibal::Subject
  permission({
    :actor => User,
    :verb => :edit,
    :actor_proc => Proc.new { |user|
      user.role == 'administrator'
    }
  })
end

@user = User.new; @user.role = 'user'
@admin = User.new; @user.role = 'administrator'
@thing = Thing.new

puts "Back off!" unless @user.can? :edit, @thing
puts "Yay!" if @admin.can? :edit, @thing

Actor and subject object-to-object level permissions:

class User
  include Cannibal::Actor
  attr_accessor :role
end

class Thing
  include Cannibal::Subject
  attr_accessor :owner
  permission({
    :actor => User,
    :verb => :edit,
    :proc => Proc.new { |user, thing|
      user.role == 'administrator' or user == thing.owner
    }
  })
end

@user_a = User.new; @user.role = 'user'
@user_b = User.new; @user.role = 'user'
@admin = User.new; @user.role = 'administrator'

@thing = Thing.new; @thing.owner = @user_a

puts "Back off!" unless @user_b.can? :edit, @thing
puts "Yay!" if @user_a.can? :edit, @thing
puts "Yay!" if @admin.can? :edit, @thing
Something went wrong with that request. Please try again.