| 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 |