diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/OpenIDConnect/Metadata.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/OpenIDConnect/Metadata.pm index 7eedccc8f38f420bd5c3893dd323fbf20f20daf8..56a50fadce077ee490e136904b51d2ee64401a68 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/OpenIDConnect/Metadata.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/OpenIDConnect/Metadata.pm @@ -7,7 +7,8 @@ use Mouse::Role; our $VERSION = '2.19.0'; sub metadataDoc { - my ( $self, $conf ) = @_; + my ( $self, $issuer ) = @_; + my $conf = $self->conf; my $issuerDBOpenIDConnectPath = $conf->{issuerDBOpenIDConnectPath}; my $authorize_uri = $conf->{oidcServiceMetaDataAuthorizeURI}; my $token_uri = $conf->{oidcServiceMetaDataTokenURI}; @@ -18,8 +19,7 @@ sub metadataDoc { my $checksession_uri = $conf->{oidcServiceMetaDataCheckSessionURI}; my $introspection_uri = $conf->{oidcServiceMetaDataIntrospectionURI}; - my $path = $self->path . '/'; - my $issuer = $self->iss; + my $path = $self->path . '/'; $path = "/" . $path unless ( $issuer =~ /\/$/ ); my $baseUrl = $issuer . $path; diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI.pm index cc3e1fc8fd7ceb9de37c0371ec40a4748288794b..032a133e95d539b37332b8c884d3af9c46bd35d6 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI.pm @@ -15,7 +15,6 @@ our $_json = JSON->new->allow_nonref; has error => ( is => 'rw', default => '' ); has languages => ( is => 'rw', isa => 'Str', default => 'en' ); has logLevel => ( is => 'rw', isa => 'Str', default => 'info' ); -has portal => ( is => 'rw', isa => 'Str' ); has staticPrefix => ( is => 'rw', isa => 'Str' ); has instanceName => ( is => 'rw', isa => 'Str', default => '' ); has customCSS => ( is => 'rw', isa => 'Str', default => '' ); @@ -26,6 +25,10 @@ has menuLinks => ( is => 'rw', isa => 'ArrayRef' ); has logger => ( is => 'rw' ); has userLogger => ( is => 'rw' ); +# portal only stores the "default" portal URL, in case a request +# is not available in the current context to compute the dynamic portal URL +has portal => ( is => 'rw', isa => 'Str' ); + # INITIALIZATION sub init { @@ -265,13 +268,15 @@ sub sendJs { $sp =~ s/\/*$/\//; my $sc = $req->script_name // ""; + my $portal = $req->can('portal') ? $req->portal : $self->portal; + # Javascript scriptname is assumed by our JS code to end with / $sc =~ s#/*$#/#; my $s = sprintf 'var staticPrefix="%s";' . 'var scriptname="%s";' . 'var availableLanguages="%s".split(/[,;] */);' - . 'var portal="%s";', $sp, $sc, $self->languages, $self->portal; + . 'var portal="%s";', $sp, $sc, $self->languages, $portal; $s .= $self->javascript($req) if ( $self->can('javascript') ); return [ 200, diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm index fae36715a1ac9aeea7d5c244a490eb585be05f25..e1b60478fa75cc8cdb76917392006e9841a076d3 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/ApacheMP2/Main.pm @@ -177,18 +177,19 @@ sub redirectFilter { return $class->OK; } - -sub logout_app -{ +sub logout_app { my $class = shift; - my $u = shift || $class->tsv->{portal}->(); + my $u = shift; $class->logger->debug("logout_app redirect to $u"); eval 'use Apache2::Filter' unless ( $INC{"Apache2/Filter.pm"} ); return ( sub { - $_[0]->{env}->{'psgi.r'}->add_output_filter( + my ($req) = @_; + my $portal = $class->tsv->{portal}->($req); + my $dest = $u || $portal; + $req->{env}->{'psgi.r'}->add_output_filter( sub { - return $class->redirectFilter( $u, @_ ); + return $class->redirectFilter( $dest, @_ ); } ); 1; @@ -199,19 +200,21 @@ sub logout_app sub logout_app_sso { my $class = shift; - my $u = shift || $class->tsv->{portal}->(); + my $u = shift; $class->logger->debug("logout_app_sso redirect to $u"); eval 'use Apache2::Filter' unless ( $INC{"Apache2/Filter.pm"} ); return ( sub { - my ($req) = @_; + my ($req) = @_; + my $portal = $class->tsv->{portal}->($req); + my $dest = $u || $portal; $class->localUnlog( $req, @_ ); $req->{env}->{'psgi.r'}->add_output_filter( sub { my $r = $_[0]->r; return $class->redirectFilter( - &{ $class->tsv->{portal} }() . "?url=" - . $class->encodeUrl( $req, $u ) + $portal . "?url=" + . $class->encodeUrl( $req, $dest ) . "&logout=1", @_ ); diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/AuthBasic.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/AuthBasic.pm index 91b9a30518086cd48260bc05a73bca5171df28ff..5e1e418cce75f71a6d2f3288e2e502ecf6cb0910 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/AuthBasic.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/AuthBasic.pm @@ -95,7 +95,7 @@ sub createSession { $class->logger->debug("AuthBasic authentication for user: $user"); #my $soapRequest = $soapClient->getCookies( $user, $pwd, $id ); - my $url = $class->tsv->{portal}->() . "/sessions/global/$id?auth"; + my $url = $class->tsv->{portal}->($req) . "/sessions/global/$id?auth"; $url =~ s#//sessions/#/sessions/#g; my $get = HTTP::Request->new( POST => $url ); $get->header( 'X-Forwarded-For' => $xheader ); diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/PSGI.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/PSGI.pm index b1b81bb3513c4edbe29487adc01f64a8d33e76a0..62f86da74a7f288a95572f2f03fc41af8d1c54e0 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/PSGI.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Lib/PSGI.pm @@ -28,6 +28,7 @@ sub init { ); return 0; } + eval { $self->portal( $self->api->tsv->{portal}->() ) }; my $rule = $args->{protection} || $self->api->localConfig->{protection} || ''; @@ -135,7 +136,7 @@ sub _authAndTrace { eval "require $type"; die $@ if ($@); my ( $res, $session ) = $type->run( $req, $self->{rule} ); - eval { $self->portal( $type->tsv->{portal}->() ) } unless $self->portal; + my $portal = eval { $type->tsv->{portal}->($req) }; $self->logger->warn($@) if $@; $req->userData($session) if ($session); @@ -155,18 +156,16 @@ sub _authAndTrace { my %h = ( $req->spliceHdrs ); my $host = $req->env->{HTTP_HOST}; if ( $h{Location} - and $h{Location} =~ m#^\Q$self->{portal}\E# + and $h{Location} =~ m#^\Q$portal\E# and $h{Location} !~ m#^https?://$host# ) { - return [ - 401, [ 'WWW-Authenticate' => 'SSO ' . $self->{portal} ], [] - ]; + return [ 401, [ 'WWW-Authenticate' => 'SSO ' . $portal ], [] ]; } } return [ $res, [ $req->spliceHdrs ], [] ]; } else { - my $s = ( $self->portal ? $self->portal . "/lmerror/$res" : '' ); + my $s = ( $portal ? $portal . "/lmerror/$res" : '' ); $s = 'Redirection' . qq{} 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 b4d72723a70225f6af8c1d5c90f3fd80aed10ad6..6980f2323093470851523385dd89f9ee07f5ade2 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Reload.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Reload.pm @@ -523,14 +523,16 @@ sub conditionSub { $url ? ( sub { - $_[1]->{_logout} = $url; + my ( $req, $session ) = @_; + $session->{_logout} = $url; return 0; }, 0 ) : ( sub { - $_[1]->{_logout} = $class->tsv->{portal}->(); + my ( $req, $session ) = @_; + $session->{_logout} = $class->tsv->{portal}->($req); return 0; }, 0 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 fea1feb67b9e303bbc1d11c903e8f3e9442ee64c..00fcd1acaaef339cee986f3582108a9693b6cdf6 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm @@ -398,7 +398,7 @@ sub grant { sub forbidden { my ( $class, $req, $session, $vhost ) = @_; my $uri = $req->{env}->{REQUEST_URI}; - my $portal = $class->tsv->{portal}->(); + my $portal = $class->tsv->{portal}->($req); $portal = ( $portal =~ m#^https?://([^/]*).*# )[0]; $portal =~ s/:\d+$//; $vhost ||= $class->resolveAlias($req); @@ -476,7 +476,7 @@ sub goToPortal { $class->logger->debug( 'Redirect ' . $req->address . " to portal (url was $url)" ); $class->set_header_out( $req, - 'Location' => $class->tsv->{portal}->() + 'Location' => $class->tsv->{portal}->($req) . "$path?url=$urlc_init" . ( $arg ? "&$arg" : "" ) ); return $class->REDIRECT; @@ -488,7 +488,7 @@ sub goToError { $class->logger->debug( "Redirect $req->{env}->{REMOTE_ADDR} to lmError (url was $url)"); $class->set_header_out( $req, - 'Location' => $class->tsv->{portal}->() + 'Location' => $class->tsv->{portal}->($req) . "/lmerror/$code" . "?url=$urlc_init" ); return $class->REDIRECT; @@ -905,7 +905,7 @@ sub postJavascript { my $jqueryUrl = $formParams->{jqueryUrl} || ""; $jqueryUrl = - &{ $class->tsv->{portal} } . "static/bwr/jquery/dist/jquery.min.js" + $class->tsv->{portal}->($req) . "static/bwr/jquery/dist/jquery.min.js" if ( $jqueryUrl eq "default" ); $jqueryUrl = "\n" if ($jqueryUrl); diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Main.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Main.pm index 0e342b22e1536799718cb69a07095ee287373ad6..0fc5d942e9e29873039694ad507a9e1355e0dfe0 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Main.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Main.pm @@ -109,13 +109,17 @@ sub cgiName { sub logout_app { my $class = shift; - my $u = shift || $class->tsv->{portal}->(); + my $u = shift; $class->logger->debug("Nginx logout_app redirect to $u"); return ( sub { my ( $req, $session ) = @_; - $class->logger->debug("logout_app_sso: sending internal_lemonldap_logout_url with $u"); - $class->set_header_out( $req, ( "internal_lemonldap_logout_url" => "$u" ) ); + my $dest = $u || $class->tsv->{portal}->($req); + $class->logger->debug( +"logout_app_sso: sending internal_lemonldap_logout_url with $dest" + ); + $class->set_header_out( $req, + ( "internal_lemonldap_logout_url" => "$dest" ) ); 1; }, 0 @@ -124,17 +128,22 @@ sub logout_app { sub logout_app_sso { my $class = shift; - my $u = shift || $class->tsv->{portal}->(); + my $u = shift; $class->logger->debug("Nginx logout_app_sso redirect to $u"); return ( sub { my ( $req, $session ) = @_; $class->localUnlog( $req, @_ ); - my $rurl = &{ $class->tsv->{portal} }() . "?url=" - . $class->encodeUrl( $req, $u ) - . "&logout=1"; - $class->logger->debug( "logout_app: sending internal_lemonldap_logout_url with $rurl" ); - $class->set_header_out( $req, ( "internal_lemonldap_logout_url" => "$rurl" ) ); + my $portal = $class->tsv->{portal}->($req); + my $dest = $u || $portal; + my $rurl = + $portal . "?url=" + . $class->encodeUrl( $req, $dest ) + . "&logout=1"; + $class->logger->debug( + "logout_app: sending internal_lemonldap_logout_url with $rurl"); + $class->set_header_out( $req, + ( "internal_lemonldap_logout_url" => "$rurl" ) ); 1; }, 0 diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm index b151c71a05bcd064b0f25149b077a8674b4695cc..6c90b85b1f5629caf665763acf0872041b0c05d7 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Conf/Tests.pm @@ -53,16 +53,20 @@ sub tests { portalURL => sub { my $url = $conf->{portal}; - # Append or remove trailing slashes - $conf->{portal} =~ s%/*$%/%; - return ( - 1, - ( - ( $url =~ m%/$% ) - ? '' - : "Portal URL should end with a /" - ) - ); + # Looks like a sub, user knows what they are doing + if ( $url =~ /[\$\(&\|"']/ ) { + return 1; + } + else { + return ( + 1, + ( + ( $url =~ m%/$% ) + ? '' + : "Portal URL should end with a /" + ) + ); + } }, # Check if virtual hosts are in the domain diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Engines/Default.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Engines/Default.pm index 967d99f55c8559443aa9b59802b757dc99239a7c..16f0b263f9cda29d7ead7f38d95eac1f108aba00 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Engines/Default.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Engines/Default.pm @@ -379,8 +379,11 @@ sub run { $self->regOtt->createToken( $self->save2faSessionInfo($req) ); $self->logger->debug("Just one 2F is enabled"); $self->logger->debug(" -> Redirect to 2fregisters/"); - $req->response( - [ 302, [ Location => $self->p->buildUrl('2fregisters') ], [] ] + $req->response( [ + 302, + [ Location => $self->p->buildUrl( $req->portal, '2fregisters' ) ], + [] + ] ); return PE_SENDRESPONSE; } @@ -526,9 +529,11 @@ sub _choice { sub _redirect { my ( $self, $req ) = @_; my $arg = $req->env->{QUERY_STRING}; + $self->logger->debug('Call sfEngine _redirect method'); return [ - 302, [ Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' ) ], [] + 302, + [ Location => $req->portal . ( $arg ? "?$arg" : '' ) ], [] ]; } @@ -595,7 +600,11 @@ sub _displayRegister { # If only one 2F is available, redirect to it return [ 302, - [ Location => $self->p->buildUrl( '2fregisters', $am[0]->{CODE} ) ], [] + [ + Location => + $self->p->buildUrl( $req->portal, '2fregisters', $am[0]->{CODE} ) + ], + [] ] if ( @am == 1 @@ -640,7 +649,7 @@ sub _displayRegister { MSG => $self->canUpdateSfa($req) || 'choose2f', ALERT => ( $self->canUpdateSfa($req) ? 'warning' : 'positive' ), SFREGISTERS_URL => - encode_base64( "$self->{conf}->{portal}2fregisters", '' ), + encode_base64( $self->p->buildUrl( $req->portal, '2fregisters' ), '' ), %tplParams, } ); @@ -718,7 +727,7 @@ sub restoreSession { unless ( $sessionData->{_2fRealSession} ) { $self->logger->error("Invalid 2FA registration token"); - return [ 302, [ Location => $self->conf->{portal} ], [] ]; + return [ 302, [ Location => $req->portal ], [] ]; } $self->restore2faSessionInfo( $req, $sessionData ); @@ -750,7 +759,7 @@ sub restoreSession { } $self->logger->warn( "Cannot restore session state during mandatory 2FA registration"); - return [ 302, [ Location => $self->conf->{portal} ], [] ]; + return [ 302, [ Location => $req->portal ], [] ]; } sub continueLoginAfterRegistration { diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm index b53fbb6bbab0e76a695902b7e83e4f9efaa6ed31..d7ca25623a10666d12ec8a6d3988faa8f396ab09 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm @@ -161,7 +161,7 @@ sub run { } ); unless ( $issuer = $self->conf->{totp2fIssuer} ) { - $issuer = $self->conf->{portal}; + $issuer = $req->portal; $issuer =~ s#^https?://([^/:]+).*$#$1#; } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm index 7bfe48f1f3cf30878d33e4c2a97dd3b0c43010a3..2117b4dd82af2d1e9a4a1f512d09d958f993e30d 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm @@ -76,7 +76,7 @@ sub run { 302, [ Location => $self->p->buildUrl( - "2fregisters", { continue => 1 } + $req->portal, "2fregisters", { continue => 1 } ) ], [] diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/CAS.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/CAS.pm index 0ac1a4550ad06c842842480fe17397adedfdf9a8..a5a9519bddd6d26a4edfbbb1c80486b409c7b08c 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/CAS.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/CAS.pm @@ -38,7 +38,6 @@ sub init { } $self->srvNumber( scalar @tab ); my @list; - my $portalPath = $self->conf->{portal}; foreach (@tab) { my $name = $_; @@ -59,7 +58,7 @@ sub init { $img_src = ( $icon =~ m#^https?://# ) ? $icon - : $portalPath . $self->p->staticPrefix . "/common/" . $icon; + : $self->p->staticPrefix . "/common/" . $icon; } push @list, { diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Facebook.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Facebook.pm index 6ce0c2521bd5f0ab2eaadd8e96f149ed1116f824..133271c4609c4854791e6559e84cf403439b53d9 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Facebook.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Facebook.pm @@ -161,7 +161,7 @@ sub fb { my $conf = $self->{conf}; my $fb; my $sep = '?'; - my $ret = $conf->{portal}; + my $ret = $req->portal; eval { $fb = Net::Facebook::Oauth2->new( diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/GitHub.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/GitHub.pm index 7b7ac149ddfea223941fbced4b70997ac5646b1e..8cbab6f1f21c8d01cc677613338011a1be923817 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/GitHub.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/GitHub.pm @@ -93,7 +93,7 @@ sub extractFormInfo { my $nonce = time; # Build redirect_uri - my $callback_url = $self->conf->{portal}; + my $callback_url = $req->portal; # Check return values my $code = $req->param("code"); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/LinkedIn.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/LinkedIn.pm index 10bbaaa364070ad313f71b9ba8950a9049725b92..059d91c6454eda5739792885ad342a07a2879780 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/LinkedIn.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/LinkedIn.pm @@ -84,7 +84,7 @@ sub extractFormInfo { my $nonce = time; # Build redirect_uri - my $callback_url = $self->conf->{portal}; + my $callback_url = $req->portal; # Check return values my $error = $req->param("error"); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenID.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenID.pm index 795e5cc5701806fe846efffe69fbf27059c9106e..807a114e8e790c4a95bb0b2d180b92cf55a92c85 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenID.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenID.pm @@ -81,7 +81,7 @@ sub extractFormInfo { || Cache::FileCache->new, args => $req, consumer_secret => $self->conf->{openIdSecret}, - required_root => $self->conf->{portal}, + required_root => $req->portal ); my ( $url, $openid ); @@ -149,15 +149,15 @@ sub extractFormInfo { # Build the redirection $self->logger->debug("OpenID redirection to $url"); my $req_url = $req->data->{_url}; + my $return_to = $self->p->buildUrl( $req->portal, + { + openid => 1, + ( $req_url ? ( url => $req_url ) : () ) + } + ); my $check_url = $claimed_identity->check_url( - return_to => $self->conf->{portal} - . '?openid=1&' - . ( - $req_url - ? build_urlencoded( url => $req_url ) - : '' - ), - trust_root => $self->conf->{portal}, + return_to => $return_to, + trust_root => $req->portal, delayed_return => 1, ); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm index 0674c004d5554d888e08a6f64f2fff7b96c520aa..483471f9068b5fa36c6c8c0d3aecc1b35936e5e5 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm @@ -39,7 +39,6 @@ sub init { return 0; } my @list = (); - my $portalPath = $self->conf->{portal}; foreach (@tab) { my $name = $_; @@ -56,7 +55,7 @@ sub init { $img_src = ( $icon =~ m#^https?://# ) ? $icon - : $portalPath . $self->p->staticPrefix . "/common/" . $icon; + : $self->p->staticPrefix . "/common/" . $icon; } push @list, @@ -69,12 +68,10 @@ sub init { }; } - my $portal = $self->conf->{portal}; my $re = '^/' . $self->path . '/(?:' . join( '|', map { my $s = $self->conf->{$_}; - $s =~ s/^$portal\/*//; $s =~ s/#PORTAL#\/*//; $s } ( qw( @@ -433,7 +430,7 @@ sub authLogout { my $client_id = $self->opOptions->{$op}->{oidcOPMetaDataOptionsClientID}; if ($endsession_endpoint) { - my $logout_url = $self->conf->{portal} . '?logout=1'; + my $logout_url = $self->p->buildUrl( $req->portal, { logout => 1 } ); $req->urldc( $self->buildLogoutRequest( $endsession_endpoint, $req->{sessionInfo}->{_oidc_id_token}, diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/SAML.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/SAML.pm index 7e49e2e32394cec7fc011793f4f0bf14828bde71..db7268e632069d94e3ccebb8d5f83fbb7b021af2 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/SAML.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/SAML.pm @@ -548,7 +548,7 @@ sub extractFormInfo { } if ( $req->urldc - and $self->conf->{portal} !~ /$req->{urldc}\/?/ ) + and $req->portal !~ /$req->{urldc}\/?/ ) { $req->steps( [] ); $req->user('TODO'); @@ -920,7 +920,7 @@ sub extractFormInfo { $req->pdata->{_url} = encode_base64( $req->urldc, '' ); } my $disco_url = $self->conf->{samlDiscoveryProtocolURL}; - my $portal = $self->conf->{portal}; + my $portal = $req->portal; $disco_url .= ( $disco_url =~ /\?/ ? '&' : '?' ) . build_urlencoded( entityID => $self->getMetaDataURL( 'samlEntityID', 0, 1 ), @@ -961,7 +961,7 @@ sub extractFormInfo { # IDP list my @list = (); - my $portalPath = $self->conf->{portal}; + my $portalPath = $req->portal; foreach ( keys %{ $self->idpList } ) { my $idpName = $self->{idpList}->{$_}->{name}; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm index 51efa74a5f661b8af7cdb964cbd98c3d1ea8c881..41957c21ca769bb14089d000ac8ecc0c977e52a9 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/CAS.pm @@ -994,7 +994,8 @@ sub _send_back_channel_LogoutRequests { # Build the URL that could be used to play this logout request my $slo_url = - $self->p->buildUrl( "cas", "relayLogout", { relay => $relayid } ); + $self->p->buildUrl( $req->portal, "cas", "relayLogout", + { relay => $relayid } ); my $name = $self->getNameFromService($service); @@ -1064,13 +1065,13 @@ sub relayLogout { if ( $response->is_success ) { $self->logger->debug("CAS back-channel logout to $service OK"); - return $self->p->imgok; + return $self->p->imgok($req); } else { $self->logger->error( "CAS back-channel logout to $service error: " . $response->message ); $self->logger->debug( $response->dump ); - return $self->p->imgnok; + return $self->p->imgnok($req); } } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenID.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenID.pm index 2f367af3a58f187c6df110864277ab44bc114779..2ab23c5bf7735a819ecc74f60a63b62f324a1bab 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenID.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/OpenID.pm @@ -76,7 +76,7 @@ sub init { return 0; } return 0 unless ( $self->SUPER::init() ); - $self->openidPortal( $self->conf->{portal} . '/' . $self->path . '/' ); + $self->openidPortal( $self->p->buildUrl( $self->path ) . '/' ); #$openidPortal =~ s#(? ( is => 'ro', lazy => 1, default => sub { - $_[0]->conf->{oidcServiceMetaDataIssuer} || $_[0]->conf->{portal}; + $_[0]->conf->{oidcServiceMetaDataIssuer} + || $_[0]->p->HANDLER->tsv->{portal}->(); } ); +sub get_issuer { + my ( $self, $req ) = @_; + if ( $self->iss ) { + return $self->iss; + } + else { + return $req->portal; + } +} + # OIDC has 7 endpoints managed here as PSGI endpoints or in run() [Main/Issuer.pm # manage transparent authentication for run()]: # - authorize : in run() @@ -2191,7 +2202,7 @@ sub introspection { if $oidcSession->{data}->{scope}; $response->{client_id} = $oidcSession->{data}->{client_id} if $oidcSession->{data}->{client_id}; - $response->{iss} = $self->iss; + $response->{iss} = $self->get_issuer($req); $response->{exp} = $oidcSession->{data}->{_utime} + $self->conf->{timeout}; } @@ -2475,7 +2486,7 @@ sub logout { { $url .= ( $url =~ /\?/ ? '&' : '?' ) . build_urlencoded( - iss => $self->iss, + iss => $self->get_issuer($req), sid => $self->getSidFromSession( $rp, $req->{sessionInfo} ) @@ -2522,7 +2533,7 @@ sub logout { my $userId = $self->getUserIDForRP( $req, $rp, $req->userData ); my $logoutToken = { - iss => $self->iss, + iss => $self->get_issuer($req), sub => $userId, aud => $self->getAudiences($rp), iat => time, @@ -2580,7 +2591,7 @@ sub metadata { my ( $self, $req ) = @_; $req->data->{dropCsp} = 1 if $self->conf->{oidcDropCspHeaders}; return $self->p->sendJSONresponse( $req, - $self->metadataDoc( $self->conf ) ); + $self->metadataDoc( $self->get_issuer($req) ) ); } # Store request parameters in %ENV @@ -2732,7 +2743,7 @@ sub _generateIDToken { my $user_id = $self->getUserIDForRP( $req, $rp, $sessionInfo ); my $id_token_payload_hash = { - iss => $self->iss, # Issuer Identifier + iss => $self->get_issuer($req), # Issuer Identifier sub => $user_id, # Subject Identifier aud => $self->getAudiences($rp), # Audience exp => $id_token_exp, # expiration diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/SAML.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/SAML.pm index b69591c2d056fc2e992b409f5257595a2840ab41..d8608324f2aac808bc595c08ec62599f4acd0529 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/SAML.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Issuer/SAML.pm @@ -1520,7 +1520,7 @@ sub logout { # for them. # Redirect on logout page when all is done. if ( $self->sendLogoutRequestToProviders( $req, $logout ) ) { - $req->urldc( $self->p->buildUrl( { logout => 1 } ) ); + $req->urldc( $self->p->buildUrl($req->portal, { logout => 1 } ) ); return PE_OK; } @@ -1820,7 +1820,7 @@ sub _finishSlo { # Else build SLO status relay URL and display info else { $req->{urldc} = - $self->conf->{portal} . '/saml/relaySingleLogoutTermination'; + $self->p->buildUrl( $req->portal, 'saml', 'relaySingleLogoutTermination' ); $self->p->setHiddenFormValue( $req, 'relay', $relayID, '', 0 ); return $self->p->do( $req, [] ); } @@ -2039,7 +2039,8 @@ sub sloServer { $logoutContextSession = $self->getSamlSession( undef, $logoutInfos ); my $uri = - URI->new( $self->conf->{portal} . '/saml/singleLogoutResume' ); + URI->new( + $self->p->buildUrl( $req->portal, 'saml', 'singleLogoutResume' ) ); $uri->query_param( ResumeParams => $logoutContextSession->id ); $req->{issuerUrldc} = $uri->as_string; } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm index dfe27245a6656c71260c7980f86f97658f016b77..0576c03cd7d78274e96778b78e6b3fa46754b5a0 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/OpenIDConnect.pm @@ -408,11 +408,7 @@ sub getCallbackUri { my $callback_get_param = $self->conf->{oidcRPCallbackGetParam}; - my $callback_uri = $self->conf->{portal}; - $callback_uri .= - ( $self->conf->{portal} =~ /\?/ ) - ? '&' . $callback_get_param . '=1' - : '?' . $callback_get_param . '=1'; + my $callback_uri = $self->p->buildUrl( $req->portal, { $callback_get_param => 1 } ); $self->logger->debug("OpenIDConnect Callback URI: $callback_uri"); return $callback_uri; @@ -1243,7 +1239,7 @@ sub makeJWT { my $client_id = $self->rpOptions->{$rp}->{oidcRPMetaDataOptionsClientID}; my $access_token_payload = { - iss => $self->iss, # Issuer Identifier + iss => $self->get_issuer($req), # Issuer Identifier exp => $exp, # expiration aud => $self->getAudiences($rp), # Audience client_id => $client_id, # Client ID diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Remote.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Remote.pm index e469e3e08db9e4887159f2e18ba5f02ac970b75f..5606b3fb949f4f117ae6442c7b36fb26b8fab8a6 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Remote.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Remote.pm @@ -90,7 +90,7 @@ sub goToPortal { $req->urldc( $self->conf->{remotePortal} . '?url=' . encode_base64( - $self->conf->{portal} + $req->portal . ( $req->query_string ? '?' . $req->query_string : '' ), '' ) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm index dbdedb87450feeace89f0a5f8b42f360d961ba2f..391b2bd9cb39fddd9efa17f78d6e716d0e7b2e52 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/SAML.pm @@ -194,7 +194,9 @@ sub loadService { # Create Lasso server with service metadata my $server = $self->createServer( - $service_metadata->serviceToXML( $self->conf, '' ), + $service_metadata->serviceToXML( + { %{ $self->conf }, portal => $self->p->HANDLER->tsv->{portal}->() }, '' + ), $self->conf->{samlServicePrivateKeySig}, $self->conf->{samlServicePrivateKeySigPwd}, @@ -1795,8 +1797,8 @@ sub getMetaDataURL { my $url = ( split( /;/, $self->conf->{$key} ) )[$index] || ''; - # Get portal value - my $portal = $self->conf->{portal}; + # Get default portal value + my $portal = $self->p->buildUrl; $portal =~ s/\/$//; # Replace #PORTAL# macro @@ -2836,7 +2838,7 @@ sub sendLogoutRequestToProvider { } # Get portal value - my $portal = $self->conf->{portal}; + my $portal = $self->p->buildUrl; $portal =~ s/\/$//; # Send logout request to the provider depending of the request method @@ -3111,7 +3113,7 @@ sub checkDestination { $self->logger->debug("Destination $destination found in SAML message"); # Retrieve full URL - my $portal = $self->conf->{portal}; + my $portal = $self->p->buildUrl; $portal =~ s#^(https?://[^/]+)/.*#$1#; # remove path of portal URL $url = $portal . $url; $url =~ s/\?.*//; @@ -3394,7 +3396,9 @@ sub metadata { my $type = $req->param('type') || 'all'; require Lemonldap::NG::Common::Conf::SAML::Metadata; if ( my $metadata = Lemonldap::NG::Common::Conf::SAML::Metadata->new() ) { - my $s = $metadata->serviceToXML( $self->conf, $type ); + my $s = + $metadata->serviceToXML( { %{ $self->conf }, portal => $req->portal }, + $type ); return [ 200, [ diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/U2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/U2F.pm index a6cc76b1e604395247773f736e0c01579fa3ecc3..42bf26026790fde86c5e16f7a7a225a140e5d1dd 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/U2F.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/U2F.pm @@ -15,7 +15,7 @@ sub init { $self->error("Can't load U2F library: $@"); return 0; } - my $p = $_[0]->{conf}->{portal}; + my $p = $self->p->HANDLER->tsv->{portal}->(); $p =~ s#^(https?://[^/]+).*$#$1#; $self->origin($p); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm index ec5fc0d6fdec0460594f848df3f262694eec27ad..41b7cb74101a647b47f72895193ce277e6cc6c16 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm @@ -12,9 +12,6 @@ use Lemonldap::NG::Common::Util qw/display2F/; our $VERSION = '2.18.0'; -has rp_id => ( is => 'rw', lazy => 1, builder => "_build_rp_id" ); -has origin => ( is => 'rw', lazy => 1, builder => "_build_origin" ); -has verifier => ( is => 'rw', lazy => 1, builder => "_build_verifier" ); has trust_anchors => ( is => 'rw', lazy => 1, @@ -26,25 +23,24 @@ sub _build_trust_anchors { return []; } -sub _build_verifier { - my $self = shift; +sub verifier { + my ( $self, $req ) = @_; return Authen::WebAuthn->new( - rp_id => $self->rp_id, - origin => $self->origin, + rp_id => $self->rp_id($req), + origin => $self->origin($req), ); } -sub _build_rp_id { - my ($self) = @_; +sub rp_id { + my ( $self, $req ) = @_; - # TODO make this configurable - my $portal_uri = URI->new( $self->{conf}->{portal} ); + my $portal_uri = URI->new( $req->portal ); return $portal_uri->authority; } -sub _build_origin { - my ($self) = @_; - my $portal_uri = URI->new( $self->{conf}->{portal} ); +sub origin { + my ( $self, $req ) = @_; + my $portal_uri = URI->new( $req->portal ); return ( $portal_uri->scheme . "://" . $portal_uri->authority ); } @@ -93,7 +89,7 @@ sub generateChallenge { ], ( $userVerification ? ( userVerification => $userVerification ) : () ), extensions => { - appid => $self->origin, + appid => $self->origin($req), }, }; } @@ -120,15 +116,18 @@ sub validateCredential { . " cannot validate attestation" ); } - return $self->verifier->validate_registration( + return $self->verifier($req)->validate_registration( challenge_b64 => $challenge_b64, requested_uv => $requested_uv, client_data_json_b64 => $client_data_json_b64, attestation_object_b64 => $attestation_object_b64, token_binding_id_b64 => $token_binding_id_b64, trust_anchors => $self->trust_anchors, - ($attestation ne "none" ? ( - allowed_attestation_types => [ "Basic" ] ) : ()), + ( + $attestation ne "none" + ? ( allowed_attestation_types => ["Basic"] ) + : () + ), ); } @@ -194,7 +193,7 @@ sub validateAssertion { $req->headers->header('Sec-Provided-Token-Binding-ID') ) : ''; - my $validation_result = $self->verifier->validate_assertion( + my $validation_result = $self->verifier($req)->validate_assertion( challenge_b64 => $signature_options->{challenge}, credential_pubkey_b64 => $matching_credential->{_credentialPublicKey}, stored_sign_count => $matching_credential->{_signCount}, diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm index 5131e2dda637292fb43ed1f358cc11814767545e..a4709722c8d831e495791a875e09a999593c50b1 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm @@ -21,34 +21,22 @@ has passwordResetUrl => ( is => 'ro', lazy => 1, default => sub { - my $self = $_[0]; - my $p = $self->conf->{portal}; - $p =~ s#/*$##; - return $self->conf->{mailUrl} ? $self->conf->{mailUrl} : "$p/resetpwd"; + $_[0]->conf->{mailUrl} || $_[0]->buildUrl("resetpwd"); } ); has certificateResetUrl => ( is => 'ro', lazy => 1, default => sub { - my $self = $_[0]; - my $p = $self->conf->{portal}; - $p =~ s#/*$##; - return $self->conf->{certificateResetByMailURL} - ? $self->conf->{certificateResetByMailURL} - : "$p/certificateReset"; + $_[0]->conf->{certificateResetByMailURL} + || $_[0]->buildUrl("certificateReset"); } ); has registerUrl => ( is => 'ro', lazy => 1, default => sub { - my $self = $_[0]; - my $p = $self->conf->{portal}; - $p =~ s#/*$##; - return $self->conf->{registerUrl} - ? $self->conf->{registerUrl} - : "$p/register"; + $_[0]->conf->{registerUrl} || $_[0]->buildUrl("register"); } ); @@ -226,7 +214,7 @@ sub display { AUTH_ERROR_ROLE => $req->error_role, ( 'AUTH_ERROR_' . $req->error ) => 1, MSG => $info, - URL => $req->{urldc} || $self->conf->{portal}, # Fix 2158 + URL => $req->{urldc} || $req->portal, # Fix 2158 HIDDEN_INPUTS => $self->buildOutgoingHiddenForm( $req, $method ), ACTIVE_TIMER => $req->data->{activeTimer}, CHOICE_PARAM => $self->conf->{authChoiceParam}, @@ -249,8 +237,9 @@ sub display { or $req->{error} == PE_OPENID_BADID ) { $skinfile = 'openid'; - my $p = $self->conf->{portal} . $self->conf->{issuerDBOpenIDPath}; - $p =~ s#(?conf->{issuerDBOpenIDPath} =~ s/^.*?(\w+).*?$/$1/r; my $id = $req->{sessionInfo} ->{ $self->conf->{openIdAttr} || $self->conf->{whatToTrace} }; %templateParams = ( @@ -258,8 +247,8 @@ sub display { AUTH_ERROR_TYPE => $req->error_type, AUTH_ERROR_ROLE => $req->error_role, ( 'AUTH_ERROR_' . $req->error ) => 1, - PROVIDERURI => $p, - MSG => $req->info(), + PROVIDERURI => $self->buildUrl( $req->portal, $openid_path, "" ), + MSG => $req->info(), ( $req->data->{customScript} ? ( CUSTOM_SCRIPT => $req->data->{customScript} ) @@ -295,7 +284,7 @@ sub display { %templateParams = ( AUTH_USER => $req->{sessionInfo}->{ $self->conf->{portalUserAttr} }, NEWWINDOW => $self->conf->{portalOpenLinkInNewWindow}, - LOGOUT_URL => $self->conf->{portal} . "?logout=1", + LOGOUT_URL => $self->buildUrl( $req->portal, { logout => 1 } ), APPSLIST_ORDER => $req->{sessionInfo}->{'_appsListOrder'}, PING => $self->conf->{portalPingInterval}, DONT_STORE_PASSWORD => $self->conf->{browsersDontStorePassword}, @@ -327,7 +316,7 @@ sub display { PORTALBUTTON => 1, BUTTON => 'upgradeSession', CONFIRMKEY => $self->stamp, - PORTAL => $self->conf->{portal}, + PORTAL => $req->portal, URL => $req->data->{_url}, ( $req->data->{customScript} @@ -344,7 +333,7 @@ sub display { FORMACTION => '/renewsession', MSG => 'askToRenew', CONFIRMKEY => $self->stamp, - PORTAL => $self->conf->{portal}, + PORTAL => $req->portal, PORTALBUTTON => 1, BUTTON => 'renewSession', URL => $req->data->{_url}, @@ -364,7 +353,7 @@ sub display { MSG => 'PE87', CONFIRMKEY => $self->stamp, BUTTON => 'renewSession', - PORTAL => $self->conf->{portal}, + PORTAL => $req->portal, URL => $req->data->{_url}, ( $req->data->{customScript} @@ -459,15 +448,15 @@ sub display { # External links if ( $self->conf->{portalDisplayResetPassword} ) { $templateParams{"MAIL_URL_EXTERNAL"} = - $self->_isExternalUrl( $self->passwordResetUrl ); + $self->_isExternalUrl( $req, $self->passwordResetUrl ); } if ( $self->conf->{portalDisplayRegister} ) { $templateParams{"REGISTER_URL_EXTERNAL"} = - $self->_isExternalUrl( $self->registerUrl ); + $self->_isExternalUrl( $req, $self->registerUrl ); } if ( $self->conf->{portalDisplayCertificateResetByMail} ) { $templateParams{MAILCERTIF_URL_EXTERNAL} = - $self->_isExternalUrl( $self->certificateResetUrl ); + $self->_isExternalUrl( $req, $self->certificateResetUrl ); } # Display captcha if it's enabled @@ -619,7 +608,7 @@ sub display { : "", AUTH_LOOP => [], PORTAL_URL => - ( $displayType eq "logo" ? $self->conf->{portal} : 0 ), + ( $displayType eq "logo" ? $req->portal : 0 ), MSG => $req->info(), MANDATORY => $mandatory, FIELDS => $fields, @@ -879,8 +868,8 @@ sub mkOidcConsent { } sub _isExternalUrl { - my ( $self, $url ) = @_; - return ( index( $url, $self->conf->{portal} ) < 0 ); + my ( $self, $req, $url ) = @_; + return ( index( $url, $req->portal ) < 0 ); } sub getPasswordPolicyTemplateVars { diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Init.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Init.pm index e5c802ae280744c74f5d43bccb0477a24dae0c44..899148cfaaff00ec2cce0b86888779078b381688 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Init.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Init.pm @@ -16,6 +16,7 @@ use strict; use Mouse; use Regexp::Assemble; use Lemonldap::NG::Common::Util qw(getSameSite); +use URI; # PROPERTIES @@ -224,6 +225,9 @@ sub setPortalRoutes { sub reloadConf { my ( $self, $conf ) = @_; + # Store default portal value in $self->portal + $self->portal( Lemonldap::NG::Handler::Main->tsv->{portal}->() ); + # Handle requests (other path may be declared in enabled plugins) $self->setPortalRoutes; @@ -395,9 +399,8 @@ sub reloadConf { $re->add("$_"); } } - foreach ( @{ $self->{additionalTrustedDomains} }, - $self->conf->{portal} ) - { + my $default_portal = HANDLER->tsv->{portal}->(); + foreach ( @{ $self->{additionalTrustedDomains} }, $default_portal ) { my $p = $_; $p =~ s#https?://([^/]*).*$#$1#; $re->add( quotemeta($p) ); @@ -448,9 +451,12 @@ sub reloadConf { } return PE_OK; }; - my $portal = $self->conf->{portal}; - $portal =~ s#^https?://(.*?)(?:[:/].*)?$#$1#; - HANDLER->tsv->{defaultCondition}->{$portal} ||= sub { 1 }; + # Failsafe: allow handler access to portal in case it's missing in conf + my $default_portal_uri = URI->new( HANDLER->tsv->{portal}->() ); + my $default_portal_host = eval { $default_portal_uri->host }; + if ($default_portal_host) { + HANDLER->tsv->{defaultCondition}->{$default_portal_host} ||= sub { 1 }; + } 1; } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Issuer.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Issuer.pm index 3969b3669a9395618f4a5a3f1c757d619691be39..aa5371b255e6ea569a57ea84a40a91925dfef964 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Issuer.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Issuer.pm @@ -100,7 +100,7 @@ sub _redirect { $self->logger->debug( 'Add ' . $self->ipath . ', ' . $self->ipath . 'Path in keepPdata' ); push @{ $req->pdata->{keepPdata} }, $self->ipath, $self->ipath . 'Path'; - $req->{urldc} = $self->p->buildUrl( $self->path ); + $req->{urldc} = $self->p->buildUrl( $req->portal, $self->path ); $req->pdata->{_url} = encode_base64( $req->urldc, '' ); $req->pdata->{issuerTs} = time; } @@ -246,7 +246,7 @@ sub reAuth { qq'' if ( $self->conf->{skipRenewConfirmation} ); $req->data->{_url} = - encode_base64( $self->conf->{portal} . $req->path_info, '' ); + encode_base64( $req->portal . $req->path_info, '' ); $req->pdata->{ $self->ipath } = $self->storeRequest($req); push @{ $req->pdata->{keepPdata} }, $self->ipath, $self->ipath . 'Path'; $req->pdata->{issuerTs} = time; @@ -259,7 +259,7 @@ sub upgradeAuth { qq'' if ( $self->conf->{skipUpgradeConfirmation} ); $req->data->{_url} = - encode_base64( $self->conf->{portal} . $req->path_info, '' ); + encode_base64( $req->portal . $req->path_info, '' ); $req->pdata->{ $self->ipath } = $self->storeRequest($req); push @{ $req->pdata->{keepPdata} }, $self->ipath, $self->ipath . 'Path'; $req->pdata->{issuerTs} = time; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm index fbf22c97b47558e000dd7467f350f9d7cc534c02..6ca0dc52be449e77e4d23530fec0f156d51749a9 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm @@ -335,7 +335,7 @@ sub deleteSession { # Redirect on logout page if no other target defined if ( !$req->urldc and !$req->postUrl ) { $self->logger->debug('No other target defined, redirect on logout'); - $req->urldc( $self->buildUrl( { logout => 1 } ) ); + $req->urldc( $self->buildUrl( $req->portal, { logout => 1 } ) ); } } $req->userData( {} ); @@ -346,14 +346,14 @@ sub deleteSession { return PE_OK; } - if ( $req->urldc and $req->urldc ne $self->conf->{portal} ) { + if ( $req->urldc and $req->urldc ne $req->portal ) { $req->steps( [] ); return PE_REDIRECT; } # If logout redirects to another URL, just remove next steps for the # request so autoRedirect will be called - if ( $req->{urldc} and $req->{urldc} ne $self->conf->{portal} ) { + if ( $req->{urldc} and $req->{urldc} ne $req->portal ) { $req->steps( [] ); return PE_OK; } @@ -671,11 +671,11 @@ sub getCookieDomain { if ( my $domain = $self->conf->{domain} ) { if ( $domain eq '#PORTALDOMAIN#' ) { - my $host = URI->new( $self->portal )->host; + my $host = URI->new( $req->portal )->host; return $host =~ s/^([^\.]*)//r; } elsif ( $domain eq '#PORTAL#' ) { - my $host = URI->new( $self->portal )->host; + my $host = URI->new( $req->portal )->host; return ".$host"; } else { diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Request.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Request.pm index 92cdcb9412109f1ae8d15dd80379e60991633248..cc5be9c5da54683d743bcd3dbc7deccbf9598fc5 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Request.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Request.pm @@ -44,6 +44,10 @@ has mustRedirect => ( is => 'rw' ); # authentication) has noLoginDisplay => ( is => 'rw' ); +# Portal URL, can be dynamic + +has portal => ( is => 'rw' ); + # Store URL for redirections has urldc => ( is => 'rw' ); has postUrl => ( is => 'rw' ); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm index 07792c5074b411f5a94f26ef87b5dbf369482758..d7c6f0e1d5031c274d83cafcc825469c070b040d 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm @@ -54,6 +54,9 @@ sub handler { bless $req, 'Lemonldap::NG::Portal::Main::Request'; $req->init( $self->conf ); + + # Set portal URL dynamically + $req->portal( HANDLER->tsv->{portal}->($req) ); my $sp = 0; # Restore pdata @@ -270,7 +273,7 @@ sub refresh { 'simpleInfo', params => { trspan => 'rightsReloadNeedsLogout' } ) ); - $req->urldc( $self->conf->{portal} ); + $req->urldc( $req->portal ); return $self->do( $req, [ sub { PE_INFO } ] ); } return $self->do( $req, [ sub { PE_OK } ] ); @@ -354,7 +357,7 @@ sub do { $req, $json, code => 401, headers => [ - 'WWW-Authenticate' => "SSO " . $self->conf->{portal}, + 'WWW-Authenticate' => "SSO " . $req->portal, "Content-Type" => "application/json" ], ); @@ -450,7 +453,7 @@ sub autoRedirect { my ( $self, $req ) = @_; # Set redirection URL if needed - $req->{urldc} ||= $self->conf->{portal} + $req->{urldc} ||= $req->portal if ( $req->mustRedirect and not( $req->info ) ); # Redirection should be made if urldc defined @@ -880,7 +883,7 @@ sub info { sub fullUrl { my ( $self, $req ) = @_; - my $pHost = $self->conf->{portal}; + my $pHost = $req->portal; $pHost =~ s#^(https?://[^/]+)(?:/.*)?$#$1#; return $pHost . $req->env->{REQUEST_URI}; } @@ -1007,7 +1010,7 @@ sub sendHtml { $self->setCorsHeaderFromConfig($res); if ( $self->conf->{strictTransportSecurityMax_Age} - and $self->conf->{portal} =~ /^https:/ ) + and $req->portal =~ /^https:/ ) { push @{ $res->[1] }, 'Strict-Transport-Security' => @@ -1110,10 +1113,7 @@ sub sendImage { return [ 302, [ - 'Location' => $self->conf->{portal} - . $self->staticPrefix - . '/common/' - . $img, + 'Location' => ($req->portal . $self->staticPrefix . "/common/$img") ], [], ]; @@ -1161,7 +1161,7 @@ sub lmError { my %templateParams = ( MAIN_LOGO => $self->conf->{portalMainLogo}, LANGS => $self->conf->{showLanguages}, - LOGOUT_URL => $self->conf->{portal} . "?logout=1", + LOGOUT_URL => $self->buildUrl( $req->portal, { logout => 1 } ), URL => $req->{urldc}, ); @@ -1186,7 +1186,7 @@ sub tplParams { my ( $self, $req ) = @_; my %templateParams; - my $portalPath = $self->conf->{portal}; + my $portalPath = $req->portal; $portalPath =~ s#^https?://[^/]+/?#/#; $portalPath =~ s#[^/]+\.fcgi$##; @@ -1200,7 +1200,7 @@ sub tplParams { } return ( - PORTAL_URL => $self->conf->{portal}, + PORTAL_URL => $req->portal, MAIN_LOGO => $self->conf->{portalMainLogo}, LANGS => $self->conf->{showLanguages}, SCROLL_TOP => $self->conf->{scrollTop}, @@ -1334,7 +1334,7 @@ sub sendJSONresponse { # (Ajax SSL to a different VHost) # we allow CORS if ( $req->origin - and index( $self->conf->{portal}, $req->origin ) == 0 ) + and index( $req->portal, $req->origin ) == 0 ) { $self->logger->debug('AJAX request from portal, allowing CORS'); push @{ $res->[1] }, diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/CertificateResetByMail.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/CertificateResetByMail.pm index c33d2b5b8d645fad5a3c9843cce1ba2b8835d7d1..3c309dafbe8d669c564b97195ef977b0f9df7e4f 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/CertificateResetByMail.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/CertificateResetByMail.pm @@ -60,20 +60,6 @@ has registerModule => ( is => 'rw' ); # Captcha generator has captcha => ( is => 'rw' ); -# Reset URL to set in mail -has resetUrl => ( - is => 'ro', - lazy => 1, - default => sub { - my $self = $_[0]; - my $p = $self->conf->{portal}; - $p =~ s#/*$##; - return $self->conf->{certificateResetByMailURL} - ? $self->conf->{certificateResetByMailURL} - : "$p/certificateReset"; - } -); - # Mail timeout token generator has mailott => ( is => 'rw', @@ -338,13 +324,16 @@ sub _certificateReset { # Build confirmation url my $req_url = $req->data->{_url}; my $skin = $self->p->getSkin($req); - my $url = - $self->resetUrl . '?' - . build_urlencoded( - mail_token => $req->{id}, - skin => $skin, - ( $req_url ? ( url => $req_url ) : () ), - ); + my $url = $self->p->buildUrl( ( + $self->conf->{certificateResetByMailURL} + || ( $req->portal, "certificateReset" ) + ), + { + mail_token => $req->{id}, + skin => $skin, + ( $req_url ? ( url => $req_url ) : () ), + } + ); # Build mail content my $tr = $self->translate($req); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm index d9b923ff40cbb2631dba45b2582c4c99102f193c..930bf82f75b2aa8b4ec3dec12bf854093245b81c 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm @@ -178,7 +178,7 @@ sub run { } else { $self->logger->debug("contextSwitching NOT required"); - $req->urldc( $self->conf->{portal} ); + $req->urldc( $req->portal ); return $self->p->do( $req, [ sub { PE_OK } ] ); } @@ -290,7 +290,7 @@ sub _abortImpersonation { # Restore real session $req->{$type} = { %{ $session->data } }; $req->{user} = $session->data->{_user}; - $req->urldc( $self->conf->{portal} ); + $req->urldc( $req->portal ); $req->id($realSessionId); $self->p->buildCookie($req); delete $req->{$type}->{ $self->prefixedSId }; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ForceAuthn.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ForceAuthn.pm index 6a1d504755fa36e5bfb01477587b7fb4b205a1af..1982704c611b1828d5017905f3ebc8d266e3968f 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ForceAuthn.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/ForceAuthn.pm @@ -20,7 +20,7 @@ use constant forAuthUser => 'run'; sub run { my ( $self, $req ) = @_; if ( $req->env->{HTTP_HOST} - and $self->conf->{portal} =~ /\Q$req->{env}->{HTTP_HOST}/ ) + and $req->portal =~ /\Q$req->{env}->{HTTP_HOST}/ ) { my $delta = time - $req->{sessionInfo}->{_utime}; $self->logger->debug( "Delta with last Authn -> " . $delta ); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/GrantSession.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/GrantSession.pm index dd0fdf9dbf22c0663d40a777988cd51f27fe6b49..67e48c888454469ec8b560463596afce78c92b24 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/GrantSession.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/GrantSession.pm @@ -76,7 +76,7 @@ sub run { . $req->{sessionInfo}->{ $self->conf->{whatToTrace} } . '" was not granted to open session (rule ->' . "$rule)" ); - $req->urldc( $self->conf->{portal} ); + $req->urldc( $req->portal ); return $req->authResult(PE_SESSIONNOTGRANTED); } else { @@ -85,7 +85,7 @@ sub run { . '" was not granted to open session (rule -> ' . $self->conf->{grantSessionRules}->{$_} . ")" ); - $req->urldc( $self->conf->{portal} ); + $req->urldc( $req->portal ); return $req->authResult(PE_SESSIONNOTGRANTED); } } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/InitializePasswordReset.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/InitializePasswordReset.pm index 22244e14353c8859eea262df63a74aad962adaaa..181782b97e140c2542ec7795ece4f933540b8d9d 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/InitializePasswordReset.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/InitializePasswordReset.pm @@ -88,9 +88,10 @@ sub initializePasswordReset { my $mail_session_id = $mailSession->id; $response_params->{"mail_token"} = $mail_session_id; - $response_params->{"url"} = - $self->p->buildUrl( $self->p->passwordResetUrl, - { mail_token => $mail_session_id } ); + $response_params->{"url"} = $self->p->buildUrl( + $self->p->passwordResetUrl, + { mail_token => $mail_session_id } + ); return $self->sendJSONresponse( $req, $response_params ); } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm index d530eebfa5e32d4efd8b7f8107fa9bee58607792..18aef0755d41be464ccb49b44630733e6045a62d 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm @@ -43,18 +43,6 @@ extends qw( # PROPERTIES -# Reset URL to set in mail -has resetUrl => ( - is => 'ro', - lazy => 1, - default => sub { - my $self = $_[0]; - my $p = $self->conf->{portal}; - $p =~ s#/*$##; - return $self->conf->{mailUrl} ? $self->conf->{mailUrl} : "$p/resetpwd"; - } -); - # Mail timeout token generator # Form timout token generator (used even if requireToken is not set) has ott => ( @@ -333,13 +321,14 @@ sub _reset { # Build confirmation url my $req_url = $req->data->{_url}; my $skin = $self->p->getSkin($req); - my $url = - $self->resetUrl . '?' - . build_urlencoded( - mail_token => $req->{id}, - skin => $skin, - ( $req_url ? ( url => $req_url ) : () ), - ); + my $url = $self->p->buildUrl( + ( $self->conf->{mailUrl} || ( $req->portal, "resetpwd" ) ), + { + mail_token => $req->{id}, + skin => $skin, + ( $req_url ? ( url => $req_url ) : () ), + } + ); # Build mail content my $tr = $self->translate($req); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Notifications.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Notifications.pm index 25f9eb6f7a8033d40334e162c6dd401f2836b921..2f8781b84111cdebfb93e48e1b1accbffdf454bf 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Notifications.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Notifications.pm @@ -141,7 +141,7 @@ sub checkNotifDuringAuth { and $req->env->{PATH_INFO} ne '/' ) { $req->data->{_url} = - encode_base64( $self->conf->{portal} . $req->env->{PATH_INFO}, + encode_base64( $req->portal . $req->env->{PATH_INFO}, '' ); } 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 5aa01862be4943218f6864dc8e45d646427a4a78..99b0be36c8d3cd56849b9ad5ca2e3f348a9652ad 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm @@ -785,7 +785,7 @@ sub getUser { sub myApplications { my ( $self, $req ) = @_; - my $basePath = $self->conf->{portal}; + my $basePath = $req->portal; $basePath =~ s#/*$##; $basePath .= $self->p->{staticPrefix} . '/common/apps/'; my @appslist = map { @@ -819,7 +819,9 @@ sub languages { { code => $_, name => langName->{$_}, - flag => $self->conf->{portal}.$self->p->{staticPrefix} . "/languages/$_.png" + flag => $req->portal + . $self->p->{staticPrefix} + . "/languages/$_.png" } } split /[,;]\s*/, $self->p->{languages} diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Register.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Register.pm index 66c17b2b1a7137b88855315f95b7062d9bc3a326..8ff89d69b3640ccc4871fbcb43012bcfa68cbb33 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Register.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Register.pm @@ -35,20 +35,6 @@ extends qw( # Sub module (Demo, LDAP,...) has registerModule => ( is => 'rw' ); -# Register URL to set in mail -has registerUrl => ( - is => 'ro', - lazy => 1, - default => sub { - my $self = $_[0]; - my $p = $self->conf->{portal}; - $p =~ s#/*$##; - return $self->conf->{registerUrl} - ? $self->conf->{registerUrl} - : "$p/register"; - } -); - # Mail timeout token generator has mailott => ( is => 'rw', @@ -278,13 +264,14 @@ sub _register { # Build confirmation url my $req_url = $req->data->{_url}; my $skin = $self->p->getSkin($req); - my $url = - $self->registerUrl . '?' - . build_urlencoded( - register_token => $req->{id}, - skin => $skin, - ( $req_url ? ( url => $req_url ) : () ), - ); + my $url = $self->p->buildUrl( + ( $self->conf->{registerUrl} || ( $req->portal, "register" ) ), + { + register_token => $req->{id}, + skin => $skin, + ( $req_url ? ( url => $req_url ) : () ), + } + ); # Build mail content my $tr = $self->translate($req); @@ -361,15 +348,15 @@ sub _register { } # Build portal url - my $url = $self->conf->{portal}; - $url =~ s#/*$##; my $req_url = $req->data->{_url}; my $skin = $self->p->getSkin($req); - $url .= '/?' - . build_urlencoded( - skin => $skin, - ( $req_url ? ( url => $req_url ) : () ), - ); + my $url = $self->p->buildUrl( + $req->portal, + { + skin => $skin, + ( $req_url ? ( url => $req_url ) : () ), + } + ); # Build mail content my $tr = $self->translate($req); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm index 721232881eca1253ff3a45e28ee21de5aa4e1f9f..aec7fca27fce6152131dce22764bea5b557017c9 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm @@ -93,7 +93,7 @@ has wsdl => ( close DATA; $resp =~ s/\$cookieList/$cookieList/g; $resp =~ s/\$attrList/$attrList/g; - $resp =~ s/\$portal/$self->conf->{portal}/ge; + $resp =~ s/\$portal/$self->p->buildUrl/ge; return [ 200, [ diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SingleSession.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SingleSession.pm index 77687208e0f24863d801857a5ecbc62270953208..a9b7498f573c01b4482c24d4deaefc537e7bd329 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SingleSession.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/SingleSession.pm @@ -209,7 +209,8 @@ sub _mkRemoveOtherLink { $req, 'removeOther', params => { - link => $self->conf->{portal} . "removeOther?token=$token" + link => + $self->p->buildUrl( $req->portal, "removeOther", { token => $token } ), } ); } diff --git a/lemonldap-ng-portal/t/01-Cookie-Domain.t b/lemonldap-ng-portal/t/01-Cookie-Domain.t index d655d4584dcafa313a54874b5068dbe340bc4aef..502ba2f43c1d0515cd8e8d75b5d36e615662db46 100644 --- a/lemonldap-ng-portal/t/01-Cookie-Domain.t +++ b/lemonldap-ng-portal/t/01-Cookie-Domain.t @@ -32,6 +32,7 @@ sub assertStandardBehavior { { name => "coucou", value => 0 }, "coucou=0; path=/; HttpOnly=1; SameSite=Lax" ); + # cookie() with explicit domain assertCookieValue( $client, @@ -41,8 +42,7 @@ sub assertStandardBehavior { # Domain can be overwritten assertGenCookieValue( - $client, - $req, + $client, $req, { name => "coucou", value => 0, domain => "other.com" }, "coucou=0; domain=other.com; path=/; HttpOnly=1; SameSite=Lax" ); @@ -60,13 +60,11 @@ subtest "Behavior with domain = example.com" => sub { my $req = Lemonldap::NG::Portal::Main::Request->new( { PATH_INFO => "", REQUEST_URI => "/" } ); - - assertStandardBehavior($client, $req); + assertStandardBehavior( $client, $req ); # Domain is automatically set from portal config assertGenCookieValue( - $client, - $req, + $client, $req, { name => "coucou", value => 0 }, "coucou=0; domain=.example.com; path=/; HttpOnly=1; SameSite=Lax" ); @@ -85,12 +83,11 @@ subtest "Behavior with unset domain" => sub { my $req = Lemonldap::NG::Portal::Main::Request->new( { PATH_INFO => "", REQUEST_URI => "/" } ); - assertStandardBehavior($client, $req); + assertStandardBehavior( $client, $req ); # Domain is not automatically set assertGenCookieValue( - $client, - $req, + $client, $req, { name => "coucou", value => 0 }, "coucou=0; path=/; HttpOnly=1; SameSite=Lax" ); @@ -108,15 +105,13 @@ subtest "Behavior with special #PORTAL# value" => sub { # Stub Request object my $req = Lemonldap::NG::Portal::Main::Request->new( { PATH_INFO => "", REQUEST_URI => "/" } ); + $req->portal("http://auth.dynamic.com/"); - assertStandardBehavior($client, $req); + assertStandardBehavior( $client, $req ); # Domain is not automatically set - assertGenCookieValue( - $client, - $req, - { name => "coucou", value => 0 }, - "coucou=0; domain=.auth.example.com; path=/; HttpOnly=1; SameSite=Lax" + assertGenCookieValue( $client, $req, { name => "coucou", value => 0 }, + "coucou=0; domain=.auth.dynamic.com; path=/; HttpOnly=1; SameSite=Lax" ); }; @@ -132,15 +127,15 @@ subtest "Behavior with special #PORTALDOMAIN# value" => sub { # Stub Request object my $req = Lemonldap::NG::Portal::Main::Request->new( { PATH_INFO => "", REQUEST_URI => "/" } ); + $req->portal("http://auth.dynamic.com/"); - assertStandardBehavior($client, $req); + assertStandardBehavior( $client, $req ); # Domain is not automatically set assertGenCookieValue( - $client, - $req, + $client, $req, { name => "coucou", value => 0 }, - "coucou=0; domain=.example.com; path=/; HttpOnly=1; SameSite=Lax" + "coucou=0; domain=.dynamic.com; path=/; HttpOnly=1; SameSite=Lax" ); }; diff --git a/lemonldap-ng-portal/t/01-Dynamic-Portal-Url.t b/lemonldap-ng-portal/t/01-Dynamic-Portal-Url.t new file mode 100644 index 0000000000000000000000000000000000000000..1641742a5d95404106c7a50c43487db77496b8b6 --- /dev/null +++ b/lemonldap-ng-portal/t/01-Dynamic-Portal-Url.t @@ -0,0 +1,134 @@ +use warnings; +use Test::More; +use strict; +use IO::String; + +use Lemonldap::NG::Portal::Main::Constants qw( + PE_FIRSTACCESS +); + +BEGIN { + require 't/test-lib.pm'; + require 't/oidc-lib.pm'; +} + +my $res; +my $debug = "error"; + +# Handler part +use_ok('Lemonldap::NG::Handler::Server'); +use_ok('Lemonldap::NG::Common::PSGI::Cli::Lib'); + +my ( $handler, $portal ); +$portal = portal(); +$handler = Lemonldap::NG::Handler::Server->run( $portal->ini ); + +sub runTest { + my ( $portal, $handler, $domain ) = @_; + + $res = handler_req( $handler, "test1.$domain" ); + my ($query) = expectRedirection( $res, qr,http://auth\Q.$domain\E/\?(.*), ); + + ok( + $res = $portal->_get( + "/", + query => $query, + accept => "text/html", + host => "auth.$domain" + ) + ); + + ( my $host, my $uri, $query ) = + expectForm( $res, undef, undef, 'user', 'password' ); + $query =~ s/user=/user=dwho/; + $query =~ s/password=/password=dwho/; + + ok( + $res = $portal->_post( + '/', + $query, + accept => 'text/html', + host => "auth.$domain", + ), + 'Auth query' + ); + expectCookie($res); + my $set_cookie = getHeader( $res, "Set-Cookie" ); + like( $set_cookie, qr/domain=\Q.$domain\E/, + "Cookie set on correct domain" ); + expectRedirection( $res, qr,http://test1.$domain/, ); + + # Check dynamic portal URL in js + ok( + $res = $portal->_get( + "/psgi.js", + accept => "text/html", + host => "auth.$domain" + ) + ); + like( + $res->[2]->[0], + qr#portal="http://auth\Q.$domain\E/"#, + "Correct domain in psgi.js" + ); +} + +runTest( $portal, $handler, "example.com" ); +runTest( $portal, $handler, "acme.com" ); + +clean_sessions(); + +done_testing(); + +sub handler_req { + my ( $handler, $host, $cookie ) = @_; + return $handler->( { + 'HTTP_ACCEPT' => 'text/html', + 'SCRIPT_NAME' => '/', + 'SERVER_NAME' => '127.0.0.1', + 'HTTP_CACHE_CONTROL' => 'max-age=0', + 'PATH_INFO' => '/', + 'REQUEST_METHOD' => 'GET', + 'REQUEST_URI' => '/', + 'X_ORIGINAL_URI' => '/', + 'SERVER_PORT' => '80', + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'REMOTE_ADDR' => '127.0.0.1', + 'HTTP_HOST' => $host, + ( $cookie ? ( HTTP_COOKIE => "lemonldap=$cookie" ) : () ), + } + ); +} + +sub portal { + return LLNG::Manager::Test->new( { + ini => { + logLevel => $debug, + domain => '#PORTALDOMAIN#', + portal => +'inDomain("acme.com") ? "http://auth.acme.com/" : "http://auth.example.com/"', + authentication => 'Demo', + userDB => 'Same', + vhostOptions => { + 'test1.example.com' => { + 'vhostType' => 'Main' + }, + 'test1.acme.com' => { + 'vhostType' => 'Main' + }, + }, + locationRules => { + 'auth.example.com' => { + default => 'accept', + }, + 'test1.example.com' => { + 'default' => 'accept', + }, + 'test1.acme.com' => { + 'default' => 'accept', + }, + }, + } + } + ); +} diff --git a/lemonldap-ng-portal/t/31-Auth-and-issuer-CAS-with-choice-and-cancel.t b/lemonldap-ng-portal/t/31-Auth-and-issuer-CAS-with-choice-and-cancel.t index d6fd86ad13a23d84a2c6bc2b756e611241d27140..4b7f612b656766b0e04e77c55c426839b8f1729e 100644 --- a/lemonldap-ng-portal/t/31-Auth-and-issuer-CAS-with-choice-and-cancel.t +++ b/lemonldap-ng-portal/t/31-Auth-and-issuer-CAS-with-choice-and-cancel.t @@ -118,7 +118,7 @@ qr% # Found CAS idp logo and display name ok( $res->[2]->[0] =~ -qr%idp4%, +qr%idp4%, 'Found CAS idp logo and tooltip' ) or print STDERR Dumper( $res->[2]->[0] ); ok( $res->[2]->[0] =~ qr%CAS1%, 'Found CAS idp display name' ) diff --git a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-sorted.t b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-sorted.t index 525641dc332cc34a2ed132ee6801f1d6300adb3b..2ec681a5e983ebc4fe975add83b20374b945318a 100644 --- a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-sorted.t +++ b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-sorted.t @@ -98,7 +98,7 @@ count(3); # Found OIDC idp logo and display name ok( $res->[2]->[0] =~ -qr%op2%, +qr%op2%, 'Found OIDC idp logo and tooltip' ) or print STDERR Dumper( $res->[2]->[0] ); ok( $res->[2]->[0] =~ qr%idpOPtest%, 'Found OIDC idp display name' )