diff --git a/lemonldap-ng-portal/MANIFEST b/lemonldap-ng-portal/MANIFEST index 3698467af..5adc01827 100644 --- a/lemonldap-ng-portal/MANIFEST +++ b/lemonldap-ng-portal/MANIFEST @@ -62,6 +62,7 @@ lib/Lemonldap/NG/Portal/IssuerDBNull.pm lib/Lemonldap/NG/Portal/IssuerDBOpenID.pm lib/Lemonldap/NG/Portal/IssuerDBOpenIDConnect.pm lib/Lemonldap/NG/Portal/IssuerDBSAML.pm +lib/Lemonldap/NG/Portal/Lib/CAS.pm lib/Lemonldap/NG/Portal/Lib/Choice.pm lib/Lemonldap/NG/Portal/Lib/DBI.pm lib/Lemonldap/NG/Portal/Lib/LDAP.pm diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/CAS.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/CAS.pm new file mode 100644 index 000000000..acfd112bb --- /dev/null +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/CAS.pm @@ -0,0 +1,375 @@ +package Lemonldap::NG::Portal::Lib::CAS; + +use strict; +use Mouse; + +use Lemonldap::NG::Portal::Main::Constants qw( + PE_OK + PE_SENDRESPONSE +); + +our $VERSION = '2.0.0'; + +# PROPERTIES + +# return LWP::UserAgent object +has ua => ( + is => 'rw', + lazy => 1, + builder => sub { + + # TODO : LWP options to use a proxy for example + my $ua = LWP::UserAgent->new(); + push @{ $ua->requests_redirectable }, 'POST'; + $ua->env_proxy(); + return $ua; + } +); + +# INITIALIZATION + +sub init { + my ($self) = @_; +} + +sub sendSoapResponse { + my ( $self, $req, $s ) = @_; + $req->response( + [ + 200, + [ + 'Content-Length' => length($s) + ], + [$s] + ] + ); + return PE_SENDRESPONSE; +} + +# Try to recover the CAS session corresponding to id and return session datas +# If id is set to undef, return a new session +sub getCasSession { + my ( $self, $id ) = @_; + + my $casSession = Lemonldap::NG::Common::Session->new( + { + storageModule => $self->conf->{casStorage}, + storageModuleOptions => $self->conf->{casStorageOptions}, + cacheModule => $self->conf->{localSessionStorage}, + cacheModuleOptions => $self->conf->{localSessionStorageOptions}, + id => $id, + kind => "CAS", + } + ); + + if ( $casSession->error ) { + if ($id) { + $self->p->userInfo("CAS session $id isn't yet available"); + } + else { + $self->lmLog( "Unable to create new CAS session", 'error' ); + $self->lmLog( $casSession->error, 'error' ); + } + return undef; + } + + return $casSession; +} + +# Return an error for CAS VALIDATE request +sub returnCasValidateError { + my ( $self, $req ) = @_; + + $self->lmLog( "Return CAS validate error", 'debug' ); + + $req->response( [ 200, [ 'Content-Length' => 4 ], ["no\n\n"] ] ); + return PE_SENDRESPONSE; +} + +# Return success for CAS VALIDATE request +sub returnCasValidateSuccess { + my ( $self, $req, $username ) = @_; + + $self->lmLog( "Return CAS validate success with username $username", + 'debug' ); + + return $self->sendSoapResponse( $req, "yes\n$username\n" ); +} + +# Return an error for CAS SERVICE VALIDATE request +sub returnCasServiceValidateError { + my ( $self, $req, $code, $text ) = @_; + + $code ||= 'INTERNAL_ERROR'; + $text ||= 'No description provided'; + + $self->lmLog( "Return CAS service validate error $code ($text)", 'debug' ); + + return $self->sendSoapResponse( + $req, " +\t +\t\t$text +\t +\n" + ); +} + +# Return success for CAS SERVICE VALIDATE request +sub returnCasServiceValidateSuccess { + my ( $self, $req, $username, $pgtIou, $proxies, $attributes ) = @_; + + $self->lmLog( "Return CAS service validate success with username $username", + 'debug' ); + + my $s = "$pgtIou\n"; + } + if ($proxies) { + $self->lmLog( "Add proxies $proxies in response", 'debug' ); + $s .= "\t\t\n\t\t\t$_\n" + foreach ( split( /$self->{multiValuesSeparator}/, $proxies ) ); + $s .= "\t\t\n"; + } + $s .= "\t\n\n"; + + return $self->sendSoapResponse( $req, $s ); +} + +# Return an error for CAS PROXY request +sub returnCasProxyError { + my ( $self, $req, $code, $text ) = @_; + + $code ||= 'INTERNAL_ERROR'; + $text ||= 'No description provided'; + + $self->lmLog( "Return CAS proxy error $code ($text)", 'debug' ); + + return $self->sendSoapResponse( + $req, " +\t +\t\t$text +\t +\n" + ); +} + +# Return success for CAS PROXY request +sub returnCasProxySuccess { + my ( $self, $req, $ticket ) = @_; + + $self->lmLog( "Return CAS proxy success with ticket $ticket", 'debug' ); + + return $self->sendSoapResponse( + $req, " +\t +\t\t$ticket +\t +\n" + ); +} + +# Find and delete CAS sessions bounded to a primary session +sub deleteCasSecondarySessions { + my ( $self, $session_id ) = @_; + my $result = 1; + + # Find CAS sessions + my $moduleOptions = $self->conf->{casStorageOptions} || {}; + $moduleOptions->{backend} = $self->conf->{casStorage}; + my $module = "Lemonldap::NG::Common::Apache::Session"; + + my $cas_sessions = + $module->searchOn( $moduleOptions, "_cas_id", $session_id ); + + if ( my @cas_sessions_keys = keys %$cas_sessions ) { + + foreach my $cas_session (@cas_sessions_keys) { + + # Get session + $self->lmLog( "Retrieve CAS session $cas_session", 'debug' ); + + my $casSession = $self->getCasSession($cas_session); + + # Delete session + $result = $self->deleteCasSession($casSession); + } + } + else { + $self->lmLog( "No CAS session found for session $session_id ", + 'debug' ); + } + + return $result; + +} + +# Delete an opened CAS session +sub deleteCasSession { + my ( $self, $session ) = @_; + + # Check session object + unless ( $session && $session->data ) { + $self->lmLog( "No session to delete", 'error' ); + return 0; + } + + # Get session_id + my $session_id = $session->id; + + # Delete session + unless ( $session->remove ) { + $self->lmLog( $session->error, 'error' ); + return 0; + } + + $self->lmLog( "CAS session $session_id deleted", 'debug' ); + + return 1; +} + +# Call proxy granting URL on CAS client +sub callPgtUrl { + my ( $self, $pgtUrl, $pgtIou, $pgtId ) = @_; + + # Build URL + my $url = + $pgtUrl . ( $pgtUrl =~ /\?/ ? '&' : '?' ) . "pgtIou=$pgtIou&pgtId=$pgtId"; + + $self->lmLog( "Call URL $url", 'debug' ); + + # GET URL + my $response = $self->ua->get($url); + + # Return result + return $response->is_success(); +} + +1; +__END__ + +=head1 NAME + +=encoding utf8 + +Lemonldap::NG::Portal::_CAS - Common CAS functions + +=head1 SYNOPSIS + +use Lemonldap::NG::Portal::_CAS; + +=head1 DESCRIPTION + +This module contains common methods for CAS + +=head1 METHODS + +=head2 getCasSession + +Try to recover the CAS session corresponding to id and return session datas +If id is set to undef, return a new session + +=head2 returnCasValidateError + +Return an error for CAS VALIDATE request + +=head2 returnCasValidateSuccess + +Return success for CAS VALIDATE request + +=head2 deleteCasSecondarySessions + +Find and delete CAS sessions bounded to a primary session + +=head2 returnCasServiceValidateError + +Return an error for CAS SERVICE VALIDATE request + +=head2 returnCasServiceValidateSuccess + +Return success for CAS SERVICE VALIDATE request + +=head2 returnCasProxyError + +Return an error for CAS PROXY request + +=head2 returnCasProxySuccess + +Return success for CAS PROXY request + +=head2 deleteCasSession + +Delete an opened CAS session + +=head2 callPgtUrl + +Call proxy granting URL on CAS client + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +=over + +=item Clement Oudot, Eclem.oudot@gmail.comE + +=item Xavier Guimard, Ex.guimard@free.frE + +=back + +=head1 BUG REPORT + +Use OW2 system to report bug or ask for features: +L + +=head1 DOWNLOAD + +Lemonldap::NG is available at +L + +=head1 COPYRIGHT AND LICENSE + +=over + +=item Copyright (C) 2010-2012 by Clement Oudot, Eclem.oudot@gmail.comE + +=item Copyright (C) 2016 by Xavier Guimard, Ex.guimard@free.frE + +=back + +This library is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see L. + +=cut