Abilities
view release on metacpan - search on metacpan
view release on metacpan or search on metacpan
lib/Abilities.pm view on Meta::CPAN
( 'moderator', 'supporter' )
NOTE: In previous versions, this method was required to return
an array of role objects, not a list of role names. This has been changed
in version 0.3.
=cut
requires 'roles';
=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()
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.
=cut
requires 'is_super';
=head2 get_role( $name )
This is a method that returns the object of the role named C<$name>.
=cut
requires 'get_role';
=head1 PROVIDED METHODS
Classes that consume this role will have the following methods available
to them:
=head2 can_perform( $action, [ $constraint ] )
Receives the name of an action, and possibly a constraint, and returns a true
value if the user/role can perform the provided action.
=cut
sub can_perform {
my ($self, $action, $constraint) = @_;
# a super-user/super-role can do whatever they want
return 1 if $self->is_super;
# return false if user/role doesn't have that ability
return unless $self->abilities->{$action};
# user/role has ability, but is there a constraint?
if ($constraint && $constraint ne '_all_') {
# return true if user/role's ability is not constrained
return 1 if !ref $self->abilities->{$action};
# it is constrained (or at least it should be, let's make
# sure we have an array-ref of constraints)
if (ref $self->abilities->{$action} eq 'ARRAY') {
return 1 if $constraint eq '_any_'; # caller wants to know if
# user/role has any constraint,
# which we now know is true
foreach (@{$self->abilities->{$action}}) {
return 1 if $_ eq $constraint;
}
return; # constraint not met
} else {
carp "Expected an array-ref of constraints for action $action, received ".ref($self->abilities->{$action}).", returning false.";
return;
}
} else {
# no constraint, make sure user/role's ability is indeed
# not constrained
return if ref $self->abilities->{$action}; # implied: ref == 'ARRAY', thus constrained
return 1; # not constrained
}
}
=head2 assigned_role( $role_name )
This method receives a role name and returns a true value if the user/role
is a direct member of the provided role. Only direct membership is checked,
so the user/role must be specifically assigned to the provided role, and
not to a role that inherits from that role (see L</"does_role( $role )">
instead).
=cut
sub assigned_role {
my ($self, $role) = @_;
return unless $role;
foreach ($self->roles) {
return 1 if $_ eq $role;
}
return;
}
=head2 does_role( $role_name )
Receives the name of a role, and returns a true value if the user/role
inherits the abilities of the provided role. This method takes inheritance
into account, so if a user was directly assigned to the 'admins' role,
and the 'admins' role inherits from the 'devs' role, then C<does_role('devs')>
will return true for that user (while C<assigned_role('devs')> returns false).
=cut
sub does_role {
my ($self, $role) = @_;
return unless $role;
foreach (map([$_, $self->get_role($_)], $self->roles)) {
return 1 if $_->[0] eq $role || $_->[1]->does_role($role);
}
return;
}
=head2 abilities()
Returns a hash reference of all the abilities a user/role object can
perform, after consolidating abilities inherited from roles (including
recursively) and directly granted. Keys in the hash-ref will be names
of actions, values will be 1 (for yes/no actions) or a single-item array-ref
with the name of a constraint (for constrained actions).
=cut
sub abilities {
my $self = shift;
my $abilities = {};
# load direct actions granted to this user/role
foreach ($self->actions) {
# is this action constrained/scoped?
unless (ref $_) {
$abilities->{$_} = 1;
} elsif (ref $_ eq 'ARRAY' && scalar @$_ == 2) {
$abilities->{$_->[0]} = [$_->[1]];
} else {
carp "Can't handle action of reference ".ref($_);
}
}
# load actions from roles this user/role consumes
my @hashes = map { $self->get_role($_)->abilities } $self->roles;
# merge all abilities
while (scalar @hashes) {
$abilities = merge($abilities, shift @hashes);
}
return $abilities;
}
=head1 UPGRADING FROM v0.2
Up to version 0.2, C<Abilities> required the C<roles> and C<actions>
attributes to return objects. While this made it easier to calculate
abilities, it made this system a bit less flexible.
In version 0.3, C<Abilities> changed the requirement such that both these
attributes need to return strings (the names of the roles/actions). If your implementation
has granted roles and actions stored in a database by names, this made life a bit easier
for you. On other implementations, however, this has the potential of
requiring you to write a bit more code. If that is the case, I apologize,
but keep in mind that you can still store granted roles and actions
any way you want in a database (either by names or by references), just
as long as you correctly provide C<roles> and C<actions>.
Unfortunately, in both versions 0.3 and 0.4, I made a bit of a mess
that rendered both versions unusable. While I documented the C<roles>
attribute as requiring role names instead of role objects, the actual
implementation still required role objects. This has now been fixed,
but it also meant I had to add a new requirement: consuming classes
now have to provide a method called C<get_role()> that takes the name
of a role and returns its object. This will probably means loading the
role from a database and blessing it into your role class that also consumes
this module.
I apologize for any inconvenience this might have caused.
=head1 AUTHOR
Ido Perlmuter, C<< <ido at ido50 dot net> >>
=head1 BUGS
view all matches for this distributionview release on metacpan - search on metacpan
( run in 0.561 second using v1.00-cache-2.02-grep-82fe00e-cpan-2c419f77a38b )