X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FPlugin%2FAuthentication.pm;h=c1a05cef875fa600d4c8f6dbdb89f7c8c943a1ed;hb=56716182b9a3b46d1cc080e858aeb169cb4564d6;hp=fc7388fd702580ec8d07564a89e1d9d73bb2d8ca;hpb=8f86f0299ac931a3a23963fd11fa2abf6f171f3a;p=catagits%2FCatalyst-Plugin-Authentication.git diff --git a/lib/Catalyst/Plugin/Authentication.pm b/lib/Catalyst/Plugin/Authentication.pm index fc7388f..c1a05ce 100644 --- a/lib/Catalyst/Plugin/Authentication.pm +++ b/lib/Catalyst/Plugin/Authentication.pm @@ -22,7 +22,7 @@ use Class::Inspector; # constant->import(have_want => eval { require Want }); #} -our $VERSION = "0.04"; +our $VERSION = "0.05"; sub set_authenticated { my ( $c, $user ) = @_; @@ -85,10 +85,10 @@ sub logout { } sub get_user { - my ( $c, $uid ) = @_; + my ( $c, $uid, @rest ) = @_; if ( my $store = $c->default_auth_store ) { - return $store->get_user($uid); + return $store->get_user( $uid, @rest ); } else { Catalyst::Exception->throw( @@ -228,12 +228,223 @@ the user. Multiple stores can be accessed from within one application. Credentials are used to verify users, using the store, given data from the frontend. -To implement authentication in a catalyst application you need to add this +To implement authentication in a Catalyst application you need to add this module, plus at least one store and one credential module. Authentication data can also be stored in a session, if the application is using the L module. +=head1 INTRODUCTION + +=head2 The Authentication/Authorization Process + +Web applications typically need to identify a user - to tell the user apart +from other users. This is usually done in order to display private information +that is only that user's business, or to limit access to the application so +that only certain entities can access certain parts. + +This process is split up into several steps. First you ask the user to identify +themselves. At this point you can't be sure that the user is really who they +claim to be. + +Then the user tells you who they are, and backs this claim with some piece of +information that only the real user could give you. For example, a password is +a secret that is known to both the user and you. When the user tells you this +password you can assume they're in on the secret and can be trusted (ignore +identity theft for now). Checking the password, or any other proof is called +B. + +By this time you know exactly who the user is - the user's identity is +B. This is where this module's job stops, and other plugins step +in. The next logical step is B, the process of deciding what a +user is (or isn't) allowed to do. For example, say your users are split into +two main groups - regular users and administrators. You should verify that the +currently logged in user is indeed an administrator before performing the +actions of an administrative part of your application. One way to do this is +with role based access control. + +=head2 The Components In This Framework + +=head3 Credential Verifiers + +When user input is transferred to the L application (typically via +form inputs) this data then enters the authentication framework through these +plugins. + +These plugins check the data, and ensure that it really proves the user is who +they claim to be. + +=head3 Storage Backends + +The credentials also identify a user, and this family of modules is supposed to +take this identification data and return a standardized object oriented +representation of users. + +When a user is retrieved from a store it is not necessarily authenticated. +Credential verifiers can either accept a user object, or fetch the object +themselves from the default store. + +=head3 The Core Plugin + +This plugin on its own is the glue, providing store registration, session +integration, and other goodness for the other plugins. + +=head3 Other Plugins + +More layers of plugins can be stacked on top of the authentication code. For +example, L provides an abstraction of +browser sessions that is more persistent per users. +L provides an accepted way to separate +and group users into categories, and then check which categories the current +user belongs to. + +=head1 EXAMPLE + +Let's say we were storing users in an Apache style htpasswd file. Users are +stored in that file, with a hashed password and some extra comments. Users are +verified by supplying a password which is matched with the file. + +This means that our application will begin like this: + + package MyApp; + + use Catalyst qw/ + Authentication + Authentication::Credential::Password + Authentication::Store::Htpasswd + /; + + __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile"; + +This loads the appropriate methods and also sets the htpasswd store as the +default store. + +So, now that we have the code loaded we need to get data from the user into the +credential verifier. + +Let's create an authentication controller: + + package MyApp::Controller::Auth; + + sub login : Local { + my ( $self, $c ) = @_; + + if ( my $user = $c->req->param("user") + and my $password = $c->req->param("password") ) + { + if ( $c->login( $user, $password ) ) { + $c->res->body( "hello " . $c->user->name ); + } else { + # login incorrect + } + } + else { + # invalid form input + } + } + +This code should be very readable. If all the necessary fields are supplied, +call the L method on the +controller. If that succeeds the user is logged in. + +It could be simplified though: + + sub login : Local { + my ( $self, $c ) = @_; + + if ( $c->login ) { + ... + } + } + +Since the C method knows how to find logically named parameters on it's +own. + +The credential verifier will ask the default store to get the user whose ID is +the user parameter. In this case the default store is the htpasswd one. Once it +fetches the user from the store the password is checked and if it's OK +C<< $c->user >> will contain the user object returned from the htpasswd store. + +We can also pass a user object to the credential verifier manually, if we have +several stores per app. This is discussed in +L. + +Now imagine each admin user has a comment set in the htpasswd file saying +"admin". + +A restricted action might look like this: + + sub restricted : Local { + my ( $self, $c ) = @_; + + $c->detach("unauthorized") + unless $c->user_exists + and $c->user->extra_info() eq "admin"; + + # do something restricted here + } + +This is somewhat similar to role based access control. +L treats the extra info +field as a comma separated list of roles if it's treated that way. Let's +leverage this. Add the role authorization plugin: + + use Catalyst qw/ + ... + Authorization::Roles + /; + + sub restricted : Local { + my ( $self, $c ) = @_; + + $c->detach("unauthorized") unless $c->check_roles("admin"); + + # do something restricted here + } + +This is somewhat simpler and will work if you change your store, too, since the +role interface is consistent. + +Let's say your app grew, and you now have 10000 users. It's no longer efficient +to maintain an htpasswd file, so you move this data to a database. + + use Catalyst qw/ + Authentication + Authentication::Credential::Password + Authentication::Store::DBIC + Authorization::Roles + /; + + __PACKAGE__->config->{authentication}{dbic} = ...; # see the DBIC store docs + +The rest of your code should be unchanged. Now let's say you are integrating +typekey authentication to your system. For simplicity's sake we'll assume that +the user's are still keyed in the same way. + + use Catalyst qw/ + Authentication + Authentication::Credential::Password + Authentication::Credential::TypeKey + Authentication::Store::DBIC + Authorization::Roles + /; + +And in your auth controller add a new action: + + sub typekey : Local { + my ( $self, $c ) = @_; + + if ( $c->authenticate_typekey) { # uses $c->req and Authen::TypeKey + # same stuff as the $c->login method + # ... + } + } + +You've now added a new credential verification mechanizm orthogonally to the +other components. All you have to do is make sure that the credential verifiers +pass on the same types of parameters to the store in order to retrieve user +objects. + =head1 METHODS =over 4 @@ -251,14 +462,14 @@ user from the auth store. If you're just going to say - if ( $c->user_user ) { + if ( $c->user_exists ) { # foo } else { $c->forward("login"); } -it should be more efficient than C<< $c->user >> when a user is marked in the session -but C<< $c->user >> hasn't been called yet. +it should be more efficient than C<< $c->user >> when a user is marked in the +session but C<< $c->user >> hasn't been called yet. =item logout @@ -375,22 +586,45 @@ Sets the default configuration parameters. =head1 SEE ALSO -L, +This list might not be up to date. + +=head2 User Storage Backends + L, +L, +L (also works with Class::DBI). + +=head2 Credential verification + +L, +L, +L + +=head2 Authorization + L, -L. +L + +=head2 Internals Documentation + +L + +=head2 Misc + +L, +L =head1 DON'T SEE ALSO -This module along with it's sub plugins deprecate a great number of other -modules. These include Catalyst::Plugin::Authentication::Simple, -Catalyst::Plugin::Authentication::CDBI. +This module along with its sub plugins deprecate a great number of other +modules. These include L, +L. At the time of writing these plugins have not yet been replaced or updated, but -should be eventually: Catalyst::Plugin::Authentication::OpenID, -Catalyst::Plugin::Authentication::LDAP, -Catalyst::Plugin::Authentication::CDBI::Basic, -Catalyst::Plugin::Authentication::Basic::Remote +should be eventually: L, +L, +L, +L. =head1 AUTHORS