Adding a progressive realm to try multiple realms in a sequence
[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
15This Realm allows an application to be built so that multiple realms are
16supported and tried incrementally until a successful authentication.
17
18A simple use case is a Temporary Password that looks and acts exactly as a
19regular password. Without changing the authentication code, you can
20authenticate against multiple realms.
21
22=head2 EXAMPLE
23
24If your application has multiple realms to authenticate, such as a temporary
25password realm and a normal realm, you can configure the progressive realm as
26the default, and configure it to iteratively call the temporary realm and then
27the normal realm.
28
29 __PACKAGE__->config(
30 'Plugin::Authentication' => {
31 default_realm => 'progressive',
32 realms => {
33 progressive => {
34 class => 'Progressive',
35 realms => [ 'temp', 'normal' ],
36 # Modify the authinfo passed into authenticate by merging
37 # these hashes into the realm's authenticate call:
38 authinfo_munge => {
39 'local' => { 'realm' => 'normal' },
40 'temp' => { 'realm' => 'temp' },
41 }
42 },
43 'normal' => {
44 credential => {
45 class => 'Password',
46 password_field => 'secret',
47 password_type => 'hashed',
48 password_hash_type => 'SHA-1',
49 },
50 store => {
51 class => 'DBIx::Class',
52 user_class => 'Schema::Person::Identity',
53 id_field => 'id',
54 }
55 },
56 temp => {
57 credential => {
58 class => 'Password',
59 password_field => 'secret',
60 password_type => 'hashed',
61 password_hash_type => 'SHA-1',
62 },
63 store => {
64 class => 'DBIx::Class',
65 user_class => 'Schema::Person::Identity',
66 id_field => 'id',
67 }
68 },
69 }
70 }
71 );
72
73Then, in your controller code, to attempt authentication against both realms
74you just have to do a simple authenticate call:
75
76 if ( $c->authenticate({ id => $username, password => $password }) ) {
77 if ( $c->user->realm eq 'temp' ) {
78 # Force user to change password
79 }
80 }
81
82=head1 CONFIGURATION
83
84=over
85
86=item realms
87
88An array reference consisting of each realm to attempt authentication against,
89in the order listed. If the realm does not exist, calling authenticate will
90die.
91
92=item authinfo_munge
93
94A hash reference keyed by realm names, with values being hash references to
95merge into the authinfo call that is subsequently passed into the realm's
96authenticate method. This is useful if your store uses the same class for each
97realm, separated by some other token (in the L<EXAMPLE> authinfo_mungesection,
98the 'realm' is a column on C<Schema::Person::Identity> that will be either
99'temp' or 'local', to ensure the query to fetch the user finds the right
100Identity record for that realm.
101
102=back
103
104=head1 METHODS
105
106=head2 authenticate
107
108This method iteratively calls each realm listed in the C<realms> configuration
109key. It returns after the first successful authentication call is done.
110
111=cut
112
113sub authenticate {
114 my ( $self, $c, $authinfo ) = @_;
115 my $realms = $self->config->{realms};
116 carp "No realms to authenticate against, check configuration"
117 unless $realms;
118 carp "Realms configuration must be an array reference"
119 unless ref $realms eq 'ARRAY';
120 foreach my $realm_name ( @$realms ) {
121 my $realm = $c->get_auth_realm( $realm_name );
122 carp "Unable to find realm: $realm_name, check configuration"
123 unless $realm;
124 my $auth = { %$authinfo };
125 $auth->{realm} ||= $realm->name;
126 if ( my $info = $realm->config->{authinfo_munge}->{$realm->name} ) {
127 $auth = Catalyst::Utils::merge_hashes($auth, $info);
128 }
129 if ( my $obj = $realm->authenticate( $c, $auth ) ) {
130 $c->set_authenticated( $obj, $realm->name );
131 return $obj;
132 }
133 }
134 return;
135}
136
137=head1 AUTHORS
138
139J. Shirley C<< <jshirley@cpan.org> >>
140
141Jay Kuri C<< <jayk@cpan.org> >>
142
143=head1 COPYRIGHT & LICENSE
144
145Copyright (c) 2008 the aforementioned authors. All rights reserved. This program
146is free software; you can redistribute it and/or modify it under the same terms
147as Perl itself.
148
149=cut
150
1511;