Improve documentation
[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 for both authentication (checking the user is who they claim to be), and authorization (allowing the user to do what the system authorises them to do).
218
219 Using authentication is split into two parts. A Store is used to actually store the user information, and can store any amount of data related to the user. Multiple stores can be accessed from within one application. Credentials are used to verify users, using the store, given data from the frontend.
220
221 To implement authentication in a catalyst application you need to add this module, plus at least one store and one credential module.
222
223 Authentication data can also be stored in a session, if the application is using the L<Catalyst::Plugin::Session> module.
224
225 =head1 METHODS
226
227 =over 4 
228
229 =item user
230
231 Returns the currently logged in user or undef if there is none.
232
233 =item user_exists
234
235 Whether or not a user is logged in right now.
236
237 The reason this method exists is that C<<$c->user>> may needlessly load the
238 user from the auth store.
239
240 If you're just going to say
241
242         if ( $c->user_user ) {
243                 # foo
244         } else {
245                 $c->forward("login");
246         }
247
248 it should be more efficient than C<<$c->user>> when a user is marked in the session
249 but C<<$c->user>> hasn't been called yet.
250
251 =item logout
252
253 Delete the currently logged in user from C<user> and the session.
254
255 =item get_user $uid
256
257 Fetch a particular users details, defined by the given ID, via the default store.
258
259 =back
260
261 =head1 CONFIGURATION
262
263 =over 4
264
265 =item use_session
266
267 Whether or not to store the user's logged in state in the session, if the
268 application is also using the L<Catalyst::Plugin::Session> plugin.
269
270 =back
271
272 =head1 METHODS FOR STORE MANAGEMENT
273
274 =over 4
275
276 =item default_auth_store
277
278 Return the store whose name is 'default'.
279
280 This is set to C<< $c->config->{authentication}{store} >> if that value exists,
281 or by using a Store plugin:
282
283         use Catalyst qw/Authentication Authentication::Store::Minimal/;
284
285 Sets the default store to
286 L<Catalyst::Plugin::Authentication::Store::Minimal::Backend>.
287
288
289 =item get_auth_store $name
290
291 Return the store whose name is $name.
292
293 =item get_auth_store_name $store
294
295 Return the name of the store $store.
296
297 =item auth_stores
298
299 A hash keyed by name, with the stores registered in the app.
300
301 =item auth_store_names
302
303 A ref-hash keyed by store, which contains the names of the stores.
304
305 =item register_auth_stores %stores_by_name
306
307 Register stores into the application.
308
309 =back
310
311 =head1 INTERNAL METHODS
312
313 =over 4
314
315 =item set_authenticated $user
316
317 Marks a user as authenticated. Should be called from a
318 C<Catalyst::Plugin::Authentication::Credential> plugin after successful
319 authentication.
320
321 This involves setting C<user> and the internal data in C<session> if
322 L<Catalyst::Plugin::Session> is loaded.
323
324 =item auth_restore_user $user
325
326 Used to restore a user from the session, by C<user> only when it's actually
327 needed.
328
329 =item save_user_in_session $user
330
331 Used to save the user in a session.
332
333 =item prepare
334
335 Revives a user from the session object if there is one.
336
337 =item setup
338
339 Sets the default configuration parameters.
340
341 =item 
342
343 =back
344
345 =head1 SEE ALSO
346
347 L<Catalyst::Plugin::Authentication::Credential::Password>,
348 L<Catalyst::Plugin::Authentication::Store::Minimal>,
349 L<Catalyst::Plugin::Authorization::ACL>,
350 L<Catalyst::Plugin::Authorization::Roles>.
351
352 =head1 AUTHOR
353
354 Yuval Kogman, C<nothingmuch@woobling.org>
355
356 =head1 COPYRIGHT & LICNESE
357
358         Copyright (c) 2005 the aforementioned authors. All rights
359         reserved. This program is free software; you can redistribute
360         it and/or modify it under the same terms as Perl itself.
361
362 =cut
363