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