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