Change example configs to fit in with recent DBIC store
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Authentication / Realm / Progressive.pm
CommitLineData
714e2bdc 1package Catalyst::Authentication::Realm::Progressive;
2
3use Carp;
4use warnings;
5use strict;
6
7use base 'Catalyst::Authentication::Realm';
8
9=head1 NAME
10
11Catalyst::Authentication::Realm::Progressive - Authenticate against multiple realms
12
13=head1 SYNOPSIS
14
d02b2273 15This Realm allows an application to use a single authenticate() call during
16which multiple realms are used and tried incrementally until one performs
17a successful authentication is accomplished.
714e2bdc 18
d02b2273 19A simple use case is a Temporary Password that looks and acts exactly as a
20regular password. Without changing the authentication code, you can
714e2bdc 21authenticate against multiple realms.
22
d02b2273 23Another use might be to support a legacy website authentication system, trying
24the current auth system first, and upon failure, attempting authentication against
25the legacy system.
26
714e2bdc 27=head2 EXAMPLE
28
29If your application has multiple realms to authenticate, such as a temporary
30password realm and a normal realm, you can configure the progressive realm as
31the default, and configure it to iteratively call the temporary realm and then
32the normal realm.
33
34 __PACKAGE__->config(
35 'Plugin::Authentication' => {
36 default_realm => 'progressive',
37 realms => {
38 progressive => {
39 class => 'Progressive',
40 realms => [ 'temp', 'normal' ],
41 # Modify the authinfo passed into authenticate by merging
42 # these hashes into the realm's authenticate call:
43 authinfo_munge => {
d02b2273 44 'local' => { 'type' => 'normal' },
45 'temp' => { 'type' => 'temporary' },
714e2bdc 46 }
47 },
48 'normal' => {
49 credential => {
50 class => 'Password',
51 password_field => 'secret',
52 password_type => 'hashed',
53 password_hash_type => 'SHA-1',
54 },
55 store => {
56 class => 'DBIx::Class',
055630f7 57 user_model => 'Schema::Person::Identity',
714e2bdc 58 id_field => 'id',
59 }
60 },
61 temp => {
62 credential => {
63 class => 'Password',
64 password_field => 'secret',
65 password_type => 'hashed',
66 password_hash_type => 'SHA-1',
67 },
68 store => {
69 class => 'DBIx::Class',
055630f7 70 user_model => 'Schema::Person::Identity',
714e2bdc 71 id_field => 'id',
72 }
73 },
74 }
75 }
76 );
77
78Then, in your controller code, to attempt authentication against both realms
79you just have to do a simple authenticate call:
80
81 if ( $c->authenticate({ id => $username, password => $password }) ) {
d02b2273 82 if ( $c->user->type eq 'temporary' ) {
714e2bdc 83 # Force user to change password
84 }
85 }
86
87=head1 CONFIGURATION
88
89=over
90
91=item realms
92
93An array reference consisting of each realm to attempt authentication against,
94in the order listed. If the realm does not exist, calling authenticate will
95die.
96
97=item authinfo_munge
98
99A hash reference keyed by realm names, with values being hash references to
100merge into the authinfo call that is subsequently passed into the realm's
101authenticate method. This is useful if your store uses the same class for each
102realm, separated by some other token (in the L<EXAMPLE> authinfo_mungesection,
103the 'realm' is a column on C<Schema::Person::Identity> that will be either
104'temp' or 'local', to ensure the query to fetch the user finds the right
105Identity record for that realm.
106
107=back
108
109=head1 METHODS
110
3dae968a 111=head2 new ($realmname, $config, $app)
112
113Constructs an instance of this realm.
114
714e2bdc 115=head2 authenticate
116
117This method iteratively calls each realm listed in the C<realms> configuration
118key. It returns after the first successful authentication call is done.
119
120=cut
121
122sub authenticate {
123 my ( $self, $c, $authinfo ) = @_;
124 my $realms = $self->config->{realms};
125 carp "No realms to authenticate against, check configuration"
126 unless $realms;
127 carp "Realms configuration must be an array reference"
128 unless ref $realms eq 'ARRAY';
129 foreach my $realm_name ( @$realms ) {
130 my $realm = $c->get_auth_realm( $realm_name );
131 carp "Unable to find realm: $realm_name, check configuration"
132 unless $realm;
133 my $auth = { %$authinfo };
134 $auth->{realm} ||= $realm->name;
135 if ( my $info = $realm->config->{authinfo_munge}->{$realm->name} ) {
136 $auth = Catalyst::Utils::merge_hashes($auth, $info);
137 }
138 if ( my $obj = $realm->authenticate( $c, $auth ) ) {
139 $c->set_authenticated( $obj, $realm->name );
140 return $obj;
141 }
142 }
143 return;
144}
145
6657dfec 146## we can not rely on inheriting new() because in this case we do not
147## load a credential or store, which is what new() sets up in the
148## standard realm. So we have to create our realm object, set our name
149## and return $self in order to avoid nasty warnings.
150
151sub new {
152 my ($class, $realmname, $config, $app) = @_;
153
154 my $self = { config => $config };
155 bless $self, $class;
156
157 $self->name($realmname);
158 return $self;
159}
160
714e2bdc 161=head1 AUTHORS
162
163J. Shirley C<< <jshirley@cpan.org> >>
164
165Jay Kuri C<< <jayk@cpan.org> >>
166
167=head1 COPYRIGHT & LICENSE
168
169Copyright (c) 2008 the aforementioned authors. All rights reserved. This program
170is free software; you can redistribute it and/or modify it under the same terms
171as Perl itself.
172
173=cut
174
1751;