line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::Authentication::Credential::RemoteHTTP; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# ABSTRACT: Authenticate against remote HTTP server |
4
|
|
|
|
|
|
|
|
5
|
1
|
|
|
1
|
|
30849
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
41
|
|
6
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
31
|
|
7
|
1
|
|
|
1
|
|
447
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
use MooseX::Types::Moose qw/Object/; |
9
|
|
|
|
|
|
|
use 5.008005; |
10
|
|
|
|
|
|
|
use Catalyst::Exception (); |
11
|
|
|
|
|
|
|
use Catalyst::Authentication::Credential::RemoteHTTP::UserAgent; |
12
|
|
|
|
|
|
|
use namespace::autoclean; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.05'; # VERSION |
15
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:NIGELM'; # AUTHORITY |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
has realm => ( isa => Object, is => 'ro', required => 1 ); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
has [qw/http_keep_alive defer_find_user/] => ( is => 'ro', default => 0 ); |
20
|
|
|
|
|
|
|
has username_field => ( is => 'ro', default => 'username' ); |
21
|
|
|
|
|
|
|
has password_field => ( is => 'ro', default => 'password' ); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
has url => ( is => 'ro', required => 1 ); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
has [qw/ user_prefix user_suffix /] => ( is => 'ro', default => '' ); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub BUILDARGS { |
28
|
|
|
|
|
|
|
my ( $class, $config, $app, $realm ) = @_; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$config->{realm} = $realm; |
31
|
|
|
|
|
|
|
$config->{app} = $app; |
32
|
|
|
|
|
|
|
$config->{class} = $class; |
33
|
|
|
|
|
|
|
return $config; |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub authenticate { |
37
|
|
|
|
|
|
|
my ( $self, $c, $realm, $authinfo ) = @_; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my $username = $authinfo->{ $self->username_field }; |
40
|
|
|
|
|
|
|
unless ( defined($username) ) { |
41
|
|
|
|
|
|
|
$c->log->debug("No username supplied") |
42
|
|
|
|
|
|
|
if $c->debug; |
43
|
|
|
|
|
|
|
return; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
## we remove the password_field before we pass it to the user |
46
|
|
|
|
|
|
|
## routine, as some store modules use all data passed to them |
47
|
|
|
|
|
|
|
## to find a matching user... |
48
|
|
|
|
|
|
|
my $userfindauthinfo = { %{$authinfo} }; |
49
|
|
|
|
|
|
|
delete( $userfindauthinfo->{ $self->password_field } ); |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
my $user_obj; |
52
|
|
|
|
|
|
|
$user_obj = $realm->find_user( $userfindauthinfo, $c ) |
53
|
|
|
|
|
|
|
unless ( $self->defer_find_user ); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
if ( ref($user_obj) || $self->defer_find_user ) { |
56
|
|
|
|
|
|
|
my $ua = |
57
|
|
|
|
|
|
|
Catalyst::Authentication::Credential::RemoteHTTP::UserAgent->new( |
58
|
|
|
|
|
|
|
keep_alive => $self->http_keep_alive ? 1 : 0 ); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# add prefix/suffix to user data to make auth_user, get password |
61
|
|
|
|
|
|
|
my $auth_user = sprintf( '%s%s%s', $self->user_prefix, $username, $self->user_suffix ); |
62
|
|
|
|
|
|
|
my $password = $authinfo->{ $self->password_field }; |
63
|
|
|
|
|
|
|
$ua->set_credentials( $auth_user, $password ); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# do the request |
66
|
|
|
|
|
|
|
my $res = $ua->head( $self->url ); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# did it succeed |
69
|
|
|
|
|
|
|
if ( $res->is_success ) { |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# TODO: should we check here that it was actually authenticated? |
72
|
|
|
|
|
|
|
# this could be done by ensuring there is a request chain... |
73
|
|
|
|
|
|
|
$c->log->debug( "remote http auth succeeded for user " . $auth_user ) |
74
|
|
|
|
|
|
|
if $c->debug; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
else { |
77
|
|
|
|
|
|
|
$c->log->debug( "remote http auth FAILED for user " . $auth_user ) |
78
|
|
|
|
|
|
|
if $c->debug; |
79
|
|
|
|
|
|
|
return; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# get the user object now, if deferred before |
84
|
|
|
|
|
|
|
$user_obj = $realm->find_user( $userfindauthinfo, $c ) |
85
|
|
|
|
|
|
|
if ( $self->defer_find_user ); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# deal with no-such-user in store |
88
|
|
|
|
|
|
|
unless ( ref($user_obj) ) { |
89
|
|
|
|
|
|
|
$c->log->debug("Unable to locate user matching user info provided") |
90
|
|
|
|
|
|
|
if $c->debug; |
91
|
|
|
|
|
|
|
return; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
return $user_obj; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
1; # End of Catalyst::Authentication::Credential::RemoteHTTP |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
__END__ |
100
|
|
|
|
|
|
|
=pod |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=for stopwords ACKNOWLEDGEMENTS Daisuke Fixups LDAP Murase NTLM classname http ie linux url validator |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head1 NAME |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
Catalyst::Authentication::Credential::RemoteHTTP - Authenticate against remote HTTP server |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head1 VERSION |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
version 0.05 |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head1 SYNOPSIS |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
package MyApp::Controller::Auth; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
use Catalyst qw/ |
117
|
|
|
|
|
|
|
Authentication |
118
|
|
|
|
|
|
|
/; |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub login : Local { |
121
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
$c->authenticate( { username => $c->req->param('username'), |
124
|
|
|
|
|
|
|
password => $c->req->param('password') }); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=head1 DESCRIPTION |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
This authentication credential checker takes authentication |
130
|
|
|
|
|
|
|
information (most often a username) and a password, and attempts to |
131
|
|
|
|
|
|
|
validate the username and password provided against a remote http |
132
|
|
|
|
|
|
|
server - ie against another web server. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
This is useful for environments where you want to have a single |
135
|
|
|
|
|
|
|
source of authentication information, but are not able to |
136
|
|
|
|
|
|
|
conveniently use a networked authentication mechanism such as LDAP. |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=head1 CONFIGURATION |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# example |
141
|
|
|
|
|
|
|
__PACKAGE__->config( |
142
|
|
|
|
|
|
|
'Plugin::Authentication' => { |
143
|
|
|
|
|
|
|
default_realm => 'members', |
144
|
|
|
|
|
|
|
realms => { |
145
|
|
|
|
|
|
|
members => { |
146
|
|
|
|
|
|
|
credential => { |
147
|
|
|
|
|
|
|
class => 'RemoteHTTP', |
148
|
|
|
|
|
|
|
url => 'http://intranet.company.com/authenticated.html', |
149
|
|
|
|
|
|
|
password_field => 'password', |
150
|
|
|
|
|
|
|
username_prefix => 'MYDOMAIN\\', |
151
|
|
|
|
|
|
|
http_keep_alive => 1, |
152
|
|
|
|
|
|
|
defer_find_user => 1, |
153
|
|
|
|
|
|
|
}, |
154
|
|
|
|
|
|
|
... |
155
|
|
|
|
|
|
|
}, |
156
|
|
|
|
|
|
|
}, |
157
|
|
|
|
|
|
|
); |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=over 4 |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=item class |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
The classname used for Credential. This is part of |
164
|
|
|
|
|
|
|
L<Catalyst::Plugin::Authentication> and is the method by which |
165
|
|
|
|
|
|
|
Catalyst::Authentication::Credential::RemoteHTTP is loaded as the |
166
|
|
|
|
|
|
|
credential validator. For this module to be used, this must be set to |
167
|
|
|
|
|
|
|
'RemoteHTTP'. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item url |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
The URL that is used to authenticate the user. The module attempts |
172
|
|
|
|
|
|
|
to fetch this URL using a HEAD request (to prevent dragging a large |
173
|
|
|
|
|
|
|
page across the network) with the credentials given. If this fails |
174
|
|
|
|
|
|
|
then the authentication fails. If no URL is supplied in the config, |
175
|
|
|
|
|
|
|
then an exception is thrown on startup. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=item username_field |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
The field in the authentication hash that contains the username. |
180
|
|
|
|
|
|
|
This may vary, but is most likely 'username'. In fact, this is so |
181
|
|
|
|
|
|
|
common that if this is left out of the config, it defaults to |
182
|
|
|
|
|
|
|
'username'. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=item password_field |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
The field in the authentication hash that contains the password. |
187
|
|
|
|
|
|
|
This may vary, but is most likely 'password'. In fact, this is so |
188
|
|
|
|
|
|
|
common that if this is left out of the config, it defaults to |
189
|
|
|
|
|
|
|
'password'. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item username_prefix |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
This is an optional prefix to the username, which is added to the |
194
|
|
|
|
|
|
|
username before it is used for authenticating to the remote http |
195
|
|
|
|
|
|
|
server. It may be used (for example) to apply a domain to the |
196
|
|
|
|
|
|
|
authenticated username. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item username_suffix |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
This is an optional suffix to the username, which is added to the |
201
|
|
|
|
|
|
|
username before it is used for authenticating to the remote http |
202
|
|
|
|
|
|
|
server. It may be used (for example) to apply a domain to the |
203
|
|
|
|
|
|
|
authenticated username. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=item http_keep_alive |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
If C<http_keep_alive> is set then keep_alive is set on the |
208
|
|
|
|
|
|
|
connections to the remote http server. This is required if you are |
209
|
|
|
|
|
|
|
using NTLM authentication (since an additional encryption nonce is |
210
|
|
|
|
|
|
|
passed in the http negotiation). It is optional, but normally |
211
|
|
|
|
|
|
|
harmless, for other forms of authentication. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=item defer_find_user |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Normally the associated user store is queried for user information |
216
|
|
|
|
|
|
|
before the remote http authentication takes place. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
However if, for example, you are using a |
219
|
|
|
|
|
|
|
L<Catalyst::Authentication::Store::DBIx::Class> store with the |
220
|
|
|
|
|
|
|
C<auto_create_user> option, then you can end up with invalid users |
221
|
|
|
|
|
|
|
added to the store. If C<defer_find_user> is set true then the |
222
|
|
|
|
|
|
|
remote http authentication occurs before the user is queried |
223
|
|
|
|
|
|
|
against the store, ensuring that any users passed to the store are |
224
|
|
|
|
|
|
|
known to be valid to the remote http server. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=back |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head1 METHODS |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
There are no publicly exported routines in the RemoteHTTP module |
231
|
|
|
|
|
|
|
(or indeed in most credential modules.) However, below is a |
232
|
|
|
|
|
|
|
description of the routines required by |
233
|
|
|
|
|
|
|
L<Catalyst::Plugin::Authentication> for all credential modules. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=head2 new( $config, $app, $realm ) |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
Instantiate a new RemoteHTTP object using the configuration hash |
238
|
|
|
|
|
|
|
provided in $config. A reference to the application is provided as |
239
|
|
|
|
|
|
|
the second argument. |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=head2 authenticate( $authinfo, $c ) |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Try to log a user in, receives a hashref containing authentication information |
244
|
|
|
|
|
|
|
as the first argument, and the current context as the second. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head1 JUSTIFICATION |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Why would you use this module rather than one of the similar ones? |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
This module gives a combination of authentication against a remote |
251
|
|
|
|
|
|
|
http server, but maintains a local user store. This allows your |
252
|
|
|
|
|
|
|
authentication to be delegated, but the authorization (for example |
253
|
|
|
|
|
|
|
allocation and use of roles) to be determined by the local user |
254
|
|
|
|
|
|
|
store. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Nearly all the other alternatives require you to combine your |
257
|
|
|
|
|
|
|
authentication and authorization databases. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
L<Catalyst::Authentication::Credential::HTTP::Proxy> has a similar |
260
|
|
|
|
|
|
|
basis, but requires you to use HTTP basic authentication for the |
261
|
|
|
|
|
|
|
application, which may not be appropriate. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head1 NTLM NOTES |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
There are a number of issues relating to NTLM authentication. In |
266
|
|
|
|
|
|
|
particular the supporting modules can be rather picky. To make NTLM |
267
|
|
|
|
|
|
|
authentication work you must have an installed copy of libwww-perl |
268
|
|
|
|
|
|
|
that includes L<LWP::Authen::Ntlm> (some linux distributions may drop |
269
|
|
|
|
|
|
|
this component as it gives you additional dependency requirements over |
270
|
|
|
|
|
|
|
the basic L<LWP> package). |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
Additionally you require L<Authen::NTLM> of version 1.02 or later. |
273
|
|
|
|
|
|
|
There are 2 different CPAN module distributions that provide this |
274
|
|
|
|
|
|
|
module - but only one of them has the appropriate version number. |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Finally, if you are using L<NTLM-1.02> then you need to apply the |
277
|
|
|
|
|
|
|
patch described in RT entry 9521 |
278
|
|
|
|
|
|
|
L<http://rt.cpan.org/Ticket/Display.html?id=9521>. |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
When using NTLM authentication the configuration option |
281
|
|
|
|
|
|
|
C<http_keep_alive> must be set true - otherwise the session to the |
282
|
|
|
|
|
|
|
remote server is not maintained and the authentication nonce will |
283
|
|
|
|
|
|
|
be lost between sessions. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
You may also need to set C<username_prefix> or C<username_suffix> |
286
|
|
|
|
|
|
|
to set the correct domain for the authentication, unless the |
287
|
|
|
|
|
|
|
username as given to your application includes the domain |
288
|
|
|
|
|
|
|
information. |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Daisuke Murase <typester@cpan.org> - original |
293
|
|
|
|
|
|
|
L<Catalyst::Plugin::Authentication::Store::HTTP> used as the base |
294
|
|
|
|
|
|
|
for a previous version of this module. |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
The code framework was taken from |
297
|
|
|
|
|
|
|
L<Catalyst::Authentication::Credential::Password> |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
Tomas Doran (t0m) <t0m@state51.co.uk> - Fixups to best practice guidelines |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=head1 INSTALLATION |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
See perlmodinstall for information and options on installing Perl modules. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
You can make new bug reports, and view existing ones, through the |
308
|
|
|
|
|
|
|
web interface at L<http://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Authentication-Credential-RemoteHTTP>. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=head1 AVAILABILITY |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
The project homepage is L<https://metacpan.org/release/Catalyst-Authentication-Credential-RemoteHTTP>. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
The latest version of this module is available from the Comprehensive Perl |
315
|
|
|
|
|
|
|
Archive Network (CPAN). Visit L<http://www.perl.com/CPAN/> to find a CPAN |
316
|
|
|
|
|
|
|
site near you, or see L<https://metacpan.org/module/Catalyst::Authentication::Credential::RemoteHTTP/>. |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=head1 AUTHOR |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
Nigel Metheringham <nigelm@cpan.org> |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
This software is copyright (c) 2012 by Nigel Metheringham <nigelm@cpan.org>. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
327
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=cut |
330
|
|
|
|
|
|
|
|