--- loncom/lonnet/perl/lonnet.pm 2022/02/07 12:09:33 1.1172.2.146.2.3 +++ loncom/lonnet/perl/lonnet.pm 2023/01/22 17:32:08 1.1172.2.146.2.10 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.1172.2.146.2.3 2022/02/07 12:09:33 raeburn Exp $ +# $Id: lonnet.pm,v 1.1172.2.146.2.10 2023/01/22 17:32:08 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -1254,7 +1254,7 @@ sub changepass { sub queryauthenticate { my ($uname,$udom)=@_; my $uhome=&homeserver($uname,$udom); - if (!$uhome) { + if ((!$uhome) || ($uhome eq 'no_host')) { &logthis("User $uname at $udom is unknown when looking for authentication mechanism"); return 'no_host'; } @@ -1303,12 +1303,35 @@ sub authenticate { } if ($answer eq 'non_authorized') { &logthis("User $uname at $udom rejected by $uhome"); - return 'no_host'; + return 'no_host'; } &logthis("User $uname at $udom threw error $answer when checking authentication mechanism"); return 'no_host'; } +sub can_switchserver { + my ($udom,$home) = @_; + my ($canswitch,@intdoms); + my $internet_names = &get_internet_names($home); + if (ref($internet_names) eq 'ARRAY') { + @intdoms = @{$internet_names}; + } + my $uint_dom = &internet_dom(&domain($udom,'primary')); + if ($uint_dom ne '' && grep(/^\Q$uint_dom\E$/,@intdoms)) { + $canswitch = 1; + } else { + my $serverhomeID = &get_server_homeID(&hostname($home)); + my $serverhomedom = &host_domain($serverhomeID); + my %defdomdefaults = &get_domain_defaults($serverhomedom); + my %udomdefaults = &get_domain_defaults($udom); + my $remoterev = &get_server_loncaparev('',$home); + $canswitch = &can_host_session($udom,$home,$remoterev, + $udomdefaults{'remotesessions'}, + $defdomdefaults{'hostedsessions'}); + } + return $canswitch; +} + sub can_host_session { my ($udom,$lonhost,$remoterev,$remotesessions,$hostedsessions) = @_; my $canhost = 1; @@ -1997,6 +2020,57 @@ sub del_dom { } } +sub store_dom { + my ($storehash,$id,$namespace,$dom,$home,$encrypt) = @_; + $$storehash{'ip'}=&get_requestor_ip(); + $$storehash{'host'}=$perlvar{'lonHostID'}; + my $namevalue=''; + foreach my $key (keys(%{$storehash})) { + $namevalue.=&escape($key).'='.&freeze_escape($$storehash{$key}).'&'; + } + $namevalue=~s/\&$//; + if (grep { $_ eq $home } current_machine_ids()) { + return LONCAPA::Lond::store_dom("storedom:$dom:$namespace:$id:$namevalue"); + } else { + if ($namespace eq 'private') { + return 'refused'; + } elsif ($encrypt) { + return reply("encrypt:storedom:$dom:$namespace:$id:$namevalue",$home); + } else { + return reply("storedom:$dom:$namespace:$id:$namevalue",$home); + } + } +} + +sub restore_dom { + my ($id,$namespace,$dom,$home,$encrypt) = @_; + my $answer; + if (grep { $_ eq $home } current_machine_ids()) { + $answer = LONCAPA::Lond::restore_dom("restoredom:$dom:$namespace:$id"); + } elsif ($namespace ne 'private') { + if ($encrypt) { + $answer=&reply("encrypt:restoredom:$dom:$namespace:$id",$home); + } else { + $answer=&reply("restoredom:$dom:$namespace:$id",$home); + } + } + my %returnhash=(); + unless (($answer eq '') || ($answer eq 'con_lost') || ($answer eq 'refused') || + ($answer eq 'unknown_cmd') || ($answer eq 'rejected')) { + foreach my $line (split(/\&/,$answer)) { + my ($name,$value)=split(/\=/,$line); + $returnhash{&unescape($name)}=&thaw_unescape($value); + } + my $version; + for ($version=1;$version<=$returnhash{'version'};$version++) { + foreach my $item (split(/\:/,$returnhash{$version.':keys'})) { + $returnhash{$item}=$returnhash{$version.':'.$item}; + } + } + } + return %returnhash; +} + # ----------------------------------construct domainconfig user for a domain sub get_domainconfiguser { my ($udom) = @_; @@ -2275,6 +2349,10 @@ sub inst_rulecheck { $response=&unescape(&reply('instidrulecheck:'.&escape($udom). ':'.&escape($id).':'.$rulestr, $homeserver)); + } elsif ($item eq 'unamemap') { + $response=&unescape(&reply('instunamemapcheck:'. + &escape($udom).':'.&escape($uname). + ':'.$rulestr,$homeserver)); } elsif ($item eq 'selfcreate') { $response=&unescape(&reply('instselfcreatecheck:'. &escape($udom).':'.&escape($uname). @@ -2308,6 +2386,9 @@ sub inst_userrules { } elsif ($check eq 'email') { $response=&reply('instemailrules:'.&escape($udom), $homeserver); + } elsif ($check eq 'unamemap') { + $response=&reply('unamemaprules:'.&escape($udom), + $homeserver); } else { $response=&reply('instuserrules:'.&escape($udom), $homeserver); @@ -2354,7 +2435,7 @@ sub get_domain_defaults { 'coursedefaults','usersessions', 'requestauthor','selfenrollment', 'coursecategories','autoenroll', - 'helpsettings','wafproxy'],$domain); + 'helpsettings','wafproxy','ltisec'],$domain); my @coursetypes = ('official','unofficial','community','textbook'); if (ref($domconfig{'defaults'}) eq 'HASH') { $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; @@ -2366,6 +2447,7 @@ sub get_domain_defaults { $domdefaults{'intauth_cost'} = $domconfig{'defaults'}{'intauth_cost'}; $domdefaults{'intauth_switch'} = $domconfig{'defaults'}{'intauth_switch'}; $domdefaults{'intauth_check'} = $domconfig{'defaults'}{'intauth_check'}; + $domdefaults{'unamemap_rule'} = $domconfig{'defaults'}{'unamemap_rule'}; } else { $domdefaults{'lang_def'} = &domain($domain,'lang_def'); $domdefaults{'auth_def'} = &domain($domain,'auth_def'); @@ -2509,6 +2591,18 @@ sub get_domain_defaults { } } } + if (ref($domconfig{'ltisec'}) eq 'HASH') { + if (ref($domconfig{'ltisec'}{'encrypt'}) eq 'HASH') { + $domdefaults{'linkprotenc_crs'} = $domconfig{'ltisec'}{'encrypt'}{'crs'}; + $domdefaults{'linkprotenc_dom'} = $domconfig{'ltisec'}{'encrypt'}{'dom'}; + $domdefaults{'ltienc_consumers'} = $domconfig{'ltisec'}{'encrypt'}{'consumers'}; + } + if (ref($domconfig{'ltisec'}{'private'}) eq 'HASH') { + if (ref($domconfig{'ltisec'}{'private'}{'keys'}) eq 'ARRAY') { + $domdefaults{'privhosts'} = $domconfig{'ltisec'}{'private'}{'keys'}; + } + } + } &do_cache_new('domdefaults',$domain,\%domdefaults,$cachetime); return %domdefaults; } @@ -2545,6 +2639,7 @@ sub get_dom_instcats { if (&auto_instcode_format($caller,$dom,\%coursecodes,\%codes, \@codetitles,\%cat_titles,\%cat_order) eq 'ok') { $instcats = { + totcodes => $totcodes, codes => \%codes, codetitles => \@codetitles, cat_titles => \%cat_titles, @@ -4565,6 +4660,7 @@ sub get_scantronformat_file { close($fh); } } + chomp(@lines); } return @lines; } @@ -6582,31 +6678,31 @@ sub course_adhocrole_privs { $full{$priv} = $restrict; } foreach my $item (split(/,/,$overrides{"internal.adhocpriv.$rolename"})) { - next if ($item eq ''); - my ($rule,$rest) = split(/=/,$item); - next unless (($rule eq 'off') || ($rule eq 'on')); - foreach my $priv (split(/:/,$rest)) { - if ($priv ne '') { - if ($rule eq 'off') { - $possremove{$priv} = 1; - } else { - $possadd{$priv} = 1; - } - } - } - } - foreach my $priv (sort(keys(%full))) { - if (exists($currprivs{$priv})) { - unless (exists($possremove{$priv})) { - $storeprivs{$priv} = $currprivs{$priv}; - } - } elsif (exists($possadd{$priv})) { - $storeprivs{$priv} = $full{$priv}; - } - } - $coursepriv = ':'.join(':',map { $_.'&'.$storeprivs{$_}; } sort(keys(%storeprivs))); - } - return $coursepriv; + next if ($item eq ''); + my ($rule,$rest) = split(/=/,$item); + next unless (($rule eq 'off') || ($rule eq 'on')); + foreach my $priv (split(/:/,$rest)) { + if ($priv ne '') { + if ($rule eq 'off') { + $possremove{$priv} = 1; + } else { + $possadd{$priv} = 1; + } + } + } + } + foreach my $priv (sort(keys(%full))) { + if (exists($currprivs{$priv})) { + unless (exists($possremove{$priv})) { + $storeprivs{$priv} = $currprivs{$priv}; + } + } elsif (exists($possadd{$priv})) { + $storeprivs{$priv} = $full{$priv}; + } + } + $coursepriv = ':'.join(':',map { $_.'&'.$storeprivs{$_}; } sort(keys(%storeprivs))); + } + return $coursepriv; } sub group_roleprivs { @@ -7663,6 +7759,7 @@ sub usertools_access { blog => 1, webdav => 1, portfolio => 1, + timezone => 1, ); } return if (!defined($tools{$tool})); @@ -11696,15 +11793,24 @@ sub resdata { sub get_domain_lti { my ($cdom,$context) = @_; - my ($name,%lti); + my ($name,$cachename,%lti); if ($context eq 'consumer') { $name = 'ltitools'; } elsif ($context eq 'provider') { $name = 'lti'; + } elsif ($context eq 'linkprot') { + $name = 'ltisec'; } else { return %lti; } - my ($result,$cached)=&is_cached_new($name,$cdom); + + if ($context eq 'linkprot') { + $cachename = $context; + } else { + $cachename = $name; + } + + my ($result,$cached)=&is_cached_new($cachename,$cdom); if (defined($cached)) { if (ref($result) eq 'HASH') { %lti = %{$result}; @@ -11712,20 +11818,28 @@ sub get_domain_lti { } else { my %domconfig = &get_dom('configuration',[$name],$cdom); if (ref($domconfig{$name}) eq 'HASH') { - %lti = %{$domconfig{$name}}; - my %encdomconfig = &get_dom('encconfig',[$name],$cdom,undef,1); - if (ref($encdomconfig{$name}) eq 'HASH') { - foreach my $id (keys(%lti)) { - if (ref($encdomconfig{$name}{$id}) eq 'HASH') { - foreach my $item ('key','secret') { - $lti{$id}{$item} = $encdomconfig{$name}{$id}{$item}; + if ($context eq 'linkprot') { + if (ref($domconfig{$name}{'linkprot'}) eq 'HASH') { + %lti = %{$domconfig{$name}{'linkprot'}}; + } + } else { + %lti = %{$domconfig{$name}}; + } + if (($context eq 'consumer') && (keys(%lti))) { + my %encdomconfig = &get_dom('encconfig',[$name],$cdom,undef,1); + if (ref($encdomconfig{$name}) eq 'HASH') { + foreach my $id (keys(%lti)) { + if (ref($encdomconfig{$name}{$id}) eq 'HASH') { + foreach my $item ('key','secret') { + $lti{$id}{$item} = $encdomconfig{$name}{$id}{$item}; + } } } } } } my $cachetime = 24*60*60; - &do_cache_new($name,$cdom,\%lti,$cachetime); + &do_cache_new($cachename,$cdom,\%lti,$cachetime); } return %lti; } @@ -11796,24 +11910,64 @@ sub domainlti_itemid { return $itemid; } -sub get_numsuppfiles { - my ($cnum,$cdom,$ignorecache)=@_; +sub count_supptools { + my ($cnum,$cdom,$ignorecache,$reload)=@_; my $hashid=$cnum.':'.$cdom; - my ($suppcount,$cached); + my ($numexttools,$cached); unless ($ignorecache) { - ($suppcount,$cached) = &is_cached_new('suppcount',$hashid); + ($numexttools,$cached) = &is_cached_new('supptools',$hashid); } unless (defined($cached)) { my $chome=&homeserver($cnum,$cdom); + $numexttools = 0; unless ($chome eq 'no_host') { - ($suppcount,my $errors) = (0,0); - my $suppmap = 'supplemental.sequence'; - ($suppcount,$errors) = - &Apache::loncommon::recurse_supplemental($cnum,$cdom,$suppmap,$suppcount,$errors); + my ($supplemental) = &Apache::loncommon::get_supplemental($cnum,$cdom,$reload); + if (ref($supplemental) eq 'HASH') { + if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) { + foreach my $key (keys(%{$supplemental->{'ids'}})) { + if ($key =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) { + $numexttools ++; + } + } + } + } } - &do_cache_new('suppcount',$hashid,$suppcount,600); + &do_cache_new('supptools',$hashid,$numexttools,600); } - return $suppcount; + return $numexttools; +} + +sub has_unhidden_suppfiles { + my ($cnum,$cdom,$ignorecache,$possdel)=@_; + my $hashid=$cnum.':'.$cdom; + my ($showsupp,$cached); + unless ($ignorecache) { + ($showsupp,$cached) = &is_cached_new('showsupp',$hashid); + } + unless (defined($cached)) { + my $chome=&homeserver($cnum,$cdom); + unless ($chome eq 'no_host') { + my ($supplemental) = &Apache::loncommon::get_supplemental($cnum,$cdom,$ignorecache,$possdel); + if (ref($supplemental) eq 'HASH') { + if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) { + foreach my $key (keys(%{$supplemental->{'ids'}})) { + next if ($key =~ /\.sequence$/); + if (ref($supplemental->{'ids'}->{$key}) eq 'ARRAY') { + foreach my $id (@{$supplemental->{'ids'}->{$key}}) { + unless ($supplemental->{'hidden'}->{$id}) { + $showsupp = 1; + last; + } + } + } + last if ($showsupp); + } + } + } + } + &do_cache_new('showsupp',$hashid,$showsupp,600); + } + return $showsupp; } # @@ -12825,11 +12979,68 @@ sub get_coursechange { } sub devalidate_coursechange_cache { - my ($cnum,$cdom)=@_; - my $hashid=$cnum.':'.$cdom; + my ($cdom,$cnum)=@_; + my $hashid=$cdom.'_'.$cnum; &devalidate_cache_new('crschange',$hashid); } +sub get_suppchange { + my ($cdom,$cnum) = @_; + if ($cdom eq '' || $cnum eq '') { + return unless ($env{'request.course.id'}); + $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + } + my $hashid=$cdom.'_'.$cnum; + my ($change,$cached)=&is_cached_new('suppchange',$hashid); + if ((defined($cached)) && ($change ne '')) { + return $change; + } else { + my %crshash = &get('environment',['internal.supplementalchange'],$cdom,$cnum); + if ($crshash{'internal.supplementalchange'} eq '') { + $change = $env{'course.'.$cdom.'_'.$cnum.'.internal.created'}; + if ($change eq '') { + %crshash = &get('environment',['internal.created'],$cdom,$cnum); + $change = $crshash{'internal.created'}; + } + } else { + $change = $crshash{'internal.supplementalchange'}; + } + my $cachetime = 600; + &do_cache_new('suppchange',$hashid,$change,$cachetime); + } + return $change; +} + +sub devalidate_suppchange_cache { + my ($cdom,$cnum)=@_; + my $hashid=$cdom.'_'.$cnum; + &devalidate_cache_new('suppchange',$hashid); +} + +sub update_supp_caches { + my ($cdom,$cnum) = @_; + my %servers = &internet_dom_servers($cdom); + my @ids=¤t_machine_ids(); + foreach my $server (keys(%servers)) { + next if (grep(/^\Q$server\E$/,@ids)); + my $hashid=$cnum.':'.$cdom; + my $cachekey = &escape('showsupp').':'.&escape($hashid); + &remote_devalidate_cache($server,[$cachekey]); + } + &has_unhidden_suppfiles($cnum,$cdom,1,1); + &count_supptools($cnum,$cdom,1); + my $now = time; + if ($env{'request.course.id'} eq $cdom.'_'.$cnum) { + &Apache::lonnet::appenv({'request.course.suppupdated' => $now}); + } + &put('environment',{'internal.supplementalchange' => $now}, + $cdom,$cnum); + &Apache::lonnet::appenv( + {'course.'.$cdom.'_'.$cnum.'.internal.supplementalchange' => $now}); + &do_cache_new('suppchange',$cdom.'_'.$cnum,$now,600); +} + # ------------------------------------------------- Update symbolic store links sub symblist { @@ -13016,17 +13227,10 @@ sub symbread { my %bighash; my $syval=''; if (($env{'request.course.fn'}) && ($thisfn)) { - my $targetfn = $thisfn; - if ( ($thisfn =~ m/^(uploaded|editupload)\//) && ($thisfn !~ m/\.(page|sequence)$/) ) { - $targetfn = 'adm/wrapper/'.$thisfn; - } - if ($targetfn =~ m|^adm/wrapper/(ext/.*)|) { - $targetfn=$1; - } unless ($ignoresymbdb) { if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { - $syval=$hash{$targetfn}; + $syval=$hash{$thisfn}; untie(%hash); } if ($syval && $checkforblock) { @@ -15564,10 +15768,6 @@ data base, returning a hash that is keye values that are the resource value. I believe that the timestamps and versions are also returned. -get_numsuppfiles($cnum,$cdom) : retrieve number of files in a course's -supplemental content area. This routine caches the number of files for -10 minutes. - =back =head2 Course Modification