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