--- loncom/interface/domainprefs.pm 2023/03/19 16:05:48 1.421
+++ loncom/interface/domainprefs.pm 2023/06/01 18:09:59 1.424
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set domain-wide configuration settings
#
-# $Id: domainprefs.pm,v 1.421 2023/03/19 16:05:48 raeburn Exp $
+# $Id: domainprefs.pm,v 1.424 2023/06/01 18:09:59 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -178,6 +178,7 @@ use DateTime::TimeZone;
use DateTime::Locale;
use Time::HiRes qw( sleep );
use Net::CIDR;
+use Crypt::CBC;
my $registered_cleanup;
my $modified_urls;
@@ -225,23 +226,46 @@ sub handler {
'privacy','passwords','proctoring','wafproxy','ipaccess'],$dom);
my %encconfig =
&Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1);
+ my ($checked_is_home,$is_home);
if (ref($domconfig{'ltitools'}) eq 'HASH') {
if (ref($encconfig{'ltitools'}) eq 'HASH') {
+ my $home = &Apache::lonnet::domain($dom,'primary');
+ unless (($home eq 'no_host') || ($home eq '')) {
+ my @ids=&Apache::lonnet::current_machine_ids();
+ if (grep(/^\Q$home\E$/,@ids)) {
+ $is_home = 1;
+ }
+ }
+ $checked_is_home = 1;
foreach my $id (keys(%{$domconfig{'ltitools'}})) {
if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') &&
(ref($encconfig{'ltitools'}{$id}) eq 'HASH')) {
$domconfig{'ltitools'}{$id}{'key'} = $encconfig{'ltitools'}{$id}{'key'};
+ if (($is_home) && ($phase eq 'process')) {
+ $domconfig{'ltitools'}{$id}{'secret'} = $encconfig{'ltitools'}{$id}{'secret'};
+ }
}
}
}
}
if (ref($domconfig{'lti'}) eq 'HASH') {
if (ref($encconfig{'lti'}) eq 'HASH') {
+ unless ($checked_is_home) {
+ my $home = &Apache::lonnet::domain($dom,'primary');
+ unless (($home eq 'no_host') || ($home eq '')) {
+ my @ids=&Apache::lonnet::current_machine_ids();
+ if (grep(/^\Q$home\E$/,@ids)) {
+ $is_home = 1;
+ }
+ }
+ $checked_is_home = 1;
+ }
foreach my $id (keys(%{$domconfig{'lti'}})) {
if ((ref($domconfig{'lti'}{$id}) eq 'HASH') &&
(ref($encconfig{'lti'}{$id}) eq 'HASH')) {
- foreach my $item ('key','secret') {
- $domconfig{'lti'}{$id}{$item} = $encconfig{'lti'}{$id}{$item};
+ $domconfig{'lti'}{$id}{'key'} = $encconfig{'lti'}{$id}{'key'};
+ if (($is_home) && ($phase eq 'process')) {
+ $domconfig{'lti'}{$id}{'secret'} = $encconfig{'lti'}{$id}{'secret'};
}
}
}
@@ -6176,6 +6200,9 @@ sub print_lti {
} elsif ($position eq 'lower') {
$datatable .= &Apache::courseprefs::print_linkprotection($dom,'',$settings,$rowtotal,'','','domain');
} else {
+ my ($switchserver,$switchmessage);
+ $switchserver = &check_switchserver($dom);
+ $switchmessage = &mt("submit from domain's primary library server: [_1].",$switchserver);
my $maxnum = 0;
my %ordered;
if (ref($settings) eq 'HASH') {
@@ -6196,10 +6223,10 @@ sub print_lti {
for (my $i=0; $i<@items; $i++) {
$css_class = $itemcount%2?' class="LC_odd_row"':'';
my $item = $ordered{$items[$i]};
- my ($key,$secret,$lifetime,$consumer,$requser,$crsinc,$current);
+ my ($key,$secret,$usable,$lifetime,$consumer,$requser,$crsinc,$current);
if (ref($settings->{$item}) eq 'HASH') {
$key = $settings->{$item}->{'key'};
- $secret = $settings->{$item}->{'secret'};
+ $usable = $settings->{$item}->{'usable'};
$lifetime = $settings->{$item}->{'lifetime'};
$consumer = $settings->{$item}->{'consumer'};
$requser = $settings->{$item}->{'requser'};
@@ -6247,8 +6274,56 @@ sub print_lti {
' '.
(' 'x2).
''.$lt{'lifetime'}.':'.
- (' 'x2).
+ 'value="'.$lifetime.'" size="3" />
';
+ if ($key ne '') {
+ $datatable .= ''.$lt{'key'};
+ if ($switchserver) {
+ $datatable .= ': ['.&mt('[_1] to view/edit',$switchserver).']';
+ } else {
+ $datatable .= ':';
+ }
+ $datatable .= ' '.(' 'x2);
+ } elsif (!$switchserver) {
+ $datatable .= ''.$lt{'key'}.':'.
+ ''.
+ ' '.(' 'x2);
+ }
+ if ($switchserver) {
+ if ($usable ne '') {
+ $datatable .= '
'.
+ $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).''.
+ ''.&mt('Change secret?').
+ ''.
+ (' 'x2).
+ ''.(' 'x2).
+ ''.
+ ' - '.$switchmessage.''.
+ '';
+ } elsif ($key eq '') {
+ $datatable .= ''.&mt('Key and Secret are required').' - '.$switchmessage.''."\n";
+ } else {
+ $datatable .= ''.&mt('Secret required').' - '.$switchmessage.''."\n";
+ }
+ } else {
+ if ($usable ne '') {
+ $datatable .= ''.
+ $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).''.
+ ''.&mt('Change?').
+ ''.
+ (' 'x2).
+ ' '.
+ ''.&mt('New Secret').':'.
+ ''.
+ '';
+ } else {
+ $datatable .=
+ ''.$lt{'secret'}.':'.
+ ''.
+ '';
+ }
+ }
+ $datatable .= '
'.
''.$lt{'requser'}.':'.
' '."\n".
''."\n".
@@ -6257,12 +6332,6 @@ sub print_lti {
' '."\n".
''."\n".
(' 'x4).
- ''.$lt{'key'}.
- ': '.
- (' 'x2).
- ''.$lt{'secret'}.':'.
- ''.
- ''.
''.
''.<i_options($i,$current,$itemcount,%lt).'';
$itemcount ++;
@@ -6291,8 +6360,16 @@ sub print_lti {
''.$lt{'version'}.': '."\n".
(' 'x2).
- ''.$lt{'lifetime'}.': '."\n".
- (' 'x2).
+ ''.$lt{'lifetime'}.':
'."\n";
+ if ($switchserver) {
+ $datatable .= ''.&mt('Key and Secret are required').' - '.$switchmessage.''."\n";
+ } else {
+ $datatable .= ''.$lt{'key'}.': '."\n".
+ (' 'x2).
+ ''.$lt{'secret'}.':'.
+ ' '."\n";
+ }
+ $datatable .= '
'.
''.$lt{'requser'}.':'.
' '."\n".
''."\n".
@@ -6300,11 +6377,6 @@ sub print_lti {
''.$lt{'crsinc'}.':'.
' '."\n".
''."\n".
- (' 'x4).
- ''.$lt{'key'}.': '."\n".
- (' 'x2).
- ''.$lt{'secret'}.':'.
- ' '."\n".
''.<i_options('add',undef,$itemcount,%lt).
''."\n".
''."\n";
@@ -6746,12 +6818,16 @@ sub print_coursedefaults {
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',
+ domexttool => 'External Tools defined in the domain may be used in courses/communities (by type)',
+ exttool => 'External Tools can be defined and configured in courses/communities (by type)',
);
my %staticdefaults = (
anonsurvey_threshold => 10,
uploadquota => 500,
postsubmit => 60,
mysqltables => 172800,
+ domexttool => 1,
+ exttool => 0,
);
if ($position eq 'top') {
%defaultchecked = (
@@ -6869,11 +6945,33 @@ sub print_coursedefaults {
my $currusecredits = 0;
my $postsubmitclient = 1;
my $ltiauth = 0;
+ my %domexttool;
+ my %exttool;
my @types = ('official','unofficial','community','textbook','placement');
if (ref($settings) eq 'HASH') {
if ($settings->{'ltiauth'}) {
$ltiauth = 1;
}
+ if (ref($settings->{'domexttool'}) eq 'HASH') {
+ foreach my $type (@types) {
+ if ($settings->{'domexttool'}->{$type}) {
+ $domexttool{$type} = ' checked="checked"';
+ }
+ }
+ } else {
+ foreach my $type (@types) {
+ if ($staticdefaults{'domexttool'}) {
+ $domexttool{$type} = ' checked="checked"';
+ }
+ }
+ }
+ if (ref($settings->{'exttool'}) eq 'HASH') {
+ foreach my $type (@types) {
+ if ($settings->{'exttool'}->{$type}) {
+ $exttool{$type} = ' checked="checked"';
+ }
+ }
+ }
$currdefresponder = $settings->{'anonsurvey_threshold'};
if (ref($settings->{'uploadquota'}) eq 'HASH') {
foreach my $type (keys(%{$settings->{'uploadquota'}})) {
@@ -6925,6 +7023,9 @@ sub print_coursedefaults {
} else {
foreach my $type (@types) {
$deftimeout{$type} = $staticdefaults{'postsubmit'};
+ if ($staticdefaults{'domexttool'}) {
+ $domexttool{$type} = ' checked="checked"';
+ }
}
}
if (!$currdefresponder) {
@@ -7028,7 +7129,35 @@ sub print_coursedefaults {
&radiobutton_prefs($current,\@toggles,\%defaultchecked,
\%choices,$itemcount,undef,undef,'left');
$datatable .= $table;
+ $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
+ $datatable .= ''.
+ $choices{'domexttool'}.
+ ' | '.
+ ''.
+ ' |
'."\n";
$itemcount ++;
+ $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
+ $datatable .= ''.
+ $choices{'exttool'}.
+ ' | '.
+ ''.
+ ' |
'."\n";
}
$$rowtotal += $itemcount;
return $datatable;
@@ -13355,10 +13484,14 @@ sub subscribed_hosts {
sub check_switchserver {
my ($dom,$confname) = @_;
- my ($allowed,$switchserver);
- my $home = &Apache::lonnet::homeserver($confname,$dom);
- if ($home eq 'no_host') {
+ my ($allowed,$switchserver,$home);
+ if ($confname eq '') {
$home = &Apache::lonnet::domain($dom,'primary');
+ } else {
+ $home = &Apache::lonnet::homeserver($confname,$dom);
+ if ($home eq 'no_host') {
+ $home = &Apache::lonnet::domain($dom,'primary');
+ }
}
my @ids=&Apache::lonnet::current_machine_ids();
foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } }
@@ -14188,15 +14321,22 @@ sub modify_ltitools {
$action => { %newtoolsenc }
);
&Apache::lonnet::put_dom('encconfig',\%toolsenchash,$dom,undef,1);
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('ltitoolsenc',$dom,\%newtoolsenc,$cachetime);
&store_security($dom,'ltitools',\%secchanges,\%newkeyset,\%keystore,$lastactref);
}
$resulttext = &mt('Changes made:').'';
if (keys(%secchanges) > 0) {
- $resulttext .= <i_security_results('ltitools',\%secchanges,\%newtoolsec,\%newkeyset,\%keystore);
+ $resulttext .= <i_security_results($dom,'ltitools',\%secchanges,\%newtoolsec,\%newkeyset,\%keystore);
}
if (keys(%ltitoolschg) > 0) {
$resulttext .= $ltitoolsoutput;
}
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('ltitools',$dom,\%newltitools,$cachetime);
+ if (ref($lastactref) eq 'HASH') {
+ $lastactref->{'ltitools'} = 1;
+ }
} else {
$errors .= '- '.&mt('Failed to save changes').'
';
}
@@ -14277,7 +14417,6 @@ sub fetch_secrets {
foreach my $hostid (keys(%servers)) {
if (($hostid ne '') && (grep(/^\Q$hostid\E$/,@ids))) {
- my $newkey;
my $keyitem = 'form.'.$context.'_privkey_'.$hostid;
if (exists($env{$keyitem})) {
$env{$keyitem} =~ s/(`)/'/g;
@@ -14301,7 +14440,7 @@ sub fetch_secrets {
}
sub store_security {
- my ($dom,$context,$secchanges,$newkeyset,$keystore,$lastactref) = @_;
+ my ($dom,$context,$secchanges,$newkeyset,$keystore) = @_;
return unless ((ref($secchanges) eq 'HASH') && (ref($newkeyset) eq 'HASH') &&
(ref($keystore) eq 'HASH'));
if (keys(%{$secchanges})) {
@@ -14316,19 +14455,17 @@ sub store_security {
$dom,$hostid);
}
}
- if (ref($lastactref) eq 'HASH') {
- if (($secchanges->{'encrypt'}) || ($secchanges->{'private'})) {
- $lastactref->{'domdefaults'} = 1;
- }
- }
}
}
sub lti_security_results {
- my ($context,$secchanges,$newsec,$newkeyset,$keystore) = @_;
+ my ($dom,$context,$secchanges,$newsec,$newkeyset,$keystore) = @_;
my $output;
+ my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
+ my $needs_update;
foreach my $item (keys(%{$secchanges})) {
if ($item eq 'encrypt') {
+ $needs_update = 1;
my %encrypted;
if ($context eq 'lti') {
%encrypted = (
@@ -14360,12 +14497,29 @@ sub lti_security_results {
}
my @types= ('crs','dom');
if ($context eq 'lti') {
+ foreach my $type (@types) {
+ undef($domdefaults{'linkprotenc_'.$type});
+ }
push(@types,'consumers');
+ undef($domdefaults{'ltienc_consumers'});
+ } elsif ($context eq 'ltitools') {
+ foreach my $type (@types) {
+ undef($domdefaults{'toolenc_'.$type});
+ }
}
foreach my $type (@types) {
my $shown = $encrypted{$type}{'off'};
if (ref($newsec->{$item}) eq 'HASH') {
if ($newsec->{$item}{$type}) {
+ if ($context eq 'lti') {
+ if ($type eq 'consumers') {
+ $domdefaults{'ltienc_consumers'} = 1;
+ } else {
+ $domdefaults{'linkprotenc_'.$type} = 1;
+ }
+ } elsif ($context eq 'ltitools') {
+ $domdefaults{'toolenc_'.$type} = 1;
+ }
$shown = $encrypted{$type}{'on'};
}
}
@@ -14409,10 +14563,27 @@ sub lti_security_results {
$output .= '- '.&mt('[_1] set to none',$titles{'chars'}).'
';
}
} elsif ($item eq 'private') {
+ $needs_update = 1;
+ if ($context eq 'lti') {
+ undef($domdefaults{'ltiprivhosts'});
+ } elsif ($context eq 'ltitools') {
+ undef($domdefaults{'toolprivhosts'});
+ }
if (keys(%{$newkeyset})) {
+ my @privhosts;
foreach my $hostid (sort(keys(%{$newkeyset}))) {
if ($keystore->{$hostid} eq 'ok') {
$output .= '- '.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'
';
+ unless (grep(/^\Q$hostid\E$/,@privhosts)) {
+ push(@privhosts,$hostid);
+ }
+ }
+ }
+ if (@privhosts) {
+ if ($context eq 'lti') {
+ $domdefaults{'ltiprivhosts'} = \@privhosts;
+ } elsif ($context eq 'ltitools') {
+ $domdefaults{'toolprivhosts'} = \@privhosts;
}
}
}
@@ -14420,6 +14591,10 @@ sub lti_security_results {
next;
}
}
+ if ($needs_update) {
+ my $cachetime = 24*60*60;
+ &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
+ }
return $output;
}
@@ -14959,7 +15134,7 @@ sub process_proctoring_image {
sub modify_lti {
my ($r,$dom,$action,$lastactref,%domconfig) = @_;
my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
- my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext);
+ my ($newid,@allpos,%changes,%confhash,%ltienc,$errors,$resulttext);
my (%posslti,%posslticrs,%posscrstype);
my @courseroles = ('cc','in','ta','ep','st');
my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator);
@@ -15073,29 +15248,43 @@ sub modify_lti {
}
}
}
+ my (%keystore,$secstored);
+ if ($is_home) {
+ &store_security($dom,'lti',\%secchanges,\%newkeyset,\%keystore);
+ }
+
+ my ($cipher,$privnum);
+ if ((@items > 0) && ($is_home)) {
+ ($cipher,$privnum) = &get_priv_creds($dom,$home,$secchanges{'encrypt'},
+ $newltisec{'encrypt'},$keystore{$home});
+ }
foreach my $idx (@items) {
my $itemid = $itemids{$idx};
next unless ($itemid);
+ my %currlti;
+ unless ($idx eq 'add') {
+ if (ref($domconfig{$action}) eq 'HASH') {
+ if (ref($domconfig{$action}{$itemid}) eq 'HASH') {
+ %currlti = %{$domconfig{$action}{$itemid}};
+ }
+ }
+ }
my $position = $env{'form.lti_pos_'.$itemid};
$position =~ s/\D+//g;
if ($position ne '') {
$allpos[$position] = $itemid;
}
- foreach my $item ('consumer','key','secret','lifetime','requser','crsinc') {
+ foreach my $item ('consumer','lifetime','requser','crsinc') {
my $formitem = 'form.lti_'.$item.'_'.$idx;
$env{$formitem} =~ s/(`)/'/g;
if ($item eq 'lifetime') {
$env{$formitem} =~ s/[^\d.]//g;
}
if ($env{$formitem} ne '') {
- if (($item eq 'key') || ($item eq 'secret')) {
- $encconfig{$itemid}{$item} = $env{$formitem};
- } else {
- $confhash{$itemid}{$item} = $env{$formitem};
- unless (($idx eq 'add') || ($changes{$itemid})) {
- if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) {
- $changes{$itemid} = 1;
- }
+ $confhash{$itemid}{$item} = $env{$formitem};
+ unless (($idx eq 'add') || ($changes{$itemid})) {
+ if ($currlti{$item} ne $confhash{$itemid}{$item}) {
+ $changes{$itemid} = 1;
}
}
}
@@ -15236,27 +15425,27 @@ sub modify_lti {
unless (($idx eq 'add') || ($changes{$itemid})) {
if ($confhash{$itemid}{'crsinc'}) {
foreach my $field ('mapcrs','storecrs','makecrs','section','passback','roster') {
- if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) {
+ if ($currlti{$field} ne $confhash{$itemid}{$field}) {
$changes{$itemid} = 1;
}
}
unless ($changes{$itemid}) {
- if ($domconfig{$action}{$itemid}{'passback'} eq $confhash{$itemid}{'passback'}) {
- if ($domconfig{$action}{$itemid}{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) {
+ if ($currlti{'passback'} eq $confhash{$itemid}{'passback'}) {
+ if ($currlti{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) {
$changes{$itemid} = 1;
}
}
}
foreach my $field ('mapcrstype','selfenroll') {
unless ($changes{$itemid}) {
- if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') {
+ if (ref($currlti{$field}) eq 'ARRAY') {
if (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
- my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field},
+ my @diffs = &Apache::loncommon::compare_arrays($currlti{$field},
$confhash{$itemid}{$field});
if (@diffs) {
$changes{$itemid} = 1;
}
- } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) {
+ } elsif (@{$currlti{$field}} > 0) {
$changes{$itemid} = 1;
}
} elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
@@ -15267,10 +15456,10 @@ sub modify_lti {
}
}
unless ($changes{$itemid}) {
- if (ref($domconfig{$action}{$itemid}{'maproles'}) eq 'HASH') {
+ if (ref($currlti{'maproles'}) eq 'HASH') {
if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {
- foreach my $ltirole (keys(%{$domconfig{$action}{$itemid}{'maproles'}})) {
- if ($domconfig{$action}{$itemid}{'maproles'}{$ltirole} ne
+ foreach my $ltirole (keys(%{$currlti{'maproles'}})) {
+ if ($currlti{'maproles'}{$ltirole} ne
$confhash{$itemid}{'maproles'}{$ltirole}) {
$changes{$itemid} = 1;
last;
@@ -15279,13 +15468,13 @@ sub modify_lti {
unless ($changes{$itemid}) {
foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) {
if ($confhash{$itemid}{'maproles'}{$ltirole} ne
- $domconfig{$action}{$itemid}{'maproles'}{$ltirole}) {
+ $currlti{'maproles'}{$ltirole}) {
$changes{$itemid} = 1;
last;
}
}
}
- } elsif (keys(%{$domconfig{$action}{$itemid}{'maproles'}}) > 0) {
+ } elsif (keys(%{$currlti{'maproles'}}) > 0) {
$changes{$itemid} = 1;
}
} elsif (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {
@@ -15299,20 +15488,20 @@ sub modify_lti {
}
unless ($changes{$itemid}) {
foreach my $field ('mapuser','lcauth','lcauthparm','topmenu','inlinemenu','callback') {
- if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) {
+ if ($currlti{$field} ne $confhash{$itemid}{$field}) {
$changes{$itemid} = 1;
}
}
unless ($changes{$itemid}) {
foreach my $field ('makeuser','lcmenu') {
- if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') {
+ if (ref($currlti{$field}) eq 'ARRAY') {
if (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
- my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field},
+ my @diffs = &Apache::loncommon::compare_arrays($currlti{$field},
$confhash{$itemid}{$field});
if (@diffs) {
$changes{$itemid} = 1;
}
- } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) {
+ } elsif (@{$currlti{$field}} > 0) {
$changes{$itemid} = 1;
}
} elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
@@ -15325,6 +15514,71 @@ sub modify_lti {
}
}
}
+ if ($is_home) {
+ my $keyitem = 'form.lti_key_'.$idx;
+ $env{$keyitem} =~ s/(`)/'/g;
+ if ($env{$keyitem} ne '') {
+ $ltienc{$itemid}{'key'} = $env{$keyitem};
+ unless ($changes{$itemid}) {
+ if ($currlti{'key'} ne $env{$keyitem}) {
+ $changes{$itemid} = 1;
+ }
+ }
+ }
+ my $secretitem = 'form.lti_secret_'.$idx;
+ $env{$secretitem} =~ s/(`)/'/g;
+ if ($currlti{'usable'}) {
+ if ($env{'form.lti_changesecret_'.$idx}) {
+ if ($env{$secretitem} ne '') {
+ if ($privnum && $cipher) {
+ $ltienc{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem});
+ $confhash{$itemid}{'cipher'} = $privnum;
+ } else {
+ $ltienc{$itemid}{'secret'} = $env{$secretitem};
+ }
+ $changes{$itemid} = 1;
+ }
+ } else {
+ $ltienc{$itemid}{'secret'} = $currlti{'secret'};
+ $confhash{$itemid}{'cipher'} = $currlti{'cipher'};
+ }
+ if (ref($ltienc{$itemid}) eq 'HASH') {
+ if (($ltienc{$itemid}{'key'} ne '') && ($ltienc{$itemid}{'secret'} ne '')) {
+ $confhash{$itemid}{'usable'} = 1;
+ }
+ }
+ } elsif ($env{$secretitem} ne '') {
+ if ($privnum && $cipher) {
+ $ltienc{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem});
+ $confhash{$itemid}{'cipher'} = $privnum;
+ } else {
+ $ltienc{$itemid}{'secret'} = $env{$secretitem};
+ }
+ if (ref($ltienc{$itemid}) eq 'HASH') {
+ if (($ltienc{$itemid}{'key'} ne '') && ($ltienc{$itemid}{'key'} ne '')) {
+ $confhash{$itemid}{'usable'} = 1;
+ }
+ }
+ $changes{$itemid} = 1;
+ }
+ }
+ unless ($changes{$itemid}) {
+ foreach my $key (keys(%currlti)) {
+ if (ref($currlti{$key}) eq 'HASH') {
+ if (ref($confhash{$itemid}{$key}) eq 'HASH') {
+ foreach my $innerkey (keys(%{$currlti{$key}})) {
+ unless (exists($confhash{$itemid}{$key}{$innerkey})) {
+ $changes{$itemid} = 1;
+ last;
+ }
+ }
+ } elsif (keys(%{$currlti{$key}}) > 0) {
+ $changes{$itemid} = 1;
+ }
+ }
+ last if ($changes{$itemid});
+ }
+ }
}
if (@allpos > 0) {
my $idx = 0;
@@ -15342,12 +15596,21 @@ sub modify_lti {
}
}
}
+
+ if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) {
+ return &mt('No changes made.');
+ }
+
my %ltihash = (
$action => { %confhash }
);
- my %ltienchash = (
- $action => { %encconfig }
- );
+ my %ltienchash;
+
+ if ($is_home) {
+ %ltienchash = (
+ $action => { %ltienc }
+ );
+ }
if (keys(%secchanges)) {
$ltihash{'ltisec'} = \%newltisec;
if ($secchanges{'linkprot'}) {
@@ -15358,43 +15621,32 @@ sub modify_lti {
}
my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash,$dom);
if ($putresult eq 'ok') {
- my %keystore;
- &store_security($dom,'lti',\%secchanges,\%newkeyset,\%keystore,$lastactref);
- &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1);
- if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) {
- return &mt('No changes made.');
+ if (keys(%ltienchash)) {
+ &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1);
}
$resulttext = &mt('Changes made:').'';
if (keys(%secchanges) > 0) {
- $resulttext .= <i_security_results('lti',\%secchanges,\%newltisec,\%newkeyset,\%keystore);
+ $resulttext .= <i_security_results($dom,'lti',\%secchanges,\%newltisec,\%newkeyset,\%keystore);
if (exists($secchanges{'linkprot'})) {
$resulttext .= $linkprotoutput;
}
}
if (keys(%changes) > 0) {
my $cachetime = 24*60*60;
- my %ltiall = %confhash;
- foreach my $id (keys(%ltiall)) {
- if (ref($encconfig{$id}) eq 'HASH') {
- foreach my $item ('key','secret') {
- $ltiall{$id}{$item} = $encconfig{$id}{$item};
- }
- }
- }
- &Apache::lonnet::do_cache_new('lti',$dom,\%ltiall,$cachetime);
+ &Apache::lonnet::do_cache_new('lti',$dom,\%confhash,$cachetime);
if (ref($lastactref) eq 'HASH') {
$lastactref->{'lti'} = 1;
}
my %bynum;
foreach my $itemid (sort(keys(%changes))) {
- my $position = $confhash{$itemid}{'order'};
- $bynum{$position} = $itemid;
+ if (ref($confhash{$itemid}) eq 'HASH') {
+ my $position = $confhash{$itemid}{'order'};
+ $bynum{$position} = $itemid;
+ }
}
foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
my $itemid = $bynum{$pos};
- if (ref($confhash{$itemid}) ne 'HASH') {
- $resulttext .= '- '.&mt('Deleted: [_1]',$changes{$itemid}).'
';
- } else {
+ if (ref($confhash{$itemid}) eq 'HASH') {
$resulttext .= '- '.$confhash{$itemid}{'consumer'}.'
';
my $position = $pos + 1;
$resulttext .= '- '.&mt('Order: [_1]',$position).'
';
@@ -15403,13 +15655,11 @@ sub modify_lti {
$resulttext .= '- '.$lt{$item}.': '.$confhash{$itemid}{$item}.'
';
}
}
- if ($encconfig{$itemid}{'key'} ne '') {
- $resulttext .= '- '.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'
';
+ if ($ltienc{$itemid}{'key'} ne '') {
+ $resulttext .= '- '.$lt{'key'}.': '.$ltienc{$itemid}{'key'}.'
';
}
- if ($encconfig{$itemid}{'secret'} ne '') {
- $resulttext .= '- '.$lt{'secret'}.': ';
- my $num = length($encconfig{$itemid}{'secret'});
- $resulttext .= ('*'x$num).'
';
+ if ($ltienc{$itemid}{'secret'} ne '') {
+ $resulttext .= '- '.$lt{'secret'}.': ['.&mt('not shown').']
';
}
if ($confhash{$itemid}{'requser'}) {
if ($confhash{$itemid}{'callback'}) {
@@ -15561,8 +15811,18 @@ sub modify_lti {
$resulttext .= '
';
}
}
+ if (keys(%deletions)) {
+ foreach my $itemid (sort { $a <=> $b } keys(%deletions)) {
+ $resulttext .= '- '.&mt('Deleted: [_1]',$changes{$itemid}).'
';
+ }
+ }
}
$resulttext .= '
';
+ if (ref($lastactref) eq 'HASH') {
+ if (($secchanges{'encrypt'}) || ($secchanges{'private'})) {
+ $lastactref->{'domdefaults'} = 1;
+ }
+ }
} else {
$errors .= '- '.&mt('Failed to save changes').'
';
}
@@ -15573,6 +15833,30 @@ sub modify_lti {
return $resulttext;
}
+sub get_priv_creds {
+ my ($dom,$home,$encchg,$encrypt,$storedsec) = @_;
+ my ($needenc,$cipher,$privnum);
+ my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
+ if (($encchg) && (ref($encrypt) eq 'HASH')) {
+ $needenc = $encrypt->{'consumers'}
+ } else {
+ $needenc = $domdefs{'ltienc_consumers'};
+ }
+ if ($needenc) {
+ if (($storedsec eq 'ok') || ((ref($domdefs{'ltiprivhosts'}) eq 'ARRAY') &&
+ (grep(/^\Q$home\E$/,@{$domdefs{'ltiprivhosts'}})))) {
+ my %privhash = &Apache::lonnet::restore_dom('lti','private',$dom,$home,1);
+ my $privkey = $privhash{'key'};
+ $privnum = $privhash{'version'};
+ if (($privnum) && ($privkey ne '')) {
+ $cipher = Crypt::CBC->new({'key' => $privkey,
+ 'cipher' => 'DES'});
+ }
+ }
+ }
+ return ($cipher,$privnum);
+}
+
sub get_lti_id {
my ($domain,$consumer) = @_;
# get lock on lti db
@@ -20212,6 +20496,7 @@ sub modify_coursedefaults {
uploadquota => 500,
postsubmit => 60,
mysqltables => 172800,
+ domexttool => 1,
);
my %texoptions = (
MathJax => 'MathJax',
@@ -20403,6 +20688,47 @@ sub modify_coursedefaults {
$changes{'postsubmit'} = 1;
}
}
+ my (%newdomexttool,%newexttool,%olddomexttool,%oldexttool);
+ map { $newdomexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.domexttool');
+ map { $newexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.exttool');
+ if (ref($domconfig{'coursedefaults'}{'domexttool'}) eq 'HASH') {
+ %olddomexttool = %{$domconfig{'coursedefaults'}{'domexttool'}};
+ } else {
+ foreach my $type (@types) {
+ if ($staticdefaults{'domexttool'}) {
+ $olddomexttool{$type} = 1;
+ } else {
+ $olddomexttool{$type} = 0;
+ }
+ }
+ }
+ if (ref($domconfig{'coursedefaults'}{'exttool'}) eq 'HASH') {
+ %oldexttool = %{$domconfig{'coursedefaults'}{'exttool'}};
+ } else {
+ foreach my $type (@types) {
+ if ($staticdefaults{'exttool'}) {
+ $oldexttool{$type} = 1;
+ } else {
+ $oldexttool{$type} = 0;
+ }
+ }
+ }
+ foreach my $type (@types) {
+ unless ($newdomexttool{$type}) {
+ $newdomexttool{$type} = 0;
+ }
+ unless ($newexttool{$type}) {
+ $newexttool{$type} = 0;
+ }
+ if ($newdomexttool{$type} != $olddomexttool{$type}) {
+ $changes{'domexttool'} = 1;
+ }
+ if ($newexttool{$type} != $oldexttool{$type}) {
+ $changes{'exttool'} = 1;
+ }
+ }
+ $defaultshash{'coursedefaults'}{'domexttool'} = \%newdomexttool;
+ $defaultshash{'coursedefaults'}{'exttool'} = \%newexttool;
}
my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
$dom);
@@ -20412,8 +20738,10 @@ sub modify_coursedefaults {
if (($changes{'canuse_pdfforms'}) || ($changes{'uploadquota'}) || ($changes{'postsubmit'}) ||
($changes{'coursecredits'}) || ($changes{'uselcmath'}) || ($changes{'usejsme'}) ||
($changes{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'}) ||
- ($changes{'inline_chem'}) || ($changes{'ltiauth'})) {
- foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine','ltiauth') {
+ ($changes{'inline_chem'}) || ($changes{'ltiauth'}) || ($changes{'domexttool'}) ||
+ ($changes{'exttool'}) ) {
+ foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine',
+ 'ltiauth') {
if ($changes{$item}) {
$domdefaults{$item}=$defaultshash{'coursedefaults'}{$item};
}
@@ -20456,6 +20784,20 @@ sub modify_coursedefaults {
$domdefaults{'canclone'}=$defaultshash{'coursedefaults'}{'canclone'};
}
}
+ if ($changes{'domexttool'}) {
+ if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') {
+ foreach my $type (@types) {
+ $domdefaults{$type.'domexttool'}=$defaultshash{'coursedefaults'}{'domexttool'}{$type};
+ }
+ }
+ }
+ if ($changes{'exttool'}) {
+ if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') {
+ foreach my $type (@types) {
+ $domdefaults{$type.'exttool'}=$defaultshash{'coursedefaults'}{'exttool'}{$type};
+ }
+ }
+ }
my $cachetime = 24*60*60;
&Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
if (ref($lastactref) eq 'HASH') {
@@ -20593,6 +20935,34 @@ sub modify_coursedefaults {
} else {
$resulttext .= '- '.&mt('LTI launch of deep-linked URL will require re-authentication').'
';
}
+ } elsif ($item eq 'domexttool') {
+ my @noyes = (&mt('no'),&mt('yes'));
+ if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') {
+ $resulttext .= '- '.&mt('External Tools defined in the domain may be used as follows:').'
'.
+ '- '.&mt('Official courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'official'}].'').'
'.
+ '- '.&mt('Unofficial courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'unofficial'}].'').'
'.
+ '- '.&mt('Textbook courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'textbook'}].'').'
'.
+ '- '.&mt('Placement tests: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'placement'}].'').'
'.
+ '- '.&mt('Communities: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'community'}].'').'
'.
+ '
'.
+ ' ';
+ } else {
+ $resulttext .= '- '.&mt('External Tools defined in the domain may be used in all course types, by default').'
';
+ }
+ } elsif ($item eq 'exttool') {
+ my @noyes = (&mt('no'),&mt('yes'));
+ if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') {
+ $resulttext .= '- '.&mt('External Tools can be defined and configured in course containers as follows:').'
'.
+ '- '.&mt('Official courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'official'}].'').'
'.
+ '- '.&mt('Unofficial courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'unofficial'}].'').'
'.
+ '- '.&mt('Textbook courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'textbook'}].'').'
'.
+ '- '.&mt('Placement tests: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'placement'}].'').'
'.
+ '- '.&mt('Communities: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'community'}].'').'
'.
+ '
'.
+ ' ';
+ } else {
+ $resulttext .= '- '.&mt('External Tools can not be defined in any course types, by default').'
';
+ }
}
}
$resulttext .= '
';