Add additional information for transition from YAML to Config::General
[catagits/Catalyst-Manual.git] / lib / Catalyst / Manual / Tutorial / Authentication.pod
index 28e2501..3990f74 100644 (file)
@@ -1,11 +1,11 @@
 =head1 NAME
 
-Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Part 4: Authentication
+Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Part 5: Authentication
 
 
 =head1 OVERVIEW
 
-This is B<Part 4 of 9> for the Catalyst tutorial.
+This is B<Part 5 of 10> for the Catalyst tutorial.
 
 L<Tutorial Overview|Catalyst::Manual::Tutorial>
 
@@ -21,30 +21,34 @@ L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
 
 =item 3
 
-L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
+L<More Catalyst Basics|Catalyst::Manual::Tutorial::MoreCatalystBasics>
 
 =item 4
 
-B<Authentication>
+L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
 
 =item 5
 
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+B<Authentication>
 
 =item 6
 
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
 
 =item 7
 
-L<Testing|Catalyst::Manual::Tutorial::Testing>
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
 
 =item 8
 
-L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+L<Testing|Catalyst::Manual::Tutorial::Testing>
 
 =item 9
 
+L<Advanced CRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 10
+
 L<Appendices|Catalyst::Manual::Tutorial::Appendices>
 
 =back
@@ -52,8 +56,8 @@ L<Appendices|Catalyst::Manual::Tutorial::Appendices>
 
 =head1 DESCRIPTION
 
-Now that we finally have a simple yet functional application, we can
-focus on providing authentication (with authorization coming next in
+Now that we finally have a simple yet functional application, we can 
+focus on providing authentication (with authorization coming next in 
 Part 5).
 
 This part of the tutorial is divided into two main sections: 1) basic,
@@ -117,58 +121,29 @@ Then load this into the C<myapp.db> database with the following command:
 
 =head2 Add User and Role Information to DBIC Schema
 
-This step adds DBIC-based classes for the user-related database tables
-(the role information will not be used until Part 5):
+Although we could manually edit the DBIC schema information to include
+the new tables added in the previous step, let's use the C<create=static>
+option on the DBIC model helper to do most of the work for us:
 
-Edit C<lib/MyAppDB.pm> and update the contents to match (only the
-C<MyAppDB =E<gt> [qw/Book BookAuthor Author User UserRole Role/]> line
-has changed):
+    $ script/myapp_create.pl model MyAppDB DBIC::Schema MyApp::Schema::MyAppDB create=static dbi:SQLite:myapp.db
+    $ ls lib/MyApp/Schema/MyAppDB
+    Authors.pm  BookAuthors.pm  Books.pm  Roles.pm  UserRoles.pm  Users.pm
 
-    package MyAppDB;
-    
-    =head1 NAME 
-    
-    MyAppDB -- DBIC Schema Class
-    
-    =cut
-    
-    # Our schema needs to inherit from 'DBIx::Class::Schema'
-    use base qw/DBIx::Class::Schema/;
-    
-    # Need to load the DB Model classes here.
-    # You can use this syntax if you want:
-    #    __PACKAGE__->load_classes(qw/Book BookAuthor Author User UserRole Role/);
-    # Also, if you simply want to load all of the classes in a directory
-    # of the same name as your schema class (as we do here) you can use:
-    #    __PACKAGE__->load_classes(qw//);
-    # But the variation below is more flexible in that it can be used to 
-    # load from multiple namespaces.
-    __PACKAGE__->load_classes({
-        MyAppDB => [qw/Book BookAuthor Author User UserRole Role/]
-    });
-    
-    1;
+Notice how the helper has added three new table-specific result source 
+files to the C<lib/MyApp/Schema/MyApp> directory.  And, more 
+importantly, even if there were changes to the existing result source 
+files, those changes would have only been written above the C<# DO NOT 
+MODIFY THIS OR ANYTHING ABOVE!> comment and your hand-editted 
+enhancements would have been preserved.
 
 
-=head2 Create New "Result Source Objects"
+Speaking of "hand-editted enhancements," we should now add 
+relationship information to the three new result source files.  Edit 
+each of these files and add the following information between the C<# 
+DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and the closing C<1;>:
 
-Create the following three files with the content shown below.
+C<lib/MyApp/Schema/MyAppDB/Users.pm>:
 
-C<lib/MyAppDB/User.pm>:
-
-    package MyAppDB::User;
-    
-    use base qw/DBIx::Class/;
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('users');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/id username password email_address first_name last_name/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key('id');
-    
     #
     # Set relationships:
     #
@@ -178,41 +153,19 @@ C<lib/MyAppDB/User.pm>:
     #     1) Name of relationship, DBIC will create accessor with this name
     #     2) Name of the model class referenced by this relationship
     #     3) Column name in *foreign* table
-    __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'user_id');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::User - A model object representing a person with access to the system.
-    
-    =head1 DESCRIPTION
+    __PACKAGE__->has_many(map_user_role => 'MyApp::Schema::MyAppDB::UserRoles', 'user_id');
     
-    This is an object that represents a row in the 'users' table of your application
-    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    Offline utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
+    # many_to_many():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of has_many() relationship this many_to_many() is shortcut for 
+    #     3) Name of belongs_to() relationship in model class of has_many() above 
+    #   You must already have the has_many() defined to use a many_to_many().
+    __PACKAGE__->many_to_many(roles => 'map_user_role', 'role');
 
 
-C<lib/MyAppDB/Role.pm>:
+C<lib/MyApp/Schema/MyAppDB/Roles.pm>:
 
-    package MyAppDB::Role;
-    
-    use base qw/DBIx::Class/;
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('roles');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/id role/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key('id');
-    
     #
     # Set relationships:
     #
@@ -222,42 +175,11 @@ C<lib/MyAppDB/Role.pm>:
     #     1) Name of relationship, DBIC will create accessor with this name
     #     2) Name of the model class referenced by this relationship
     #     3) Column name in *foreign* table
-    __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'role_id');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::Role - A model object representing a class of access permissions to 
-    the system.
-    
-    =head1 DESCRIPTION
-    
-    This is an object that represents a row in the 'roles' table of your 
-    application database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    "Offline" utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
+    __PACKAGE__->has_many(map_user_role => 'MyApp::Schema::MyAppDB::UserRoles', 'role_id');
 
 
-C<lib/MyAppDB/UserRole.pm>:
+C<lib/MyApp/Schema/MyAppDB/UserRoles.pm>:
 
-    package MyAppDB::UserRole;
-    
-    use base qw/DBIx::Class/;
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('user_roles');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/user_id role_id/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key(qw/user_id role_id/);
-    
     #
     # Set relationships:
     #
@@ -267,41 +189,33 @@ C<lib/MyAppDB/UserRole.pm>:
     #     1) Name of relationship, DBIC will create accessor with this name
     #     2) Name of the model class referenced by this relationship
     #     3) Column name in *this* table
-    __PACKAGE__->belongs_to(user => 'MyAppDB::User', 'user_id');
+    __PACKAGE__->belongs_to(user => 'MyApp::Schema::MyAppDB::Users', 'user_id');
     
     # belongs_to():
     #   args:
     #     1) Name of relationship, DBIC will create accessor with this name
     #     2) Name of the model class referenced by this relationship
     #     3) Column name in *this* table
-    __PACKAGE__->belongs_to(role => 'MyAppDB::Role', 'role_id');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::UserRole - A model object representing the JOIN between Users and Roles.
-    
-    =head1 DESCRIPTION
-    
-    This is an object that represents a row in the 'user_roles' table of your application
-    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    You probably won't need to use this class directly -- it will be automatically
-    used by DBIC where joins are needed.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    Offline utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
+    __PACKAGE__->belongs_to(role => 'MyApp::Schema::MyAppDB::Roles', 'role_id');
 
-The code for these three result source classes is obviously very familiar to the C<Book>, C<Author>, and C<BookAuthor> classes created in Part 2.
+
+The code for these three sets of updates is obviously very similar to 
+the edits we made to the C<Books>, C<Authors>, and C<BookAuthors> 
+classes created in Part 3.
+
+Note that we do not need to make any change to the 
+C<lib/MyApp/Schema/MyAppDB.pm> schema file.  It simple tells DBIC to 
+load all of the result source files it finds in below the 
+C<lib/MyApp/Schema/MyAppDB> directory, so it will automatically pick 
+up our new table information.
 
 
 =head2 Sanity-Check Reload of Development Server
 
-We aren't ready to try out the authentication just yet; we only want to do a quick check to be sure our model loads correctly.  Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
+We aren't ready to try out the authentication just yet; we only want 
+to do a quick check to be sure our model loads correctly.  Press 
+C<Ctrl-C> to kill the previous server instance (if it's still running) 
+and restart it:
 
     $ script/myapp_server.pl
 
@@ -315,21 +229,23 @@ Look for the three new model objects in the startup debug output:
     | MyApp::Controller::Root                                           | instance |
     | MyApp::Model::MyAppDB                                             | instance |
     | MyApp::Model::MyAppDB::Author                                     | class    |
-    | MyApp::Model::MyAppDB::Book                                       | class    |
-    | MyApp::Model::MyAppDB::BookAuthor                                 | class    |
-    | MyApp::Model::MyAppDB::Role                                       | class    |
-    | MyApp::Model::MyAppDB::User                                       | class    |
-    | MyApp::Model::MyAppDB::UserRole                                   | class    |
+    | MyApp::Model::MyAppDB::Books                                      | class    |
+    | MyApp::Model::MyAppDB::BookAuthors                                | class    |
+    | MyApp::Model::MyAppDB::Roles                                      | class    |
+    | MyApp::Model::MyAppDB::Users                                      | class    |
+    | MyApp::Model::MyAppDB::UserRoles                                  | class    |
     | MyApp::View::TT                                                   | instance |
     '-------------------------------------------------------------------+----------'
     ...
 
-Again, notice that your "result source" classes have been "re-loaded" by Catalyst under C<MyApp::Model>.
+Again, notice that your "result source" classes have been "re-loaded" 
+by Catalyst under C<MyApp::Model>.
 
 
 =head2 Include Authentication and Session Plugins
 
-Edit C<lib/MyApp.pm> and update it as follows (everything below C<StackTrace> is new):
+Edit C<lib/MyApp.pm> and update it as follows (everything below 
+C<StackTrace> is new):
 
     use Catalyst qw/
             -Debug
@@ -345,71 +261,89 @@ Edit C<lib/MyApp.pm> and update it as follows (everything below C<StackTrace> is
             Session::State::Cookie
             /;
 
-The C<Authentication> plugin supports
-Authentication while the C<Session> plugins are required to maintain
-state across multiple HTTP requests.  
+The C<Authentication> plugin supports Authentication while the 
+C<Session> plugins are required to maintain state across multiple HTTP 
+requests.  
 
-Note that the only required Authentication class is the main
-one. This is a change that occured in version 0.09999_01
-of the C<Authentication> plugin. You B<do not need> to specify a 
-particular Authentication::Store or Authentication::Credential plugin. 
-Instead, indicate the Store and Credential you want to use in your application
+Note that the only required Authentication class is the main one. This 
+is a change that occurred in version 0.09999_01 of the 
+C<Authentication> plugin. You B<do not need> to specify a particular 
+Authentication::Store or Authentication::Credential plugin. Instead, 
+indicate the Store and Credential you want to use in your application 
 configuration (see below).
 
-Note that there are several
-options for L<Session::Store|Catalyst::Plugin::Session::Store>
-(L<Session::Store::FastMmap|Catalyst::Plugin::Session::Store::FastMmap>
-is generally a good choice if you are on Unix; try
-L<Session::Store::File|Catalyst::Plugin::Session::Store::File> if you
-are on Win32) -- consult
-L<Session::Store|Catalyst::Plugin::Session::Store> and its subclasses
-for additional information and options (for example to use a
-database-backed session store).
+Note that there are several options for 
+L<Session::Store|Catalyst::Plugin::Session::Store> 
+(L<Session::Store::FastMmap|Catalyst::Plugin::Session::Store::FastMmap> 
+is generally a good choice if you are on Unix; try 
+L<Session::Store::File|Catalyst::Plugin::Session::Store::File> if you 
+are on Win32) -- consult 
+L<Session::Store|Catalyst::Plugin::Session::Store> and its subclasses 
+for additional information and options (for example to use a database-
+backed session store).
 
 
 =head2 Configure Authentication
 
-Although C<__PACKAGE__-E<gt>config(name =E<gt> 'value');> is still
-supported, newer Catalyst applications tend to place all configuration
-information in C<myapp.yml> and automatically load this information into
-C<MyApp-E<gt>config> using the 
-L<ConfigLoader|Catalyst::Plugin::ConfigLoader> plugin.  Here, we need
-to load several parameters that tell 
-L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>
+Although C<__PACKAGE__-E<gt>config(name =E<gt> 'value');> is still 
+supported, newer Catalyst applications tend to place all configuration 
+information in C<myapp.conf> and automatically load this information 
+into C<MyApp-E<gt>config> using the 
+L<ConfigLoader|Catalyst::Plugin::ConfigLoader> plugin.  
+
+First, as noted in Part 3 of the tutorial, Catalyst has recently 
+switched from a default config file format of YAML to 
+C<Config::General> (an apache-like format).  In case you are using
+a version of Catalyst earlier than v5.7014, delete the C<myapp.yml>
+file and simply follow the directions below to create a new
+C<myapp.conf> file.
+
+Here, we need to load several parameters that tell 
+L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> 
 where to locate information in your database.  To do this, edit the 
-C<myapp.yml> YAML and update it to match:
-
-    ---
-    name: MyApp
-    authentication:
-        default_realm: dbic
-        realms:
-            dbic:
-                credential:
-                    class: Password
-                    password_field: password
-                    password_type:  self_check
-                store:
-                    class:          DBIx::Class
-            # This is the model object created by Catalyst::Model::DBIC from your
-            # schema (you created 'MyAppDB::User' but as the Catalyst startup
-            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
-            # NOTE: Omit 'MyApp::Model' to avoid a component lookup issue in Catalyst 5.66
-                    user_class:     MyApp::Users
-            # This is the name of the field in your 'users' table that contains the user's name
-                    id_field:       username
-                    role_relation:  roles
-                    role_field:     rolename
-                    ignore_fields_in_find: [ 'remote_name' ]
+C<myapp.conf> file and update it to match:
+
+    name MyApp
+    <authentication>
+        default_realm dbic
+        <realms>
+            <dbic>
+                <credential>
+                    # Note this first definition would be the same as setting
+                    # __PACKAGE__->config->{authentication}->{realms}->{dbic}
+                    #     ->{credential} = 'Password' in lib/MyApp.pm 
+                    #
+                    # Specify that we are going to do password-based auth
+                    class Password
+                    # This is the name of the field in the users table with the
+                    # password stored in it
+                    password_field password
+                    # We are using an unencrypted password now
+                    password_type clear
+                </credential>
+                <store>
+                    # Use DBIC to retrieve username, password & role information
+                    class DBIx::Class
+                    # This is the model object created by Catalyst::Model::DBIC 
+                    # from your schema (you created 'MyAppDB::User' but as the 
+                    # Catalyst startup debug messages show, it was loaded as 
+                    # 'MyApp::Model::MyAppDB::Users').
+                    # NOTE: Omit 'MyApp::Model' here just as you would when using 
+                    # '$c->model("MyAppDB::Users)'
+                    user_class MyAppDB::Users
+                    # This is the name of the field in your 'users' table that 
+                    # contains the user's name
+                    id_field username
+                </store>
+            </dbic>
+          </realms>
+        </authentication>
 
 Inline comments in the code above explain how each field is being used.
 
-B<TIP>: Although YAML uses a very simple and easy-to-ready format, it
-does require the use of a consistent level of indenting.  Be sure you
-line up everything on a given 'level' with the same number of indents.
-Also, be sure not to use C<tab> characters (YAML does not support them
-because they are handled inconsistently across editors).
-
+Note that you can use many other config file formats with catalyst.
+See L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
+for details.
 
 =head2 Add Login and Logout Controllers
 
@@ -447,7 +381,8 @@ Then update it to match:
         # If the username and password values were found in form
         if ($username && $password) {
             # Attempt to log the user in
-            if ($c->authenticate($username, $password)) {
+            if ($c->authenticate({ username => $username, 
+                                   password => $password} )) {
                 # If successful, then let them use the application
                 $c->response->redirect($c->uri_for('/books/list'));
                 return;
@@ -462,11 +397,11 @@ Then update it to match:
     }
 
 This controller fetches the C<username> and C<password> values from the
-login form and attempts to perform a login.  If successful, it redirects
-the user to the book list page.  If the login fails, the user will stay
-at the login page but receive an error message.  If the C<username> and
-C<password> values are not present in the form, the user will be taken
-to the empty login form.
+login form and attempts to authenticate the user.  If successful, it 
+redirects the user to the book list page.  If the login fails, the user 
+will stay at the login page but receive an error message.  If the 
+C<username> and C<password> values are not present in the form, the 
+user will be taken to the empty login form.
 
 Note that we could have used something like C<sub default :Private>; 
 however, the use of C<default> actions is discouraged because it does
@@ -486,8 +421,8 @@ We make the match even more specific with the C<:Args(0)> action
 modifier -- this forces the match on I<only> C</login>, not 
 C</login/somethingelse>.
 
-Next, update the corresponding method in C<lib/MyApp/Controller/Logout.pm>
-to match:
+Next, update the corresponding method in 
+C<lib/MyApp/Controller/Logout.pm> to match:
 
     =head2 index
     
@@ -582,22 +517,41 @@ the following method:
         return 1;
     }
 
-B<Note:> Catalyst provides a number of different types of actions, such
-as C<Local>, C<Regex>, and C<Private>.  You should refer to
-L<Catalyst::Manual::Intro> for a more detailed explanation, but the
-following bullet points provide a quick introduction:
+
+B<Note:> Catalyst provides a number of different types of actions, 
+such as C<Local>, C<Regex>, C<Private> and the new C<Path>.  You 
+should refer to L<Catalyst::Manual::Intro|Catalyst::Manual::Intro> for 
+a more detailed explanation, but the following bullet points provide a 
+quick introduction:
 
 =over 4
 
 =item *
 
-The majority of application use C<Local> actions for items that respond
-to user requests and C<Private> actions for those that do not directly
-respond to user input.
+The majority of application have traditionally use C<Local> actions 
+for items that respond to user requests and C<Private> actions for 
+those that do not directly respond to user input.
+
+=item *
+
+Newer Catalyst applications tend to use C<Path> actions and the 
+C<Args> attribute because of their power and flexibility.  You can
+specify the path to match relative to the namespace of the current
+module as an argument to C<Path>.  For example C<Path('list')> in
+C<lib/MyApp/Controller/Books.pm> would match on the URL 
+C<http://localhost:3000/books/list> but C<Path('/list')> would 
+match on C<http://localhost:3000/list>.
 
 =item *
 
-There are five types of C<Private> actions: C<begin>, C<end>,
+Automatic "chaining" of actions by the dispatcher is a powerful 
+feature that allows multiple methods to handle a single URL.  See 
+L<Catalyst::DispatchType::Chained|Catalyst::DispatchType::Chained>
+for more information on chained actions.
+
+=item *
+
+There are five types of build-in C<Private> actions: C<begin>, C<end>,
 C<default>, C<index>, and C<auto>.
 
 =item *
@@ -647,7 +601,7 @@ lines to the bottom of the file:
        TT code, it's probably a little too subtle for use in "normal" 
        comments.
     %]
- </p>
+    </p>
 
 Although most of the code is comments, the middle few lines provide a
 "you are already logged in" reminder if the user returns to the login
@@ -684,7 +638,7 @@ username C<test01> and password C<mypass>, and you should be taken to
 the Book List page.
 
 Open C<root/src/books/list.tt2> and add the following lines to the
-bottom:
+bottom (below the closing </table> tag):
 
     <p>
       <a href="[% Catalyst.uri_for('/login') %]">Login</a>
@@ -766,29 +720,43 @@ Edit C<myapp.yml> and update it to match (the C<password_type> and
 C<password_hash_type> are new, everything else is the same):
 
     ---
-    name: MyApp
-    authentication:
-        dbic:
-            # Note this first definition would be the same as setting
-            # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
-            # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
-            #
-            # This is the model object created by Catalyst::Model::DBIC from your
-            # schema (you created 'MyAppDB::User' but as the Catalyst startup
-            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
-            # NOTE: Omit 'MyApp::Model' here just as you would when using 
-            # '$c->model("MyAppDB::User)'
-            user_class: MyAppDB::User
-            # This is the name of the field in your 'users' table that contains the user's name
-            user_field: username
-            # This is the name of the field in your 'users' table that contains the password
-            password_field: password
-            # Other options can go here for hashed passwords
-            # Enabled hashed passwords
-            password_type: hashed
-            # Use the SHA-1 hashing algorithm
-            password_hash_type: SHA-1
-
+    name MyApp
+    <authentication>
+        default_realm dbic
+        <realms>
+            <dbic>
+                <credential>
+                    # Note this first definition would be the same as setting
+                    # __PACKAGE__->config->{authentication}->{realms}->{dbic}
+                    #     ->{credential} = 'Password' in lib/MyApp.pm 
+                    #
+                    # Specify that we are going to do password-based auth
+                    class Password
+                    # This is the name of the field in the users table with the
+                    # password stored in it
+                    password_field password
+                    # Switch to more secure hashed passwords
+                    password_type  hashed
+                    # Use the SHA-1 hashing algorithm
+                    password_hash_type SHA-1
+                 </credential>
+                <store>
+                    # Use DBIC to retrieve username, password & role information
+                    class DBIx::Class
+                    # This is the model object created by Catalyst::Model::DBIC 
+                    # from your schema (you created 'MyAppDB::User' but as the 
+                    # Catalyst startup debug messages show, it was loaded as 
+                    # 'MyApp::Model::MyAppDB::Users').
+                    # NOTE: Omit 'MyApp::Model' here just as you would when using 
+                    # '$c->model("MyAppDB::Users)'
+                    user_class MyAppDB::Users
+                    # This is the name of the field in your 'users' table that 
+                    # contains the user's name
+                    id_field username
+                 </store>
+              </dbic>
+           </realms>
+         </authentication>
 
 =head2 Try Out the Hashed Passwords
 
@@ -801,15 +769,6 @@ You should now be able to go to L<http://localhost:3000/books/list> and
 login as before.  When done, click the "Logout" link on the login page
 (or point your browser at L<http://localhost:3000/logout>).
 
-B<Note:> If you receive the debug screen in your browser with a 
-C<Can't call method "stash" on an undefined value...> error message,
-make sure that you are using v0.07 of 
-L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>.
-The following command can be a useful way to quickly dump the version number
-of this module on your system:
-
-    perl -MCatalyst::Plugin::Authorization::ACL -e 'print $Catalyst::Plugin::Authorization::ACL::VERSION, "\n";'
-
 
 =head1 USING THE SESSION FOR FLASH
 
@@ -824,7 +783,8 @@ L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD> part of the
 tutorial.
 
 First, open C<lib/MyApp/Controller/Books.pm> and modify C<sub delete>
-to match the following:
+to match the following (everything after the model search line of code
+has changed):
 
     =head2 delete 
     
@@ -837,12 +797,12 @@ to match the following:
         my ($self, $c, $id) = @_;
     
         # Search for the book and then delete it
-        $c->model('MyAppDB::Book')->search({id => $id})->delete_all;
+        $c->model('MyAppDB::Books')->search({id => $id})->delete_all;
     
         # 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 with status msg as an arg
+        # Redirect the user back to the list page
         $c->response->redirect($c->uri_for('/books/list'));
     }
 
@@ -863,10 +823,10 @@ flash vs. the C<status_msg> query parameter:
 =head2 Try Out Flash
 
 Restart the development server and point your browser to 
-L<http://localhost:3000/books/url_create/Test/1/4> to create an extra
-book.  Click the "Return to list" link and delete the "Test" book you
-just added.  The C<flash> mechanism should retain our "Book deleted" 
-status message across the redirect.
+L<http://localhost:3000/books/url_create/Test/1/4> to create an extra 
+several books.  Click the "Return to list" link and delete one of the 
+"Test" books you just added.  The C<flash> mechanism should retain our 
+"Book deleted" status message across the redirect.
 
 B<NOTE:> While C<flash> will save information across multiple requests,
 I<it does get cleared the first time it is read>.  In general, this is
@@ -876,6 +836,43 @@ after that first time (unless you reset it).  Please refer to
 L<Catalyst::Plugin::Session|Catalyst::Plugin::Session> for additional
 information.
 
+=head2 Switch To Flash-To-Stash
+
+Although the a use of flash above is certainly an improvement over the 
+C<status_msg> we employed in Part 4 of the tutorial, the C<status_msg 
+|| Catalyst.flash.status_msg> statement is a little ugly. A nice 
+alternative is to use the C<flash_to_stash> feature that automatically 
+copies the content of flash to stash.  This makes your code controller 
+and template code work regardless of where it was directly access, a 
+forward, or a redirect. To enable C<flash_to_stash>, you can either 
+set the value in C<lib/MyApp.pm> by changing the default 
+C<__PACKAGE__-E<gt>config> setting to something like:
+
+    __PACKAGE__->config(
+            name => 'MyApp',
+            session => {flash_to_stash => 1}
+        );
+
+B<or> add the following to C<myapp.yml>:
+
+    session:
+        flash_to_stash: 1
+
+The C<__PACKAGE__-E<gt>config> option is probably preferable here 
+since it's not something you will want to change at runtime without it 
+possibly breaking some of your code.
+
+Then edit C<root/lib/site/layout> and change the C<status_msg> line
+to look like the following:
+
+    <span class="message">[% status_msg %]</span>
+
+Restart the development server and go to
+L<http://localhost:3000/books/list> in your browser.  Delete another 
+of the "Test" books you added in the previous step.  Flash should still
+maintain the status message across the redirect even though you are no
+longer explicitly accessing C<Catalyst.flash>.
+
 
 =head1 AUTHOR