Auth 0.03, user_exists and a few pod fixes
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Plugin / Authentication.pm
1 #!/usr/bin/perl
2
3 package Catalyst::Plugin::Authentication;
4
5 use base qw/Class::Accessor::Fast Class::Data::Inheritable/;
6
7 BEGIN {
8     __PACKAGE__->mk_accessors(qw/_user/);
9     __PACKAGE__->mk_classdata($_) for qw/_auth_stores _auth_store_names/;
10 }
11
12 use strict;
13 use warnings;
14
15 use Tie::RefHash;
16 use Class::Inspector;
17
18 #BEGIN {
19 #       require constant;
20 #       constant->import(have_want => eval { require Want });
21 #}
22
23 our $VERSION = "0.03";
24
25 sub set_authenticated {
26     my ( $c, $user ) = @_;
27
28     $c->user($user);
29     $c->request->{user} = $user;    # compatibility kludge
30
31     if (    $c->isa("Catalyst::Plugin::Session")
32         and $c->config->{authentication}{use_session}
33         and $user->supports("session") )
34     {
35         $c->save_user_in_session($user);
36     }
37
38     $c->NEXT::set_authenticated($user);
39 }
40
41 sub user {
42     my $c = shift;
43
44     if (@_) {
45         return $c->_user(@_);
46     }
47
48     my $user = $c->_user;
49
50     if ( $user and !Scalar::Util::blessed($user) ) {
51 #               return 1 if have_want() && Want::want("BOOL");
52         return $c->auth_restore_user($user);
53     }
54
55     return $user;
56 }
57
58 sub user_exists {
59         my $c = shift;
60         return defined($c->_user);
61 }
62
63 sub save_user_in_session {
64     my ( $c, $user ) = @_;
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
71 sub logout {
72     my $c = shift;
73
74     $c->user(undef);
75
76     if (    $c->isa("Catalyst::Plugin::Session")
77         and $c->config->{authentication}{use_session} )
78     {
79         delete @{ $c->session }{qw/__user __user_store/};
80     }
81     
82     $c->NEXT::logout(@_);
83 }
84
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
98 sub prepare {
99     my $c = shift->NEXT::prepare(@_);
100
101     if ( $c->isa("Catalyst::Plugin::Session")
102         and !$c->user )
103     {
104         if ( $c->sessionid and my $frozen_user = $c->session->{__user} ) {
105             $c->_user($frozen_user);
106         }
107     }
108
109     return $c;
110 }
111
112 sub auth_restore_user {
113     my ( $c, $frozen_user, $store_name ) = @_;
114
115     return
116       unless $c->isa("Catalyst::Plugin::Session")
117       and $c->config->{authentication}{use_session}
118       and $c->sessionid;
119
120     $store_name  ||= $c->session->{__user_store};
121     $frozen_user ||= $c->session->{__user};
122
123     my $store = $c->get_auth_store($store_name);
124     $c->_user( my $user = $store->from_session( $c, $frozen_user ) );
125
126     return $user;
127
128 }
129
130 sub setup {
131     my $c = shift;
132
133     my $cfg = $c->config->{authentication} || {};
134
135     %$cfg = (
136         use_session => 1,
137         %$cfg,
138     );
139
140     $c->register_auth_stores(
141         default => $cfg->{store},
142         %{ $cfg->{stores} || {} },
143     );
144
145     $c->NEXT::setup(@_);
146 }
147
148 sub get_auth_store {
149     my ( $self, $name ) = @_;
150     $self->auth_stores->{$name} || ( Class::Inspector->loaded($name) && $name );
151 }
152
153 sub get_auth_store_name {
154     my ( $self, $store ) = @_;
155     $self->auth_store_names->{$store};
156 }
157
158 sub register_auth_stores {
159     my ( $self, %new ) = @_;
160
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     }
166 }
167
168 sub auth_stores {
169     my $self = shift;
170     $self->_auth_stores(@_) || $self->_auth_stores( {} );
171 }
172
173 sub auth_store_names {
174     my $self = shift;
175
176     $self->_auth_store_names || do {
177         tie my %hash, 'Tie::RefHash';
178         $self->_auth_store_names( \%hash );
179       }
180 }
181
182 sub default_auth_store {
183     my $self = shift;
184
185     if ( my $new = shift ) {
186         $self->register_auth_stores( default => $new );
187     }
188
189     $self->get_auth_store("default");
190 }
191
192 __PACKAGE__;
193
194 __END__
195
196 =pod
197
198 =head1 NAME
199
200 Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst
201 authentication framework.
202
203 =head1 SYNOPSIS
204
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;
216
217 =head1 DESCRIPTION
218
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).
222
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.
228
229 To implement authentication in a catalyst application you need to add this 
230 module, plus at least one store and one credential module.
231
232 Authentication data can also be stored in a session, if the application 
233 is using the L<Catalyst::Plugin::Session> module.
234
235 =head1 METHODS
236
237 =over 4 
238
239 =item user
240
241 Returns the currently logged in user or undef if there is none.
242
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
259 but C<< $c->user >> hasn't been called yet.
260
261 =item logout
262
263 Delete the currently logged in user from C<user> and the session.
264
265 =item get_user $uid
266
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
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
283 If multiple stores are being used, set the module you want as default here.
284
285 =item stores
286
287 If multiple stores are being used, you need to provide a name for each store
288 here, as a hash, the keys are the names you wish to use, and the values are
289 the the names of the plugins.
290
291  # example
292  __PACKAGE__->config( authentication => {
293                         store => 'Catalyst::Plugin::Authentication::Store::HtPasswd',
294                         stores => { 
295                            'dbic' => 'Catalyst::Plugin::Authentication::Store::DBIC'
296                                   }
297                                          });
298
299 =back
300
301 =head1 METHODS FOR STORE MANAGEMENT
302
303 =over 4
304
305 =item default_auth_store
306
307 Return the store whose name is 'default'.
308
309 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
310 or by using a Store plugin:
311
312         use Catalyst qw/Authentication Authentication::Store::Minimal/;
313
314 Sets the default store to
315 L<Catalyst::Plugin::Authentication::Store::Minimal::Backend>.
316
317
318 =item get_auth_store $name
319
320 Return the store whose name is $name.
321
322 =item get_auth_store_name $store
323
324 Return the name of the store $store.
325
326 =item auth_stores
327
328 A hash keyed by name, with the stores registered in the app.
329
330 =item auth_store_names
331
332 A ref-hash keyed by store, which contains the names of the stores.
333
334 =item register_auth_stores %stores_by_name
335
336 Register stores into the application.
337
338 =back
339
340 =head1 INTERNAL METHODS
341
342 =over 4
343
344 =item set_authenticated $user
345
346 Marks a user as authenticated. Should be called from a
347 C<Catalyst::Plugin::Authentication::Credential> plugin after successful
348 authentication.
349
350 This involves setting C<user> and the internal data in C<session> if
351 L<Catalyst::Plugin::Session> is loaded.
352
353 =item auth_restore_user $user
354
355 Used to restore a user from the session, by C<user> only when it's actually
356 needed.
357
358 =item save_user_in_session $user
359
360 Used to save the user in a session.
361
362 =item prepare
363
364 Revives a user from the session object if there is one.
365
366 =item setup
367
368 Sets the default configuration parameters.
369
370 =item 
371
372 =back
373
374 =head1 SEE ALSO
375
376 L<Catalyst::Plugin::Authentication::Credential::Password>,
377 L<Catalyst::Plugin::Authentication::Store::Minimal>,
378 L<Catalyst::Plugin::Authorization::ACL>,
379 L<Catalyst::Plugin::Authorization::Roles>.
380
381 =head1 AUTHORS
382
383 Yuval Kogman, C<nothingmuch@woobling.org>
384
385 Jess Robinson
386
387 David Kamholz
388
389 =head1 COPYRIGHT & LICNESE
390
391         Copyright (c) 2005 the aforementioned authors. All rights
392         reserved. This program is free software; you can redistribute
393         it and/or modify it under the same terms as Perl itself.
394
395 =cut
396