X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FTutorial%2FAuthentication.pod;h=996ac4c37fe3c4edfde14fe710b9e9cfd47aca03;hb=45d511e089515b7a59b6d943bb79a9fbf0ba45d4;hp=673a93dc12f4b1f93e3e5f246c213d74a5228904;hpb=6d0971ad6e54665ea8c239499e715a64fe1b83ca;p=catagits%2FCatalyst-Manual.git
diff --git a/lib/Catalyst/Manual/Tutorial/Authentication.pod b/lib/Catalyst/Manual/Tutorial/Authentication.pod
index 673a93d..996ac4c 100644
--- a/lib/Catalyst/Manual/Tutorial/Authentication.pod
+++ b/lib/Catalyst/Manual/Tutorial/Authentication.pod
@@ -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 for the Catalyst tutorial.
+This is B for the Catalyst tutorial.
L
@@ -21,30 +21,34 @@ L
=item 3
-L
+L
=item 4
-B
+L
=item 5
-L
+B
=item 6
-L
+L
=item 7
-L
+L
=item 8
-L
+L
=item 9
+L
+
+=item 10
+
L
=back
@@ -52,8 +56,8 @@ L
=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 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
+option on the DBIC model helper to do most of the work for us:
-Edit C and update the contents to match (only the
-C [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 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:
-C:
-
- 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:
# 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:
+C:
- 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:
# 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:
+C:
- 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:
# 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, C, and C 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, C, and C
+classes created in Part 3.
+
+Note that we do not need to make any change to the
+C schema file. It simple tells DBIC to
+load all of the result source files it finds in below the
+C 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 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 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.
+Again, notice that your "result source" classes have been "re-loaded"
+by Catalyst under C.
=head2 Include Authentication and Session Plugins
-Edit C and update it as follows (everything below C is new):
+Edit C and update it as follows (everything below
+C is new):
use Catalyst qw/
-Debug
@@ -345,71 +261,89 @@ Edit C and update it as follows (everything below C is
Session::State::Cookie
/;
-The C plugin supports
-Authentication while the C plugins are required to maintain
-state across multiple HTTP requests.
+The C plugin supports Authentication while the
+C 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 plugin. You B 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 plugin. You B 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
-(L
-is generally a good choice if you are on Unix; try
-L if you
-are on Win32) -- consult
-L 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
+(L
+is generally a good choice if you are on Unix; try
+L if you
+are on Win32) -- consult
+L and its subclasses
+for additional information and options (for example to use a database-
+backed session store).
=head2 Configure Authentication
-Although C<__PACKAGE__-Econfig(name =E 'value');> is still
-supported, newer Catalyst applications tend to place all configuration
-information in C and automatically load this information into
-Cconfig> using the
-L plugin. Here, we need
-to load several parameters that tell
-L
+Although C<__PACKAGE__-Econfig(name =E 'value');> is still
+supported, newer Catalyst applications tend to place all configuration
+information in C and automatically load this information
+into Cconfig> using the
+L plugin.
+
+First, as noted in Part 3 of the tutorial, Catalyst has recently
+switched from a default config file format of YAML to
+C (an apache-like format). In case you are using
+a version of Catalyst earlier than v5.7014, delete the C
+file and simply follow the directions below to create a new
+C file.
+
+Here, we need to load several parameters that tell
+L
where to locate information in your database. To do this, edit the
-C 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 file and update it to match:
+
+ name MyApp
+
+ default_realm dbic
+
+
+
+ # 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
+
+
+ # 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
+
+
+
+
Inline comments in the code above explain how each field is being used.
-B: 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 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
+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->login($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 and C 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 and
-C 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 and C 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;
however, the use of C 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 C, not
C.
-Next, update the corresponding method in C
-to match:
+Next, update the corresponding method in
+C to match:
=head2 index
@@ -582,22 +517,41 @@ the following method:
return 1;
}
-B Catalyst provides a number of different types of actions, such
-as C, C, and C. You should refer to
-L for a more detailed explanation, but the
-following bullet points provide a quick introduction:
+
+B Catalyst provides a number of different types of actions,
+such as C, C, C and the new C. You
+should refer to L for
+a more detailed explanation, but the following bullet points provide a
+quick introduction:
=over 4
=item *
-The majority of application use C actions for items that respond
-to user requests and C actions for those that do not directly
-respond to user input.
+The majority of application have traditionally use C actions
+for items that respond to user requests and C actions for
+those that do not directly respond to user input.
+
+=item *
+
+Newer Catalyst applications tend to use C actions and the
+C 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. For example C in
+C would match on the URL
+C but C would
+match on C.
=item *
-There are five types of C actions: C, C,
+Automatic "chaining" of actions by the dispatcher is a powerful
+feature that allows multiple methods to handle a single URL. See
+L
+for more information on chained actions.
+
+=item *
+
+There are five types of build-in C actions: C, C,
C, C, and C.
=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.
%]
-
+
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 and password C, and you should be taken to
the Book List page.
Open C and add the following lines to the
-bottom:
+bottom (below the closing tag):
Login
@@ -762,33 +716,47 @@ algorithms are supported. See C for more information.
=head2 Enable SHA-1 Hash Passwords in
C
-Edit C and update it to match (the C and
+Edit C and update it to match (the C and
C 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
+
+ default_realm dbic
+
+
+
+ # 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
+
+
+ # 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
+
+
+
+
=head2 Try Out the Hashed Passwords
@@ -801,15 +769,6 @@ You should now be able to go to L and
login as before. When done, click the "Logout" link on the login page
(or point your browser at L).
-B If you receive the debug screen in your browser with a
-C error message,
-make sure that you are using v0.07 of
-L.
-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 part of the
tutorial.
First, open C and modify C
-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 query parameter:
=head2 Try Out Flash
Restart the development server and point your browser to
-L to create an extra
-book. Click the "Return to list" link and delete the "Test" book you
-just added. The C mechanism should retain our "Book deleted"
-status message across the redirect.
+L to create an extra
+several books. Click the "Return to list" link and delete one of the
+"Test" books you just added. The C mechanism should retain our
+"Book deleted" status message across the redirect.
B While C will save information across multiple requests,
I. In general, this is
@@ -876,6 +836,44 @@ after that first time (unless you reset it). Please refer to
L for additional
information.
+=head2 Switch To Flash-To-Stash
+
+Although the a use of flash above is certainly an improvement over the
+C we employed in Part 4 of the tutorial, the C statement is a little ugly. A nice
+alternative is to use the C 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, you can either
+set the value in C by changing the default
+C<__PACKAGE__-Econfig> setting to something like:
+
+ __PACKAGE__->config(
+ name => 'MyApp',
+ session => {flash_to_stash => 1}
+ );
+
+B add the following to C:
+
+
+ flash_to_stash 1
+
+
+The C<__PACKAGE__-Econfig> 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 and change the C line
+to look like the following:
+
+ [% status_msg %]
+
+Restart the development server and go to
+L 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.
+
=head1 AUTHOR