diff --git a/TODO-2.0.md b/TODO-2.0.md index d089d90e4..1e488b582 100644 --- a/TODO-2.0.md +++ b/TODO-2.0.md @@ -1,3 +1,4 @@ +* updateStatus( Main, SOAP server ) * replace SOAP calls in Handler/Lib/AuthBasic and Auth/Proxy * replace SOAP by REST for notification creation * "mail" in UserDB/* diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Apache/Session/SOAP.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Apache/Session/SOAP.pm index 59b1d2d8f..04fe3e149 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Apache/Session/SOAP.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Apache/Session/SOAP.pm @@ -126,7 +126,7 @@ sub _connect { if ( $self->{proxyOptions} ) { push @args, %{ $self->{proxyOptions} }; } - $self->{ns} ||= 'urn:Lemonldap/NG/Common/CGI/SOAPService'; + $self->{ns} ||= 'urn:Lemonldap/NG/Common/PSGI/SOAPService'; return $self->{service} = SOAP::Lite->ns( $self->{ns} )->proxy(@args); } diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/SOAPServer.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/SOAPServer.pm index c8eca79eb..b8dfad7ab 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/SOAPServer.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/SOAPServer.pm @@ -6,7 +6,7 @@ use bytes; use strict; use SOAP::Transport::HTTP; -our @ISA = ('SOAP::Transport::HTTP'); +our @ISA = ('SOAP::Transport::HTTP::Server'); our $VERSION = '2.0.0'; @@ -17,7 +17,8 @@ sub new { my $self = shift; return $self if ref $self; - $self = $self->SUPER::new(@_); + my $class = ref($self) || $self; + $self = $class->SUPER::new(@_); SOAP::Trace::objects('()'); return $self; diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/SOAPService.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/SOAPService.pm index af48e52bb..aaf869685 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/SOAPService.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/SOAPService.pm @@ -15,9 +15,9 @@ our $VERSION = '2.0.0'; # @param @func authorizated methods # @return Lemonldap::NG::Common::PSGI::SOAPService object sub new { - my ( $class, $obj, @func ) = @_; + my ( $class, $obj, $req, @func ) = @_; s/.*::// foreach (@func); - return bless { obj => $obj, func => \@func }, $class; + return bless { obj => $obj, func => \@func, req => $req }, $class; } ## @method datas AUTOLOAD() @@ -30,7 +30,7 @@ sub AUTOLOAD { my $self = shift; $AUTOLOAD =~ s/.*:://; if ( grep { $_ eq $AUTOLOAD } @{ $self->{func} } ) { - my $tmp = $self->{obj}->$AUTOLOAD(@_); + my $tmp = $self->{obj}->$AUTOLOAD( $self->{req}, @_ ); unless ( ref($tmp) and ref($tmp) eq 'SOAP::Data' ) { $tmp = SOAP::Data->name( result => $tmp ); } diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/AuthBasic.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/AuthBasic.pm index 8c20e7e81..844bb8a21 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/AuthBasic.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/AuthBasic.pm @@ -67,7 +67,7 @@ sub createSession { my $soapClient = SOAP::Lite->proxy( $class->tsv->{portal}->(), default_headers => $soapHeaders ) - ->uri('urn:Lemonldap::NG::Common::CGI::SOAPService'); + ->uri('urn:Lemonldap/NG/Common/PSGI/SOAPService'); my $creds = $class->header_in('Authorization'); $creds =~ s/^Basic\s+//; diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm index d355386c3..3fd81e558 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm @@ -956,7 +956,11 @@ sub attributes { type => 'bool', documentation => 'Enable SOAP services', }, - exportedAttr => { type => 'text', }, + exportedAttr => { + type => 'text', + documentation => + 'List of attributes to export by SOAP or REST servers', + }, ## Virtualhosts diff --git a/lemonldap-ng-portal/MANIFEST b/lemonldap-ng-portal/MANIFEST index 3f6f7c6a9..ad6038d1d 100644 --- a/lemonldap-ng-portal/MANIFEST +++ b/lemonldap-ng-portal/MANIFEST @@ -372,6 +372,7 @@ t/32-Auth-and-issuer-OIDC-authorization_code.t t/32-Auth-and-issuer-OIDC-hybrid.t t/32-Auth-and-issuer-OIDC-implicit.t t/33-Auth-and-issuer-OpenID2.t +t/34-Auth-Proxy-and-SOAP-Server.t t/40-Notifications-DBI.t t/50-IssuerGet.t t/90-translations.t diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Proxy.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Proxy.pm index c3f4d6ff3..af8f00f15 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Proxy.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Proxy.pm @@ -13,12 +13,12 @@ our $VERSION = '2.0.0'; sub init { my ($self) = @_; $self->conf->{soapSessionService} ||= - $self->conf->{soapAuthService} . 'index.pl/sessions'; + $self->conf->{soapAuthService} . '/sessions'; $self->conf->{soapSessionService} =~ s/\.plindex.pl/\.pl/; $self->conf->{remoteCookieName} ||= $self->conf->{cookieName}; - unless ( defined $self->conf->{soapAuthService} ) { - $self->error("Missing soapAuthService parameter"); + unless ( defined $self->conf->{soapSessionService} ) { + $self->error("Missing soapSessionService parameter"); return 0; } return 1; @@ -32,7 +32,7 @@ sub getUser { my ( $self, $req ) = @_; return PE_OK if ( $req->datas->{_proxyQueryDone} ); my $soap = SOAP::Lite->proxy( $self->conf->{soapSessionService} ) - ->uri('urn:Lemonldap::NG::Common::CGI::SOAPService'); + ->uri('urn:Lemonldap/NG/Common/PSGI/SOAPService'); my $r = $soap->getCookies( $req->{user}, $req->datas->{password} ); if ( $r->fault ) { $self->lmLog( @@ -65,7 +65,7 @@ sub setSessionInfo { return PE_OK if ( $req->datas->{_setSessionInfoDone} ); my $soap = SOAP::Lite->proxy( $self->conf->{soapSessionService} ) - ->uri('urn:Lemonldap::NG::Common::CGI::SOAPService'); + ->uri('urn:Lemonldap/NG/Common/PSGI/SOAPService'); my $r = $soap->getAttributes( $req->datas->{_remoteId} ); if ( $r->fault ) { $self->lmLog( diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Plugins.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Plugins.pm index eb8523a56..471e31bd9 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Plugins.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Plugins.pm @@ -36,8 +36,10 @@ sub enabledPlugins { #} # Check if SOAP is enabled - # TODO: REST - push @res, 'SOAP' if ( $self->conf->{Soap} ); + push @res, '::Plugins::SOAPServer' if ( $self->conf->{Soap} ); + + # Check if REST is enabled + push @res, '::Plugins::RESTServer' if ( $self->conf->{rest} ); # Check if notification is enabled push @res, '::Plugins::Notifications' if ( $self->conf->{notifications} ); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm index 8daf4f4c7..ddd5eb8ba 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm @@ -129,6 +129,7 @@ sub do { return $req->response; } if ( !$self->conf->{noAjaxHook} and $req->wantJSON ) { + $self->lmLog('Processing to JSON response','debug'); if ( $err > 0 and !%{ $req->sessionInfo } ) { return [ 401, @@ -170,9 +171,11 @@ sub do { ) { my ( $tpl, $prms ) = $self->display($req); + $self->lmLog("Calling sendHtml with template $tpl",'debug'); return $self->sendHtml( $req, $tpl, params => $prms ); } else { + $self->lmLog('Calling autoredirect','debug'); return $self->autoRedirect($req); } } @@ -227,6 +230,7 @@ sub autoRedirect { } } my ( $tpl, $prms ) = $self->display($req); + $self->lmLog("Calling sendHtml with template $tpl",'debug'); return $self->sendHtml( $req, $tpl, params => $prms ); } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm index ede09d59d..fa44158ea 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm @@ -148,8 +148,7 @@ sub getCookies { # Launch process else { - $req->{error} = $self->p->process( - $req, + $req->steps( [ qw(getUser setAuthSessionInfo), @{ $self->p->betweenAuthAndDatas }, @@ -157,6 +156,7 @@ sub getCookies { @{ $self->p->afterDatas }, ] ); + $req->{error} = $self->p->process($req); $self->lmLog( "SOAP authentication result for $user: code $req->{error}", 'debug' ); @@ -176,7 +176,9 @@ sub getCookies { } push @tmp, SOAP::Data->name( cookies => \SOAP::Data->value(@cookies) ); my $res = SOAP::Data->name( session => \SOAP::Data->value(@tmp) ); - $self->p->updateStatus($req); + + #TODO: updateStatus + #$self->p->updateStatus($req); return $res; } @@ -209,7 +211,7 @@ sub getAttributes { push @tmp, SOAP::Data->name( error => 0 )->type('int'); push @tmp, SOAP::Data->name( attributes => - _buildSoapHash( $session->data, @{ $self->exportedAttr($req) } ) + _buildSoapHash( $session->data, @{ $self->exportedAttr } ) ); } my $res = SOAP::Data->name( session => \SOAP::Data->value(@tmp) ); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_Proxy.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_Proxy.pm index 20f0a40ae..aa2faa196 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_Proxy.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_Proxy.pm @@ -50,7 +50,7 @@ sub proxyQuery { return PE_OK if ( $self->{_proxyQueryDone} ); my $soap = SOAP::Lite->proxy( $self->{soapAuthService} ) - ->uri('urn:Lemonldap::NG::Common::CGI::SOAPService'); + ->uri('urn:Lemonldap/NG/Common/CGI/SOAPService'); my $r = $soap->getCookies( $self->{user}, $self->{password} ); if ( $r->fault ) { $self->abort( "Unable to query authentication service", @@ -79,7 +79,7 @@ sub setSessionInfo { return PE_OK if ( $self->{_setSessionInfoDone} ); my $soap = SOAP::Lite->proxy( $self->{soapSessionService} ) - ->uri('urn:Lemonldap::NG::Common::CGI::SOAPService'); + ->uri('urn:Lemonldap/NG/Common/CGI/SOAPService'); my $r = $soap->getAttributes( $self->{_remoteId} ); if ( $r->fault ) { $self->abort( "Unable to query authentication service", diff --git a/lemonldap-ng-portal/t/34-Auth-Proxy-and-SOAP-Server.t b/lemonldap-ng-portal/t/34-Auth-Proxy-and-SOAP-Server.t new file mode 100644 index 000000000..bf2721503 --- /dev/null +++ b/lemonldap-ng-portal/t/34-Auth-Proxy-and-SOAP-Server.t @@ -0,0 +1,127 @@ +use Test::More; +use strict; +use IO::String; + +BEGIN { + require 't/test-lib.pm'; +} + +my $maintests = 4; +my $debug = 'error'; +my ( $issuer, $sp, $res ); +my %handlerOR = ( issuer => [], sp => [] ); + +SKIP: { + eval 'use SOAP::Lite'; + if ($@) { + skip 'SOAP::Lite not found', $maintests; + } + + ok( $issuer = issuer(), 'Issuer portal' ); + $handlerOR{issuer} = \@Lemonldap::NG::Handler::Main::Reload::_onReload; + switch ('sp'); + + ok( $sp = sp(), 'SP portal' ); + $handlerOR{sp} = \@Lemonldap::NG::Handler::Main::Reload::_onReload; + + # Simple SP access + my $res; + ok( + $res = $sp->_get( + '/', accept => 'text/html', + ), + 'Unauth SP request' + ); + expectOK($res); + + # Try to auth + ok( + $res = $sp->_post( + '/', IO::String->new('user=dwho&password=dwho'), + length => 23, + accept => 'text/html' + ), + 'Post user/password' + ); + expectRedirection( $res, 'http://auth.sp.com' ); + expectCookie($res); +} + +count($maintests); +clean_sessions(); +done_testing( count() ); + +# Redefine LWP methods for tests +no warnings 'redefine'; + +sub LWP::UserAgent::request { + my ( $self, $req ) = @_; + ok( $req->uri =~ m#http://auth.((?:id|s)p).com(.*)#, 'SOAP request' ); + my $host = $1; + my $url = $2; + my $res; + my $s = $req->content; + my $client = ( $host eq 'idp' ? $issuer : $sp ); + ok( + $res = $client->_post( + $url, + IO::String->new($s), + length => length($s), + type => $req->header('Content-Type'), + custom => { + HTTP_SOAPACTION => $req->header('Soapaction'), + }, + ), + 'Execute request' + ); + expectOK($res); + ok( getHeader( $res, 'Content-Type' ) =~ m#^(?:text|application)/xml#, + 'Content is XML' ) + or explain( $res->[1], 'Content-Type => application/xml' ); + my $httpResp = HTTP::Response->new( $res->[0], 'OK' ); + + while ( my $name = shift @{ $res->[1] } ) { + $httpResp->header( $name, shift( @{ $res->[1] } ) ); + } + $httpResp->content( join( '', @{ $res->[2] } ) ); + count(3); + return $httpResp; +} + +sub switch { + my $type = shift; + @Lemonldap::NG::Handler::Main::Reload::_onReload = @{ + $handlerOR{$type}; + }; +} + +sub issuer { + return LLNG::Manager::Test->new( + { + ini => { + logLevel => $debug, + templatesDir => 'site/htdocs/static', + domain => 'idp.com', + portal => 'http://auth.idp.com', + authentication => 'Demo', + userDB => 'Demo', + Soap => 1, + } + } + ); +} + +sub sp { + return LLNG::Manager::Test->new( + { + ini => { + logLevel => $debug, + domain => 'sp.com', + portal => 'http://auth.sp.com', + authentication => 'Proxy', + userDB => 'Proxy', + soapSessionService => 'http://auth.idp.com/sessions', + }, + } + ); +} diff --git a/lemonldap-ng-portal/t/test-lib.pm b/lemonldap-ng-portal/t/test-lib.pm index fd0072762..f421f1b7f 100644 --- a/lemonldap-ng-portal/t/test-lib.pm +++ b/lemonldap-ng-portal/t/test-lib.pm @@ -332,7 +332,7 @@ sub _post { 'SERVER_PORT' => '80', 'SERVER_PROTOCOL' => 'HTTP/1.1', ( $args{custom} ? %{ $args{custom} } : () ), - 'psgix.input.buffered' => 1, + 'psgix.input.buffered' => 0, 'psgi.input' => $body, 'CONTENT_LENGTH' => $args{length} // scalar( ( stat $body )[7] ), 'CONTENT_TYPE' => $args{type}