minor fixes
[catagits/Catalyst-Manual.git] / lib / Catalyst / Manual / Tutorial / Authorization.pod
CommitLineData
d442cc9f 1=head1 NAME
2
3533daff 3Catalyst::Manual::Tutorial::Authorization - Catalyst Tutorial - Part 6: Authorization
d442cc9f 4
5
6=head1 OVERVIEW
7
3533daff 8This is B<Part 6 of 10> for the Catalyst tutorial.
d442cc9f 9
10L<Tutorial Overview|Catalyst::Manual::Tutorial>
11
12=over 4
13
14=item 1
15
16L<Introduction|Catalyst::Manual::Tutorial::Intro>
17
18=item 2
19
20L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
21
22=item 3
23
3533daff 24L<More Catalyst Basics|Catalyst::Manual::Tutorial::MoreCatalystBasics>
d442cc9f 25
26=item 4
27
3533daff 28L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
d442cc9f 29
30=item 5
31
3533daff 32L<Authentication|Catalyst::Manual::Tutorial::Authentication>
d442cc9f 33
34=item 6
35
3533daff 36B<Authorization>
d442cc9f 37
38=item 7
39
3533daff 40L<Debugging|Catalyst::Manual::Tutorial::Debugging>
d442cc9f 41
42=item 8
43
3533daff 44L<Testing|Catalyst::Manual::Tutorial::Testing>
d442cc9f 45
46=item 9
47
3533daff 48L<Advanced CRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
49
50=item 10
51
d442cc9f 52L<Appendices|Catalyst::Manual::Tutorial::Appendices>
53
54=back
55
56
d442cc9f 57=head1 DESCRIPTION
58
59This part of the tutorial adds role-based authorization to the existing
9ad715b3 60authentication implemented in Part 5. It provides simple examples of
d442cc9f 61how to use roles in both TT templates and controller actions. The first
62half looks at manually configured authorization. The second half looks
63at how the ACL authorization plugin can simplify your code.
64
65You can checkout the source code for this example from the catalyst
66subversion repository as per the instructions in
1390ef0e 67L<Catalyst::Manual::Tutorial::Intro|Catalyst::Manual::Tutorial::Intro>.
68
d442cc9f 69
70=head1 BASIC AUTHORIZATION
71
72In this section you learn how to manually configure authorization.
73
1390ef0e 74
d442cc9f 75=head2 Update Plugins to Include Support for Authorization
76
77Edit C<lib/MyApp.pm> and add C<Authorization::Roles> to the list:
78
1390ef0e 79 __PACKAGE__->setup(qw/
d442cc9f 80 -Debug
81 ConfigLoader
82 Static::Simple
1390ef0e 83
d442cc9f 84 StackTrace
1390ef0e 85
d442cc9f 86 Authentication
d442cc9f 87 Authorization::Roles
1390ef0e 88
d442cc9f 89 Session
90 Session::Store::FastMmap
91 Session::State::Cookie
1390ef0e 92 /;
d442cc9f 93
94d8da41 94B<Note:> As discussed in MoreCatalystBasics, different versions of
95C<Catalyst::Devel> have used a variety of methods to load the plugins.
533fee73 96You can put the plugins in the C<use Catalyst> statement if you prefer.
94d8da41 97
d442cc9f 98
99=head2 Add Config Information for Authorization
100
905a3a26 101Edit C<myapp.conf> and update it to match the following (the
3533daff 102C<role_relation> and C<role_field> definitions are new):
d442cc9f 103
1390ef0e 104 # rename this file to MyApp.yml and put a : in front of "name" if
105 # you want to use yaml like in old versions of Catalyst
c010ae0d 106 name MyApp
107 <authentication>
108 default_realm dbic
109 <realms>
110 <dbic>
111 <credential>
3533daff 112 # Note this first definition would be the same as setting
113 # __PACKAGE__->config->{authentication}->{realms}->{dbic}
905a3a26 114 # ->{credential} = 'Password' in lib/MyApp.pm
3533daff 115 #
116 # Specify that we are going to do password-based auth
c010ae0d 117 class Password
3533daff 118 # This is the name of the field in the users table with the
119 # password stored in it
c010ae0d 120 password_field password
d0496197 121 # Switch to more secure hashed passwords
122 password_type hashed
123 # Use the SHA-1 hashing algorithm
124 password_hash_type SHA-1
125 </credential>
c010ae0d 126 <store>
3533daff 127 # Use DBIC to retrieve username, password & role information
c010ae0d 128 class DBIx::Class
905a3a26 129 # This is the model object created by Catalyst::Model::DBIC
d0496197 130 # from your schema (you created 'MyApp::Schema::User' but as
905a3a26 131 # the Catalyst startup debug messages show, it was loaded as
d0496197 132 # 'MyApp::Model::DB::Users').
905a3a26 133 # NOTE: Omit 'MyApp::Model' here just as you would when using
d0496197 134 # '$c->model("DB::Users)'
135 user_class DB::Users
3533daff 136 # This is the name of a many_to_many relation in the users
137 # object that points to the roles for that user
c010ae0d 138 role_relation roles
3533daff 139 # This is the name of field in the roles table that contains
140 # the role information
c010ae0d 141 role_field role
d0496197 142 </store>
143 </dbic>
144 </realms>
145 </authentication>
d442cc9f 146
147
148=head2 Add Role-Specific Logic to the "Book List" Template
149
150Open C<root/src/books/list.tt2> in your editor and add the following
151lines to the bottom of the file:
152
8a7c5151 153 <p>Hello [% c.user.username %], you have the following roles:</p>
1390ef0e 154
d442cc9f 155 <ul>
156 [% # Dump list of roles -%]
8a7c5151 157 [% FOR role = c.user.roles %]<li>[% role %]</li>[% END %]
d442cc9f 158 </ul>
1390ef0e 159
d442cc9f 160 <p>
161 [% # Add some simple role-specific logic to template %]
162 [% # Use $c->check_user_roles() to check authz -%]
8a7c5151 163 [% IF c.check_user_roles('user') %]
d442cc9f 164 [% # Give normal users a link for 'logout' %]
8a7c5151 165 <a href="[% c.uri_for('/logout') %]">Logout</a>
d442cc9f 166 [% END %]
1390ef0e 167
d442cc9f 168 [% # Can also use $c->user->check_roles() to check authz -%]
8a7c5151 169 [% IF c.check_user_roles('admin') %]
d442cc9f 170 [% # Give admin users a link for 'create' %]
8a7c5151 171 <a href="[% c.uri_for('form_create') %]">Create</a>
d442cc9f 172 [% END %]
173 </p>
174
175This code displays a different combination of links depending on the
176roles assigned to the user.
177
1390ef0e 178
d442cc9f 179=head2 Limit C<Books::add> to C<admin> Users
180
181C<IF> statements in TT templates simply control the output that is sent
182to the user's browser; it provides no real enforcement (if users know or
183guess the appropriate URLs, they are still perfectly free to hit any
184action within your application). We need to enhance the controller
185logic to wrap restricted actions with role-validation logic.
186
187For example, we might want to restrict the "formless create" action to
188admin-level users by editing C<lib/MyApp/Controller/Books.pm> and
189updating C<url_create> to match the following code:
190
191 =head2 url_create
1390ef0e 192
d442cc9f 193 Create a book with the supplied title and rating,
194 with manual authorization
1390ef0e 195
d442cc9f 196 =cut
1390ef0e 197
d442cc9f 198 sub url_create : Local {
199 # In addition to self & context, get the title, rating & author_id args
200 # from the URL. Note that Catalyst automatically puts extra information
201 # after the "/<controller_name>/<action_name/" into @_
202 my ($self, $c, $title, $rating, $author_id) = @_;
1390ef0e 203
d442cc9f 204 # Check the user's roles
205 if ($c->check_user_roles('admin')) {
905a3a26 206 # Call create() on the book model object. Pass the table
d442cc9f 207 # columns/field values we want to set as hash values
d0496197 208 my $book = $c->model('DB::Books')->create({
d442cc9f 209 title => $title,
210 rating => $rating
211 });
1390ef0e 212
905a3a26 213 # Add a record to the join table for this book, mapping to
d442cc9f 214 # appropriate author
215 $book->add_to_book_authors({author_id => $author_id});
216 # Note: Above is a shortcut for this:
217 # $book->create_related('book_authors', {author_id => $author_id});
1390ef0e 218
d442cc9f 219 # Assign the Book object to the stash for display in the view
220 $c->stash->{book} = $book;
1390ef0e 221
d442cc9f 222 # This is a hack to disable XSUB processing in Data::Dumper
223 # (it's used in the view). This is a work-around for a bug in
224 # the interaction of some versions or Perl, Data::Dumper & DBIC.
225 # You won't need this if you aren't using Data::Dumper (or if
905a3a26 226 # you are running DBIC 0.06001 or greater), but adding it doesn't
d442cc9f 227 # hurt anything either.
228 $Data::Dumper::Useperl = 1;
1390ef0e 229
d442cc9f 230 # Set the TT template to use
231 $c->stash->{template} = 'books/create_done.tt2';
232 } else {
233 # Provide very simple feedback to the user
234 $c->response->body('Unauthorized!');
235 }
236 }
237
238
239To add authorization, we simply wrap the main code of this method in an
240C<if> statement that calls C<check_user_roles>. If the user does not
241have the appropriate permissions, they receive an "Unauthorized!"
242message. Note that we intentionally chose to display the message this
243way to demonstrate that TT templates will not be used if the response
244body has already been set. In reality you would probably want to use a
245technique that maintains the visual continuity of your template layout
246(for example, using the "status" or "error" message feature added in
9ad715b3 247Part 3).
d442cc9f 248
249B<TIP>: If you want to keep your existing C<url_create> method, you can
250create a new copy and comment out the original by making it look like a
251Pod comment. For example, put something like C<=begin> before C<sub add
252: Local {> and C<=end> after the closing C<}>.
253
1390ef0e 254
d442cc9f 255=head2 Try Out Authentication And Authorization
256
257Press C<Ctrl-C> to kill the previous server instance (if it's still
258running) and restart it:
259
260 $ script/myapp_server.pl
261
1390ef0e 262Now trying going to L<http://localhost:3000/books/list> and you should
263be taken to the login page (you might have to C<Shift+Reload> or
264C<Ctrl+Reload> your browser and/or click the "Logout" link on the book
265list page). Try logging in with both C<test01> and C<test02> (both
266use a password of C<mypass>) and notice how the roles information
267updates at the bottom of the "Book List" page. Also try the C<Logout>
268link on the book list page.
d442cc9f 269
270Now the "url_create" URL will work if you are already logged in as user
271C<test01>, but receive an authorization failure if you are logged in as
272C<test02>. Try:
273
274 http://localhost:3000/books/url_create/test/1/6
275
1390ef0e 276while logged in as each user. Use one of the 'Logout' links (or go to
277L<http://localhost:3000/logout> in your browser directly) when you are
d442cc9f 278done.
279
280
281=head1 ENABLE ACL-BASED AUTHORIZATION
282
283This section takes a brief look at how the
284L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>
905a3a26 285plugin can automate much of the work required to perform role-based
d442cc9f 286authorization in a Catalyst application.
287
1390ef0e 288
d442cc9f 289=head2 Add the C<Catalyst::Plugin::Authorization::ACL> Plugin
290
291Open C<lib/MyApp.pm> in your editor and add the following plugin to the
1390ef0e 292C<__PACKAGE__-E<gt>setup> statement:
d442cc9f 293
294 Authorization::ACL
295
296Note that the remaining C<use Catalyst> plugins from earlier sections
297are not shown here, but they should still be included.
298
1390ef0e 299
d442cc9f 300=head2 Add ACL Rules to the Application Class
301
302Open C<lib/MyApp.pm> in your editor and add the following B<BELOW> the
1390ef0e 303C<__PACKAGE__-E<gt>setup> statement:
d442cc9f 304
305 # Authorization::ACL Rules
306 __PACKAGE__->deny_access_unless(
307 "/books/form_create",
308 [qw/admin/],
309 );
310 __PACKAGE__->deny_access_unless(
311 "/books/form_create_do",
312 [qw/admin/],
313 );
314 __PACKAGE__->deny_access_unless(
315 "/books/delete",
316 [qw/user admin/],
317 );
318
319Each of the three statements above comprises an ACL plugin "rule". The
320first two rules only allow admin-level users to create new books using
321the form (both the form itself and the data submission logic are
322protected). The third statement allows both users and admins to delete
323books. The C</books/url_create> action will continue to be protected by
324the "manually configured" authorization created earlier in this part of
325the tutorial.
326
327The ACL plugin permits you to apply allow/deny logic in a variety of
328ways. The following provides a basic overview of the capabilities:
329
330=over 4
331
905a3a26 332=item *
d442cc9f 333
334The ACL plugin only operates on the Catalyst "private namespace". You
335are using the private namespace when you use C<Local> actions. C<Path>,
336C<Regex>, and C<Global> allow you to specify actions where the path and
337the namespace differ -- the ACL plugin will not work in these cases.
338
905a3a26 339=item *
d442cc9f 340
341Each rule is expressed in a separate
342C<__PACKAGE__-E<gt>deny_access_unless()> or
343C<__PACKAGE__-E<gt>allow_access_if()> line (there are several other
344methods that can be used for more complex policies, see the C<METHODS>
345portion of the
346L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>
347documentation for more details).
348
905a3a26 349=item *
d442cc9f 350
351Each rule can contain multiple roles but only a single path.
352
905a3a26 353=item *
d442cc9f 354
355The rules are tried in order (with the "most specific" rules tested
356first), and processing stops at the first "match" where an allow or deny
357is specified. Rules "fall through" if there is not a "match" (where a
358"match" means the user has the specified role). If a "match" is found,
359then processing stops there and the appropriate allow/deny action is
360taken.
361
905a3a26 362=item *
d442cc9f 363
364If none of the rules match, then access is allowed.
365
905a3a26 366=item *
d442cc9f 367
905a3a26 368The rules currently need to be specified in the application class
d442cc9f 369C<lib\MyApp.pm> B<after> the C<__PACKAGE__-E<gt>setup;> line.
370
371=back
372
1390ef0e 373
d442cc9f 374=head2 Add a Method to Handle Access Violations
375
376By default,
377L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>
378throws an exception when authorization fails. This will take the user
379to the Catalyst debug screen, or a "Please come back later" message if
380you are not using the C<-Debug> flag. This step uses the
381C<access_denied> method in order to provide more appropriate feedback to
382the user.
383
384Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
385following method:
386
387 =head2 access_denied
1390ef0e 388
d442cc9f 389 Handle Catalyst::Plugin::Authorization::ACL access denied exceptions
1390ef0e 390
d442cc9f 391 =cut
1390ef0e 392
d442cc9f 393 sub access_denied : Private {
394 my ($self, $c) = @_;
1390ef0e 395
d442cc9f 396 # Set the error message
397 $c->stash->{error_msg} = 'Unauthorized!';
1390ef0e 398
d442cc9f 399 # Display the list
400 $c->forward('list');
401 }
402
905a3a26 403Then run the Catalyst development server script:
d442cc9f 404
405 $ script/myapp_server.pl
406
3778bcbe 407Log in as C<test02>. Once at the book list, click the "Create" link
408to try the C<form_create> action. You should receive a red
409"Unauthorized!" error message at the top of the list. (Note that in
410the example code the "Create" link code in C<root/src/books/list.tt2>
411is inside an C<IF> statement that only displays the list to
412admin-level users.) If you log in as C<test01> you should be able to
413view the C<form_create> form and add a new book.
d442cc9f 414
415When you are done, use one of the 'Logout' links (or go to the
416L<http://localhost:3000/logout> URL directly) when you are done.
417
418
419=head1 AUTHOR
420
421Kennedy Clark, C<hkclark@gmail.com>
422
423Please report any errors, issues or suggestions to the author. The
424most recent version of the Catalyst Tutorial can be found at
82ab4bbf 425L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.70/trunk/lib/Catalyst/Manual/Tutorial/>.
d442cc9f 426
45c7830f 427Copyright 2006-2008, Kennedy Clark, under Creative Commons License
95674086 428(L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
d442cc9f 429