line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::Authentication::Realm::Adaptor; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
326010
|
use warnings; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
69
|
|
4
|
2
|
|
|
2
|
|
11
|
use strict; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
66
|
|
5
|
2
|
|
|
2
|
|
12
|
use Carp; |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
165
|
|
6
|
2
|
|
|
2
|
|
1660
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
extends 'Catalyst::Authentication::Realm'; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 NAME |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
Catalyst::Authentication::Realm::Adaptor - Adjust parameters of authentication processes on the fly |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 VERSION |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
Version 0.02 |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=cut |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
## goes in catagits@jules.scsys.co.uk:Catalyst-Authentication-Realm-Adaptor.git |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub authenticate { |
24
|
|
|
|
|
|
|
my ( $self, $c, $authinfo ) = @_; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $newauthinfo; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
if (exists($self->config->{'credential_adaptor'})) { |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
if ($self->config->{'credential_adaptor'}{'method'} eq 'merge_hash') { |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
$newauthinfo = _munge_hash($authinfo, $self->config->{'credential_adaptor'}{'merge_hash'}, $authinfo); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
} elsif ($self->config->{'credential_adaptor'}{'method'} eq 'new_hash') { |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
$newauthinfo = _munge_hash({}, $self->config->{'credential_adaptor'}{'new_hash'}, $authinfo); |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
} elsif ($self->config->{'credential_adaptor'}{'method'} eq 'action') { |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
my $controller = $c->controller($self->config->{'credential_adaptor'}{'controller'}); |
41
|
|
|
|
|
|
|
if (!$controller) { |
42
|
|
|
|
|
|
|
Catalyst::Exception->throw(__PACKAGE__ . " realm: " . $self->name . "'s credential_adaptor tried to use a controller that doesn't exist: " . |
43
|
|
|
|
|
|
|
$self->config->{'credential_adaptor'}{'controller'}); |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
my $action = $controller->action_for($self->config->{'credential_adaptor'}{'action'}); |
47
|
|
|
|
|
|
|
if (!$action) { |
48
|
|
|
|
|
|
|
Catalyst::Exception->throw(__PACKAGE__ . " realm: " . $self->name . "'s credential_adaptor tried to use an action that doesn't exist: " . |
49
|
|
|
|
|
|
|
$self->config->{'credential_adaptor'}{'controller'} . "->" . |
50
|
|
|
|
|
|
|
$self->config->{'credential_adaptor'}{'action'}); |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
$newauthinfo = $c->forward($action, $self->name, $authinfo, $self->config->{'credential_adaptor'}); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
} elsif ($self->config->{'credential_adaptor'}{'method'} eq 'code' ) { |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
if (ref($self->config->{'credential_adaptor'}{'code'}) eq 'CODE') { |
57
|
|
|
|
|
|
|
my $sub = $self->config->{'credential_adaptor'}{'code'}; |
58
|
|
|
|
|
|
|
$newauthinfo = $sub->($self->name, $authinfo, $self->config->{'credential_adaptor'}); |
59
|
|
|
|
|
|
|
} else { |
60
|
|
|
|
|
|
|
Catalyst::Exception->throw(__PACKAGE__ . " realm: " . $self->name . "'s credential_adaptor is configured to use a code ref that doesn't exist"); |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
return $self->SUPER::authenticate($c, $newauthinfo); |
64
|
|
|
|
|
|
|
} else { |
65
|
|
|
|
|
|
|
return $self->SUPER::authenticate($c, $authinfo); |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub find_user { |
70
|
|
|
|
|
|
|
my ( $self, $authinfo, $c ) = @_; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
my $newauthinfo; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
if (exists($self->config->{'store_adaptor'})) { |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
if ($self->config->{'store_adaptor'}{'method'} eq 'merge_hash') { |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
$newauthinfo = _munge_hash($authinfo, $self->config->{'store_adaptor'}{'merge_hash'}, $authinfo); |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
} elsif ($self->config->{'store_adaptor'}{'method'} eq 'new_hash') { |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
$newauthinfo = _munge_hash({}, $self->config->{'store_adaptor'}{'new_hash'}, $authinfo); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
} elsif ($self->config->{'store_adaptor'}{'method'} eq 'action') { |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my $controller = $c->controller($self->config->{'store_adaptor'}{'controller'}); |
87
|
|
|
|
|
|
|
if (!$controller) { |
88
|
|
|
|
|
|
|
Catalyst::Exception->throw(__PACKAGE__ . " realm: " . $self->name . "'s store_adaptor tried to use a controller that doesn't exist: " . |
89
|
|
|
|
|
|
|
$self->config->{'store_adaptor'}{'controller'}); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
my $action = $controller->action_for($self->config->{'store_adaptor'}{'action'}); |
93
|
|
|
|
|
|
|
if (!$action) { |
94
|
|
|
|
|
|
|
Catalyst::Exception->throw(__PACKAGE__ . " realm: " . $self->name . "'s store_adaptor tried to use an action that doesn't exist: " . |
95
|
|
|
|
|
|
|
$self->config->{'store_adaptor'}{'controller'} . "->" . |
96
|
|
|
|
|
|
|
$self->config->{'store_adaptor'}{'action'}); |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
$newauthinfo = $c->forward($action, $self->name, $authinfo, $self->config->{'store_adaptor'}); |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
} elsif ($self->config->{'store_adaptor'}{'method'} eq 'code' ) { |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
if (ref($self->config->{'store_adaptor'}{'code'}) eq 'CODE') { |
103
|
|
|
|
|
|
|
my $sub = $self->config->{'store_adaptor'}{'code'}; |
104
|
|
|
|
|
|
|
$newauthinfo = $sub->($self->name, $authinfo, $self->config->{'store_adaptor'}); |
105
|
|
|
|
|
|
|
} else { |
106
|
|
|
|
|
|
|
Catalyst::Exception->throw(__PACKAGE__ . " realm: " . $self->name . "'s store_adaptor is configured to use a code ref that doesn't exist"); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
return $self->SUPER::find_user($newauthinfo, $c); |
110
|
|
|
|
|
|
|
} else { |
111
|
|
|
|
|
|
|
return $self->SUPER::find_user($authinfo, $c); |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub _munge_hash { |
116
|
|
|
|
|
|
|
my ($sourcehash, $modhash, $referencehash) = @_; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
my $resulthash = { %{$sourcehash} }; |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
foreach my $key (keys %{$modhash}) { |
121
|
|
|
|
|
|
|
if (ref($modhash->{$key}) eq 'HASH') { |
122
|
|
|
|
|
|
|
if (ref($sourcehash->{$key}) eq 'HASH') { |
123
|
|
|
|
|
|
|
$resulthash->{$key} = _munge_hash($sourcehash->{$key}, $modhash->{$key}, $referencehash) |
124
|
|
|
|
|
|
|
} else { |
125
|
|
|
|
|
|
|
$resulthash->{$key} = _munge_hash({}, $modhash->{$key}, $referencehash); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
} else { |
128
|
|
|
|
|
|
|
if (ref($modhash->{$key} eq 'ARRAY') && ref($sourcehash->{$key}) eq 'ARRAY') { |
129
|
|
|
|
|
|
|
push @{$resulthash->{$key}}, _munge_value($modhash->{$key}, $referencehash) |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
$resulthash->{$key} = _munge_value($modhash->{$key}, $referencehash); |
132
|
|
|
|
|
|
|
if (ref($resulthash->{$key}) eq 'SCALAR' && ${$resulthash->{$key}} eq '-') { |
133
|
|
|
|
|
|
|
## Scalar reference to a string '-' means delete the element from the source array. |
134
|
|
|
|
|
|
|
delete($resulthash->{$key}); |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
return($resulthash); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub _munge_value { |
142
|
|
|
|
|
|
|
my ($modvalue, $referencehash) = @_; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
my $newvalue; |
145
|
|
|
|
|
|
|
if ($modvalue =~ m/^([+-])\((.*)\)$/) { |
146
|
|
|
|
|
|
|
my $action = $1; |
147
|
|
|
|
|
|
|
my $keypath = $2; |
148
|
|
|
|
|
|
|
## do magic |
149
|
|
|
|
|
|
|
if ($action eq '+') { |
150
|
|
|
|
|
|
|
## action = string '-' means delete the element from the source array. |
151
|
|
|
|
|
|
|
## otherwise it means copy it from a field in the original hash with nesting |
152
|
|
|
|
|
|
|
## indicated via '.' - IE similar to Template Toolkit handling of nested hashes |
153
|
|
|
|
|
|
|
my @hashpath = split /\./, $keypath; |
154
|
|
|
|
|
|
|
my $val = $referencehash; |
155
|
|
|
|
|
|
|
foreach my $subkey (@hashpath) { |
156
|
|
|
|
|
|
|
if (ref($val) eq 'HASH') { |
157
|
|
|
|
|
|
|
$val = $val->{$subkey}; |
158
|
|
|
|
|
|
|
} elsif (ref($val) eq 'ARRAY') { |
159
|
|
|
|
|
|
|
$val = $val->[$subkey]; |
160
|
|
|
|
|
|
|
} else { |
161
|
|
|
|
|
|
|
## failed to find that key in the hash / array |
162
|
|
|
|
|
|
|
$val = undef; |
163
|
|
|
|
|
|
|
last; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
$newvalue = $val; |
167
|
|
|
|
|
|
|
} else { |
168
|
|
|
|
|
|
|
## delete the value... so we return a scalar ref to '-' |
169
|
|
|
|
|
|
|
$newvalue = \'-'; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
} elsif (ref($modvalue) eq 'ARRAY') { |
172
|
|
|
|
|
|
|
$newvalue = []; |
173
|
|
|
|
|
|
|
foreach my $row (0..$#{$modvalue}) { |
174
|
|
|
|
|
|
|
if (defined($modvalue->[$row])) { |
175
|
|
|
|
|
|
|
my $val = _munge_value($modvalue->[$row], $referencehash); |
176
|
|
|
|
|
|
|
## this is the first time I've ever wanted to use unless |
177
|
|
|
|
|
|
|
## to make things clearer |
178
|
|
|
|
|
|
|
unless (ref($val) eq 'SCALAR' && ${$val} eq '-') { |
179
|
|
|
|
|
|
|
$newvalue->[$row] = $val; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
} else { |
184
|
|
|
|
|
|
|
$newvalue = $modvalue; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
return $newvalue; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 SYNOPSIS |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
The Catalyst::Authentication::Realm::Adaptor allows for modification of |
193
|
|
|
|
|
|
|
authentication parameters within the catalyst application. It's basically a |
194
|
|
|
|
|
|
|
filter used to adjust authentication parameters globally within the |
195
|
|
|
|
|
|
|
application or to adjust user retrieval parameters provided by the credential |
196
|
|
|
|
|
|
|
in order to be compatible with a different store. It provides for better |
197
|
|
|
|
|
|
|
control over interaction between credentials and stores. This is particularly |
198
|
|
|
|
|
|
|
useful when working with external authentication such as OpenID or OAuth. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
__PACKAGE__->config( |
201
|
|
|
|
|
|
|
'Plugin::Authentication' => { |
202
|
|
|
|
|
|
|
'default' => { |
203
|
|
|
|
|
|
|
class => 'Adaptor' |
204
|
|
|
|
|
|
|
credential => { |
205
|
|
|
|
|
|
|
class => 'Password', |
206
|
|
|
|
|
|
|
password_field => 'secret', |
207
|
|
|
|
|
|
|
password_type => 'hashed', |
208
|
|
|
|
|
|
|
password_hash_type => 'SHA-1', |
209
|
|
|
|
|
|
|
}, |
210
|
|
|
|
|
|
|
store => { |
211
|
|
|
|
|
|
|
class => 'DBIx::Class', |
212
|
|
|
|
|
|
|
user_class => 'Schema::Person', |
213
|
|
|
|
|
|
|
}, |
214
|
|
|
|
|
|
|
store_adaptor => { |
215
|
|
|
|
|
|
|
method => 'merge_hash', |
216
|
|
|
|
|
|
|
merge_hash => { |
217
|
|
|
|
|
|
|
status => [ 'temporary', 'active' ] |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
}, |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
); |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
The above example ensures that no matter how $c->authenticate() is called |
227
|
|
|
|
|
|
|
within your application, the key 'status' is added to the authentication hash. |
228
|
|
|
|
|
|
|
This allows you to, among other things, set parameters that should always be |
229
|
|
|
|
|
|
|
applied to your authentication process or modify the parameters to better |
230
|
|
|
|
|
|
|
connect a credential and a store that were not built to work together. In the |
231
|
|
|
|
|
|
|
above example, we are making sure that the user search is restricted to those |
232
|
|
|
|
|
|
|
with a status of either 'temporary' or 'active.' |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
This realm works by intercepting the original authentication information |
235
|
|
|
|
|
|
|
between the time C<< $c->authenticate($authinfo) >> is called and the time the |
236
|
|
|
|
|
|
|
realm's C<< $realm->authenticate($c,$authinfo) >> method is called, allowing for |
237
|
|
|
|
|
|
|
the $authinfo parameter to be modified or replaced as your application |
238
|
|
|
|
|
|
|
requires. It can also operate after the call to the credential's |
239
|
|
|
|
|
|
|
C<authenticate()> method but before the call to the store's C<find_user> |
240
|
|
|
|
|
|
|
method. |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
If you don't know what the above means, you probably do not need this module. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=head1 CONFIGURATION |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
The configuration for this module goes within your realm configuration alongside your |
247
|
|
|
|
|
|
|
credential and store options. |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
This module can operate in two points during authentication processing. |
250
|
|
|
|
|
|
|
The first is prior the realm's C<authenticate> call (immediately after the call to |
251
|
|
|
|
|
|
|
C<< $c->authenticate() >>.) To operate here, your filter options should go in a hash |
252
|
|
|
|
|
|
|
under the key C<credential_adaptor>. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
The second point is after the call to credential's C<authenticate> method but |
255
|
|
|
|
|
|
|
immediately before the call to the user store's C<find_user> method. To operate |
256
|
|
|
|
|
|
|
prior to C<find_user>, your filter options should go in a hash under the key |
257
|
|
|
|
|
|
|
C<store_adaptor>. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
The filtering options for both points are the same, and both the C<store_adaptor> and |
260
|
|
|
|
|
|
|
C<credential_adaptor> can be used simultaneously in a single realm. |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=head2 method |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
There are four ways to configure your filters. You specify which one you want by setting |
265
|
|
|
|
|
|
|
the C<method> configuration option to one of the following: C<merge_hash>, C<new_hash>, |
266
|
|
|
|
|
|
|
C<code>, or C<action>. You then provide the additional information based on which method |
267
|
|
|
|
|
|
|
you have chosen. The different options are described below. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=over 8 |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item merge_hash |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
credential_adaptor => { |
274
|
|
|
|
|
|
|
method => 'merge_hash', |
275
|
|
|
|
|
|
|
merge_hash => { |
276
|
|
|
|
|
|
|
status => [ 'temporary', 'active' ] |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
This causes the original authinfo hash to be merged with a hash provided by |
281
|
|
|
|
|
|
|
the realm configuration under the key C<merge_hash> key. This is a deep merge |
282
|
|
|
|
|
|
|
and in the case of a conflict, the hash specified by merge_hash takes |
283
|
|
|
|
|
|
|
precedence over what was passed into the authenticate or find_user call. The |
284
|
|
|
|
|
|
|
method of merging is described in detail in the L<HASH MERGING> section below. |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=item new_hash |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
store_adaptor => { |
289
|
|
|
|
|
|
|
method => 'new_hash', |
290
|
|
|
|
|
|
|
new_hash => { |
291
|
|
|
|
|
|
|
username => '+(user)', # this sets username to the value of $originalhash{user} |
292
|
|
|
|
|
|
|
user_source => 'openid' |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
This causes the original authinfo hash to be set aside and replaced with a new hash provided under the |
297
|
|
|
|
|
|
|
C<new_hash> key. The new hash can grab portions of the original hash. This can be used to remap the authinfo |
298
|
|
|
|
|
|
|
into a new format. See the L<HASH MERGING> section for information on how to do this. |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=item code |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
store_adaptor => { |
303
|
|
|
|
|
|
|
method => 'code', |
304
|
|
|
|
|
|
|
code => sub { |
305
|
|
|
|
|
|
|
my ($realmname, $original_authinfo, $hashref_to_config ) = @_; |
306
|
|
|
|
|
|
|
my $newauthinfo = {}; |
307
|
|
|
|
|
|
|
## do something |
308
|
|
|
|
|
|
|
return $newauthinfo; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
The C<code> method allows for more complex filtering by executing code |
313
|
|
|
|
|
|
|
provided as a subroutine reference in the C<code> key. The realm name, |
314
|
|
|
|
|
|
|
original auth info and the portion of the config specific to this filter are |
315
|
|
|
|
|
|
|
passed as arguments to the provided subroutine. In the above example, it would |
316
|
|
|
|
|
|
|
be the entire store_adaptor hash. If you were using a code ref in a |
317
|
|
|
|
|
|
|
credential_adaptor, you'd get the credential_adapter config instead. |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item action |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
credential_adaptor => { |
322
|
|
|
|
|
|
|
method => 'action', |
323
|
|
|
|
|
|
|
controller => 'UserProcessing', |
324
|
|
|
|
|
|
|
action => 'FilterCredentials' |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
The C<action> method causes the adaptor to delegate filtering to a Catalyst |
328
|
|
|
|
|
|
|
action. This is similar to the code ref above, except that instead of simply |
329
|
|
|
|
|
|
|
calling the routine, the action specified is called via C<<$c->forward>>. The |
330
|
|
|
|
|
|
|
arguments passed to the action are the same as the code method as well, |
331
|
|
|
|
|
|
|
namely the realm name, the original authinfo hash and the config for the adaptor. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=back |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=head1 HASH MERGING |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
The hash merging mechanism in Catalyst::Authentication::Realm::Adaptor is not |
338
|
|
|
|
|
|
|
a simple merge of two hashes. It has some niceties which allow for both |
339
|
|
|
|
|
|
|
re-mapping of existing keys, and a mechanism for removing keys from the |
340
|
|
|
|
|
|
|
original hash. When using the 'merge_hash' method above, the keys from the |
341
|
|
|
|
|
|
|
original hash and the keys for the merge hash are simply combined with the |
342
|
|
|
|
|
|
|
merge_hash taking precedence in the case of a key conflict. If there are |
343
|
|
|
|
|
|
|
sub-hashes they are merged as well. |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
If both the source and merge hash contain an array for a given hash-key, the |
346
|
|
|
|
|
|
|
values in the merge array are appended to the original array. Note that hashes |
347
|
|
|
|
|
|
|
within arrays will not be merged, and will instead simply be copied. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
Simple values are left intact, and in the case of a key existing in both |
350
|
|
|
|
|
|
|
hashes, the value from the merge_hash takes precedence. Note that in the case |
351
|
|
|
|
|
|
|
of a key conflict where the values are of different types, the value from the |
352
|
|
|
|
|
|
|
merge_hash will be used and no attempt is made to merge or otherwise convert |
353
|
|
|
|
|
|
|
them. |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=head2 Advanced merging |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Whether you are using C<merge_hash> or C<new_hash> as the method, you have access |
358
|
|
|
|
|
|
|
to the values from the original authinfo hash. In your new or merged hash, you |
359
|
|
|
|
|
|
|
can use values from anywhere within the original hash. You do this by setting |
360
|
|
|
|
|
|
|
the value for the key you want to set to a special string indicating the key |
361
|
|
|
|
|
|
|
path in the original hash. The string is formatted as follows: |
362
|
|
|
|
|
|
|
C<<'+(key1.key2.key3)'>> This will grab the hash associated with key1, retrieve the hash |
363
|
|
|
|
|
|
|
associated with key2, and finally obtain the value associated with key3. This is easier to |
364
|
|
|
|
|
|
|
show than to explain: |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
my $originalhash = { |
367
|
|
|
|
|
|
|
user => { |
368
|
|
|
|
|
|
|
details => { |
369
|
|
|
|
|
|
|
age => 27, |
370
|
|
|
|
|
|
|
haircolor => 'black', |
371
|
|
|
|
|
|
|
favoritenumbers => [ 17, 42, 19 ] |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
}; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
my $newhash = { |
377
|
|
|
|
|
|
|
# would result in a value of 'black' |
378
|
|
|
|
|
|
|
haircolor => '+(user.details.haircolor)', |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# bestnumber would be 42. |
381
|
|
|
|
|
|
|
bestnumber => '+(user.details.favoritenumbers.1)' |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Given the example above, the value for the userage key would be 27, (obtained |
385
|
|
|
|
|
|
|
via C<<'+(user.details.age)'>>) and the value for bestnumber would be 42. Note |
386
|
|
|
|
|
|
|
that you can traverse both hashes and arrays using this method. This can be |
387
|
|
|
|
|
|
|
quite useful when you need the values that were passed in, but you need to put |
388
|
|
|
|
|
|
|
them under different keys. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
When using the C<merge_hash> method, you sometimes may want to remove an item |
391
|
|
|
|
|
|
|
from the original hash. You can do this by providing a key in your merge_hash |
392
|
|
|
|
|
|
|
at the same point, but setting it's value to '-()'. This will remove the key |
393
|
|
|
|
|
|
|
entirely from the resultant hash. This works better than simply setting the |
394
|
|
|
|
|
|
|
value to undef in some cases. |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=head1 NOTES and CAVEATS |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
The authentication system for Catalyst is quite flexible. In most cases this |
399
|
|
|
|
|
|
|
module is not needed. Evidence of this fact is that the Catalyst auth system |
400
|
|
|
|
|
|
|
was substantially unchanged for 2+ years prior to this modules first release. |
401
|
|
|
|
|
|
|
If you are looking at this module, then there is a good chance your problem would |
402
|
|
|
|
|
|
|
be better solved by adjusting your credential or store directly. |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
That said, there are some areas where this module can be particularly useful. |
405
|
|
|
|
|
|
|
For example, this module allows for global application of additional arguments |
406
|
|
|
|
|
|
|
to authinfo for a certain realm via your config. It also allows for preliminary |
407
|
|
|
|
|
|
|
testing of alternate configs before you adjust every C<< $c->authenticate() >> call |
408
|
|
|
|
|
|
|
within your application. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
It is also useful when combined with the various external authentication |
411
|
|
|
|
|
|
|
modules available, such as OpenID, OAuth or Facebook. These modules expect to |
412
|
|
|
|
|
|
|
store their user information in the Hash provided by the Minimal user store. |
413
|
|
|
|
|
|
|
Often, however, you want to store user information locally in a database or |
414
|
|
|
|
|
|
|
other storage mechanism. Doing this lies somewhere between difficult and |
415
|
|
|
|
|
|
|
impossible normally. With the Adapter realm, you can massage the authinfo hash |
416
|
|
|
|
|
|
|
between the credential's verification and the creation of the local user, and |
417
|
|
|
|
|
|
|
instead use the information returned to look up a user instead. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
Using the external auth mechanisms and the C<action> method, you can actually |
420
|
|
|
|
|
|
|
trigger an action to create a user record on the fly when the user has |
421
|
|
|
|
|
|
|
authenticated via an external method. These are just some of the possibilities |
422
|
|
|
|
|
|
|
that Adaptor provides that would otherwise be very difficult to accomplish, |
423
|
|
|
|
|
|
|
even with Catalyst's flexible authentication system. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
With all of that said, caution is warranted when using this module. It modifies |
426
|
|
|
|
|
|
|
the behavior of the application in ways that are not obvious and can therefore |
427
|
|
|
|
|
|
|
lead to extremely hard to track-down bugs. This is especially true when using |
428
|
|
|
|
|
|
|
the C<action> filter method. When a developer calls C<< $c->authenticate() >> |
429
|
|
|
|
|
|
|
they are not expecting any actions to be called before it returns. |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
If you use the C<action> method, I strongly recommend that you use it only as a |
432
|
|
|
|
|
|
|
filter routine and do not do other catalyst dispatch related activities (such as |
433
|
|
|
|
|
|
|
further forwards, detach's or redirects). Also note that it is B<EXTREMELY |
434
|
|
|
|
|
|
|
DANGEROUS> to call authentication routines from within a filter action. It is |
435
|
|
|
|
|
|
|
extremely easy to accidentally create an infinite recursion bug which can crash |
436
|
|
|
|
|
|
|
your Application. In short - B<DON'T DO IT>. |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=head1 AUTHOR |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
Jay Kuri, C<< <jayk at cpan.org> >> |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=head1 BUGS |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
Please report any bugs or feature requests to C<bug-catalyst-authentication-realm-adaptor at rt.cpan.org>, or through |
445
|
|
|
|
|
|
|
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-Authentication-Realm-Adaptor>. I will be notified, and then you'll |
446
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=head1 SUPPORT |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
perldoc Catalyst::Authentication::Realm::Adaptor |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
You can also look for information at: |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=over 4 |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=item * Search CPAN |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
L<http://search.cpan.org/dist/Catalyst-Authentication-Realm-Adaptor/> |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=item * Catalyzed.org Wiki |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
L<http://wiki.catalyzed.org/cpan-modules/Catalyst-Authentication-Realm-Adaptor> |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=back |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
Copyright 2009 Jay Kuri, all rights reserved. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
478
|
|
|
|
|
|
|
under the same terms as Perl itself. |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=cut |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
1; # End of Catalyst::Authentication::Realm::Adaptor |