Why not adopt me?
NAME
Tree::Authz - inheritance-based authorization scheme
VERSION
0.01
DEVELOPER RELEASE
There's only a few methods in this class, so the interface should stabilise pretty quickly. Feel free to suggest changes. For the moment, keep a close eye on the CHANGES file when updating.
SYNOPSIS
use Tree::Authz;
my $groups = { superuser => [ qw( spymasters politicians ) ],
spymasters => [ qw( spies moles ) ],
spies => [ 'informants' ],
informants => [ 'base' ],
moles => [ 'base' ],
politicians => [ 'citizens' ],
citizens => [ 'base' ],
};
my $authz = Tree::Authz->setup_hierarchy( $groups, 'SpyLand' );
my $superuser = $authz->get_group( 'superuser' );
my $spies = $authz->get_group( 'spies' );
my $citizens = $authz->get_group( 'citizens' );
my $base = $authz->get_group( 'base' );
$spies ->setup_permissions( [ qw( read_secrets wear_disguise ) ] );
$citizens->setup_permissions( 'vote' );
$base ->setup_permissions( 'breathe' );
foreach my $group ( $superuser, $spies, $citizens, $base ) {
foreach my $ability ( qw( unspecified_ability
spy
spies
read_secrets
wear_disguise
vote
breathe
can ) ) {
if ( $group->can( $ability ) ) {
printf "%s can '%s'\n", $group->group_name, $ability;
}
else {
printf "%s cannot '%s'\n", $group->group_name, $ability;
}
}
}
# prints:
superuser can 'unspecified_ability' # superpowers!
superuser can 'spy'
superuser can 'spies'
superuser can 'read_secrets'
superuser can 'wear_disguise'
superuser can 'vote'
superuser can 'breathe'
superuser can 'can'
spies cannot 'unspecified_ability'
spies can 'spy'
spies can 'spies'
spies can 'read_secrets'
spies can 'wear_disguise'
spies can 'vote'
spies can 'breathe'
spies can 'can'
citizens cannot 'unspecified_ability'
citizens cannot 'spy'
citizens cannot 'spies'
citizens cannot 'read_secrets'
citizens cannot 'wear_disguise'
citizens can 'vote'
citizens can 'breathe'
citizens can 'can'
base cannot 'unspecified_ability'
base cannot 'spy'
base cannot 'spies'
base cannot 'read_secrets'
base cannot 'wear_disguise'
base cannot 'vote'
base cannot 'breathe' # !
base cannot 'can' # !!
# storing code on the nodes of the tree
$spies->setup_abilities( read_secret => $coderef );
print $spies->read_secret( '/path/to/secret/file' );
$spies->setup_plugins( 'My::Spies::Skills' );
$spies->fly( $jet ); # My::Spies::Skills::fly
DESCRIPTION
Class for inheritable, groups-based permissions system (Access Control List).
Custom methods can be placed on group objects. Authorization can be performed either by checking whether the group name matches the required name, or by testing (via can
) whether the group can perform the method required.
Two groups are specified by default. At the top, superusers can do anything ($superuser->can( $action )
always returns a coderef). At the bottom, the base group can do nothing ($base->can( $action )
always returns undef).
All groups are automatically capable of authorizing actions named for the singular and plural of the group name.
METHODS
Methods can be called on classes or instances, except where stated otherwise.
Namespaces and class methods
This class is designed to work in environments where multiple applications run within the same process (i.e. websites under mod_perl
). If the optional namespace parameter is supplied to setup_hierarchy
, the groups are isolated to the specified namespace. All class methods should be called through the class returned from setup_hierarchy
.
If your program is not operating in such an environment (e.g. CGI scripts), then you can completely ignore this parameter, and call class methods either through Tree::Authz
, or through the string returned from setup_hierarchy
(which, funnily enough, will be 'Tree::Authz').
- get_group( $group_name )
-
Factory method, returns a Tree::Authz subclass object.
Sets up two permitted actions on the group - the singular and plural of the group name. This might be too cute, and could change to just the group name in a near future release. Opinions welcome.
- new( $group_name )
-
Alias for
new
. - group_name()
-
Instance method.
Returns the name of the group.
- group_exists( $group_name )
-
Returns true if the specified group exists anywhere within the hierarchy.
- subgroup_exists( $subgroup_name, [ $group_name ] )
-
Method not implemented yet.
Give me a nudge if this would be useful.
Returns true if the specified group exists anywhere in the hierarchy underneath the current or specified group.
- list_groups()
-
Returns an arrayref of all the group names in the hierarchy, sorted by name.
- dump_hierarchy( [ $namespace ] )
-
Get a simple printout of the structure of your hierarchy.
This method
require
s Devel::Symdump.If you find yourself parsing the output and using it somehow in your code, let me know, and I'll find a Better Way to provide the data. This method is just intended for quick and dirty printouts and could change at any time.
- setup_hierarchy( $groups, [ $namespace ] )
-
Class method.
$groups has:
keys - group names values - arrayrefs of subgroup name(s)
Sets up a hierarchy of Perl classes representing the group structure.
The hierarchy will be contained within the $namespace top level if supplied. This makes it easy to set up several independent hierarchies to use within the same process, e.g. for different websites under
mod_perl
.Returns a class name through which group objects can be retrieved and other class methods called. This will be 'Tree::Authz' if no namespace is specified.
- setup_permissions( $cando )
-
Instance method.
Adds methods to the class representing the group. $cando is a single method name, or arrayref of method names. No-op methods are added to the class representing the group:
my $spies = $authz->get_group( 'spies' ); my $cando = [ qw( read_secret wear_disguise ) ]; $spies->setup_permissions( $cando ); if ( $spies->can( 'read_secret' ) ) { warn 'Compromised!'; } warn 'Trust no-one' if $spies->can( 'wear_disguise' );
- setup_permissions_on_group( $group_name, $cando )
-
Class method version of
setup_permissions
. - setup_abilities( $name => $coderef, [ $name2 => $coderef2 ], ... )
-
Instance method.
Adds methods to the class representing the group. Keys give method names and values are coderefs that will be installed as methods on the group class:
my $spies = $authz->get_group( 'spies' ); my %able = ( read_secret => sub { my ($self, $file) = @_; open( SECRET, $file ); local $/; <SECRET>; }, find_moles => sub { ... }, ); $spies->setup_abilities( %able ); if ( $spies->can( 'read_secret' ) ) { print $spies->read_secret( '/path/to/secret/file' ); } # or if ( my $read = $spies->can( 'read_secret' ) ) { print $spies->$read( '/path/to/secret/file' ); } # with an unknown $group my $get_secret = $group->can( 'read_secret' ) || # spy $group->can( 'steal_document' ) || # mole $group->can( 'create_secret' ) || # spymaster $group->can( 'do_illicit_thing' ) || # politician sub {}; # boring life my $secret = $group->$get_secret;
- setup_abilities_on_group( $group_name, %code )
-
Class method version of
setup_abilities
. - setup_plugins( $plugins )
-
Instance method.
Instead of adding a set of coderefs to a group's class, this method adds a class to the
@ISA
array of the group's class.package My::Spies; sub wear_disguise {} sub read_secret { my ($self, $file) = @_; open( SECRET, $file ); local $/; <SECRET>; } package main; my $spies = $authz->get_group( 'spies' ); $spies->setup_plugins( 'My::Spies' ); if ( $spies->can( 'read_secret' ) ) { warn 'Compromised!'; print $spies->read_secret( '/path/to/secret/file' ); } warn 'Trust no-one' if $spies->can( 'wear_disguise' );
- setup_plugins_on_group( $group_name, $plugins )
-
Class method version of
setup_plugins
.
TODO
More methods for returning meta information, e.g. immediate subgroups of a group, all subgroups of a group, list available actions of a group and its subgroups.
Might be nice to register users with groups.
Make group objects be singletons - not necessary if the only data they carry is their own name.
Under mod_perl
, all setup of hierarchies and permissions must be completed during server startup, before the startup process forks off Apache children. It would be nice to have some way of communicating updates to other processes. Alternatively, you could run the full startup sequence every time you need to access a Tree::Authz group, but that seems sub-optimal.
DEPENDENCIES
Lingua::EN::Inflect::Number, Class::Data::Inheritable.
Optional - Devel::Symdump.
Sub::Override for the test suite.
BUGS
Please report all bugs via the CPAN Request Tracker at http://rt.cpan.org/NoAuth/Bugs.html?Dist=Tree-Authz.
COPYRIGHT AND LICENSE
Copyright 2004 by David Baird.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
AUTHOR
David Baird, cpan@riverside-cms.co.uk