SAML in progress (#595)

To do: authSAML SOAP server
This commit is contained in:
Xavier Guimard 2016-12-17 07:58:53 +00:00
parent 8e2418ceb8
commit ec83414576
6 changed files with 187 additions and 28 deletions

View File

@ -19,10 +19,11 @@ our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::Main::Issuer',
'Lemonldap::NG::Portal::Lib::SAML';
has ssoUrlRe => ( is => 'rw' );
has sloRe => ( is => 'rw' );
has artRe => ( is => 'rw' );
has soapSloRe => ( is => 'rw' );
has ssoUrlRe => ( is => 'rw' );
has sloRe => ( is => 'rw' );
has artRe => ( is => 'rw' );
has soapSloRe => ( is => 'rw' );
has sloRelaySoapRe => ( is => 'rw' );
# INITIALIZATION
@ -76,6 +77,8 @@ qr/^($saml_sso_soap_url|$saml_sso_soap_url_ret|$saml_sso_get_url|$saml_sso_get_u
qr/^($saml_slo_soap_url|$saml_slo_soap_url_ret|$saml_slo_get_url|$saml_slo_get_url_ret|$saml_slo_post_url|$saml_slo_post_url_ret)(?:\?.*)?$/i
);
$self->sloRelaySoapRe(qr#^/saml/relaySingleLogoutSOAP(?:\?.*)?$#i);
return (
$self->Lemonldap::NG::Portal::Main::Issuer::init()
@ -104,7 +107,20 @@ sub _pRedirect {
return $self->soapSloServer($req);
}
else {
return $self->SUPER::_pRedirect($req);
$req->parseBody;
return $self->SUPER::_redirect($req);
}
}
# Override _redirect to catch SLO relay
sub _redirect {
my ( $self, $req ) = @_;
if ( $req->uri =~ $self->sloRelaySoapRe ) {
return $self->sloRelaySoap($req);
}
else {
return $self->SUPER::_redirect($req);
}
}
@ -1461,6 +1477,105 @@ sub logout {
return PE_OK;
}
sub sloRelaySoap {
my ( $self, $req ) = @_;
$self->lmLog( "URL " . $req->uri . " detected as a SOAP relay service URL",
'debug' );
# Check if relay parameter is present (mandatory)
my $relayID;
unless ( $relayID = $req->param('relay') ) {
$self->lmLog( "No relayID detected", 'error' );
return $self->imgnok($req);
}
# Retrieve the corresponding data from samlStorage
my $relayInfos = $self->getSamlSession($relayID);
unless ($relayInfos) {
$self->lmLog( "Could not get relay session $relayID", 'error' );
return $self->imgnok($req);
}
$self->lmLog( "Found relay session $relayID", 'debug' );
# Rebuild the logout object
my $logout;
unless ( $logout = $self->createLogout( $self->lassoServer ) ) {
$self->lmLog( "Could not rebuild logout object", 'error' );
return $self->imgnok($req);
}
# Load Session and Identity if they exist
my $session = $relayInfos->data->{_lassoSessionDump};
my $identity = $relayInfos->data->{_lassoIdentityDump};
my $providerID = $relayInfos->data->{_providerID};
my $relayState = $relayInfos->data->{_relayState};
my $spConfKey = $self->spList->{$providerID}->{confKey};
if ($session) {
unless ( $self->setSessionFromDump( $logout, $session ) ) {
$self->lmLog( "Unable to load Lasso Session", 'error' );
return $self->imgnok($req);
}
$self->lmLog( "Lasso Session loaded", 'debug' );
}
if ($identity) {
unless ( $self->setIdentityFromDump( $logout, $identity ) ) {
$self->lmLog( "Unable to load Lasso Identity", 'error' );
return $self->imgnok($req);
}
$self->lmLog( "Lasso Identity loaded", 'debug' );
}
# Send the logout request
my ( $rstatus, $rmethod, $rinfo ) =
$self->sendLogoutRequestToProvider( $req, $logout, $providerID,
Lasso::Constants::HTTP_METHOD_SOAP );
unless ($rstatus) {
$self->lmLog( "Fail to process SOAP logout request to $providerID",
'error' );
return $self->imgnok($req);
}
# Store success status for this SLO request
my $sloStatusSessionInfos = $self->getSamlSession($relayState);
if ($sloStatusSessionInfos) {
$sloStatusSessionInfos->update( { $spConfKey => 1 } );
$self->lmLog( "Store SLO status for $spConfKey in session $relayState",
'debug' );
}
else {
$self->lmLog(
"Unable to store SLO status for $spConfKey in session $relayState",
'warn'
);
}
# Delete relay session
$relayInfos->remove();
# SLO response is OK
$self->lmLog( "Display OK status for SLO on $spConfKey", 'debug' );
return $self->imgok($req);
}
# INTERNAL METHODS
sub imgok {
my ( $self, $req, ) = @_;
return $self->sendImage( $req, 'ok.png' );
}
sub imgnok {
my ( $self, $req, ) = @_;
return $self->sendImage( $req, 'warning.png' );
}
sub sendImage {
my ( $self, $req,, $img ) = @_;
return $self->p->staticFile( $req, "common/$img", 'image/png' );
}
1;

View File

@ -61,15 +61,8 @@ sub display {
# 1. Good authentication
# 1.1 Image mode
if ( $req->{error} == PE_IMG_OK || $req->{error} == PE_IMG_NOK ) {
$self->lmLog( 'Request for file', 'debug' );
return staticFile( "common/"
. ( $req->{error} == PE_IMG_OK ? 'ok.png' : 'warning.png' ) );
}
# 1.2 Case : there is a message to display
elsif ( my $info = $req->info() ) {
# 1.1 Case : there is a message to display
if ( my $info = $req->info() ) {
$skinfile = 'info';
%templateParams = (
AUTH_ERROR_TYPE => $req->error_type,
@ -81,7 +74,7 @@ sub display {
);
}
# 1.3 Redirection
# 1.2 Redirection
elsif ( $req->{error} == PE_REDIRECT ) {
$skinfile = "redirect";
%templateParams = (
@ -91,7 +84,7 @@ sub display {
);
}
# 1.4 Case : display menu
# 1.3 Case : display menu
elsif ( $req->error == PE_OK ) {
$skinfile = 'menu';
@ -110,8 +103,8 @@ sub display {
# 2. Authentication not complete
# 2.1 A notification has to be done (session is created but hidden and unusable
# until the user has accept the message)
# 2.1 A notification has to be done (session is created but hidden and
# unusable until the user has accept the message)
elsif ( my $notif = $req->datas->{notification} ) {
$skinfile = 'notification';
%templateParams = (
@ -344,12 +337,12 @@ sub display {
# @param $type The content-type to use (ie: image/png)
# @return void
sub staticFile {
my ( $self, $file, $type ) = @_;
my ( $self, $req, $file, $type ) = @_;
require Plack::Util;
require Cwd;
require HTTP::Date;
open my $fh, '<:raw', $self->conf->{templatesDir} . "/$file"
or return $self->sendError( $!, 403 );
or return $self->sendError( $req, $!, 403 );
my @stat = stat $file;
Plack::Util::set_io_path( $fh, Cwd::realpath($file) );
return [

View File

@ -127,7 +127,9 @@ sub loginInfo {
}
sub info {
print STDERR "TODO Request::info()\n";
my ( $self, $info ) = @_;
$self->datas->{_info} .= $info if ( defined $info );
return $self->datas->{_info};
}
# TODO: oldpassword

View File

@ -583,8 +583,8 @@ sub getFirstValue {
}
sub info {
my($self,$req,$info)=@_;
print STDERR "####### TODO: info()\n";
my ( $self, $req, $info ) = @_;
return $req->info($info);
}
1;

View File

@ -7,7 +7,7 @@ BEGIN {
require 't/test-lib.pm';
}
my $maintests = 28;
my $maintests = 26;
my $debug = 'error';
my ( $issuer, $sp, $res );
my %handlerOR = ( issuer => [], sp => [] );
@ -220,7 +220,7 @@ sub LWP::UserAgent::request {
$httpResp->header( $name, shift( @{ $res->[1] } ) );
}
$httpResp->content( join( '', @{ $res->[2] } ) );
count(3);
count(4);
return $httpResp;
}

View File

@ -7,7 +7,7 @@ BEGIN {
require 't/test-lib.pm';
}
my $maintests = 18;
my $maintests = 21;
my $debug = 'debug';
my ( $issuer, $sp, $res );
my %handlerOR = ( issuer => [], sp => [] );
@ -104,17 +104,65 @@ SKIP: {
cookie => "lemonldap=$idpId",
accept => 'text/html'
),
'Query SP for logout'
'Query IdP for logout'
);
ok( $res->[0] == 200, 'Return code is 200' );
#print STDERR Dumper($res);
ok(
$res->[2]->[0] =~
m#img src="http://auth.idp.com(/saml/relaySingleLogoutSOAP)\?(relay=.*?)"#s,
'Get image request'
);
ok(
$res = $issuer->_get(
$1,
query => $2,
#cookie => "lemonldap=$idpId",
accept => 'text/html'
),
'Get image'
);
ok( $issuer->getHeader( $res, 'Content-Type' ) eq 'image/png',
'Get an image' );
}
count($maintests);
clean_sessions();
done_testing( count() );
# Redefine LWP methods for tests
sub LWP::UserAgent::request {
my ( $self, $req ) = @_;
ok( $req->uri =~ m#http://auth.sp.com(.*)#, 'Request from SP to IdP' );
my $url = $1;
my $res;
my $s = $req->content;
ok(
$res = $sp->_post(
$url, IO::String->new($s),
length => length($s),
type => 'application/xml',
),
'Execute request'
);
#ok( ( $res->[0] == 200 or $res->[0] == 400 ), 'Response is 200 or 400' )
# or explain( $res->[0], "200 or 400" );
#ok( $issuer->getHeader( $res, 'Content-Type' ) =~ m#^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(2);
return $httpResp;
}
sub switch {
my $type = shift;
@Lemonldap::NG::Handler::Main::Reload::_onReload = @{
@ -127,6 +175,7 @@ sub issuer {
{
ini => {
logLevel => $debug,
templatesDir => 'site/htdocs/static',
domain => 'idp.com',
portal => 'http://auth.idp.com',
authentication => 'Demo',