--- loncom/auth/lonauth.pm 2021/08/10 15:28:13 1.166 +++ loncom/auth/lonauth.pm 2021/11/17 00:44:47 1.170 @@ -1,7 +1,7 @@ # The LearningOnline Network # User Authentication Module # -# $Id: lonauth.pm,v 1.166 2021/08/10 15:28:13 raeburn Exp $ +# $Id: lonauth.pm,v 1.170 2021/11/17 00:44:47 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -42,11 +42,12 @@ use Apache::lonlocal; use Apache::File(); use HTML::Entities; use Digest::MD5; +use CGI::Cookie(); # ------------------------------------------------------------ Successful login sub success { my ($r, $username, $domain, $authhost, $lowerurl, $extra_env, - $form,$skipcritical,$cid) = @_; + $form,$skipcritical,$cid,$expirepub) = @_; # ------------------------------------------------------------ Get cookie ready my $cookie = @@ -178,6 +179,22 @@ sub success { $destination .= 'source=login'; } + if (($env{'request.deeplink.login'} eq $lowerurl) && + (($env{'request.linkprot'}) || ($env{'request.linkkey'} ne ''))) { + my %info; + if ($env{'request.linkprot'}) { + $info{'linkprot'} = $env{'request.linkprot'}; + } elsif ($env{'request.linkkey'} ne '') { + $info{'linkkey'} = $env{'request.linkkey'}; + } + $info{'origurl'} = $lowerurl; + my $token = &Apache::lonnet::tmpput(\%info,$r->dir_config('lonHostID'),'link'); + unless (($token eq 'con_lost') || ($token eq 'refused') || + ($token eq 'unknown_cmd') || ($token eq 'no_such_host')) { + $destination .= (($destination =~ /\?/) ? '&' : '?') . 'ttoken='.$token; + } + } + my $windowname = 'loncapaclient'; if ($env{'request.lti.login'}) { $windowname .= 'lti'; @@ -217,6 +234,12 @@ sub success { if ($defaultcookie) { $r->headers_out->add('Set-cookie' => $defaultcookie); } + if ($expirepub) { + my $c = new CGI::Cookie(-name => 'lonPubID', + -value => '', + -expires => '-10y',); + $r->headers_out->add('Set-cookie' => $c); + } $r->send_http_header; my ($start_page,$js,$pagebody,$end_page); @@ -298,37 +321,44 @@ sub failed { if ($udom) { $retry .= (($retry=~/\?/)?'&':'?').'domain='.$udom; } - if (exists($form->{role})) { - my $role = &Apache::loncommon::cleanup_html($form->{role}); - if ($role ne '') { - $retry .= (($retry=~/\?/)?'&':'?').'role='.$role; - } - } - if (exists($form->{symb})) { - my $symb = &Apache::loncommon::cleanup_html($form->{symb}); - if ($symb ne '') { - $retry .= (($retry=~/\?/)?'&':'?').'symb='.$symb; - } - } - if (exists($form->{firsturl})) { - my $firsturl = &Apache::loncommon::cleanup_html($form->{firsturl}); - if ($firsturl ne '') { - $retry .= (($retry=~/\?/)?'&':'?').'firsturl='.$firsturl; - if ($firsturl =~ m{^/tiny/$match_domain/\w+$}) { - unless (exists($form->{linkprot})) { - if (exists($form->{linkkey})) { - $retry .= 'linkkey='.$form->{linkkey}; + my $lonhost = $r->dir_config('lonHostID'); + my $querystr; + my $result = &set_retry_token($form,$lonhost,\$querystr); + if ($result eq 'fail') { + if (exists($form->{role})) { + my $role = &Apache::loncommon::cleanup_html($form->{role}); + if ($role ne '') { + $retry .= (($retry=~/\?/)?'&':'?').'role='.$role; + } + } + if (exists($form->{symb})) { + my $symb = &Apache::loncommon::cleanup_html($form->{symb}); + if ($symb ne '') { + $retry .= (($retry=~/\?/)?'&':'?').'symb='.$symb; + } + } + if (exists($form->{firsturl})) { + my $firsturl = &Apache::loncommon::cleanup_html($form->{firsturl}); + if ($firsturl ne '') { + $retry .= (($retry=~/\?/)?'&':'?').'firsturl='.$firsturl; + if ($form->{firsturl} =~ m{^/tiny/$match_domain/\w+$}) { + unless (exists($form->{linkprot})) { + if (exists($form->{linkkey})) { + $retry .= 'linkkey='.$form->{linkkey}; + } } } } } - } - if (exists($form->{linkprot})) { - my $ltoken = &Apache::lonnet::tmpput({linkprot => $form->{'linkprot'}}, - $r->dir_config('lonHostID')); - if ($ltoken) { - $retry .= (($retry =~ /\?/) ? '&' : '?').'ltoken='.$ltoken; + if (exists($form->{linkprot})) { + my $ltoken = &Apache::lonnet::tmpput({linkprot => $form->{'linkprot'}}, + $r->dir_config('lonHostID'),'retry'); + if ($ltoken) { + $retry .= (($retry =~ /\?/) ? '&' : '?').'ltoken='.$ltoken; + } } + } elsif ($querystr ne '') { + $retry .= (($retry=~/\?/)?'&':'?').$querystr; } my $end_page = &Apache::loncommon::end_page(); &Apache::loncommon::content_type($r,'text/html'); @@ -391,44 +421,71 @@ sub handler { my $end_page = &Apache::loncommon::end_page(); my $dest = '/adm/roles'; - if ($env{'form.firsturl'} ne '') { - $dest = $env{'form.firsturl'}; - if (($dest =~ m{^/tiny/$match_domain/\w+$}) && ($env{'request.course.id'})) { - my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - my $symb = &Apache::loncommon::symb_from_tinyurl($dest,$cnum,$cdom); - if ($symb) { - my $buffer; - if ($r->header_in('Content-length') > 0) { - $r->read($buffer,$r->header_in('Content-length'),0); - } - my %form; - foreach my $pair (split(/&/,$buffer)) { - my ($name,$value) = split(/=/,$pair); - $value =~ tr/+/ /; - $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; - $form{$name}=$value; + my %form = &get_form_items($r); + if ($form{'logtoken'}) { + my $tmpinfo = &Apache::lonnet::reply('tmpget:'.$form{'logtoken'}, + $form{'serverid'}); + unless (($tmpinfo=~/^error/) || ($tmpinfo eq 'con_lost') || + ($tmpinfo eq 'no_such_host')) { + my ($des_key,$firsturl,@rest)=split(/&/,$tmpinfo); + $firsturl = &unescape($firsturl); + my %info; + foreach my $item (@rest) { + my ($key,$value) = split(/=/,$item); + $info{$key} = &unescape($value); + } + if ($firsturl ne '') { + $info{'firsturl'} = $firsturl; + $dest = $firsturl; + my $relogin; + if ($dest =~ m{^/tiny/$match_domain/\w+$}) { + if ($env{'request.course.id'}) { + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $symb = &Apache::loncommon::symb_from_tinyurl($dest,$cnum,$cdom); + if ($symb) { + unless (&set_deeplink_login(%info) eq 'ok') { + $relogin = 1; + } + } + } + if ($relogin) { + $r->print( + $start_page + .'

'.&mt('You are already logged in!').'

' + .'

'.&mt('Please [_1]log out[_2] first, and then try your access again', + '','') + .'

' + .$end_page); + } else { + if (($info{'linkprot'}) || ($info{'linkkey'} ne '')) { + my $token = &Apache::lonnet::tmpput(\%info,$r->dir_config('lonHostID'),'link'); + unless (($token eq 'con_lost') || ($token eq 'refused') || + ($token eq 'unknown_cmd') || ($token eq 'no_such_host')) { + $dest .= (($dest =~ /\?/) ? '&' : '?') . 'ttoken='.$token; + } + } + $r->print( + $start_page + .'

'.&mt('You are already logged in!').'

' + .'

'.&mt('Please either [_1]continue the current session[_2] or [_3]log out[_4] first, and then try your access again', + '','', + '','') + .'

' + .$end_page); + } + return OK; } - &set_deeplink_login(%form); - } else { - $r->print( - $start_page - .'

'.&mt('You are already logged in!').'

' - .'

'.&mt('Please [_1]log out[_2] first, and then try your access again', - '','') - .'

' - .$end_page); - return OK; } } } $r->print( - $start_page - .'

'.&mt('You are already logged in!').'

' - .'

'.&mt('Please either [_1]continue the current session[_2] or [_3]log out[_4].' - ,'','','','') - .'

' - .$end_page + $start_page + .'

'.&mt('You are already logged in!').'

' + .'

'.&mt('Please either [_1]continue the current session[_2] or [_3]log out[_4].' + ,'','','','') + .'

' + .$end_page ); return OK; } @@ -436,19 +493,7 @@ sub handler { # ---------------------------------------------------- No valid token, continue - - my $buffer; - if ($r->header_in('Content-length') > 0) { - $r->read($buffer,$r->header_in('Content-length'),0); - } - my %form; - foreach my $pair (split(/&/,$buffer)) { - my ($name,$value) = split(/=/,$pair); - $value =~ tr/+/ /; - $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; - $form{$name}=$value; - } - + my %form = &get_form_items($r); if ((!$form{'uname'}) || (!$form{'upass0'}) || (!$form{'udom'})) { &failed($r,'Username, password and domain need to be specified.', \%form); @@ -492,39 +537,17 @@ sub handler { return OK; } - my ($key,$firsturl,$rolestr,$symbstr,$iptokenstr,$linkstr)=split(/&/,$tmpinfo); - if ($rolestr) { - $rolestr = &unescape($rolestr); - } - if ($symbstr) { - $symbstr= &unescape($symbstr); - } - if ($iptokenstr) { - $iptokenstr = &unescape($iptokenstr); - } - if ($linkstr) { - $linkstr = &unescape($linkstr); + my ($des_key,$firsturl,@rest)=split(/&/,$tmpinfo); + $firsturl = &unescape($firsturl); + foreach my $item (@rest) { + my ($key,$value) = split(/=/,$item); + $form{$key} = &unescape($value); } if ($firsturl =~ m{^/tiny/$match_domain/\w+$}) { $form{'firsturl'} = $firsturl; } - if ($rolestr =~ /^role=/) { - (undef,$form{'role'}) = split('=',$rolestr); - } - if ($symbstr =~ /^symb=/) { - (undef,$form{'symb'}) = split('=',$symbstr); - } - if ($iptokenstr =~ /^iptoken=/) { - (undef,$form{'iptoken'}) = split('=',$iptokenstr); - } - if ($linkstr =~ /^linkprot=/) { - (undef,$form{'linkprot'}) = split('=',$linkstr); - } elsif ($linkstr =~ /^linkkey=/) { - (undef,$form{'linkkey'}) = split('=',$linkstr); - } - my $upass = $ENV{HTTPS} ? $form{'upass0'} - : &Apache::loncommon::des_decrypt($key,$form{'upass0'}); + : &Apache::loncommon::des_decrypt($des_key,$form{'upass0'}); # ---------------------------------------------------------------- Authenticate @@ -555,7 +578,8 @@ sub handler { return OK; } my $start_page = - &Apache::loncommon::start_page('Create a user account in LON-CAPA'); + &Apache::loncommon::start_page('Create a user account in LON-CAPA', + '',{'no_inline_link' => 1,}); my $lonhost = $r->dir_config('lonHostID'); my $origmail = $Apache::lonnet::perlvar{'lonSupportEMail'}; my $contacts = @@ -585,9 +609,9 @@ sub handler { $firsturl='/adm/roles'; } - my $hosthere; + my ($hosthere,%sessiondata); if ($form{'iptoken'}) { - my %sessiondata = &Apache::lonnet::tmpget($form{'iptoken'}); + %sessiondata = &Apache::lonnet::tmpget($form{'iptoken'}); my $delete = &Apache::lonnet::tmpdel($form{'iptoken'}); if (($sessiondata{'domain'} eq $form{'udom'}) && ($sessiondata{'username'} eq $form{'uname'})) { @@ -631,7 +655,7 @@ sub handler { unless ($suprim eq $uprim) { unless ($suintdom eq $uintdom) { &Apache::lonnet::logthis('Attempted switch user ' - .'to user with different "internet domain".'); + .'to user with different "internet domain".'); $noprivswitch = 1; } } @@ -699,7 +723,7 @@ sub handler { \%form); my $switchto = '/adm/switchserver?otherserver='.$otherserver; if (($firsturl) && ($firsturl ne '/adm/switchserver') && ($firsturl ne '/adm/roles')) { - $switchto .= '&origurl='.$firsturl; #should escape + $switchto .= '&origurl='.$firsturl; } if ($form{'role'}) { $switchto .= '&role='.$form{'role'}; @@ -709,7 +733,7 @@ sub handler { } if ($form{'linkprot'}) { $env{'request.linkprot'} = $form{'linkprot'}; - } elsif ($form{'linkkey'}) { + } elsif ($form{'linkkey'} ne '') { $env{'request.linkkey'} = $form{'linkkey'}; } if ($form{'firsturl'} =~ m{^/tiny/$match_domain/\w+$}) { @@ -730,7 +754,7 @@ sub handler { \%form); my $switchto = '/adm/switchserver?otherserver='.$otherserver; if (($firsturl) && ($firsturl ne '/adm/switchserver') && ($firsturl ne '/adm/roles')) { - $switchto .= '&origurl='.$firsturl; #should escape + $switchto .= '&origurl='.$firsturl; } if ($form{'role'}) { $switchto .= '&role='.$form{'role'}; @@ -740,7 +764,7 @@ sub handler { } if ($form{'linkprot'}) { $env{'request.linkprot'} = $form{'linkprot'}; - } elsif ($form{'linkkey'}) { + } elsif ($form{'linkkey'} ne '') { $env{'request.linkkey'} = $form{'linkkey'}; } if ($form{'firsturl'} =~ m{^/tiny/$match_domain/\w+$}) { @@ -779,7 +803,7 @@ sub handler { undef,\%form); if ($form{'linkprot'}) { $env{'request.linkprot'} = $form{'linkprot'}; - } elsif ($form{'linkkey'}) { + } elsif ($form{'linkkey'} ne '') { $env{'request.linkkey'} = $form{'linkkey'}; } if ($form{'firsturl'} =~ m{^/tiny/$match_domain/\w+$}) { @@ -793,13 +817,25 @@ sub handler { $form{'noloadbalance'} = $hosthere; } my $extra_env; + if (($hosthere) && ($sessiondata{'sessionserver'} ne '')) { + if ($sessiondata{'origurl'} ne '') { + $firsturl = $sessiondata{'origurl'}; + $form{'firsturl'} = $sessiondata{'origurl'}; + my @names = ('role','symb','linkprot','linkkey'); + foreach my $item (@names) { + if ($sessiondata{$item} ne '') { + $form{$item} = $sessiondata{$item}; + } + } + } + } if ($form{'linkprot'}) { my ($linkprotector,$uri) = split(/:/,$form{'linkprot'},2); if ($linkprotector) { $extra_env = {'user.linkprotector' => $linkprotector, 'user.linkproturi' => $uri}; } - } elsif ($form{'linkkey'}) { + } elsif ($form{'linkkey'} ne '') { $extra_env = {'user.deeplinkkey' => $form{'linkkey'}, 'user.keyedlinkuri' => $form{'firsturl'}}; } @@ -811,7 +847,7 @@ sub handler { } else { $extra_env = {'request.linkprot' => $form{'linkprot'}}; } - } elsif ($form{'linkkey'}) { + } elsif ($form{'linkkey'} ne '') { if (ref($extra_env) eq 'HASH') { %{$extra_env} = ( %{$extra_env}, 'request.linkkey' => $form{'linkkey'} ); } else { @@ -832,14 +868,31 @@ sub handler { } } +sub get_form_items { + my ($r) = @_; + my $buffer; + if ($r->header_in('Content-length') > 0) { + $r->read($buffer,$r->header_in('Content-length'),0); + } + my %form; + foreach my $pair (split(/&/,$buffer)) { + my ($name,$value) = split(/=/,$pair); + $value =~ tr/+/ /; + $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; + $form{$name}=$value; + } + return %form; +} + sub set_deeplink_login { my (%form) = @_; + my $disallow; if ($form{'firsturl'} =~ m{^/tiny/($match_domain)/\w+$}) { my $cdom = $1; my ($cnum,$symb) = &Apache::loncommon::symb_from_tinyurl($form{'firsturl'},'',$cdom); if ($symb) { if ($env{'request.course.id'} eq $cdom.'_'.$cnum) { - my ($disallow,$deeplink); + my $deeplink; if ($symb =~ /\.(page|sequence)$/) { my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($symb))[2]); my $navmap = Apache::lonnavmaps::navmap->new(); @@ -872,6 +925,38 @@ sub set_deeplink_login { } } } + if ($disallow) { + return; + } + return 'ok'; +} + +sub set_retry_token { + my ($form,$lonhost,$querystr) = @_; + if (ref($form) eq 'HASH') { + my ($firsturl,$token,$extras,@names); + @names = ('role','symb','linkprot','linkkey','iptoken'); + foreach my $name (@names) { + if ($form->{$name} ne '') { + $extras .= '&'.$name.'='.&escape($form->{$name}); + last if ($name eq 'linkprot'); + } + } + my $firsturl = $form->{'firsturl'}; + if (($firsturl ne '') || ($extras ne '')) { + $extras .= ':retry'; + $token = &Apache::lonnet::reply('tmpput:'.&escape($firsturl). + $extras,$lonhost); + if (($token eq 'con_lost') || ($token eq 'no_such_host')) { + return 'fail'; + } else { + if (ref($querystr)) { + $$querystr = 'retry='.$token; + } + return 'ok'; + } + } + } return; } @@ -935,13 +1020,13 @@ sub check_can_host { } else { &success($r,$form->{'uname'},$udom,$authhost,'noredirect',undef, $form); + if ($form->{'firsturl'} =~ m{^/tiny/$match_domain/\w+$}) { + $env{'request.deeplink.login'} = $form->{'firsturl'}; + } if ($form->{'linkprot'}) { $env{'request.linkprot'} = $form->{'linkprot'}; - } elsif ($form->{'firsturl'} =~ m{^/tiny/$match_domain/\w+$}) { - if ($form->{'linkkey'}) { - $env{'request.linkkey'} = $form->{'linkkey'}; - } - $env{'request.deeplink.login'} = $form->{'firsturl'}; + } elsif ($form->{'linkkey'} ne '') { + $env{'request.linkkey'} = $form->{'linkkey'}; } my ($otherserver) = &Apache::lonnet::choose_server($udom); $r->internal_redirect('/adm/switchserver?otherserver='.$otherserver);