SOAP server (#970)

This commit is contained in:
Xavier Guimard 2017-01-07 20:37:07 +00:00
parent b24343bd10
commit 1983842f79
14 changed files with 164 additions and 22 deletions

View File

@ -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/*

View File

@ -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);
}

View File

@ -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;

View File

@ -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 );
}

View File

@ -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+//;

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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} );

View File

@ -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 );
}

View File

@ -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) );

View File

@ -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",

View File

@ -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',
},
}
);
}

View File

@ -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}