1 package Catalyst::Plugin::Authentication;
3 use base qw/Class::Accessor::Fast Class::Data::Inheritable/;
6 __PACKAGE__->mk_accessors(qw/_user/);
14 use Catalyst::Authentication::Realm;
16 # this optimization breaks under Template::Toolkit
17 # use user_exists instead
20 # constant->import(have_want => eval { require Want });
23 our $VERSION = "0.10004";
25 sub set_authenticated {
26 my ( $c, $user, $realmname ) = @_;
29 $c->request->{user} = $user; # compatibility kludge
32 $realmname = 'default';
34 my $realm = $c->get_auth_realm($realmname);
37 Catalyst::Exception->throw(
38 "set_authenticated called with nonexistant realm: '$realmname'.");
41 if ( $c->isa("Catalyst::Plugin::Session")
42 and $c->config->{authentication}{use_session}
43 and $user->supports("session") )
45 $realm->save_user_in_session($c, $user);
47 $user->auth_realm($realm->name);
49 $c->NEXT::set_authenticated($user, $realmname);
59 if ( defined($c->_user) ) {
62 return $c->auth_restore_user;
66 # change this to allow specification of a realm - to verify the user is part of that realm
67 # in addition to verifying that they exist.
70 return defined($c->_user) || defined($c->_user_in_session);
73 # works like user_exists - except only returns true if user
74 # exists AND is in the realm requested.
76 my ($c, $realmname) = @_;
78 if (defined($c->_user)) {
79 return ($c->_user->auth_realm eq $realmname);
80 } elsif (defined($c->_user_in_session)) {
81 return ($c->session->{__user_realm} eq $realmname);
87 sub __old_save_user_in_session {
88 my ( $c, $user, $realmname ) = @_;
90 $c->session->{__user_realm} = $realmname;
92 # we want to ask the store for a user prepared for the session.
93 # but older modules split this functionality between the user and the
94 # store. We try the store first. If not, we use the old method.
95 my $realm = $c->get_auth_realm($realmname);
96 if ($realm->{'store'}->can('for_session')) {
97 $c->session->{__user} = $realm->{'store'}->for_session($c, $user);
99 $c->session->{__user} = $user->for_session;
109 $c->isa("Catalyst::Plugin::Session")
110 and $c->config->{authentication}{use_session}
111 and $c->session_is_valid
113 delete @{ $c->session }{qw/__user __user_realm/};
116 $c->NEXT::logout(@_);
120 my ( $c, $userinfo, $realmname ) = @_;
122 $realmname ||= 'default';
123 my $realm = $c->get_auth_realm($realmname);
126 Catalyst::Exception->throw(
127 "find_user called with nonexistant realm: '$realmname'.");
129 return $realm->find_user($userinfo, $c);
133 sub _user_in_session {
137 $c->isa("Catalyst::Plugin::Session")
138 and $c->config->{authentication}{use_session}
139 and $c->session_is_valid;
141 return $c->session->{__user};
144 sub auth_restore_user {
145 my ( $c, $frozen_user, $realmname ) = @_;
147 $frozen_user ||= $c->_user_in_session;
148 return unless defined($frozen_user);
150 $realmname ||= $c->session->{__user_realm};
151 return unless $realmname; # FIXME die unless? This is an internal inconsistency
153 my $realm = $c->get_auth_realm($realmname);
154 $c->_user( my $user = $realm->from_session( $c, $frozen_user ) );
156 # this sets the realm the user originated in.
157 $user->auth_realm($realmname);
163 # we can't actually do our setup in setup because the model has not yet been loaded.
164 # So we have to trigger off of setup_finished. :-(
168 $app->_authentication_initialize();
169 $app->NEXT::setup(@_);
172 ## the actual initialization routine. whee.
173 sub _authentication_initialize {
176 ## let's avoid recreating / configuring everything if we have already done it, eh?
177 if ($app->can('_auth_realms')) { return };
179 ## make classdata where it is used.
180 $app->mk_classdata( '_auth_realms' => {});
182 my $cfg = $app->config->{'Plugin::Authentication'} ||= $app->config->{'authentication'} ||= {};
184 $cfg->{use_session} = 1;
186 if (exists($cfg->{'realms'})) {
187 foreach my $realm (keys %{$cfg->{'realms'}}) {
188 $app->setup_auth_realm($realm, $cfg->{'realms'}{$realm});
190 # if we have a 'default_realm' in the config hash and we don't already
191 # have a realm called 'default', we point default at the realm specified
192 if (exists($cfg->{'default_realm'}) && !$app->get_auth_realm('default')) {
193 $app->_set_default_auth_realm($cfg->{'default_realm'});
197 ## BACKWARDS COMPATIBILITY - if realms is not defined - then we are probably dealing
198 ## with an old-school config. The only caveat here is that we must add a classname
200 ## also - we have to treat {store} as {stores}{default} - because
201 ## while it is not a clear as a valid config in the docs, it
202 ## is functional with the old api. Whee!
203 if (exists($cfg->{'store'}) && !exists($cfg->{'stores'}{'default'})) {
204 $cfg->{'stores'}{'default'} = $cfg->{'store'};
207 foreach my $storename (keys %{$cfg->{'stores'}}) {
209 store => { class => $cfg->{'stores'}{$storename} },
211 $app->setup_auth_realm($storename, $realmcfg);
218 sub setup_auth_realm {
219 my ($app, $realmname, $config) = @_;
221 my $realmclass = $config->{class};
224 $realmclass = 'Catalyst::Authentication::Realm';
225 } elsif ($realmclass !~ /^\+(.*)$/ ) {
226 $realmclass = "Catalyst::Authentication::Realm::${realmclass}";
231 Catalyst::Utils::ensure_class_loaded( $realmclass );
233 my $realm = $realmclass->new($realmname, $config, $app);
235 $app->auth_realms->{$realmname} = $realm;
237 $app->log->debug("realm initialization for '$realmname' failed.");
244 return($self->_auth_realms);
248 my ($app, $realmname) = @_;
249 return $app->auth_realms->{$realmname};
253 # Very internal method. Vital Valuable Urgent, Do not touch on pain of death.
254 # Using this method just assigns the default realm to be the value associated
255 # with the realmname provided. It WILL overwrite any real realm called 'default'
256 # so can be very confusing if used improperly. It's used properly already.
257 # Translation: don't use it.
258 sub _set_default_auth_realm {
259 my ($app, $realmname) = @_;
261 if (exists($app->auth_realms->{$realmname})) {
262 $app->auth_realms->{'default'} = $app->auth_realms->{$realmname};
264 return $app->get_auth_realm('default');
268 my ($app, $userinfo, $realmname) = @_;
271 $realmname = 'default';
274 my $realm = $app->get_auth_realm($realmname);
276 ## note to self - make authenticate throw an exception if realm is invalid.
279 return $realm->authenticate($app, $userinfo);
281 Catalyst::Exception->throw(
282 "authenticate called with nonexistant realm: '$realmname'.");
288 ## BACKWARDS COMPATIBILITY -- Warning: Here be monsters!
290 # What follows are backwards compatibility routines - for use with Stores and Credentials
291 # that have not been updated to work with C::P::Authentication v0.10.
292 # These are here so as to not break people's existing installations, but will go away
293 # in a future version.
295 # The old style of configuration only supports a single store, as each store module
296 # sets itself as the default store upon being loaded. This is the only supported
297 # 'compatibility' mode.
301 my ( $c, $uid, @rest ) = @_;
303 return $c->find_user( {'id' => $uid, 'rest'=>\@rest }, 'default' );
307 ## this should only be called when using old-style authentication plugins. IF this gets
308 ## called in a new-style config - it will OVERWRITE the store of your default realm. Don't do it.
309 ## also - this is a partial setup - because no credential is instantiated... in other words it ONLY
310 ## works with old-style auth plugins and C::P::Authentication in compatibility mode. Trying to combine
311 ## this with a realm-type config will probably crash your app.
312 sub default_auth_store {
315 my $realm = $self->get_auth_realm('default');
317 $realm = $self->setup_auth_realm('default', { class => 'Compatibility' });
319 if ( my $new = shift ) {
324 $storeclass = ref($new);
329 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
330 # of get_user and add it to the class. this is because the auth routines use find_user,
331 # and rely on it being present. (this avoids per-call checks)
332 if (!$storeclass->can('find_user')) {
334 *{"${storeclass}::find_user"} = sub {
335 my ($self, $info) = @_;
336 my @rest = @{$info->{rest}} if exists($info->{rest});
337 $self->get_user($info->{id}, @rest);
342 return $self->get_auth_realm('default')->store;
345 ## BACKWARDS COMPATIBILITY
346 ## this only ever returns a hash containing 'default' - as that is the only
347 ## supported mode of calling this.
348 sub auth_store_names {
351 my %hash = ( $self->get_auth_realm('default')->store => 'default' );
355 my ( $self, $name ) = @_;
357 if ($name ne 'default') {
358 Carp::croak "get_auth_store called on non-default realm '$name'. Only default supported in compatibility mode";
360 $self->default_auth_store();
364 sub get_auth_store_name {
365 my ( $self, $store ) = @_;
369 # sub auth_stores is only used internally - here for completeness
373 my %hash = ( 'default' => $self->get_auth_realm('default')->store);
384 Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst
385 authentication framework.
394 $c->authenticate({ username => 'myusername',
395 password => 'mypassword' });
396 my $age = $c->user->get('age');
401 The authentication plugin provides generic user support for Catalyst apps. It
402 is the basis for both authentication (checking the user is who they claim to
403 be), and authorization (allowing the user to do what the system authorises
406 Using authentication is split into two parts. A Store is used to actually
407 store the user information, and can store any amount of data related to the
408 user. Credentials are used to verify users, using information from the store,
409 given data from the frontend. A Credential and a Store are paired to form a
410 'Realm'. A Catalyst application using the authentication framework must have
411 at least one realm, and may have several.
413 To implement authentication in a Catalyst application you need to add this
414 module, and specify at least one realm in the configuration.
416 Authentication data can also be stored in a session, if the application
417 is using the L<Catalyst::Plugin::Session> module.
419 B<NOTE> in version 0.10 of this module, the interface to this module changed.
420 Please see L</COMPATIBILITY ROUTINES> for more information.
424 =head2 The Authentication/Authorization Process
426 Web applications typically need to identify a user - to tell the user apart
427 from other users. This is usually done in order to display private information
428 that is only that user's business, or to limit access to the application so
429 that only certain entities can access certain parts.
431 This process is split up into several steps. First you ask the user to identify
432 themselves. At this point you can't be sure that the user is really who they
435 Then the user tells you who they are, and backs this claim with some piece of
436 information that only the real user could give you. For example, a password is
437 a secret that is known to both the user and you. When the user tells you this
438 password you can assume they're in on the secret and can be trusted (ignore
439 identity theft for now). Checking the password, or any other proof is called
440 B<credential verification>.
442 By this time you know exactly who the user is - the user's identity is
443 B<authenticated>. This is where this module's job stops, and your application
444 or other plugins step in.
446 The next logical step is B<authorization>, the process of deciding what a user
447 is (or isn't) allowed to do. For example, say your users are split into two
448 main groups - regular users and administrators. You want to verify that the
449 currently logged in user is indeed an administrator before performing the
450 actions in an administrative part of your application. These decisions may be
451 made within your application code using just the information available after
452 authentication, or it may be facilitated by a number of plugins.
454 =head2 The Components In This Framework
458 Configuration of the Catalyst::Plugin::Authentication framework is done in
459 terms of realms. In simplest terms, a realm is a pairing of a Credential
460 verifier and a User storage (Store) backend. As of version 0.10003, realms are
461 now objects that you can create and customize.
463 An application can have any number of Realms, each of which operates
464 independant of the others. Each realm has a name, which is used to identify it
465 as the target of an authentication request. This name can be anything, such as
466 'users' or 'members'. One realm must be defined as the default_realm, which is
467 used when no realm name is specified. More information about configuring
468 realms is available in the configuration section.
470 =head3 Credential Verifiers
472 When user input is transferred to the L<Catalyst> application (typically via
473 form inputs) the application may pass this information into the authentication
474 system through the $c->authenticate() method. From there, it is passed to the
475 appropriate Credential verifier.
477 These plugins check the data, and ensure that it really proves the user is who
480 =head3 Storage Backends
482 The authentication data also identifies a user, and the Storage backend modules
483 use this data to locate and return a standardized object-oriented
484 representation of a user.
486 When a user is retrieved from a store it is not necessarily authenticated.
487 Credential verifiers accept a set of authentication data and use this
488 information to retrieve the user from the store they are paired with.
490 =head3 The Core Plugin
492 This plugin on its own is the glue, providing realm configuration, session
493 integration, and other goodness for the other plugins.
497 More layers of plugins can be stacked on top of the authentication code. For
498 example, L<Catalyst::Plugin::Session::PerUser> provides an abstraction of
499 browser sessions that is more persistent per users.
500 L<Catalyst::Plugin::Authorization::Roles> provides an accepted way to separate
501 and group users into categories, and then check which categories the current
506 Let's say we were storing users in a simple perl hash. Users are
507 verified by supplying a password which is matched within the hash.
509 This means that our application will begin like this:
517 __PACKAGE__->config->{authentication} =
519 default_realm => 'members',
524 password_field => 'password',
525 password_type => 'clear'
531 password => "s00p3r",
533 roles => [qw/edit delete/],
536 password => "s3cr3t",
537 roles => [qw/comment/],
546 This tells the authentication plugin what realms are available, which
547 credential and store modules are used, and the configuration of each. With
548 this code loaded, we can now attempt to authenticate users.
550 To show an example of this, let's create an authentication controller:
552 package MyApp::Controller::Auth;
555 my ( $self, $c ) = @_;
557 if ( my $user = $c->req->param("user")
558 and my $password = $c->req->param("password") )
560 if ( $c->authenticate( { username => $user,
561 password => $password } ) ) {
562 $c->res->body( "hello " . $c->user->get("name") );
572 This code should be very readable. If all the necessary fields are supplied,
573 call the "authenticate" method from the controller. If it succeeds the
576 The credential verifier will attempt to retrieve the user whose details match
577 the authentication information provided to $c->authenticate(). Once it fetches
578 the user the password is checked and if it matches the user will be
579 B<authenticated> and C<< $c->user >> will contain the user object retrieved
582 In the above case, the default realm is checked, but we could just as easily
583 check an alternate realm. If this were an admin login, for example, we could
584 authenticate on the admin realm by simply changing the $c->authenticate()
587 if ( $c->authenticate( { username => $user,
588 password => $password }, 'admin' )l ) {
589 $c->res->body( "hello " . $c->user->get("name") );
593 Now suppose we want to restrict the ability to edit to a user with an
594 'editor' value of yes.
596 The restricted action might look like this:
599 my ( $self, $c ) = @_;
601 $c->detach("unauthorized")
602 unless $c->user_exists
603 and $c->user->get('editor') eq 'yes';
605 # do something restricted here
608 (Note that if you have multiple realms, you can use $c->user_in_realm('realmname')
609 in place of $c->user_exists(); This will essentially perform the same
610 verification as user_exists, with the added requirement that if there is a
611 user, it must have come from the realm specified.)
613 The above example is somewhat similar to role based access control.
614 L<Catalyst::Authentication::Store::Minimal> treats the roles field as
615 an array of role names. Let's leverage this. Add the role authorization
624 my ( $self, $c ) = @_;
626 $c->detach("unauthorized") unless $c->check_roles("edit");
628 # do something restricted here
631 This is somewhat simpler and will work if you change your store, too, since the
632 role interface is consistent.
634 Let's say your app grew, and you now have 10000 users. It's no longer
635 efficient to maintain a hash of users, so you move this data to a database.
636 You can accomplish this simply by installing the DBIx::Class Store and
637 changing your config:
639 __PACKAGE__->config->{authentication} =
641 default_realm => 'members',
646 password_field => 'password',
647 password_type => 'clear'
650 class => 'DBIx::Class',
651 user_class => 'MyApp::Users',
652 role_column => 'roles'
658 The authentication system works behind the scenes to load your data from the
659 new source. The rest of your application is completely unchanged.
665 __PACKAGE__->config->{authentication} =
667 default_realm => 'members',
672 password_field => 'password',
673 password_type => 'clear'
676 class => 'DBIx::Class',
677 user_class => 'MyApp::Users',
678 role_column => 'roles'
684 password_field => 'password',
685 password_type => 'clear'
688 class => '+MyApp::Authentication::Store::NetAuth',
689 authserver => '192.168.10.17'
700 Whether or not to store the user's logged in state in the session, if the
701 application is also using L<Catalyst::Plugin::Session>. This
702 value is set to true per default.
706 This defines which realm should be used as when no realm is provided to methods
707 that require a realm such as authenticate or find_user.
711 This contains the series of realm configurations you want to use for your app.
712 The only rule here is that there must be at least one. A realm consists of a
713 name, which is used to reference the realm, a credential and a store.
715 You can also specify a realm class to instantiate instead of the default
716 L<Catalyst::Authentication::Realm> class using the 'class' element within the
719 Each realm config contains two hashes, one called 'credential' and one called
720 'store', each of which provide configuration details to the respective modules.
721 The contents of these hashes is specific to the module being used, with the
722 exception of the 'class' element, which tells the core Authentication module the
723 classname to instantiate.
725 The 'class' element follows the standard Catalyst mechanism of class
726 specification. If a class is prefixed with a +, it is assumed to be a complete
727 class name. Otherwise it is considered to be a portion of the class name. For
728 credentials, the classname 'B<Password>', for example, is expanded to
729 Catalyst::Authentication::Credential::B<Password>. For stores, the
730 classname 'B<storename>' is expanded to:
731 Catalyst::Authentication::Store::B<storename>.
737 =head2 authenticate( $userinfo, $realm )
739 Attempts to authenticate the user using the information in the $userinfo hash
740 reference using the realm $realm. $realm may be omitted, in which case the
741 default realm is checked.
745 Returns the currently logged in user or undef if there is none.
747 =head2 user_exists( )
749 Returns true if a user is logged in right now. The difference between
750 user_exists and user is that user_exists will return true if a user is logged
751 in, even if it has not been yet retrieved from the storage backend. If you only
752 need to know if the user is logged in, depending on the storage mechanism this
753 can be much more efficient.
755 =head2 user_in_realm( $realm )
757 Works like user_exists, except that it only returns true if a user is both
758 logged in right now and was retrieved from the realm provided.
762 Logs the user out, Deletes the currently logged in user from $c->user and the session.
764 =head2 find_user( $userinfo, $realm )
766 Fetch a particular users details, matching the provided user info, from the realm
769 =head1 INTERNAL METHODS
771 These methods are for Catalyst::Plugin::Authentication B<INTERNAL USE> only.
772 Please do not use them in your own code, whether application or credential /
773 store modules. If you do, you will very likely get the nasty shock of having
774 to fix / rewrite your code when things change. They are documented here only
777 =head2 set_authenticated( $user, $realmname )
779 Marks a user as authenticated. This is called from within the authenticate
780 routine when a credential returns a user. $realmname defaults to 'default'
782 =head2 auth_restore_user( $user, $realmname )
784 Used to restore a user from the session. In most cases this is called without
785 arguments to restore the user via the session. Can be called with arguments
786 when restoring a user from some other method. Currently not used in this way.
788 =head2 save_user_in_session( $user, $realmname )
790 Used to save the user in a session. Saves $user in session, marked as
791 originating in $realmname. Both arguments are required.
793 =head2 auth_realms( )
795 Returns a hashref containing realmname -> realm instance pairs. Realm
796 instances contain an instantiated store and credential object as the 'store'
797 and 'credential' elements, respectively
799 =head2 get_auth_realm( $realmname )
801 Retrieves the realm instance for the realmname provided.
805 This list might not be up to date. Below are modules known to work with the updated
806 API of 0.10 and are therefore compatible with realms.
810 L<Catalyst::Authentication::Realm>
812 =head2 User Storage Backends
814 L<Catalyst::Authentication::Store::Minimal>,
815 L<Catalyst::Authentication::Store::DBIx::Class>,
817 =head2 Credential verification
819 L<Catalyst::Authentication::Credential::Password>,
823 L<Catalyst::Plugin::Authorization::ACL>,
824 L<Catalyst::Plugin::Authorization::Roles>
826 =head2 Internals Documentation
828 L<Catalyst::Plugin::Authentication::Internals>
832 L<Catalyst::Plugin::Session>,
833 L<Catalyst::Plugin::Session::PerUser>
835 =head1 DON'T SEE ALSO
837 This module along with its sub plugins deprecate a great number of other
838 modules. These include L<Catalyst::Plugin::Authentication::Simple>,
839 L<Catalyst::Plugin::Authentication::CDBI>.
841 At the time of writing these plugins have not yet been replaced or updated, but
842 should be eventually: L<Catalyst::Plugin::Authentication::OpenID>,
843 L<Catalyst::Plugin::Authentication::LDAP>,
844 L<Catalyst::Plugin::Authentication::CDBI::Basic>,
845 L<Catalyst::Plugin::Authentication::Basic::Remote>.
847 =head1 INCOMPATABILITIES
849 The realms based configuration and functionality of the 0.10 update
850 of L<Catalyst::Plugin::Authentication> required a change in the API used by
851 credentials and stores. It has a compatibility mode which allows use of
852 modules that have not yet been updated. This, however, completely mimics the
853 older api and disables the new realm-based features. In other words you can
854 not mix the older credential and store modules with realms, or realm-based
855 configs. The changes required to update modules are relatively minor and are
856 covered in L<Catalyst::Plugin::Authentication::Internals>. We hope that most
857 modules will move to the compatible list above very quickly.
859 =head1 COMPATIBILITY ROUTINES
861 In version 0.10 of L<Catalyst::Plugin::Authentication>, the API
862 changed. For app developers, this change is fairly minor, but for
863 Credential and Store authors, the changes are significant.
865 Please see the documentation in version 0.09 of
866 Catalyst::Plugin::Authentication for a better understanding of how the old API
869 The items below are still present in the plugin, though using them is
870 deprecated. They remain only as a transition tool, for those sites which can
871 not yet be upgraded to use the new system due to local customizations or use
872 of Credential / Store modules that have not yet been updated to work with the
875 These routines should not be used in any application using realms
876 functionality or any of the methods described above. These are for reference
881 This method is used to initiate authentication and user retrieval. Technically
882 this is part of the old Password credential module and it still resides in the
883 L<Password|Catalyst::Plugin::Authentication::Credential::Password> class. It is
884 included here for reference only.
886 =head2 default_auth_store( )
888 Return the store whose name is 'default'.
890 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
891 or by using a Store plugin:
893 # load the Minimal authentication store.
894 use Catalyst qw/Authentication Authentication::Store::Minimal/;
896 Sets the default store to
897 L<Catalyst::Plugin::Authentication::Store::Minimal>.
899 =head2 get_auth_store( $name )
901 Return the store whose name is $name.
903 =head2 get_auth_store_name( $store )
905 Return the name of the store $store.
907 =head2 auth_stores( )
909 A hash keyed by name, with the stores registered in the app.
911 =head2 register_auth_stores( %stores_by_name )
913 Register stores into the application.
915 =head2 auth_store_names( )
921 =head2 setup_auth_realm( )
925 Yuval Kogman, C<nothingmuch@woobling.org>
927 Jay Kuri, C<jayk@cpan.org>
933 =head1 COPYRIGHT & LICENSE
935 Copyright (c) 2005 the aforementioned authors. All rights
936 reserved. This program is free software; you can redistribute
937 it and/or modify it under the same terms as Perl itself.