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