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
({
:actor => User,
:verb => :edit,
:actor_proc => Proc.new { |user|
if user.role == 'administrator'
true
else
false
end
}
})
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
({
:actor => User,
:verb => :edit,
:proc => Proc.new { |user, thing|
if user.role == 'administrator' or user == thing.owner
true
else
false
end
}
})
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