X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FAuthentication%2FRealm.pm;h=db9b4c27a81b3fe66965620872502ca435eb53b0;hb=f0312fca4a199bf694276f180d566ca4e4e28875;hp=08c554134eaeb06ee9751a1746384aab3551bbb3;hpb=5c5af3452cf4b03d7d55e20870942f1b600081b7;p=catagits%2FCatalyst-Plugin-Authentication.git diff --git a/lib/Catalyst/Authentication/Realm.pm b/lib/Catalyst/Authentication/Realm.pm index 08c5541..db9b4c2 100644 --- a/lib/Catalyst/Authentication/Realm.pm +++ b/lib/Catalyst/Authentication/Realm.pm @@ -9,6 +9,8 @@ BEGIN { __PACKAGE__->mk_accessors(qw/store credential name config/); }; +## Add use_session config item to realm. + sub new { my ($class, $realmname, $config, $app) = @_; @@ -17,12 +19,23 @@ sub new { $self->name($realmname); + if (!exists($self->config->{'use_session'})) { + if (exists($app->config->{'Plugin::Authentication'}{'use_session'})) { + $self->config->{'use_session'} = $app->config->{'Plugin::Authentication'}{'use_session'}; + } else { + $self->config->{'use_session'} = 1; + } + } + $app->log->debug("Setting up auth realm $realmname") if $app->debug; - # use the Null store as a default - if( ! exists $config->{store}{class} ) { + # use the Null store as a default - Don't complain if the realm class is being overridden, + # as the new realm may behave differently. + if( ! exists($config->{store}{class}) ) { $config->{store}{class} = '+Catalyst::Authentication::Store::Null'; - $app->log->debug( qq(No Store specified for realm "$realmname", using the Null store.) ); + if (! exists($config->{class})) { + $app->log->debug( qq(No Store specified for realm "$realmname", using the Null store.) ); + } } my $storeclass = $config->{'store'}{'class'}; @@ -54,14 +67,28 @@ sub new { ### we must eval the ensure_class_loaded - because we might need to try the old-style ### ::Plugin:: module naming if the standard method fails. + ## Note to self - catch second exception and bitch in detail? + eval { Catalyst::Utils::ensure_class_loaded( $credentialclass ); }; if ($@) { + # If the file is missing, then try the old-style fallback, + # but re-throw anything else for the user to deal with. + die unless $@ =~ /^Can't locate/; $app->log->warn( qq(Credential class "$credentialclass" not found, trying deprecated ::Plugin:: style naming. ) ); + my $origcredentialclass = $credentialclass; $credentialclass =~ s/Catalyst::Authentication/Catalyst::Plugin::Authentication/; - Catalyst::Utils::ensure_class_loaded( $credentialclass ); + + eval { Catalyst::Utils::ensure_class_loaded( $credentialclass ); }; + if ($@) { + # Likewise this croak is useful if the second exception is also "not found", + # but would be confusing if it's anything else. + die unless $@ =~ /^Can't locate/; + Carp::croak "Unable to load credential class, " . $origcredentialclass . " OR " . $credentialclass . + " in realm " . $self->name; + } } eval { @@ -69,9 +96,20 @@ sub new { }; if ($@) { + # If the file is missing, then try the old-style fallback, + # but re-throw anything else for the user to deal with. + die unless $@ =~ /^Can't locate/; $app->log->warn( qq(Store class "$storeclass" not found, trying deprecated ::Plugin:: style naming. ) ); + my $origstoreclass = $storeclass; $storeclass =~ s/Catalyst::Authentication/Catalyst::Plugin::Authentication/; - Catalyst::Utils::ensure_class_loaded( $storeclass ); + eval { Catalyst::Utils::ensure_class_loaded( $storeclass ); }; + if ($@) { + # Likewise this croak is useful if the second exception is also "not found", + # but would be confusing if it's anything else. + die unless $@ =~ /^Can't locate/; + Carp::croak "Unable to load store class, " . $origstoreclass . " OR " . $storeclass . + " in realm " . $self->name; + } } # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms @@ -132,19 +170,89 @@ sub authenticate { } } -sub save_user_in_session { - my ( $self, $c, $user ) = @_; +sub user_is_restorable { + my ($self, $c) = @_; + + return unless + $c->can('session') + and $self->config->{'use_session'} + and $c->session_is_valid; + + return $c->session->{__user}; +} - $c->session->{__user_realm} = $self->name; +sub restore_user { + my ($self, $c, $frozen_user) = @_; - # we want to ask the store for a user prepared for the session. - # but older modules split this functionality between the user and the - # store. We try the store first. If not, we use the old method. - if ($self->store->can('for_session')) { - $c->session->{__user} = $self->store->for_session($c, $user); - } else { - $c->session->{__user} = $user->for_session; + $frozen_user ||= $self->user_is_restorable($c); + return unless defined($frozen_user); + + my $user = $self->from_session( $c, $frozen_user ); + + if ($user) { + $c->_user( $user ); + + # this sets the realm the user originated in. + $user->auth_realm($self->name); + } + else { + $self->failed_user_restore($c) || + $c->error("Store claimed to have a restorable user, but restoration failed. Did you change the user's id_field?"); + } + + return $user; +} + +## this occurs if there is a session but the thing the session refers to +## can not be found. Do what you must do here. +## Return true if you can fix the situation and find a user, false otherwise +sub failed_user_restore { + my ($self, $c) = @_; + + $self->remove_persisted_user($c); + return; +} + +sub persist_user { + my ($self, $c, $user) = @_; + + if ( + $c->can('session') + and $self->config->{'use_session'} + and $user->supports("session") + ) { + $c->session->{__user_realm} = $self->name; + + # we want to ask the store for a user prepared for the session. + # but older modules split this functionality between the user and the + # store. We try the store first. If not, we use the old method. + if ($self->store->can('for_session')) { + $c->session->{__user} = $self->store->for_session($c, $user); + } else { + $c->session->{__user} = $user->for_session; + } } + return $user; +} + +sub remove_persisted_user { + my ($self, $c) = @_; + + if ( + $c->can('session') + and $self->config->{'use_session'} + and $c->session_is_valid + ) { + delete @{ $c->session }{qw/__user __user_realm/}; + } +} + +## backwards compatibility - I don't think many people wrote realms since they +## have only existed for a short time - but just in case. +sub save_user_in_session { + my ( $self, $c, $user ) = @_; + + return $self->persist_user($c, $user); } sub from_session { @@ -172,8 +280,11 @@ Catalyst::Authentication::Realm - Base class for realm objects. =item class -By default this class is the default realm class. You can specify a custom -realm class with this config parameter. +By default this class is used by +L for all +realms. The class parameter allows you to choose a different class to use for +this realm. Creating a new Realm class can allow for authentication methods +that fall outside the normal credential/store methodology. =item auto_create_user @@ -185,38 +296,87 @@ user doesn't exist (most useful for remote authentication schemes). Set this to true if you wish this realm to auto-update user accounts after authentication (most useful for remote authentication schemes). +=item use_session + +Sets session usage for this particular realm - overriding the global use_sesion setting. + + =back =head1 METHODS -=head2 new( ) +=head2 new( $realmname, $config, $app ) Instantiantes this realm, plus the specified store and credential classes. =head2 store( ) -Holds an instance of the store object for this realm. +Returns an instance of the store object for this realm. =head2 credential( ) -Holds an instance of the credential object for this realm. +Returns an instance of the credential object for this realm. -=head2 find_user( ) +=head2 find_user( $authinfo, $c ) -Delegates to the store object. Will also re-delegate auto_create_user and -auto_update_user at this time, if necessary. +Retrieves the user given the authentication information provided. This +is most often called from the credential. The default realm class simply +delegates this call the store object. If enabled, auto-creation and +auto-updating of users is also handled here. -=head2 authenticate( ) +=head2 authenticate( $c, $authinfo) -Delegates to the credential objects and sets the authenticated user on success. +Performs the authentication process for the current realm. The default +realm class simply delegates this to the credential and sets +the authenticated user on success. Returns the authenticated user object; -=head2 save_user_in_session( ) +=head1 USER PERSISTENCE -Delegates to the store object. +The Realm class allows complete control over the persistance of users +between requests. By default the realm attempts to use the Catalyst +session system to accomplish this. By overriding the methods below +in a custom Realm class, however, you can handle user persistance in +any way you see fit. -=head2 from_session( ) +=head2 persist_user($c, $user) -Delegates to the store object. +persist_user is the entry point for saving user information between requests +in most cases this will utilize the session. By default this uses the +catalyst session system to store the user by calling for_session on the +active store. The user object must be a subclass of +Catalyst::Authentication::User. If you have updated the user object, you +must call persist_user again to ensure that the persisted user object reflects +your updates. -=cut +=head2 remove_persisted_user($c) +Removes any persisted user data. By default, removes the user from the session. + +=head2 user_is_restorable( $c ) + +Returns whether there is a persisted user that may be restored. Returns +a token used to restore the user. With the default session persistance +it returns the raw frozen user information. + +=head2 restore_user($c, [$frozen_user]) + +Restores the user from the given frozen_user parameter, or if not provided, +using the response from $self->user_is_restorable(); Uses $self->from_session() +to decode the frozen user. + +=head2 failed_user_restore($c) + +If there is a session to restore, but the restore fails for any reason then this method +is called. This method supplied just removes the persisted user, but can be overridden +if required to have more complex logic (e.g. finding a the user by their 'old' username). + +=head2 from_session($c, $frozenuser ) + +Decodes the frozenuser information provided and returns an instantiated +user object. By default, this call is delegated to $store->from_session(). + +=head2 save_user_in_session($c, $user) + +DEPRECATED. Use persist_user instead. (this simply calls persist_user) + +=cut