[LON-CAPA-cvs] cvs: doc /loncapafiles loncapafiles.lpml loncom loncapa_apache.conf lontrans.pm loncom/auth lonacc.pm lonauth.pm lonlogin.pm lonrelaunch.pm lonshibauth.pm migrateuser.pm switchserver.pm loncom/lti ltiauth.pm

raeburn raeburn at source.lon-capa.org
Fri Jun 17 22:10:20 EDT 2022


raeburn		Sat Jun 18 02:10:20 2022 EDT

  Added files:                 
    /loncom/auth	lonrelaunch.pm 

  Modified files:              
    /loncom/auth	lonacc.pm lonauth.pm lonlogin.pm lonshibauth.pm 
                	migrateuser.pm switchserver.pm 
    /loncom/lti	ltiauth.pm 
    /loncom	lontrans.pm loncapa_apache.conf 
    /doc/loncapafiles	loncapafiles.lpml 
  Log:
  - Bug 6907
    For LTI-protected deep links in which username is included in launch payload 
    compare username in payload with username for any existing LON-CAPA session 
    in current web browser and expire old session, if different user. 
  
  
-------------- next part --------------
Index: loncom/auth/lonacc.pm
diff -u loncom/auth/lonacc.pm:1.202 loncom/auth/lonacc.pm:1.203
--- loncom/auth/lonacc.pm:1.202	Tue Dec  7 23:19:00 2021
+++ loncom/auth/lonacc.pm	Sat Jun 18 02:10:18 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Cookie Based Access Handler
 #
-# $Id: lonacc.pm,v 1.202 2021/12/07 23:19:00 raeburn Exp $
+# $Id: lonacc.pm,v 1.203 2022/06/18 02:10:18 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -281,10 +281,18 @@
         be identified by the third arg ($usename), except when lonacc is called in 
         an internal redirect to /adm/switchserver (e.g., load-balancing following
         successful authentication) -- no cookie set yet.  For that particular case
-        simply skip the call to sso_login(). 
+        simply skip the call to sso_login().
 
 	returns OK if it was SSO and user was handled.
         returns undef if not SSO or no means to handle the user.
+
+        In the case where the session was started from /adm/launch/tiny/$domain/$id,
+        i.e., for a protected link, with launch from another CMS, and user information
+        is accepted from the LTI payload, then, if the user has privileged roles,
+        authentication will be required.  If SSO authentication is with a username
+        and/or domain that differ from the username in the LTI payload and domain
+        in the launch URL, then $r->user() will be unset and /adm/relaunch will be
+        called.
         
 =cut
 
@@ -325,7 +333,7 @@
         }
     }
 
-    my ($linkprot,$linkkey);
+    my ($linkprot,$linkprotuser,$linkkey,$deeplinkurl);
 
 #
 # If Shibboleth auth is in use, and a dual SSO and non-SSO login page
@@ -357,9 +365,13 @@
         &Apache::lonnet::tmpdel($form{'ttoken'});
         if ($info{'origurl'}) {
             $form{'origurl'} = $info{'origurl'};
+            if ($form{'origurl'} =~ m{^/tiny/$match_domain/\w+$}) {
+                $deeplinkurl = $form{'origurl'};
+            }
         }
         if ($info{'linkprot'}) {
             $linkprot = $info{'linkprot'};
+            $linkprotuser = $info{'linkprotuser'};
         } elsif ($info{'linkkey'} ne '') {
             $linkkey = $info{'linkkey'};
         }
@@ -380,9 +392,11 @@
             }
             if ($firsturl =~ m{^/tiny/$match_domain/\w+$}) {
                 $form{'origurl'} = $firsturl;
+                $deeplinkurl = $firsturl;
             }
             if ($form{'linkprot'}) {
                 $linkprot = $form{'linkprot'};
+                $linkprotuser = $form{'linkprotuser'};
             } elsif ($form{'linkkey'} ne '') {
                 $linkkey = $form{'linkkey'};
             }
@@ -400,6 +414,9 @@
             my ($firsturl, at rest)=split(/\&/,$info);
             if ($firsturl ne '') {
                 $form{'origurl'} = &unescape($firsturl);
+                if ($form{'origurl'} =~ m{^/tiny/$match_domain/\w+$}) {
+                    $deeplinkurl = $form{'origurl'};
+                }
             }
             foreach my $item (@rest) {
                 my ($key,$value) = split(/=/,$item);
@@ -407,6 +424,7 @@
             }
             if ($form{'linkprot'}) {
                 $linkprot = $form{'linkprot'};
+                $linkprotuser = $form{'linkprotuser'};
             } elsif ($form{'linkkey'} ne '') {
                 $linkkey = $form{'linkkey'};
             }
@@ -414,8 +432,14 @@
     } elsif ($form{'ltoken'}) {
         my %link_info = &Apache::lonnet::tmpget($form{'ltoken'});
         $linkprot = $link_info{'linkprot'};
+        if (($linkprot) && ($link_info{'linkprotuser'} ne '')) {
+            $linkprotuser = $link_info{'linkprotuser'};    
+        }
         my $delete = &Apache::lonnet::tmpdel($form{'ltoken'});
         delete($form{'ltoken'});
+        if ($form{'origurl'} =~ m{^/tiny/$match_domain/\w+$}) {
+            $deeplinkurl = $form{'origurl'};
+        }
     } elsif ($form{'linkkey'} ne '') {
         $linkkey = $form{'linkkey'};
     }
@@ -424,6 +448,23 @@
     if ($domain eq '') {
         $domain = $r->dir_config('lonDefDomain');
     }
+    if (($deeplinkurl) && ($linkprot) && ($linkprotuser ne '')) {
+        unless ($linkprotuser eq $user.':'.$domain) {
+            $r->user();
+            my %data = (
+                           origurl => $deeplinkurl,
+                           linkprot => $linkprot,
+                           linkprotuser => $linkprotuser,
+                       );
+            my $token = &Apache::lonnet::tmpput(\%data,$r->dir_config('lonHostID'),'link');
+            unless (($token eq 'con_lost') || ($token eq 'refused') || ($token =~ /^error:/) ||
+                    ($token eq 'unknown_cmd') || ($token eq 'no_such_host')) {
+                $r->internal_redirect('/adm/relaunch?rtoken='.$token);
+                $r->set_handlers('PerlHandler'=> undef);
+                return OK;
+            }
+        }
+    }
     my $home=&Apache::lonnet::homeserver($user,$domain);
     if ($home !~ /(con_lost|no_host|no_such_host)/) {
 	&Apache::lonnet::logthis(" SSO authorized user $user ");
@@ -477,6 +518,9 @@
             if ($env{'request.deeplink.login'}) {
                 if ($linkprot) {
                     $env{'request.linkprot'} = $linkprot;
+                    if ($linkprotuser ne '') {
+                        $env{'request.linkprotuser'} = $linkprotuser;
+                    }
                 } elsif ($linkkey ne '') {
                     $env{'request.linkkey'} = $linkkey;
                 }
@@ -536,9 +580,7 @@
             if (($is_balancer) && ($hosthere)) {
                 $info{'noloadbalance'} = $hosthere;
             }
-	    my $token = 
-		&Apache::lonnet::tmpput(\%info,
-					$r->dir_config('lonHostID'));
+	    my $token = &Apache::lonnet::tmpput(\%info,$r->dir_config('lonHostID'),'sso');
 	    $env{'form.token'} = $token;
 	    $r->internal_redirect('/adm/migrateuser');
 	    $r->set_handlers('PerlHandler'=> undef);
@@ -731,13 +773,36 @@
             }
         }
         if ($requrl=~m{^/+tiny/+$match_domain/+\w+$}) {
+            if ($r->args) {
+                &Apache::loncommon::get_unprocessed_cgi($r->args,['ttoken']);
+                if (defined($env{'form.ttoken'})) {
+                    my %info = &Apache::lonnet::tmpget($env{'form.ttoken'});
+                    if (($info{'linkprotuser'} ne '') && ($info{'origurl'} ne '')) {
+                        if (($info{'linkprot'}) && ($info{'origurl'} eq $requrl) &&
+                            ($info{'linkprotuser'} ne $env{'user.name'}.':'.$env{'user.domain'})) {
+                            my %data = (
+                                origurl => $requrl,
+                                linkprot => $info{'linkprot'},
+                                linkprotuser => $info{'linkprotuser'},
+                            );
+                            my $token =
+                                &Apache::lonnet::tmpput(\%data,$r->dir_config('lonHostID'),'retry');
+                            unless (($token eq 'con_lost') || ($token eq 'refused') || ($token =~ /^error:/) ||
+                                    ($token eq 'unknown_cmd') || ($token eq 'no_such_host')) {
+                                $r->internal_redirect('/adm/relaunch?rtoken='.$token);
+                                $r->set_handlers('PerlHandler'=> undef);
+                                return OK;
+                            }
+                        }
+                    }
+                }
+            }
             if ($env{'user.name'} eq 'public' &&
                 $env{'user.domain'} eq 'public') {
                 $env{'request.firsturl'}=$requrl;
                 return FORBIDDEN;
-            } else {
-                return OK;
             }
+            return OK;
         }
 # ---------------------------------------------------------------- Check access
 	my $now = time;
Index: loncom/auth/lonauth.pm
diff -u loncom/auth/lonauth.pm:1.173 loncom/auth/lonauth.pm:1.174
--- loncom/auth/lonauth.pm:1.173	Sat May 21 05:16:43 2022
+++ loncom/auth/lonauth.pm	Sat Jun 18 02:10:18 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # User Authentication Module
 #
-# $Id: lonauth.pm,v 1.173 2022/05/21 05:16:43 raeburn Exp $
+# $Id: lonauth.pm,v 1.174 2022/06/18 02:10:18 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -308,6 +308,11 @@
     if ($clientunicode && !$clientmathml) {
         $args = {'browser.unicode' => 1};
     }
+    if ($form->{firsturl} =~ m{^/tiny/$match_domain/\w+$}) {
+        if ($form->{linkprot}) {
+            $args->{only_body} = 1;
+        }
+    }
 
     my @actions;
     my $start_page = &Apache::loncommon::start_page('Unsuccessful Login',undef,$args);
@@ -357,7 +362,13 @@
             }
         }
         if (exists($form->{linkprot})) {
-            my $ltoken = &Apache::lonnet::tmpput({linkprot => $form->{'linkprot'}},
+            my %info = (
+                         'linkprot' => $form->{'linkprot'},
+                       );
+            if ($form->{linkprotuser} ne '') {
+                $info{'linkprotuser'} = $form->{linkprotuser};
+            }
+            my $ltoken = &Apache::lonnet::tmpput(\%info,
                                                  $r->dir_config('lonHostID'),'retry');
             if ($ltoken) {
                 $retry .= (($retry =~ /\?/) ? '&' : '?').'ltoken='.$ltoken;
@@ -475,6 +486,19 @@
                                      .$end_page);
                             } else {
                                 if (($info{'linkprot'}) || ($info{'linkkey'} ne '')) {
+                                    if (($info{'linkprot'}) && ($info{'linkprotuser'} ne '')) {
+                                        unless ($info{'linkprotuser'} eq $env{'user.name'}.':'.$env{'user.domain'}) {
+                                            $r->print(
+                                                      $start_page
+                                                      .'<p class="LC_warning">'.&mt('You are already logged in, but as a different user from the one expected for the link you followed from another system').'</p>'
+                                                      .'<p>'.&mt('Please [_1]log out[_2] first, and then try following the link again from the other system',
+                                                                 '<a href="/adm/logout">','</a>')
+
+                                                      .'</p>'
+                                                      .$end_page);
+                                            return OK;
+                                        }
+                                    }
                                     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')) {
@@ -730,6 +754,18 @@
 	}
     }
 
+    if ($form{'firsturl'} =~ m{^/tiny/$match_domain/\w+$}) {
+        if (($form{'linkprot'}) && ($form{'linkprotuser'} ne '')) {
+            unless($form{'linkprotuser'} eq $form{'uname'}.':'.$form{'udom'}) {
+                delete($form{'udom'});
+                delete($form{'uname'});
+                &failed($r,'Username and/or domain are different to that expected for the link you followed from another system',
+                        \%form,$authhost);
+                return OK;
+            }
+        }
+    }
+
     my ($is_balancer,$otherserver);
 
     unless ($hosthere) {
@@ -975,7 +1011,7 @@
     my ($form,$lonhost,$querystr) = @_;
     if (ref($form) eq 'HASH') {
         my ($firsturl,$token,$extras, at names);
-        @names = ('role','symb','linkprot','linkkey','iptoken');
+        @names = ('role','symb','linkprotuser','linkprot','linkkey','iptoken');
         foreach my $name (@names) {
             if ($form->{$name} ne '') {
                 $extras .= '&'.$name.'='.&escape($form->{$name});
Index: loncom/auth/lonlogin.pm
diff -u loncom/auth/lonlogin.pm:1.198 loncom/auth/lonlogin.pm:1.199
--- loncom/auth/lonlogin.pm:1.198	Wed Jun  1 12:12:32 2022
+++ loncom/auth/lonlogin.pm	Sat Jun 18 02:10:18 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Login Screen
 #
-# $Id: lonlogin.pm,v 1.198 2022/06/01 12:12:32 raeburn Exp $
+# $Id: lonlogin.pm,v 1.199 2022/06/18 02:10:18 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -68,6 +68,9 @@
             $env{'form.ltoken'} = $info{'ltoken'};
         } elsif ($info{'linkprot'}) {
             $env{'form.linkprot'} = $info{'linkprot'};
+            if ($info{'linkprotuser'} ne '') {
+                $env{'form.linkprotuser'} = $info{'linkprotuser'};
+            }
         } elsif ($info{'linkkey'} ne '') {
             $env{'form.linkkey'} = $info{'linkkey'};
         }
@@ -181,6 +184,9 @@
                     $link_info{'ltoken'} = $env{'form.ltoken'};
                 } elsif ($env{'form.linkprot'}) {
                     $link_info{'linkprot'} = $env{'form.linkprot'};
+                    if ($env{'form.linkprotuser'} ne '') {
+                        $link_info{'linkprotuser'} = $env{'form.linkprotuser'};
+                    }
                 } elsif ($env{'form.linkkey'} ne '') {
                     $link_info{'linkkey'} = $env{'form.linkkey'};
                 }
@@ -249,16 +255,31 @@
             $dest = &HTML::Entities::encode($env{'form.firsturl'},'\'"<>&');
         }
         if (($env{'form.ltoken'}) || ($env{'form.linkprot'})) {
-            my $linkprot;
+            my ($linkprot,$linkprotuser);
             if ($env{'form.ltoken'}) {
                 my %info = &Apache::lonnet::tmpget($env{'form.ltoken'});
                 $linkprot = $info{'linkprot'};
+                if ($info{'linkprotuser'} ne '') {
+                    $linkprotuser = $info{'linkprotuser'};
+                } 
                 my $delete = &Apache::lonnet::tmpdel($env{'form.ltoken'});
             } else {
                 $linkprot = $env{'form.linkprot'};
+                $linkprotuser = $env{'form.linkprotuser'};
             }
             if ($linkprot) {
                 my ($linkprotector,$deeplink) = split(/:/,$linkprot,2);
+                if (($deeplink =~ m{^/tiny/$match_domain/\w+$}) &&
+                    ($linkprotuser ne '') && ($linkprotuser ne $env{'user.name'}.':'.$env{'user.domain'})) {
+                    my $ip = &Apache::lonnet::get_requestor_ip();
+                    my %linkprotinfo = (
+                                          origurl => $deeplink,
+                                          linkprot => $linkprot,
+                                          linkprotuser => $linkprotuser,
+                                       );    
+                    &Apache::migrateuser::logout($r,$ip,$handle,undef,undef,\%linkprotinfo);
+                    return OK;
+                }
                 if ($env{'user.linkprotector'}) {
                     my @protectors = split(/,/,$env{'user.linkprotector'});
                     unless (grep(/^\Q$linkprotector\E$/, at protectors)) {
@@ -429,7 +450,7 @@
 
 # -------------------------------------------------------- Store away log token
     my ($tokenextras,$tokentype,$linkprot_for_login);
-    my @names = ('role','symb','iptoken','ltoken','linkprot','linkkey');
+    my @names = ('role','symb','iptoken','ltoken','linkprotuser','linkprot','linkkey');
     foreach my $name (@names) {
         if ($env{'form.'.$name} ne '') {
             if ($name eq 'ltoken') {
@@ -437,6 +458,9 @@
                 if ($info{'linkprot'}) {
                     $linkprot_for_login = $info{'linkprot'};
                     $tokenextras .= '&linkprot='.&escape($info{'linkprot'});
+                    if ($info{'linkprotuser'}) {
+                        $tokenextras .= '&linkprotuser='.&escape($info{'linkprotuser'});
+                    }
                     $tokentype = 'link';
                     last;
                 }
@@ -1125,28 +1149,52 @@
         $path = '/'.$path;
     }
     my $url = $protocol.'://'.$hostname.$path;
-    if ($env{'form.firsturl'} ne '') {
+    if ($env{'form.firsturl'} =~ m{^/tiny/$match_domain/\w+$}) {
+        $url = $protocol.'://'.$hostname.$env{'form.firsturl'};
+        if (($env{'form.ltoken'}) || ($env{'form.linkprot'} ne '') ||
+            ($env{'form.linkkey'} ne '')) {
+            my %link_info;
+            if ($env{'form.ltoken'}) {
+                %link_info = &Apache::lonnet::tmpget($env{'form.ltoken'});
+                &Apache::lonnet::tmpdel($env{'form.ltoken'});
+            } elsif ($env{'form.linkprot'}) {
+                $link_info{'linkprot'} = $env{'form.linkprot'};
+                if ($env{'form.linkprotuser'}) {
+                    $link_info{'linkprotuser'} = $env{'form.linkprotuser'};
+                }
+            } elsif ($env{'form.linkkey'} ne '') {
+                $link_info{'linkkey'} = $env{'form.linkkey'};
+            }
+            my $token = &Apache::lonnet::tmpput(\%link_info,$desthost,'link');
+            unless (($token eq 'con_lost') || ($token eq 'refused') ||
+                    ($token eq 'unknown_cmd') || ($token eq 'no_such_host')) {
+                $url .= '?ltoken='.$token;
+            }
+        }
+    } else {
         my $querystring;
-        if ($env{'form.firsturl'} =~ /[^\x00-\xFF]/) {
-            $querystring = &uri_escape_utf8($env{'form.firsturl'});
-        } else {
-            $querystring = &uri_escape($env{'form.firsturl'});
+        if ($env{'form.firsturl'} ne '') {
+            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,"'");
+            $querystring = '?firsturl='.$querystring;
         }
-        $querystring = &HTML::Entities::encode($querystring,"'");
-        $url .='?firsturl='.$querystring;
-    }
-    if (($env{'form.ltoken'}) || ($env{'form.linkkey'} ne '')) {
-        my %link_info;
         if ($env{'form.ltoken'}) {
-            $link_info{'ltoken'} = $env{'form.ltoken'};
-        } elsif ($env{'form.linkkey'} ne '') {
-            $link_info{'linkkey'} = $env{'form.linkkey'};
-        }
-        my $token = &Apache::lonnet::tmpput(\%link_info,$desthost,'link');
-        unless (($token eq 'con_lost') || ($token eq 'refused') ||
-                ($token eq 'unknown_cmd') || ($token eq 'no_such_host')) {
-            $url .= (($url=~/\?/)?'&':'?') . 'ttoken='.$token;
+            my %link_info = &Apache::lonnet::tmpget($env{'form.ltoken'});
+            &Apache::lonnet::tmpdel($env{'form.ltoken'});
+            my $token = &Apache::lonnet::tmpput(\%link_info,$desthost,'link');
+            unless (($token eq 'con_lost') || ($token eq 'refused') || ($token =~ /^error:/) ||
+                    ($token eq 'unknown_cmd') || ($token eq 'no_such_host')) {
+                unless (($path eq '/adm/roles') || ($path eq '/adm/login')) {
+                    $url = $protocol.'://'.$hostname.'/adm/roles';
+                }
+                $querystring .= (($querystring =~/^\?/)?'&':'?') . 'ttoken='.$token;
+            }
         }
+        $url .= $querystring;
     }
     my $start_page = &Apache::loncommon::start_page('Switching Server ...',undef,
                                                     {'redirect' => [0,$url],});
Index: loncom/auth/lonshibauth.pm
diff -u loncom/auth/lonshibauth.pm:1.14 loncom/auth/lonshibauth.pm:1.15
--- loncom/auth/lonshibauth.pm:1.14	Sun Dec 12 20:49:26 2021
+++ loncom/auth/lonshibauth.pm	Sat Jun 18 02:10:18 2022
@@ -2,7 +2,7 @@
 # Redirect Single Sign On authentication to designated URL: 
 # /adm/sso, by default.
 #
-# $Id: lonshibauth.pm,v 1.14 2021/12/12 20:49:26 raeburn Exp $
+# $Id: lonshibauth.pm,v 1.15 2022/06/18 02:10:18 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -293,6 +293,10 @@
                 &Apache::lonacc::get_posted_cgi($r,['linkkey']);
             }
         }
+        unless (($r->is_initial_req()) || ($env{'form.ltoken'}) ||
+                ($env{'form.linkkey'})) {
+            return;
+        }
     }
     my $extras;
     foreach my $name (@names) {
@@ -302,6 +306,9 @@
                 &Apache::lonnet::tmpdel($env{'form.ltoken'});
                 if ($info{'linkprot'}) {
                     $extras .= '&linkprot='.&escape($info{'linkprot'});
+                    if ($info{'linkprotuser'} ne '') {
+                        $extras .= '&linkprotuser='.&escape($info{'linkprotuser'});
+                    }
                     last;
                 }
             } else {
Index: loncom/auth/migrateuser.pm
diff -u loncom/auth/migrateuser.pm:1.60 loncom/auth/migrateuser.pm:1.61
--- loncom/auth/migrateuser.pm:1.60	Sat May 21 06:28:35 2022
+++ loncom/auth/migrateuser.pm	Sat Jun 18 02:10:18 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Starts a user off based of an existing token.
 #
-# $Id: migrateuser.pm,v 1.60 2022/05/21 06:28:35 raeburn Exp $
+# $Id: migrateuser.pm,v 1.61 2022/06/18 02:10:18 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -285,7 +285,7 @@
                     'username'        => $dataref->{'username'},
                     'sessionserver'   => $lonhost,
                   );
-        my @names = ('origurl','symb','role','linkprot','linkkey');
+        my @names = ('origurl','symb','role','linkprotuser','linkprot','linkkey');
         foreach my $name (@names) {
             if ($dataref->{$name} ne '') {
                 $info{$name} = $dataref->{$name};
@@ -308,7 +308,7 @@
 }
 
 sub logout {
-    my ($r,$ip,$handle,$data,$lti_env) = @_;
+    my ($r,$ip,$handle,$data,$lti_env,$linkprot_info) = @_;
     my $lonidsdir=$r->dir_config('lonIDsDir');
     if (unlink("$lonidsdir/$handle.id")) {
         if (($env{'user.linkedenv'} =~ /^[a-f0-9]+_linked$/) &&
@@ -335,15 +335,8 @@
                                 -expires => '-10y',);
         $r->headers_out->add('Set-cookie' => $c);
     }
-    my (%info,%user_info,%lti_info);
-    if (ref($lti_env) eq 'HASH') {
-        foreach my $key (sort(keys(%{$lti_env}))) {
-            if ($key =~ /^request\.(.+)$/) {
-                $lti_info{$1} = $lti_env->{$key};
-            }
-        }
-    }
     my $lonhost = $r->dir_config('lonHostID');
+    my ($url,$token,%info,%user_info);
     if (ref($data) eq 'HASH') {
         %user_info=('ip'       => $ip,
                     'domain'   => $data->{'domain'},
@@ -354,9 +347,33 @@
                     'symb'     => $data->{'symb'},
                     'server'   => $lonhost);
     }
-    %info = (%user_info,%lti_info);
-    my $token = &Apache::lonnet::tmpput(\%info,$lonhost);
-    my $url = '/adm/migrateuser?token='.$token;
+    if (ref($linkprot_info) eq 'HASH') {
+        if ($linkprot_info->{'linkprotuser'} ne '') {
+            $token = &Apache::lonnet::tmpput($linkprot_info,$lonhost);
+            $url = '/adm/login?ltoken='.$token;
+        } else {
+            foreach my $key (keys(%user_info)) {
+                if ($user_info{$key} eq '') {
+                    delete($user_info{$key});
+                }
+            }
+            %info = (%user_info,%{$linkprot_info});
+            $token = &Apache::lonnet::tmpput(\%info,$lonhost);
+            $url = '/adm/migrateuser?token='.$token;
+        }
+    } else {
+        my %lti_info;
+        if (ref($lti_env) eq 'HASH') {
+            foreach my $key (sort(keys(%{$lti_env}))) {
+                if ($key =~ /^request\.(.+)$/) {
+                    $lti_info{$1} = $lti_env->{$key};
+                }
+            }
+        }
+        %info = (%user_info,%lti_info);
+        $token = &Apache::lonnet::tmpput(\%info,$lonhost);
+        $url = '/adm/migrateuser?token='.$token;
+    }
     $r->send_http_header;
     $r->print(
         &Apache::loncommon::start_page('Updating Session ...',undef,
@@ -738,10 +755,10 @@
                     my %linkprot_env;
                     foreach my $item ('linkprot','deeplink.login') {
                         if ($data{$item}) {
-                            $linkprot_env{'request.'.$item} = $data{$item};
+                            $linkprot_env{$item} = $data{$item};
                         }
                     }
-                    &logout($r,$ip,$handle,\%data,\%linkprot_env);
+                    &logout($r,$ip,$handle,\%data,undef,\%linkprot_env);
                     return OK;
                 }
                 &Apache::lonnet::appenv({'request.linkprot' => $data{'linkprot'}});
@@ -891,7 +908,7 @@
                         $info{'checklaunch'} = 1;
                     }
                     my $token = &Apache::lonnet::tmpput(\%info,$r->dir_config('lonHostID'),'link');
-                    unless (($token eq 'con_lost') || ($token eq 'refused') ||
+                    unless (($token eq 'con_lost') || ($token eq 'refused') || ($token =~ /^error:/) ||
                             ($token eq 'unknown_cmd') || ($token eq 'no_such_host')) {
                         $dest .= (($dest =~ /\?/) ? '&' : '?') . 'ttoken='.$token;
                     }
Index: loncom/auth/switchserver.pm
diff -u loncom/auth/switchserver.pm:1.59 loncom/auth/switchserver.pm:1.60
--- loncom/auth/switchserver.pm:1.59	Tue Oct 26 14:20:40 2021
+++ loncom/auth/switchserver.pm	Sat Jun 18 02:10:18 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Switch Servers Handler
 #
-# $Id: switchserver.pm,v 1.59 2021/10/26 14:20:40 raeburn Exp $
+# $Id: switchserver.pm,v 1.60 2022/06/18 02:10:18 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -97,7 +97,7 @@
 
 sub handler {
     my ($r) = @_;
-    
+
     my $handle=&init_env($r);
     if (!defined($handle)) { return FORBIDDEN; }
 
@@ -312,6 +312,9 @@
     }
     if ($env{'request.linkprot'}) {
         $info{'linkprot'} = $env{'request.linkprot'};
+        if ($env{'request.linkprotuser'}) {
+            $info{'linkprotuser'} = $env{'request.linkprotuser'};
+        }
     } elsif ($env{'request.linkkey'} ne '') {
         $info{'linkkey'} = $env{'request.linkkey'};
     }
Index: loncom/lti/ltiauth.pm
diff -u loncom/lti/ltiauth.pm:1.36 loncom/lti/ltiauth.pm:1.37
--- loncom/lti/ltiauth.pm:1.36	Wed Apr  6 21:44:49 2022
+++ loncom/lti/ltiauth.pm	Sat Jun 18 02:10:19 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Basic LTI Authentication Module
 #
-# $Id: ltiauth.pm,v 1.36 2022/04/06 21:44:49 raeburn Exp $
+# $Id: ltiauth.pm,v 1.37 2022/06/18 02:10:19 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -31,7 +31,7 @@
 use strict;
 use LONCAPA qw(:DEFAULT :match);
 use Encode;
-use Apache::Constants qw(:common :http);
+use Apache::Constants qw(:common :http :remotehost);
 use Apache::lonlocal;
 use Apache::lonnet;
 use Apache::loncommon;
@@ -117,7 +117,7 @@
 #
     if ($requri =~ m{^/adm/launch(|/.*)$}) {
         my $tail = $1;
-        if ($tail =~ m{^/tiny/($match_domain)/(\w+)$}) {
+        if ($tail =~ m{^/tiny/$match_domain/\w+$}) {
             my ($urlcdom,$urlcnum) = &course_from_tinyurl($tail);
             if (($urlcdom ne '') && ($urlcnum ne '')) {
                 $cdom = $urlcdom;
@@ -143,7 +143,7 @@
 # where url was /adm/launch/tiny/$cdom/$uniqueid
 #
 
-                        my ($itemid,$ltitype,%crslti,%lti_in_use);
+                        my ($itemid,$ltitype,%crslti,%lti_in_use,$ltiuser);
                         $itemid = &get_lti_itemid($requri,$hostname,$params,$cdom,$cnum,'linkprot');
                         if ($itemid) {
                             %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom);
@@ -229,6 +229,7 @@
                                                 return OK;
                                             }
                                         }
+                                        $ltiuser = $uname.':'.$cdom;
                                     }
                                 }
                                 if ($lti_in_use{'notstudent'} eq 'reject') {
@@ -241,13 +242,20 @@
                             foreach my $key (%{$params}) {
                                 delete($env{'form.'.$key});
                             }
-                            my $ltoken = &Apache::lonnet::tmpput({'linkprot' => $itemid.$ltitype.':'.$tail},
-                                                                 $lonhost,'link');
-                            if ($ltoken) {
+                            my %info = (
+                                          'linkprot' => $itemid.$ltitype.':'.$tail,
+                                       );
+                            if ($ltiuser ne '') {
+                                $info{'linkprotuser'} = $ltiuser;
+                            }
+                            my $ltoken = &Apache::lonnet::tmpput(\%info,$lonhost,'link');
+                            if (($ltoken eq 'con_lost') || ($ltoken eq 'refused') || ($ltoken =~ /^error:/) ||
+                                ($ltoken eq 'unknown_cmd') || ($ltoken eq 'no_such_host') ||
+                                ($ltoken eq '')) {
+                                &invalid_request($r,'Failed to store information from launch request');
+                            } else {
                                 $r->internal_redirect($tail.'?ltoken='.$ltoken);
                                 $r->set_handlers('PerlHandler'=> undef);
-                            } else {
-                                &invalid_request($r,'Failed to store information from launch request');
                             }
                         } else {
                             &invalid_request($r,'Launch request could not be validated');
@@ -986,7 +994,7 @@
                                                       $r->dir_config('ltiIDsDir'),
                                                       $protocol,$r->hostname);
         }
-        my $ip = $r->get_remote_host();
+        my $ip = &Apache::lonnet::get_requestor_ip($r,REMOTE_NOLOOKUP);
         my %info=('ip'        => $ip,
                   'domain'    => $udom,
                   'username'  => $uname,
@@ -1075,7 +1083,7 @@
         $r->set_handlers('PerlHandler'=> undef);
     } else {
         # need to login them in, so generate the data migrate expects to do login
-        my $ip = $r->get_remote_host();
+        my $ip = &Apache::lonnet::get_requestor_ip($r,REMOTE_NOLOOKUP);
         my %info=('ip'             => $ip,
                   'domain'         => $cdom,
                   'username'       => $uname,
Index: loncom/lontrans.pm
diff -u loncom/lontrans.pm:1.38 loncom/lontrans.pm:1.39
--- loncom/lontrans.pm:1.38	Mon Dec  6 03:31:54 2021
+++ loncom/lontrans.pm	Sat Jun 18 02:10:19 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # URL translation for User Files
 #
-# $Id: lontrans.pm,v 1.38 2021/12/06 03:31:54 raeburn Exp $
+# $Id: lontrans.pm,v 1.39 2022/06/18 02:10:19 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -276,8 +276,11 @@
             my %link_info = &Apache::lonnet::tmpget($info{'ltoken'});
             if ($link_info{'linkprot'}) {
                 $info{'linkprot'} = $link_info{'linkprot'};
+                if ($link_info{'linkprotuser'} ne '') {
+                    $info{'linkprotuser'} = $link_info{'linkprotuser'};
+                }
             }
-            &Apache::lonnet::tmpdel($info{'ltoken'});;
+            &Apache::lonnet::tmpdel($info{'ltoken'});
             delete($info{'ltoken'});
         }
         unless ($info{'role'}) {
Index: loncom/loncapa_apache.conf
diff -u loncom/loncapa_apache.conf:1.276 loncom/loncapa_apache.conf:1.277
--- loncom/loncapa_apache.conf:1.276	Sat Oct  2 13:54:29 2021
+++ loncom/loncapa_apache.conf	Sat Jun 18 02:10:19 2022
@@ -2,7 +2,7 @@
 ## loncapa_apache.conf -- Apache HTTP LON-CAPA configuration file
 ##
 
-# $Id: loncapa_apache.conf,v 1.276 2021/10/02 13:54:29 raeburn Exp $
+# $Id: loncapa_apache.conf,v 1.277 2022/06/18 02:10:19 raeburn Exp $
 
 #
 # LON-CAPA Section (extensions to httpd.conf daemon configuration)
@@ -775,6 +775,11 @@
 PerlHandler Apache::ltiauth
 </LocationMatch>
 
+<Location /adm/relaunch>
+SetHandler perl-script
+PerlHandler Apache::lonrelaunch
+</Location>
+
 <LocationMatch "^/+adm/lti($|/)">
 SetHandler perl-script
 PerlHandler Apache::ltiauth
Index: doc/loncapafiles/loncapafiles.lpml
diff -u doc/loncapafiles/loncapafiles.lpml:1.1029 doc/loncapafiles/loncapafiles.lpml:1.1030
--- doc/loncapafiles/loncapafiles.lpml:1.1029	Sat Jun 11 14:51:49 2022
+++ doc/loncapafiles/loncapafiles.lpml	Sat Jun 18 02:10:20 2022
@@ -2,7 +2,7 @@
  "http://lpml.sourceforge.net/DTD/lpml.dtd">
 <!-- loncapafiles.lpml -->
 
-<!-- $Id: loncapafiles.lpml,v 1.1029 2022/06/11 14:51:49 raeburn Exp $ -->
+<!-- $Id: loncapafiles.lpml,v 1.1030 2022/06/18 02:10:20 raeburn Exp $ -->
 
 <!--
 
@@ -6961,6 +6961,16 @@
   <status>works/unverified</status>
 </file>
 <file>
+  <source>loncom/auth/lonrelaunch.pm</source>
+  <target dist='default'>home/httpd/lib/perl/Apache/lonrelaunch.pm</target>
+  <categoryname>handler</categoryname>
+  <description>For a deep link protected by LTI where username is included
+               in launch payload, expire user's current session if username
+               from payload does not match username in LON-CAPA session. 
+  </description>
+  <status>works/unverified</status>
+</file>
+<file>
   <source>loncom/auth/lonshibauth.pm</source>
   <target dist='default'>home/httpd/lib/perl/Apache/lonshibauth.pm</target>
   <categoryname>handler</categoryname>

Index: loncom/auth/lonrelaunch.pm
+++ loncom/auth/lonrelaunch.pm
# The LearningOnline Network
# Re-launch guidance for deep linked access with username mismatch
#
# $Id: lonrelaunch.pm,v 1.1 2022/06/18 02:10:18 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

package Apache::lonrelaunch;

use strict;
use lib '/home/httpd/lib/perl/';
use Apache::Constants qw(:common :http REDIRECT);
use Apache::lonnet;
use Apache::loncommon();
use Apache::lonlocal;
use LONCAPA qw(:DEFAULT :match);
use CGI::Cookie();

sub handler {
    my $r = shift;

    my %data;
    if ($r->args) {
        &Apache::loncommon::get_unprocessed_cgi($r->args,['rtoken']);
        if (exists($env{'form.rtoken'})) {
            %data = &Apache::lonnet::tmpget($env{'form.rtoken'});
            my $delete = &Apache::lonnet::tmpdel($env{'form.rtoken'});
        }
    }
    my $handle = &Apache::lonnet::check_for_valid_session($r);
    if ($handle ne '') {
        my $lonidsdir=$r->dir_config('lonIDsDir');
        &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle);
        if (unlink("$lonidsdir/$handle.id")) {
            if (($env{'user.linkedenv'} =~ /^[a-f0-9]+_linked$/) &&
                (-l "$lonidsdir/$env{'user.linkedenv'}.id") &&
                (readlink("$lonidsdir/$env{'user.linkedenv'}.id") eq "$lonidsdir/$handle.id")) {
                unlink("$lonidsdir/$env{'user.linkedenv'}.id");
            }
        }
        my %temp=('logout' => time);
        my $ip = &Apache::lonnet::get_requestor_ip();
        &Apache::lonnet::put('email_status',\%temp);
        &Apache::lonnet::log($env{'user.domain'},
                             $env{'user.name'},
                             $env{'user.home'},
                             "Logout $ip");
        #expire the cookies
        my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
        foreach my $name (keys(%cookies)) {
            next unless ($name =~ /^lon(|S|Link|Pub)ID$/);
            my $c = new CGI::Cookie(-name    => $name,
                                    -value   => '',
                                    -expires => '-10y',);
            $r->headers_out->add('Set-cookie' => $c);
        }
    }
    if (!$Apache::lonlocal::lh) {
        &Apache::lonlocal::get_language_handle($r);
    }
    &Apache::loncommon::content_type($r,'text/html');
    $r->send_http_header;
    return OK if $r->header_only;

    if ((keys(%data)) && ($data{'origurl'} =~ m{^/tiny/$match_domain/\w+$})) {
        my $url = $data{'origurl'};
        if (($data{'linkprot'} =~ m{^\w+(c|d):\Q$url\E$}) &&
            ($data{'linkprotuser'} =~ m{^($match_username):($match_domain)$})) {
            my $brcrum = [{'href' => '','text' => 'Update session'},];
            $url .= '?ltoken='.$env{'form.rtoken'};
            $r->print(&Apache::loncommon::start_page('Updating session',undef,
                                                     {'only_body'    => 1,
                                                      'redirect'     => [1,$url],
                                                      'bread_crumbs' => $brcrum,}).
                      '<p>'.&mt('Updating old session information').'</p>'.
                      &Apache::loncommon::end_page());
            return OK;
        }
    }
    $r->print(&Apache::loncommon::start_page('Username mismatch',undef,{'only_body' => 1}));
    $r->print(&mt('Although your credentials were authenticated, the username you entered did not match what was expected [_1] from the link you followed',"($data{'linkprotuser'})"));
    $r->print(&Apache::loncommon::end_page());
    return OK;
}

1;


More information about the LON-CAPA-cvs mailing list