X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Manual.git;a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FCookbook.pod;h=1975e5a8cc424dda4ad5bd663b179977fe1062e1;hp=75bae02494968e719d0d357caa1f8a8025305a8e;hb=97e6fe8948b1a35ced7fd86e92936e7d724e4b98;hpb=ca7528dfa8625d0ed6688f2bb93372ab3d958881 diff --git a/lib/Catalyst/Manual/Cookbook.pod b/lib/Catalyst/Manual/Cookbook.pod index 75bae02..1975e5a 100644 --- a/lib/Catalyst/Manual/Cookbook.pod +++ b/lib/Catalyst/Manual/Cookbook.pod @@ -11,7 +11,7 @@ Yummy code like your mum used to bake! =head1 Basics These recipes cover some basic stuff that is worth knowing for -catalyst developers. +Catalyst developers. =head2 Delivering a Custom Error Page @@ -62,11 +62,10 @@ nifty statistics in your debug messages. =head2 Enable debug status in the environment Normally you enable the debugging info by adding the C<-Debug> flag to -your C statement (or C<__PACKAGE__->setup(qw/-Debug/) -). However, you can also enable it using environment variable, so you -can (for example) get debug info without modifying your application -scripts. Just set C or CMYAPPE_DEBUG> to a -true value. +your C statement . However, you can also enable it using +environment variable, so you can (for example) get debug info without +modifying your application scripts. Just set C or +CMYAPPE_DEBUG> to a true value. =head2 Sessions @@ -113,14 +112,22 @@ reference. =head3 EXAMPLE - use parent qw/Catalyst/; - __PACKAGE__->setup( qw/ + package MyApp; + use Moose; + use namespace::autoclean; + + use Catalyst qw/ Session Session::Store::FastMmap Session::State::Cookie - /;) - - + /; + extends 'Catalyst'; + __PACKAGE__->setup; + + package MyApp::Controller::Foo; + use Moose; + use namespace::autoclean; + BEGIN { extends 'Catalyst::Controller' }; ## Write data into the session sub add_item : Local { @@ -205,7 +212,7 @@ See also L. =head1 Skipping your VCS's directories -Catalyst uses Module::Pluggable to load Models, Views and Controllers. +Catalyst uses Module::Pluggable to load Models, Views, and Controllers. Module::Pluggable will scan through all directories and load modules it finds. Sometimes you might want to skip some of these directories, for example when your version control system makes a subdirectory with @@ -214,7 +221,7 @@ Catalyst skips subversion and CVS directories already, there are other source control systems. Here is the configuration you need to add their directories to the list to skip. -You can make catalyst skip these directories using the Catalyst config: +You can make Catalyst skip these directories using the Catalyst config: # Configure the application __PACKAGE__->config( @@ -227,7 +234,7 @@ and other options. =head1 Users and Access Control -Most multiuser, and some single user web applications require that +Most multiuser, and some single-user web applications require that users identify themselves, and the application is often required to define those roles. The recipes below describe some ways of doing this. @@ -236,7 +243,7 @@ this. This is extensively covered in other documentation; see in particular L and the Authentication chapter -of the Tutorial at L. +of the Tutorial at L. =head2 Pass-through login (and other actions) @@ -255,68 +262,6 @@ like so: } } - -=head2 Role-based Authorization - -For more advanced access control, you may want to consider using role-based -authorization. This means you can assign different roles to each user, e.g. -"user", "admin", etc. - -The C and C methods and view template are exactly the same as -in the previous example. - -The L plugin is required when -implementing roles: - - use parent qw/Catalyst/; - __PACKAGE__->setup (qw/ - Authentication - Authentication::Credential::Password - Authentication::Store::Htpasswd - Authorization::Roles - /); - -Roles are implemented automatically when using -L: - - # no additional role configuration required - __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile"; - -Or can be set up manually when using L: - - # Authorization using a many-to-many role relationship - __PACKAGE__->config->{authorization}{dbic} = { - 'role_class' => 'My::Model::DBIC::Role', - 'role_field' => 'name', - 'user_role_user_field' => 'user', - - # DBIx::Class only (omit if using Class::DBI) - 'role_rel' => 'user_role', - - # Class::DBI only, (omit if using DBIx::Class) - 'user_role_class' => 'My::Model::CDBI::UserRole' - 'user_role_role_field' => 'role', - }; - -To restrict access to any action, you can use the C method: - - sub restricted : Local { - my ( $self, $c ) = @_; - - $c->detach("unauthorized") - unless $c->check_user_roles( "admin" ); - - # do something restricted here - } - -You can also use the C method. This just gives an -error if the current user does not have one of the required roles: - - sub also_restricted : Global { - my ( $self, $c ) = @_; - $c->assert_user_roles( qw/ user admin / ); - } - =head2 Authentication/Authorization This is done in several steps: @@ -333,7 +278,7 @@ verification>. =item Authorization Making sure the user only accesses functions you want them to -access. This is done by checking the verified users data against your +access. This is done by checking the verified user's data against your internal list of groups, or allowed persons for the current page. =back @@ -360,17 +305,17 @@ Examples: A Storage backend contains the actual data representing the users. It is queried by the credential verifiers. Updating the store is not done -within this system, you will need to do it yourself. +within this system; you will need to do it yourself. Examples: - DBIC - Storage using a database. + DBIC - Storage using a database via DBIx::Class. Minimal - Storage using a simple hash (for testing). =head3 User objects A User object is created by either the storage backend or the -credential verifier, and filled with the retrieved user information. +credential verifier, and is filled with the retrieved user information. Examples: @@ -379,7 +324,7 @@ Examples: =head3 ACL authorization ACL stands for Access Control List. The ACL plugin allows you to -regulate access on a path by path basis, by listing which users, or +regulate access on a path-by-path basis, by listing which users, or roles, have access to which paths. =head3 Roles authorization @@ -390,26 +335,54 @@ then be assigned to ACLs, or just checked when needed. =head3 Logging in When you have chosen your modules, all you need to do is call the C<< -$c->login >> method. If called with no parameters, it will try to find +$c->authenticate >> method. If called with no parameters, it will try to find suitable parameters, such as B and B, or you can pass it these values. =head3 Checking roles -Role checking is done by using the C<< $c->check_user_roles >> method, -this will check using the currently logged in user (via C<< $c->user +Role checking is done by using the C<< $c->check_user_roles >> method. +This will check using the currently logged-in user (via C<< $c->user >>). You pass it the name of a role to check, and it returns true if the user is a member. =head3 EXAMPLE - use parent qw/Catalyst/; - __PACKAGE__->setup( qw/Authentication - Authentication::Credential::Password - Authentication::Store::Htpasswd - Authorization::Roles/); + package MyApp; + use Moose; + use namespace::autoclean; + extends qw/Catalyst/; + use Catalyst qw/ + Authentication + Authorization::Roles + /; + + __PACKAGE__->config( + authentication => { + default_realm => 'test', + realms => { + test => { + credential => { + class => 'Password', + password_field => 'password', + password_type => 'self_check', + }, + store => { + class => 'Htpasswd', + file => 'htpasswd', + }, + }, + }, + }, + ); + + package MyApp::Controller::Root; + use Moose; + use namespace::autoclean; + + BEGIN { extends 'Catalyst::Controller' } - __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile"; + __PACKAGE__->config(namespace => ''); sub login : Local { my ($self, $c) = @_; @@ -417,7 +390,7 @@ the user is a member. if ( my $user = $c->req->param("user") and my $password = $c->req->param("password") ) { - if ( $c->login( $user, $password ) ) { + if ( $c->authenticate( username => $user, password => $password ) ) { $c->res->body( "hello " . $c->user->name ); } else { # login incorrect @@ -439,11 +412,11 @@ the user is a member. =head3 Using authentication in a testing environment -Ideally, to write tests for authentication/authorization code one -would first set up a test database with known data, then use +Ideally, to write tests for authentication/authorization code one would +first set up a test database with known data, then use L to simulate a user logging -in. Unfortunately the former can be rather awkward, which is why it's -a good thing that the authentication framework is so flexible. +in. Unfortunately this can be rather awkward, which is why it's a good +thing that the authentication framework is so flexible. Instead of using a test database, one can simply change the authentication store to something a bit easier to deal with in a @@ -451,32 +424,23 @@ testing environment. Additionally, this has the advantage of not modifying one's database, which can be problematic if one forgets to use the testing instead of production database. -e.g., - - use Catalyst::Plugin::Authentication::Store::Minimal::Backend; - - # Sets up the user `test_user' with password `test_pass' - MyApp->default_auth_store( - Catalyst::Plugin::Authentication::Store::Minimal::Backend->new({ - test_user => { password => 'test_pass' }, - }) - ); - -Now, your test code can call C<$c->login('test_user', 'test_pass')> and -successfully login, without messing with the database at all. +Alternatively, if you want to authenticate real users, but not have to +worry about their passwords, you can use +L to force all users to +authenticate with a global password. =head3 More information -L has a longer explanation. +L has a longer explanation. =head2 Authorization =head3 Introduction Authorization is the step that comes after -authentication. Authentication establishes that the user agent is -really representing the user we think it's representing, and then -authorization determines what this user is allowed to do. +authentication. Authentication establishes that the user agent is really +representing the user we think it's representing, and then authorization +determines what this user is allowed to do. =head3 Role Based Access Control @@ -497,14 +461,14 @@ With this action, anyone can just come into the moose cage and feed the moose, which is a very dangerous thing. We need to restrict this action, so that only a qualified moose feeder can perform that action. -The Authorization::Roles plugin let's us perform role based access +The Authorization::Roles plugin lets us perform role based access control checks. Let's load it: use parent qw/Catalyst/; - __PACKAGE__->setup(qw/ - Authentication # yadda yadda - Authorization::Roles - /); + use Catalyst qw/ + Authentication + Authorization::Roles + /; And now our action should look like this: @@ -545,7 +509,7 @@ administration). Checking for roles all the time can be tedious and error prone. -The Authorization::ACL plugin let's us declare where we'd like checks +The Authorization::ACL plugin lets us declare where we'd like checks to be done automatically for us. For example, we may want to completely block out anyone who isn't a @@ -596,7 +560,7 @@ If this action does not exist, an error will be thrown, which you can clean up in your C private action instead. Also, it's important to note that if you restrict access to "/" then -C, C, etc will also be restricted. +C, C, etc. will also be restricted. MyApp->acl_allow_root_internals; @@ -617,11 +581,14 @@ can be used outside of Catalyst, e.g. in a cron job). It's trivial to write a simple component in Catalyst that slurps in an outside Model: package MyApp::Model::DB; + use base qw/Catalyst::Model::DBIC::Schema/; + __PACKAGE__->config( schema_class => 'Some::DBIC::Schema', connect_info => ['dbi:SQLite:foo.db', '', '', {AutoCommit=>1}]; ); + 1; and that's it! Now C is part of your @@ -634,9 +601,9 @@ See L. =head2 Create accessors to preload static data once per server instance When you have data that you want to load just once from the model at -server load instead of for each request, use mk_group_accessors to +startup, instead of for each request, use mk_group_accessors to create accessors and tie them to resultsets in your package that -inherits from DBIx::Class::Schema +inherits from DBIx::Class::Schema: package My::Schema; use base qw/DBIx::Class::Schema/; @@ -648,7 +615,7 @@ inherits from DBIx::Class::Schema sub connection { my ($self, @rest) = @_; $self->next::method(@rest); - # $self is now a live My::Schema object, complete with DB connection + # $self is now a live My::Schema object, complete with DB connection $self->ACCESSORNAME1([ $self->resultset('RESULTSOURCEMONIKER')->all ]); $self->ACCESSORNAME2([ $self->resultset('RESULTSOURCEMONIKER')->search({ COLUMN => { '<' => '30' } })->all ]); @@ -660,12 +627,12 @@ inherits from DBIx::Class::Schema and now in the controller, you can now access any of these without a per-request fetch: - $c->stash->{something} = $c->model('My::Schema')->schema->ACCESSORNAMEn; + $c->stash->{something} = $c->model('My::Schema')->schema->ACCESSORNAME; =head2 XMLRPC -Unlike SOAP, XMLRPC is a very simple (and imo elegant) web-services +Unlike SOAP, XMLRPC is a very simple (and elegant) web-services protocol, exchanging small XML messages like these: Request: @@ -718,7 +685,7 @@ later) and SOAP::Lite (for XMLRPCsh.pl). 3. Add the XMLRPC plugin to MyApp.pm - __PACKAGE__->setup( qw/-Debug Static::Simple XMLRPC/); + use Catalyst qw/-Debug Static::Simple XMLRPC/; 4. Add an API controller @@ -764,13 +731,11 @@ enforce a specific one. my ( $self, $c, $a, $b ) = @_; return RPC::XML::int->new( $a + $b ); } - - =head1 Views Views pertain to the display of your application. As with models, -catalyst is uncommonly flexible. The recipes below are just a start. +Catalyst is uncommonly flexible. The recipes below are just a start. =head2 Catalyst::View::TT @@ -780,8 +745,8 @@ display your data; you can choose to generate HTML, PDF files, or plain text if you wanted. Most Catalyst applications use a template system to generate their HTML, -and though there are several template systems available, Template -Toolkit is probably the most popular. +and though there are several template systems available, +L