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