=head1 Catalyst Advent - Day 24 - Authorization B Please refer to L. =head1 Introduction Authorization is the step that comes after authentication. Authentication establishes that the user agent is really representing the user we think it's representing, and then authorization determines what this user is allowed to do. =head1 Role Based Access Control Under role based access control each user is allowed to perform any number of roles. For example, at a zoo no one but specially trained personnel can enter the moose cage (Mynd you, moose bites kan be pretty nasti!). For example: package Zoo::Controller::MooseCage; sub feed_moose : Local { my ( $self, $c ) = @_; $c->model( "Moose" )->eat( $c->req->param("food") ); } With this action, anyone can just come into the moose cage and feed the moose, which is a very dangerous thing. We need to restrict this action, so that only a qualified moose feeder can perform that action. The Authorization::Roles plugin let's us perform role based access control checks. Let's load it: use Catalyst qw/ Authentication # yadda yadda Authorization::Roles /; And now our action should look like this: sub feed_moose : Local { my ( $self, $c ) = @_; if ( $c->check_user_roles( "moose_feeder" ) ) { $c->model( "Moose" )->eat( $c->req->param("food") ); } else { $c->stash->{error} = "unauthorized"; } } This checks C<< $c->user >>, and only if the user has B the roles in the list, a true value is returned. C has a sister method, C, which throws an exception if any roles are missing. Some roles that might actually make sense in, say, a forum application: =over 4 =item * administrator =item * moderator =back each with a distinct task (system administration versus content administration). =head1 Access Control Lists Checking for roles all the time can be tedious and error prone. The Authorization::ACL plugin let's us declare where we'd like checks to be done automatically for us. For example, we may want to completely block out anyone who isn't a C from the entire C controller: Zoo->deny_access_unless( "/moose_cage", [qw/moose_feeder/] ); The role list behaves in the same way as C. However, the ACL plugin isn't limited to just interacting with the Roles plugin. We can use a code reference instead. For example, to allow either moose trainers or moose feeders into the moose cage, we can create a more complex check: Zoo->deny_access_unless( "/moose_cage", sub { my $c = shift; $c->check_user_roles( "moose_trainer" ) || $c->check_user_roles( "moose_feeder" ); }); The more specific a role, the earlier it will be checked. Let's say moose feeders are now restricted to only the C action, while moose trainers get access everywhere: Zoo->deny_access_unless( "/moose_cage", [qw/moose_trainer/] ); Zoo->allow_access_if( "/moose_cage/feed_moose", [qw/moose_feeder/]); When the C action is accessed the second check will be made. If the user is a C, then access will be immediately granted. Otherwise, the next rule in line will be tested - the one checking for a C. If this rule is not satisfied, access will be immediately denied. Rules applied to the same path will be checked in the order they were added. Lastly, handling access denial events is done by creating an C private action: sub access_denied : Private { my ( $self, $c, $action ) = @_; } This action works much like auto, in that it is inherited across namespaces (not like object oriented code). This means that the C action which is B to the action which was blocked will be triggered. If this action does not exist, an error will be thrown, which you can clean up in your C private action instead. Also, it's important to note that if you restrict access to "/" then C, C, etc will also be restricted. MyApp->acl_allow_root_internals; will create rules that permit access to C, C, and C in the root of your app (but not in any other controller). =head2 More Information L L --nothingmuch