Commit | Line | Data |
06675d2e |
1 | #!/usr/bin/perl |
2 | |
3 | package Catalyst::Plugin::Authentication; |
4 | |
b003080b |
5 | use base qw/Class::Accessor::Fast Class::Data::Inheritable/; |
06675d2e |
6 | |
b003080b |
7 | BEGIN { |
7bb06c91 |
8 | __PACKAGE__->mk_accessors(qw/_user/); |
96777f3a |
9 | __PACKAGE__->mk_classdata($_) for qw/_auth_stores _auth_store_names/; |
b003080b |
10 | } |
06675d2e |
11 | |
12 | use strict; |
13 | use warnings; |
14 | |
96777f3a |
15 | use Tie::RefHash; |
12dae309 |
16 | use Class::Inspector; |
96777f3a |
17 | |
e145babc |
18 | #BEGIN { |
19 | # require constant; |
20 | # constant->import(have_want => eval { require Want }); |
21 | #} |
a1e5bd36 |
22 | |
e09a839c |
23 | our $VERSION = "0.02"; |
c7c003d3 |
24 | |
06675d2e |
25 | sub set_authenticated { |
26 | my ( $c, $user ) = @_; |
27 | |
28 | $c->user($user); |
e300c5b6 |
29 | $c->request->{user} = $user; # compatibility kludge |
06675d2e |
30 | |
31 | if ( $c->isa("Catalyst::Plugin::Session") |
96777f3a |
32 | and $c->config->{authentication}{use_session} |
12dae309 |
33 | and $user->supports("session") ) |
06675d2e |
34 | { |
12dae309 |
35 | $c->save_user_in_session($user); |
06675d2e |
36 | } |
55395841 |
37 | |
4fbe2e14 |
38 | $c->NEXT::set_authenticated($user); |
06675d2e |
39 | } |
40 | |
7bb06c91 |
41 | sub user { |
e300c5b6 |
42 | my $c = shift; |
7bb06c91 |
43 | |
e300c5b6 |
44 | if (@_) { |
45 | return $c->_user(@_); |
46 | } |
7bb06c91 |
47 | |
e300c5b6 |
48 | my $user = $c->_user; |
7bb06c91 |
49 | |
e300c5b6 |
50 | if ( $user and !Scalar::Util::blessed($user) ) { |
cde43a59 |
51 | # return 1 if have_want() && Want::want("BOOL"); |
e300c5b6 |
52 | return $c->auth_restore_user($user); |
53 | } |
7bb06c91 |
54 | |
e300c5b6 |
55 | return $user; |
7bb06c91 |
56 | } |
57 | |
ce0b058d |
58 | sub user_exists { |
59 | my $c = shift; |
60 | return defined($c->_user); |
61 | } |
62 | |
12dae309 |
63 | sub save_user_in_session { |
e300c5b6 |
64 | my ( $c, $user ) = @_; |
12dae309 |
65 | |
66 | my $store = $user->store || ref $user; |
67 | $c->session->{__user_store} = $c->get_auth_store_name($store) || $store; |
68 | $c->session->{__user} = $user->for_session; |
69 | } |
70 | |
06675d2e |
71 | sub logout { |
72 | my $c = shift; |
73 | |
74 | $c->user(undef); |
b003080b |
75 | |
76 | if ( $c->isa("Catalyst::Plugin::Session") |
77 | and $c->config->{authentication}{use_session} ) |
78 | { |
96777f3a |
79 | delete @{ $c->session }{qw/__user __user_store/}; |
b003080b |
80 | } |
351e2a82 |
81 | |
82 | $c->NEXT::logout(@_); |
06675d2e |
83 | } |
84 | |
7d0922d8 |
85 | sub get_user { |
86 | my ( $c, $uid ) = @_; |
87 | |
88 | if ( my $store = $c->default_auth_store ) { |
89 | return $store->get_user($uid); |
90 | } |
91 | else { |
92 | Catalyst::Exception->throw( |
93 | "The user id $uid was passed to an authentication " |
94 | . "plugin, but no default store was specified" ); |
95 | } |
96 | } |
97 | |
06675d2e |
98 | sub prepare { |
99 | my $c = shift->NEXT::prepare(@_); |
100 | |
4fbe2e14 |
101 | if ( $c->isa("Catalyst::Plugin::Session") |
06675d2e |
102 | and !$c->user ) |
103 | { |
7bb06c91 |
104 | if ( $c->sessionid and my $frozen_user = $c->session->{__user} ) { |
e300c5b6 |
105 | $c->_user($frozen_user); |
06675d2e |
106 | } |
107 | } |
108 | |
109 | return $c; |
110 | } |
111 | |
7bb06c91 |
112 | sub auth_restore_user { |
e300c5b6 |
113 | my ( $c, $frozen_user, $store_name ) = @_; |
7bb06c91 |
114 | |
4fbe2e14 |
115 | return |
cde43a59 |
116 | unless $c->isa("Catalyst::Plugin::Session") |
4fbe2e14 |
117 | and $c->config->{authentication}{use_session} |
118 | and $c->sessionid; |
4402d92d |
119 | |
e300c5b6 |
120 | $store_name ||= $c->session->{__user_store}; |
121 | $frozen_user ||= $c->session->{__user}; |
7bb06c91 |
122 | |
e300c5b6 |
123 | my $store = $c->get_auth_store($store_name); |
124 | $c->_user( my $user = $store->from_session( $c, $frozen_user ) ); |
7bb06c91 |
125 | |
e300c5b6 |
126 | return $user; |
7bb06c91 |
127 | |
128 | } |
129 | |
06675d2e |
130 | sub setup { |
131 | my $c = shift; |
132 | |
712a35bf |
133 | my $cfg = $c->config->{authentication} || {}; |
06675d2e |
134 | |
135 | %$cfg = ( |
136 | use_session => 1, |
137 | %$cfg, |
138 | ); |
b003080b |
139 | |
12dae309 |
140 | $c->register_auth_stores( |
141 | default => $cfg->{store}, |
142 | %{ $cfg->{stores} || {} }, |
143 | ); |
96777f3a |
144 | |
b003080b |
145 | $c->NEXT::setup(@_); |
06675d2e |
146 | } |
147 | |
96777f3a |
148 | sub get_auth_store { |
12dae309 |
149 | my ( $self, $name ) = @_; |
150 | $self->auth_stores->{$name} || ( Class::Inspector->loaded($name) && $name ); |
96777f3a |
151 | } |
152 | |
153 | sub get_auth_store_name { |
12dae309 |
154 | my ( $self, $store ) = @_; |
155 | $self->auth_store_names->{$store}; |
96777f3a |
156 | } |
157 | |
158 | sub register_auth_stores { |
12dae309 |
159 | my ( $self, %new ) = @_; |
96777f3a |
160 | |
12dae309 |
161 | foreach my $name ( keys %new ) { |
162 | my $store = $new{$name} or next; |
163 | $self->auth_stores->{$name} = $store; |
164 | $self->auth_store_names->{$store} = $name; |
165 | } |
96777f3a |
166 | } |
167 | |
168 | sub auth_stores { |
12dae309 |
169 | my $self = shift; |
170 | $self->_auth_stores(@_) || $self->_auth_stores( {} ); |
96777f3a |
171 | } |
172 | |
173 | sub auth_store_names { |
12dae309 |
174 | my $self = shift; |
96777f3a |
175 | |
4402d92d |
176 | $self->_auth_store_names || do { |
12dae309 |
177 | tie my %hash, 'Tie::RefHash'; |
178 | $self->_auth_store_names( \%hash ); |
4fbe2e14 |
179 | } |
96777f3a |
180 | } |
181 | |
182 | sub default_auth_store { |
12dae309 |
183 | my $self = shift; |
96777f3a |
184 | |
12dae309 |
185 | if ( my $new = shift ) { |
186 | $self->register_auth_stores( default => $new ); |
187 | } |
96777f3a |
188 | |
12dae309 |
189 | $self->get_auth_store("default"); |
96777f3a |
190 | } |
191 | |
06675d2e |
192 | __PACKAGE__; |
193 | |
194 | __END__ |
195 | |
196 | =pod |
197 | |
198 | =head1 NAME |
199 | |
55395841 |
200 | Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst |
201 | authentication framework. |
06675d2e |
202 | |
203 | =head1 SYNOPSIS |
204 | |
189b5b0c |
205 | use Catalyst qw/ |
206 | Authentication |
207 | Authentication::Store::Foo |
208 | Authentication::Credential::Password |
209 | /; |
210 | |
211 | # later on ... |
212 | # ->login is provided by the Credential::Password module |
213 | $c->login('myusername', 'mypassword'); |
214 | my $age = $c->user->age; |
215 | $c->logout; |
06675d2e |
216 | |
217 | =head1 DESCRIPTION |
218 | |
e7522758 |
219 | The authentication plugin provides generic user support. It is the basis |
220 | for both authentication (checking the user is who they claim to be), and |
221 | authorization (allowing the user to do what the system authorises them to do). |
06675d2e |
222 | |
e7522758 |
223 | Using authentication is split into two parts. A Store is used to actually |
224 | store the user information, and can store any amount of data related to |
225 | the user. Multiple stores can be accessed from within one application. |
226 | Credentials are used to verify users, using the store, given data from |
227 | the frontend. |
189b5b0c |
228 | |
e7522758 |
229 | To implement authentication in a catalyst application you need to add this |
230 | module, plus at least one store and one credential module. |
189b5b0c |
231 | |
e7522758 |
232 | Authentication data can also be stored in a session, if the application |
233 | is using the L<Catalyst::Plugin::Session> module. |
06675d2e |
234 | |
235 | =head1 METHODS |
236 | |
237 | =over 4 |
238 | |
06675d2e |
239 | =item user |
240 | |
189b5b0c |
241 | Returns the currently logged in user or undef if there is none. |
06675d2e |
242 | |
ce0b058d |
243 | =item user_exists |
244 | |
245 | Whether or not a user is logged in right now. |
246 | |
247 | The reason this method exists is that C<<$c->user>> may needlessly load the |
248 | user from the auth store. |
249 | |
250 | If you're just going to say |
251 | |
252 | if ( $c->user_user ) { |
253 | # foo |
254 | } else { |
255 | $c->forward("login"); |
256 | } |
257 | |
258 | it should be more efficient than C<<$c->user>> when a user is marked in the session |
1e055395 |
259 | but C<< $c->user >> hasn't been called yet. |
ce0b058d |
260 | |
4402d92d |
261 | =item logout |
262 | |
263 | Delete the currently logged in user from C<user> and the session. |
264 | |
7d0922d8 |
265 | =item get_user $uid |
266 | |
189b5b0c |
267 | Fetch a particular users details, defined by the given ID, via the default store. |
268 | |
269 | =back |
270 | |
271 | =head1 CONFIGURATION |
272 | |
273 | =over 4 |
274 | |
275 | =item use_session |
276 | |
277 | Whether or not to store the user's logged in state in the session, if the |
e7522758 |
278 | application is also using the L<Catalyst::Plugin::Session> plugin. This |
279 | value is set to true per default. |
280 | |
281 | =item store |
282 | |
1e055395 |
283 | If multiple stores are being used, set the module you want as default here. |
7d0922d8 |
284 | |
4fbe2e14 |
285 | =back |
286 | |
1e055395 |
287 | =item stores |
288 | |
289 | If multiple stores are being used, you need to provide a name for each store |
290 | here, as a hash, the keys are the names you wish to use, and the values are |
291 | the the names of the plugins. |
292 | |
293 | # example |
294 | __PACKAGE__->config( authentication => { |
295 | store => 'Catalyst::Plugin::Authentication::Store::HtPasswd', |
296 | stores => { |
297 | 'dbic' => 'Catalyst::Plugin::Authentication::Store::DBIC' |
298 | } |
299 | }); |
300 | |
301 | |
4fbe2e14 |
302 | =head1 METHODS FOR STORE MANAGEMENT |
303 | |
fe4cf44a |
304 | =over 4 |
305 | |
7d0922d8 |
306 | =item default_auth_store |
307 | |
4fbe2e14 |
308 | Return the store whose name is 'default'. |
7d0922d8 |
309 | |
189b5b0c |
310 | This is set to C<< $c->config->{authentication}{store} >> if that value exists, |
4fbe2e14 |
311 | or by using a Store plugin: |
312 | |
313 | use Catalyst qw/Authentication Authentication::Store::Minimal/; |
314 | |
315 | Sets the default store to |
316 | L<Catalyst::Plugin::Authentication::Store::Minimal::Backend>. |
317 | |
a1e5bd36 |
318 | |
4fbe2e14 |
319 | =item get_auth_store $name |
320 | |
321 | Return the store whose name is $name. |
322 | |
323 | =item get_auth_store_name $store |
324 | |
325 | Return the name of the store $store. |
326 | |
327 | =item auth_stores |
328 | |
329 | A hash keyed by name, with the stores registered in the app. |
330 | |
331 | =item auth_store_names |
332 | |
333 | A ref-hash keyed by store, which contains the names of the stores. |
334 | |
335 | =item register_auth_stores %stores_by_name |
336 | |
337 | Register stores into the application. |
06675d2e |
338 | |
fe4cf44a |
339 | =back |
340 | |
06675d2e |
341 | =head1 INTERNAL METHODS |
342 | |
343 | =over 4 |
344 | |
345 | =item set_authenticated $user |
346 | |
347 | Marks a user as authenticated. Should be called from a |
348 | C<Catalyst::Plugin::Authentication::Credential> plugin after successful |
349 | authentication. |
350 | |
351 | This involves setting C<user> and the internal data in C<session> if |
352 | L<Catalyst::Plugin::Session> is loaded. |
353 | |
e300c5b6 |
354 | =item auth_restore_user $user |
355 | |
356 | Used to restore a user from the session, by C<user> only when it's actually |
357 | needed. |
358 | |
359 | =item save_user_in_session $user |
360 | |
361 | Used to save the user in a session. |
362 | |
06675d2e |
363 | =item prepare |
364 | |
365 | Revives a user from the session object if there is one. |
366 | |
367 | =item setup |
368 | |
369 | Sets the default configuration parameters. |
370 | |
371 | =item |
372 | |
373 | =back |
374 | |
fbe577ac |
375 | =head1 SEE ALSO |
376 | |
377 | L<Catalyst::Plugin::Authentication::Credential::Password>, |
378 | L<Catalyst::Plugin::Authentication::Store::Minimal>, |
379 | L<Catalyst::Plugin::Authorization::ACL>, |
380 | L<Catalyst::Plugin::Authorization::Roles>. |
381 | |
382 | =head1 AUTHOR |
383 | |
384 | Yuval Kogman, C<nothingmuch@woobling.org> |
7d2f34eb |
385 | Jess Robinson |
386 | David Kamholz |
06675d2e |
387 | |
fbe577ac |
388 | =head1 COPYRIGHT & LICNESE |
389 | |
390 | Copyright (c) 2005 the aforementioned authors. All rights |
391 | reserved. This program is free software; you can redistribute |
392 | it and/or modify it under the same terms as Perl itself. |
393 | |
394 | =cut |
06675d2e |
395 | |