$c->user lazy restores from session
[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 our $VERSION = "0.01";
19
20 sub set_authenticated {
21     my ( $c, $user ) = @_;
22
23     $c->user($user);
24
25     if (    $c->isa("Catalyst::Plugin::Session")
26         and $c->config->{authentication}{use_session}
27         and $user->supports("session") )
28     {
29         $c->save_user_in_session($user);
30     }
31 }
32
33 sub user {
34         my $c = shift;
35
36         if ( @_ ) {
37                 return $c->_user( @_ );
38         }
39
40         my $user = $c->_user;
41
42         if ( $user and !Scalar::Util::blessed( $user ) ) {
43                 return $c->auth_restore_user( $user );
44         }
45
46         return $user;
47 }
48
49 sub save_user_in_session {
50         my ( $c, $user ) = @_;
51
52     my $store = $user->store || ref $user;
53     $c->session->{__user_store} = $c->get_auth_store_name($store) || $store;
54     $c->session->{__user} = $user->for_session;
55 }
56
57 sub logout {
58     my $c = shift;
59
60     $c->user(undef);
61
62     if (    $c->isa("Catalyst::Plugin::Session")
63         and $c->config->{authentication}{use_session} )
64     {
65         delete @{ $c->session }{qw/__user __user_store/};
66     }
67 }
68
69 sub get_user {
70     my ( $c, $uid ) = @_;
71
72     if ( my $store = $c->default_auth_store ) {
73         return $store->get_user($uid);
74     }
75     else {
76         Catalyst::Exception->throw(
77                 "The user id $uid was passed to an authentication "
78               . "plugin, but no default store was specified" );
79     }
80 }
81
82 sub prepare {
83     my $c = shift->NEXT::prepare(@_);
84
85     if (    $c->isa("Catalyst::Plugin::Session")
86         and $c->default_auth_store
87         and !$c->user )
88     {
89         if ( $c->sessionid and my $frozen_user = $c->session->{__user} ) {
90                         $c->_user( $frozen_user );
91         }
92     }
93
94     return $c;
95 }
96
97 sub auth_restore_user {
98         my ( $c, $frozen_user, $store_name ) = @_;
99
100         $store_name  ||= $c->session->{__user_store};
101         $frozen_user ||= $c->session->{__user};
102
103         my $store = $c->get_auth_store( $store_name );
104         $c->_user( my $user = $store->from_session( $c, $frozen_user ) );
105         $c->request->{user} = $user;    # compatibility kludge
106
107         return $user;
108
109 }
110
111 sub setup {
112     my $c = shift;
113
114     my $cfg = $c->config->{authentication} || {};
115
116     %$cfg = (
117         use_session => 1,
118         %$cfg,
119     );
120
121     $c->register_auth_stores(
122         default => $cfg->{store},
123         %{ $cfg->{stores} || {} },
124     );
125
126     $c->NEXT::setup(@_);
127 }
128
129 sub get_auth_store {
130     my ( $self, $name ) = @_;
131     $self->auth_stores->{$name} || ( Class::Inspector->loaded($name) && $name );
132 }
133
134 sub get_auth_store_name {
135     my ( $self, $store ) = @_;
136     $self->auth_store_names->{$store};
137 }
138
139 sub register_auth_stores {
140     my ( $self, %new ) = @_;
141
142     foreach my $name ( keys %new ) {
143         my $store = $new{$name} or next;
144         $self->auth_stores->{$name}       = $store;
145         $self->auth_store_names->{$store} = $name;
146     }
147 }
148
149 sub auth_stores {
150     my $self = shift;
151     $self->_auth_stores(@_) || $self->_auth_stores( {} );
152 }
153
154 sub auth_store_names {
155     my $self = shift;
156
157     unless ( $self->_auth_store_names ) {
158         tie my %hash, 'Tie::RefHash';
159         $self->_auth_store_names( \%hash );
160     }
161
162     $self->_auth_store_names;
163 }
164
165 sub default_auth_store {
166     my $self = shift;
167
168     if ( my $new = shift ) {
169         $self->register_auth_stores( default => $new );
170     }
171
172     $self->get_auth_store("default");
173 }
174
175 __PACKAGE__;
176
177 __END__
178
179 =pod
180
181 =head1 NAME
182
183 Catalyst::Plugin::Authentication - 
184
185 =head1 SYNOPSIS
186
187         use Catalyst qw/
188                 Authentication
189                 Authentication::Store::Foo
190                 Authentication::Credential::Password
191         /;
192
193 =head1 DESCRIPTION
194
195 The authentication plugin is used by the various authentication and
196 authorization plugins in catalyst.
197
198 It defines the notion of a logged in user, and provides integration with the 
199
200 =head1 METHODS
201
202 =over 4 
203
204 =item logout
205
206 Delete the currently logged in user from C<user> and the session.
207
208 =item user
209
210 Returns the currently logged user or undef if there is none.
211
212 =item get_user $uid
213
214 Delegate C<get_user> to the default store.
215
216 =item default_auth_store
217
218 Returns C<< $c->config->{authentication}{store} >>.
219
220 =back
221
222 =head1 INTERNAL METHODS
223
224 =over 4
225
226 =item set_authenticated $user
227
228 Marks a user as authenticated. Should be called from a
229 C<Catalyst::Plugin::Authentication::Credential> plugin after successful
230 authentication.
231
232 This involves setting C<user> and the internal data in C<session> if
233 L<Catalyst::Plugin::Session> is loaded.
234
235 =item prepare
236
237 Revives a user from the session object if there is one.
238
239 =item setup
240
241 Sets the default configuration parameters.
242
243 =item 
244
245 =back
246
247 =head1 CONFIGURATION
248
249 =over 4
250
251 =item use_session
252
253 Whether or not to store the user's logged in state in the session, if the
254 application is also using the L<Catalyst::Plugin::Authentication> plugin.
255
256 =back
257
258 =cut
259
260