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