Decoupling of Session and Auth-
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Authentication / Realm.pm
CommitLineData
5c5af345 1package Catalyst::Authentication::Realm;
646ea5b1 2
3use strict;
4use warnings;
1489b476 5
646ea5b1 6use base qw/Class::Accessor::Fast/;
7
8BEGIN {
9 __PACKAGE__->mk_accessors(qw/store credential name config/);
10};
11
12sub new {
13 my ($class, $realmname, $config, $app) = @_;
14
15 my $self = { config => $config };
16 bless $self, $class;
17
18 $self->name($realmname);
19
20 $app->log->debug("Setting up auth realm $realmname") if $app->debug;
5ef7a3dc 21
22 # use the Null store as a default
23 if( ! exists $config->{store}{class} ) {
39ce54e8 24 $config->{store}{class} = '+Catalyst::Authentication::Store::Null';
5ef7a3dc 25 $app->log->debug( qq(No Store specified for realm "$realmname", using the Null store.) );
646ea5b1 26 }
646ea5b1 27 my $storeclass = $config->{'store'}{'class'};
28
29 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
30 ## taken to mean C::P::A::Store::(specifiedclass)
31 if ($storeclass !~ /^\+(.*)$/ ) {
39ce54e8 32 $storeclass = "Catalyst::Authentication::Store::${storeclass}";
646ea5b1 33 } else {
34 $storeclass = $1;
35 }
646ea5b1 36
37 # a little niceness - since most systems seem to use the password credential class,
38 # if no credential class is specified we use password.
39ce54e8 39 $config->{credential}{class} ||= '+Catalyst::Authentication::Credential::Password';
646ea5b1 40
41 my $credentialclass = $config->{'credential'}{'class'};
42
43 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
39ce54e8 44 ## taken to mean C::A::Credential::(specifiedclass)
646ea5b1 45 if ($credentialclass !~ /^\+(.*)$/ ) {
39ce54e8 46 $credentialclass = "Catalyst::Authentication::Credential::${credentialclass}";
646ea5b1 47 } else {
48 $credentialclass = $1;
49 }
50
39ce54e8 51 # if we made it here - we have what we need to load the classes
52
53 ### BACKWARDS COMPATIBILITY - DEPRECATION WARNING:
54 ### we must eval the ensure_class_loaded - because we might need to try the old-style
55 ### ::Plugin:: module naming if the standard method fails.
56
8a7bd676 57 ## Note to self - catch second exception and bitch in detail?
58
39ce54e8 59 eval {
60 Catalyst::Utils::ensure_class_loaded( $credentialclass );
61 };
62
63 if ($@) {
64 $app->log->warn( qq(Credential class "$credentialclass" not found, trying deprecated ::Plugin:: style naming. ) );
8a7bd676 65 my $origcredentialclass = $credentialclass;
39ce54e8 66 $credentialclass =~ s/Catalyst::Authentication/Catalyst::Plugin::Authentication/;
8a7bd676 67
68 eval { Catalyst::Utils::ensure_class_loaded( $credentialclass ); };
69 if ($@) {
70 Carp::croak "Unable to load credential class, " . $origcredentialclass . " OR " . $credentialclass .
71 " in realm " . $self->name;
72 }
39ce54e8 73 }
74
75 eval {
76 Catalyst::Utils::ensure_class_loaded( $storeclass );
77 };
78
79 if ($@) {
80 $app->log->warn( qq(Store class "$storeclass" not found, trying deprecated ::Plugin:: style naming. ) );
8a7bd676 81 my $origstoreclass = $storeclass;
39ce54e8 82 $storeclass =~ s/Catalyst::Authentication/Catalyst::Plugin::Authentication/;
8a7bd676 83 eval { Catalyst::Utils::ensure_class_loaded( $storeclass ); };
84 if ($@) {
85 Carp::croak "Unable to load store class, " . $origstoreclass . " OR " . $storeclass .
86 " in realm " . $self->name;
87 }
39ce54e8 88 }
646ea5b1 89
90 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
91 # of get_user and add it to the class. this is because the auth routines use find_user,
92 # and rely on it being present. (this avoids per-call checks)
93 if (!$storeclass->can('find_user')) {
94 no strict 'refs';
95 *{"${storeclass}::find_user"} = sub {
96 my ($self, $info) = @_;
97 my @rest = @{$info->{rest}} if exists($info->{rest});
98 $self->get_user($info->{id}, @rest);
99 };
100 }
101
102 ## a little cruft to stay compatible with some poorly written stores / credentials
103 ## we'll remove this soon.
104 if ($storeclass->can('new')) {
105 $self->store($storeclass->new($config->{'store'}, $app, $self));
106 } else {
107 $app->log->error("THIS IS DEPRECATED: $storeclass has no new() method - Attempting to use uninstantiated");
108 $self->store($storeclass);
109 }
110 if ($credentialclass->can('new')) {
111 $self->credential($credentialclass->new($config->{'credential'}, $app, $self));
112 } else {
113 $app->log->error("THIS IS DEPRECATED: $credentialclass has no new() method - Attempting to use uninstantiated");
114 $self->credential($credentialclass);
115 }
116
117 return $self;
118}
119
120sub find_user {
121 my ( $self, $authinfo, $c ) = @_;
122
123 my $res = $self->store->find_user($authinfo, $c);
124
4bd1f581 125 if (!$res) {
68c40c8b 126 if ($self->config->{'auto_create_user'} && $self->store->can('auto_create_user') ) {
127 $res = $self->store->auto_create_user($authinfo, $c);
4bd1f581 128 }
68c40c8b 129 } elsif ($self->config->{'auto_update_user'} && $self->store->can('auto_update_user')) {
130 $res = $self->store->auto_update_user($authinfo, $c, $res);
4bd1f581 131 }
646ea5b1 132
133 return $res;
134}
135
136sub authenticate {
137 my ($self, $c, $authinfo) = @_;
138
139 my $user = $self->credential->authenticate($c, $self, $authinfo);
140 if (ref($user)) {
141 $c->set_authenticated($user, $self->name);
142 return $user;
143 } else {
144 return undef;
145 }
146}
147
8a7bd676 148sub user_is_restorable {
149 my ($self, $c) = @_;
150
151 return unless
152 $c->isa("Catalyst::Plugin::Session")
153 and $c->config->{'Plugin::Authentication'}{'use_session'}
154 and $c->session_is_valid;
646ea5b1 155
8a7bd676 156 return $c->session->{__user};
157}
158
159sub restore_user {
160 my ($self, $c, $frozen_user) = @_;
646ea5b1 161
8a7bd676 162 $frozen_user ||= $self->user_is_restorable($c);
163 return unless defined($frozen_user);
164
165 $c->_user( my $user = $self->from_session( $c, $frozen_user ) );
166
167 # this sets the realm the user originated in.
168 $user->auth_realm($self->name);
169
170 return $user;
171}
172
173sub persist_user {
174 my ($self, $c, $user) = @_;
175
176 if (
177 $c->isa("Catalyst::Plugin::Session")
178 and $c->config->{'Plugin::Authentication'}{'use_session'}
179 and $user->supports("session")
180 ) {
181 $c->session->{__user_realm} = $self->name;
182
183 # we want to ask the store for a user prepared for the session.
184 # but older modules split this functionality between the user and the
185 # store. We try the store first. If not, we use the old method.
186 if ($self->store->can('for_session')) {
187 $c->session->{__user} = $self->store->for_session($c, $user);
188 } else {
189 $c->session->{__user} = $user->for_session;
190 }
646ea5b1 191 }
8a7bd676 192 return $user;
193}
194
195sub remove_persisted_user {
196 my ($self, $c) = @_;
197
198 if (
199 $c->isa("Catalyst::Plugin::Session")
200 and $c->config->{'Plugin::Authentication'}{'use_session'}
201 and $c->session_is_valid
202 ) {
203 delete @{ $c->session }{qw/__user __user_realm/};
204 }
205}
206
207## backwards compatibility - I don't think many people wrote realms since they
208## have only existed for a short time - but just in case.
209sub save_user_in_session {
210 my ( $self, $c, $user ) = @_;
211
212 return $self->persist_user($c, $user);
646ea5b1 213}
214
215sub from_session {
216 my ($self, $c, $frozen_user) = @_;
217
218 return $self->store->from_session($c, $frozen_user);
219}
220
221
222__PACKAGE__;
223
52a3537a 224__END__
1489b476 225
226=pod
227
228=head1 NAME
229
5c5af345 230Catalyst::Authentication::Realm - Base class for realm objects.
1489b476 231
232=head1 DESCRIPTION
233
5afc0dde 234=head1 CONFIGURATION
1489b476 235
236=over 4
237
5afc0dde 238=item class
239
8a7bd676 240By default this class is used by
241L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> for all
242realms. The class parameter allows you to choose a different class to use for
243this realm. Creating a new Realm class can allow for authentication methods
244that fall outside the normal credential/store methodology.
85593aa9 245
5afc0dde 246=item auto_create_user
1489b476 247
85593aa9 248Set this to true if you wish this realm to auto-create user accounts when the
249user doesn't exist (most useful for remote authentication schemes).
250
5afc0dde 251=item auto_update_user
1489b476 252
85593aa9 253Set this to true if you wish this realm to auto-update user accounts after
254authentication (most useful for remote authentication schemes).
255
5afc0dde 256=back
257
258=head1 METHODS
1489b476 259
8a7bd676 260=head2 new( $realmname, $config, $app )
1489b476 261
85593aa9 262Instantiantes this realm, plus the specified store and credential classes.
263
264=head2 store( )
265
8a7bd676 266Returns an instance of the store object for this realm.
85593aa9 267
268=head2 credential( )
269
8a7bd676 270Returns an instance of the credential object for this realm.
85593aa9 271
8a7bd676 272=head2 find_user( $authinfo, $c )
5afc0dde 273
8a7bd676 274Retrieves the user given the authentication information provided. This
275is most often called from the credential. The default realm class simply
276delegates this call the store object. If enabled, auto-creation and
277auto-updating of users is also handled here.
85593aa9 278
8a7bd676 279=head2 authenticate( $c, $authinfo)
5afc0dde 280
8a7bd676 281Performs the authentication process for the current realm. The default
282realm class simply delegates this to the credential and sets
283the authenticated user on success. Returns the authenticated user object;
85593aa9 284
8a7bd676 285=head save_user_in_session($c, $user)
5afc0dde 286
8a7bd676 287Used to save the user in a session. Saves $user in the current session,
288marked as originating in the current realm. Calls $store->for_session() by
289default. If for_session is not available in the store class, will attempt
290to call $user->for_session().
85593aa9 291
8a7bd676 292=head2 from_session($c, $frozenuser )
1489b476 293
8a7bd676 294Triggers restoring of the user from data in the session. The default realm
295class simply delegates the call to $store->from_session($c, $frozenuser);
85593aa9 296
1489b476 297=cut
298