r36208@cain (orig r6089): jayk | 2007-02-19 18:35:25 +0000
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Plugin / Authentication.pm
CommitLineData
07e49bc7 1#!/usr/bin/perl
2
3package Catalyst::Plugin::Authentication;
4
dde93f12 5use base qw/Class::Accessor::Fast Class::Data::Inheritable/;
07e49bc7 6
dde93f12 7BEGIN {
2e3b369c 8 __PACKAGE__->mk_accessors(qw/_user/);
dde93f12 9}
07e49bc7 10
11use strict;
12use warnings;
13
8b52f75e 14use Tie::RefHash;
f2fee7ad 15use Class::Inspector;
8b52f75e 16
7605d422 17# this optimization breaks under Template::Toolkit
18# use user_exists instead
c8c961f5 19#BEGIN {
20# require constant;
21# constant->import(have_want => eval { require Want });
22#}
816e5745 23
66b2264f 24our $VERSION = "0.09999_01";
49bd39d0 25
07e49bc7 26sub set_authenticated {
f869137d 27 my ( $c, $user, $realmname ) = @_;
07e49bc7 28
29 $c->user($user);
f0348b1d 30 $c->request->{user} = $user; # compatibility kludge
07e49bc7 31
f869137d 32 if (!$realmname) {
33 $realmname = 'default';
07e49bc7 34 }
f869137d 35
36 if ( $c->isa("Catalyst::Plugin::Session")
37 and $c->config->{authentication}{use_session}
38 and $user->supports("session") )
39 {
c9b72c5b 40 $c->save_user_in_session($user, $realmname);
f869137d 41 }
808fc188 42 $user->auth_realm($realmname);
f869137d 43
44 $c->NEXT::set_authenticated($user, $realmname);
07e49bc7 45}
46
04c8c049 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
2e3b369c 68sub user {
f0348b1d 69 my $c = shift;
2e3b369c 70
f0348b1d 71 if (@_) {
72 return $c->_user(@_);
73 }
2e3b369c 74
808fc188 75 if ( defined($c->_user) ) {
76 return $c->_user;
58009177 77 } else {
d0d5fbef 78 return $c->auth_restore_user;
f0348b1d 79 }
2e3b369c 80}
81
f869137d 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.
1a2be169 84sub user_exists {
85 my $c = shift;
30d5659d 86 return defined($c->_user) || defined($c->_user_in_session);
58009177 87}
88
808fc188 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}
f869137d 102
f2fee7ad 103sub save_user_in_session {
c9b72c5b 104 my ( $c, $user, $realmname ) = @_;
f2fee7ad 105
f869137d 106 $c->session->{__user_realm} = $realmname;
107
808fc188 108 # we want to ask the store for a user prepared for the session.
f869137d 109 # but older modules split this functionality between the user and the
808fc188 110 # store. We try the store first. If not, we use the old method.
f869137d 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 }
f2fee7ad 117}
118
07e49bc7 119sub logout {
120 my $c = shift;
121
122 $c->user(undef);
dde93f12 123
f869137d 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/};
dde93f12 130 }
49df63da 131
132 $c->NEXT::logout(@_);
07e49bc7 133}
134
f869137d 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');
5435c348 144 }
145}
146
f869137d 147
d0d5fbef 148sub _user_in_session {
149 my $c = shift;
150
04c8c049 151 return unless $c->_should_load_user_from_session;
d0d5fbef 152
153 return $c->session->{__user};
04c8c049 154}
d0d5fbef 155
2e3b369c 156sub auth_restore_user {
f869137d 157 my ( $c, $frozen_user, $realmname ) = @_;
2e3b369c 158
d0d5fbef 159 $frozen_user ||= $c->_user_in_session;
160 return unless defined($frozen_user);
b7a0db6d 161
f869137d 162 $realmname ||= $c->session->{__user_realm};
163 return unless $realmname; # FIXME die unless? This is an internal inconsistency
2e3b369c 164
f869137d 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.
808fc188 169 $user->auth_realm($realmname);
f0348b1d 170 return $user;
2e3b369c 171
172}
173
f869137d 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. :-(
07e49bc7 176sub setup {
66b2264f 177 my $app = shift;
07e49bc7 178
66b2264f 179 $app->_authentication_initialize();
180 $app->NEXT::setup(@_);
f869137d 181}
182
183## the actual initialization routine. whee.
184sub _authentication_initialize {
66b2264f 185 my $app = shift;
f869137d 186
22cf3725 187 ## let's avoid recreating / configuring everything if we have already done it, eh?
188 if ($app->can('_auth_realms')) { return };
66b2264f 189
22cf3725 190 ## make classdata where it is used.
191 $app->mk_classdata( '_auth_realms' => {});
8655b8a5 192
66b2264f 193 my $cfg = $app->config->{'authentication'} ||= {};
07e49bc7 194
66b2264f 195 $cfg->{use_session} = 1;
66b2264f 196
f869137d 197 if (exists($cfg->{'realms'})) {
f869137d 198 foreach my $realm (keys %{$cfg->{'realms'}}) {
66b2264f 199 $app->setup_auth_realm($realm, $cfg->{'realms'}{$realm});
f869137d 200 }
f869137d 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
66b2264f 203 if (exists($cfg->{'default_realm'}) && !$app->get_auth_realm('default')) {
204 $app->_set_default_auth_realm($cfg->{'default_realm'});
f869137d 205 }
206 } else {
66b2264f 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
f869137d 211 foreach my $storename (keys %{$cfg->{'stores'}}) {
212 my $realmcfg = {
213 store => $cfg->{'stores'}{$storename},
214 };
66b2264f 215 $app->setup_auth_realm($storename, $realmcfg);
f869137d 216 }
217 }
218
07e49bc7 219}
220
f869137d 221# set up realmname.
222sub setup_auth_realm {
223 my ($app, $realmname, $config) = @_;
224
225 $app->log->debug("Setting up $realmname");
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
808fc188 234 ## taken to mean C::P::A::Store::(specifiedclass)
f869137d 235 if ($storeclass !~ /^\+(.*)$/ ) {
808fc188 236 $storeclass = "Catalyst::Plugin::Authentication::Store::${storeclass}";
f869137d 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);
808fc188 273 $app->auth_realms->{$realmname}{'credential'} = $credentialclass->new($config->{'credential'}, $app);
8b52f75e 274}
275
f869137d 276sub auth_realms {
277 my $self = shift;
278 return($self->_auth_realms);
8b52f75e 279}
280
f869137d 281sub get_auth_realm {
282 my ($app, $realmname) = @_;
283 return $app->auth_realms->{$realmname};
284}
8b52f75e 285
c9b72c5b 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 {
f869137d 293 my ($app, $realmname) = @_;
294
295 if (exists($app->auth_realms->{$realmname})) {
296 $app->auth_realms->{'default'} = $app->auth_realms->{$realmname};
f2fee7ad 297 }
f869137d 298 return $app->get_auth_realm('default');
8b52f75e 299}
300
f869137d 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
808fc188 310 ## note to self - make authenticate throw an exception if realm is invalid.
311
f869137d 312 if ($realm && exists($realm->{'credential'})) {
313 my $user = $realm->{'credential'}->authenticate($app, $realm->{store}, $userinfo);
6229329e 314 if (ref($user)) {
f869137d 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 }
46c08636 322 return undef;
8b52f75e 323}
324
f869137d 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 ) = @_;
8b52f75e 339
f869137d 340 return $c->find_user( {'id' => $uid, 'rest'=>\@rest }, 'default' );
8b52f75e 341}
342
c9b72c5b 343
f869137d 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.
8b52f75e 349sub default_auth_store {
f2fee7ad 350 my $self = shift;
8b52f75e 351
f2fee7ad 352 if ( my $new = shift ) {
f869137d 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 }
f2fee7ad 367 }
8b52f75e 368
f869137d 369 return $self->get_auth_realm('default')->{'store'};
8b52f75e 370}
371
f869137d 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
07e49bc7 403__PACKAGE__;
404
405__END__
406
407=pod
408
409=head1 NAME
410
033d2c24 411Catalyst::Plugin::Authentication - Infrastructure plugin for the Catalyst
412authentication framework.
07e49bc7 413
414=head1 SYNOPSIS
415
18a3c897 416 use Catalyst qw/
417 Authentication
18a3c897 418 /;
419
420 # later on ...
66b2264f 421 $c->authenticate({ username => 'myusername',
422 password => 'mypassword' });
f869137d 423 my $age = $c->user->get('age');
18a3c897 424 $c->logout;
07e49bc7 425
426=head1 DESCRIPTION
427
5d0fd266 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
c9b72c5b 437'Realm'. A Catalyst application using the authentication framework must have
438at least one realm, and may have several.
18a3c897 439
f0f9cd72 440To implement authentication in a Catalyst application you need to add this
5d0fd266 441module, and specify at least one realm in the configuration.
18a3c897 442
14929a35 443Authentication data can also be stored in a session, if the application
444is using the L<Catalyst::Plugin::Session> module.
07e49bc7 445
d19dc3ce 446B<NOTE> in version 0.10 of this module, the interface to this module changed.
447Please see L</COMPATIBILITY ROUTINES> for more information.
c9b72c5b 448
e5108df9 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
f0f9cd72 462Then the user tells you who they are, and backs this claim with some piece of
e5108df9 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
5d0fd266 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
e5108df9 476currently logged in user is indeed an administrator before performing the
66b2264f 477actions in an administrative part of your application. These decisions may be
5d0fd266 478made within your application code using just the information available after
479authentication, or it may be facilitated by a number of plugins.
e5108df9 480
481=head2 The Components In This Framework
482
c955e5dc 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
5d0fd266 493used when no realm name is specified. More information about configuring
494realms is available in the configuration section.
c955e5dc 495
e5108df9 496=head3 Credential Verifiers
497
498When user input is transferred to the L<Catalyst> application (typically via
5d0fd266 499form inputs) the application may pass this information into the authentication
500system through the $c->authenticate() method. From there, it is passed to the
c955e5dc 501appropriate Credential verifier.
e5108df9 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
808fc188 508The authentication data also identifies a user, and the Storage backend modules
c955e5dc 509use this data to locate and return a standardized object-oriented
510representation of a user.
e5108df9 511
512When a user is retrieved from a store it is not necessarily authenticated.
c955e5dc 513Credential verifiers accept a set of authentication data and use this
514information to retrieve the user from the store they are paired with.
e5108df9 515
516=head3 The Core Plugin
517
c955e5dc 518This plugin on its own is the glue, providing realm configuration, session
e5108df9 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
caae740f 530=head1 EXAMPLE
531
5d0fd266 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.
caae740f 534
535This means that our application will begin like this:
536
537 package MyApp;
538
539 use Catalyst qw/
540 Authentication
caae740f 541 /;
542
c955e5dc 543 __PACKAGE__->config->{authentication} =
66b2264f 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 };
c955e5dc 570
caae740f 571
5d0fd266 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.
caae740f 575
c955e5dc 576To show an example of this, let's create an authentication controller:
caae740f 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 {
c955e5dc 586 if ( $c->authenticate( { username => $user,
587 password => $password } ) ) {
588 $c->res->body( "hello " . $c->user->get("name") );
caae740f 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,
66b2264f 599call the "authenticate" method from the controller. If it succeeds the
600user is logged in.
caae740f 601
c955e5dc 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
5d0fd266 605B<authenticated> and C<< $c->user >> will contain the user object retrieved
c955e5dc 606from the store.
caae740f 607
c955e5dc 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:
caae740f 612
c955e5dc 613 if ( $c->authenticate( { username => $user,
614 password => $password }, 'admin' )l ) {
615 $c->res->body( "hello " . $c->user->get("name") );
616 } ...
caae740f 617
caae740f 618
66b2264f 619Now suppose we want to restrict the ability to edit to a user with an
620'editor' value of yes.
caae740f 621
c955e5dc 622The restricted action might look like this:
caae740f 623
c955e5dc 624 sub edit : Local {
caae740f 625 my ( $self, $c ) = @_;
626
627 $c->detach("unauthorized")
628 unless $c->user_exists
66b2264f 629 and $c->user->get('editor') eq 'yes';
caae740f 630
631 # do something restricted here
632 }
633
66b2264f 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.
c955e5dc 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:
caae740f 643
644 use Catalyst qw/
645 ...
646 Authorization::Roles
647 /;
648
c955e5dc 649 sub edit : Local {
caae740f 650 my ( $self, $c ) = @_;
651
c955e5dc 652 $c->detach("unauthorized") unless $c->check_roles("edit");
caae740f 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
46c08636 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:
caae740f 664
46c08636 665 __PACKAGE__->config->{authentication} =
666 {
667 default_realm => 'members',
668 realms => {
669 members => {
670 credential => {
66b2264f 671 class => 'Password',
672 password_field => 'password',
673 password_type => 'clear'
46c08636 674 },
675 store => {
676 class => 'DBIx::Class',
677 user_class => 'MyApp::Users',
678 role_column => 'roles'
679 }
680 }
681 }
682 };
caae740f 683
46c08636 684The authentication system works behind the scenes to load your data from the
685new source. The rest of your application is completely unchanged.
caae740f 686
18a3c897 687
688=head1 CONFIGURATION
689
690=over 4
691
46c08636 692 # example
693 __PACKAGE__->config->{authentication} =
694 {
695 default_realm => 'members',
696 realms => {
697 members => {
698 credential => {
66b2264f 699 class => 'Password',
700 password_field => 'password',
701 password_type => 'clear'
46c08636 702 },
703 store => {
704 class => 'DBIx::Class',
705 user_class => 'MyApp::Users',
706 role_column => 'roles'
707 }
708 },
709 admins => {
710 credential => {
66b2264f 711 class => 'Password',
712 password_field => 'password',
713 password_type => 'clear'
46c08636 714 },
715 store => {
716 class => '+MyApp::Authentication::Store::NetAuth',
717 authserver => '192.168.10.17'
718 }
719 }
720
721 }
722 };
723
18a3c897 724=item use_session
725
726Whether or not to store the user's logged in state in the session, if the
c9b72c5b 727application is also using L<Catalyst::Plugin::Session>. This
14929a35 728value is set to true per default.
729
46c08636 730=item default_realm
b12e226d 731
46c08636 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.
5435c348 734
46c08636 735=item realms
5435c348 736
46c08636 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.
b260654c 740
46c08636 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
c9b72c5b 745classname to instantiate.
b260654c 746
46c08636 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
c9b72c5b 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:
808fc188 753Catalyst::Plugin::Authentication::Store::B<storename>.
b260654c 754
816e5745 755
b12e226d 756=back
757
c9b72c5b 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
808fc188 777in, even if it has not been yet retrieved from the storage backend. If you only
c9b72c5b 778need to know if the user is logged in, depending on the storage mechanism this
779can be much more efficient.
780
808fc188 781=item user_in_realm ( $realm )
782
783Works like user_exists, except that it only returns true if a user is both
66b2264f 784logged in right now and was retrieved from the realm provided.
808fc188 785
c9b72c5b 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
46c08636 796
07e49bc7 797=head1 INTERNAL METHODS
798
c9b72c5b 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.
07e49bc7 804
c9b72c5b 805=over 4
07e49bc7 806
c9b72c5b 807=item set_authenticated ( $user, $realmname )
07e49bc7 808
c9b72c5b 809Marks a user as authenticated. This is called from within the authenticate
810routine when a credential returns a user. $realmname defaults to 'default'
07e49bc7 811
c9b72c5b 812=item auth_restore_user ( $user, $realmname )
f0348b1d 813
c9b72c5b 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.
f0348b1d 817
c9b72c5b 818=item save_user_in_session ( $user, $realmname )
f0348b1d 819
c9b72c5b 820Used to save the user in a session. Saves $user in session, marked as
821originating in $realmname. Both arguments are required.
f0348b1d 822
c9b72c5b 823=item auth_realms
07e49bc7 824
c9b72c5b 825Returns a hashref containing realmname -> realm instance pairs. Realm
826instances contain an instantiated store and credential object as the 'store'
827and 'credential' elements, respectively
07e49bc7 828
c9b72c5b 829=item get_auth_realm ( $realmname )
07e49bc7 830
c9b72c5b 831Retrieves the realm instance for the realmname provided.
07e49bc7 832
833=item
834
835=back
836
36fba990 837=head1 SEE ALSO
838
6229329e 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.
e5108df9 841
842=head2 User Storage Backends
843
36fba990 844L<Catalyst::Plugin::Authentication::Store::Minimal>,
d19dc3ce 845L<Catalyst::Plugin::Authentication::Store::DBIx::Class>,
e5108df9 846
847=head2 Credential verification
848
849L<Catalyst::Plugin::Authentication::Credential::Password>,
e5108df9 850
851=head2 Authorization
852
36fba990 853L<Catalyst::Plugin::Authorization::ACL>,
e5108df9 854L<Catalyst::Plugin::Authorization::Roles>
855
caae740f 856=head2 Internals Documentation
857
6229329e 858L<Catalyst::Plugin::Authentication::Internals>
caae740f 859
e5108df9 860=head2 Misc
861
862L<Catalyst::Plugin::Session>,
863L<Catalyst::Plugin::Session::PerUser>
36fba990 864
d304b38a 865=head1 DON'T SEE ALSO
866
1db33018 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>.
d304b38a 870
871At the time of writing these plugins have not yet been replaced or updated, but
1db33018 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>.
d304b38a 876
6229329e 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.
46c08636 888
889=head1 COMPATIBILITY ROUTINES
890
c9b72c5b 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
d19dc3ce 896Catalyst::Plugin::Authentication for a better understanding of how the old API
c9b72c5b 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
d19dc3ce 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
808fc188 903new API.
c9b72c5b 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.
46c08636 908
909=over 4
910
c9b72c5b 911=item login
912
913This method is used to initiate authentication and user retrieval. Technically
6229329e 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.
c9b72c5b 917
46c08636 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
d19dc3ce 925 # load the Minimal authentication store.
46c08636 926 use Catalyst qw/Authentication Authentication::Store::Minimal/;
927
928Sets the default store to
808fc188 929L<Catalyst::Plugin::Authentication::Store::Minimal>.
46c08636 930
46c08636 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
46c08636 943=item register_auth_stores %stores_by_name
944
945Register stores into the application.
946
947=back
948
949
950
51111c81 951=head1 AUTHORS
36fba990 952
953Yuval Kogman, C<nothingmuch@woobling.org>
51111c81 954
6229329e 955Jay Kuri, C<jayk@cpan.org>
956
7d4c2ed8 957Jess Robinson
51111c81 958
7d4c2ed8 959David Kamholz
07e49bc7 960
c9b72c5b 961
8f86f029 962=head1 COPYRIGHT & LICENSE
36fba990 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
07e49bc7 969