diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/CAS.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/CAS.pm index caa8b0251..715fd83b1 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/CAS.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/CAS.pm @@ -3,7 +3,8 @@ package Lemonldap::NG::Portal::Lib::CAS; use strict; use Mouse; use Lemonldap::NG::Common::FormEncode; -use XML::Simple; +use Hash::MultiValue; +use XML::LibXML; use Lemonldap::NG::Common::UserAgent; our $VERSION = '2.1.0'; @@ -28,6 +29,14 @@ has casAppList => ( is => 'rw', default => sub { {} }, ); has spRules => ( is => 'rw', default => sub { {} }, ); has spMacros => ( is => 'rw', default => sub { {} }, ); +# XML parser +has parser => ( + is => 'rw', + builder => sub { + return XML::LibXML->new( load_ext_dtd => 0, expand_entities => 0 ); + } +); + # RUNNING METHODS # Load CAS server list @@ -389,20 +398,30 @@ sub validateST { return 0 if $response->is_error; - my $xml = $response->decoded_content( default_charset => 'UTF-8' ); - utf8::encode($xml); - $xml = XMLin($xml); + my $xml = $response->decoded_content( default_charset => 'UTF-8' ); + my $casResponse = $self->parser->parse_string($xml)->documentElement; - if ( defined $xml->{'cas:authenticationFailure'} ) { + unless ( $casResponse->nodeName eq "cas:serviceResponse" ) { $self->logger->error( "Failed to validate Service Ticket $ticket: " - . $xml->{'cas:authenticationFailure'} ); + . "unexpected top-level XML element: " + . $casResponse->nodeName ); + return 0; + } + + if ( my $failure = + $casResponse->getElementsByTagName('cas:authenticationFailure') ) + { + $self->logger->error( "Failed to validate Service Ticket $ticket: " + . $failure->string_value =~ s/\R//r ); return 0; } # Get proxy data and store pgtId if ($proxy_url) { my $pgtIou = - $xml->{'cas:authenticationSuccess'}->{'cas:proxyGrantingTicket'}; + $casResponse->find( + '//cas:authenticationSuccess/cas:proxyGrantingTicket') + ->string_value; if ($pgtIou) { my $moduleOptions; @@ -435,24 +454,37 @@ sub validateST { } } - my $user = $xml->{'cas:authenticationSuccess'}->{'cas:user'}; - my $attrs = {}; - if ( my $casAttr = $xml->{'cas:authenticationSuccess'}->{'cas:attributes'} ) + my $user = + $casResponse->find('//cas:authenticationSuccess/cas:user')->string_value; + unless ($user) { + $self->logger->error( + "Could not extract cas:user field from XML response"); + return 0; + } + + my $attrs = Hash::MultiValue->new; + if ( my $casAttr = + $casResponse->find('//cas:authenticationSuccess/cas:attributes/cas:*') ) { - foreach my $k ( keys %$casAttr ) { - my $v = $casAttr->{$k}; - if ( ref($v) eq "ARRAY" ) { - $v = join( $self->conf->{multiValuesSeparator}, @$v ); + $casAttr->foreach( + sub { + my $k = $_[0]->localname; + my $v = $_[0]->textContent; + utf8::encode($v); + $attrs->add( $k => $v ); } - utf8::encode($v); - $k =~ s/^cas://; - $attrs->{$k} = $v; - } + ); + } + + # Flatten each list of attribute values + my $result = {}; + for ( $attrs->keys ) { + $result->{$_} = join $self->conf->{multiValuesSeparator}, + $attrs->get_all($_); } # TODO store attributes for UserDBCAS - - return ( $user, $attrs ); + return ( $user, $result ); } # Store PGT IOU and PGT ID @@ -484,15 +516,18 @@ sub retrievePT { return 0 if $response->is_error; - my $xml = XMLin( $response->decoded_content ); + my $casResponse = $self->parser->parse_string( $response->decoded_content ) + ->documentElement; - if ( defined $xml->{'cas:proxyFailure'} ) { + if ( my $failure = $casResponse->getElementsByTagName('cas:proxyFailure') ) + { $self->logger->error( - "Failed to get PT: " . $xml->{'cas:proxyFailure'} ); + "Failed to get PT: " . $failure->string_value =~ s/\R//r ); return 0; } - my $pt = $xml->{'cas:proxySuccess'}->{'cas:proxyTicket'}; + my $pt = + $casResponse->find('//cas:proxySuccess/cas:proxyTicket')->string_value; return $pt; }