diff --git a/fastcgi-server/man/llng-fastcgi-server.8p b/fastcgi-server/man/llng-fastcgi-server.8p index 37a70d6ca..3bc402340 100644 --- a/fastcgi-server/man/llng-fastcgi-server.8p +++ b/fastcgi-server/man/llng-fastcgi-server.8p @@ -129,7 +129,7 @@ .\" ======================================================================== .\" .IX Title "llng-fastcgi-server 8" -.TH llng-fastcgi-server 8 "2019-09-24" "perl v5.28.1" "User Contributed Perl Documentation" +.TH llng-fastcgi-server 8 "2019-10-30" "perl v5.26.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/lemonldap-ng-common/lemonldap-ng.ini b/lemonldap-ng-common/lemonldap-ng.ini index 6bf6f201d..746ca7a15 100644 --- a/lemonldap-ng-common/lemonldap-ng.ini +++ b/lemonldap-ng-common/lemonldap-ng.ini @@ -305,9 +305,10 @@ languages = en, fr, vi, it, ar, de, fi ; Read Lemonldap::NG::Portal::Main::Plugin(3pm) man page. ;customPlugins = My::Package1, My::Package2 -; To avoid bad/expired OTT if authssl and auth are served by different Load Balancers -; you can override OTT configuration to store Upgrade OTT into global storage +; To avoid bad/expired OTT if "authssl" and "auth" are served by different Load Balancers +; you can override OTT configuration to store Upgrade or Issuer OTT into global storage ;forceGlobalStorageUpgradeOTT = 1 +;forceGlobalStorageIssuerOTT = 1 [handler] diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/DefaultValues.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/DefaultValues.pm index 26c9f2493..05bdc52db 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/DefaultValues.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/DefaultValues.pm @@ -260,8 +260,7 @@ sub defaultValues { 'samlAuthnContextMapPassword' => 2, 'samlAuthnContextMapPasswordProtectedTransport' => 3, 'samlAuthnContextMapTLSClient' => 5, - 'samlEntityID' => '#PORTAL#/saml/metadata', - 'samlIdPResolveCookie' => 'lemonldapidp', + 'samlEntityID' => '#PORTAL#/saml/metadata', 'samlIDPSSODescriptorArtifactResolutionServiceArtifact' => '1;0;urn:oasis:names:tc:SAML:2.0:bindings:SOAP;#PORTAL#/saml/artifact', 'samlIDPSSODescriptorSingleLogoutServiceHTTPPost' => 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 6a854c69c..5357e4112 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/RESTServer.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/RESTServer.pm @@ -195,9 +195,11 @@ sub virtualHosts { type => 'keyText', }; - # If rule contains a comment, split it + # If rule contains a comment or an AuthLevel, split them if ( $query eq 'locationRules' ) { $res->{comment} = ''; + $res->{level} = ''; + $res->{level} = $1 if ( $r =~ s/\(\?#AuthnLevel=(-?\d+)\)// ); if ( $r =~ s/\(\?#(.*?)\)// ) { $res->{title} = $res->{comment} = $1; } diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/ReConstants.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/ReConstants.pm index d927b983d..db4f07ebd 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/ReConstants.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/ReConstants.pm @@ -67,7 +67,7 @@ our $issuerParameters = { issuerDBSAML => [qw(issuerDBSAMLActivation issuerDBSAMLPath issuerDBSAMLRule)], issuerOptions => [qw(issuersTimeout)], }; -our $samlServiceParameters = [qw(samlEntityID samlServicePrivateKeySig samlServicePrivateKeySigPwd samlServicePublicKeySig samlServicePrivateKeyEnc samlServicePrivateKeyEncPwd samlServicePublicKeyEnc samlServiceUseCertificateInResponse samlServiceSignatureMethod samlNameIDFormatMapEmail samlNameIDFormatMapX509 samlNameIDFormatMapWindows samlNameIDFormatMapKerberos samlAuthnContextMapPassword samlAuthnContextMapPasswordProtectedTransport samlAuthnContextMapTLSClient samlAuthnContextMapKerberos samlOrganizationDisplayName samlOrganizationName samlOrganizationURL samlSPSSODescriptorAuthnRequestsSigned samlSPSSODescriptorWantAssertionsSigned samlSPSSODescriptorSingleLogoutServiceHTTPRedirect samlSPSSODescriptorSingleLogoutServiceHTTPPost samlSPSSODescriptorSingleLogoutServiceSOAP samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact samlSPSSODescriptorAssertionConsumerServiceHTTPPost samlSPSSODescriptorArtifactResolutionServiceArtifact samlIDPSSODescriptorWantAuthnRequestsSigned samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect samlIDPSSODescriptorSingleSignOnServiceHTTPPost samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect samlIDPSSODescriptorSingleLogoutServiceHTTPPost samlIDPSSODescriptorSingleLogoutServiceSOAP samlIDPSSODescriptorArtifactResolutionServiceArtifact samlAttributeAuthorityDescriptorAttributeServiceSOAP samlIdPResolveCookie samlMetadataForceUTF8 samlStorage samlStorageOptions samlRelayStateTimeout samlUseQueryStringSpecific samlCommonDomainCookieActivation samlCommonDomainCookieDomain samlCommonDomainCookieReader samlCommonDomainCookieWriter samlDiscoveryProtocolActivation samlDiscoveryProtocolURL samlDiscoveryProtocolPolicy samlDiscoveryProtocolIsPassive samlOverrideIDPEntityID)]; +our $samlServiceParameters = [qw(samlEntityID samlServicePrivateKeySig samlServicePrivateKeySigPwd samlServicePublicKeySig samlServicePrivateKeyEnc samlServicePrivateKeyEncPwd samlServicePublicKeyEnc samlServiceUseCertificateInResponse samlServiceSignatureMethod samlNameIDFormatMapEmail samlNameIDFormatMapX509 samlNameIDFormatMapWindows samlNameIDFormatMapKerberos samlAuthnContextMapPassword samlAuthnContextMapPasswordProtectedTransport samlAuthnContextMapTLSClient samlAuthnContextMapKerberos samlOrganizationDisplayName samlOrganizationName samlOrganizationURL samlSPSSODescriptorAuthnRequestsSigned samlSPSSODescriptorWantAssertionsSigned samlSPSSODescriptorSingleLogoutServiceHTTPRedirect samlSPSSODescriptorSingleLogoutServiceHTTPPost samlSPSSODescriptorSingleLogoutServiceSOAP samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact samlSPSSODescriptorAssertionConsumerServiceHTTPPost samlSPSSODescriptorArtifactResolutionServiceArtifact samlIDPSSODescriptorWantAuthnRequestsSigned samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect samlIDPSSODescriptorSingleSignOnServiceHTTPPost samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect samlIDPSSODescriptorSingleLogoutServiceHTTPPost samlIDPSSODescriptorSingleLogoutServiceSOAP samlIDPSSODescriptorArtifactResolutionServiceArtifact samlAttributeAuthorityDescriptorAttributeServiceSOAP samlMetadataForceUTF8 samlStorage samlStorageOptions samlRelayStateTimeout samlUseQueryStringSpecific samlCommonDomainCookieActivation samlCommonDomainCookieDomain samlCommonDomainCookieReader samlCommonDomainCookieWriter samlDiscoveryProtocolActivation samlDiscoveryProtocolURL samlDiscoveryProtocolPolicy samlDiscoveryProtocolIsPassive samlOverrideIDPEntityID)]; our $oidcServiceParameters = [qw(oidcServiceMetaDataAuthorizeURI oidcServiceMetaDataTokenURI oidcServiceMetaDataUserInfoURI oidcServiceMetaDataJWKSURI oidcServiceMetaDataRegistrationURI oidcServiceMetaDataIntrospectionURI oidcServiceMetaDataEndSessionURI oidcServiceMetaDataCheckSessionURI oidcServiceMetaDataFrontChannelURI oidcServiceMetaDataBackChannelURI oidcServiceMetaDataAuthnContext oidcServicePrivateKeySig oidcServicePublicKeySig oidcServiceKeyIdSig oidcServiceAllowDynamicRegistration oidcServiceAllowAuthorizationCodeFlow oidcServiceAllowImplicitFlow oidcServiceAllowHybridFlow oidcStorage oidcStorageOptions)]; 1; diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/SecureToken.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/SecureToken.pm index 67b1ce02e..d62dd0e02 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/SecureToken.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/SecureToken.pm @@ -43,11 +43,11 @@ sub run { # Catch Secure Token parameters my $localConfig = $class->localConfig; - my $secureTokenMemcachedServers = + our $secureTokenMemcachedServers = $localConfig->{secureTokenMemcachedServers} || ['127.0.0.1:11211']; my $secureTokenExpiration = $localConfig->{secureTokenExpiration} || 60; my $secureTokenAttribute = $localConfig->{secureTokenAttribute} || 'uid'; - my $secureTokenUrls = $localConfig->{'secureTokenUrls'} || ['.*']; + our $secureTokenUrls = $localConfig->{'secureTokenUrls'} || ['.*']; my $secureTokenHeader = $localConfig->{secureTokenHeader} || 'Auth-Token'; my $secureTokenAllowOnError = $localConfig->{'secureTokenAllowOnError'} // 1; diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Reload.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Reload.pm index 6d1a809fd..4c05cf990 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Reload.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Reload.pm @@ -281,6 +281,7 @@ sub locationRulesInit { $class->tsv->{locationProtection}->{$vhost} = []; $class->tsv->{locationRegexp}->{$vhost} = []; $class->tsv->{locationConditionText}->{$vhost} = []; + $class->tsv->{locationAuthnLevel}->{$vhost} = []; foreach my $url ( sort keys %{$rules} ) { my ( $cond, $prot ) = $class->conditionSub( $rules->{$url} ); @@ -300,10 +301,14 @@ sub locationRulesInit { push @{ $class->tsv->{locationCondition}->{$vhost} }, $cond; push @{ $class->tsv->{locationProtection}->{$vhost} }, $prot; push @{ $class->tsv->{locationRegexp}->{$vhost} }, qr/$url/; + push @{ $class->tsv->{locationAuthnLevel}->{$vhost} }, + $url =~ /\(\?#AuthnLevel=(-?\d+)\)/ + ? $1 + : undef; push @{ $class->tsv->{locationConditionText}->{$vhost} }, $url =~ /^\(\?#(.*?)\)/ ? $1 : $url =~ /^(.*?)##(.+)$/ ? $2 - : $url; + : $url; $class->tsv->{locationCount}->{$vhost}++; } } @@ -451,6 +456,7 @@ sub postUrlInit { # @return array (ref(sub), int) sub conditionSub { my ( $class, $cond ) = @_; + $cond =~ s/\(\?#(\d+)\)$//; my ( $OK, $NOK ) = ( sub { 1 }, sub { 0 } ); # Simple cases : accept and deny diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm index eeb2fd1eb..cd0bcd5be 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm @@ -267,10 +267,31 @@ sub checkMaintenanceMode { # @return True if the user is granted to access to the current URL sub grant { my ( $class, $req, $session, $uri, $cond, $vhost ) = @_; + my $level; + return $cond->( $req, $session ) if ($cond); $vhost ||= $class->resolveAlias($req); - if ( my $level = $class->tsv->{authnLevel}->{$vhost} ) { + + # Using URL authentification level if exists + for ( + my $i = 0 ; + $i < ( $class->tsv->{locationCount}->{$vhost} || 0 ) ; + $i++ + ) + { + if ( $uri =~ $class->tsv->{locationRegexp}->{$vhost}->[$i] ) { + $level = $class->tsv->{locationAuthnLevel}->{$vhost}->[$i]; + last; + } + } + $level + ? $class->logger->debug( + 'Found AuthnLevel=' . $level . ' for "' . "$vhost$uri" . '"' ) + : $class->logger->debug("No URL authentication level found..."); + + # Using VH authentification level if exists + if ( $level ||= $class->tsv->{authnLevel}->{$vhost} ) { if ( $session->{authenticationLevel} < $level ) { $class->logger->debug( "User authentication level = $session->{authenticationLevel}"); diff --git a/lemonldap-ng-handler/t/60-Lemonldap-NG-Handler-PSGI.t b/lemonldap-ng-handler/t/60-Lemonldap-NG-Handler-PSGI.t index c0764b8c8..5e6020a92 100644 --- a/lemonldap-ng-handler/t/60-Lemonldap-NG-Handler-PSGI.t +++ b/lemonldap-ng-handler/t/60-Lemonldap-NG-Handler-PSGI.t @@ -10,6 +10,7 @@ init('Lemonldap::NG::Handler::PSGI'); my $res; # Unauthentified query +# -------------------- ok( $res = $client->_get('/'), 'Unauthentified query' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); @@ -24,17 +25,14 @@ ok( 'Location => http://auth.example.com/?url=' . encode_base64( 'http://test1.example.com/', '' ) ); - count(4); # Authentified queries # -------------------- - # Authorized query ok( $res = $client->_get( '/', undef, undef, "lemonldap=$sessionId" ), 'Authentified query' ); ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 ); - count(2); ok( $res = $client->_get( '/user_dwho/', undef, undef, "lemonldap=$sessionId" ), @@ -47,7 +45,12 @@ count(2); ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ), 'Denied query' ); ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 ); +count(2); +# Required AuthnLevel = 1 +ok( $res = $client->_get( '/AuthWeak', undef, undef, "lemonldap=$sessionId" ), + 'Weak Authentified query' ); +ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 ); count(2); ok( $res = $client->_get( '/user_rtyler/', undef, undef, "lemonldap=$sessionId" ), @@ -56,6 +59,25 @@ ok( $res->[0] == 403, 'Code is 403' ) or explain( $res, 403 ); count(2); +# Required AuthnLevel = 5 +ok( + $res = $client->_get( '/AuthStrong', undef, undef, "lemonldap=$sessionId" ), + 'Strong Authentified query' +); +ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 ); +%h = @{ $res->[1] }; +ok( + $h{Location} eq 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test1.example.com/AuthStrong', '' ), + 'Redirection points to http://test1.example.com/AuthStrong' + ) + or explain( + \%h, + 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test1.example.com/AuthStrong', '' ) + ); +count(3); + # Bad cookie ok( $res = $client->_get( @@ -70,9 +92,38 @@ ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); unlink( 't/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock' ); - count(2); +# Required AuthnLevel = 1 +ok( + $res = $client->_get( + '/AuthWeak', undef, 'test2.example.com', "lemonldap=$sessionId" + ), + 'Weak Authentified query' +); +ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 ); +count(2); + +# Required AuthnLevel = 5 +ok( + $res = + $client->_get( '/', undef, 'test2.example.com', "lemonldap=$sessionId" ), + 'Default Authentified query' +); +ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 ); +%h = @{ $res->[1] }; +ok( + $h{Location} eq 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test2.example.com/', '' ), + 'Redirection points to http://test2.example.com/' + ) + or explain( + \%h, + 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test2.example.com/', '' ) + ); +count(3); + done_testing( count() ); clean(); diff --git a/lemonldap-ng-handler/t/61-Lemonldap-NG-Handler-PSGI-Server.t b/lemonldap-ng-handler/t/61-Lemonldap-NG-Handler-PSGI-Server.t index 2fb6f0cf6..03b8f52fd 100644 --- a/lemonldap-ng-handler/t/61-Lemonldap-NG-Handler-PSGI-Server.t +++ b/lemonldap-ng-handler/t/61-Lemonldap-NG-Handler-PSGI-Server.t @@ -9,6 +9,7 @@ init('Lemonldap::NG::Handler::Server'); my $res; # Unauthentified query +# -------------------- ok( $res = $client->_get('/'), 'Unauthentified query' ); ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); @@ -23,17 +24,14 @@ ok( 'Location => http://auth.example.com/?url=' . encode_base64( 'http://test1.example.com/', '' ) ); - count(4); # Authentified queries # -------------------- - # Authorized query ok( $res = $client->_get( '/', undef, undef, "lemonldap=$sessionId" ), 'Authentified query' ); ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 ); - count(2); # Check headers @@ -46,9 +44,33 @@ count(1); ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ), 'Denied query' ); ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 ); - count(2); +# Required AuthnLevel = 1 +ok( $res = $client->_get( '/AuthWeak', undef, undef, "lemonldap=$sessionId" ), + 'Weak Authentified query' ); +ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 ); +count(2); + +# Required AuthnLevel = 5 +ok( + $res = $client->_get( '/AuthStrong', undef, undef, "lemonldap=$sessionId" ), + 'Strong Authentified query' +); +ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 ); +%h = @{ $res->[1] }; +ok( + $h{Location} eq 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test1.example.com/AuthStrong', '' ), + 'Redirection points to http://test1.example.com/AuthStrong' + ) + or explain( + \%h, + 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test1.example.com/AuthStrong', '' ) + ); +count(3); + # Bad cookie ok( $res = $client->_get( @@ -63,9 +85,38 @@ ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); unlink( 't/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock' ); - count(2); +# Required AuthnLevel = 1 +ok( + $res = $client->_get( + '/AuthWeak', undef, 'test2.example.com', "lemonldap=$sessionId" + ), + 'Weak Authentified query' +); +ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 ); +count(2); + +# Required AuthnLevel = 5 +ok( + $res = + $client->_get( '/', undef, 'test2.example.com', "lemonldap=$sessionId" ), + 'Default Authentified query' +); +ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 ); +%h = @{ $res->[1] }; +ok( + $h{Location} eq 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test2.example.com/', '' ), + 'Redirection points to http://test2.example.com/' + ) + or explain( + \%h, + 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test2.example.com/', '' ) + ); +count(3); + done_testing( count() ); clean(); diff --git a/lemonldap-ng-handler/t/62-Lemonldap-NG-Handler-Nginx.t b/lemonldap-ng-handler/t/62-Lemonldap-NG-Handler-Nginx.t index 148ceef80..784af80e8 100644 --- a/lemonldap-ng-handler/t/62-Lemonldap-NG-Handler-Nginx.t +++ b/lemonldap-ng-handler/t/62-Lemonldap-NG-Handler-Nginx.t @@ -34,7 +34,6 @@ count(4); ok( $res = $client->_get( '/', undef, undef, "lemonldap=$sessionId" ), 'Authentified query' ); ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 ); - count(2); # Check headers @@ -49,9 +48,33 @@ count(2); ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ), 'Denied query' ); ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 ); - count(2); +# Required AuthnLevel = 1 +ok( $res = $client->_get( '/AuthWeak', undef, undef, "lemonldap=$sessionId" ), + 'Weak Authentified query' ); +ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 ); +count(2); + +# Required AuthnLevel = 5 +ok( + $res = $client->_get( '/AuthStrong', undef, undef, "lemonldap=$sessionId" ), + 'Strong Authentified query' +); +ok( $res->[0] == 401, 'Code is 401' ) or explain( $res, 401 ); +%h = @{ $res->[1] }; +ok( + $h{Location} eq 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test1.example.com/AuthStrong', '' ), + 'Redirection points to http://test1.example.com/AuthStrong' + ) + or explain( + \%h, + 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test1.example.com/AuthStrong', '' ) + ); +count(3); + # Bad cookie ok( $res = $client->_get( @@ -66,9 +89,38 @@ ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 ); unlink( 't/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock' ); - count(2); +# Required AuthnLevel = 1 +ok( + $res = $client->_get( + '/AuthWeak', undef, 'test2.example.com', "lemonldap=$sessionId" + ), + 'Weak Authentified query' +); +ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 ); +count(2); + +# Required AuthnLevel = 5 +ok( + $res = + $client->_get( '/', undef, 'test2.example.com', "lemonldap=$sessionId" ), + 'Default Authentified query' +); +ok( $res->[0] == 401, 'Code is 401' ) or explain( $res, 401 ); +%h = @{ $res->[1] }; +ok( + $h{Location} eq 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test2.example.com/', '' ), + 'Redirection points to http://test2.example.com/' + ) + or explain( + \%h, + 'http://auth.example.com//upgradesession?url=' + . encode_base64( 'http://test2.example.com/', '' ) + ); +count(3); + done_testing( count() ); clean(); diff --git a/lemonldap-ng-handler/t/lmConf-1.json b/lemonldap-ng-handler/t/lmConf-1.json index bc031dd3f..e4c00d3be 100644 --- a/lemonldap-ng-handler/t/lmConf-1.json +++ b/lemonldap-ng-handler/t/lmConf-1.json @@ -41,12 +41,15 @@ "default": "$uid eq \"dwho\"" }, "test1.example.com": { + "^/AuthStrong(?#AuthnLevel=5)": "accept", + "^/AuthWeak(?#AuthnLevel=1)": "accept", "^/logout": "logout_sso", "^/deny": "deny", "^/user_(\\w+)/": "$uid eq $_rulematch[1]", "default": "accept" }, "test2.example.com": { + "^/AuthWeak(?#AuthnLevel=1)": "accept", "^/logout": "logout_sso", "default": "accept" }, @@ -61,5 +64,10 @@ "portal": "http://auth.example.com/", "reloadUrls": {}, "userDB": "Demo", + "vhostOptions": { + "test2.example.com": { + "vhostAuthnLevel": 5 + } + }, "whatToTrace": "_whatToTrace" } diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Attributes.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Attributes.pm index 0ecb91a87..a97196112 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Attributes.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Attributes.pm @@ -1254,6 +1254,9 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][ 'default' => 3, 'type' => 'int' }, + 'forceGlobalStorageIssuerOTT' => { + 'type' => 'bool' + }, 'forceGlobalStorageUpgradeOTT' => { 'type' => 'bool' }, @@ -3032,10 +3035,6 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.] }, 'type' => 'file' }, - 'samlIdPResolveCookie' => { - 'default' => 'lemonldapidp', - 'type' => 'text' - }, 'samlIDPSSODescriptorArtifactResolutionServiceArtifact' => { 'default' => '1;0;urn:oasis:names:tc:SAML:2.0:bindings:SOAP;#PORTAL#/saml/artifact', 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 ff3f63003..7040a597d 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm @@ -553,6 +553,11 @@ sub attributes { documentation => 'Avoid asking confirmation when an Issuer asks to renew auth', }, + forceGlobalStorageIssuerOTT => { + type => 'bool', + documentation => + 'Force Issuer tokens be stored into Global Storage', + }, handlerInternalCache => { type => 'int', default => 15, @@ -1532,7 +1537,7 @@ sub attributes { forceGlobalStorageUpgradeOTT => { type => 'bool', documentation => - 'Force upgrade tokens be stored into Global Storage', + 'Force Upgrade tokens be stored into Global Storage', }, # 2F @@ -2319,11 +2324,6 @@ sub attributes { documentation => 'Use certificate instead of public key in SAML responses', }, - samlIdPResolveCookie => { - type => 'text', - default => 'lemonldapidp', - documentation => 'SAML IDP resolution cookie', - }, samlMetadataForceUTF8 => { default => 1, type => 'bool', diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Tree.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Tree.pm index bd0c6402c..8a6e73a4d 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Tree.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Tree.pm @@ -1138,7 +1138,6 @@ sub tree { title => 'samlAdvanced', help => 'samlservice.html#advanced', nodes => [ - 'samlIdPResolveCookie', 'samlMetadataForceUTF8', 'samlStorage', 'samlStorageOptions', diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Parser.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Parser.pm index aa8532138..c0b99bd3c 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Parser.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Parser.pm @@ -266,6 +266,7 @@ sub _scanNodes { $leaf->{comment} ? "(?#$leaf->{comment})$leaf->{re}" : $leaf->{re}; + $k .= "(?#AuthnLevel=$leaf->{level})" if $leaf->{level}; $self->set( $target, $key, $k, $leaf->{data} ); } else { diff --git a/lemonldap-ng-manager/site/coffee/manager.coffee b/lemonldap-ng-manager/site/coffee/manager.coffee index 75ebb1a0b..1aa5c3b57 100644 --- a/lemonldap-ng-manager/site/coffee/manager.coffee +++ b/lemonldap-ng-manager/site/coffee/manager.coffee @@ -592,6 +592,10 @@ llapp.controller 'TreeCtrl', [ if a.template a._nodes = templates a.template, a.title node.nodes.push a + if a.type.match /^rule$/ + console.log "Parse rule AuthnLevel as integer" + if a.level and typeof a.level == 'string' + a.level = parseInt(a.level, 10) d.resolve 'OK' $scope.waiting = false , (response) -> diff --git a/lemonldap-ng-manager/site/htdocs/static/forms/rule.html b/lemonldap-ng-manager/site/htdocs/static/forms/rule.html index 002f36641..64811cfc2 100644 --- a/lemonldap-ng-manager/site/htdocs/static/forms/rule.html +++ b/lemonldap-ng-manager/site/htdocs/static/forms/rule.html @@ -17,6 +17,10 @@