C::P::Authentication:
[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     $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 =back
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
302 =head1 METHODS FOR STORE MANAGEMENT
303
304 =over 4
305
306 =item default_auth_store
307
308 Return the store whose name is 'default'.
309
310 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
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
318
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.
338
339 =back
340
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
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
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
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>
385 Jess Robinson
386 David Kamholz
387
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
395