0079f964eb068d0ef002c541473edb6a1c38ead0
[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, name the store you want as default here.
282
283 =back
284
285 =head1 METHODS FOR STORE MANAGEMENT
286
287 =over 4
288
289 =item default_auth_store
290
291 Return the store whose name is 'default'.
292
293 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
294 or by using a Store plugin:
295
296         use Catalyst qw/Authentication Authentication::Store::Minimal/;
297
298 Sets the default store to
299 L<Catalyst::Plugin::Authentication::Store::Minimal::Backend>.
300
301
302 =item get_auth_store $name
303
304 Return the store whose name is $name.
305
306 =item get_auth_store_name $store
307
308 Return the name of the store $store.
309
310 =item auth_stores
311
312 A hash keyed by name, with the stores registered in the app.
313
314 =item auth_store_names
315
316 A ref-hash keyed by store, which contains the names of the stores.
317
318 =item register_auth_stores %stores_by_name
319
320 Register stores into the application.
321
322 =back
323
324 =head1 INTERNAL METHODS
325
326 =over 4
327
328 =item set_authenticated $user
329
330 Marks a user as authenticated. Should be called from a
331 C<Catalyst::Plugin::Authentication::Credential> plugin after successful
332 authentication.
333
334 This involves setting C<user> and the internal data in C<session> if
335 L<Catalyst::Plugin::Session> is loaded.
336
337 =item auth_restore_user $user
338
339 Used to restore a user from the session, by C<user> only when it's actually
340 needed.
341
342 =item save_user_in_session $user
343
344 Used to save the user in a session.
345
346 =item prepare
347
348 Revives a user from the session object if there is one.
349
350 =item setup
351
352 Sets the default configuration parameters.
353
354 =item 
355
356 =back
357
358 =head1 SEE ALSO
359
360 L<Catalyst::Plugin::Authentication::Credential::Password>,
361 L<Catalyst::Plugin::Authentication::Store::Minimal>,
362 L<Catalyst::Plugin::Authorization::ACL>,
363 L<Catalyst::Plugin::Authorization::Roles>.
364
365 =head1 AUTHOR
366
367 Yuval Kogman, C<nothingmuch@woobling.org>
368
369 =head1 COPYRIGHT & LICNESE
370
371         Copyright (c) 2005 the aforementioned authors. All rights
372         reserved. This program is free software; you can redistribute
373         it and/or modify it under the same terms as Perl itself.
374
375 =cut
376