Kantox::Roles
Kantox Roles is the library to transparently handle an authorization. It is
fully backend-agnostic. Current implementation contains a working example
of pundit wrapping.
The main goal is to separate the wheat from the chaff and not to pollute
model/controller classes with authorization stuff.
With Kantox Roles one is to define the rules in one or more yaml files:
'Kantox::Managed':
:yo : 'aspect'
:yo2 : :aspect
:yo3 :
:runner: 'Kantox::Strategies::Wrapper#wrap'
:yo5 :
:lambda: '->(context, im) { 42 }'
:yo6 :
:kantox_managedhandler:
:params: ['param1', 'param2']
'Kantox::WildManaged':
'y*' : :aspect
There are four different kinds of handlers available:
- simple — like
aspect
above. There should be Kantox::Strategies#aspect
module function available. It will be called with parameters context
and
im
supplying the context instance (usually an instance of guarded controller
class) and the name of the guarded method in Module::Class#method
notation.
The aforementioned function should raise an exception of type
Kantox::Strategies::StrategyError
whether the authority check is not passed. - runner — the most powerful yet complicated guard. Should have the
executable module function as parameter in
Module::Class#method
notation.
This function will be called, yielding context
, im
and the guarded
method, converted to proc
as parameters. The typical usage:
def my_runner context, params = nil
fail Kantox::Strategies::MyRunnerError unless check_passed
params[:user] = :demo if demo_mode
params[:credit_card].gsub /\d/, 'X'
yield *params if block_given?
end
As seen above, the full control over context is provided by this guard.
- lambda — the simple lambda, getting
context
and im
. Is actually
a syntax sugar for the simple guard - object — used when there is a need to pass complicated parameters and/or
some other stuff to the guard. The class
Kantox::Managedhandler
must have
a constructor, accepting one argument (the hash of parameters) and to_proc
method, accepting two arguments (context
, im
). The class will be
instantiated with a parameters list and to_proc
would be called on context.
Methods to guard
Wildcard notation is allowed. That said, 'y*'
will guard all the methods
starting with y
, and '*'
will guard everything on the respective class.
Deny ⇒ Allow
As soon as a method guard is specified in yaml file, the method is considered
as guarded. If there was an error finding the guard (class not exists, method
can not be instantiated etc,) the authorization request will be rejected.
Rails integration
require 'kantox/roles'
Kantox::Roles.init Admin::AdminController
Dir['strategies/**/*.yml'].each do |f|
Kantox::Roles.configure f
end
Kantox::Helpers.logger_stopwords File.join 'strategies', 'stopwords.txt'
Kantox::Helpers.info "Strategies were read: #{Kantox::Roles.options}"
Dir[File.expand_path('../../app/policies/**/*', __FILE__)].each do |f|
require f[/(.*?)\.rb$/, 1] unless File.directory? f
end
'Admin::TodosController' :
'*' : :pundit
...
Pundit plug-in
This section shows how to integrate pundit
to act as backend guard.
Everything one needs is to implement policies.
Everything in TodosController
is to be handled by pundit. So, it’s time
to implement our pundit
guard. Besides some syntax sugar stuff it is (complete implementation for kantox-flow
may be seen here:
def pundit context, im
begin
model = context.instance_eval 'controller_path.classify'
policy = PolicyFactory.lookup model.split('::').last
unless policy.new(context.current_user, model).send("#{im.split('#').last}?")
fail PunditError.new(context, im, policy)
end
rescue NameError => e
Kantox::Helpers.err "Error punditing «#{context}». Will reject request.\nOriginal error: #{e}"
throw PunditError.new(context, im)
end
end
module_function :pundit
Needless to say, the above handler is to be written once per backend. Pundit one is shipped with Kantox::Roles
. The last but not least is to specify a strategy:
module Kantox
module Policies
def historic?
@user.admin? and [true, false].sample
end
alias_method :index?, :historic?
end
end
end
The above will randomly accept admin’s requests to a controller. I am pretty
sure one would do more sophisticated check here.
That’s it.
Policies Generator
Kantox::Roles
has it’s generator for creating policies.
Generator invocation
$ bundle exec rails generate kantox:pundit_policy Todo --users Administrator SalesPerson
The above will generate (assuming that Todo
is a proper model name and TodosController
exists:
- Policy itself, in
app/policies
, - Spec for a policy in
spec/policies
, - Default strategy in
strategies
. - [optional] if this is a first run, the
pundit:install
generator will be invoked
to generate default pundit ApplicationPolicy
.
By default all the public controller methods will be guarded with method?
pundit guards,
returning true
if the current user was listed during generation process.
Generated specs
The generator above would generate smart specs, more or less ready to use. For
an example above, it would generate specs, checking whether all the specified
users are allowed to access the controller, and all others are restricted.
Vandal-proof
The generator will gracefully reject a request to damage anything as well as
to generate policies for inexisting controller.
Installation
gem 'kantox-roles'
TODOs
- generate policies and rspecs by
yaml
pointcuts syntax — DSL vs YAML/JSON- automatic tests for pointcuts — by adding them to descriptions
- allow⇒deny vs deny⇒allow
supersupervisor to change rights of supervisorsgroups with roles, or smth morre sophisticated? What?where to store rights? 3rd party?- edit rights from the interface.
Usage
Add a line to configuration
file (or explicitly by calling Kantox::Roles.configure
with either hash, or passing a block.)
Optionally, one may specify a custom handler as denoted by :runner
key in config.
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
to create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
- Fork it ( https://github.com/[my-github-username]/kantox-roles/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request