line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Authen::CAS::Client; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
require 5.006_001; |
4
|
|
|
|
|
|
|
|
5
|
5
|
|
|
5
|
|
267645
|
use strict; |
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
202
|
|
6
|
5
|
|
|
5
|
|
26
|
use warnings; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
160
|
|
7
|
|
|
|
|
|
|
|
8
|
5
|
|
|
5
|
|
2912
|
use Authen::CAS::Client::Response; |
|
5
|
|
|
|
|
14
|
|
|
5
|
|
|
|
|
140
|
|
9
|
5
|
|
|
5
|
|
2091
|
use LWP::UserAgent; |
|
5
|
|
|
|
|
122905
|
|
|
5
|
|
|
|
|
173
|
|
10
|
5
|
|
|
5
|
|
40
|
use URI; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
125
|
|
11
|
5
|
|
|
5
|
|
4470
|
use URI::QueryParam; |
|
5
|
|
|
|
|
4305
|
|
|
5
|
|
|
|
|
148
|
|
12
|
5
|
|
|
5
|
|
6878
|
use XML::LibXML; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.07'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
#====================================================================== |
18
|
|
|
|
|
|
|
# constructor |
19
|
|
|
|
|
|
|
# |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub new { |
22
|
|
|
|
|
|
|
my ( $class, $cas, %args ) = @_; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $self = { |
25
|
|
|
|
|
|
|
_cas => URI->new( $cas ), |
26
|
|
|
|
|
|
|
_ua => LWP::UserAgent->new( agent => "Authen-CAS-Client/$VERSION" ), |
27
|
|
|
|
|
|
|
_fatal => $args{fatal} ? 1 : 0, |
28
|
|
|
|
|
|
|
}; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
bless $self, $class; |
31
|
|
|
|
|
|
|
} |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
#====================================================================== |
35
|
|
|
|
|
|
|
# private methods |
36
|
|
|
|
|
|
|
# |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub _error { |
39
|
|
|
|
|
|
|
my ( $self, $error, $doc ) = @_; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
die $error |
42
|
|
|
|
|
|
|
if $self->{_fatal}; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Authen::CAS::Client::Response::Error->new( error => $error, doc => $doc ); |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub _parse_auth_response { |
48
|
|
|
|
|
|
|
my ( $self, $xml ) = @_; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
my $doc = eval { XML::LibXML->new->parse_string( $xml ) }; |
51
|
|
|
|
|
|
|
return $self->_error( 'Failed to parse XML', $xml ) |
52
|
|
|
|
|
|
|
if $@; |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
my ( $node, $response ); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
eval { |
57
|
|
|
|
|
|
|
if( $node = $doc->find( '/cas:serviceResponse/cas:authenticationSuccess' )->get_node( 1 ) ) { |
58
|
|
|
|
|
|
|
$response = eval { |
59
|
|
|
|
|
|
|
my $user = $node->find( './cas:user' )->get_node( 1 )->textContent; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
my $iou = $node->find( './cas:proxyGrantingTicket' )->get_node( 1 ); |
62
|
|
|
|
|
|
|
$iou = $iou->textContent |
63
|
|
|
|
|
|
|
if( defined $iou ); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
my $proxies = $node->findnodes( './cas:proxies/cas:proxy' ); |
66
|
|
|
|
|
|
|
$proxies = [ map $_->textContent, @$proxies ] |
67
|
|
|
|
|
|
|
if defined $proxies; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Authen::CAS::Client::Response::AuthSuccess->new( |
70
|
|
|
|
|
|
|
user => $user, |
71
|
|
|
|
|
|
|
iou => $iou, |
72
|
|
|
|
|
|
|
proxies => $proxies, |
73
|
|
|
|
|
|
|
doc => $doc, |
74
|
|
|
|
|
|
|
); |
75
|
|
|
|
|
|
|
}; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
$response = $self->_error( 'Failed to parse authentication success response', $doc ) |
78
|
|
|
|
|
|
|
if $@; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
elsif( $node = $doc->find( '/cas:serviceResponse/cas:authenticationFailure' )->get_node( 1 ) ) { |
81
|
|
|
|
|
|
|
$response = eval { |
82
|
|
|
|
|
|
|
die |
83
|
|
|
|
|
|
|
unless $node->hasAttribute( 'code' ); |
84
|
|
|
|
|
|
|
my $code = $node->getAttribute( 'code' ); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my $message = $node->textContent; |
87
|
|
|
|
|
|
|
s/^\s+//, s/\s+\z// |
88
|
|
|
|
|
|
|
for $message; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Authen::CAS::Client::Response::AuthFailure->new( |
91
|
|
|
|
|
|
|
code => $code, |
92
|
|
|
|
|
|
|
message => $message, |
93
|
|
|
|
|
|
|
doc => $doc, |
94
|
|
|
|
|
|
|
); |
95
|
|
|
|
|
|
|
}; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
$response = $self->_error( 'Failed to parse authentication failure response', $doc ) |
98
|
|
|
|
|
|
|
if $@; |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
else { |
101
|
|
|
|
|
|
|
die; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
}; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
$response = $self->_error( 'Invalid CAS response', $doc ) |
106
|
|
|
|
|
|
|
if $@; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
return $response; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub _parse_proxy_response { |
112
|
|
|
|
|
|
|
my ( $self, $xml ) = @_; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
my $doc = eval { XML::LibXML->new->parse_string( $xml ) }; |
115
|
|
|
|
|
|
|
return $self->_error( 'Failed to parse XML', $xml ) |
116
|
|
|
|
|
|
|
if $@; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
my ( $node, $response ); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
eval { |
121
|
|
|
|
|
|
|
if( $node = $doc->find( '/cas:serviceResponse/cas:proxySuccess' )->get_node( 1 ) ) { |
122
|
|
|
|
|
|
|
$response = eval { |
123
|
|
|
|
|
|
|
my $proxy_ticket = $node->find( './cas:proxyTicket' )->get_node( 1 )->textContent; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Authen::CAS::Client::Response::ProxySuccess->new( |
126
|
|
|
|
|
|
|
proxy_ticket => $proxy_ticket, |
127
|
|
|
|
|
|
|
doc => $doc, |
128
|
|
|
|
|
|
|
); |
129
|
|
|
|
|
|
|
}; |
130
|
|
|
|
|
|
|
$response = $self->_error( 'Failed to parse proxy success response', $doc ) |
131
|
|
|
|
|
|
|
if $@; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
elsif( $node = $doc->find( '/cas:serviceResponse/cas:proxyFailure' )->get_node( 1 ) ) { |
134
|
|
|
|
|
|
|
$response = eval { |
135
|
|
|
|
|
|
|
die |
136
|
|
|
|
|
|
|
unless $node->hasAttribute( 'code' ); |
137
|
|
|
|
|
|
|
my $code = $node->getAttribute( 'code' ); |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
my $message = $node->textContent; |
140
|
|
|
|
|
|
|
s/^\s+//, s/\s+\z// |
141
|
|
|
|
|
|
|
for $message; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Authen::CAS::Client::Response::ProxyFailure->new( |
144
|
|
|
|
|
|
|
code => $code, |
145
|
|
|
|
|
|
|
message => $message, |
146
|
|
|
|
|
|
|
doc => $doc, |
147
|
|
|
|
|
|
|
); |
148
|
|
|
|
|
|
|
}; |
149
|
|
|
|
|
|
|
$response = $self->_error( 'Failed to parse proxy failure response', $doc ) |
150
|
|
|
|
|
|
|
if $@; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
else { |
153
|
|
|
|
|
|
|
die; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
}; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
$response = $self->_error( 'Invalid CAS response', $doc ) |
158
|
|
|
|
|
|
|
if $@; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
return $response; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub _server_request { |
164
|
|
|
|
|
|
|
my ( $self, $path, $params ) = @_; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
my $url = $self->_url( $path, $params )->canonical; |
167
|
|
|
|
|
|
|
my $response = $self->{_ua}->get( $url ); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
unless( $response->is_success ) { |
170
|
|
|
|
|
|
|
return $self->_error( |
171
|
|
|
|
|
|
|
'HTTP request failed: ' . $response->code . ': ' . $response->message, |
172
|
|
|
|
|
|
|
$response->content |
173
|
|
|
|
|
|
|
); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
return $response->content; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _url { |
180
|
|
|
|
|
|
|
my ( $self, $path, $params ) = @_; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
my $url = $self->{_cas}->clone; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
$url->path( $url->path . $path ); |
185
|
|
|
|
|
|
|
$url->query_param_append( $_ => $params->{$_} ) |
186
|
|
|
|
|
|
|
for keys %$params; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
return $url; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub _v20_validate { |
192
|
|
|
|
|
|
|
my ( $self, $path, $service, $ticket, %args ) = @_; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
my %params = ( service => $service, ticket => $ticket ); |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$params{renew} = 'true' |
197
|
|
|
|
|
|
|
if $args{renew}; |
198
|
|
|
|
|
|
|
$params{pgtUrl} = URI->new( $args{pgtUrl} )->canonical |
199
|
|
|
|
|
|
|
if defined $args{pgtUrl}; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
my $content = $self->_server_request( $path, \%params ); |
202
|
|
|
|
|
|
|
return $content |
203
|
|
|
|
|
|
|
if ref $content; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
return $self->_parse_auth_response( $content ); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
#====================================================================== |
210
|
|
|
|
|
|
|
# public methods |
211
|
|
|
|
|
|
|
# |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub login_url { |
214
|
|
|
|
|
|
|
my ( $self, $service, %args ) = @_; |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my %params = ( service => $service ); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
for ( qw/ renew gateway / ) { |
219
|
|
|
|
|
|
|
$params{$_} = 'true', last |
220
|
|
|
|
|
|
|
if $args{$_}; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
return $self->_url( '/login', \%params )->canonical; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub logout_url { |
227
|
|
|
|
|
|
|
my ( $self, %args ) = @_; |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
my %params; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
$params{url} = $args{url} |
232
|
|
|
|
|
|
|
if defined $args{url}; |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
return $self->_url( '/logout', \%params )->canonical; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub validate { |
238
|
|
|
|
|
|
|
my ( $self, $service, $ticket, %args ) = @_; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
my %params = ( service => $service, ticket => $ticket ); |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
$params{renew} = 'true' |
243
|
|
|
|
|
|
|
if $args{renew}; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
my $content = $self->_server_request( '/validate', \%params ); |
246
|
|
|
|
|
|
|
return $content |
247
|
|
|
|
|
|
|
if ref $content; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
my $response; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
if( $content =~ /^no\n\n\z/ ) { |
252
|
|
|
|
|
|
|
$response = Authen::CAS::Client::Response::AuthFailure->new( code => 'V10_AUTH_FAILURE', doc => $content ); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
elsif( $content =~ /^yes\n([^\n]+)\n\z/ ) { |
255
|
|
|
|
|
|
|
$response = Authen::CAS::Client::Response::AuthSuccess->new( user => $1, doc => $content ); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
else { |
258
|
|
|
|
|
|
|
$response = $self->_error( 'Invalid CAS response', $content ); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
return $response; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub service_validate { |
265
|
|
|
|
|
|
|
my ( $self, $service, $ticket, %args ) = @_; |
266
|
|
|
|
|
|
|
return $self->_v20_validate( '/serviceValidate', $service, $ticket, %args ); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
sub proxy_validate { |
270
|
|
|
|
|
|
|
my ( $self, $service, $ticket, %args ) = @_; |
271
|
|
|
|
|
|
|
return $self->_v20_validate( '/proxyValidate', $service, $ticket, %args ); |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub proxy { |
275
|
|
|
|
|
|
|
my ( $self, $pgt, $target ) = @_; |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
my %params = ( pgt => $pgt, targetService => URI->new( $target ) ); |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
my $content = $self->_server_request( '/proxy', \%params ); |
280
|
|
|
|
|
|
|
return $content |
281
|
|
|
|
|
|
|
if ref $content; |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
return $self->_parse_proxy_response( $content ); |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
1 |
288
|
|
|
|
|
|
|
__END__ |