From 906f081b314e4dc4a70141bb9c7ea41629eadb45 Mon Sep 17 00:00:00 2001 From: Xavier Guimard Date: Mon, 6 Mar 2017 15:06:49 +0000 Subject: [PATCH] Verify REST backend config (#970) --- .../Lemonldap/NG/Common/Conf/Backends/REST.pm | 10 +- .../Lemonldap/NG/Common/Conf/RESTServer.pm | 38 ++-- lemonldap-ng-portal/MANIFEST | 1 + .../Lemonldap/NG/Portal/Plugins/RESTServer.pm | 7 + .../t/35-REST-config-backend.t | 203 ++++++++++++++++++ .../t/35-REST-sessions-with-REST-server.t | 1 - 6 files changed, 245 insertions(+), 15 deletions(-) create mode 100644 lemonldap-ng-portal/t/35-REST-config-backend.t diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/Backends/REST.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/Backends/REST.pm index a91b0bdf6..f6398e06d 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/Backends/REST.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/Backends/REST.pm @@ -8,6 +8,12 @@ our $VERSION = '2.0.0'; #parameter baseUrl, user, password, realm, lwpOpts +BEGIN { + *Lemonldap::NG::Common::Conf::getJson = \&getJson; + *Lemonldap::NG::Common::Conf::ua = \&ua; + *Lemonldap::NG::Common::Conf::base = \&base; +} + sub prereq { my $self = shift; unless ( $self->{baseUrl} ) { @@ -116,8 +122,8 @@ sub store { } sub load { - my $self = shift; - my $res = $self->getJson('latest?full') or return; + my ( $self, $cfgNum ) = @_; + my $res = $self->getJson("$cfgNum?full=1") or return; return $res; } diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/RESTServer.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/RESTServer.pm index 477e03473..f3f3ac722 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/RESTServer.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/RESTServer.pm @@ -41,8 +41,9 @@ sub getConfKey { if ( $req->params('cfgNum') eq 'latest' ) { my $tmp = $self->confAcc->lastCfg; $req->set_param( 'cfgNum', $tmp ); - if ($Lemonldap::NG::Common::Conf::msg) { - $req->error($Lemonldap::NG::Common::Conf::msg); + unless ($tmp) { + $req->error($Lemonldap::NG::Common::Conf::msg) + if ($Lemonldap::NG::Common::Conf::msg); return undef; } } @@ -68,7 +69,8 @@ sub getConfKey { sub getConfByNum { my ( $self, $cfgNum, @args ) = @_; - unless ( %{ $self->currentConf } + unless ($self->currentConf + and %{ $self->currentConf } and $cfgNum == $self->currentConf->{cfgNum} ) { my $tmp = $self->confAcc->getConf( @@ -619,10 +621,16 @@ sub metadatas { if ( $req->params('full') and $req->params('full') !~ NO ) { my $c = $self->getConfKey( $req, 'cfgNum' ); return $self->sendError( $req, undef, 400 ) if ( $req->error ); - $self->userLogger->notice( 'User ' - . $self->userId($req) - . ' ask for full configuration ' - . $c ); + if ( $self->can('userId') ) { + $self->userLogger->notice( 'User ' + . $self->userId($req) + . ' ask for full configuration ' + . $c ); + } + else { + $self->logger->info( + "REST request to get full configuration $c"); + } return $self->sendJSONresponse( $req, $self->currentConf, @@ -648,11 +656,17 @@ sub metadatas { my ($ind) = map { $id++; $_ == $res->{cfgNum} ? ($id) : () } @a; if ($ind) { $res->{prev} = $a[ $ind - 1 ]; } if ( $ind and $ind < $#a ) { $res->{next} = $a[ $ind + 1 ]; } - $self->userLogger->notice( 'User ' - . $self->userId($req) - . ' ask for configuration metadatas (' - . $res->{cfgNum} - . ')' ); + if ( $self->can('userId') ) { + $self->userLogger->info( 'User ' + . $self->userId($req) + . ' ask for configuration metadatas (' + . $res->{cfgNum} + . ')' ); + } + else { + $self->logger->info( + "REST request to get configuration metadatas ($res->{cfgNum})"); + } return $self->sendJSONresponse( $req, $res ); } } diff --git a/lemonldap-ng-portal/MANIFEST b/lemonldap-ng-portal/MANIFEST index 56f46c99c..b203974c7 100644 --- a/lemonldap-ng-portal/MANIFEST +++ b/lemonldap-ng-portal/MANIFEST @@ -309,6 +309,7 @@ t/32-Auth-and-issuer-OIDC-implicit.t t/33-Auth-and-issuer-OpenID2.t t/34-Auth-Proxy-and-REST-Server.t t/34-Auth-Proxy-and-SOAP-Server.t +t/35-REST-config-backend.t t/35-REST-sessions-with-REST-server.t t/35-SOAP-config-backend.t t/35-SOAP-sessions-with-SOAP-server.t diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm index c04e89012..01c3e2ac8 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm @@ -45,6 +45,13 @@ our $VERSION = '2.0.0'; extends 'Lemonldap::NG::Portal::Main::Plugin'; +has configStorage => ( + is => 'ro', + default => sub { + $_[0]->{p}->HANDLER->localConfig->{configStorage}; + } +); + has exportedAttr => ( is => 'rw', default => sub { diff --git a/lemonldap-ng-portal/t/35-REST-config-backend.t b/lemonldap-ng-portal/t/35-REST-config-backend.t new file mode 100644 index 000000000..6bdde0d8a --- /dev/null +++ b/lemonldap-ng-portal/t/35-REST-config-backend.t @@ -0,0 +1,203 @@ +use Test::More; +use strict; +use IO::String; + +BEGIN { + require 't/test-lib.pm'; +} + +my $debug = 'error'; +my ( $issuer, $sp, $res, $spId ); +my %handlerOR = ( issuer => [], sp => [] ); + +ok( $issuer = issuer(), 'Issuer portal' ); +$handlerOR{issuer} = \@Lemonldap::NG::Handler::Main::_onReload; + +# Test REST config backend +ok( $res = $issuer->_get('/confs/latest'), 'Get latest conf metadatas' ); +count(1); +expectOK($res); + +switch ('sp'); + +ok( $sp = sp(), 'SP portal' ); +$handlerOR{sp} = \@Lemonldap::NG::Handler::Main::_onReload; +count(2); + +# Simple SP access +ok( + $res = $sp->_get( + '/', accept => 'text/html', + ), + 'Unauth SP request' +); +expectOK($res); + +# Try to auth +ok( + $res = $sp->_post( + '/', IO::String->new('user=french&password=french'), + length => 27, + accept => 'text/html' + ), + 'Post user/password' +); +count(2); +expectRedirection( $res, 'http://auth.sp.com' ); +$spId = expectCookie($res); + +# Test auth +ok( $res = $sp->_get( '/', cookie => "lemonldap=$spId" ), 'Auth test' ); +count(1); +expectOK($res); + +# Test other REST queries +switch ('issuer'); + +# Session content +ok( $res = $issuer->_get("/sessions/global/$spId"), 'Session content' ); +expectOK($res); +ok( $res = eval { JSON::from_json( $res->[2]->[0] ) }, ' GET JSON' ) + or print STDERR $@; +ok( $res->{_session_id} eq $spId, ' Good ID' ) + or explain( $res, "_session_id => $spId" ); +count(3); + +# Session key +ok( $res = $issuer->_get("/sessions/global/$spId/[_session_id,uid,cn]"), + 'Some session keys' ); +expectOK($res); +ok( $res = eval { JSON::from_json( $res->[2]->[0] ) }, ' GET JSON' ) + or print STDERR $@; +ok( $res->{_session_id} eq $spId, ' Good ID' ) + or explain( $res, "_session_id => $spId" ); +ok( $res->{uid} eq 'french', ' Uid is french' ) + or explain( $res, 'uid => french' ); +ok( $res->{cn} eq 'Frédéric Accents', 'UTF-8 values' ); +count(5); + +# Logout +switch ('sp'); +ok( + $res = $sp->_get( + '/', + query => 'logout', + accept => 'text/html', + cookie => "lemonldap=$spId" + ), + 'Ask for logout' +); +count(1); +expectOK($res); + +# Test if user is reject on IdP +ok( + $res = $sp->_get( + '/', cookie => "lemonldap=$spId", + ), + 'Test if user is reject on IdP' +); +count(1); +expectReject($res); + +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.idp.com(.*?)(?:\?(.*))?$#, + ' @ REST request (' . $req->method . " $1)" + ); + count(1); + my $url = $1; + my $query = $2; + my $res; + my $s = $req->content; + if ( $req->method =~ /^(post|put)$/i ) { + my $mth = '_' . lc($1); + my $s = $req->content; + ok( + $res = $issuer->$mth( + $url, + IO::String->new($s), + length => length($s), + type => $req->header('Content-Type'), + ), + ' Post request' + ); + count(1); + expectOK($res); + } + elsif ( $req->method =~ /^(get|delete)$/i ) { + my $mth = '_' . lc($1); + ok( + $res = $issuer->$mth( + $url, + accept => $req->header('Accept'), + cookie => $req->header('Cookie'), + query => $query, + ), + ' Execute request' + ); + ok( ( $res->[0] == 200 or $res->[0] == 400 ), + ' Response is 200 or 400' ) + or explain( $res->[0], '200 or 400' ); + count(2); + } + my $httpResp; + $httpResp = HTTP::Response->new( $res->[0], 'OK' ); + + while ( my $name = shift @{ $res->[1] } ) { + $httpResp->header( $name, shift( @{ $res->[1] } ) ); + } + $httpResp->content( join( '', @{ $res->[2] } ) ); + pass(' @ END OF REST REQUEST'); + count(1); + return $httpResp; +} + +sub switch { + my $type = shift; + @Lemonldap::NG::Handler::Main::_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 => 'Same', + restSessionServer => 1, + restConfigServer => 1, + } + } + ); +} + +sub sp { + return LLNG::Manager::Test->new( + { + ini => { + logLevel => $debug, + domain => 'sp.com', + portal => 'http://auth.sp.com', + authentication => 'Demo', + userDB => 'Same', + configStorage => { + type => 'REST', + baseUrl => 'http://auth.idp.com/confs', + }, + }, + } + ); +} diff --git a/lemonldap-ng-portal/t/35-REST-sessions-with-REST-server.t b/lemonldap-ng-portal/t/35-REST-sessions-with-REST-server.t index 0f1d03693..102ce51f2 100644 --- a/lemonldap-ng-portal/t/35-REST-sessions-with-REST-server.t +++ b/lemonldap-ng-portal/t/35-REST-sessions-with-REST-server.t @@ -170,7 +170,6 @@ sub issuer { authentication => 'Demo', userDB => 'Same', restSessionServer => 1, - restConfigServer => 1, } } );