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