Abilities

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

    grant access later-on after deployment (assuming you're also the
    end-user).

    Abilities to perform certain actions can be given to a user
    specifically, or via roles the user can assume (as in role-based access
    control). For example, if user 'user01' is a member of role 'admin', and
    this user wishes to perform some action, for example 'delete_foo', then
    they will only be able to do so if the 'delete_foo' ability was given to
    either the user itself or the 'admin' role itself. Furthermore, roles
    can recursively inherit other roles; for example, the role 'mega_mods'
    can inherit the roles 'mods' and 'editors'. Users of the 'mega_mods'
    role will assume all actions owned by the 'mods' and 'editors' roles.

    A commonly known use-case for this type of access control is message
    boards, where the administrator might wish to create roles with certain
    actions and associate users with the roles (more commonly called 'user
    groups'); for example, the admin can create an 'editor' role, giving
    users of this role the ability to edit and delete posts, but not any
    other administrative action. So in essence, this type of access control
    relieves the developer of deciding who gets to do what and passes these
    decisions to the end-user, which might actually be necessary in certain
    situations.

    The "Abilities" module is implemented as a Moo role (which makes it
    compatible with Moose code). In order to be able to use this mechanism,
    applications must implement a user management system that will consume
    this role. More specifically, a user class and a role class must be
    implemented, consuming this role. Entities is a reference implementation
    that can be used by applications, or just taken as an example of an
    ability-based authorization system. Entities::User and Entities::Role
    are the user and role classes that consume the Abilities role in the
    Entities distribution.

  CONSTRAINTS
    Generally, an ability is a yes/no option. Either the user can or can't
    perform a specific action. At times, this might not be flexible enough,
    and the user's ability to perform a certain action should be
    constrained. For example, a user might be granted the ability to edit
    posts in a blog, but this ability should be constrained to the user's
    posts only. The user is not to be allowed to edit posts created by other
    users. "Abilities" supports constraints by allowing to set a name-based
    constraint when granting a user/role a certain ability. Then, checking
    the user's ability to perform an action can include the constraint, for
    example:

            if ($post->{user_id} eq $user->id && $user->can_perform('edit_posts', 'only_his')) {
                    # allow
            } else {
                    # do not allow
            }

    Here, the "Abilities" module allows you to check if the user's ability
    is constrained, but the responsibility for making sure the constraint is
    actually relevant to the case is left to you. In the above example, it
    is the application that checks if the post the user is trying to edit
    was created by them, not the "Abilities" module.

  (PAID) SUBSCRIPTION-BASED WEB SERVICES
    Apart from the scenario described above, this module also provides
    optional support for subscription-based web services, such as those
    where customers subscribe to a certain paid (or free, doesn't matter)
    plan from a list of available plans (GitHub is an example of such a
    service). This functionality is also implemented as a Moo(se) role, in
    the Abilities::Features module provided with this distribution. Read its
    documentation for detailed information.

README  view on Meta::CPAN


  actions()
    Returns a list of all action names that a user object has been
    explicitely granted, or that a role object has been granted. If a
    certain action is constrained, then it should be added to the list as an
    array reference with two items, the first being the name of the action,
    the second being the name of the constraint.

    Example return structure:

            ( 'create_posts', ['edit_posts', 'only_his'], 'comment_on_posts' )

    NOTE: In previous versions, this method was required to return an array
    of action objects, not a list of action names. This has been changed in
    version 0.3.

  is_super()
    This is a boolean attribute that both user and role objects should have.
    If a user/role object has a true value for this attribute, then they
    will be able to perform any action, even if it wasn't granted to them.

lib/Abilities.pm  view on Meta::CPAN

as you (the developer) do not need to think about who should be allowed
to perform a certain action, and can easily grant access later-on after
deployment (assuming you're also the end-user).

Abilities to perform certain actions can be given to a user specifically, or
via roles the user can assume (as in role-based access control). For example,
if user 'user01' is a member of role 'admin', and this user wishes to perform
some action, for example 'delete_foo', then they will only be able to do
so if the 'delete_foo' ability was given to either the user itself or the
'admin' role itself. Furthermore, roles can recursively inherit other roles;
for example, the role 'mega_mods' can inherit the roles 'mods' and 'editors'.
Users of the 'mega_mods' role will assume all actions owned by the 'mods'
and 'editors' roles.

A commonly known use-case for this type of access control is message boards,
where the administrator might wish to create roles with certain actions
and associate users with the roles (more commonly called 'user groups');
for example, the admin can create an 'editor' role, giving users of this
role the ability to edit and delete posts, but not any other administrative
action. So in essence, this type of access control relieves the developer
of deciding who gets to do what and passes these decisions to the
end-user, which might actually be necessary in certain situations.

The C<Abilities> module is implemented as a L<Moo role|Moo::Role> (which makes
it compatible with L<Moose> code). In order to be able to use this mechanism,
applications must implement a user management system that will consume this role.
More specifically, a user class and a role class must be implemented, consuming this role. L<Entities> is a reference implementation that can be used by applications, or
just taken as an example of an ability-based authorization system. L<Entities::User>
and L<Entities::Role> are the user and role classes that consume the Abilities
role in the Entities distribution.

=head2 CONSTRAINTS

Generally, an ability is a yes/no option. Either the user can or can't perform
a specific action. At times, this might not be flexible enough, and the user's
ability to perform a certain action should be constrained. For example, a user
might be granted the ability to edit posts in a blog, but this ability should
be constrained to the user's posts only. The user is not to be allowed to edit
posts created by other users. C<Abilities> supports constraints by allowing to
set a name-based constraint when granting a user/role a certain ability. Then,
checking the user's ability to perform an action can include the constraint,
for example:

	if ($post->{user_id} eq $user->id && $user->can_perform('edit_posts', 'only_his')) {
		# allow
	} else {
		# do not allow
	}

Here, the C<Abilities> module allows you to check if the user's ability is constrained,
but the responsibility for making sure the constraint is actually relevant
to the case is left to you. In the above example, it is the application that
checks if the post the user is trying to edit was created by them, not the C<Abilities>
module.

=head2 (PAID) SUBSCRIPTION-BASED WEB SERVICES

Apart from the scenario described above, this module also provides optional
support for subscription-based web services, such as those where customers
subscribe to a certain paid (or free, doesn't matter) plan from a list
of available plans (GitHub is an example of such a service). This functionality
is also implemented as a Moo(se) role, in the L<Abilities::Features> module provided
with this distribution. Read its documentation for detailed information.

lib/Abilities.pm  view on Meta::CPAN


=head2 actions()

Returns a list of all action names that a user object has been explicitely granted,
or that a role object has been granted. If a certain action is constrained, then
it should be added to the list as an array reference with two items, the first being
the name of the action, the second being the name of the constraint.

Example return structure:

	( 'create_posts', ['edit_posts', 'only_his'], 'comment_on_posts' )

NOTE: In previous versions, this method was required to return
an array of action objects, not a list of action names. This has been changed
in version 0.3.

=cut

requires 'actions';

=head2 is_super()

t/01-abilities.t  view on Meta::CPAN

use lib 't/lib';
use TestManager;
use TestRole;
use TestUser;
use Test::More tests => 29;

my $mg = TestManager->new;

my $ra = TestRole->new(name => 'RA', actions => ['create_comment'], mg => $mg);
my $rc = TestRole->new(name => 'RC', actions => ['do_something'], mg => $mg);
my $rb = TestRole->new(name => 'RB', actions => ['edit_comment', 'delete_comment', 'edit_post', 'delete_post'], roles => ['RC'], mg => $mg);

my $ua = TestUser->new(name => 'UA', actions => ['create_post', ['edit_post', 'his'], ['delete_post', 'his']], roles => ['RA'], mg => $mg);
my $ub = TestUser->new(name => 'UB', is_super => 1, mg => $mg);
my $uc = TestUser->new(name => 'UC', actions => [['create_post', 'news_only']], roles => ['RA', 'RB'], mg => $mg);

$mg->add_objects($ra, $rb, $rc);

ok($ra, 'Got RA');
ok($rb, 'Got RB');
ok($rc, 'Got RC');
ok($ua, 'Got UA');
ok($ub, 'Got UB');

t/01-abilities.t  view on Meta::CPAN


ok($ua->assigned_role('RA'), 'UA assigned to RA');
ok(!$ua->assigned_role('RB'), 'UA not assigned to RB');
ok(!$uc->assigned_role('RC'), 'UC not assigned to RC');
ok($uc->does_role('RC'), 'UC does RC');
ok(!$ua->does_role('RC'), 'UA does not do RC');
ok(!$ub->assigned_role('RA'), 'UB not assigned RA');
ok($rb->assigned_role('RC'), 'RB assigned RC');

ok($ua->can_perform('create_post'), 'UA can create posts');
ok($ua->can_perform('edit_post', 'his'), 'UA can edit his posts');
ok(!$ua->can_perform('edit_post'), 'UA cannot edit all posts');
ok($ua->can_perform('create_comment'), 'UA can create comments');

ok($ub->can_perform('create_post'), 'UB can create posts');
ok($ub->can_perform('fake_action'), 'UB can even perform fake actions');
ok($ub->can_perform('delete_post', 'his'), 'UB can delete its own posts');
ok($ub->can_perform('delete_comment', 'all'), 'UB can delete all comments');

ok($uc->can_perform('create_comment'), 'UC can create comments');
ok(!$uc->can_perform('fake_action'), 'UC cannot perform fake action');
ok($uc->can_perform('delete_comment'), 'UC can delete comments');
ok($uc->can_perform('delete_comment', 'his'), 'UC can delete its own comments');
ok($uc->can_perform('do_something'), 'UC can do something');

# let's check the _all_ and _any_ options
ok($ua->can_perform('edit_post', '_any_'), '_any_ works when UA has constraint on edit_post');
ok($ua->can_perform('create_post', '_all_'), '_all_ works when UA has no constraint on create_post');
ok($ua->can_perform('create_post', '_any_'), '_any_ also works when UA has no constrain on create_post');

done_testing();



( run in 0.614 second using v1.01-cache-2.11-cpan-de7293f3b23 )