3 package Catalyst::Plugin::Authentication;
5 use base qw/Class::Accessor::Fast Class::Data::Inheritable/;
8 __PACKAGE__->mk_accessors(qw/_user/);
9 __PACKAGE__->mk_classdata($_) for qw/_auth_realms/;
18 # this optimization breaks under Template::Toolkit
19 # use user_exists instead
22 # constant->import(have_want => eval { require Want });
25 our $VERSION = "0.10";
27 sub set_authenticated {
28 my ( $c, $user, $realmname ) = @_;
31 $c->request->{user} = $user; # compatibility kludge
34 $realmname = 'default';
37 if ( $c->isa("Catalyst::Plugin::Session")
38 and $c->config->{authentication}{use_session}
39 and $user->supports("session") )
41 $c->save_user_in_session($realmname, $user);
43 $user->_set_auth_realm($realmname);
45 $c->NEXT::set_authenticated($user, $realmname);
48 sub _should_save_user_in_session {
49 my ( $c, $user ) = @_;
51 $c->_auth_sessions_supported
52 and $c->config->{authentication}{use_session}
53 and $user->supports("session");
56 sub _should_load_user_from_session {
57 my ( $c, $user ) = @_;
59 $c->_auth_sessions_supported
60 and $c->config->{authentication}{use_session}
61 and $c->session_is_valid;
64 sub _auth_sessions_supported {
66 $c->isa("Catalyst::Plugin::Session");
76 if ( defined(my $user = $c->_user) ) {
79 return $c->auth_restore_user;
83 # change this to allow specification of a realm - to verify the user is part of that realm
84 # in addition to verifying that they exist.
87 return defined($c->_user) || defined($c->_user_in_session);
91 sub save_user_in_session {
92 my ( $c, $realmname, $user ) = @_;
94 $c->session->{__user_realm} = $realmname;
96 # we want to ask the backend for a user prepared for the session.
97 # but older modules split this functionality between the user and the
98 # backend. We try the store first. If not, we use the old method.
99 my $realm = $c->get_auth_realm($realmname);
100 if ($realm->{'store'}->can('for_session')) {
101 $c->session->{__user} = $realm->{'store'}->for_session($c, $user);
103 $c->session->{__user} = $user->for_session;
113 $c->isa("Catalyst::Plugin::Session")
114 and $c->config->{authentication}{use_session}
115 and $c->session_is_valid
117 delete @{ $c->session }{qw/__user __user_realm/};
120 $c->NEXT::logout(@_);
124 my ( $c, $userinfo, $realmname ) = @_;
126 $realmname ||= 'default';
127 my $realm = $c->get_auth_realm($realmname);
128 if ( $realm->{'store'} ) {
129 return $realm->{'store'}->find_user($userinfo, $c);
131 $c->log->debug('find_user: unable to locate a store matching the requested realm');
136 sub _user_in_session {
139 return unless $c->_should_load_user_from_session;
141 return $c->session->{__user};
144 sub _store_in_session {
147 # we don't need verification, it's only called if _user_in_session returned something useful
149 return $c->session->{__user_store};
152 sub auth_restore_user {
153 my ( $c, $frozen_user, $realmname ) = @_;
155 $frozen_user ||= $c->_user_in_session;
156 return unless defined($frozen_user);
158 $realmname ||= $c->session->{__user_realm};
159 return unless $realmname; # FIXME die unless? This is an internal inconsistency
161 my $realm = $c->get_auth_realm($realmname);
162 $c->_user( my $user = $realm->{'store'}->from_session( $c, $frozen_user ) );
164 # this sets the realm the user originated in.
165 $user->_set_auth_realm($realmname);
170 # we can't actually do our setup in setup because the model has not yet been loaded.
171 # So we have to trigger off of setup_finished. :-(
175 $c->_authentication_initialize();
179 ## the actual initialization routine. whee.
180 sub _authentication_initialize {
183 if ($c->_auth_realms) { return };
185 my $cfg = $c->config->{'authentication'} || {};
193 $c->_auth_realms($realmhash);
195 ## BACKWARDS COMPATIBILITY - if realm is not defined - then we are probably dealing
196 ## with an old-school config. The only caveat here is that we must add a classname
197 if (exists($cfg->{'realms'})) {
199 foreach my $realm (keys %{$cfg->{'realms'}}) {
200 $c->setup_auth_realm($realm, $cfg->{'realms'}{$realm});
203 # if we have a 'default-realm' in the config hash and we don't already
204 # have a realm called 'default', we point default at the realm specified
205 if (exists($cfg->{'default_realm'}) && !$c->get_auth_realm('default')) {
206 $c->set_default_auth_realm($cfg->{'default_realm'});
209 foreach my $storename (keys %{$cfg->{'stores'}}) {
211 store => $cfg->{'stores'}{$storename},
213 $c->setup_auth_realm($storename, $realmcfg);
221 sub setup_auth_realm {
222 my ($app, $realmname, $config) = @_;
224 $app->log->debug("Setting up $realmname");
225 if (!exists($config->{'store'}{'class'})) {
226 Carp::croak "Couldn't setup the authentication realm named '$realmname', no class defined";
230 my $storeclass = $config->{'store'}{'class'};
232 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
233 ## taken to mean C::P::A::Store::(specifiedclass)::Backend
234 if ($storeclass !~ /^\+(.*)$/ ) {
235 $storeclass = "Catalyst::Plugin::Authentication::Store::${storeclass}::Backend";
241 # a little niceness - since most systems seem to use the password credential class,
242 # if no credential class is specified we use password.
243 $config->{credential}{class} ||= "Catalyst::Plugin::Authentication::Credential::Password";
245 my $credentialclass = $config->{'credential'}{'class'};
247 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
248 ## taken to mean C::P::A::Credential::(specifiedclass)
249 if ($credentialclass !~ /^\+(.*)$/ ) {
250 $credentialclass = "Catalyst::Plugin::Authentication::Credential::${credentialclass}";
252 $credentialclass = $1;
255 # if we made it here - we have what we need to load the classes;
256 Catalyst::Utils::ensure_class_loaded( $credentialclass );
257 Catalyst::Utils::ensure_class_loaded( $storeclass );
259 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
260 # of get_user and add it to the class. this is because the auth routines use find_user,
261 # and rely on it being present. (this avoids per-call checks)
262 if (!$storeclass->can('find_user')) {
264 *{"${storeclass}::find_user"} = sub {
265 my ($self, $info) = @_;
266 my @rest = @{$info->{rest}} if exists($info->{rest});
267 $self->get_user($info->{id}, @rest);
271 $app->auth_realms->{$realmname}{'store'} = $storeclass->new($config->{'store'}, $app);
272 if ($credentialclass->can('new')) {
273 $app->auth_realms->{$realmname}{'credential'} = $credentialclass->new($config->{'credential'}, $app);
275 # if the credential class is not actually a class - has no 'new' operator, we wrap it,
276 # once again - to allow our code to be simple at runtime and allow non-OO packages to function.
277 my $wrapperclass = 'Catalyst::Plugin::Authentication::Credential::Wrapper';
278 Catalyst::Utils::ensure_class_loaded( $wrapperclass );
279 $app->auth_realms->{$realmname}{'credential'} = $wrapperclass->new($config->{'credential'}, $app);
285 return($self->_auth_realms);
289 my ($app, $realmname) = @_;
290 return $app->auth_realms->{$realmname};
293 sub set_default_auth_realm {
294 my ($app, $realmname) = @_;
296 if (exists($app->auth_realms->{$realmname})) {
297 $app->auth_realms->{'default'} = $app->auth_realms->{$realmname};
299 return $app->get_auth_realm('default');
303 my ($app, $userinfo, $realmname) = @_;
306 $realmname = 'default';
309 my $realm = $app->get_auth_realm($realmname);
311 if ($realm && exists($realm->{'credential'})) {
312 my $user = $realm->{'credential'}->authenticate($app, $realm->{store}, $userinfo);
314 $app->set_authenticated($user, $realmname);
318 $app->log->debug("The realm requested, '$realmname' does not exist," .
319 " or there is no credential associated with it.")
324 ## BACKWARDS COMPATIBILITY -- Warning: Here be monsters!
326 # What follows are backwards compatibility routines - for use with Stores and Credentials
327 # that have not been updated to work with C::P::Authentication v0.10.
328 # These are here so as to not break people's existing installations, but will go away
329 # in a future version.
331 # The old style of configuration only supports a single store, as each store module
332 # sets itself as the default store upon being loaded. This is the only supported
333 # 'compatibility' mode.
337 my ( $c, $uid, @rest ) = @_;
339 return $c->find_user( {'id' => $uid, 'rest'=>\@rest }, 'default' );
343 ## this should only be called when using old-style authentication plugins. IF this gets
344 ## called in a new-style config - it will OVERWRITE the store of your default realm. Don't do it.
345 ## also - this is a partial setup - because no credential is instantiated... in other words it ONLY
346 ## works with old-style auth plugins and C::P::Authentication in compatibility mode. Trying to combine
347 ## this with a realm-type config will probably crash your app.
348 sub default_auth_store {
351 if ( my $new = shift ) {
352 $self->auth_realms->{'default'}{'store'} = $new;
353 my $storeclass = ref($new);
355 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
356 # of get_user and add it to the class. this is because the auth routines use find_user,
357 # and rely on it being present. (this avoids per-call checks)
358 if (!$storeclass->can('find_user')) {
360 *{"${storeclass}::find_user"} = sub {
361 my ($self, $info) = @_;
362 my @rest = @{$info->{rest}} if exists($info->{rest});
363 $self->get_user($info->{id}, @rest);
368 return $self->get_auth_realm('default')->{'store'};
371 ## BACKWARDS COMPATIBILITY
372 ## this only ever returns a hash containing 'default' - as that is the only
373 ## supported mode of calling this.
374 sub auth_store_names {
377 my %hash = ( $self->get_auth_realm('default')->{'store'} => 'default' );
381 my ( $self, $name ) = @_;
383 if ($name ne 'default') {
384 Carp::croak "get_auth_store called on non-default realm '$name'. Only default supported in compatibility mode";
386 $self->default_auth_store();
390 sub get_auth_store_name {
391 my ( $self, $store ) = @_;
395 # sub auth_stores is only used internally - here for completeness
399 my %hash = ( 'default' => $self->get_auth_realm('default')->{'store'});
415 Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst
416 authentication framework.
425 $c->authenticate({ username => 'myusername', password => 'mypassword' });
426 my $age = $c->user->get('age');
431 The authentication plugin provides generic user support for Catalyst apps. It
432 is the basis for both authentication (checking the user is who they claim to
433 be), and authorization (allowing the user to do what the system authorises
436 Using authentication is split into two parts. A Store is used to actually
437 store the user information, and can store any amount of data related to the
438 user. Credentials are used to verify users, using information from the store,
439 given data from the frontend. A Credential and a Store are paired to form a
440 'Realm'. A Catalyst applicaiton using the authentication framework must have
441 at least one realm, and may have multiple.
443 To implement authentication in a Catalyst application you need to add this
444 module, and specify at least one realm in the configuration.
446 Authentication data can also be stored in a session, if the application
447 is using the L<Catalyst::Plugin::Session> module.
451 =head2 The Authentication/Authorization Process
453 Web applications typically need to identify a user - to tell the user apart
454 from other users. This is usually done in order to display private information
455 that is only that user's business, or to limit access to the application so
456 that only certain entities can access certain parts.
458 This process is split up into several steps. First you ask the user to identify
459 themselves. At this point you can't be sure that the user is really who they
462 Then the user tells you who they are, and backs this claim with some piece of
463 information that only the real user could give you. For example, a password is
464 a secret that is known to both the user and you. When the user tells you this
465 password you can assume they're in on the secret and can be trusted (ignore
466 identity theft for now). Checking the password, or any other proof is called
467 B<credential verification>.
469 By this time you know exactly who the user is - the user's identity is
470 B<authenticated>. This is where this module's job stops, and your application
471 or other plugins step in.
473 The next logical step is B<authorization>, the process of deciding what a user
474 is (or isn't) allowed to do. For example, say your users are split into two
475 main groups - regular users and administrators. You want to verify that the
476 currently logged in user is indeed an administrator before performing the
477 actions in an administrative part of your application. These decisionsmay be
478 made within your application code using just the information available after
479 authentication, or it may be facilitated by a number of plugins.
481 =head2 The Components In This Framework
485 Configuration of the Catalyst::Plugin::Authentication framework is done in
486 terms of realms. In simplest terms, a realm is a pairing of a Credential
487 verifier and a User storage (Store) backend.
489 An application can have any number of Realms, each of which operates
490 independant of the others. Each realm has a name, which is used to identify it
491 as the target of an authentication request. This name can be anything, such as
492 'users' or 'members'. One realm must be defined as the default_realm, which is
493 used when no realm name is specified. More information about configuring
494 realms is available in the configuration section.
496 =head3 Credential Verifiers
498 When user input is transferred to the L<Catalyst> application (typically via
499 form inputs) the application may pass this information into the authentication
500 system through the $c->authenticate() method. From there, it is passed to the
501 appropriate Credential verifier.
503 These plugins check the data, and ensure that it really proves the user is who
506 =head3 Storage Backends
508 The authentication data also identifies a user, and the Storage Backend modules
509 use this data to locate and return a standardized object-oriented
510 representation of a user.
512 When a user is retrieved from a store it is not necessarily authenticated.
513 Credential verifiers accept a set of authentication data and use this
514 information to retrieve the user from the store they are paired with.
516 =head3 The Core Plugin
518 This plugin on its own is the glue, providing realm configuration, session
519 integration, and other goodness for the other plugins.
523 More layers of plugins can be stacked on top of the authentication code. For
524 example, L<Catalyst::Plugin::Session::PerUser> provides an abstraction of
525 browser sessions that is more persistent per users.
526 L<Catalyst::Plugin::Authorization::Roles> provides an accepted way to separate
527 and group users into categories, and then check which categories the current
532 Let's say we were storing users in a simple perl hash. Users are
533 verified by supplying a password which is matched within the hash.
535 This means that our application will begin like this:
543 __PACKAGE__->config->{authentication} =
545 default_realm => 'members',
555 password => "s00p3r",
557 roles => [qw/edit delete/],
560 password => "s3cr3t",
561 roles => [qw/comment/],
570 This tells the authentication plugin what realms are available, which
571 credential and store modules are used, and the configuration of each. With
572 this code loaded, we can now attempt to authenticate users.
574 To show an example of this, let's create an authentication controller:
576 package MyApp::Controller::Auth;
579 my ( $self, $c ) = @_;
581 if ( my $user = $c->req->param("user")
582 and my $password = $c->req->param("password") )
584 if ( $c->authenticate( { username => $user,
585 password => $password } ) ) {
586 $c->res->body( "hello " . $c->user->get("name") );
596 This code should be very readable. If all the necessary fields are supplied,
597 call the L<Catalyst::Plugin::Authentication/authenticate> method in the
598 controller. If it succeeds the user is logged in.
600 The credential verifier will attempt to retrieve the user whose details match
601 the authentication information provided to $c->authenticate(). Once it fetches
602 the user the password is checked and if it matches the user will be
603 B<authenticated> and C<< $c->user >> will contain the user object retrieved
606 In the above case, the default realm is checked, but we could just as easily
607 check an alternate realm. If this were an admin login, for example, we could
608 authenticate on the admin realm by simply changing the $c->authenticate()
611 if ( $c->authenticate( { username => $user,
612 password => $password }, 'admin' )l ) {
613 $c->res->body( "hello " . $c->user->get("name") );
617 Now suppose we want to restrict the ability to edit to a user with 'edit'
620 The restricted action might look like this:
623 my ( $self, $c ) = @_;
625 $c->detach("unauthorized")
626 unless $c->user_exists
627 and $c->user->get('editor') == 'yes';
629 # do something restricted here
632 This is somewhat similar to role based access control.
633 L<Catalyst::Plugin::Authentication::Store::Minimal> treats the roles field as
634 an array of role names. Let's leverage this. Add the role authorization
643 my ( $self, $c ) = @_;
645 $c->detach("unauthorized") unless $c->check_roles("edit");
647 # do something restricted here
650 This is somewhat simpler and will work if you change your store, too, since the
651 role interface is consistent.
653 Let's say your app grew, and you now have 10000 users. It's no longer
654 efficient to maintain a hash of users, so you move this data to a database.
655 You can accomplish this simply by installing the DBIx::Class Store and
656 changing your config:
658 __PACKAGE__->config->{authentication} =
660 default_realm => 'members',
667 class => 'DBIx::Class',
668 user_class => 'MyApp::Users',
669 role_column => 'roles'
675 The authentication system works behind the scenes to load your data from the
676 new source. The rest of your application is completely unchanged.
683 =item authenticate( $userinfo, $realm )
685 Attempts to authenticate the user using the information in the $userinfo hash
686 reference using the realm $realm. $realm may be omitted, in which case the
687 default realm is checked.
691 Returns the currently logged in user or undef if there is none.
695 Returns true if a user is logged in right now. The difference between
696 user_exists and user is that user_exists will return true if a user is logged
697 in, even if it has not been retrieved from the storage backend. If you only
698 need to know if the user is logged in, depending on the storage mechanism this
699 can be much more efficient.
703 Logs the user out, Deletes the currently logged in user from $c->user and the session.
705 =item find_user( $userinfo, $realm )
707 Fetch a particular users details, matching the provided user info, from the realm
717 __PACKAGE__->config->{authentication} =
719 default_realm => 'members',
726 class => 'DBIx::Class',
727 user_class => 'MyApp::Users',
728 role_column => 'roles'
736 class => '+MyApp::Authentication::Store::NetAuth',
737 authserver => '192.168.10.17'
746 Whether or not to store the user's logged in state in the session, if the
747 application is also using the L<Catalyst::Plugin::Session> plugin. This
748 value is set to true per default.
752 This defines which realm should be used as when no realm is provided to methods
753 that require a realm such as authenticate or find_user.
757 This contains the series of realm configurations you want to use for your app.
758 The only rule here is that there must be at least one. A realm consists of a
759 name, which is used to reference the realm, a credential and a store.
761 Each realm config contains two hashes, one called 'credential' and one called
762 'store', each of which provide configuration details to the respective modules.
763 The contents of these hashes is specific to the module being used, with the
764 exception of the 'class' element, which tells the core Authentication module the
765 classname to use for that entry.
767 The 'class' element follows the standard Catalyst mechanism of class
768 specification. If a class is prefixed with a +, it is assumed to be a complete
769 class name. Otherwise it is considered to be a portion of the class name. For
770 credentials, the classname 'Password', for example, is expanded to
771 Catalyst::Plugin::Authentication::Credential::Password. For stores, the
772 classname 'storename' is expanded to:
773 Catalyst::Plugin::Authentication::Store::storename::Backend.
778 ... this is where the documentation fairy got distracted and moved on to other things ...
780 =head1 INTERNAL METHODS
784 =item set_authenticated $user
786 Marks a user as authenticated. Should be called from a
787 C<Catalyst::Plugin::Authentication::Credential> plugin after successful
790 This involves setting C<user> and the internal data in C<session> if
791 L<Catalyst::Plugin::Session> is loaded.
793 =item auth_restore_user $user
795 Used to restore a user from the session, by C<user> only when it's actually
798 =item save_user_in_session $user
800 Used to save the user in a session.
804 Revives a user from the session object if there is one.
808 Sets the default configuration parameters.
816 This list might not be up to date.
818 =head2 User Storage Backends
820 L<Catalyst::Plugin::Authentication::Store::Minimal>,
821 L<Catalyst::Plugin::Authentication::Store::Htpasswd>,
822 L<Catalyst::Plugin::Authentication::Store::DBIC> (also works with Class::DBI).
824 =head2 Credential verification
826 L<Catalyst::Plugin::Authentication::Credential::Password>,
827 L<Catalyst::Plugin::Authentication::Credential::HTTP>,
828 L<Catalyst::Plugin::Authentication::Credential::TypeKey>
832 L<Catalyst::Plugin::Authorization::ACL>,
833 L<Catalyst::Plugin::Authorization::Roles>
835 =head2 Internals Documentation
837 L<Catalyst::Plugin::Authentication::Store>
841 L<Catalyst::Plugin::Session>,
842 L<Catalyst::Plugin::Session::PerUser>
844 =head1 DON'T SEE ALSO
846 This module along with its sub plugins deprecate a great number of other
847 modules. These include L<Catalyst::Plugin::Authentication::Simple>,
848 L<Catalyst::Plugin::Authentication::CDBI>.
850 At the time of writing these plugins have not yet been replaced or updated, but
851 should be eventually: L<Catalyst::Plugin::Authentication::OpenID>,
852 L<Catalyst::Plugin::Authentication::LDAP>,
853 L<Catalyst::Plugin::Authentication::CDBI::Basic>,
854 L<Catalyst::Plugin::Authentication::Basic::Remote>.
857 =head1 COMPATIBILITY ROUTINES
861 In version 0.10 of the L<Catalyst::Plugin::Authentication> plugin, the API
862 used changed. For app developers, this change is fairly minor, but for
863 Credential and Store authors, the changes are significant. The items below are
864 still present in the plugin, though using them is deprecated. They remain only
865 as a transition tool, for those sites which can not be upgraded to use the new
866 system due to local customizations, or use of Credential / store modules that
867 have not yet been updated.
869 =head1 METHODS FOR STORE MANAGEMENT
873 =item default_auth_store
875 Return the store whose name is 'default'.
877 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
878 or by using a Store plugin:
880 use Catalyst qw/Authentication Authentication::Store::Minimal/;
882 Sets the default store to
883 L<Catalyst::Plugin::Authentication::Store::Minimal::Backend>.
886 =item get_auth_store $name
888 Return the store whose name is $name.
890 =item get_auth_store_name $store
892 Return the name of the store $store.
896 A hash keyed by name, with the stores registered in the app.
898 =item auth_store_names
900 A ref-hash keyed by store, which contains the names of the stores.
902 =item register_auth_stores %stores_by_name
904 Register stores into the application.
912 Yuval Kogman, C<nothingmuch@woobling.org>
918 =head1 COPYRIGHT & LICENSE
920 Copyright (c) 2005 the aforementioned authors. All rights
921 reserved. This program is free software; you can redistribute
922 it and/or modify it under the same terms as Perl itself.