[LON-CAPA-cvs] cvs: loncom /auth migrateuser.pm /interface courseprefs.pm domainprefs.pm lonconfigsettings.pm lonmodifycourse.pm /lonnet/perl lonnet.pm /lti ltiauth.pm
raeburn
raeburn at source.lon-capa.org
Sun Feb 6 16:37:08 EST 2022
raeburn Sun Feb 6 21:37:08 2022 EDT
Modified files:
/loncom/interface domainprefs.pm lonconfigsettings.pm
lonmodifycourse.pm courseprefs.pm
/loncom/lti ltiauth.pm
/loncom/auth migrateuser.pm
/loncom/lonnet/perl lonnet.pm
Log:
- Bug 6907
- Domain default to determine whether LTI launch of deep-linked URL requires
student to authenticate; can be overridden for specific course(s).
- If domain config permits it, link protection setting in a course can
specify whether to accept username included in LTI payload, and action
to take if username is not for an enrolled student.
- Second arg passed to ltiauth::invalid_request() is text string stating why
LTI launch was invalid.
-------------- next part --------------
Index: loncom/interface/domainprefs.pm
diff -u loncom/interface/domainprefs.pm:1.403 loncom/interface/domainprefs.pm:1.404
--- loncom/interface/domainprefs.pm:1.403 Wed Jan 19 16:54:16 2022
+++ loncom/interface/domainprefs.pm Sun Feb 6 21:36:59 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set domain-wide configuration settings
#
-# $Id: domainprefs.pm,v 1.403 2022/01/19 16:54:16 raeburn Exp $
+# $Id: domainprefs.pm,v 1.404 2022/02/06 21:36:59 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -6863,6 +6863,7 @@
postsubmit => 'Disable submit button/keypress following student submission',
canclone => "People who may clone a course (besides course's owner and coordinators)",
mysqltables => 'Lifetime (s) of "Temporary" MySQL tables (student performance data) on homeserver',
+ ltiauth => 'Student username in LTI launch of deep-linked URL can be accepted without re-authentication',
);
my %staticdefaults = (
anonsurvey_threshold => 10,
@@ -6985,8 +6986,12 @@
my ($currdefresponder,%defcredits,%curruploadquota,%deftimeout,%currmysql);
my $currusecredits = 0;
my $postsubmitclient = 1;
+ my $ltiauth = 0;
my @types = ('official','unofficial','community','textbook','placement');
if (ref($settings) eq 'HASH') {
+ if ($settings->{'ltiauth'}) {
+ $ltiauth = 1;
+ }
$currdefresponder = $settings->{'anonsurvey_threshold'};
if (ref($settings->{'uploadquota'}) eq 'HASH') {
foreach my $type (keys(%{$settings->{'uploadquota'}})) {
@@ -7132,7 +7137,16 @@
}
$datatable .= '</tr></table></td></tr>'."\n";
$itemcount ++;
-
+ %defaultchecked = ('ltiauth' => 'off');
+ @toggles = ('ltiauth');
+ $current = {
+ 'ltiauth' => $ltiauth,
+ };
+ ($table,$itemcount) =
+ &radiobutton_prefs($current,\@toggles,\%defaultchecked,
+ \%choices,$itemcount,undef,undef,'left');
+ $datatable .= $table;
+ $itemcount ++;
}
$$rowtotal += $itemcount;
return $datatable;
@@ -20447,8 +20461,9 @@
'uselcmath' => 'on',
'usejsme' => 'on',
'inline_chem' => 'on',
+ 'ltiauth' => 'off',
);
- my @toggles = ('canuse_pdfforms','uselcmath','usejsme','inline_chem');
+ my @toggles = ('canuse_pdfforms','uselcmath','usejsme','inline_chem','ltiauth');
my @numbers = ('anonsurvey_threshold','uploadquota_official','uploadquota_unofficial',
'uploadquota_community','uploadquota_textbook','uploadquota_placement',
'mysqltables_official','mysqltables_unofficial','mysqltables_community',
@@ -20659,8 +20674,8 @@
if (($changes{'canuse_pdfforms'}) || ($changes{'uploadquota'}) || ($changes{'postsubmit'}) ||
($changes{'coursecredits'}) || ($changes{'uselcmath'}) || ($changes{'usejsme'}) ||
($changes{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'}) ||
- ($changes{'inline_chem'})) {
- foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine') {
+ ($changes{'inline_chem'}) || ($changes{'ltiauth'})) {
+ foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine','ltiauth') {
if ($changes{$item}) {
$domdefaults{$item}=$defaultshash{'coursedefaults'}{$item};
}
@@ -20834,6 +20849,12 @@
} else {
$resulttext .= '<li>'.&mt('By default, only course owner and coordinators may clone a course.').'</li>';
}
+ } elsif ($item eq 'ltiauth') {
+ if ($env{'form.'.$item} eq '1') {
+ $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL need not require re-authentication').'</li>';
+ } else {
+ $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL will require re-authentication').'</li>';
+ }
}
}
$resulttext .= '</ul>';
Index: loncom/interface/lonconfigsettings.pm
diff -u loncom/interface/lonconfigsettings.pm:1.56 loncom/interface/lonconfigsettings.pm:1.57
--- loncom/interface/lonconfigsettings.pm:1.56 Tue Feb 1 23:13:19 2022
+++ loncom/interface/lonconfigsettings.pm Sun Feb 6 21:36:59 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set domain-wide configuration settings
#
-# $Id: lonconfigsettings.pm,v 1.56 2022/02/01 23:13:19 raeburn Exp $
+# $Id: lonconfigsettings.pm,v 1.57 2022/02/06 21:36:59 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -291,10 +291,27 @@
if (grep(/^linkprotection$/, at actions)) {
if (ref($values) eq 'HASH') {
if (ref($values->{'linkprotection'}) eq 'HASH') {
+ my $ltiauth;
+ if (exists($env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'})) {
+ $ltiauth = $env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'};
+ } else {
+ my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
+ $ltiauth = $domdefs{'crsltiauth'};
+ }
my $posslti = scalar(keys(%{$values->{'linkprotection'}}));
- for (my $i=0; $i<$posslti; $i++) {
- if ($values->{'linkprotection'}->{'usable'}) {
- $onload .= "toggleLTI(document.display,'$i','secret');";
+ for (my $i=0; $i<=$posslti; $i++) {
+ my $num = $i;
+ if ($i == $posslti) {
+ $num = 'add';
+ }
+ if (ref($values->{'linkprotection'}->{$i}) eq 'HASH') {
+ if ($values->{'linkprotection'}->{$i}->{'usable'}) {
+ $onload .= "toggleLTI(document.display,'$num','secret');";
+ }
+ }
+ if ($ltiauth) {
+ $onload .= "toggleLTIReqUser(document.display,'requser','optional','1','block','$num');".
+ "toggleLTIReqUser(document.display,'mapuser','userfield','other','inline-block','$num');";
}
}
}
Index: loncom/interface/lonmodifycourse.pm
diff -u loncom/interface/lonmodifycourse.pm:1.96 loncom/interface/lonmodifycourse.pm:1.97
--- loncom/interface/lonmodifycourse.pm:1.96 Tue Jun 15 20:52:26 2021
+++ loncom/interface/lonmodifycourse.pm Sun Feb 6 21:36:59 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# handler for DC-only modifiable course settings
#
-# $Id: lonmodifycourse.pm,v 1.96 2021/06/15 20:52:26 raeburn Exp $
+# $Id: lonmodifycourse.pm,v 1.97 2022/02/06 21:36:59 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -430,6 +430,7 @@
'setanon' => 'View/Modify responders threshold for anonymous survey submissions display',
'selfenroll' => 'View/Modify Self-Enrollment configuration',
'setpostsubmit' => 'View/Modify submit button behavior, post-submission',
+ 'setltiauth' => 'View/Modify re-authentication requirement for LTI launch of deep-linked item',
);
} else {
%linktext = (
@@ -437,6 +438,7 @@
'setanon' => 'View responders threshold for anonymous survey submissions display',
'selfenroll' => 'View Self-Enrollment configuration',
'setpostsubmit' => 'View submit button behavior, post-submission',
+ 'setltiauth' => 'View re-authentication requirement for LTI launch of deep-linked item',
);
}
if ($type eq 'Community') {
@@ -475,6 +477,7 @@
my $anon_text = &mt('Responder threshold required to display anonymous survey submissions.');
my $postsubmit_text = &mt('Override defaults for submit button behavior post-submission for this specific course.');
my $mysqltables_text = &mt('Override default for lifetime of "temporary" MySQL tables containing student performance data.');
+ my $ltiauth_text = &mt('Override default for requirement for re-authentication for LTI-limited launch of deep-linked item');
$linktext{'viewparms'} = 'Display current settings for automated enrollment';
my %domconf = &Apache::lonnet::get_dom('configuration',['coursecategories'],$dom);
@@ -556,6 +559,14 @@
permission => $permission->{'setpostsubmit'},
linktitle => '',
},
+ {
+ linktext => $linktext{'setltiauth'},
+ icon => 'system-lock-screen.png',
+ #help => '',
+ url => &phaseurl('setltiauth'),
+ permission => $permission->{'setltiauth'},
+ linktitle => '',
+ },
]
},
);
@@ -586,7 +597,8 @@
'<li>'.$setquota_text.'</li>'."\n".
'<li>'.$setuploadquota_text.'</li>'."\n".
'<li>'.$anon_text.'</li>'."\n".
- '<li>'.$postsubmit_text.'</li>'."\n";
+ '<li>'.$postsubmit_text.'</li>'."\n".
+ '<li>'.$ltiauth_text.'</li>'."\n";
my ($categories_link_start,$categories_link_end);
if ($permission->{'catsettings'} eq 'edit') {
$categories_link_start = '<a href="/adm/domainprefs?actions=coursecategories&phase=display">';
@@ -1261,6 +1273,79 @@
return;
}
+sub print_set_ltiauth {
+ my ($r,$cdom,$cnum,$cdesc,$type,$readonly) = @_;
+ my %lt = &Apache::lonlocal::texthash(
+ 'requ' => 'Requirement for re-authentication for student LTI-limited launch of deep-linked item',
+ 'link' => 'Link protection can be set to accept username for an enrolled student (if sent by Consumer)',
+ 'logi' => 'Login needed, regardless of user information sent by LTI Consumer in (signed) parameters',
+ 'used' => 'Use domain default',
+ 'cour' => 'Use course-specific setting',
+ 'curd' => 'Current domain default is',
+ 'valu' => 'Value for this course',
+ 'modi' => 'Save',
+ 'back' => 'Pick another action',
+ );
+ my ($domdef,$checkeddom,$checkedcrs,$domdefdisplay,$divsty,$authok,$authno);
+ $domdef = 0;
+ $checkeddom = 'checked="checked "';
+ $domdefdisplay = $lt{'logi'};
+ $divsty = 'display:none';
+ $authno = 'checked="checked" ';
+ my %domconfig =
+ &Apache::lonnet::get_dom('configuration',['coursedefaults'],$cdom);
+ if (ref($domconfig{'coursedefaults'}) eq 'HASH') {
+ $domdef = $domconfig{'coursedefaults'}{'ltiauth'};
+ }
+ if ($domdef) {
+ $domdefdisplay = $lt{'link'};
+ }
+ my %settings = &Apache::lonnet::get('environment',['internal.ltiauth'],$cdom,$cnum);
+ my $ltiauth = $settings{'internal.ltiauth'};
+
+ if ($ltiauth ne '') {
+ $checkedcrs = $checkeddom;
+ $checkeddom = '';
+ $divsty = 'display:inline-block';
+ if ($ltiauth) {
+ $authok = 'checked="checked "';
+ }
+ }
+ &print_header($r,$type);
+ my $hidden_elements = &hidden_form_elements();
+ my ($disabled,$submit);
+ if ($readonly) {
+ $disabled = ' disabled="disabled"';
+ } else {
+ $submit = '<input type="button" onclick="javascript:changePage(this.form,'."'processltiauth'".');" value="'.$lt{'modi'}.'" />';
+ }
+ my $helpitem = &Apache::loncommon::help_open_topic('Modify_Course_LTI_Authen');
+ $r->print(<<ENDDOCUMENT);
+<form action="/adm/modifycourse" method="post" name="setltiauth">
+<h3>$helpitem $lt{'requ'} <span class="LC_nobreak">$cdesc</span></h3>
+<p><span class="LC_nobreak">$lt{'curd'}: <span style="font-style:italic">'.$domdefdisplay</span></span</p>
+<p><span class="LC_nobreak">
+<label><input type="radio" name="ltiauthset" value="dom" onclick="toggleLTIOptions(this.form);" $checkeddom$disabled/>$lt{'used'}</label></span><br />
+<span class="LC_nobreak">
+<label><input type="radio" name="ltiauthset" value="course" onclick="toggleLTIOptions(this.form);" $checkedcrs$disabled/>$lt{'cour'}</label></span>
+<fieldset id="crsltiauth" style="$divsty">
+<legend>$lt{'valu'}</legend>
+<span class="LC_nobreak">
+<label><input type="radio" name="ltiauth" value="0" $authno$disabled/>$lt{'logi'}</label>
+</span><br />
+<span class="LC_nobreak">
+<label><input type="radio" name="ltiauth" value="1" $authok$disabled/>$lt{'link'}</label>
+</span>
+</fieldset>
+$submit
+</p>
+$hidden_elements
+<a href="javascript:changePage(document.setltiauth,'menu')">$lt{'back'}</a>
+</form>
+ENDDOCUMENT
+ return;
+}
+
sub modify_selfenrollconfig {
my ($r,$type,$cdesc,$coursehash) = @_;
return unless(ref($coursehash) eq 'HASH');
@@ -2258,6 +2343,109 @@
return;
}
+sub modify_ltiauth {
+ my ($r,$cdom,$cnum,$cdesc,$domdesc,$type) = @_;
+ my %lt = &Apache::lonlocal::texthash(
+ 'requ' => 'Requirement for re-authentication for student LTI-limited launch of deep-linked item',
+ 'link' => 'Link protection can be set to accept username for an enrolled student (if sent by Consumer)',
+ 'logi' => 'Login needed, regardless of user information sent by LTI Consumer in (signed) parameters',
+ 'used' => 'Use domain default',
+ 'cour' => 'Use course-specific setting',
+ 'modi' => 'Save',
+ 'back' => 'Pick another action',
+ );
+ &print_header($r,$type);
+ $r->print('<form action="/adm/modifycourse" method="post" name="processltiauth">'."\n".
+ '<h3>'.$lt{'requ'}.
+ ' <span class="LC_nobreak">'.$cdesc.'</span></h3>');
+ my %oldsettings = &Apache::lonnet::get('environment',['internal.ltiauth'],$cdom,$cnum);
+ my $oldltiauth = $oldsettings{'internal.ltiauth'};
+ my $domdef;
+ my %domconfig =
+ &Apache::lonnet::get_dom('configuration',['coursedefaults'],$cdom);
+ if (ref($domconfig{'coursedefaults'}) eq 'HASH') {
+ $domdef = $domconfig{'coursedefaults'}{'ltiauth'};
+ }
+ my ($newltiauth,$nochange,$change,$status,$error,$ltiauth);
+ if ($env{'form.ltiauthset'} eq 'dom') {
+ if ($oldltiauth eq '') {
+ $nochange = 1;
+ } else {
+ $change = 1;
+ }
+ } elsif ($env{'form.ltiauthset'} eq 'course') {
+ if ($env{'form.ltiauth'} =~ /^0|1$/) {
+ $newltiauth = $env{'form.ltiauth'};
+ }
+ if ($oldltiauth == $newltiauth) {
+ $nochange = 1;
+ } else {
+ $change = 1;
+ }
+ }
+ if ($change) {
+ if ($newltiauth ne '') {
+ my %cenv = (
+ 'internal.ltiauth' => $newltiauth,
+ );
+ if (&Apache::lonnet::put('environment',\%cenv,$cdom,$cnum) eq 'ok') {
+ if ($env{'course.'.$cdom.'_'.$cnum.'.description'} ne '') {
+ &Apache::lonnet::appenv(
+ {'course.'.$cdom.'_'.$cnum.'.internal.ltiauth' => $newltiauth});
+ }
+ } else {
+ $error = 1;
+ }
+ } else {
+ if (&Apache::lonnet::del('environment',['internal.ltiauth'],$cdom,$cnum) eq 'ok') {
+ if (exists($env{'course.'.$cdom.'_'.$cnum.'.internal.ltiauth'})) {
+ &Apache::lonnet::delenv('course.'.$cdom.'_'.$cnum.'.internal.ltiauth');
+ }
+ } else {
+ $error = 1;
+ }
+ }
+ }
+ if ($error) {
+ $nochange = 1;
+ }
+ if ($nochange) {
+ $ltiauth = $oldltiauth;
+ } else {
+ $ltiauth = $newltiauth;
+ }
+ if ($ltiauth eq '') {
+ $status = $lt{'used'}.': ';
+ if ($domdef) {
+ $status .= '<span style="font-style:italic">'.$lt{'link'}.'</span>';
+ } else {
+ $status .= '<span style="font-style:italic">'.$lt{'logi'}.'</span>';
+ }
+ } else {
+ $status = $lt{'cour'}.': ';
+ if ($ltiauth) {
+ $status .= '<span style="font-style:italic">'.$lt{'link'}.'</span>';
+ } else {
+ $status .= '<span style="font-style:italic">'.$lt{'logi'}.'</span>';
+ }
+ }
+ if ($error) {
+ $r->print('<p class="LC_warning">'.&mt('An error occurred when saving your changes').'</p>');
+ }
+ $r->print('<p>');
+ if ($nochange) {
+ $r->print(&mt('Re-authentication requirement for LTI launch of deep-linked item is unchanged'));
+ } elsif ($change) {
+ $r->print(&mt('Re-authentication requirement for LTI launch of deep-linked changed'));
+ }
+ $r->print('<br />'.$status);
+ $r->print('</p><p>'.
+ '<a href="javascript:changePage(document.processltiauth,'."'menu'".')">'.
+ &mt('Pick another action').'</a></p>');
+ $r->print(&hidden_form_elements().'</form>');
+ return;
+}
+
sub print_header {
my ($r,$type,$javascript_validations) = @_;
my $phase = "start";
@@ -2413,6 +2601,35 @@
ENDSCRIPT
+ } elsif ($phase eq 'setltiauth') {
+ $js .= <<"ENDJS";
+function toggleLTIOptions(form) {
+ var radioname = 'ltiauthset';
+ var divid = 'crsltiauth';
+ var num = form.elements[radioname].length;
+ if (num) {
+ var setvis = '';
+ for (var i=0; i<num; i++) {
+ if (form.elements[radioname][i].checked) {
+ if (form.elements[radioname][i].value == 'course') {
+ if (document.getElementById(divid)) {
+ document.getElementById(divid).style.display = 'inline-block';
+ }
+ setvis = 1;
+ }
+ break;
+ }
+ }
+ if (!setvis) {
+ if (document.getElementById(divid)) {
+ document.getElementById(divid).style.display = 'none';
+ }
+ }
+ }
+ return;
+}
+
+ENDJS
}
my $starthash;
if ($env{'form.phase'} eq 'adhocrole') {
@@ -2423,6 +2640,10 @@
$starthash = {
add_entries => {'onload' => "hide_searching(); courseSet(document.filterpicker.official, 'load');"},
};
+ } elsif ($env{'form.phase'} eq 'setltiauth') {
+ $starthash = {
+ add_entries => {'onload' => "toggleLTIOptions(document.setltiauth);"},
+ };
}
$r->print(&Apache::loncommon::start_page('View/Modify Course/Community Settings',
&Apache::lonhtmlcommon::scripttag($js),
@@ -2522,7 +2743,7 @@
'locarg','krbarg','krbver','counter','hidefromcat','usecategory',
'threshold','postsubmit','postsubtimeout','defaultcredits','uploadquota',
'selfenrollmgrdc','selfenrollmgrcc','action','state','currsec_st',
- 'sections','newsec','mysqltables','nopasswdchg'],
+ 'sections','newsec','mysqltables','nopasswdchg','ltiauth','ltiauthset'],
['^selfenrollmgr_','^selfenroll_'])."\n".
'<input type="hidden" name="prevphase" value="'.$env{'form.phase'}.'" />';
return $hidden_elements;
@@ -2556,6 +2777,8 @@
processcat => 'edit',
selfenroll => 'edit',
adhocrole => 'coord',
+ setltiauth => 'edit',
+ processltiauth => 'edit',
);
if ($passwdconf{'crsownerchg'}) {
$permission{passwdchg} = 'edit';
@@ -2571,6 +2794,7 @@
catsettings => 'view',
selfenroll => 'view',
adhocrole => 'custom',
+ setltiauth => 'view',
);
if ($passwdconf{'crsownerchg'}) {
$permission{passwdchg} = 'view';
@@ -2774,6 +2998,19 @@
text=>"Result"});
&modify_selfenrollconfig($r,$type,$cdesc,$coursehash);
}
+ } elsif (($phase eq 'setltiauth') && ($permission->{'setltiauth'})) {
+ &Apache::lonhtmlcommon::add_breadcrumb
+ ({href=>"javascript:changePage(document.$phase,'$phase')",
+ text=>"Requirement for re-authentication for LTI launch of deep-linked item"});
+ &print_set_ltiauth($r,$cdom,$cnum,$cdesc,$type,$readonly);
+ } elsif (($phase eq 'processltiauth') && ($permission->{'processltiauth'})) {
+ &Apache::lonhtmlcommon::add_breadcrumb
+ ({href=>"javascript:changePage(document.$phase,'setltiauth')",
+ text=>"Requirement for re-authentication for LTI launch of deep-linked item"});
+ &Apache::lonhtmlcommon::add_breadcrumb
+ ({href=>"javascript:changePage(document.$phase,'$phase')",
+ text=>"Result"});
+ &modify_ltiauth($r,$cdom,$cnum,$cdesc,$domdesc,$type);
}
}
} else {
Index: loncom/interface/courseprefs.pm
diff -u loncom/interface/courseprefs.pm:1.99 loncom/interface/courseprefs.pm:1.100
--- loncom/interface/courseprefs.pm:1.99 Tue Feb 1 23:13:19 2022
+++ loncom/interface/courseprefs.pm Sun Feb 6 21:36:59 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set configuration settings for a course
#
-# $Id: courseprefs.pm,v 1.99 2022/02/01 23:13:19 raeburn Exp $
+# $Id: courseprefs.pm,v 1.100 2022/02/06 21:36:59 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -808,7 +808,7 @@
sub process_changes {
my ($cdom,$cnum,$action,$values,$item,$changes,$allitems,$disallowed,$crstype) = @_;
- my (%newvalues,%lti,%ltienc,$errors);
+ my (%newvalues,%lti,%ltienc,$ltiauth,$errors);
if (ref($item) eq 'HASH') {
if (ref($changes) eq 'HASH') {
my @ordered;
@@ -840,6 +840,12 @@
if (($env{'form.linkprot_add'}) && ($env{'form.linkprot_maxnum'} =~ /^\d+$/)) {
push(@ordered,$env{'form.linkprot_maxnum'});
}
+ if (exists($env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'})) {
+ $ltiauth = $env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'};
+ } else {
+ my %domdefs = &Apache::lonnet::get_domain_defaults($cdom);
+ $ltiauth = $domdefs{'crsltiauth'};
+ }
} elsif (ref($item->{'ordered'}) eq 'ARRAY') {
if ($action eq 'courseinfo') {
my ($can_toggle_cat,$can_categorize) =
@@ -1044,6 +1050,46 @@
$lti{$itemid}{$inner} = $env{$formitem};
}
}
+ if ($ltiauth) {
+ my $reqitem = 'form.linkprot_requser_'.$idx;
+ $env{$reqitem} =~ s/(`)/'/g;
+ unless ($idx eq 'add') {
+ if ($current{'requser'} ne $env{$reqitem}) {
+ $haschanges{$itemid} = 1;
+ }
+ }
+ if ($env{$reqitem} ne '') {
+ $lti{$itemid}{'requser'} = $env{$reqitem};
+ foreach my $inner ('mapuser','notstudent') {
+ my $formitem = 'form.linkprot_'.$inner.'_'.$idx;
+ $env{$formitem} =~ s/(`)/'/g;
+ if ($inner eq 'mapuser') {
+ if ($env{$formitem} eq 'other') {
+ my $mapuser = $env{'form.linkprot_customuser_'.$idx};
+ $mapuser =~ s/(`)/'/g;
+ $mapuser =~ s/^\s+|\s+$//g;
+ if ($mapuser ne '') {
+ $lti{$itemid}{$inner} = $mapuser;
+ } else {
+ delete($lti{$itemid}{'requser'});
+ last;
+ }
+ } elsif ($env{$formitem} eq 'sourcedid') {
+ $lti{$itemid}{$inner} = 'lis_person_sourcedid';
+ } elsif ($env{$formitem} eq 'email') {
+ $lti{$itemid}{$inner} = 'lis_person_contact_email_primary';
+ }
+ } else {
+ $lti{$itemid}{$inner} = $env{$formitem};
+ }
+ unless ($idx eq 'add') {
+ if ($current{$inner} ne $lti{$itemid}{$inner}) {
+ $haschanges{$itemid} = 1;
+ }
+ }
+ }
+ }
+ }
unless ($switchserver) {
my $keyitem = 'form.linkprot_key_'.$idx;
$env{$keyitem} =~ s/(`)/'/g;
@@ -1711,7 +1757,13 @@
}
}
} elsif ($item eq 'linkprotection') {
- my (%ltienc,$lti_save_error);
+ my ($ltiauth,%ltienc,$lti_save_error);
+ if (exists($env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'})) {
+ $ltiauth = $env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'};
+ } else {
+ my %domdefs = &Apache::lonnet::get_domain_defaults($cdom);
+ $ltiauth = $domdefs{'crsltiauth'};
+ }
if (ref($changes->{$item}) eq 'HASH') {
foreach my $id (sort { $a <=> $b } keys(%{$changes->{$item}})) {
if (ref($changes->{$item}->{$id}) eq 'HASH') {
@@ -1748,7 +1800,7 @@
$chome = &Apache::lonnet::homeserver($cnum,$cdom);
unless (($chome eq 'no_host') || ($chome eq '')) {
my @ids=&Apache::lonnet::current_machine_ids();
- unless (grep(/^\Q$chome\E$/, at ids)) {
+ if (grep(/^\Q$chome\E$/, at ids)) {
&Apache::lonnet::devalidate_cache_new('courseltienc',$hashid);
}
}
@@ -1763,7 +1815,7 @@
if (exists($ltienc{$id}{$title})) {
if ($title eq 'secret') {
my $length = length($ltienc{$id}{$title});
- $display .= $desc{$title}.': '.('*' x $length);
+ $display .= $desc{$title}.': '.('*' x $length).', ';
} else {
$display .= $desc{$title}.': '.$ltienc{$id}{$title}.', ';
}
@@ -1777,6 +1829,24 @@
$display .= $desc{$title}.': '.$values{$title}.', ';
}
}
+ if ($ltiauth) {
+ if (($values{'requser'}) && ($values{'mapuser'} ne '')) {
+ if ($values{'mapuser'} eq 'lis_person_contact_email_primary') {
+ $display .= &mt('Source of username: Email address [_1]',
+ '(lis_person_contact_email_primary)').', ';
+ } elsif ($values{'mapuser'} eq 'lis_person_sourcedid') {
+ $display .= &mt('Source of username: User ID [_1]',
+ '(lis_person_sourcedid)').', ';
+ } else {
+ $display .= &mt('Source of username: [_1]',$values{'mapuser'}).', ';
+ }
+ if ($values{'notstudent'} eq 'auth') {
+ $display .= &mt('Display LON-CAPA login page if no match').', ';
+ } elsif ($values{'notstudent'} eq 'reject') {
+ $display .= &mt('Discontinue launch if no match').', ';
+ }
+ }
+ }
$display =~ s/, $//;
$output .= '<li>'.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]','<i>'.$id.'</i>',
"'$display'")).'</li>';
@@ -2511,6 +2581,31 @@
}
return;
}
+
+function toggleLTIReqUser(form,item,extra,valon,styleon,num) {
+ if (document.getElementById('linkprot_'+extra+'_'+num)) {
+ var extraid = document.getElementById('linkprot_'+extra+'_'+num);
+ var itemname = form.elements['linkprot_'+item+'_'+num];
+ if (itemname) {
+ if (itemname.length > 0) {
+ var setvis;
+ for (var i=0; i<itemname.length; i++) {
+ if (itemname[i].checked == true) {
+ if (itemname[i].value == valon) {
+ extraid.style.display = styleon;
+ setvis = 1;
+ }
+ break;
+ }
+ }
+ if (!setvis) {
+ extraid.style.display = 'none';
+ }
+ }
+ }
+ }
+ return;
+}
ENDSCRIPT
$jscript = '<script type="text/javascript" language="Javascript">'."\n".
'// <![CDATA['."\n".
@@ -5418,9 +5513,20 @@
if ($noedit) {
$disabled = ' disabled="disabled"';
}
- my %lt = &linkprot_names();
+ my %desc = &linkprot_names();
+ my %lt = &Apache::lonlocal::texthash (
+ 'requ' => 'Required settings',
+ 'opti' => 'Optional settings',
+ );
my $itemcount = 0;
+ my $ltiauth;
+ if (exists($env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'})) {
+ $ltiauth = $env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'};
+ } else {
+ my %domdefs = &Apache::lonnet::get_domain_defaults($cdom);
+ $ltiauth = $domdefs{'crsltiauth'};
+ }
my $switchserver = &check_switchserver($cdom,$cnum);
if (ref($settings->{'linkprotection'}) eq 'HASH') {
@@ -5443,18 +5549,43 @@
$datatable .=
'<tr '.$css_class.'><td><span class="LC_nobreak">'.
'<label><input type="checkbox" name="linkprot_del" value="'.$i.'"'.$disabled.' />'.
- &mt('Delete?').'</label></span></td>'.
- '<td><span class="LC_nobreak">'.$lt{'name'}.
+ &mt('Delete?').'</label></span></td><td>';
+ my ($usersty,$onclickrequser,%checkedrequser);
+ if ($ltiauth) {
+ $usersty = 'display:none';
+ $onclickrequser = ' onclick="toggleLTIReqUser(this.form,'."'requser','optional','1','block','$i'".');"';
+ %checkedrequser = (
+ no => ' checked="checked"',
+ yes => '',
+ );
+ if ($values{'requser'}) {
+ $checkedrequser{'yes'} = $checkedrequser{'no'};
+ $checkedrequser{'no'} = '';
+ }
+ $datatable .= '<fieldset><legend>'.$lt{'requ'}.'</legend>';
+ if ($values{'requser'}) {
+ $usersty = 'display:inline-block';
+ }
+ }
+ $datatable .=
+ '<span class="LC_nobreak">'.$desc{'name'}.
':<input type="text" size="15" name="linkprot_name_'.$i.'" value="'.$values{'name'}.'" autocomplete="off"'.$disabled.' /></span> '.
(' 'x2).
- '<span class="LC_nobreak">'.$lt{'version'}.':<select name="linkprot_version_'.$i.'"'.$disabled.'>'.
+ '<span class="LC_nobreak">'.$desc{'version'}.':<select name="linkprot_version_'.$i.'"'.$disabled.'>'.
'<option value="LTI-1p0" '.$selected.'>1.1</option></select></span> '."\n".
(' 'x2).
- '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" name="linkprot_lifetime_'.$i.'"'.
- 'value="'.$values{'lifetime'}.'" size="3"'.$disabled.' /></span>'.
- '<br /><br />';
+ '<span class="LC_nobreak">'.$desc{'lifetime'}.':<input type="text" name="linkprot_lifetime_'.$i.'"'.
+ ' value="'.$values{'lifetime'}.'" size="3"'.$disabled.' /></span>';
+ if ($ltiauth) {
+ $datatable .= (' 'x2).'<span class="LC_nobreak">'.$desc{'requser'}.'?'.
+ '<label><input type="radio" name="linkprot_requser_'.$i.'" value="0"'.
+ $onclickrequser.$checkedrequser{'no'}.$disabled.' />'.&mt('No').'</label> '.
+ '<label><input type="radio" name="linkprot_requser_'.$i.'" value="1"'.
+ $onclickrequser.$checkedrequser{'yes'}.$disabled.' />'.&mt('Yes').'</label></span>'.
+ '<br /><br />';
+ }
if ($values{'key'} ne '') {
- $datatable .= '<span class="LC_nobreak">'.$lt{'key'};
+ $datatable .= '<span class="LC_nobreak">'.$desc{'key'};
if ($noedit) {
$datatable .= ': ['.&mt('not shown').']';
} elsif ($switchserver) {
@@ -5464,14 +5595,14 @@
}
$datatable .= '</span> '.(' 'x2);
} elsif (!$switchserver) {
- $datatable .= '<span class="LC_nobreak">'.$lt{'key'}.':'.
+ $datatable .= '<span class="LC_nobreak">'.$desc{'key'}.':'.
'<input type="text" size="25" name="linkprot_key_'.$i.'" value="'.$values{'key'}.'" autocomplete="off"'.$disabled.' />'.
'</span> '.(' 'x2);
}
if ($switchserver) {
if ($values{'usable'} ne '') {
$datatable .= '<div id="linkprot_divcurrsecret_'.$i.'" style="display:inline-block" /><span class="LC_nobreak">'.
- $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'.
+ $desc{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'.
'<span class="LC_nobreak">'.&mt('Change secret?').
'<label><input type="radio" value="0" name="linkprot_changesecret_'.$i.'" onclick="javascript:toggleLTI(this.form,'."'$i','secret'".');" checked="checked"'.$disabled.' />'.&mt('No').'</label>'.
(' 'x2).
@@ -5487,7 +5618,7 @@
} else {
if ($values{'usable'} ne '') {
$datatable .= '<div id="linkprot_divcurrsecret_'.$i.'" style="display:inline-block" /><span class="LC_nobreak">'.
- $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'.
+ $desc{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'.
'<span class="LC_nobreak">'.&mt('Change?').
'<label><input type="radio" value="0" name="linkprot_changesecret_'.$i.'" onclick="javascript:toggleLTI(this.form,'."'$i','secret'".');" checked="checked"'.$disabled.' />'.&mt('No').'</label>'.
(' 'x2).
@@ -5499,12 +5630,19 @@
'<input type="hidden" name="linkprot_id_'.$i.'" value="'.$num.'" /></span></div>';
} else {
$datatable .=
- '<span class="LC_nobreak">'.$lt{'secret'}.':'.
+ '<span class="LC_nobreak">'.$desc{'secret'}.':'.
'<input type="password" size="20" name="linkprot_secret_'.$i.'" value="" autocomplete="off"'.$disabled.' />'.
'<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.linkprot_secret_'.$i.'.type='."'text'".' } else { this.form.linkprot_secret_'.$i.'.type='."'password'".' }"'.$disabled.' />'.&mt('Visible input').'</label>'.
'<input type="hidden" name="linkprot_id_'.$i.'" value="'.$num.'" /></span>';
}
}
+ if ($ltiauth) {
+ $datatable .=
+ '</fieldset>'.
+ '<fieldset id="linkprot_optional_'.$i.'" style="'.$usersty.'"><legend>'.$lt{'opti'}.'</legend>'.
+ &linkprot_options($i,$itemcount,$disabled,\%values,\%desc).
+ '</fieldset>';
+ }
$datatable .= '</td></tr>';
$itemcount ++;
}
@@ -5514,37 +5652,65 @@
$datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".
'<input type="hidden" name="linkprot_maxnum" value="'.$next.'" />'."\n".
'<input type="checkbox" name="linkprot_add" value="1"'.$disabled.' />'.&mt('Add').'</span></td>'."\n".
- '<td>'.
- '<span class="LC_nobreak">'.$lt{'name'}.
+ '<td>';
+ my ($usersty,$onclickrequser,%checkedrequser);
+ if ($ltiauth) {
+ $usersty = 'display:none';
+ $onclickrequser = ' onclick="toggleLTIReqUser(this.form,'."'requser','optional','1','block','add'".');"';
+ %checkedrequser = (
+ no => ' checked="checked"',
+ yes => '',
+ );
+ $datatable .= '<fieldset><legend>'.$lt{'requ'}.'</legend>';
+ }
+ $datatable .= '<span class="LC_nobreak">'.$desc{'name'}.
':<input type="text" size="15" name="linkprot_name_add" value="" autocomplete="off"'.$disabled.' /></span> '."\n".
(' 'x2).
- '<span class="LC_nobreak">'.$lt{'version'}.':<select name="linkprot_version_add"'.$disabled.'>'.
+ '<span class="LC_nobreak">'.$desc{'version'}.':<select name="linkprot_version_add"'.$disabled.'>'.
'<option value="LTI-1p0" selected="selected">1.1</option></select></span> '."\n".
(' 'x2).
- '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="3" name="linkprot_lifetime_add" value="300"'.$disabled.' /></span> '."\n".
- '<br /><br />';
+ '<span class="LC_nobreak">'.$desc{'lifetime'}.':<input type="text" size="3" name="linkprot_lifetime_add" value="300"'.$disabled.' /></span> '."\n";
+ if ($ltiauth) {
+ $datatable .= (' 'x2).'<span class="LC_nobreak">'.$desc{'requser'}.'?'.
+ '<label><input type="radio" name="linkprot_requser_add" value="0"'.
+ $onclickrequser.$checkedrequser{'no'}.$disabled.' />'.&mt('No').'</label> '.
+ '<label><input type="radio" name="linkprot_requser_add" value="1"'.
+ $onclickrequser.$checkedrequser{'yes'}.$disabled.' />'.&mt('Yes').'</label></span>';
+ }
+ $datatable .= '<br /><br />';
if ($switchserver) {
$datatable .= '<span class="LC_nobreak">'.&mt('Key and Secret are required').' - '.&mt("submit from course's home server: [_1].",$switchserver).'</span>'."\n";
} else {
- $datatable .= '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="linkprot_key_add" value="" autocomplete="off"'.$disabled.' /></span> '."\n".
+ $datatable .= '<span class="LC_nobreak">'.$desc{'key'}.':<input type="text" size="25" name="linkprot_key_add" value="" autocomplete="off"'.$disabled.' /></span> '."\n".
(' 'x2).
- '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="linkprot_secret_add" value="" autocomplete="off"'.$disabled.' />'.
+ '<span class="LC_nobreak">'.$desc{'secret'}.':<input type="password" size="20" name="linkprot_secret_add" value="" autocomplete="off"'.$disabled.' />'.
'<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.linkprot_secret_add.type='."'text'".' } else { this.form.linkprot_secret_add.type='."'password'".' }"'.$disabled.' />'.&mt('Visible input').'</label></span> '."\n";
}
+ if ($ltiauth) {
+ $datatable .= '</fieldset>'.
+ '<fieldset id="linkprot_optional_add" style="'.$usersty.'"><legend>'.$lt{'opti'}.'</legend>'.
+ &linkprot_options('add',$itemcount,$disabled,{},\%desc).
+ '</fieldset>';
+ }
$datatable .= '</td></tr>';
$$rowtotal ++;
return $datatable;;
}
sub linkprot_names {
- my %lt = &Apache::lonlocal::texthash(
+ return &Apache::lonlocal::texthash(
'version' => 'LTI Version',
'key' => 'Key',
'lifetime' => 'Nonce lifetime (s)',
- 'name' => 'Launcher Application Name',
+ 'name' => 'Launcher Application',
'secret' => 'Secret',
+ 'requser' => 'Use identity',
+ 'email' => 'Email address',
+ 'sourcedid' => 'User ID',
+ 'other' => 'Other',
+ 'auth' => 'Display LON-CAPA login page',
+ 'reject' => 'Discontinue launch process',
);
- return %lt;
}
sub check_switchserver {
@@ -5563,6 +5729,56 @@
return $switchserver;
}
+sub linkprot_options {
+ my ($num,$itemcount,$disabled,$current,$desc) = @_;
+ my %lt;
+ if (ref($desc) eq 'HASH') {
+ %lt = %{$desc};
+ }
+ my $userfieldsty = 'none';
+ my (%checked,$userfield);
+ $checked{'sourcedid'} = ' checked="checked"';
+ $checked{'reject'} = ' checked="checked"';
+ if (ref($current) eq 'HASH') {
+ if (($current->{'mapuser'} ne '') && ($current->{'mapuser'} ne 'lis_person_sourcedid')) {
+ $checked{'sourcedid'} = '';
+ if ($current->{'mapuser'} eq 'lis_person_contact_email_primary') {
+ $checked{'email'} = ' checked="checked"';
+ } else {
+ $checked{'other'} = ' checked="checked"';
+ $userfield = $current->{'mapuser'};
+ $userfieldsty = 'inline-block';
+ }
+ }
+ if (($current->{'notstudent'} ne '') && ($current->{'notstudent'} ne 'reject')) {
+ $checked{'reject'} = '';
+ $checked{'auth'} = ' checked="checked"';
+ }
+ }
+ my $onclickuser = ' onclick="toggleLTIReqUser(this.form,'."'mapuser','userfield','other','inline-block','$num'".');"';
+ my $output = '<div class="LC_floatleft"><span class="LC_nobreak">'.
+ &mt('Source of LON-CAPA username in LTI request').': ';
+ foreach my $option ('sourcedid','email','other') {
+ $output .= '<label><input type="radio" name="linkprot_mapuser_'.$num.'" value="'.$option.'"'.
+ $checked{$option}.$onclickuser.$disabled.' />'.$lt{$option}.'</label>'.
+ ($option eq 'other' ? '' : (' 'x2) );
+ }
+ $output .= '</span></div>'.
+ '<div class="LC_floatleft" style="display:'.$userfieldsty.';" id="linkprot_userfield_'.$num.'">'.
+ '<input type="text" name="linkprot_customuser_'.$num.'" '.
+ 'value="'.$userfield.'"'.$disabled.' /></div>';
+ $output .= '<br />'.
+ '<div class="LC_floatleft"><span class="LC_nobreak">'.
+ &mt('Action when username is not for an enrolled student').': ';
+ foreach my $option ('reject','auth') {
+ $output .= '<label><input type="radio" name="linkprot_notstudent_'.$num.'" value="'.$option.'"'.
+ $checked{$option}.$disabled.' />'.$lt{$option}.'</label>'.
+ ($option eq 'auth' ? '' : (' 'x2) );
+ }
+ $output .= '</span></div>';
+ return $output;
+}
+
sub print_other {
my ($cdom,$settings,$allitems,$rowtotal,$crstype,$noedit) = @_;
unless ((ref($settings) eq 'HASH') && (ref($allitems) eq 'ARRAY')) {
Index: loncom/lti/ltiauth.pm
diff -u loncom/lti/ltiauth.pm:1.31 loncom/lti/ltiauth.pm:1.32
--- loncom/lti/ltiauth.pm:1.31 Wed Feb 2 00:31:16 2022
+++ loncom/lti/ltiauth.pm Sun Feb 6 21:37:00 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Basic LTI Authentication Module
#
-# $Id: ltiauth.pm,v 1.31 2022/02/02 00:31:16 raeburn Exp $
+# $Id: ltiauth.pm,v 1.32 2022/02/06 21:37:00 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -79,7 +79,7 @@
}
unless (keys(%{$params})) {
- &invalid_request($r,1);
+ &invalid_request($r,'No parameters included in launch request');
return OK;
}
@@ -89,7 +89,7 @@
$params->{'oauth_version'} &&
$params->{'oauth_signature'} &&
$params->{'oauth_signature_method'}) {
- &invalid_request($r,2);
+ &invalid_request($r,'One or more required parameters missing from launch request');
return OK;
}
@@ -142,34 +142,99 @@
# where url was /adm/launch/tiny/$cdom/$uniqueid
#
- my ($itemid,$ltitype,%crslti);
+ my ($itemid,$ltitype,%crslti,%lti_in_use);
$itemid = &get_lti_itemid($requri,$hostname,$params,$cdom,$cnum,'deeplink');
if ($itemid) {
%crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
}
if (($itemid) && (ref($crslti{$itemid}) eq 'HASH')) {
$ltitype = 'c';
- unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
- $crslti{$itemid}{'lifetime'},$cdom,$r->dir_config('lonLTIDir'))) {
- &invalid_request($r,3);
+ if (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
+ $crslti{$itemid}{'lifetime'},$cdom,$r->dir_config('lonLTIDir'))) {
+ %lti_in_use = %{$crslti{$itemid}};
+ } else {
+ &invalid_request($r,'Time limit exceeded for launch request credentials');
return OK;
}
} else {
- my %lti;
$itemid = &get_lti_itemid($requri,$hostname,$params,$cdom,'','deeplink');
+ my %lti;
if ($itemid) {
%lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
}
if (($itemid) && (ref($lti{$itemid}) eq 'HASH')) {
$ltitype = 'd';
- unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
+ if (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
$lti{$itemid}{'lifetime'},$cdom,
$r->dir_config('lonLTIDir'))) {
- &invalid_request($r,4);
+ %lti_in_use = %{$lti{$itemid}};
+ } else {
+ &invalid_request($r,'Time limit exceeded for launch request credentials');
return OK;
}
}
}
+ if (($itemid) && ($lti_in_use{'requser'})) {
+ my %courseinfo = &Apache::lonnet::coursedescription($cdom.'_'.$cnum);
+ my $ltiauth;
+ if (exists($courseinfo{'internal.ltiauth'})) {
+ $ltiauth = $courseinfo{'internal.ltiauth'};
+ } else {
+ my %domdefs = &Apache::lonnet::get_domain_defaults($cdom);
+ $ltiauth = $domdefs{'crsltiauth'};
+ }
+ if ($ltiauth) {
+ my $possuname;
+ my $mapuser = $crslti{$itemid}{'mapuser'};
+ if ($mapuser eq 'sourcedid') {
+ if ($params->{'lis_person_sourcedid'} =~ /^$match_username$/) {
+ $possuname = $params->{'lis_person_sourcedid'};
+ }
+ } elsif ($mapuser eq 'email') {
+ if ($params->{'lis_person_contact_email_primary'} =~ /^$match_username$/) {
+ $possuname = $params->{'lis_person_contact_email_primary'};
+ }
+ } elsif (exists($params->{$mapuser})) {
+ if ($params->{$mapuser} =~ /^$match_username$/) {
+ $possuname = $params->{$mapuser};
+ }
+ }
+ if ($possuname ne '') {
+ my $uhome = &Apache::lonnet::homeserver($possuname,$cdom);
+ unless ($uhome eq 'no_host') {
+ my $uname = $possuname;
+ my ($is_student,$is_nonstudent);
+ my %course_roles =
+ &Apache::lonnet::get_my_roles($uname,$cdom,,'userroles',['active'],
+ ['cc','co','in','ta','ep','ad','st','cr'],
+ [$cdom]);
+ foreach my $key (keys(%course_roles)) {
+ my ($trest,$tdomain,$trole,$sec) = split(/:/,$key);
+ if (($trest eq $cnum) && ($tdomain eq $cdom)) {
+ if ($trole eq 'st') {
+ $is_student = 1;
+ } else {
+ $is_nonstudent = 1;
+ last;
+ }
+ }
+ }
+ if (($is_student) && (!$is_nonstudent)) {
+ unless (&Apache::lonnet::is_advanced_user($uname,$cdom)) {
+ foreach my $key (%{$params}) {
+ delete($env{'form.'.$key});
+ }
+ &linkprot_session($r,$uname,$cnum,$cdom,$uhome,$itemid,$ltitype,$tail,$lonhost);
+ return OK;
+ }
+ }
+ }
+ }
+ if ($lti_in_use{'notstudent'} eq 'reject') {
+ &invalid_request($r,'Information for valid user missing from launch request');
+ }
+ }
+ }
if ($itemid) {
foreach my $key (%{$params}) {
delete($env{'form.'.$key});
@@ -180,22 +245,22 @@
$r->internal_redirect($tail.'?ltoken='.$ltoken);
$r->set_handlers('PerlHandler'=> undef);
} else {
- &invalid_request($r,5);
+ &invalid_request($r,'Failed to store information from launch request');
}
} else {
- &invalid_request($r,6);
+ &invalid_request($r,'Launch request could not be validated');
}
} else {
- &invalid_request($r,7);
+ &invalid_request($r,'Launch unavailable on this LON-CAPA server');
}
} else {
- &invalid_request($r,8);
+ &invalid_request($r,'Launch unavailable for this domain');
}
} else {
- &invalid_request($r,9);
+ &invalid_request($r,'Invalid launch URL');
}
} else {
- &invalid_request($r,10);
+ &invalid_request($r,'Invalid launch URL');
}
return OK;
}
@@ -296,7 +361,7 @@
if ($tail =~ m{^/uploaded/($match_domain)/($match_courseid)/(?:default|supplemental)(?:|_\d+)\.(?:sequence|page)(|___\d+___.+)$}) {
($urlcdom,$urlcnum,my $rest) = ($1,$2,$3);
if (($cdom ne '') && ($cdom ne $urlcdom)) {
- &invalid_request($r,11);
+ &invalid_request($r,'Incorrect domain in requested URL');
return OK;
}
if ($rest eq '') {
@@ -315,13 +380,13 @@
} elsif ($tail =~ m{^/($match_domain)/($match_courseid)$}) {
($urlcdom,$urlcnum) = ($1,$2);
if (($cdom ne '') && ($cdom ne $urlcdom)) {
- &invalid_request($r,12);
+ &invalid_request($r,'Incorrect domain in requested URL');
return OK;
}
} elsif ($tail =~ m{^/tiny/($match_domain)/(\w+)$}) {
($urlcdom,$urlcnum) = &course_from_tinyurl($tail);
if (($urlcdom eq '') || ($urlcnum eq '')) {
- &invalid_request($r,13);
+ &invalid_request($r,'Invalid URL shortcut');
return OK;
}
}
@@ -367,7 +432,7 @@
# configuration in LON-CAPA for that LTI Consumer.
#
unless (($itemid) && (ref($lti{$itemid}) eq 'HASH')) {
- &invalid_request($r,14);
+ &invalid_request($r,'Launch request could not be validated');
return OK;
}
@@ -377,7 +442,7 @@
#
unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
$lti{$itemid}{'lifetime'},$cdom,$r->dir_config('lonLTIDir'))) {
- &invalid_request($r,15);
+ &invalid_request($r,'Time limit exceeded for launch request credentials');
return OK;
}
@@ -398,10 +463,10 @@
$r->internal_redirect($tail.'?ltoken='.$ltoken);
$r->set_handlers('PerlHandler'=> undef);
} else {
- &invalid_request($r,16);
+ &invalid_request($r,'Failed to store information from launch request');
}
} else {
- &invalid_request($r,17);
+ &invalid_request($r,'Launch URL invalid for matched launch credentials');
}
return OK;
}
@@ -463,7 +528,7 @@
my $storedcnum = $1;
my $crshome = &Apache::lonnet::homeserver($storedcnum,$cdom);
if ($crshome =~ /(con_lost|no_host|no_such_host)/) {
- &invalid_request($r,18);
+ &invalid_request($r,'Invalid courseID included in launch data');
return OK;
} else {
$posscnum = $storedcnum;
@@ -475,7 +540,7 @@
if ($urlcnum ne '') {
if ($posscnum ne '') {
if ($posscnum ne $urlcnum) {
- &invalid_request($r,19);
+ &invalid_request($r,'Course ID included in launch data incompatible with URL');
return OK;
} else {
$cnum = $posscnum;
@@ -483,7 +548,7 @@
} else {
my $crshome = &Apache::lonnet::homeserver($urlcnum,$cdom);
if ($crshome =~ /(con_lost|no_host|no_such_host)/) {
- &invalid_request($r,20);
+ &invalid_request($r,'Valid course ID could not be extracted from requested URL');
return OK;
} else {
$cnum = $urlcnum;
@@ -548,7 +613,7 @@
$domdesc,\%data,\%alerts,\%rulematch,
\%inst_results,\%curr_rules,%got_rules);
if ($result eq 'notallowed') {
- &invalid_request($r,21);
+ &invalid_request($r,'Account creation not permitted for this user');
} elsif ($result eq 'ok') {
if (($ltiroles[0] eq 'Instructor') && ($lcroles[0] eq 'cc') && ($lti{$itemid}{'mapcrs'}) &&
($lti{$itemid}{'makecrs'})) {
@@ -557,16 +622,16 @@
}
}
} else {
- &invalid_request($r,22);
+ &invalid_request($r,'An error occurred during account creation');
return OK;
}
} else {
- &invalid_request($r,23);
+ &invalid_request($r,'Account creation not permitted');
return OK;
}
}
} else {
- &invalid_request($r,24);
+ &invalid_request($r,'Could not determine username and/or domain for user');
return OK;
}
@@ -589,10 +654,10 @@
$symb,$cdom,$cnum,$params,\@ltiroles,$lti{$itemid},\@lcroles,
$reqcrs,$sourcecrs);
} else {
- &invalid_request($r,25);
+ &invalid_request($r,'No LON-CAPA course available, and creation is not permitted for this user');
}
} else {
- &invalid_request($r,26);
+ &invalid_request($r,'No LON-CAPA course available, and creation is not permitted');
}
} else {
<i_session($r,$itemid,$uname,$udom,$uhome,$lonhost,undef,$mapurl,$tail,
@@ -683,7 +748,7 @@
}
}
if ($reqrole eq '') {
- &invalid_request($r,27);
+ &invalid_request($r,'No matching role available in LON-CAPA course, and not permitted to self-enroll');
return OK;
} else {
unless (%crsenv) {
@@ -693,10 +758,10 @@
my $default_enrollment_end_date = $crsenv{'default_enrollment_end_date'};
my $now = time;
if ($default_enrollment_end_date && $default_enrollment_end_date <= $now) {
- &invalid_request($r,28);
+ &invalid_request($r,'No active role available in LON-CAPA course, and past end date for self-enrollment');
return OK;
} elsif ($default_enrollment_start_date && $default_enrollment_start_date >$now) {
- &invalid_request($r,29);
+ &invalid_request($r,'No active role available in LON-CAPA course, and brefor start date for self-enrollment');
return OK;
} else {
$selfenrollrole = $reqrole.'./'.$cdom.'/'.$cnum;
@@ -828,24 +893,7 @@
} else {
&Apache::lonnet::logthis(" LTI authorized user ($itemid): $uname:$udom, course dom: $cdom");
}
- my ($is_balancer,$otherserver,$hosthere);
- ($is_balancer,$otherserver) =
- &Apache::lonnet::check_loadbalancing($uname,$udom,'login');
- if ($is_balancer) {
- if ($otherserver eq '') {
- my $lowest_load;
- ($otherserver,undef,undef,undef,$lowest_load) = &Apache::lonnet::choose_server($udom);
- if ($lowest_load > 100) {
- $otherserver = &Apache::lonnet::spareserver($r,$lowest_load,$lowest_load,1,$udom);
- }
- }
- if ($otherserver ne '') {
- my @hosts = &Apache::lonnet::current_machine_ids();
- if (grep(/^\Q$otherserver\E$/, at hosts)) {
- $hosthere = $otherserver;
- }
- }
- }
+ my ($is_balancer,$otherserver,$hosthere) = &check_balancer($r,$uname,$udom);
my $protocol = 'http';
if ($ENV{'SERVER_PORT'} == 443) {
$protocol = 'https';
@@ -924,8 +972,7 @@
$r->internal_redirect($redirecturl);
$r->set_handlers('PerlHandler'=> undef);
} else {
- # need to login them in, so generate the need data that
- # migrate expects to do login
+ # need to login them in, so generate the data migrate expects to do login
foreach my $key (keys(%{$params})) {
delete($env{'form.'.$key});
}
@@ -1003,8 +1050,71 @@
return;
}
+sub linkprot_session {
+ my ($r,$uname,$cnum,$cdom,$uhome,$itemid,$ltitype,$dest,$lonhost) = @_;
+ $r->user($uname);
+ if ($ltitype eq 'c') {
+ &Apache::lonnet::logthis("Course Link Protector ($itemid) authorized student: $uname:$cdom, course: $cdom\_$cnum");
+ } elsif ($ltitype eq 'd') {
+ &Apache::lonnet::logthis("Domain LTI for link protection ($itemid) authorized student: $uname:$cdom, course: $cdom\_$cnum");
+ }
+ my ($is_balancer,$otherserver,$hosthere) = &check_balancer($r,$uname,$cdom);
+ if (($is_balancer) && (!$hosthere)) {
+ # login but immediately go to switch server
+ $env{'form.origurl'} = $dest;
+ $env{'request.linkprot'} = $itemid.$ltitype.':'.$dest;
+ $env{'request.deeplink.login'} = $dest;
+ my $redirecturl = '/adm/switchserver';
+ if ($otherserver ne '') {
+ $redirecturl .= '?otherserver='.$otherserver;
+ }
+ $r->internal_redirect($redirecturl);
+ $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 %info=('ip' => $ip,
+ 'domain' => $cdom,
+ 'username' => $uname,
+ 'server' => $lonhost,
+ 'linkprot' => $itemid.$ltitype.':'.$dest,
+ 'home' => $uhome,
+ 'origurl' => $dest,
+ 'deeplink.login' => $dest,
+ );
+ my $token = &Apache::lonnet::tmpput(\%info,$lonhost);
+ $env{'form.token'} = $token;
+ $r->internal_redirect('/adm/migrateuser');
+ $r->set_handlers('PerlHandler'=> undef);
+ }
+ return;
+}
+
+sub check_balancer {
+ my ($r,$uname,$udom) = @_;
+ my ($is_balancer,$otherserver,$hosthere);
+ ($is_balancer,$otherserver) =
+ &Apache::lonnet::check_loadbalancing($uname,$udom,'login');
+ if ($is_balancer) {
+ if ($otherserver eq '') {
+ my $lowest_load;
+ ($otherserver,undef,undef,undef,$lowest_load) = &Apache::lonnet::choose_server($udom);
+ if ($lowest_load > 100) {
+ $otherserver = &Apache::lonnet::spareserver($r,$lowest_load,$lowest_load,1,$udom);
+ }
+ }
+ if ($otherserver ne '') {
+ my @hosts = &Apache::lonnet::current_machine_ids();
+ if (grep(/^\Q$otherserver\E$/, at hosts)) {
+ $hosthere = $otherserver;
+ }
+ }
+ }
+ return ($is_balancer,$otherserver,$hosthere);
+}
+
sub invalid_request {
- my ($r,$num) = @_;
+ my ($r,$msg) = @_;
&Apache::loncommon::content_type($r,'text/html');
$r->send_http_header;
if ($r->header_only) {
@@ -1013,7 +1123,12 @@
&Apache::lonlocal::get_language_handle($r);
$r->print(
&Apache::loncommon::start_page('Invalid LTI call','',{ 'only_body' => 1,}).
- &mt('Invalid LTI call [_1]',$num).
+ '<h3>'.&mt('Invalid LTI launch request').'</h3>'.
+ '<p class="LC_warning">'.
+ &mt('Launch of LON-CAPA is unavailable from the "external tool" link you had followed in another web application.').
+ &mt('Launch failed for the following reason:').
+ '</p>'.
+ '<p class="LC_error">'.$msga'.</p>'.
&Apache::loncommon::end_page());
return;
}
Index: loncom/auth/migrateuser.pm
diff -u loncom/auth/migrateuser.pm:1.56 loncom/auth/migrateuser.pm:1.57
--- loncom/auth/migrateuser.pm:1.56 Thu Nov 18 20:25:27 2021
+++ loncom/auth/migrateuser.pm Sun Feb 6 21:37:04 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Starts a user off based of an existing token.
#
-# $Id: migrateuser.pm,v 1.56 2021/11/18 20:25:27 raeburn Exp $
+# $Id: migrateuser.pm,v 1.57 2022/02/06 21:37:04 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -739,6 +739,17 @@
}
}
if ($data{'linkprot'} ne '') {
+ if (($env{'user.name'} ne $data{'username'}) ||
+ ($env{'user.domain'} ne $data{'domain'})) {
+ my %linkprot_env;
+ foreach my $item ('linkprot','deeplink.login') {
+ if ($data{$item}) {
+ $linkprot_env{'request.'.$item} = $data{$item};
+ }
+ }
+ &logout($r,$ip,$handle,\%data,\%linkprot_env);
+ return OK;
+ }
&Apache::lonnet::appenv({'request.linkprot' => $data{'linkprot'}});
if ($env{'request.linkkey'}) {
&Apache::lonnet::delenv('request.linkkey');
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.1479 loncom/lonnet/perl/lonnet.pm:1.1480
--- loncom/lonnet/perl/lonnet.pm:1.1479 Tue Feb 1 23:13:20 2022
+++ loncom/lonnet/perl/lonnet.pm Sun Feb 6 21:37:07 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network
# TCP networking package
#
-# $Id: lonnet.pm,v 1.1479 2022/02/01 23:13:20 raeburn Exp $
+# $Id: lonnet.pm,v 1.1480 2022/02/06 21:37:07 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -2699,7 +2699,10 @@
}
if ($domconfig{'coursedefaults'}{'texengine'}) {
$domdefaults{'texengine'} = $domconfig{'coursedefaults'}{'texengine'};
- }
+ }
+ if (exists($domconfig{'coursedefaults'}{'ltiauth'})) {
+ $domdefaults{'crsltiauth'} = $domconfig{'coursedefaults'}{'ltiauth'};
+ }
}
if (ref($domconfig{'usersessions'}) eq 'HASH') {
if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') {
More information about the LON-CAPA-cvs
mailing list