--- loncom/auth/lonlogin.pm 2021/01/04 03:49:10 1.158.2.12 +++ loncom/auth/lonlogin.pm 2021/12/12 11:47:03 1.158.2.13 @@ -1,7 +1,7 @@ # The LearningOnline Network # Login Screen # -# $Id: lonlogin.pm,v 1.158.2.12 2021/01/04 03:49:10 raeburn Exp $ +# $Id: lonlogin.pm,v 1.158.2.13 2021/12/12 11:47:03 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -38,6 +38,8 @@ use Apache::lonlocal; use Apache::migrateuser(); use lib '/home/httpd/lib/perl/'; use LONCAPA qw(:DEFAULT :match); +use URI::Escape; +use HTML::Entities(); use CGI::Cookie(); sub handler { @@ -47,16 +49,39 @@ sub handler { (join('&',$ENV{'QUERY_STRING'},$env{'request.querystring'}, $ENV{'REDIRECT_QUERY_STRING'}), ['interface','username','domain','firsturl','localpath','localres', - 'token','role','symb','iptoken','btoken']); - if (!defined($env{'form.firsturl'})) { - &Apache::lonacc::get_posted_cgi($r,['firsturl']); - } + 'token','role','symb','iptoken','btoken','saml','sso','retry']); # -- check if they are a migrating user if (defined($env{'form.token'})) { return &Apache::migrateuser::handler($r); } + my $lonhost = $r->dir_config('lonHostID'); + if (($env{'form.sso'}) || ($env{'form.retry'})) { + my $infotoken; + if ($env{'form.sso'}) { + $infotoken = $env{'form.sso'}; + } else { + $infotoken = $env{'form.retry'}; + } + my $data = &Apache::lonnet::reply('tmpget:'.$infotoken,$lonhost); + unless (($data=~/^error/) || ($data eq 'con_lost') || + ($data eq 'no_such_host')) { + my %info = &decode_token($data); + foreach my $item (keys(%info)) { + $env{'form.'.$item} = $info{$item}; + } + &Apache::lonnet::tmpdel($infotoken); + } + } else { + if (!defined($env{'form.firsturl'})) { + &Apache::lonacc::get_posted_cgi($r,['firsturl']); + } + if ($env{'form.firsturl'} eq '/adm/logout') { + delete($env{'form.firsturl'}); + } + } + # For "public user" - remove any exising "public" cookie, as user really wants to log-in my ($handle,$lonidsdir,$expirepub,$userdom); $lonidsdir=$r->dir_config('lonIDsDir'); @@ -101,9 +126,6 @@ sub handler { return OK; } - my $lonhost = $r->dir_config('lonHostID'); - $env{'form.firsturl'} =~ s/(`)/'/g; - # Check if browser sent a LON-CAPA load balancer cookie (and this is a balancer) my ($found_server,$balancer_cookie) = &Apache::lonnet::check_for_balancer_cookie($r,1); @@ -114,14 +136,25 @@ sub handler { $protocol = 'http' if ($protocol ne 'https'); my $dest = '/adm/roles'; if ($env{'form.firsturl'} ne '') { - $dest = $env{'form.firsturl'}; + $dest = &HTML::Entities::encode($env{'form.firsturl'},'\'"<>&'); } my %info = ( balcookie => $lonhost.':'.$balancer_cookie, ); + if ($env{'form.role'}) { + $info{'role'} = $env{'form.role'}; + } + if ($env{'form.symb'}) { + $info{'symb'} = $env{'form.symb'}; + } my $balancer_token = &Apache::lonnet::tmpput(\%info,$found_server); - if ($balancer_token) { - $dest .= (($dest=~/\?/)?'&;':'?') . 'btoken='.$balancer_token; + unless (($balancer_token eq 'con_lost') || ($balancer_token eq 'refused') || + ($balancer_token eq 'unknown_cmd') || ($balancer_token eq 'no_such_host')) { + $dest .= (($dest=~/\?/)?'&':'?') . 'btoken='.$balancer_token; + } + unless ($found_server eq $lonhost) { + my $alias = &Apache::lonnet::use_proxy_alias($r,$found_server); + $hostname = $alias if ($alias ne ''); } my $url = $protocol.'://'.$hostname.$dest; my $start_page = @@ -163,8 +196,6 @@ sub handler { } } - $env{'form.firsturl'} =~ s/(`)/'/g; - # -------------------------------- Prevent users from attempting to login twice if ($handle ne '') { &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle); @@ -174,7 +205,7 @@ sub handler { &Apache::loncommon::end_page(); my $dest = '/adm/roles'; if ($env{'form.firsturl'} ne '') { - $dest = $env{'form.firsturl'}; + $dest = &HTML::Entities::encode($env{'form.firsturl'},'\'"<>&'); } $r->print( $start_page @@ -207,7 +238,7 @@ sub handler { $env{'form.interface'}=~s/\W//g; (undef,undef,undef,undef,undef,undef,my $clientmobile) = - &Apache::loncommon::decode_user_agent(); + &Apache::loncommon::decode_user_agent($r); my $iconpath= &Apache::loncommon::lonhttpdurl($r->dir_config('lonIconsURL')); @@ -301,23 +332,14 @@ sub handler { # -------------------------------------------------------- Store away log token my $tokenextras; - if ($env{'form.role'}) { - $tokenextras = '&role='.&escape($env{'form.role'}); - } - if ($env{'form.symb'}) { - if (!$tokenextras) { - $tokenextras = '&'; - } - $tokenextras .= '&symb='.&escape($env{'form.symb'}); - } - if ($env{'form.iptoken'}) { - if (!$tokenextras) { - $tokenextras = '&&'; + my @names = ('role','symb','iptoken'); + foreach my $name (@names) { + if ($env{'form.'.$name} ne '') { + $tokenextras .= '&'.$name.'='.&escape($env{'form.'.$name}); } - $tokenextras .= '&iptoken='.&escape($env{'form.iptoken'}); } my $logtoken=Apache::lonnet::reply( - 'tmpput:'.$ukey.$lkey.'&'.$firsturl.$tokenextras, + 'tmpput:'.$ukey.$lkey.'&'.&escape($firsturl).$tokenextras, $lonhost); # -- If we cannot talk to ourselves, or hostID does not map to a hostname @@ -475,17 +497,9 @@ function enableInput() { ENDSCRIPT -# --------------------------------------------------- Print login screen header - - my %add_entries = ( - bgcolor => "$mainbg", - text => "$font", - link => "$link", - vlink => "$vlink", - alink => "$alink", - onload => 'javascript:enableInput();',); - - my ($lonhost_in_use,$headextra,$headextra_exempt,@hosts,%defaultdomconf); + my ($lonhost_in_use,@hosts,%defaultdomconf,$saml_prefix,$saml_landing, + $samlssotext,$samlnonsso,$samlssoimg,$samlssoalt,$samlssourl,$samltooltip); + %defaultdomconf = &Apache::loncommon::get_domainconf($defdom); @hosts = &Apache::lonnet::current_machine_ids(); $lonhost_in_use = $lonhost; if (@hosts > 1) { @@ -496,7 +510,67 @@ ENDSCRIPT } } } - %defaultdomconf = &Apache::loncommon::get_domainconf($defdom); + $saml_prefix = $defdom.'.login.saml_'; + if ($defaultdomconf{$saml_prefix.$lonhost_in_use}) { + $saml_landing = 1; + $samlssotext = $defaultdomconf{$saml_prefix.'text_'.$lonhost_in_use}; + $samlnonsso = $defaultdomconf{$saml_prefix.'notsso_'.$lonhost_in_use}; + $samlssoimg = $defaultdomconf{$saml_prefix.'img_'.$lonhost_in_use}; + $samlssoalt = $defaultdomconf{$saml_prefix.'alt_'.$lonhost_in_use}; + $samlssourl = $defaultdomconf{$saml_prefix.'url_'.$lonhost_in_use}; + $samltooltip = $defaultdomconf{$saml_prefix.'title_'.$lonhost_in_use}; + } + if ($saml_landing) { + if ($samlssotext eq '') { + $samlssotext = 'SSO Login'; + } + if ($samlnonsso eq '') { + $samlnonsso = 'Non-SSO Login'; + } + $js .= <<"ENDSAMLJS"; + + + +ENDSAMLJS + } + +# --------------------------------------------------- Print login screen header + + my %add_entries = ( + bgcolor => "$mainbg", + text => "$font", + link => "$link", + vlink => "$vlink", + alink => "$alink", + onload => 'javascript:enableInput();',); + + my ($headextra,$headextra_exempt); $headextra = $defaultdomconf{$defdom.'.login.headtag_'.$lonhost_in_use}; $headextra_exempt = $defaultdomconf{$domain.'.login.headtag_exempt_'.$lonhost_in_use}; if ($headextra) { @@ -541,6 +615,7 @@ ENDSCRIPT 'helpdesk' => 'Contact Helpdesk', 'forgotpw' => 'Forgot password?', 'newuser' => 'New User?', + 'change' => 'Change?', ); # -------------------------------------------------- Change password field name @@ -602,7 +677,7 @@ ENDSERVERFORM $mobileargs = 'autocapitalize="off" autocorrect="off"'; } my $loginform=(< +
:
@@ -623,8 +698,70 @@ LFORM HEADER } - $r->print(< + + my $stdauthformstyle = 'inline-block'; + my $ssoauthstyle = 'none'; + my $logintype; + $r->print('
'); + if ($saml_landing) { + $ssoauthstyle = 'inline-block'; + $stdauthformstyle = 'none'; + $logintype = $samlssotext; + my $ssologin = '/adm/sso'; + if ($samlssourl ne '') { + $ssologin = $samlssourl; + } + if (($logtoken eq 'con_lost') || ($logtoken eq 'no_such_host')) { + my $querystring; + if ($env{'form.firsturl'} ne '') { + $querystring = 'origurl='; + if ($env{'form.firsturl'} =~ /[^\x00-\xFF]/) { + $querystring .= &uri_escape_utf8($env{'form.firsturl'}); + } else { + $querystring .= &uri_escape($env{'form.firsturl'}); + } + $querystring = &HTML::Entities::encode($querystring,"'"); + } + if ($querystring ne '') { + $ssologin .= (($ssologin=~/\?/)?'&':'?') . $querystring; + } + } elsif ($logtoken ne '') { + $ssologin .= (($ssologin=~/\?/)?'&':'?') . 'logtoken='.$logtoken; + } + my $ssohref; + if ($samlssoimg ne '') { + $ssohref = ''.$samlssoalt.''; + } else { + $ssohref = ''.$samlssotext.''; + } + if (($env{'form.saml'} eq 'no') || + (($env{'form.username'} ne '') && ($env{'form.domain'} ne ''))) { + $ssoauthstyle = 'none'; + $stdauthformstyle = 'inline-block'; + $logintype = $samlnonsso; + } + $r->print(< +Log-in type: +$logintype
+$lt{'change'} +

+
+
+$ssohref +$noscript_warning +
+
+$loginhelp +$contactblock +$coursecatalog +
+
+ENDSAML + } + + $r->print(<
$logintitle $loginform @@ -640,8 +777,8 @@ HEADER
-
-ENDTOP +ENDLOGIN + $r->print('
'."\n"); if ($showmainlogo) { $r->print(' '."\n"); } @@ -814,7 +951,14 @@ sub redirect_page { } my $url = $protocol.'://'.$hostname.$path; if ($env{'form.firsturl'} ne '') { - $url .='?firsturl='.$env{'form.firsturl'}; + my $querystring; + if ($env{'form.firsturl'} =~ /[^\x00-\xFF]/) { + $querystring = &uri_escape_utf8($env{'form.firsturl'}); + } else { + $querystring = &uri_escape($env{'form.firsturl'}); + } + $querystring = &HTML::Entities::encode($querystring,"'"); + $url .='?firsturl='.$querystring; } my $start_page = &Apache::loncommon::start_page('Switching Server ...',undef, {'redirect' => [0,$url],}); @@ -890,5 +1034,19 @@ sub newuser_link { return ''.$linkname.''; } +sub decode_token { + my ($info) = @_; + my ($firsturl,@rest)=split(/\&/,$info); + my %form; + if ($firsturl ne '') { + $form{'firsturl'} = &unescape($firsturl); + } + foreach my $item (@rest) { + my ($key,$value) = split(/=/,$item); + $form{$key} = &unescape($value); + } + return %form; +} + 1; __END__