Commit | Line | Data |
5c5af345 |
1 | package Catalyst::Authentication::Realm; |
646ea5b1 |
2 | |
3 | use strict; |
4 | use warnings; |
1489b476 |
5 | |
646ea5b1 |
6 | use base qw/Class::Accessor::Fast/; |
7 | |
8 | BEGIN { |
9 | __PACKAGE__->mk_accessors(qw/store credential name config/); |
10 | }; |
11 | |
12 | sub 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 | |
120 | sub 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 | |
136 | sub 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 |
148 | sub 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 | |
159 | sub 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 | |
173 | sub 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 | |
195 | sub 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. |
209 | sub save_user_in_session { |
210 | my ( $self, $c, $user ) = @_; |
211 | |
212 | return $self->persist_user($c, $user); |
646ea5b1 |
213 | } |
214 | |
215 | sub 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 |
230 | Catalyst::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 |
240 | By default this class is used by |
241 | L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication> for all |
242 | realms. The class parameter allows you to choose a different class to use for |
243 | this realm. Creating a new Realm class can allow for authentication methods |
244 | that fall outside the normal credential/store methodology. |
85593aa9 |
245 | |
5afc0dde |
246 | =item auto_create_user |
1489b476 |
247 | |
85593aa9 |
248 | Set this to true if you wish this realm to auto-create user accounts when the |
249 | user doesn't exist (most useful for remote authentication schemes). |
250 | |
5afc0dde |
251 | =item auto_update_user |
1489b476 |
252 | |
85593aa9 |
253 | Set this to true if you wish this realm to auto-update user accounts after |
254 | authentication (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 |
262 | Instantiantes this realm, plus the specified store and credential classes. |
263 | |
264 | =head2 store( ) |
265 | |
8a7bd676 |
266 | Returns an instance of the store object for this realm. |
85593aa9 |
267 | |
268 | =head2 credential( ) |
269 | |
8a7bd676 |
270 | Returns an instance of the credential object for this realm. |
85593aa9 |
271 | |
8a7bd676 |
272 | =head2 find_user( $authinfo, $c ) |
5afc0dde |
273 | |
8a7bd676 |
274 | Retrieves the user given the authentication information provided. This |
275 | is most often called from the credential. The default realm class simply |
276 | delegates this call the store object. If enabled, auto-creation and |
277 | auto-updating of users is also handled here. |
85593aa9 |
278 | |
8a7bd676 |
279 | =head2 authenticate( $c, $authinfo) |
5afc0dde |
280 | |
8a7bd676 |
281 | Performs the authentication process for the current realm. The default |
282 | realm class simply delegates this to the credential and sets |
283 | the authenticated user on success. Returns the authenticated user object; |
85593aa9 |
284 | |
8a7bd676 |
285 | =head save_user_in_session($c, $user) |
5afc0dde |
286 | |
8a7bd676 |
287 | Used to save the user in a session. Saves $user in the current session, |
288 | marked as originating in the current realm. Calls $store->for_session() by |
289 | default. If for_session is not available in the store class, will attempt |
290 | to call $user->for_session(). |
85593aa9 |
291 | |
8a7bd676 |
292 | =head2 from_session($c, $frozenuser ) |
1489b476 |
293 | |
8a7bd676 |
294 | Triggers restoring of the user from data in the session. The default realm |
295 | class simply delegates the call to $store->from_session($c, $frozenuser); |
85593aa9 |
296 | |
1489b476 |
297 | =cut |
298 | |