Commit | Line | Data |
714e2bdc |
1 | package Catalyst::Authentication::Realm::Progressive; |
2 | |
3 | use Carp; |
4 | use warnings; |
5 | use strict; |
6 | |
7 | use base 'Catalyst::Authentication::Realm'; |
8 | |
9 | =head1 NAME |
10 | |
11 | Catalyst::Authentication::Realm::Progressive - Authenticate against multiple realms |
12 | |
13 | =head1 SYNOPSIS |
14 | |
15 | This Realm allows an application to be built so that multiple realms are |
16 | supported and tried incrementally until a successful authentication. |
17 | |
18 | A simple use case is a Temporary Password that looks and acts exactly as a |
19 | regular password. Without changing the authentication code, you can |
20 | authenticate against multiple realms. |
21 | |
22 | =head2 EXAMPLE |
23 | |
24 | If your application has multiple realms to authenticate, such as a temporary |
25 | password realm and a normal realm, you can configure the progressive realm as |
26 | the default, and configure it to iteratively call the temporary realm and then |
27 | the 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 | |
73 | Then, in your controller code, to attempt authentication against both realms |
74 | you 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 | |
88 | An array reference consisting of each realm to attempt authentication against, |
89 | in the order listed. If the realm does not exist, calling authenticate will |
90 | die. |
91 | |
92 | =item authinfo_munge |
93 | |
94 | A hash reference keyed by realm names, with values being hash references to |
95 | merge into the authinfo call that is subsequently passed into the realm's |
96 | authenticate method. This is useful if your store uses the same class for each |
97 | realm, separated by some other token (in the L<EXAMPLE> authinfo_mungesection, |
98 | the '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 |
100 | Identity record for that realm. |
101 | |
102 | =back |
103 | |
104 | =head1 METHODS |
105 | |
106 | =head2 authenticate |
107 | |
108 | This method iteratively calls each realm listed in the C<realms> configuration |
109 | key. It returns after the first successful authentication call is done. |
110 | |
111 | =cut |
112 | |
113 | sub 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 | |
139 | J. Shirley C<< <jshirley@cpan.org> >> |
140 | |
141 | Jay Kuri C<< <jayk@cpan.org> >> |
142 | |
143 | =head1 COPYRIGHT & LICENSE |
144 | |
145 | Copyright (c) 2008 the aforementioned authors. All rights reserved. This program |
146 | is free software; you can redistribute it and/or modify it under the same terms |
147 | as Perl itself. |
148 | |
149 | =cut |
150 | |
151 | 1; |