From 71f142316fdf64499f6b79a1f816f6d563829478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Oudot?= Date: Fri, 12 Feb 2010 10:53:43 +0000 Subject: [PATCH] SAML: * IDP metadata are in metadata key * Use IDP internal ID instead of entityID to keep choosen IDP information * Use base64 encoding for RelayState value --- .../lib/Lemonldap/NG/Portal/AuthSAML.pm | 45 ++++++++++++---- .../lib/Lemonldap/NG/Portal/_SAML.pm | 53 ++++++++++++++++--- 2 files changed, 79 insertions(+), 19 deletions(-) diff --git a/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthSAML.pm b/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthSAML.pm index d73b88ca3..49fe71684 100644 --- a/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthSAML.pm +++ b/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthSAML.pm @@ -60,7 +60,7 @@ sub authInit { # Load identity provider metadata # IDP are listed in $self->{samlIDPMetaData} - # Each key is the IDP name and value is the metadata + # Each key is the IDP name and value is the metadata key # Build IDP list for later use in extractFormInfo foreach ( keys %{ $self->{samlIDPMetaData} } ) { @@ -70,7 +70,7 @@ sub authInit { my $idp_metadata = Lemonldap::NG::Common::Conf::SAML::Metadata->new(); unless ( $idp_metadata->initializeFromConfHash( - $self->{samlIDPMetaData}->{$_} + $self->{samlIDPMetaData}->{$_}->{metadata} ) ) { @@ -173,7 +173,24 @@ sub extractFormInfo { # TODO check conditions - # TODO get urldc in RelayState + # Extract RelayState information + if ( $self->extractRelayState($login) ) { + $self->lmLog( "RelayState found in authentication assertion", + 'debug' ); + } + + # Check IDP + my $idp = $self->{_idp}; + unless ($idp) { + $self->lmLog( "IDP was not found in RelayState", 'error' ); + return PE_ERROR; + } + + $self->lmLog( "IDP $idp found in RelayState", 'debug' ); + + # Force redirection to portal if no urldc found + # (avoid displaying the whole SAML URL in user browser URL field) + $self->{mustRedirect} = 1 unless ( $self->{urldc} ); # Get NameID my $nameid = $login->nameIdentifier; @@ -226,8 +243,8 @@ sub extractFormInfo { foreach ( keys %{ $self->{_idpList} } ) { $html .= - '' . $self->{_idpList}->{$_}->{name} . ''; @@ -266,7 +283,11 @@ sub extractFormInfo { my $html = '

' . &Lemonldap::NG::Portal::_i18n::msg( PM_SAML_IDPCHOOSEN, $ENV{HTTP_ACCEPT_LANGUAGE} ) - . "

\n

$idp

\n\n"; + . "\n" + . "

$idp

\n" . "
(" + . $self->{_idpList}->{$idp}->{entityID} + . ")
\n" + . "\n"; $self->info($html); @@ -293,10 +314,12 @@ sub extractFormInfo { } # 3. Build authentication request - $login = $self->createAuthnRequest( $server, $idp ); + $self->{_idp} = $idp; + my $IDPentityID = $self->{_idpList}->{$idp}->{entityID}; + $login = $self->createAuthnRequest( $server, $IDPentityID ); unless ($login) { - $self->lmLog( "Could not create authentication request on $idp", + $self->lmLog( "Could not create authentication request on $IDPentityID", 'error' ); return PE_ERROR; } @@ -319,7 +342,7 @@ sub extractFormInfo { # Extract attributes sent in authentication statement # @return Lemonldap::NG::Portal error code sub setAuthSessionInfo { - my $self = shift; + my $self = shift; my $server = $self->{_lassoServer}; my $login = $self->{_lassoLogin}; @@ -327,7 +350,7 @@ sub setAuthSessionInfo { # Store other informations in session $self->{sessionInfo}->{_user} = $self->{user}; - $self->{sessionInfo}->{_idp} = $login->remote_providerID; + $self->{sessionInfo}->{_idp} = $self->{_idp}; # TODO adapt _utime with SessionNotOnOrAfter @@ -338,7 +361,7 @@ sub setAuthSessionInfo { # Accept SSO from IDP # @return PE_OK sub authenticate { - my $self = shift; + my $self = shift; my $server = $self->{_lassoServer}; my $login = $self->{_lassoLogin}; diff --git a/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_SAML.pm b/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_SAML.pm index 97aed2cce..ca77ad3b5 100644 --- a/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_SAML.pm +++ b/modules/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_SAML.pm @@ -8,12 +8,13 @@ package Lemonldap::NG::Portal::_SAML; use strict; use base qw(Exporter); use XML::Simple; +use MIME::Base64; our @EXPORT = qw( loadLasso checkLassoError createServer addIDP addProvider getOrganizationName createAuthnRequest createLogin getHttpMethod initAuthnRequest buildAuthnRequestMsg processAuthnResponseMsg getNameIdentifier - createIdentity createSession acceptSSO + createIdentity createSession acceptSSO extractRelayState ); our $VERSION = '0.01'; @@ -208,6 +209,9 @@ sub getOrganizationName { return; } + # Return if node is empty + return unless $node; + # Extract organization name my $xs = XML::Simple->new(); my $data = $xs->XMLin($node); @@ -242,12 +246,15 @@ sub createAuthnRequest { return; } - # Set urldc in RelayState - my $relaystate = $self->{urldc}; - if ($relaystate) { - $login->msg_relayState($relaystate); - $self->lmLog( "Set $relaystate in RelayState", 'debug' ); + # Set RelayState as key1;value1;key2;value2;... encoded in base 64 + my $relaystate; + foreach (qw /_idp urldc/) { + $relaystate .= $_ . ";" . $self->{$_} . ";" if $self->{$_}; } + $relaystate =~ s/;$//; + $relaystate = encode_base64( $relaystate, '' ); + $login->msg_relayState($relaystate); + $self->lmLog( "Set $relaystate in RelayState", 'debug' ); # Customize request my $request = $login->request(); @@ -276,7 +283,7 @@ sub createLogin { eval { $login = Lasso::Login->new($server); }; if ($@) { - $self->checkLassoError($@); + $self->checkLassoError($@); return; } @@ -413,6 +420,33 @@ sub acceptSSO { return $self->checkLassoError($@); } +## @method boolean extractRelayState(Lasso::Login login) +# Extract RelayState information into $self +## @param login Lasso::Login object +## @return result +sub extractRelayState { + my ( $self, $login ) = splice @_; + + # Get relayState in assertion + my $relaystate = $login->msg_relayState; + + return 0 unless $relaystate; + + # Decode base64 + $relaystate = decode_base64($relaystate); + + # Recover values + my @values = split( /;/, $relaystate ); + + # Push values in $self + my $i; + for ( $i = 0, $i < $#values, $i++ ) { + $self->{ $values[$i] } = $values[ $i++ ]; + } + + return 1; +} + 1; __END__ @@ -498,6 +532,10 @@ Create Lasso::Session object Accept SSO from IDP +=head2 extractRelayState + +Extract RelayState information into $self + =head1 SEE ALSO L, L @@ -515,5 +553,4 @@ This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.0 or, at your option, any later version of Perl 5 you may have available. - =cut