[LON-CAPA-cvs] cvs: loncom / Lond.pm lond /interface courseprefs.pm lonconfigsettings.pm /lonnet/perl lonnet.pm /lti ltiauth.pm
raeburn
raeburn at source.lon-capa.org
Tue Feb 1 18:13:21 EST 2022
raeburn Tue Feb 1 23:13:21 2022 EDT
Modified files:
/loncom/interface courseprefs.pm lonconfigsettings.pm
/loncom/lonnet/perl lonnet.pm
/loncom/lti ltiauth.pm
/loncom Lond.pm lond
Log:
- Bug 6907
- Extraction of LTI itemID based on consumer key in signed LTI payload, and
verification with available secret moved from ltiauth.pm to Lond.pm.
- Verification will now occur on course's home server or domain's primary
library server, for course-defined LTI ID, and domain-defined LTI ID
respectively.
- Setting and modifying link protection key and secret now requires user
session on course's home server.
- Display of existing LTI key only available of course's home server.
- Display of stored LTI secret eliminated so a Course Coordinator will
need to record it offline or commit it to memory.
-------------- next part --------------
Index: loncom/interface/courseprefs.pm
diff -u loncom/interface/courseprefs.pm:1.98 loncom/interface/courseprefs.pm:1.99
--- loncom/interface/courseprefs.pm:1.98 Tue Feb 1 18:23:24 2022
+++ loncom/interface/courseprefs.pm Tue Feb 1 23:13:19 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set configuration settings for a course
#
-# $Id: courseprefs.pm,v 1.98 2022/02/01 18:23:24 raeburn Exp $
+# $Id: courseprefs.pm,v 1.99 2022/02/01 23:13:19 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -365,11 +365,25 @@
}
my %values=&Apache::lonnet::dump('environment',$cdom,$cnum);
- my %courselti=&Apache::lonnet::dump('lti',$cdom,$cnum,undef,undef,undef,1);
- if ($courselti{'lock'}) {
- delete($courselti{'lock'});
+ my %lti=&Apache::lonnet::dump('lti',$cdom,$cnum,undef,undef,undef,1);
+ my %ltienc = &Apache::lonnet::dump('nohist_ltienc',$cdom,$cnum,undef,undef,undef,1);
+ foreach my $id (keys(%lti)) {
+ if (ref($lti{$id}) eq 'HASH') {
+ if (ref($ltienc{$id}) eq 'HASH') {
+ $values{'linkprotection'}{$id} = { %{$lti{$id}}, %{$ltienc{$id}} };
+ } else {
+ $values{'linkprotection'}{$id} = $lti{$id};
+ }
+ }
+ unless ($phase eq 'process') {
+ if (ref($values{'linkprotection'}{$id}) eq 'HASH') {
+ delete($values{'linkprotection'}{$id}{'secret'});
+ }
+ }
+ }
+ if ($lti{'lock'}) {
+ delete($lti{'lock'});
}
- $values{'linkprotection'} = \%courselti;
my @prefs_order = ('courseinfo','localization','feedback','discussion',
'classlists','appearance','grading','printouts',
'menuitems','linkprotection','spreadsheet','bridgetasks',
@@ -596,7 +610,7 @@
my $jscript = &get_jscript($cid,$cdom,$phase,$crstype,\%values,$noedit);
my @allitems = &get_allitems(%prefs);
&Apache::lonconfigsettings::display_settings($r,$cdom,$phase,$context,
- \@prefs_order,\%prefs,\%values,undef,$jscript,\@allitems,$crstype,
+ \@prefs_order,\%prefs,\%values,$cnum,$jscript,\@allitems,$crstype,
'coursepref',$parm_permission);
} else {
&Apache::lonconfigsettings::display_choices($r,$phase,$context,
@@ -649,7 +663,7 @@
}
sub print_config_box {
- my ($r,$cdom,$phase,$action,$item,$settings,$allitems,$crstype,$parm_permission) = @_;
+ my ($r,$cdom,$cnum,$phase,$action,$item,$settings,$allitems,$crstype,$parm_permission) = @_;
my $ordered = $item->{'ordered'};
my $itemtext = $item->{'itemtext'};
my $noedit;
@@ -780,7 +794,7 @@
} elsif ($action eq 'menuitems') {
$output .= &print_menuitems('bottom',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit);
} elsif ($action eq 'linkprotection') {
- $output .= &print_linkprotection($cdom,$settings,\$rowtotal,$crstype,$noedit);
+ $output .= &print_linkprotection($cdom,$cnum,$settings,\$rowtotal,$crstype,$noedit);
} elsif ($action eq 'other') {
$output .= &print_other($cdom,$settings,$allitems,\$rowtotal,$crstype,$noedit);
}
@@ -794,7 +808,7 @@
sub process_changes {
my ($cdom,$cnum,$action,$values,$item,$changes,$allitems,$disallowed,$crstype) = @_;
- my (%newvalues,%courselti,$errors);
+ my (%newvalues,%lti,%ltienc,$errors);
if (ref($item) eq 'HASH') {
if (ref($changes) eq 'HASH') {
my @ordered;
@@ -812,12 +826,12 @@
}
}
} elsif ($action eq 'linkprotection') {
- if (ref($values->{'linkprotection'}) eq 'HASH') {
- foreach my $id (keys(%{$values->{'linkprotection'}})) {
+ if (ref($values->{$action}) eq 'HASH') {
+ foreach my $id (keys(%{$values->{$action}})) {
if ($id =~ /^\d+$/) {
push(@ordered,$id);
- unless (ref($values->{'linkprotection'}->{$id}) eq 'HASH') {
- $courselti{$id} = '';
+ unless (ref($values->{$action}->{$id}) eq 'HASH') {
+ $lti{$id} = '';
}
}
}
@@ -965,6 +979,7 @@
}
} elsif ($action eq 'linkprotection') {
my %menutitles = <imenu_titles();
+ my $switchserver = &check_switchserver($cdom,$cnum);
my (@items,%deletions,%itemids,%haschanges);
if ($env{'form.linkprot_add'}) {
my $name = $env{'form.linkprot_name_add'};
@@ -980,18 +995,18 @@
'</span>';
}
}
- if (ref($values->{'linkprotection'}) eq 'HASH') {
+ if (ref($values->{$action}) eq 'HASH') {
my @todelete = &Apache::loncommon::get_env_multiple('form.linkprot_del');
my $maxnum = $env{'form.linkprot_maxnum'};
for (my $i=0; $i<=$maxnum; $i++) {
my $itemid = $env{'form.linkprot_id_'.$i};
$itemid =~ s/\D+//g;
if ($itemid) {
- if (ref($values->{'linkprotection'}->{$itemid}) eq 'HASH') {
+ if (ref($values->{$action}->{$itemid}) eq 'HASH') {
push(@items,$i);
$itemids{$i} = $itemid;
if ((@todelete > 0) && (grep(/^$i$/, at todelete))) {
- $deletions{$itemid} = $values->{'linkprotection'}->{$itemid}->{'name'};
+ $deletions{$itemid} = $values->{$action}->{$itemid}->{'name'};
}
}
}
@@ -1002,19 +1017,19 @@
my $itemid = $itemids{$idx};
next unless ($itemid);
if (exists($deletions{$itemid})) {
- $courselti{$itemid} = $deletions{$itemid};
+ $lti{$itemid} = $deletions{$itemid};
$haschanges{$itemid} = 1;
next;
}
my %current;
- if (ref($values->{'linkprotection'}) eq 'HASH') {
- if (ref($values->{'linkprotection'}->{$itemid}) eq 'HASH') {
- foreach my $key (keys(%{$values->{'linkprotection'}->{$itemid}})) {
- $current{$key} = $values->{'linkprotection'}->{$itemid}->{$key};
+ if (ref($values->{$action}) eq 'HASH') {
+ if (ref($values->{$action}->{$itemid}) eq 'HASH') {
+ foreach my $key (keys(%{$values->{$action}->{$itemid}})) {
+ $current{$key} = $values->{$action}->{$itemid}->{$key};
}
}
}
- foreach my $inner ('name','key','secret','lifetime','version') {
+ foreach my $inner ('name','lifetime','version') {
my $formitem = 'form.linkprot_'.$inner.'_'.$idx;
$env{$formitem} =~ s/(`)/'/g;
if ($inner eq 'lifetime') {
@@ -1026,13 +1041,40 @@
}
}
if ($env{$formitem} ne '') {
- $courselti{$itemid}{$inner} = $env{$formitem};
+ $lti{$itemid}{$inner} = $env{$formitem};
+ }
+ }
+ unless ($switchserver) {
+ my $keyitem = 'form.linkprot_key_'.$idx;
+ $env{$keyitem} =~ s/(`)/'/g;
+ unless ($idx eq 'add') {
+ if ($current{'key'} ne $env{$keyitem}) {
+ $haschanges{$itemid} = 1;
+ }
+ }
+ if ($env{$keyitem} ne '') {
+ $lti{$itemid}{'key'} = $env{$keyitem};
+ }
+ my $secretitem = 'form.linkprot_secret_'.$idx;
+ $env{$secretitem} =~ s/(`)/'/g;
+ if ($current{'usable'}) {
+ if ($env{'form.linkprot_changesecret_'.$idx}) {
+ if ($env{$secretitem} ne '') {
+ $lti{$itemid}{'secret'} = $env{$secretitem};
+ $haschanges{$itemid} = 1;
+ }
+ } else {
+ $lti{$itemid}{'secret'} = $current{'secret'};
+ }
+ } elsif ($env{$secretitem} ne '') {
+ $lti{$itemid}{'secret'} = $env{$secretitem};
+ $haschanges{$itemid} = 1;
}
}
}
if (keys(%haschanges)) {
foreach my $entry (keys(%haschanges)) {
- $changes->{$entry} = $courselti{$entry};
+ $changes->{$entry} = $lti{$entry};
}
}
} else {
@@ -1655,7 +1697,7 @@
if (grep(/^\Q$item\E$/,@{$actions})) {
$output .= '<h3>'.&mt($prefs->{$item}{'text'}).'</h3>';
if (ref($changes->{$item}) eq 'HASH') {
- if ((keys(%{$changes->{$item}}) > 0) || ($item eq 'linkprotection')) {
+ if (keys(%{$changes->{$item}}) > 0) {
$output .= &mt('Changes made:').'<ul style="list-style:none;">';
if ($item eq 'other') {
foreach my $key (sort(keys(%{$changes->{$item}}))) {
@@ -1669,34 +1711,96 @@
}
}
} elsif ($item eq 'linkprotection') {
- if (&Apache::lonnet::put('lti',$changes->{'linkprotection'},$cdom,$cnum,1) eq 'ok') {
- my $hashid=$cdom.'_'.$cnum;
- &Apache::lonnet::devalidate_cache_new('courselti',$hashid);
- foreach my $itemid (sort { $a <=> $b } %{$changes->{'linkprotection'}}) {
- if (ref($changes->{'linkprotection'}->{$itemid}) eq 'HASH') {
- my %values = %{$changes->{'linkprotection'}->{$itemid}};
- my %desc = &linkprot_names();
- my $display;
- foreach my $title ('name','lifetime','version','key','secret') {
- if ($title eq 'secret') {
- my $length = length($values{$title});
- $display .= $desc{$title}.': '.('*' x $length);
- } elsif ($title eq 'version') {
- if ($values{$title} eq 'LTI-1p0') {
- $display .= $desc{$title}.': 1.1, ';
+ my (%ltienc,$lti_save_error);
+ if (ref($changes->{$item}) eq 'HASH') {
+ foreach my $id (sort { $a <=> $b } keys(%{$changes->{$item}})) {
+ if (ref($changes->{$item}->{$id}) eq 'HASH') {
+ if (exists($changes->{$item}->{$id}->{'key'})) {
+ $ltienc{$id}{'key'} = $changes->{$item}->{$id}->{'key'};
+ delete($changes->{$item}->{$id}->{'key'});
+ }
+ if (exists($changes->{$item}->{$id}->{'secret'})) {
+ $ltienc{$id}{'secret'} = $changes->{$item}->{$id}->{'secret'};
+ delete($changes->{$item}->{$id}->{'secret'});
+ } elsif (ref($oldlinkprot{$id}) eq 'HASH') {
+ if (exists($oldlinkprot{$id}{'usable'})) {
+ $changes->{$item}->{$id}->{'usable'} = 1;
+ }
+ }
+ }
+ }
+ }
+ if (keys(%ltienc) > 0) {
+ if (&Apache::lonnet::put('nohist_ltienc',\%ltienc,$cdom,$cnum,1) eq 'ok') {
+ foreach my $id (keys(%ltienc)) {
+ if (exists($ltienc{$id}{'secret'})) {
+ $changes->{$item}->{$id}->{'usable'} = 1;
+ }
+ }
+ } else {
+ $lti_save_error = 1;
+ }
+ }
+ unless ($lti_save_error) {
+ if (&Apache::lonnet::put('lti',$changes->{$item},$cdom,$cnum,1) eq 'ok') {
+ my $hashid=$cdom.'_'.$cnum;
+ &Apache::lonnet::devalidate_cache_new('courselti',$hashid);
+ $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)) {
+ &Apache::lonnet::devalidate_cache_new('courseltienc',$hashid);
+ }
+ }
+ foreach my $id (sort { $a <=> $b } %{$changes->{$item}}) {
+ if (ref($changes->{$item}->{$id}) eq 'HASH') {
+ my %values = %{$changes->{$item}->{$id}};
+ my %desc = &linkprot_names();
+ my $display;
+ foreach my $title ('name','lifetime','version','key','secret') {
+ if (($title eq 'key') || ($title eq 'secret')) {
+ if (ref($ltienc{$id}) eq 'HASH') {
+ if (exists($ltienc{$id}{$title})) {
+ if ($title eq 'secret') {
+ my $length = length($ltienc{$id}{$title});
+ $display .= $desc{$title}.': '.('*' x $length);
+ } else {
+ $display .= $desc{$title}.': '.$ltienc{$id}{$title}.', ';
+ }
+ }
+ }
+ } elsif ($title eq 'version') {
+ if ($values{$title} eq 'LTI-1p0') {
+ $display .= $desc{$title}.': 1.1, ';
+ }
+ } else {
+ $display .= $desc{$title}.': '.$values{$title}.', ';
}
- } else {
- $display .= $desc{$title}.': '.$values{$title}.', ';
}
+ $display =~ s/, $//;
+ $output .= '<li>'.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]','<i>'.$id.'</i>',
+ "'$display'")).'</li>';
+ } elsif (ref($oldlinkprot{$id}) eq 'HASH') {
+ my $oldname = $oldlinkprot{$id}{'name'};
+ $output .= '<li>'.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]','<i>'."$id ($oldname)".'</i>')).'</li>';
}
- $output .= '<li>'.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]','<i>'.$itemid.'</i>',
- "'$display'")).'</li>';
- } elsif (ref($oldlinkprot{$itemid}) eq 'HASH') {
- my $oldname = $oldlinkprot{$itemid}{'name'};
- $output .= '<li>'.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]','<i>'."$itemid ($oldname)".'</i>')).'</li>';
}
+ } else {
+ $lti_save_error = 1;
}
- } else {
+ }
+ unless ($lti_save_error) {
+ my @deletions;
+ foreach my $id (sort { $a <=> $b } keys(%{$changes->{$item}})) {
+ unless (ref($changes->{$item}->{$id}) eq 'HASH') {
+ push (@deletions,$id);
+ }
+ }
+ if (@deletions) {
+ &Apache::lonnet::del('nohist_ltienc',\@deletions,$cdom,$cnum);
+ }
+ }
+ if ($lti_save_error) {
$output .= '<li>'.
'<span class="LC_error">'.
&mt('An error occurred when saving changes to link protection settings, which remain unchanged.').
@@ -2375,11 +2479,45 @@
}
ENDSCRIPT
}
+ my $linkprotector_js = <<"ENDSCRIPT";
+function toggleLTI(form,num,item) {
+ var radioname = '';
+ var currdivid = '';
+ var newdivid = '';
+ if ((document.getElementById('linkprot_divcurr'+item+'_'+num)) &&
+ (document.getElementById('linkprot_divchg'+item+'_'+num))) {
+ currdivid = document.getElementById('linkprot_divcurr'+item+'_'+num);
+ newdivid = document.getElementById('linkprot_divchg'+item+'_'+num);
+ radioname = form.elements['linkprot_change'+item+'_'+num];
+ if (radioname) {
+ if (radioname.length > 0) {
+ var setvis;
+ for (var i=0; i<radioname.length; i++) {
+ if (radioname[i].checked == true) {
+ if (radioname[i].value == 1) {
+ newdivid.style.display = 'inline-block';
+ currdivid.style.display = 'none';
+ setvis = 1;
+ }
+ break;
+ }
+ }
+ if (!setvis) {
+ newdivid.style.display = 'none';
+ currdivid.style.display = 'inline-block';
+ }
+ }
+ }
+ }
+ return;
+}
+ENDSCRIPT
$jscript = '<script type="text/javascript" language="Javascript">'."\n".
'// <![CDATA['."\n".
$browse_js."\n".$categorize_js."\n".$loncaparev_js."\n".
$cloners_js."\n".$instcode_js.
- $syllabus_js."\n".$menuitems_js."\n".'//]]>'."\n".
+ $syllabus_js."\n".$menuitems_js."\n".
+ $linkprotector_js."\n".'//]]>'."\n".
'</script>'."\n".$stubrowse_js."\n";
return $jscript;
}
@@ -5268,7 +5406,7 @@
}
sub print_linkprotection {
- my ($cdom,$settings,$rowtotal,$crstype,$noedit) = @_;
+ my ($cdom,$cnum,$settings,$rowtotal,$crstype,$noedit) = @_;
unless (ref($settings) eq 'HASH') {
return;
}
@@ -5283,6 +5421,8 @@
my %lt = &linkprot_names();
my $itemcount = 0;
+ my $switchserver = &check_switchserver($cdom,$cnum);
+
if (ref($settings->{'linkprotection'}) eq 'HASH') {
if (keys(%{$settings->{'linkprotection'}})) {
my @current = sort { $a <=> $b } keys(%{$settings->{'linkprotection'}});
@@ -5307,20 +5447,65 @@
'<td><span class="LC_nobreak">'.$lt{'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.'">'.
+ '<span class="LC_nobreak">'.$lt{'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">'.$lt{'key'}.
- ':<input type="text" size="25" name="linkprot_key_'.$i.'" value="'.$values{'key'}.'" autocomplete="off"'.$disabled.' /></span> '.
- (' 'x2).
- '<span class="LC_nobreak">'.$lt{'secret'}.':'.
- '<input type="password" size="20" name="linkprot_secret_'.$i.'" value="'.$values{'secret'}.'" 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>'.
- '</td></tr>';
+ '<br /><br />';
+ if ($values{'key'} ne '') {
+ $datatable .= '<span class="LC_nobreak">'.$lt{'key'};
+ if ($noedit) {
+ $datatable .= ': ['.&mt('not shown').']';
+ } elsif ($switchserver) {
+ $datatable .= ': ['.&mt('[_1] to view/edit',$switchserver).']';
+ } else {
+ $datatable .= ':<input type="text" size="25" name="linkprot_key_'.$i.'" value="'.$values{'key'}.'" autocomplete="off"'.$disabled.' />';
+ }
+ $datatable .= '</span> '.(' 'x2);
+ } elsif (!$switchserver) {
+ $datatable .= '<span class="LC_nobreak">'.$lt{'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>'.
+ '<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).
+ '<label><input type="radio" value="1" name="linkprot_changesecret_'.$i.'" onclick="javascript:toggleLTI(this.form,'."'$i','secret'".');" '.$disabled.' />'.&mt('Yes').'</label>'.(' 'x2).
+ '</span><div id="linkprot_divchgsecret_'.$i.'" style="display:none" />'.
+ '<span class="LC_nobreak"> - '.&mt("submit from course's home server: [_1].",$switchserver).'</span>'.
+ '</div>';
+ } elsif ($values{'key'} eq '') {
+ $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">'.&mt('Secret required').' - '.&mt("submit from course's home server: [_1].",$switchserver).'</span>'."\n";
+ }
+ } 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>'.
+ '<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).
+ '<label><input type="radio" value="1" name="linkprot_changesecret_'.$i.'" onclick="javascript:toggleLTI(this.form,'."'$i','secret'".');"'.$disabled.' />'.&mt('Yes').
+ '</label> </span><div id="linkprot_divchgsecret_'.$i.'" style="display:none" />'.
+ '<span class="LC_nobreak">'.&mt('New 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></div>';
+ } else {
+ $datatable .=
+ '<span class="LC_nobreak">'.$lt{'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>';
+ }
+ }
+ $datatable .= '</td></tr>';
$itemcount ++;
}
}
@@ -5337,12 +5522,16 @@
'<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">'.$lt{'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.' />'.
- '<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".
- '</td></tr>';
+ '<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".
+ (' 'x2).
+ '<span class="LC_nobreak">'.$lt{'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";
+ }
+ $datatable .= '</td></tr>';
$$rowtotal ++;
return $datatable;;
}
@@ -5358,6 +5547,22 @@
return %lt;
}
+sub check_switchserver {
+ my ($cdom,$cnum) = @_;
+ my ($allowed,$switchserver);
+ my $home = &Apache::lonnet::homeserver($cnum,$cdom);
+ unless ($home eq 'no_host') {
+ my @ids=&Apache::lonnet::current_machine_ids();
+ foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } }
+ if (!$allowed) {
+ $switchserver='<a href="/adm/switchserver?otherserver='.$home.'&role='.
+ &HTML::Entities::encode($env{'request.role'},'\'<>"&').
+ '&destinationurl=/adm/courseprefs">'.&mt('Switch Server').'</a>';
+ }
+ }
+ return $switchserver;
+}
+
sub print_other {
my ($cdom,$settings,$allitems,$rowtotal,$crstype,$noedit) = @_;
unless ((ref($settings) eq 'HASH') && (ref($allitems) eq 'ARRAY')) {
Index: loncom/interface/lonconfigsettings.pm
diff -u loncom/interface/lonconfigsettings.pm:1.55 loncom/interface/lonconfigsettings.pm:1.56
--- loncom/interface/lonconfigsettings.pm:1.55 Tue Dec 28 02:20:07 2021
+++ loncom/interface/lonconfigsettings.pm Tue Feb 1 23:13:19 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set domain-wide configuration settings
#
-# $Id: lonconfigsettings.pm,v 1.55 2021/12/28 02:20:07 raeburn Exp $
+# $Id: lonconfigsettings.pm,v 1.56 2022/02/01 23:13:19 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -282,13 +282,30 @@
$additem = {'add_entries' => \%loaditems,};
}
} elsif ($context eq 'course') {
+ my $onload;
if (grep(/^courseinfo$/, at actions)) {
- if (@code_order) {
- $additem = {
- add_entries => {'onload' => "courseSet('','load');toggleCloners(document.display.cloners_instcode);"},
- };
+ if (@code_order) {
+ $onload = "courseSet('','load');toggleCloners(document.display.cloners_instcode);";
+ }
+ }
+ if (grep(/^linkprotection$/, at actions)) {
+ if (ref($values) eq 'HASH') {
+ if (ref($values->{'linkprotection'}) eq 'HASH') {
+ my $posslti = scalar(keys(%{$values->{'linkprotection'}}));
+ for (my $i=0; $i<$posslti; $i++) {
+ if ($values->{'linkprotection'}->{'usable'}) {
+ $onload .= "toggleLTI(document.display,'$i','secret');";
+ }
+ }
+ }
}
}
+ if ($onload) {
+ my %loaditems = (
+ 'onload' => $onload,
+ );
+ $additem = {'add_entries' => \%loaditems,};
+ }
}
}
$r->print(&Apache::loncommon::start_page($pagetitle,$js,$additem));
@@ -495,7 +512,7 @@
$phase,$item,$prefs->{$item},$settings);
} else {
($output{$item},$rowtotal{$item}) =
- &Apache::courseprefs::print_config_box($r,$dom,$phase,
+ &Apache::courseprefs::print_config_box($r,$dom,$confname,$phase,
$item,$prefs->{$item},$values,$allitems,$crstype,$parm_permission);
}
$rowsum += $rowtotal{$item};
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.1478 loncom/lonnet/perl/lonnet.pm:1.1479
--- loncom/lonnet/perl/lonnet.pm:1.1478 Tue Feb 1 19:10:54 2022
+++ loncom/lonnet/perl/lonnet.pm Tue Feb 1 23:13:20 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network
# TCP networking package
#
-# $Id: lonnet.pm,v 1.1478 2022/02/01 19:10:54 raeburn Exp $
+# $Id: lonnet.pm,v 1.1479 2022/02/01 23:13:20 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -12266,6 +12266,55 @@
return %courselti;
}
+sub courselti_itemid {
+ my ($cnum,$cdom,$url,$method,$params,$context) = @_;
+ my ($chome,$itemid);
+ $chome = &homeserver($cnum,$cdom);
+ return if ($chome eq 'no_host');
+ if (ref($params) eq 'HASH') {
+ my $items = &freeze_escape($params);
+ my $rep;
+ if (grep { $_ eq $chome } current_machine_ids()) {
+ $rep = LONCAPA::Lond::crslti_itemid($cdom,$cnum,$url,$method,$params,$perlvar{'lonVersion'});
+ } else {
+ my $escurl = &escape($url);
+ my $escmethod = &escape($method);
+ my $items = &freeze_escape($params);
+ $rep = &reply("encrypt:lti:$cdom:$cnum:$context:$escurl:$escmethod:$items",$chome);
+ }
+ unless (($rep=~/^(refused|rejected|error)/) || ($rep eq 'con_lost') ||
+ ($rep eq 'unknown_cmd')) {
+ $itemid = $rep;
+ }
+ }
+ return $itemid;
+}
+
+sub domainlti_itemid {
+ my ($cdom,$url,$method,$params,$context) = @_;
+ my ($primary_id,$itemid);
+ $primary_id = &domain($cdom,'primary');
+ return if ($primary_id eq '');
+ if (ref($params) eq 'HASH') {
+ my $items = &freeze_escape($params);
+ my $rep;
+ if (grep { $_ eq $primary_id } current_machine_ids()) {
+ $rep = LONCAPA::Lond::domlti_itemid($cdom,$context,$url,$method,$params,$perlvar{'lonVersion'});
+ } else {
+ my $cnum = '';
+ my $escurl = &escape($url);
+ my $escmethod = &escape($method);
+ my $items = &freeze_escape($params);
+ $rep = &reply("encrypt:lti:$cdom:$cnum:$context:$escurl:$escmethod:$items",$primary_id);
+ }
+ unless (($rep=~/^(refused|rejected|error)/) || ($rep eq 'con_lost') ||
+ ($rep eq 'unknown_cmd')) {
+ $itemid = $rep;
+ }
+ }
+ return $itemid;
+}
+
sub get_numsuppfiles {
my ($cnum,$cdom,$ignorecache)=@_;
my $hashid=$cnum.':'.$cdom;
Index: loncom/lti/ltiauth.pm
diff -u loncom/lti/ltiauth.pm:1.29 loncom/lti/ltiauth.pm:1.30
--- loncom/lti/ltiauth.pm:1.29 Tue Feb 1 19:54:36 2022
+++ loncom/lti/ltiauth.pm Tue Feb 1 23:13:20 2022
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Basic LTI Authentication Module
#
-# $Id: ltiauth.pm,v 1.29 2022/02/01 19:54:36 raeburn Exp $
+# $Id: ltiauth.pm,v 1.30 2022/02/01 23:13:20 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -31,7 +31,6 @@
use strict;
use LONCAPA qw(:DEFAULT :match);
use Apache::Constants qw(:common :http);
-use Net::OAuth;
use Apache::lonlocal;
use Apache::lonnet;
use Apache::loncommon;
@@ -127,20 +126,6 @@
my $intdom = &Apache::lonnet::internet_dom($primary_id);
if (($intdom ne '') && (grep(/^\Q$intdom\E$/, at intdoms))) {
#
-# Retrieve information for LTI link protectors in course
-# where url was /adm/launch/tiny/$cdom/$uniqueid
-#
- my (%crslti,%crslti_by_key,$itemid,$ltitype);
- %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
- if (keys(%crslti)) {
- foreach my $id (keys(%crslti)) {
- if (ref($crslti{$id}) eq 'HASH') {
- my $key = $crslti{$id}{'key'};
- push(@{$crslti_by_key{$key}},$id);
- }
- }
- }
-#
# Verify the signed request using the secret for LTI link
# protectors for which the key in the POSTed data matches
# keys in the course configuration.
@@ -153,8 +138,14 @@
# Determine if nonce in POSTed data has expired.
# If unexpired, confirm it has not already been used.
#
- if (keys(%crslti)) {
- $itemid = &get_lti_itemid($requri,$hostname,$params,\%crslti,\%crslti_by_key);
+# Retrieve information for LTI link protectors in course
+# where url was /adm/launch/tiny/$cdom/$uniqueid
+#
+
+ my ($itemid,$ltitype,%crslti);
+ $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';
@@ -164,30 +155,17 @@
return OK;
}
} else {
- my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
- unless (keys(%lti) > 0) {
- &invalid_request($r,4);
- return OK;
- }
- my (%domlti_by_key,%domlti);
- foreach my $id (keys(%lti)) {
- if (ref($lti{$id}) eq 'HASH') {
- my $key = $lti{$id}{'key'};
- if (!$lti{$itemid}{'requser'}) {
- push(@{$domlti_by_key{$key}},$id);
- $domlti{$id} = $lti{$id};
- }
- }
- }
- if (keys(%domlti)) {
- $itemid = &get_lti_itemid($requri,$hostname,$params,\%domlti,\%domlti_by_key);
+ my %lti;
+ $itemid = &get_lti_itemid($requri,$hostname,$params,$cdom,'','deeplink');
+ if ($itemid) {
+ %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
}
- if (($itemid) && (ref($domlti{$itemid}) eq 'HASH')) {
+ if (($itemid) && (ref($lti{$itemid}) eq 'HASH')) {
$ltitype = 'd';
unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
- $domlti{$itemid}{'lifetime'},$cdom,
+ $lti{$itemid}{'lifetime'},$cdom,
$r->dir_config('lonLTIDir'))) {
- &invalid_request($r,5);
+ &invalid_request($r,4);
return OK;
}
}
@@ -202,22 +180,22 @@
$r->internal_redirect($tail.'?ltoken='.$ltoken);
$r->set_handlers('PerlHandler'=> undef);
} else {
- &invalid_request($r,6);
+ &invalid_request($r,5);
}
} else {
- &invalid_request($r,7);
+ &invalid_request($r,6);
}
} else {
- &invalid_request($r,8);
+ &invalid_request($r,7);
}
} else {
- &invalid_request($r,9);
+ &invalid_request($r,8);
}
} else {
- &invalid_request($r,10);
+ &invalid_request($r,9);
}
} else {
- &invalid_request($r,11);
+ &invalid_request($r,10);
}
return OK;
}
@@ -318,7 +296,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,12);
+ &invalid_request($r,11);
return OK;
}
if ($rest eq '') {
@@ -337,13 +315,13 @@
} elsif ($tail =~ m{^/($match_domain)/($match_courseid)$}) {
($urlcdom,$urlcnum) = ($1,$2);
if (($cdom ne '') && ($cdom ne $urlcdom)) {
- &invalid_request($r,13);
+ &invalid_request($r,12);
return OK;
}
} elsif ($tail =~ m{^/tiny/($match_domain)/(\w+)$}) {
($urlcdom,$urlcnum) = &course_from_tinyurl($tail);
if (($urlcdom eq '') || ($urlcnum eq '')) {
- &invalid_request($r,14);
+ &invalid_request($r,13);
return OK;
}
}
@@ -369,33 +347,19 @@
#
# Retrieve information for LTI Consumers in course's domain
-# and populate hash -- %lti_by_key -- for which keys
-# are those defined in domain configuration for LTI.
-#
-
- my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
- unless (keys(%lti) > 0) {
- &invalid_request($r,15);
- return OK;
- }
- my %lti_by_key;
- if (keys(%lti)) {
- foreach my $id (keys(%lti)) {
- if (ref($lti{$id}) eq 'HASH') {
- my $key = $lti{$id}{'key'};
- push(@{$lti_by_key{$key}},$id);
- }
- }
- }
-
+# defined in domain configuration for LTI.
#
# Verify the signed request using the secret for those
-# Consumers for which the key in the POSTed data matches
+# Consumers for which the key in the POSTed data matches
# keys in the course configuration or the domain configuration
# for LTI.
#
- my $itemid = &get_lti_itemid($requri,$hostname,$params,\%lti,\%lti_by_key);
+ my %lti;
+ my $itemid = &get_lti_itemid($requri,$hostname,$params,$cdom);
+ if ($itemid) {
+ %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
+ }
#
# Request is invalid if the signed request could not be verified
@@ -403,7 +367,7 @@
# configuration in LON-CAPA for that LTI Consumer.
#
unless (($itemid) && (ref($lti{$itemid}) eq 'HASH')) {
- &invalid_request($r,16);
+ &invalid_request($r,14);
return OK;
}
@@ -413,7 +377,7 @@
#
unless (&LONCAPA::ltiutils::check_nonce($params->{'oauth_nonce'},$params->{'oauth_timestamp'},
$lti{$itemid}{'lifetime'},$cdom,$r->dir_config('lonLTIDir'))) {
- &invalid_request($r,17);
+ &invalid_request($r,15);
return OK;
}
@@ -434,10 +398,10 @@
$r->internal_redirect($tail.'?ltoken='.$ltoken);
$r->set_handlers('PerlHandler'=> undef);
} else {
- &invalid_request($r,18);
+ &invalid_request($r,16);
}
} else {
- &invalid_request($r,19);
+ &invalid_request($r,17);
}
return OK;
}
@@ -499,7 +463,7 @@
my $storedcnum = $1;
my $crshome = &Apache::lonnet::homeserver($storedcnum,$cdom);
if ($crshome =~ /(con_lost|no_host|no_such_host)/) {
- &invalid_request($r,20);
+ &invalid_request($r,18);
return OK;
} else {
$posscnum = $storedcnum;
@@ -511,7 +475,7 @@
if ($urlcnum ne '') {
if ($posscnum ne '') {
if ($posscnum ne $urlcnum) {
- &invalid_request($r,21);
+ &invalid_request($r,19);
return OK;
} else {
$cnum = $posscnum;
@@ -519,7 +483,7 @@
} else {
my $crshome = &Apache::lonnet::homeserver($urlcnum,$cdom);
if ($crshome =~ /(con_lost|no_host|no_such_host)/) {
- &invalid_request($r,22);
+ &invalid_request($r,20);
return OK;
} else {
$cnum = $urlcnum;
@@ -584,7 +548,7 @@
$domdesc,\%data,\%alerts,\%rulematch,
\%inst_results,\%curr_rules,%got_rules);
if ($result eq 'notallowed') {
- &invalid_request($r,23);
+ &invalid_request($r,21);
} elsif ($result eq 'ok') {
if (($ltiroles[0] eq 'Instructor') && ($lcroles[0] eq 'cc') && ($lti{$itemid}{'mapcrs'}) &&
($lti{$itemid}{'makecrs'})) {
@@ -593,16 +557,16 @@
}
}
} else {
- &invalid_request($r,24);
+ &invalid_request($r,22);
return OK;
}
} else {
- &invalid_request($r,25);
+ &invalid_request($r,23);
return OK;
}
}
} else {
- &invalid_request($r,26);
+ &invalid_request($r,24);
return OK;
}
@@ -625,10 +589,10 @@
$symb,$cdom,$cnum,$params,\@ltiroles,$lti{$itemid},\@lcroles,
$reqcrs,$sourcecrs);
} else {
- &invalid_request($r,27);
+ &invalid_request($r,25);
}
} else {
- &invalid_request($r,28);
+ &invalid_request($r,26);
}
} else {
<i_session($r,$itemid,$uname,$udom,$uhome,$lonhost,undef,$mapurl,$tail,
@@ -719,7 +683,7 @@
}
}
if ($reqrole eq '') {
- &invalid_request($r,29);
+ &invalid_request($r,27);
return OK;
} else {
unless (%crsenv) {
@@ -729,10 +693,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,30);
+ &invalid_request($r,28);
return OK;
} elsif ($default_enrollment_start_date && $default_enrollment_start_date >$now) {
- &invalid_request($r,31);
+ &invalid_request($r,29);
return OK;
} else {
$selfenrollrole = $reqrole.'./'.$cdom.'/'.$cnum;
@@ -794,38 +758,19 @@
}
sub get_lti_itemid {
- my ($requri,$hostname,$params,$lti,$lti_by_key) = @_;
- return unless ((ref($params) eq 'HASH') && (ref($lti) eq 'HASH') && (ref($lti_by_key) eq 'HASH'));
-
- if (exists($params->{'oauth_callback'})) {
- $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
- } else {
- $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
- }
-
+ my ($requri,$hostname,$params,$cdom,$cnum,$context) = @_;
+ return unless ((ref($params) eq 'HASH');
my $protocol = 'http';
if ($ENV{'SERVER_PORT'} == 443) {
$protocol = 'https';
}
-
- my ($itemid,$consumer_key,$secret);
- my $consumer_key = $params->{'oauth_consumer_key'};
- if (ref($lti_by_key->{$consumer_key}) eq 'ARRAY') {
- foreach my $id (@{$lti_by_key->{$consumer_key}}) {
- if (ref($lti->{$id}) eq 'HASH') {
- $secret = $lti->{$id}{'secret'};
- my $request = Net::OAuth->request('request token')->from_hash($params,
- request_url => $protocol.'://'.$hostname.$requri,
- request_method => $env{'request.method'},
- consumer_secret => $secret,);
- if ($request->verify()) {
- $itemid = $id;
- last;
- }
- }
- }
+ my $url = $protocol.'://'.$hostname.$requri;
+ my $method = $env{'request.method'};
+ if ($cnum ne '') {
+ return &Apache::lonnet::courselti_itemid($cnum,$cdom,$url,$method,$params,$context);
+ } else {
+ return &Apache::lonnet::domainlti_itemid($cdom,$url,$method,$params,$context);
}
- return $itemid;
}
sub lti_enroll {
Index: loncom/Lond.pm
diff -u loncom/Lond.pm:1.17 loncom/Lond.pm:1.18
--- loncom/Lond.pm:1.17 Wed Jan 19 16:02:59 2022
+++ loncom/Lond.pm Tue Feb 1 23:13:21 2022
@@ -1,6 +1,6 @@
# The LearningOnline Network
#
-# $Id: Lond.pm,v 1.17 2022/01/19 16:02:59 raeburn Exp $
+# $Id: Lond.pm,v 1.18 2022/02/01 23:13:21 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -41,6 +41,7 @@
use Crypt::OpenSSL::X509;
use Crypt::X509::CRL;
use Crypt::PKCS10;
+use Net::OAuth;
sub dump_with_regexp {
my ( $tail, $clientversion ) = @_;
@@ -1043,6 +1044,159 @@
return $qresult;
}
+sub crslti_itemid {
+ my ($cdom,$cnum,$url,$method,$params,$loncaparev) = @_;
+ unless (ref($params) eq 'HASH') {
+ return;
+ }
+ if (($cdom eq '') || ($cnum eq '')) {
+ return;
+ }
+ my ($itemid,$consumer_key,$secret);
+
+ if (exists($params->{'oauth_callback'})) {
+ $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
+ } else {
+ $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
+ }
+
+ my $consumer_key = $params->{'oauth_consumer_key'};
+ return if ($consumer_key eq '');
+
+ my (%crslti,%crslti_by_key);
+ my $hashid=$cdom.'_'.$cnum;
+ my ($result,$cached)=&Apache::lonnet::is_cached_new('courseltienc',$hashid);
+ if (defined($cached)) {
+ if (ref($result) eq 'HASH') {
+ %crslti = %{$result};
+ }
+ } else {
+ my $reply = &dump_with_regexp(join(":",($cdom,$cnum,'nohist_ltienc','','')),$loncaparev);
+ %crslti = %{&Apache::lonnet::unserialize($reply)};
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('courseltienc',$hashid,\%crslti,$cachetime);
+ }
+
+ return if (!keys(%crslti));
+
+ foreach my $id (keys(%crslti)) {
+ if (ref($crslti{$id}) eq 'HASH') {
+ my $key = $crslti{$id}{'key'};
+ if (($key ne '') && ($crslti{$id}{'secret'} ne '')) {
+ push(@{$crslti_by_key{$key}},$id);
+ }
+ }
+ }
+
+ return if (!keys(%crslti_by_key));
+
+ if (ref($crslti_by_key{$consumer_key}) eq 'ARRAY') {
+ foreach my $id (@{$crslti_by_key{$consumer_key}}) {
+ my $secret = $crslti{$id}{'secret'};
+ my $request = Net::OAuth->request('request token')->from_hash($params,
+ request_url => $url,
+ request_method => $method,
+ consumer_secret => $secret,);
+ if ($request->verify()) {
+ $itemid = $id;
+ last;
+ }
+ }
+ }
+ return $itemid;
+}
+
+sub domlti_itemid {
+ my ($dom,$context,$url,$method,$params,$loncaparev) = @_;
+ unless (ref($params) eq 'HASH') {
+ return;
+ }
+ if ($dom eq '') {
+ return;
+ }
+ my ($itemid,$consumer_key,$secret);
+
+ if (exists($params->{'oauth_callback'})) {
+ $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
+ } else {
+ $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0;
+ }
+
+ my $consumer_key = $params->{'oauth_consumer_key'};
+ return if ($consumer_key eq '');
+
+ my %ltienc;
+ my ($encresult,$enccached)=&Apache::lonnet::is_cached_new('ltienc',$dom);
+ if (defined($enccached)) {
+ if (ref($encresult) eq 'HASH') {
+ %ltienc = %{$encresult};
+ }
+ } else {
+ my $reply = &get_dom("getdom:$dom:encconfig:lti");
+ my $ltiencref = &Apache::lonnet::thaw_unescape($reply);
+ if (ref($ltiencref) eq 'HASH') {
+ %ltienc = %{$ltiencref};
+ }
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('ltienc',$dom,\%ltienc,$cachetime);
+ }
+
+ return if (!keys(%ltienc));
+
+ my %lti;
+ if ($context eq 'deeplink') {
+ my ($result,$cached)=&Apache::lonnet::is_cached_new('lti',$dom);
+ if (defined($cached)) {
+ if (ref($result) eq 'HASH') {
+ %lti = %{$result};
+ }
+ } else {
+ my $reply = &get_dom("getdom:$dom:configuration:lti");
+ my $ltiref = &Apache::lonnet::thaw_unescape($reply);
+ if (ref($ltiref) eq 'HASH') {
+ %lti = %{$ltiref};
+ }
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('lti',$dom,\%lti,$cachetime);
+ }
+ }
+ return if (!keys(%lti));
+
+ my %lti_by_key;
+ foreach my $id (keys(%ltienc)) {
+ if (ref($ltienc{$id}) eq 'HASH') {
+ my $key = $ltienc{$id}{'key'};
+ if (($key ne '') && ($ltienc{$id}{'secret'} ne '')) {
+ if ($context eq 'deeplink') {
+ if (ref($lti{$id}) eq 'HASH') {
+ if (!$lti{$id}{'requser'}) {
+ push(@{$lti_by_key{$key}},$id);
+ }
+ }
+ } else {
+ push(@{$lti_by_key{$key}},$id);
+ }
+ }
+ }
+ }
+ return if (!keys(%lti_by_key));
+
+ if (ref($lti_by_key{$consumer_key}) eq 'ARRAY') {
+ foreach my $id (@{$lti_by_key{$consumer_key}}) {
+ my $secret = $ltienc{$id}{'secret'};
+ my $request = Net::OAuth->request('request token')->from_hash($params,
+ request_url => $url,
+ request_method => $method,
+ consumer_secret => $secret,);
+ if ($request->verify()) {
+ $itemid = $id;
+ last;
+ }
+ }
+ }
+ return $itemid;
+}
+
1;
__END__
Index: loncom/lond
diff -u loncom/lond:1.571 loncom/lond:1.572
--- loncom/lond:1.571 Tue Dec 21 13:57:47 2021
+++ loncom/lond Tue Feb 1 23:13:21 2022
@@ -2,7 +2,7 @@
# The LearningOnline Network
# lond "LON Daemon" Server (port "LOND" 5663)
#
-# $Id: lond,v 1.571 2021/12/21 13:57:47 raeburn Exp $
+# $Id: lond,v 1.572 2022/02/01 23:13:21 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -65,7 +65,7 @@
my $status='';
my $lastlog='';
-my $VERSION='$Revision: 1.571 $'; #' stupid emacs
+my $VERSION='$Revision: 1.572 $'; #' stupid emacs
my $remoteVERSION;
my $currenthostid="default";
my $currentdomainid;
@@ -266,6 +266,7 @@
ls => {remote => 1, enroll => 1, content => 1,},
ls2 => {remote => 1, enroll => 1, content => 1,},
ls3 => {remote => 1, enroll => 1, content => 1,},
+ lti => {institutiononly => 1},
makeuser => {remote => 1, enroll => 1, domroles => 1,},
mkdiruserfile => {remote => 1, enroll => 1,},
newput => {remote => 1, enroll => 1, reqcrs => 1, domroles => 1,},
@@ -5156,6 +5157,25 @@
}
®ister_handler("getdom", \&get_domain_handler, 0, 1, 0);
+#
+# Encrypted get from the namespace database file at the domain level.
+# This function retrieves a keyed item from a specific named database in the
+# domain directory.
+#
+# Parameters:
+# $cmd - Command request keyword (egetdom).
+# $tail - Tail of the command. This is a colon separated list
+# consisting of the domain and the 'namespace'
+# which selects the gdbm file to do the lookup in,
+# & separated list of keys to lookup. Note that
+# the values are returned as an & separated list too.
+# $client - File descriptor open on the client.
+# Returns:
+# 1 - Continue processing.
+# 0 - Exit.
+# Side effects:
+# reply is encrypted before being written to $client.
+#
sub encrypted_get_domain_handler {
my ($cmd, $tail, $client) = @_;
@@ -5185,6 +5205,71 @@
®ister_handler("egetdom", \&encrypted_get_domain_handler, 1, 1, 0);
#
+# Encrypted get from the namespace database file at the domain level.
+# This function retrieves a keyed item from a specific named database in the
+# domain directory.
+#
+# Parameters:
+# $cmd - Command request keyword (lti).
+# $tail - Tail of the command. This is a colon-separated list
+# consisting of the domain, coursenum, if for LTI-
+# enabled deep-linking to course content using
+# link protection configured within a course,
+# context (=deeplink) if for LTI-enabled deep-linking
+# to course content using LTI Provider settings
+# configured within a course's domain, the (escaped)
+# launch URL, the (escaped) method (typically POST),
+# and a frozen hash of the LTI launch parameters
+# from the LTI payload.
+# $client - File descriptor open on the client.
+# Returns:
+# 1 - Continue processing.
+# 0 - Exit.
+# Side effects:
+# The reply will contain an LTI itemID, if the signed LTI payload
+# could be verified using the consumer key and the shared secret
+# available for that key (for the itemID) for either the course or domain,
+# depending on values for cnum and context. The reply is encrypted before
+# being written to $client.
+#
+sub lti_handler {
+ my ($cmd, $tail, $client) = @_;
+
+ my $userinput = "$cmd:$tail";
+
+ my ($cdom,$cnum,$context,$escurl,$escmethod,$items) = split(/:/,$tail);
+ my $url = &unescape($escurl);
+ my $method = &unescape($escmethod);
+ my $params = &Apache::lonnet::thaw_unescape($items);
+ my $res;
+ if ($cnum ne '') {
+ $res = &LONCAPA::Lond::crslti_itemid($cdom,$cnum,$url,$method,$params,$perlvar{'lonVersion'});
+ } else {
+ $res = &LONCAPA::Lond::domlti_itemid($cdom,$context,$url,$method,$params,$perlvar{'lonVersion'});
+ }
+ if ($res =~ /^error:/) {
+ &Failure($client, \$res, $userinput);
+ } else {
+ if ($cipher) {
+ my $cmdlength=length($res);
+ $res.=" ";
+ my $encres='';
+ for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
+ $encres.= unpack("H16",
+ $cipher->encrypt(substr($res,
+ $encidx,
+ 8)));
+ }
+ &Reply( $client,"enc:$cmdlength:$encres\n",$userinput);
+ } else {
+ &Failure( $client, "error:no_key\n",$userinput);
+ }
+ }
+ return 1;
+}
+®ister_handler("lti", \<i_handler, 1, 1, 0);
+
+#
# Puts an id to a domains id database.
#
# Parameters:
More information about the LON-CAPA-cvs
mailing list