Bug fixes and test from nilsonsfj - Cheers!
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Plugin / Authentication.pm
CommitLineData
06675d2e 1#!/usr/bin/perl
2
3package Catalyst::Plugin::Authentication;
4
b003080b 5use base qw/Class::Accessor::Fast Class::Data::Inheritable/;
06675d2e 6
b003080b 7BEGIN {
7bb06c91 8 __PACKAGE__->mk_accessors(qw/_user/);
b003080b 9}
06675d2e 10
11use strict;
12use warnings;
13
96777f3a 14use Tie::RefHash;
12dae309 15use Class::Inspector;
96777f3a 16
bbf1cb39 17# this optimization breaks under Template::Toolkit
18# use user_exists instead
e145babc 19#BEGIN {
20# require constant;
21# constant->import(have_want => eval { require Want });
22#}
a1e5bd36 23
c5fbff80 24our $VERSION = "0.09999_01";
c7c003d3 25
06675d2e 26sub set_authenticated {
54c8dc06 27 my ( $c, $user, $realmname ) = @_;
06675d2e 28
29 $c->user($user);
e300c5b6 30 $c->request->{user} = $user; # compatibility kludge
06675d2e 31
54c8dc06 32 if (!$realmname) {
33 $realmname = 'default';
06675d2e 34 }
54c8dc06 35
36 if ( $c->isa("Catalyst::Plugin::Session")
37 and $c->config->{authentication}{use_session}
38 and $user->supports("session") )
39 {
e8919861 40 $c->save_user_in_session($user, $realmname);
54c8dc06 41 }
45c7644b 42 $user->auth_realm($realmname);
54c8dc06 43
44 $c->NEXT::set_authenticated($user, $realmname);
06675d2e 45}
46
488433fd 47sub _should_save_user_in_session {
48 my ( $c, $user ) = @_;
49
50 $c->_auth_sessions_supported
51 and $c->config->{authentication}{use_session}
52 and $user->supports("session");
53}
54
55sub _should_load_user_from_session {
56 my ( $c, $user ) = @_;
57
58 $c->_auth_sessions_supported
59 and $c->config->{authentication}{use_session}
60 and $c->session_is_valid;
61}
62
63sub _auth_sessions_supported {
64 my $c = shift;
65 $c->isa("Catalyst::Plugin::Session");
66}
67
7bb06c91 68sub user {
e300c5b6 69 my $c = shift;
7bb06c91 70
e300c5b6 71 if (@_) {
72 return $c->_user(@_);
73 }
7bb06c91 74
45c7644b 75 if ( defined($c->_user) ) {
76 return $c->_user;
56e23e7a 77 } else {
47c6643f 78 return $c->auth_restore_user;
e300c5b6 79 }
7bb06c91 80}
81
54c8dc06 82# change this to allow specification of a realm - to verify the user is part of that realm
83# in addition to verifying that they exist.
ce0b058d 84sub user_exists {
85 my $c = shift;
9deccb83 86 return defined($c->_user) || defined($c->_user_in_session);
56e23e7a 87}
88
45c7644b 89# works like user_exists - except only returns true if user
90# exists AND is in the realm requested.
91sub user_in_realm {
92 my ($c, $realmname) = @_;
93
94 if (defined($c->_user)) {
95 return ($c->_user->auth_realm eq $realmname);
96 } elsif (defined($c->_user_in_session)) {
97 return ($c->session->{__user_realm} eq $realmname);
98 } else {
99 return undef;
100 }
101}
54c8dc06 102
12dae309 103sub save_user_in_session {
e8919861 104 my ( $c, $user, $realmname ) = @_;
12dae309 105
54c8dc06 106 $c->session->{__user_realm} = $realmname;
107
45c7644b 108 # we want to ask the store for a user prepared for the session.
54c8dc06 109 # but older modules split this functionality between the user and the
45c7644b 110 # store. We try the store first. If not, we use the old method.
54c8dc06 111 my $realm = $c->get_auth_realm($realmname);
112 if ($realm->{'store'}->can('for_session')) {
113 $c->session->{__user} = $realm->{'store'}->for_session($c, $user);
114 } else {
115 $c->session->{__user} = $user->for_session;
116 }
12dae309 117}
118
06675d2e 119sub logout {
120 my $c = shift;
121
122 $c->user(undef);
b003080b 123
54c8dc06 124 if (
125 $c->isa("Catalyst::Plugin::Session")
126 and $c->config->{authentication}{use_session}
127 and $c->session_is_valid
128 ) {
129 delete @{ $c->session }{qw/__user __user_realm/};
b003080b 130 }
351e2a82 131
132 $c->NEXT::logout(@_);
06675d2e 133}
134
54c8dc06 135sub find_user {
136 my ( $c, $userinfo, $realmname ) = @_;
137
138 $realmname ||= 'default';
139 my $realm = $c->get_auth_realm($realmname);
140 if ( $realm->{'store'} ) {
141 return $realm->{'store'}->find_user($userinfo, $c);
142 } else {
143 $c->log->debug('find_user: unable to locate a store matching the requested realm');
7d0922d8 144 }
145}
146
54c8dc06 147
47c6643f 148sub _user_in_session {
149 my $c = shift;
150
488433fd 151 return unless $c->_should_load_user_from_session;
47c6643f 152
153 return $c->session->{__user};
488433fd 154}
47c6643f 155
7bb06c91 156sub auth_restore_user {
54c8dc06 157 my ( $c, $frozen_user, $realmname ) = @_;
7bb06c91 158
47c6643f 159 $frozen_user ||= $c->_user_in_session;
160 return unless defined($frozen_user);
4402d92d 161
54c8dc06 162 $realmname ||= $c->session->{__user_realm};
163 return unless $realmname; # FIXME die unless? This is an internal inconsistency
7bb06c91 164
54c8dc06 165 my $realm = $c->get_auth_realm($realmname);
166 $c->_user( my $user = $realm->{'store'}->from_session( $c, $frozen_user ) );
167
168 # this sets the realm the user originated in.
45c7644b 169 $user->auth_realm($realmname);
e300c5b6 170 return $user;
7bb06c91 171
172}
173
54c8dc06 174# we can't actually do our setup in setup because the model has not yet been loaded.
175# So we have to trigger off of setup_finished. :-(
06675d2e 176sub setup {
c5fbff80 177 my $app = shift;
06675d2e 178
c5fbff80 179 $app->_authentication_initialize();
180 $app->NEXT::setup(@_);
54c8dc06 181}
182
183## the actual initialization routine. whee.
184sub _authentication_initialize {
c5fbff80 185 my $app = shift;
54c8dc06 186
d6209239 187 ## let's avoid recreating / configuring everything if we have already done it, eh?
188 if ($app->can('_auth_realms')) { return };
c5fbff80 189
d6209239 190 ## make classdata where it is used.
191 $app->mk_classdata( '_auth_realms' => {});
937d5ab9 192
c5fbff80 193 my $cfg = $app->config->{'authentication'} ||= {};
06675d2e 194
c5fbff80 195 $cfg->{use_session} = 1;
c5fbff80 196
54c8dc06 197 if (exists($cfg->{'realms'})) {
54c8dc06 198 foreach my $realm (keys %{$cfg->{'realms'}}) {
c5fbff80 199 $app->setup_auth_realm($realm, $cfg->{'realms'}{$realm});
54c8dc06 200 }
54c8dc06 201 # if we have a 'default-realm' in the config hash and we don't already
202 # have a realm called 'default', we point default at the realm specified
c5fbff80 203 if (exists($cfg->{'default_realm'}) && !$app->get_auth_realm('default')) {
204 $app->_set_default_auth_realm($cfg->{'default_realm'});
54c8dc06 205 }
206 } else {
c5fbff80 207
208 ## BACKWARDS COMPATIBILITY - if realm is not defined - then we are probably dealing
209 ## with an old-school config. The only caveat here is that we must add a classname
210
54c8dc06 211 foreach my $storename (keys %{$cfg->{'stores'}}) {
212 my $realmcfg = {
213 store => $cfg->{'stores'}{$storename},
214 };
c5fbff80 215 $app->setup_auth_realm($storename, $realmcfg);
54c8dc06 216 }
217 }
218
06675d2e 219}
220
54c8dc06 221# set up realmname.
222sub setup_auth_realm {
223 my ($app, $realmname, $config) = @_;
224
cccb9b5e 225 $app->log->debug("Setting up auth realm $realmname") if $app->debug;
54c8dc06 226 if (!exists($config->{'store'}{'class'})) {
227 Carp::croak "Couldn't setup the authentication realm named '$realmname', no class defined";
228 }
229
230 # use the
231 my $storeclass = $config->{'store'}{'class'};
232
233 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
45c7644b 234 ## taken to mean C::P::A::Store::(specifiedclass)
54c8dc06 235 if ($storeclass !~ /^\+(.*)$/ ) {
45c7644b 236 $storeclass = "Catalyst::Plugin::Authentication::Store::${storeclass}";
54c8dc06 237 } else {
238 $storeclass = $1;
239 }
240
241
242 # a little niceness - since most systems seem to use the password credential class,
243 # if no credential class is specified we use password.
244 $config->{credential}{class} ||= "Catalyst::Plugin::Authentication::Credential::Password";
245
246 my $credentialclass = $config->{'credential'}{'class'};
247
248 ## follow catalyst class naming - a + prefix means a fully qualified class, otherwise it's
249 ## taken to mean C::P::A::Credential::(specifiedclass)
250 if ($credentialclass !~ /^\+(.*)$/ ) {
251 $credentialclass = "Catalyst::Plugin::Authentication::Credential::${credentialclass}";
252 } else {
253 $credentialclass = $1;
254 }
255
256 # if we made it here - we have what we need to load the classes;
257 Catalyst::Utils::ensure_class_loaded( $credentialclass );
258 Catalyst::Utils::ensure_class_loaded( $storeclass );
259
260 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
261 # of get_user and add it to the class. this is because the auth routines use find_user,
262 # and rely on it being present. (this avoids per-call checks)
263 if (!$storeclass->can('find_user')) {
264 no strict 'refs';
265 *{"${storeclass}::find_user"} = sub {
266 my ($self, $info) = @_;
267 my @rest = @{$info->{rest}} if exists($info->{rest});
268 $self->get_user($info->{id}, @rest);
269 };
270 }
271
272 $app->auth_realms->{$realmname}{'store'} = $storeclass->new($config->{'store'}, $app);
45c7644b 273 $app->auth_realms->{$realmname}{'credential'} = $credentialclass->new($config->{'credential'}, $app);
96777f3a 274}
275
54c8dc06 276sub auth_realms {
277 my $self = shift;
278 return($self->_auth_realms);
96777f3a 279}
280
54c8dc06 281sub get_auth_realm {
282 my ($app, $realmname) = @_;
283 return $app->auth_realms->{$realmname};
284}
96777f3a 285
e8919861 286
287# Very internal method. Vital Valuable Urgent, Do not touch on pain of death.
288# Using this method just assigns the default realm to be the value associated
289# with the realmname provided. It WILL overwrite any real realm called 'default'
290# so can be very confusing if used improperly. It's used properly already.
291# Translation: don't use it.
292sub _set_default_auth_realm {
54c8dc06 293 my ($app, $realmname) = @_;
294
295 if (exists($app->auth_realms->{$realmname})) {
296 $app->auth_realms->{'default'} = $app->auth_realms->{$realmname};
12dae309 297 }
54c8dc06 298 return $app->get_auth_realm('default');
96777f3a 299}
300
54c8dc06 301sub authenticate {
302 my ($app, $userinfo, $realmname) = @_;
303
304 if (!$realmname) {
305 $realmname = 'default';
306 }
307
308 my $realm = $app->get_auth_realm($realmname);
309
45c7644b 310 ## note to self - make authenticate throw an exception if realm is invalid.
311
54c8dc06 312 if ($realm && exists($realm->{'credential'})) {
313 my $user = $realm->{'credential'}->authenticate($app, $realm->{store}, $userinfo);
649de93b 314 if (ref($user)) {
54c8dc06 315 $app->set_authenticated($user, $realmname);
316 return $user;
317 }
318 } else {
319 $app->log->debug("The realm requested, '$realmname' does not exist," .
320 " or there is no credential associated with it.")
321 }
0cc778ab 322 return undef;
96777f3a 323}
324
54c8dc06 325## BACKWARDS COMPATIBILITY -- Warning: Here be monsters!
326#
327# What follows are backwards compatibility routines - for use with Stores and Credentials
328# that have not been updated to work with C::P::Authentication v0.10.
329# These are here so as to not break people's existing installations, but will go away
330# in a future version.
331#
332# The old style of configuration only supports a single store, as each store module
333# sets itself as the default store upon being loaded. This is the only supported
334# 'compatibility' mode.
335#
336
337sub get_user {
338 my ( $c, $uid, @rest ) = @_;
96777f3a 339
54c8dc06 340 return $c->find_user( {'id' => $uid, 'rest'=>\@rest }, 'default' );
96777f3a 341}
342
e8919861 343
54c8dc06 344## this should only be called when using old-style authentication plugins. IF this gets
345## called in a new-style config - it will OVERWRITE the store of your default realm. Don't do it.
346## also - this is a partial setup - because no credential is instantiated... in other words it ONLY
347## works with old-style auth plugins and C::P::Authentication in compatibility mode. Trying to combine
348## this with a realm-type config will probably crash your app.
96777f3a 349sub default_auth_store {
12dae309 350 my $self = shift;
96777f3a 351
12dae309 352 if ( my $new = shift ) {
54c8dc06 353 $self->auth_realms->{'default'}{'store'} = $new;
354 my $storeclass = ref($new);
355
356 # BACKWARDS COMPATIBILITY - if the store class does not define find_user, we define it in terms
357 # of get_user and add it to the class. this is because the auth routines use find_user,
358 # and rely on it being present. (this avoids per-call checks)
359 if (!$storeclass->can('find_user')) {
360 no strict 'refs';
361 *{"${storeclass}::find_user"} = sub {
362 my ($self, $info) = @_;
363 my @rest = @{$info->{rest}} if exists($info->{rest});
364 $self->get_user($info->{id}, @rest);
365 };
366 }
12dae309 367 }
96777f3a 368
54c8dc06 369 return $self->get_auth_realm('default')->{'store'};
96777f3a 370}
371
54c8dc06 372## BACKWARDS COMPATIBILITY
373## this only ever returns a hash containing 'default' - as that is the only
374## supported mode of calling this.
375sub auth_store_names {
376 my $self = shift;
377
378 my %hash = ( $self->get_auth_realm('default')->{'store'} => 'default' );
379}
380
381sub get_auth_store {
382 my ( $self, $name ) = @_;
383
384 if ($name ne 'default') {
385 Carp::croak "get_auth_store called on non-default realm '$name'. Only default supported in compatibility mode";
386 } else {
387 $self->default_auth_store();
388 }
389}
390
391sub get_auth_store_name {
392 my ( $self, $store ) = @_;
393 return 'default';
394}
395
396# sub auth_stores is only used internally - here for completeness
397sub auth_stores {
398 my $self = shift;
399
400 my %hash = ( 'default' => $self->get_auth_realm('default')->{'store'});
401}
402
06675d2e 403__PACKAGE__;
404
405__END__
406
407=pod
408
409=head1 NAME
410
55395841 411Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst
412authentication framework.
06675d2e 413
414=head1 SYNOPSIS
415
189b5b0c 416 use Catalyst qw/
417 Authentication
189b5b0c 418 /;
419
420 # later on ...
c5fbff80 421 $c->authenticate({ username => 'myusername',
422 password => 'mypassword' });
54c8dc06 423 my $age = $c->user->get('age');
189b5b0c 424 $c->logout;
06675d2e 425
426=head1 DESCRIPTION
427
16ef3eb8 428The authentication plugin provides generic user support for Catalyst apps. It
429is the basis for both authentication (checking the user is who they claim to
430be), and authorization (allowing the user to do what the system authorises
431them to do).
432
433Using authentication is split into two parts. A Store is used to actually
434store the user information, and can store any amount of data related to the
435user. Credentials are used to verify users, using information from the store,
436given data from the frontend. A Credential and a Store are paired to form a
e8919861 437'Realm'. A Catalyst application using the authentication framework must have
438at least one realm, and may have several.
189b5b0c 439
6a36933d 440To implement authentication in a Catalyst application you need to add this
16ef3eb8 441module, and specify at least one realm in the configuration.
189b5b0c 442
e7522758 443Authentication data can also be stored in a session, if the application
444is using the L<Catalyst::Plugin::Session> module.
06675d2e 445
30e90c6f 446B<NOTE> in version 0.10 of this module, the interface to this module changed.
447Please see L</COMPATIBILITY ROUTINES> for more information.
e8919861 448
4bb9b01c 449=head1 INTRODUCTION
450
451=head2 The Authentication/Authorization Process
452
453Web applications typically need to identify a user - to tell the user apart
454from other users. This is usually done in order to display private information
455that is only that user's business, or to limit access to the application so
456that only certain entities can access certain parts.
457
458This process is split up into several steps. First you ask the user to identify
459themselves. At this point you can't be sure that the user is really who they
460claim to be.
461
6a36933d 462Then the user tells you who they are, and backs this claim with some piece of
4bb9b01c 463information that only the real user could give you. For example, a password is
464a secret that is known to both the user and you. When the user tells you this
465password you can assume they're in on the secret and can be trusted (ignore
466identity theft for now). Checking the password, or any other proof is called
467B<credential verification>.
468
469By this time you know exactly who the user is - the user's identity is
16ef3eb8 470B<authenticated>. This is where this module's job stops, and your application
471or other plugins step in.
472
473The next logical step is B<authorization>, the process of deciding what a user
474is (or isn't) allowed to do. For example, say your users are split into two
475main groups - regular users and administrators. You want to verify that the
4bb9b01c 476currently logged in user is indeed an administrator before performing the
c5fbff80 477actions in an administrative part of your application. These decisions may be
16ef3eb8 478made within your application code using just the information available after
479authentication, or it may be facilitated by a number of plugins.
4bb9b01c 480
481=head2 The Components In This Framework
482
62f27384 483=head3 Realms
484
485Configuration of the Catalyst::Plugin::Authentication framework is done in
486terms of realms. In simplest terms, a realm is a pairing of a Credential
487verifier and a User storage (Store) backend.
488
489An application can have any number of Realms, each of which operates
490independant of the others. Each realm has a name, which is used to identify it
491as the target of an authentication request. This name can be anything, such as
492'users' or 'members'. One realm must be defined as the default_realm, which is
16ef3eb8 493used when no realm name is specified. More information about configuring
494realms is available in the configuration section.
62f27384 495
4bb9b01c 496=head3 Credential Verifiers
497
498When user input is transferred to the L<Catalyst> application (typically via
16ef3eb8 499form inputs) the application may pass this information into the authentication
500system through the $c->authenticate() method. From there, it is passed to the
62f27384 501appropriate Credential verifier.
4bb9b01c 502
503These plugins check the data, and ensure that it really proves the user is who
504they claim to be.
505
506=head3 Storage Backends
507
45c7644b 508The authentication data also identifies a user, and the Storage backend modules
62f27384 509use this data to locate and return a standardized object-oriented
510representation of a user.
4bb9b01c 511
512When a user is retrieved from a store it is not necessarily authenticated.
62f27384 513Credential verifiers accept a set of authentication data and use this
514information to retrieve the user from the store they are paired with.
4bb9b01c 515
516=head3 The Core Plugin
517
62f27384 518This plugin on its own is the glue, providing realm configuration, session
4bb9b01c 519integration, and other goodness for the other plugins.
520
521=head3 Other Plugins
522
523More layers of plugins can be stacked on top of the authentication code. For
524example, L<Catalyst::Plugin::Session::PerUser> provides an abstraction of
525browser sessions that is more persistent per users.
526L<Catalyst::Plugin::Authorization::Roles> provides an accepted way to separate
527and group users into categories, and then check which categories the current
528user belongs to.
529
5e91c057 530=head1 EXAMPLE
531
16ef3eb8 532Let's say we were storing users in a simple perl hash. Users are
533verified by supplying a password which is matched within the hash.
5e91c057 534
535This means that our application will begin like this:
536
537 package MyApp;
538
539 use Catalyst qw/
540 Authentication
5e91c057 541 /;
542
62f27384 543 __PACKAGE__->config->{authentication} =
c5fbff80 544 {
545 default_realm => 'members',
546 realms => {
547 members => {
548 credential => {
549 class => 'Password',
550 password_field => 'password',
551 password_type => 'clear'
552 },
553 store => {
554 class => 'Minimal',
555 users = {
556 bob => {
557 password => "s00p3r",
558 editor => 'yes',
559 roles => [qw/edit delete/],
560 },
561 william => {
562 password => "s3cr3t",
563 roles => [qw/comment/],
564 }
565 }
566 }
567 }
568 }
569 };
62f27384 570
5e91c057 571
16ef3eb8 572This tells the authentication plugin what realms are available, which
573credential and store modules are used, and the configuration of each. With
574this code loaded, we can now attempt to authenticate users.
5e91c057 575
62f27384 576To show an example of this, let's create an authentication controller:
5e91c057 577
578 package MyApp::Controller::Auth;
579
580 sub login : Local {
581 my ( $self, $c ) = @_;
582
583 if ( my $user = $c->req->param("user")
584 and my $password = $c->req->param("password") )
585 {
62f27384 586 if ( $c->authenticate( { username => $user,
587 password => $password } ) ) {
588 $c->res->body( "hello " . $c->user->get("name") );
5e91c057 589 } else {
590 # login incorrect
591 }
592 }
593 else {
594 # invalid form input
595 }
596 }
597
598This code should be very readable. If all the necessary fields are supplied,
c5fbff80 599call the "authenticate" method from the controller. If it succeeds the
600user is logged in.
5e91c057 601
62f27384 602The credential verifier will attempt to retrieve the user whose details match
603the authentication information provided to $c->authenticate(). Once it fetches
604the user the password is checked and if it matches the user will be
16ef3eb8 605B<authenticated> and C<< $c->user >> will contain the user object retrieved
62f27384 606from the store.
5e91c057 607
62f27384 608In the above case, the default realm is checked, but we could just as easily
609check an alternate realm. If this were an admin login, for example, we could
610authenticate on the admin realm by simply changing the $c->authenticate()
611call:
5e91c057 612
62f27384 613 if ( $c->authenticate( { username => $user,
614 password => $password }, 'admin' )l ) {
615 $c->res->body( "hello " . $c->user->get("name") );
616 } ...
5e91c057 617
5e91c057 618
c5fbff80 619Now suppose we want to restrict the ability to edit to a user with an
620'editor' value of yes.
5e91c057 621
62f27384 622The restricted action might look like this:
5e91c057 623
62f27384 624 sub edit : Local {
5e91c057 625 my ( $self, $c ) = @_;
626
627 $c->detach("unauthorized")
628 unless $c->user_exists
c5fbff80 629 and $c->user->get('editor') eq 'yes';
5e91c057 630
631 # do something restricted here
632 }
633
c5fbff80 634(Note that if you have multiple realms, you can use $c->user_in_realm('realmname')
635in place of $c->user_exists(); This will essentially perform the same
636verification as user_exists, with the added requirement that if there is a
637user, it must have come from the realm specified.)
638
639The above example is somewhat similar to role based access control.
62f27384 640L<Catalyst::Plugin::Authentication::Store::Minimal> treats the roles field as
641an array of role names. Let's leverage this. Add the role authorization
642plugin:
5e91c057 643
644 use Catalyst qw/
645 ...
646 Authorization::Roles
647 /;
648
62f27384 649 sub edit : Local {
5e91c057 650 my ( $self, $c ) = @_;
651
62f27384 652 $c->detach("unauthorized") unless $c->check_roles("edit");
5e91c057 653
654 # do something restricted here
655 }
656
657This is somewhat simpler and will work if you change your store, too, since the
658role interface is consistent.
659
0cc778ab 660Let's say your app grew, and you now have 10000 users. It's no longer
661efficient to maintain a hash of users, so you move this data to a database.
662You can accomplish this simply by installing the DBIx::Class Store and
663changing your config:
5e91c057 664
0cc778ab 665 __PACKAGE__->config->{authentication} =
666 {
667 default_realm => 'members',
668 realms => {
669 members => {
670 credential => {
c5fbff80 671 class => 'Password',
672 password_field => 'password',
673 password_type => 'clear'
0cc778ab 674 },
675 store => {
676 class => 'DBIx::Class',
677 user_class => 'MyApp::Users',
678 role_column => 'roles'
679 }
680 }
681 }
682 };
5e91c057 683
0cc778ab 684The authentication system works behind the scenes to load your data from the
685new source. The rest of your application is completely unchanged.
5e91c057 686
189b5b0c 687
688=head1 CONFIGURATION
689
690=over 4
691
0cc778ab 692 # example
693 __PACKAGE__->config->{authentication} =
694 {
695 default_realm => 'members',
696 realms => {
697 members => {
698 credential => {
c5fbff80 699 class => 'Password',
700 password_field => 'password',
701 password_type => 'clear'
0cc778ab 702 },
703 store => {
704 class => 'DBIx::Class',
705 user_class => 'MyApp::Users',
706 role_column => 'roles'
707 }
708 },
709 admins => {
710 credential => {
c5fbff80 711 class => 'Password',
712 password_field => 'password',
713 password_type => 'clear'
0cc778ab 714 },
715 store => {
716 class => '+MyApp::Authentication::Store::NetAuth',
717 authserver => '192.168.10.17'
718 }
719 }
720
721 }
722 };
723
189b5b0c 724=item use_session
725
726Whether or not to store the user's logged in state in the session, if the
e8919861 727application is also using L<Catalyst::Plugin::Session>. This
e7522758 728value is set to true per default.
729
0cc778ab 730=item default_realm
fe4cf44a 731
0cc778ab 732This defines which realm should be used as when no realm is provided to methods
733that require a realm such as authenticate or find_user.
7d0922d8 734
0cc778ab 735=item realms
7d0922d8 736
0cc778ab 737This contains the series of realm configurations you want to use for your app.
738The only rule here is that there must be at least one. A realm consists of a
739name, which is used to reference the realm, a credential and a store.
4fbe2e14 740
0cc778ab 741Each realm config contains two hashes, one called 'credential' and one called
742'store', each of which provide configuration details to the respective modules.
743The contents of these hashes is specific to the module being used, with the
744exception of the 'class' element, which tells the core Authentication module the
e8919861 745classname to instantiate.
4fbe2e14 746
0cc778ab 747The 'class' element follows the standard Catalyst mechanism of class
748specification. If a class is prefixed with a +, it is assumed to be a complete
749class name. Otherwise it is considered to be a portion of the class name. For
e8919861 750credentials, the classname 'B<Password>', for example, is expanded to
751Catalyst::Plugin::Authentication::Credential::B<Password>. For stores, the
752classname 'B<storename>' is expanded to:
45c7644b 753Catalyst::Plugin::Authentication::Store::B<storename>.
4fbe2e14 754
a1e5bd36 755
fe4cf44a 756=back
757
e8919861 758
759=head1 METHODS
760
761=over 4
762
763=item authenticate( $userinfo, $realm )
764
765Attempts to authenticate the user using the information in the $userinfo hash
766reference using the realm $realm. $realm may be omitted, in which case the
767default realm is checked.
768
769=item user
770
771Returns the currently logged in user or undef if there is none.
772
773=item user_exists
774
775Returns true if a user is logged in right now. The difference between
776user_exists and user is that user_exists will return true if a user is logged
45c7644b 777in, even if it has not been yet retrieved from the storage backend. If you only
e8919861 778need to know if the user is logged in, depending on the storage mechanism this
779can be much more efficient.
780
45c7644b 781=item user_in_realm ( $realm )
782
783Works like user_exists, except that it only returns true if a user is both
c5fbff80 784logged in right now and was retrieved from the realm provided.
45c7644b 785
e8919861 786=item logout
787
788Logs the user out, Deletes the currently logged in user from $c->user and the session.
789
790=item find_user( $userinfo, $realm )
791
792Fetch a particular users details, matching the provided user info, from the realm
793specified in $realm.
794
795=back
0cc778ab 796
06675d2e 797=head1 INTERNAL METHODS
798
e8919861 799These methods are for Catalyst::Plugin::Authentication B<INTERNAL USE> only.
800Please do not use them in your own code, whether application or credential /
801store modules. If you do, you will very likely get the nasty shock of having
802to fix / rewrite your code when things change. They are documented here only
803for reference.
06675d2e 804
e8919861 805=over 4
06675d2e 806
e8919861 807=item set_authenticated ( $user, $realmname )
06675d2e 808
e8919861 809Marks a user as authenticated. This is called from within the authenticate
810routine when a credential returns a user. $realmname defaults to 'default'
06675d2e 811
e8919861 812=item auth_restore_user ( $user, $realmname )
e300c5b6 813
e8919861 814Used to restore a user from the session. In most cases this is called without
815arguments to restore the user via the session. Can be called with arguments
816when restoring a user from some other method. Currently not used in this way.
e300c5b6 817
e8919861 818=item save_user_in_session ( $user, $realmname )
e300c5b6 819
e8919861 820Used to save the user in a session. Saves $user in session, marked as
821originating in $realmname. Both arguments are required.
e300c5b6 822
e8919861 823=item auth_realms
06675d2e 824
e8919861 825Returns a hashref containing realmname -> realm instance pairs. Realm
826instances contain an instantiated store and credential object as the 'store'
827and 'credential' elements, respectively
06675d2e 828
e8919861 829=item get_auth_realm ( $realmname )
06675d2e 830
e8919861 831Retrieves the realm instance for the realmname provided.
06675d2e 832
833=item
834
835=back
836
fbe577ac 837=head1 SEE ALSO
838
649de93b 839This list might not be up to date. Below are modules known to work with the updated
840API of 0.10 and are therefore compatible with realms.
4bb9b01c 841
842=head2 User Storage Backends
843
fbe577ac 844L<Catalyst::Plugin::Authentication::Store::Minimal>,
30e90c6f 845L<Catalyst::Plugin::Authentication::Store::DBIx::Class>,
4bb9b01c 846
847=head2 Credential verification
848
849L<Catalyst::Plugin::Authentication::Credential::Password>,
4bb9b01c 850
851=head2 Authorization
852
fbe577ac 853L<Catalyst::Plugin::Authorization::ACL>,
4bb9b01c 854L<Catalyst::Plugin::Authorization::Roles>
855
5e91c057 856=head2 Internals Documentation
857
649de93b 858L<Catalyst::Plugin::Authentication::Internals>
5e91c057 859
4bb9b01c 860=head2 Misc
861
862L<Catalyst::Plugin::Session>,
863L<Catalyst::Plugin::Session::PerUser>
fbe577ac 864
93f08fb0 865=head1 DON'T SEE ALSO
866
1a05e6ed 867This module along with its sub plugins deprecate a great number of other
868modules. These include L<Catalyst::Plugin::Authentication::Simple>,
869L<Catalyst::Plugin::Authentication::CDBI>.
93f08fb0 870
871At the time of writing these plugins have not yet been replaced or updated, but
1a05e6ed 872should be eventually: L<Catalyst::Plugin::Authentication::OpenID>,
873L<Catalyst::Plugin::Authentication::LDAP>,
874L<Catalyst::Plugin::Authentication::CDBI::Basic>,
875L<Catalyst::Plugin::Authentication::Basic::Remote>.
93f08fb0 876
649de93b 877=head1 INCOMPATABILITIES
878
879The realms based configuration and functionality of the 0.10 update
880of L<Catalyst::Plugin::Authentication> required a change in the API used by
881credentials and stores. It has a compatibility mode which allows use of
882modules that have not yet been updated. This, however, completely mimics the
883older api and disables the new realm-based features. In other words you can
884not mix the older credential and store modules with realms, or realm-based
885configs. The changes required to update modules are relatively minor and are
886covered in L<Catalyst::Plugin::Authentication::Internals>. We hope that most
887modules will move to the compatible list above very quickly.
0cc778ab 888
889=head1 COMPATIBILITY ROUTINES
890
e8919861 891In version 0.10 of L<Catalyst::Plugin::Authentication>, the API
892changed. For app developers, this change is fairly minor, but for
893Credential and Store authors, the changes are significant.
894
895Please see the documentation in version 0.09 of
30e90c6f 896Catalyst::Plugin::Authentication for a better understanding of how the old API
e8919861 897functioned.
898
899The items below are still present in the plugin, though using them is
900deprecated. They remain only as a transition tool, for those sites which can
30e90c6f 901not yet be upgraded to use the new system due to local customizations or use
902of Credential / Store modules that have not yet been updated to work with the
45c7644b 903new API.
e8919861 904
905These routines should not be used in any application using realms
906functionality or any of the methods described above. These are for reference
907purposes only.
0cc778ab 908
909=over 4
910
e8919861 911=item login
912
913This method is used to initiate authentication and user retrieval. Technically
649de93b 914this is part of the old Password credential module and it still resides in the
915L<Password|Catalyst::Plugin::Authentication::Credential::Password> class. It is
916included here for reference only.
e8919861 917
0cc778ab 918=item default_auth_store
919
920Return the store whose name is 'default'.
921
922This is set to C<< $c->config->{authentication}{store} >> if that value exists,
923or by using a Store plugin:
924
30e90c6f 925 # load the Minimal authentication store.
0cc778ab 926 use Catalyst qw/Authentication Authentication::Store::Minimal/;
927
928Sets the default store to
45c7644b 929L<Catalyst::Plugin::Authentication::Store::Minimal>.
0cc778ab 930
0cc778ab 931=item get_auth_store $name
932
933Return the store whose name is $name.
934
935=item get_auth_store_name $store
936
937Return the name of the store $store.
938
939=item auth_stores
940
941A hash keyed by name, with the stores registered in the app.
942
0cc778ab 943=item register_auth_stores %stores_by_name
944
945Register stores into the application.
946
947=back
948
949
950
2bcde605 951=head1 AUTHORS
fbe577ac 952
953Yuval Kogman, C<nothingmuch@woobling.org>
2bcde605 954
649de93b 955Jay Kuri, C<jayk@cpan.org>
956
7d2f34eb 957Jess Robinson
2bcde605 958
7d2f34eb 959David Kamholz
06675d2e 960
e8919861 961
ff46c00b 962=head1 COPYRIGHT & LICENSE
fbe577ac 963
964 Copyright (c) 2005 the aforementioned authors. All rights
965 reserved. This program is free software; you can redistribute
966 it and/or modify it under the same terms as Perl itself.
967
968=cut
06675d2e 969