File Coverage

blib/lib/Catalyst/Plugin/Authorization/Roles.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Catalyst::Plugin::Authorization::Roles;
4              
5 1     1   973 use strict;
  1         2  
  1         38  
6 1     1   5 use warnings;
  1         2  
  1         31  
7              
8 1     1   533 use Set::Object ();
  0            
  0            
9             use Scalar::Util ();
10             use Catalyst::Exception ();
11              
12             our $VERSION = '0.09';
13              
14             sub check_user_roles {
15             my ( $c, @roles ) = @_;
16             local $@;
17             eval { $c->assert_user_roles(@roles) };
18             return $@ ? 0 : 1;
19             }
20              
21             sub assert_user_roles {
22             my ( $c, @roles ) = @_;
23              
24             my $user;
25              
26             if ( Scalar::Util::blessed( $roles[0] )
27             && $roles[0]->isa("Catalyst::Authentication::User") )
28             {
29             $user = shift @roles;
30             }
31              
32             $user ||= $c->user;
33              
34             unless ( $user ) {
35             Catalyst::Exception->throw(
36             "No logged in user, and none supplied as argument");
37             }
38              
39             Catalyst::Exception->throw("User does not support roles")
40             unless $user->supports(qw/roles/);
41              
42             local $" = ", ";
43              
44             if ( $user->supports(qw/roles self_check/) ) {
45             if ( $user->check_roles(@roles) ) {
46             $c->log->debug("Role granted: @roles") if $c->debug;
47             return 1;
48             }
49             else {
50             $c->log->debug("Role denied: @roles") if $c->debug;
51             Catalyst::Exception->throw("Missing roles");
52             }
53             }
54             else {
55              
56             my $have = Set::Object->new($user->roles);
57             my $need = Set::Object->new(@roles);
58              
59             if ( $have->superset($need) ) {
60             $c->log->debug("Role granted: @roles") if $c->debug;
61             return 1;
62             }
63             else {
64             $c->log->debug("Role denied: @roles") if $c->debug;
65             my @missing = $need->difference($have)->members;
66             Catalyst::Exception->throw("Missing roles: @missing");
67             }
68             }
69              
70             }
71              
72             sub check_any_user_role {
73             my ( $c, @roles ) = @_;
74             local $@;
75             eval { $c->assert_any_user_role(@roles) };
76             return $@ ? 0 : 1;
77             }
78              
79             sub assert_any_user_role {
80             my ( $c, @roles ) = @_;
81              
82             my $user;
83              
84             if ( Scalar::Util::blessed( $roles[0] )
85             && $roles[0]->isa("Catalyst::Authentication::User") )
86             {
87             $user = shift @roles;
88             }
89              
90             $user ||= $c->user;
91              
92             unless ( $user ) {
93             Catalyst::Exception->throw(
94             "No logged in user, and none supplied as argument");
95             }
96              
97             Catalyst::Exception->throw("User does not support roles")
98             unless $user->supports(qw/roles/);
99              
100             if ( $user->supports(qw/roles self_check_any/) ) {
101             if ( $user->check_roles_any(@roles) ) {
102             $c->log->debug("At least one role granted: @roles") if $c->debug;
103             return 1;
104             }
105             else {
106             $c->log->debug("Roles denied: @roles") if $c->debug;
107             Catalyst::Exception->throw("Missing roles");
108             }
109             }
110             else {
111             my $have = Set::Object->new($user->roles);
112             my $need = Set::Object->new(@roles);
113              
114             if ( $have->intersection($need)->size > 0 ) {
115             $c->log->debug("At least one role granted: @roles") if $c->debug;
116             return 1;
117             }
118             else {
119             $c->log->debug("Role denied: @roles") if $c->debug;
120             Catalyst::Exception->throw( "Missing roles" );
121             }
122             }
123             }
124              
125             __PACKAGE__;
126              
127             __END__
128              
129             =pod
130              
131             =head1 NAME
132              
133             Catalyst::Plugin::Authorization::Roles - Role based authorization for Catalyst based on Catalyst::Plugin::Authentication
134              
135             =head1 SYNOPSIS
136              
137             use Catalyst qw/
138             Authentication
139             Authorization::Roles
140             /;
141              
142             sub delete : Local {
143             my ( $self, $c ) = @_;
144              
145             $c->assert_user_roles( qw/admin/ ); # only admins can delete
146              
147             $c->model("Foo")->delete_it();
148             }
149              
150             =head1 DESCRIPTION
151              
152             Role based access control is very simple: every user has a list of roles,
153             which that user is allowed to assume, and every restricted part of the app
154             makes an assertion about the necessary roles.
155              
156             With C<assert_user_roles>, if the user is a member in B<all> of the required
157             roles access is granted. Otherwise, access is denied. With
158             C<assert_any_user_role> it is enough that the user is a member in B<one>
159             role.
160              
161             There are alternative approaches to do this on a per action basis, see
162             L<Catalyst::ActionRole::ACL>.
163              
164             For example, if you have a CRUD application, for every mutating action you
165             probably want to check that the user is allowed to edit. To do this, create an
166             editor role, and add that role to every user who is allowed to edit.
167              
168             sub edit : Local {
169             my ( $self, $c ) = @_;
170             $c->assert_user_roles( qw/editor/ );
171             $c->model("TheModel")->make_changes();
172             }
173              
174              
175             When this plugin checks the roles of a user it will first see if the user
176             supports the self check method.
177              
178             When this is not supported the list of roles is extracted from the user using
179             the C<roles> method.
180              
181             When this is supported, the C<check_roles> method will be used to delegate the
182             role check to the user class. Classes like the one provided with
183             L<iCatalyst::Authentication::Store::DBIx::Class> optimize the check this way.
184              
185             =head1 METHODS
186              
187             =over 4
188              
189             =item assert_user_roles [ $user ], @roles
190              
191             Checks that the user (as supplied by the first argument, or, if omitted,
192             C<< $c->user >>) has the specified roles.
193              
194             If for any reason (C<< $c->user >> is not defined, the user is missing a role,
195             etc) the check fails, an error is thrown.
196              
197             You can either catch these errors with an eval, or clean them up in your C<end>
198             action.
199              
200             =item check_user_roles [ $user ], @roles
201              
202             Takes the same args as C<assert_user_roles>, and performs the same check, but
203             instead of throwing errors returns a boolean value.
204              
205             =item assert_any_user_role [ $user ], @roles
206              
207             Checks that the user (as supplied by the first argument, or, if omitted,
208             C<< $c->user >>) has at least one of the specified roles.
209              
210             Other than that, works like C<assert_user_roles>.
211              
212             =item check_any_user_role [ $user ], @roles
213              
214             Takes the same args as C<assert_any_user_role>, and performs the same check, but
215             instead of throwing errors returns a boolean value.
216              
217             =back
218              
219             =head1 SEE ALSO
220              
221             =over
222              
223             =item L<Catalyst::Plugin::Authentication>
224              
225             =item L<Catalyst::ActionRole::ACL>
226              
227             =item L<< Catalyst::Manual::Tutorial::06_Authorization >>
228              
229             =back
230              
231             =head1 AUTHOR
232              
233             Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
234              
235             =head1 COPYRIGHT & LICENSE
236              
237             Copyright (c) 2005-2011
238             the Catalyst::Plugin::Authorization::Roles L</AUTHOR>
239             as listed above.
240              
241             This library is free software; you can redistribute it and/or modify
242             it under the same terms as Perl itself.
243              
244             =cut
245