File Coverage

blib/lib/RBAC/Tiny.pm
Criterion Covered Total %
statement 42 42 100.0
branch 15 16 93.7
condition 8 10 80.0
subroutine 10 10 100.0
pod 4 4 100.0
total 79 82 96.3


line stmt bran cond sub pod time code
1 6     6   133313 use strict;
  6         14  
  6         155  
2 6     6   30 use warnings;
  6         11  
  6         277  
3             # ABSTRACT: Tiny Role-Based Access Control (RBAC) implementation
4             package RBAC::Tiny;
5             $RBAC::Tiny::VERSION = '0.003';
6 6     6   29 use Carp;
  6         9  
  6         481  
7 6     6   31 use List::Util;
  6         14  
  6         3355  
8              
9             sub new {
10 10     10 1 7760 my ( $class, %args ) = @_;
11 10 100       212 my $raw_roles = $args{'roles'}
12             or croak "'roles' attribute required";
13              
14 9         44 return bless { raw_roles => $raw_roles }, $class;
15             }
16              
17             sub role {
18 24     24 1 406 my ( $self, $role, $cache ) = @_;
19 24   100     97 $cache ||= {};
20 24   100     165 return $self->{'role'}{$role} ||= $self->_build_role( $role, $cache );
21             }
22              
23             sub _build_role {
24 16     16   30 my ( $self, $role, $cache ) = @_;
25 16 100       155 my $raw = $self->{'raw_roles'}{$role}
26             or croak "No data provided for role '$role'";
27              
28 15 100       378 $cache->{$role}
29             and croak("Circular dependency detected in '$role' and '$cache->{$role}'");
30              
31 13         18 my @cans;
32             # add all cans from parents, recursively
33 13 100       20 foreach my $from ( @{ $raw->{'all_from'} || [] } ) {
  13         66  
34 9 100       150 $self->{'raw_roles'}{$from}
35             or croak("Role '$from' does not exist but used by '$role'");
36              
37 8         18 $cache->{$role} = $from;
38 8         35 my $role = $self->role($from, $cache);
39 5 50       14 push @cans, @{ $role->{'can'} || [] };
  5         23  
40             }
41              
42             # add our own cans
43 9 100       38 push @cans, @{ $raw->{'can'} || [] };
  9         38  
44              
45 9         13 my %can_cache;
46 9 100       21 my %except = map +( $_ => 1 ), @{ $raw->{'except'} || [] };
  9         50  
47             return {
48             can => [
49             grep +(
50 9   66     164 !$except{$_} and !$can_cache{$_}++
51             ), @cans
52             ],
53             };
54             }
55              
56             sub can_role {
57 6     6 1 15 my ( $self, $role, $permission ) = @_;
58             return List::Util::first {
59 15     15   44 $_ eq $permission
60 6         22 } @{ $self->role($role)->{'can'} };
  6         15  
61             }
62              
63             sub roles {
64 5     5 1 625 my $self = shift;
65             return $self->{'roles'} ||= +{
66 5   50     33 map +( $_ => $self->role($_) ), keys %{ $self->{'raw_roles'} }
  5         27  
67             };
68             }
69              
70             1;
71              
72             __END__