CanTango Permits
The Permit system for CanTango.
Permits are a way to partition Ability rules into logical units.
CanTango Permits includes:
- a basic set of useful permits
- a permit engine to execute the permits
The Permits systems comes with a few useful Permit types out-of-the-box, but you are free to develop your own to suit your needs.
Built-in permit types
The set of buit-in Permit types include:
- User
- UserType
- AccountType
- Special
The CanTango roles system adds Permit types for the following:
- Role
- RoleGroup
Custom permit types
You can easily customize or create your own types of Permits to suit your needs! The Permit system will introspect the ability candidate and see which Permits apply and then build and execute these permits automatically, merging all the resulting rule sets into one. The simplest way to create a custom permit types is to subclass the Attribute permit as shown below. To create more complex permit types that go beyond mapping to an attribute, please look at the code of any of the built-in permits (such as Attribute Permit) to gain an insight for how to do this.
Example of a custom permit type:
class MembershipPermit < CanTango::Permit::Attribute
class Builder < CanTango::Permit::Attribute::Builder
attribute :membership
end
def self.inherited(base_clazz)
register base_clazz, :name => attribute_name(base_clazz)
end
# optional override of inference of attribute via class name
attribute :membership
end
Permit system
The permit system iterates over all the registered types of permits and executes all registered permits of each type. The result is a set of Ability rules that work with the CanCan Ability mechanism.
Defining Permits and Licenses in your app
The app/permits
folder will be added to Rails autoloading.
The directory layout should be the following:
- /app
- /permits
- /permit
- /role
- editor.rb
- /role_group
- editors.rb
- /user_type
- admin.rb
-/admin (account)
- /role
- editor.rb
Default :editor Role permit (scope less):
module Permit::Role
class Editor < CanTango::Permit::Role
def calc_rules
can :edit, Post
end
module Cache
def calc_rules
can :edit, Post
end
end
end
end
The :editor Role permit applicable for the Admin scope. Typically the scope is linked to the account (but doesn't have to be).
module Permit::Admin::Role
class Editor < CanTango::Permit::Role
def calc_rules
can :edit, Post if session[:edit_mode]
end
modes :no_cache
end
end
Here the #modes
macro is used to indicate that this Permit should only be executed for the :no_cache
mode.
Another option is fx to wrap the Permit class inside the Account class namespace like this:
- /app
- /models
- /admin_account
- editor_role_permit.rb
- admin_account.rb
class AdminAccount < ActiveRecord::Base
class EditorRolePermit < CanTango::Permit::Role
tango_permit :name => :editor, :type => :role, :ns => :admin
def calc_rules
can :edit, Post if session[:edit_mode]
end
modes :no_cache
end
end
By default this is not supported out-of-the-box, hence you have to use the #tango_permit
macro to tell CanTango exactly how to understand this custom class namespace layout.
Note: This is NOT a recommended approach.
Licenses
Licenses have their own namespace folder license
inside the app/permits
folder. The default (scope less) licenses are placed directly in the license
folder.
- /app
- /permits
- /license
- blogging.rb
- /admin
- blogging.rb
Default :blogging License (scope less):
module License
class Blogging < CanTango::License
module Cache
def calc_rules
can [:create, :edit], Post
end
end
module NoCache
def calc_rules
can :publish, Post if session[:publishing] == :on
end
end
end
end
The :editor License applicable for the Admin scope. Typically the scope is linked to the account (but doesn't have to be).
module License::Admin
class Blogging < CanTango::License
def calc_rules
can :edit, Post
end
modes :all
end
end
Install 'cantango-permits' gem
gem install cantango-permits
Or insert into Gemfile
gem 'cantango-permits'
Run bundler in a terminal/console from the folder of your Gemfile (root folder of app)
$ bundle
Usage
require 'cantango/permits'
require 'cantango/permit_engine'
Configuration
Turn on/off: Enable and disable types of permits and specific permits
CanTango.config.permits do |permits|
# which types of permits to enable
permits.types.enable :user_type, :account_type
permits.enable_all_for :account_type
permits.types.disable :user_type, :account_type
permits.disable_for :user_type, :admin, :editor
end
Registration: Which permits have been registered (and for which types)
CanTango.config.permits do |permits|
permits.registry_for :account_type # Registry for :account_type permits
permits.registered_for :account_type # names of AccountType permits
permits.all
permits.show_all
end
Debug: Which permits allowed/denied specific actions for specific candidates to be taken
CanTango.config.permits do |permits|
permits.allowed candidate, actions, subjects, *extra_args
permits.denied candidate, actions, subjects, *extra_args
end
Permits Engine
The Permits engine can be configured as any other CanTango Engine:
Use the on!
and off!
methods to enable to disable use of the engine.
Use the mode=
to set the execution mode.
CanTango.config.engine(:permits) do |engine|
# toggle engine on/off
engine.on!
engine.on?
engine.off!
engine.modes.valid # => [:cache, :no_cache, :both]
# set execution modes
engine.modes.register :cache, :no_cache
engine.modes.registered # => [:cache, :no_cache]
end
Permits finder
Will look up a particular registered Permit in the Permit registry (see Configuration). Permits are registered automatically by an inheritance hook. If you want to override this, you need to register your class directly (manually) with the the permits registry.
module CanTango::Finder::Permit
class Base
def initialize name, type
@name, @type = [name, type]
end
end
end
Permits Factory
Builds and returns a list of all enabled permits of a specific type
module CanTango::Factory
class Permits
def initialize ability
@ability = ability
end
# @return Array<Permit>
def create
permits.build
end
end
end
Categories
Categories are loaded from a Yaml file (by default categories.yaml
). The loader CanTango::Loader::Categories
is based on the CanTango::Loader::Yaml
from cantango-core.
The parser CanTango::Parser::Categories
parses the yaml into a hash referencing constants (models).
Macros
The macro #tango_permit
can be used to attempt to register a Permit class with the Permits registry based on naming conventions and options passed in.
class MySuperPermit < CanTango::Permit::Base
tango_permit :name => :super, :type => :user_type, :account => :admin
# the internals
end
Engine
module CanTango::Engine
class Permits
def initialize ability
super
end
def calc_rules
# push result of each permit type execution into main ability rules array
permits.each_pair do |type, permits|
perm_rules = executor(type, permits).execute!
rules << perm_rules if !perm_rules.blank?
end
end
def executor type, permits
CanTango::Ability::Executor::PermitType.new self, type, permits(type)
end
def permits type
@permits ||= permits_factory(type).build!
end
def permits_factory type
@permits_factory ||= CanTango::Factory::Permits.new self, type
end
end
end
Design goals
This CanTango extension should have the least amount of dependencies on other extensions.
Contributing to Cantango Permits
- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
- Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
- Fork the project
- Start a feature/bugfix branch
- Commit and push until you are happy with your contribution
- Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
- Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
Copyright
Copyright (c) 2011 Kristian Mandrup. See LICENSE.txt for further details.