Move mention of different "stash styles" to Ch2
[catagits/Catalyst-Manual.git] / lib / Catalyst / Manual / Tutorial / 05_Authentication.pod
index 6c41ec7..358c398 100644 (file)
@@ -81,6 +81,7 @@ add the role information here although it will not be used until the
 authorization section, Chapter 6).  Create a new SQL script file by opening
 C<myapp02.sql> in your editor and insert:
 
+    PRAGMA foreign_keys = ON;
     --
     -- Add user and role tables, along with a many-to-many join table
     --
@@ -98,8 +99,8 @@ C<myapp02.sql> in your editor and insert:
             role TEXT
     );
     CREATE TABLE user_role (
-            user_id INTEGER,
-            role_id INTEGER,
+            user_id INTEGER REFERENCES user(id) ON DELETE CASCADE ON UPDATE CASCADE,
+            role_id INTEGER REFERENCES role(id) ON DELETE CASCADE ON UPDATE CASCADE,
             PRIMARY KEY (user_id, role_id)
     );
     --
@@ -119,6 +120,7 @@ Then load this into the C<myapp.db> database with the following command:
 
     $ sqlite3 myapp.db < myapp02.sql
 
+
 =head2 Add User and Role Information to DBIC Schema
 
 Although we could manually edit the DBIC schema information to include
@@ -126,7 +128,8 @@ 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:
 
     $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
-        create=static components=TimeStamp dbi:SQLite:myapp.db
+        create=static components=TimeStamp dbi:SQLite:myapp.db \
+        on_connect_do="PRAGMA foreign_keys = ON"
      exists "/root/dev/MyApp/script/../lib/MyApp/Model"
      exists "/root/dev/MyApp/script/../t"
     Dumping manual schema for MyApp::Schema to directory /root/dev/MyApp/script/../lib ...
@@ -150,63 +153,18 @@ DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and the closing C<1;>:
 
 C<lib/MyApp/Schema/Result/User.pm>:
 
-    #
-    # Set relationships:
-    #
-    
-    # has_many():
-    #   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 *foreign* table (aka, foreign key in peer table)
-    __PACKAGE__->has_many(map_user_role => 'MyApp::Schema::Result::UserRole', 'user_id');
-    
+   
     # 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/MyApp/Schema/Result/Role.pm>:
-
-    #
-    # Set relationships:
-    #
-    
-    # has_many():
-    #   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 *foreign* table (aka, foreign key in peer table)
-    __PACKAGE__->has_many(map_user_role => 'MyApp::Schema::Result::UserRole', 'role_id');
+    __PACKAGE__->many_to_many(roles => 'user_roles', 'role');
 
 
-C<lib/MyApp/Schema/Result/UserRole.pm>:
-
-    #
-    # Set relationships:
-    #
-    
-    # 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(user => 'MyApp::Schema::Result::User', '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 => 'MyApp::Schema::Result::Role', 'role_id');
-
-The code for these three sets of updates is obviously very similar to
-the edits we made to the C<Book>, C<Author>, and C<BookAuthor>
-classes created in Chapter 3.
+The code for this update is obviously very similar to the edits we made to the
+C<Book> and C<Author> classes created in Chapter 3.
 
 Note that we do not need to make any change to the
 C<lib/MyApp/Schema.pm> schema file.  It simply tells DBIC to load all
@@ -253,7 +211,8 @@ Edit C<lib/MyApp.pm> and update it as follows (everything below
 C<StackTrace> is new):
 
     # Load plugins
-    use Catalyst qw/-Debug
+    use Catalyst qw/
+                    -Debug
                     ConfigLoader
                     Static::Simple
     
@@ -267,8 +226,9 @@ C<StackTrace> is new):
                     /;
 
 B<Note:> As discussed in MoreCatalystBasics, different versions of
-C<Catalyst::Devel> have used a variety of methods to load the plugins.
-You can put the plugins in the C<use Catalyst> statement if you prefer.
+C<Catalyst::Devel> have used a variety of methods to load the plugins,
+but we are going to use the current Catalyst 5.8X practice of putting
+them on the C<use Catalyst> line.
 
 The C<Authentication> plugin supports Authentication while the
 C<Session> plugins are required to maintain state across multiple HTTP
@@ -284,17 +244,17 @@ configuration (see below).
 Make sure you include the additional plugins as new dependencies in
 the Makefile.PL file something like this:
 
-    requires (
-        'Catalyst::Plugin::Authentication' => '0',
-        'Catalyst::Plugin::Session' => '0',
-        'Catalyst::Plugin::Session::Store::FastMmap' => '0',
-        'Catalyst::Plugin::Session::State::Cookie' => '0',
-    );
+    requires 'Catalyst::Plugin::Authentication';
+    requires 'Catalyst::Plugin::Session';
+    requires 'Catalyst::Plugin::Session::Store::FastMmap';
+    requires 'Catalyst::Plugin::Session::State::Cookie';
 
 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::Memcached|Catalyst::Plugin::Session::Store::Memcached> or
+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
@@ -333,9 +293,8 @@ for the tutorial, but if you wish to use C<myapp.conf>, just convert
 to the following code:
 
     <Plugin::Authentication>
-        use_session 1
         <default>
-            password_type self_check
+            password_type clear
             user_model    DB::User
             class         SimpleDB
         </default>
@@ -386,8 +345,8 @@ and update the definition of C<sub index> to match:
         my ($self, $c) = @_;
     
         # Get the username and password from form
-        my $username = $c->request->params->{username} || "";
-        my $password = $c->request->params->{password} || "";
+        my $username = $c->request->params->{username};
+        my $password = $c->request->params->{password};
     
         # If the username and password values were found in form
         if ($username && $password) {
@@ -402,6 +361,9 @@ and update the definition of C<sub index> to match:
                 # Set an error message
                 $c->stash->{error_msg} = "Bad username or password.";
             }
+        } else {
+            # Set an error message
+            $c->stash->{error_msg} = "Empty username or password.";
         }
     
         # If either of above don't work out, send to the login page
@@ -488,9 +450,8 @@ Create a login form by opening C<root/src/login.tt2> and inserting:
 We need something that provides enforcement for the authentication
 mechanism -- a I<global> mechanism that prevents users who have not
 passed authentication from reaching any pages except the login page.
-This is generally done via an C<auto> action/method (prior to Catalyst
-v5.66, this sort of thing would go in C<MyApp.pm>, but starting in
-v5.66, the preferred location is C<lib/MyApp/Controller/Root.pm>).
+This is generally done via an C<auto> action/method in 
+C<lib/MyApp/Controller/Root.pm>.
 
 Edit the existing C<lib/MyApp/Controller/Root.pm> class file and insert
 the following method:
@@ -504,7 +465,7 @@ the following method:
     # Note that 'auto' runs after 'begin' but before your actions and that
     # 'auto's "chain" (all from application path to most specific class are run)
     # See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
-    sub auto : Private {
+    sub auto :Private {
         my ($self, $c) = @_;
     
         # Allow unauthenticated users to reach the login page.  This
@@ -649,16 +610,6 @@ between the browser and your application, consider using SSL/TLS, made
 easy with the Catalyst plugin Catalyst::Plugin:RequireSSL.
 
 
-=head2 Install DBIx::Class::EncodedColumn
-
-L<DBIx::Class::EncodedColumn|DBIx::Class::EncodedColumn> provides features
-that can greatly simplify the maintenance of passwords.  It's currently 
-not available as a .deb package in the normal Debian repositories, so let's
-install it directly from CPAN:
-
-    $ sudo cpan DBIx::Class::EncodedColumn
-
-
 =head2 Re-Run the DBIC::Schema Model Helper to Include DBIx::Class::EncodedColumn
 
 Next, we can re-run the model helper to have it include 
@@ -668,14 +619,15 @@ saw in Chapters 3 and 4, but add C<,EncodedColumn> to the C<components>
 argument:
 
     $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
-        create=static components=TimeStamp,EncodedColumn dbi:SQLite:myapp.db
+        create=static components=TimeStamp,EncodedColumn dbi:SQLite:myapp.db \
+        on_connect_do="PRAGMA foreign_keys = ON"
 
 If you then open one of the Result Classes, you will see that it 
 includes EncodedColumn in the C<load_components> line.  Take a look at 
 C<lib/MyApp/Schema/Result/User.pm> since that's the main class where we
 want to use hashed and salted passwords:
 
-    __PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "EncodedColumn", "Core");
+    __PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "EncodedColumn");
 
 
 =head2 Modify the "password" Column to Use EncodedColumn
@@ -745,12 +697,24 @@ password stored for this user.
 
 Then run the following command:
 
-    $ perl -Ilib set_hashed_passwords.pl
+    $ DBIC_TRACE=1 perl -Ilib set_hashed_passwords.pl
 
-We had to use the C<-Ilib> arguement to tell perl to look under the 
+We had to use the C<-Ilib> argument to tell perl to look under the 
 C<lib> directory for our C<MyApp::Schema> model.
 
-Then dump the users table to verify that it worked:
+The DBIC_TRACE output should show that the update worked:
+
+    $ DBIC_TRACE=1 perl -Ilib set_hashed_passwords.pl
+    SELECT me.id, me.username, me.password, me.email_address, 
+    me.first_name, me.last_name, me.active FROM user me: 
+    UPDATE user SET password = ? WHERE ( id = ? ): 
+    'oXiyAcGOjowz7ISUhpIm1IrS8AxSZ9r4jNjpX9VnVeQmN6GRtRKTz', '1'
+    UPDATE user SET password = ? WHERE ( id = ? ): 
+    'PmyEPrkB8EGwvaF/DvJm7LIfxoZARjv8ygFIR7pc1gEA1OfwHGNzs', '2'
+    UPDATE user SET password = ? WHERE ( id = ? ): 
+    'h7CS1Fm9UCs4hjcbu2im0HumaHCJUq4Uriac+SQgdUMUfFSoOrz3c', '3'
+
+But we can further confirm our actions by dumping the users table:
 
     $ sqlite3 myapp.db "select * from user"
     1|test01|38d3974fa9e9263099f7bc2574284b2f55473a9bM=fwpX2NR8|t01@na.com|Joe|Blow|1
@@ -758,15 +722,17 @@ Then dump the users table to verify that it worked:
     3|test03|af929a151340c6aed4d54d7e2651795d1ad2e2f7UW8dHoGv9z|t03@na.com|No|Go|0
 
 As you can see, the passwords are much harder to steal from the 
-database.  Also note that this demonstrates how to use a DBIx::Class 
+database (not only are the hashes stored, but every hash is different 
+even though the passwords are the same because of the added "salt" 
+value).  Also note that this demonstrates how to use a DBIx::Class 
 model outside of your web application -- a very useful feature in many 
 situations.
 
 
 =head2 Enable Hashed and Salted Passwords
 
-Edit C<lib/MyApp.pm> and update it to match the following text (the only change
-is to the C<password_type> field):
+Edit C<lib/MyApp.pm> and update it to match the following text (the 
+only change is to the C<password_type> field):
 
     # Configure SimpleDB Authentication
     __PACKAGE__->config->{'Plugin::Authentication'} = {
@@ -844,9 +810,9 @@ flash vs. the C<status_msg> query parameter:
     ...
 
 Although the sample above only shows the C<content> div, leave the
-rest of the file intact -- the only change we made to the C<wrapper.tt2>
-was to add "C<|| c.request.params.status_msg>" to the
-C<E<lt>span class="message"E<gt>> line.
+rest of the file intact -- the only change we made to replace 
+"|| c.request.params.status_msg" with "c.flash.status_msg" in the 
+C<< <span class="message"> >> line.
 
 
 =head2 Try Out Flash
@@ -879,7 +845,7 @@ C<__PACKAGE__-E<gt>config> setting to something like:
 
     __PACKAGE__->config(
             name    => 'MyApp',
-            session => {flash_to_stash => 1}
+            session => { flash_to_stash => 1 },
         );
 
 B<or> add the following to C<myapp.conf>:
@@ -910,7 +876,7 @@ Kennedy Clark, C<hkclark@gmail.com>
 
 Please report any errors, issues or suggestions to the author.  The
 most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.70/trunk/lib/Catalyst/Manual/Tutorial/>.
+L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
 
 Copyright 2006-2008, Kennedy Clark, under Creative Commons License
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).