=head1 DESCRIPTION
This chapter of the tutorial adds role-based authorization to the
-existing authentication implemented in Chapter 5. It provides simple
-examples of how to use roles in both TT templates and controller
+existing authentication implemented in
+L<Chapter 5|Catalyst::Manual::Tutorial::05_Authentication>. It provides
+simple examples of how to use roles in both TT templates and controller
actions. The first half looks at basic authorization concepts. The
second half looks at how moving your authorization code to your model
can simplify your code and make things easier to maintain.
-You can checkout the source code for this example from the catalyst
-subversion repository as per the instructions in
+Source code for the tutorial in included in the F</home/catalyst/Final>
+directory of the Tutorial Virtual machine (one subdirectory per
+chapter). There are also instructions for downloading the code in
L<Catalyst::Manual::Tutorial::01_Intro>.
-Debug
ConfigLoader
Static::Simple
-
+
StackTrace
-
+
Authentication
Authorization::Roles
-
+
Session
Session::Store::File
Session::State::Cookie
+
+ StatusMessage
/;
Once again, include this additional plugin as a new dependency in the
...
<p>Hello [% c.user.username %], you have the following roles:</p>
-
+
<ul>
[% # Dump list of roles -%]
[% FOR role = c.user.roles %]<li>[% role %]</li>[% END %]
</ul>
-
+
<p>
[% # Add some simple role-specific logic to template %]
[% # Use $c->check_user_roles() to check authz -%]
[% # Give normal users a link for 'logout' %]
<a href="[% c.uri_for('/logout') %]">User Logout</a>
[% END %]
-
+
[% # Can also use $c->user->check_roles() to check authz -%]
[% IF c.check_user_roles('admin') %]
[% # Give admin users a link for 'create' %]
updating C<url_create> to match the following code:
=head2 url_create
-
+
Create a book with the supplied title and rating,
with manual authorization
-
+
=cut
-
+
sub url_create :Chained('base') :PathPart('url_create') :Args(3) {
# In addition to self & context, get the title, rating & author_id args
# from the URL. Note that Catalyst automatically puts extra information
# after the "/<controller_name>/<action_name/" into @_
my ($self, $c, $title, $rating, $author_id) = @_;
-
+
# Check the user's roles
if ($c->check_user_roles('admin')) {
# Call create() on the book model object. Pass the table
title => $title,
rating => $rating
});
-
+
# Add a record to the join table for this book, mapping to
# appropriate author
$book->add_to_book_authors({author_id => $author_id});
# Note: Above is a shortcut for this:
# $book->create_related('book_authors', {author_id => $author_id});
-
+
# Assign the Book object to the stash and set template
$c->stash(book => $book,
template => 'books/create_done.tt2');
way to demonstrate that TT templates will not be used if the response
body has already been set. In reality you would probably want to use a
technique that maintains the visual continuity of your template layout
-(for example, using the "status" or "error" message feature added in
-Chapter 3 or C<detach> to an action that shows an "unauthorized" page).
+(for example, using L<Catalyst::Plugin::StatusMessage> as shown in the
+L<last chapter|Catalyst::Manual::Tutorial::05_Authentication> to
+redirect to an "unauthorized" page).
B<TIP>: If you want to keep your existing C<url_create> method, you can
create a new copy and comment out the original by making it look like a
to add it below the "C<DO NOT MODIFY ...>" line):
=head2 delete_allowed_by
-
+
Can the specified user delete the current book?
-
+
=cut
-
+
sub delete_allowed_by {
my ($self, $user) = @_;
-
+
# Only allow delete if user has 'admin' role
return $user->has_role('admin');
}
the "C<DO NOT MODIFY ...>" line:
=head2 has_role
-
+
Check if a user has the specified role
-
+
=cut
-
+
use Perl6::Junction qw/any/;
sub has_role {
my ($self, $role) = @_;
-
+
# Does this user posses the required role?
return any(map { $_->role } $self->roles) eq $role;
}
-Let's also add Perl6::Junction to the requirements listed in
+Let's also add L<Perl6::Junction> to the requirements listed in
Makefile.PL:
requires 'Perl6::Junction';
+B<Note:> Feel free to use C<grep> in lieu of L<Perl6::Junction::any|Perl6::Junction/any()> if
+you prefer. Also, please don't let the use of the L<Perl6::Junction>
+module above lead you to believe that Catalyst is somehow dependent on
+Perl 6... we are simply using that module for its
+L<easy-to-read|http://blogs.perl.org/users/marc_sebastian_jakobs/2009/11/my-favorite-module-of-the-month-perl6junction.html>
+C<any> function.
+
Now we need to add some enforcement inside our controller. Open
C<lib/MyApp/Controller/Books.pm> and update the C<delete> method to
match the following code:
=head2 delete
-
+
Delete a book
-
+
=cut
-
+
sub delete :Chained('object') :PathPart('delete') :Args(0) {
my ($self, $c) = @_;
-
+
# Check permissions
$c->detach('/error_noperms')
unless $c->stash->{object}->delete_allowed_by($c->user->get_object);
-
+
+ # Saved the PK id for status_msg below
+ my $id = $c->stash->{object}->id;
+
# Use the book object saved by 'object' and delete it along
# with related 'book_authors' entries
$c->stash->{object}->delete;
-
- # Use 'flash' to save information across requests until it's read
- $c->flash->{status_msg} = "Book deleted";
-
+
# Redirect the user back to the list page
- $c->response->redirect($c->uri_for($self->action_for('list')));
+ $c->response->redirect($c->uri_for($self->action_for('list'),
+ {mid => $c->set_status_msg("Deleted book $id")}));
}
Here, we C<detach> to an error page if the user is lacking the
C<lib/MyApp/Controller/Root.pm> and add this method:
=head2 error_noperms
-
+
Permissions error screen
-
+
=cut
-
+
sub error_noperms :Chained('/') :PathPart('error_noperms') :Args(0) {
my ($self, $c) = @_;
-
+
$c->stash(template => 'error_noperms.tt2');
}
L<http://localhost:3000/logout> URL directly) when you are done.
+You can jump to the next chapter of the tutorial here:
+L<Debugging|Catalyst::Manual::Tutorial::07_Debugging>
+
+
=head1 AUTHOR
Kennedy Clark, C<hkclark@gmail.com>
Feel free to contact the author for any errors or suggestions, but the
best way to report issues is via the CPAN RT Bug system at
-<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
-
-The most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
Creative Commons Attribution Share-Alike License Version 3.0
-(L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
+(L<https://creativecommons.org/licenses/by-sa/3.0/us/>).