Intent
Lightweight authorization library, that let you choose where and how you write your rules
Cant is simple : you can use it in any existing or future framework (provided names don't clash)
Cant is small : around 120 lines of code, has no dependencies but for testing
Cant can be configured at class level, and support basic configuration inheritance
What does it look like ?
in an existing model
class User include Cant::Embeddable cant do |action, code| (action == :post and Code === code) if drunken? end end deny {bob.cant? :post, kata}
in a separate model
class Permission include Cant::Embeddable cant do |user, action, resource| (action == :post and Code === resource) if user.drunken? end end assert {.cant? jackie, :post, kata}
in a rails controller
class ApplicationController < ActionController::Base include Cant::Embeddable alias_method :authorize_user!, :die_if_cant! helper_method :cant? rescue_from Cant::AccessDenied do |error| flash[:error] = error. redirect_to request.referer end end class KatasController < ApplicationController before_filter :authenticate_user!, :authorize_user! cant do |request| current_user.drunken? if request.method == 'POST' end end
in a rack middleware ... I have not experimented yet ...
breaking authorization into small independent pieces is valuable, and you do not need a library for that, though using Cant::Editable and Cant::Questionable mixins can help you do that
Concepts
Cant is simply put a reduce (or fold) on a list of Rules.
- Rule
a tuple of functions predicate, die
- rules
a list of Rules
- fold
function defining how rules are traversed, and how each predicate is evaluated, and what is the result
Cant provide two fold functions, returning both first Rule that predicates, or nil
- predicate
default rationale is : true means cant
- die
a function, that can raise or return any result suitable to your need
- cant?
calls fold function to operate on rules
- die_if_cant!
calls fold function to operate on rules, and calls die function on result
How do I define rules ?
First, choose where you want to define your list, and what information a Rule will need
The point of cant is to define a lists of similar rules together, so that they will require similar informations
A list of rules can be embedded in an existing class using Cant::Embeddable mixin
- define rule and functions at class level
- evaluate predicates at instance level
Then there is one list of rules for any instance of this class, and instance evaluation leads to terser rules
Note that a list of rules can be shared by many inquirers, either explicitly or by using class instance variable inheritance feature
Default Values for module configuration
Cant module has "reasonable" default values for fold, die, rules
The Cant::Editable module gather methods to configure a Cant engine (fold, die, rules), and defaults to Cant module values
Inheritance
Cant support basic inheritance functionality for configuration with the Cant::Embeddable module ... Ouch what that means ?
Given Admin inherits from User
And User.die {"I'm not dead!"}
Then assert {Admin.die.call == "I'm not dead!"} is true
Well, have a look at read documentation and source code embeddable.rb if you are having trouble with this functionality
What is the arity of a predicate function ?
You are free to pick one that suit your needs
There are a couple of things to drive your choice :
params of cant? are passed to predicate
params of die_if_cant! are passed to predicate and die
number and order of params of in a rule list should be the same
the context of predicate evaluation (that is defined in fold function) the receiver methods will be available in function
a container can be a handy parameter (Hash) env, params
a block is a proc, and can use default values for params
So pick your own semantic, or grow an existing one
How do I verify authorization ?
Cant very meaning is : you can unless you cant, and you define what you cant
Defining not or negative ability require some thinking, and I believe we can do it :)
Use cant? method to check
Use die_if_cant! method to check and run die code
When you check, provide the list of parameters you specified in the list of rules you want to verify
Inspired from
cancan, as I started with it and saw that it did not functioned in presence of mongoid as of < 1.5 ... so I planned to do something no dependent of a model implementation
learn you some erlang?, for the fold illustrations had great impact
Howard and Nunemaker for inheritable class instance variables
Does it work on my interpreter?
rspec
specs are green on mri : [1.9.2-p0, 1.8.7-p302]
There is few lines of code and concept is simple : fold a list of functions... Though, we can make it better and lesser, cant we ?
1.9.2 coverage
can be ran with
COVERAGE=true rspec /spec
Licence
Is MIT, available in source code
Help|Contribute
Fill an item in tracker
Add a page on wiki
Add a spec, open a pull request on topic branch, commit granted on first accepted patch