--- loncom/lonnet/perl/lonnet.pm 2021/08/13 20:32:23 1.1464 +++ loncom/lonnet/perl/lonnet.pm 2022/02/14 02:48:53 1.1481 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.1464 2021/08/13 20:32:23 raeburn Exp $ +# $Id: lonnet.pm,v 1.1481 2022/02/14 02:48:53 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -2262,6 +2262,26 @@ sub del_dom { } } +sub store_dom { + my ($storehash,$id,$namespace,$dom,$home) = @_; + $$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'; + } else { + return reply("storedom:$dom:$namespace:$id:$namevalue","$home"); + } + } +} + # ----------------------------------construct domainconfig user for a domain sub get_domainconfiguser { my ($udom) = @_; @@ -2304,7 +2324,7 @@ sub retrieve_inst_usertypes { sub is_domainimage { my ($url) = @_; - if ($url=~m-^/+res/+($match_domain)/+\1\-domainconfig/+(img|logo|domlogo)/+[^/]-) { + if ($url=~m-^/+res/+($match_domain)/+\1\-domainconfig/+(img|logo|domlogo|login)/+[^/]-) { if (&domain($1) ne '') { return '1'; } @@ -2666,6 +2686,7 @@ sub get_domain_defaults { if (ref($domconfig{'coursedefaults'}) eq 'HASH') { $domdefaults{'canuse_pdfforms'} = $domconfig{'coursedefaults'}{'canuse_pdfforms'}; $domdefaults{'usejsme'} = $domconfig{'coursedefaults'}{'usejsme'}; + $domdefaults{'inline_chem'} = $domconfig{'coursedefaults'}{'inline_chem'}; $domdefaults{'uselcmath'} = $domconfig{'coursedefaults'}{'uselcmath'}; if (ref($domconfig{'coursedefaults'}{'postsubmit'}) eq 'HASH') { $domdefaults{'postsubmit'} = $domconfig{'coursedefaults'}{'postsubmit'}{'client'}; @@ -2698,7 +2719,10 @@ sub get_domain_defaults { } 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') { @@ -2773,6 +2797,7 @@ sub get_domain_defaults { } if (ref($domconfig{'autoenroll'}) eq 'HASH') { $domdefaults{'autofailsafe'} = $domconfig{'autoenroll'}{'autofailsafe'}; + $domdefaults{'failsafe'} = $domconfig{'autoenroll'}{'failsafe'}; } if (ref($domconfig{'helpsettings'}) eq 'HASH') { $domdefaults{'submitbugs'} = $domconfig{'helpsettings'}{'submitbugs'}; @@ -4659,7 +4684,7 @@ sub bubblesheet_converter { next if (($num == 1) && ($csvoptions{'hdr'} == 1)); $line =~ s{[\r\n]+$}{}; my %found; - my @values = split(/,/,$line); + my @values = split(/,/,$line,-1); my ($qstart,$record); for (my $i=0; $i<@values; $i++) { if ((($qstart ne '') && ($i > $qstart)) || @@ -4963,6 +4988,29 @@ sub flushcourselogs { if (! defined($dom) || $dom eq '' || ! defined($name) || $name eq '') { my $cid = $env{'request.course.id'}; +# +# FIXME 11/29/2021 +# Typo in rev. 1.458 (2003/12/09)?? +# These should likely by $env{'course.'.$cid.'.domain'} and $env{'course.'.$cid.'.num'} +# +# While these ramain as $env{'request.'.$cid.'.domain'} and $env{'request.'.$cid.'.num'} +# $dom and $name will always be null, so the &inc() call will default to storing this data +# in a nohist_accesscount.db file for the user rather than the course. +# +# That said there is a lot of noise in the data being stored. +# So counts for prtspool/ and adm/ etc. are recorded. +# +# A review of which items ending '___count' are written to %accesshash should likely be +# made before deciding whether to set these to 'course.' instead of 'request.' +# +# Under the current scheme each user receives a nohist_accesscount.db file listing +# accesses for things which are not published resources, regardless of course, and +# there is not a nohist_accesscount.db file in a course, which might log accesses from +# anyone in the course for things which are not published resources. +# +# For an author, nohist_accesscount.db ends up having records for other items +# mixed up with the legitimate access counts for the author's published resources. +# $dom = $env{'request.'.$cid.'.domain'}; $name = $env{'request.'.$cid.'.num'}; } @@ -7560,15 +7608,15 @@ sub portfolio_access { if ($result) { my %setters; if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') { - my ($startblock,$endblock) = - &Apache::loncommon::blockcheck(\%setters,'port',$unum,$udom); - if ($startblock && $endblock) { + my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = + &Apache::loncommon::blockcheck(\%setters,'port',$clientip,$unum,$udom); + if (($startblock && $endblock) || ($by_ip)) { return 'B'; } } else { - my ($startblock,$endblock) = - &Apache::loncommon::blockcheck(\%setters,'port'); - if ($startblock && $endblock) { + my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = + &Apache::loncommon::blockcheck(\%setters,'port',$clientip); + if (($startblock && $endblock) || ($by_ip)) { return 'B'; } } @@ -8130,7 +8178,7 @@ sub allowed { $uri=&declutter($uri); if ($priv eq 'evb') { -# Evade communication block restrictions for specified role in a course +# Evade communication block restrictions for specified role in a course or domain if ($env{'user.priv.'.$role} =~/evb\&([^\:]*)/) { return $1; } else { @@ -8151,9 +8199,9 @@ sub allowed { if (($space=~/^(uploaded|editupload)$/) && ($env{'user.name'} eq $name) && ($env{'user.domain'} eq $domain) && ('portfolio' eq $dir[0])) { my %setters; - my ($startblock,$endblock) = - &Apache::loncommon::blockcheck(\%setters,'port'); - if ($startblock && $endblock) { + my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = + &Apache::loncommon::blockcheck(\%setters,'port',$clientip); + if (($startblock && $endblock) || ($by_ip)) { return 'B'; } else { return 'F'; @@ -8855,7 +8903,11 @@ sub get_commblock_resources { my ($blocks) = @_; my %blockers = (); return %blockers unless ($env{'request.course.id'}); - return %blockers if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/); + my $courseurl = &courseid_to_courseurl($env{'request.course.id'}); + if ($env{'request.course.sec'}) { + $courseurl .= '/'.$env{'request.course.sec'}; + } + return %blockers if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseurl} =~/evb\&([^\:]*)/); my %commblocks; if (ref($blocks) eq 'HASH') { %commblocks = %{$blocks}; @@ -8887,10 +8939,9 @@ sub get_commblock_resources { } } elsif ($block =~ /^firstaccess____(.+)$/) { my $item = $1; - my @to_test; if (ref($commblocks{$block}{'blocks'}) eq 'HASH') { if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') { - my @interval; + my (@interval,$mapname); my $type = 'map'; if ($item eq 'course') { $type = 'course'; @@ -8899,36 +8950,11 @@ sub get_commblock_resources { if ($item =~ /___\d+___/) { $type = 'resource'; @interval=&EXT("resource.0.interval",$item); - if (ref($navmap)) { - my $res = $navmap->getBySymb($item); - push(@to_test,$res); - } } else { - my $mapsymb = &symbread($item,1); - if ($mapsymb) { - if (ref($navmap)) { - my $mapres = $navmap->getBySymb($mapsymb); - if (ref($mapres)) { - my $first = $mapres->map_start(); - my $finish = $mapres->map_finish(); - my $it = $navmap->getIterator($first,$finish,undef,0,0); - if (ref($it)) { - my $res; - while ($res = $it->next(undef,1)) { - next unless (ref($res)); - my $symb = $res->symb(); - next if (($symb eq $mapsymb) || ($symb eq '')); - @interval=&EXT("resource.0.interval",$symb); - if ($interval[1] eq 'map') { - if ($res->answerable()) { - push(@to_test,$res); - last; - } - } - } - } - } - } + $mapname = &deversion($item); + if (ref($navmap)) { + my $timelimit = $navmap->get_mapparam(undef,$mapname,'0.interval'); + @interval = ($timelimit,'map'); } } } @@ -8946,10 +8972,37 @@ sub get_commblock_resources { my $timesup = $first_access+$timelimit; if ($timesup > $now) { my $activeblock; - foreach my $res (@to_test) { - if ($res->answerable()) { - $activeblock = 1; - last; + if ($type eq 'resource') { + if (ref($navmap)) { + my $res = $navmap->getBySymb($item); + if ($res->answerable()) { + $activeblock = 1; + } + } + } elsif ($type eq 'map') { + my $mapsymb = &symbread($mapname,1); + if (($mapsymb) && (ref($navmap))) { + my $mapres = $navmap->getBySymb($mapsymb); + if (ref($mapres)) { + my $first = $mapres->map_start(); + my $finish = $mapres->map_finish(); + my $it = $navmap->getIterator($first,$finish,undef,0,0); + if (ref($it)) { + my $res; + while ($res = $it->next(undef,1)) { + next unless (ref($res)); + my $symb = $res->symb(); + next if (($symb eq $mapsymb) || ($symb eq '')); + @interval=&EXT("resource.0.interval",$symb); + if ($interval[1] eq 'map') { + if ($res->answerable()) { + $activeblock = 1; + last; + } + } + } + } + } } } if ($activeblock) { @@ -8979,8 +9032,12 @@ sub has_comm_blocking { my @blockers; return unless ($env{'request.course.id'}); return unless ($priv eq 'bre'); - return if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/); return if ($env{'request.state'} eq 'construct'); + my $courseurl = &courseid_to_courseurl($env{'request.course.id'}); + if ($env{'request.course.sec'}) { + $courseurl .= '/'.$env{'request.course.sec'}; + } + return if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseurl} =~/evb\&([^\:]*)/); my %blockinfo; if (ref($blocks) eq 'HASH') { %blockinfo = &get_commblock_resources($blocks); @@ -9379,7 +9436,7 @@ sub fetch_enrollment_query { } sub get_query_reply { - my ($queryid,$sleep,$loopmax) = @_;; + my ($queryid,$sleep,$loopmax) = @_; if (($sleep eq '') || ($sleep !~ /^\d+\.?\d*$/)) { $sleep = 0.2; } @@ -12232,6 +12289,55 @@ sub get_course_lti { 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; @@ -12323,7 +12429,7 @@ sub EXT { if ( (defined($Apache::lonhomework::parsing_a_problem) || defined($Apache::lonhomework::parsing_a_task)) && - ($symbparm eq &symbread()) ) { + ($symbparm eq &symbread()) ) { # if we are in the middle of processing the resource the # get the value we are planning on committing if (defined($Apache::lonhomework::results{$qualifierrest})) { @@ -14387,10 +14493,15 @@ sub machine_ids { sub additional_machine_domains { my @domains; - open(my $fh,"<","$perlvar{'lonTabDir'}/expected_domains.tab"); - while( my $line = <$fh>) { - $line =~ s/\s//g; - push(@domains,$line); + if (-e "$perlvar{'lonTabDir'}/expected_domains.tab") { + if (open(my $fh,"<","$perlvar{'lonTabDir'}/expected_domains.tab")) { + while (my $line = <$fh>) { + chomp($line); + $line =~ s/\s//g; + push(@domains,$line); + } + close($fh); + } } return @domains; } @@ -14603,7 +14714,7 @@ sub use_proxy_alias { if ($alias) { my $dom = &host_domain($lonid); if ($dom ne '') { - my $proxyinfo = &get_proxy_settings($dom ); + my $proxyinfo = &get_proxy_settings($dom); my ($vpnint,$remote_ip); if (ref($proxyinfo) eq 'HASH') { $vpnint = $proxyinfo->{'vpnint'}; @@ -14618,6 +14729,81 @@ sub use_proxy_alias { } return; } + +sub alias_sso { + my ($lonid) = @_; + if ($lonid eq '') { + $lonid = $perlvar{'lonHostID'}; + } + if (!defined(&hostname($lonid))) { + return; + } + if ($lonid ne '') { + my ($use_alias,$cached) = &is_cached_new('proxysaml',$lonid); + if ($cached) { + return $use_alias; + } + my $dom = &Apache::lonnet::host_domain($lonid); + if ($dom ne '') { + my $cachetime = 60*60*24; + my %domconfig = + &Apache::lonnet::get_dom('configuration',['wafproxy'],$dom); + if (ref($domconfig{'wafproxy'}) eq 'HASH') { + if (ref($domconfig{'wafproxy'}{'saml'}) eq 'HASH') { + $use_alias = $domconfig{'wafproxy'}{'saml'}{$lonid}; + } + } + return &do_cache_new('proxysaml',$lonid,$use_alias,$cachetime); + } + } + return; +} + +sub get_saml_landing { + my ($lonid) = @_; + if ($lonid eq '') { + my $defdom = &default_login_domain(); + my @hosts = ¤t_machine_ids(); + if (@hosts > 1) { + foreach my $hostid (@hosts) { + if (&host_domain($hostid) eq $defdom) { + $lonid = $hostid; + last; + } + } + } else { + $lonid = $perlvar{'lonHostID'}; + } + if ($lonid) { + unless (&Apache::lonnet::host_domain($lonid) eq $defdom) { + return; + } + } else { + return; + } + } elsif (!defined(&hostname($lonid))) { + return; + } + my ($landing,$cached) = &is_cached_new('samllanding',$lonid); + if ($cached) { + return $landing; + } + my $dom = &Apache::lonnet::host_domain($lonid); + if ($dom ne '') { + my $cachetime = 60*60*24; + my %domconfig = + &Apache::lonnet::get_dom('configuration',['login'],$dom); + if (ref($domconfig{'login'}) eq 'HASH') { + if (ref($domconfig{'login'}{'saml'}) eq 'HASH') { + if (ref($domconfig{'login'}{'saml'}{$lonid}) eq 'HASH') { + $landing = 1; + } + } + } + return &do_cache_new('samllanding',$lonid,$landing,$cachetime); + } + return; +} # ------------------------------------------------------------- Declutters URLs