--- loncom/lonnet/perl/lonnet.pm 2010/05/26 17:14:43 1.1056.4.2 +++ loncom/lonnet/perl/lonnet.pm 2010/08/17 00:19:24 1.1056.4.3 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.1056.4.2 2010/05/26 17:14:43 raeburn Exp $ +# $Id: lonnet.pm,v 1.1056.4.3 2010/08/17 00:19:24 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -76,7 +76,7 @@ use HTTP::Date; use Image::Magick; use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir - $_64bit %env %protocol); + $_64bit %env %protocol %loncaparevs %serverhomeIDs); my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash, %userrolehash, $processmarker, $dumpcount, %coursedombuf, @@ -196,7 +196,7 @@ sub get_server_timezone { } sub get_server_loncaparev { - my ($dom,$lonhost) = @_; + my ($dom,$lonhost,$ignore_cache,$caller) = @_; if (defined($lonhost)) { if (!defined(&hostname($lonhost))) { undef($lonhost); @@ -211,15 +211,74 @@ sub get_server_loncaparev { } } if (defined($lonhost)) { - my $cachetime = 24*3600; - my ($loncaparev,$cached)=&is_cached_new('serverloncaparev',$lonhost); + my $cachetime = 12*3600; + if (!$ignore_cache) { + my ($loncaparev,$cached)=&is_cached_new('serverloncaparev',$lonhost); + if (defined($cached)) { + return $loncaparev; + } + } + my ($answer,$loncaparev); + my @ids=¤t_machine_ids(); + if (grep(/^\Q$lonhost\E$/,@ids)) { + $answer = $perlvar{'lonVersion'}; + if ($answer =~ /^[\'\"]?([\d.\-]+)[\'\"]?$/) { + $loncaparev = $1; + } + } else { + $answer = &reply('serverloncaparev',$lonhost); + if (($answer eq 'unknown_cmd') || ($answer eq 'con_lost')) { + if ($caller eq 'loncron') { + my $ua=new LWP::UserAgent; + $ua->timeout(20); + my $protocol = $protocol{$lonhost}; + $protocol = 'http' if ($protocol ne 'https'); + my $url = $protocol.'://'.&hostname($lonhost).'/adm/about.html'; + my $request=new HTTP::Request('GET',$url); + my $response=$ua->request($request); + unless ($response->is_error()) { + my $content = $response->content; + if ($content =~ /

VERSION\:\s*([\d.\-]+)<\/p>/) { + $loncaparev = $1; + } + } + } else { + $loncaparev = $loncaparevs{$lonhost}; + } + } elsif ($answer =~ /^[\'\"]?([\d.\-]+)[\'\"]?$/) { + $loncaparev = $1; + } + } + return &do_cache_new('serverloncaparev',$lonhost,$loncaparev,$cachetime); + } +} + +sub get_server_homeID { + my ($hostname,$ignore_cache,$caller) = @_; + unless ($ignore_cache) { + my ($serverhomeID,$cached)=&is_cached_new('serverhomeID',$hostname); if (defined($cached)) { - return $loncaparev; - } else { - my $loncaparev = &reply('serverloncaparev',$lonhost); - return &do_cache_new('serverloncaparev',$lonhost,$loncaparev,$cachetime); + return $serverhomeID; + } + } + my $cachetime = 12*3600; + my $serverhomeID; + if ($caller eq 'loncron') { + my @machine_ids = &machine_ids($hostname); + foreach my $id (@machine_ids) { + my $response = &reply('serverhomeID',$id); + unless (($response eq 'unknown_cmd') || ($response eq 'con_lost')) { + $serverhomeID = $response; + last; + } } + if ($serverhomeID eq '') { + $serverhomeID = $machine_ids[-1]; + } + } else { + $serverhomeID = $serverhomeIDs{$hostname}; } + return &do_cache_new('serverhomeID',$hostname,$serverhomeID,$cachetime); } # -------------------------------------------------- Non-critical communication @@ -734,7 +793,7 @@ sub compare_server_load { my $userloadans = &reply('userload',$try_server); if ($loadans !~ /\d/ && $userloadans !~ /\d/) { - next; #didn't get a number from the server + return; #didn't get a number from the server } my $load; @@ -777,6 +836,27 @@ sub has_user_session { return 0; } +# --------- determine least loaded server in a user's domain which allows login + +sub choose_server { + my ($udom) = @_; + my %domconfhash = &Apache::loncommon::get_domainconf($udom); + my %servers = &get_servers($udom); + my $lowest_load = 30000; + my ($login_host,$hostname); + foreach my $lonhost (keys(%servers)) { + my $loginvia = $domconfhash{$udom.'.login.loginvia_'.$lonhost}; + if ($loginvia eq '') { + ($login_host, $lowest_load) = + &compare_server_load($lonhost, $login_host, $lowest_load); + } + } + if ($login_host ne '') { + $hostname = $servers{$login_host}; + } + return ($login_host,$hostname); +} + # --------------------------------------------- Try to change a user's password sub changepass { @@ -835,7 +915,7 @@ sub queryauthenticate { # --------- Try to authenticate user from domain's lib servers (first this one) sub authenticate { - my ($uname,$upass,$udom,$checkdefauth)=@_; + my ($uname,$upass,$udom,$checkdefauth,$clientcancheckhost)=@_; $upass=&escape($upass); $uname= &LONCAPA::clean_username($uname); my $uhome=&homeserver($uname,$udom,1); @@ -858,7 +938,7 @@ sub authenticate { return 'no_host'; } } - my $answer=reply("encrypt:auth:$udom:$uname:$upass:$checkdefauth",$uhome); + my $answer=reply("encrypt:auth:$udom:$uname:$upass:$checkdefauth:$clientcancheckhost",$uhome); if ($answer eq 'authorized') { if ($newhome) { &logthis("User $uname at $udom authorized by $uhome, but needs account"); @@ -876,6 +956,64 @@ sub authenticate { return 'no_host'; } +sub can_host_session { + my ($udom,$lonhost,$remoterev,$remotesessions,$hostedsessions) = @_; + my $canhost = 1; + my $host_idn = &Apache::lonnet::internet_dom($lonhost); + if (ref($remotesessions) eq 'HASH') { + if (ref($remotesessions->{'excludedomain'}) eq 'ARRAY') { + if (grep(/^\Q$host_idn\E$/,@{$remotesessions->{'excludedomain'}})) { + $canhost = 0; + } else { + $canhost = 1; + } + } + if (ref($remotesessions->{'includedomain'}) eq 'ARRAY') { + if (grep(/^\Q$host_idn\E$/,@{$remotesessions->{'includedomain'}})) { + $canhost = 1; + } else { + $canhost = 0; + } + } + if ($canhost) { + if ($remotesessions->{'version'} ne '') { + my ($reqmajor,$reqminor) = ($remotesessions->{'version'} =~ /^(\d+)\.(\d+)$/); + if ($reqmajor ne '' && $reqminor ne '') { + if ($remoterev =~ /^\'?(\d+)\.(\d+)/) { + my $major = $1; + my $minor = $2; + if (($major < $reqmajor ) || + (($major == $reqmajor) && ($minor < $reqminor))) { + $canhost = 0; + } + } else { + $canhost = 0; + } + } + } + } + } + if ($canhost) { + if (ref($hostedsessions) eq 'HASH') { + if (ref($hostedsessions->{'excludedomain'}) eq 'ARRAY') { + if (grep(/^\Q$udom\E$/,@{$hostedsessions->{'excludedomain'}})) { + $canhost = 0; + } else { + $canhost = 1; + } + } + if (ref($hostedsessions->{'includedomain'}) eq 'ARRAY') { + if (grep(/^\Q$udom\E$/,@{$hostedsessions->{'includedomain'}})) { + $canhost = 1; + } else { + $canhost = 0; + } + } + } + } + return $canhost; +} + # ---------------------- Find the homebase for a user from domain's lib servers my %homecache; @@ -1352,7 +1490,7 @@ sub get_domain_defaults { my %domconfig = &Apache::lonnet::get_dom('configuration',['defaults','quotas', 'requestcourses','inststatus', - 'coursedefaults'],$domain); + 'coursedefaults','usersessions'],$domain); if (ref($domconfig{'defaults'}) eq 'HASH') { $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'}; @@ -1392,6 +1530,14 @@ sub get_domain_defaults { $domdefaults{$item} = $domconfig{'coursedefaults'}{$item}; } } + if (ref($domconfig{'usersessions'}) eq 'HASH') { + if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') { + $domdefaults{'remotesessions'} = $domconfig{'usersessions'}{'remote'}; + } + if (ref($domconfig{'usersessions'}{'hosted'}) eq 'HASH') { + $domdefaults{'hostedsessions'} = $domconfig{'usersessions'}{'hosted'}; + } + } &Apache::lonnet::do_cache_new('domdefaults',$domain,\%domdefaults, $cachetime); return %domdefaults; @@ -3059,7 +3205,7 @@ sub courseiddump { my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter, $coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok, $selfenrollonly,$catfilter,$showhidden,$caller,$cloner,$cc_clone, - $cloneonly,$createdbefore,$createdafter,$creationcontext)=@_; + $cloneonly,$createdbefore,$createdafter,$creationcontext,$domcloner)=@_; my $as_hash = 1; my %returnhash; if (!$domfilter) { $domfilter=''; } @@ -3081,7 +3227,8 @@ sub courseiddump { $showhidden.':'.$caller.':'.&escape($cloner).':'. &escape($cc_clone).':'.$cloneonly.':'. &escape($createdbefore).':'.&escape($createdafter).':'. - &escape($creationcontext),$tryserver); + &escape($creationcontext).':'.$domcloner, + $tryserver); my @pairs=split(/\&/,$rep); foreach my $item (@pairs) { my ($key,$value)=split(/\=/,$item,2); @@ -3905,9 +4052,10 @@ sub rolesinit { my ($domain,$username,$authhost)=@_; my $now=time; my %userroles = ('user.login.time' => $now); - my $rolesdump=reply("dump:$domain:$username:roles",$authhost); + my $extra = &freeze_escape({'clientcheckrole' => 1}); + my $rolesdump=reply("dump:$domain:$username:roles:.::$extra",$authhost); if (($rolesdump eq 'con_lost') || ($rolesdump eq '') || - ($rolesdump =~ /^error:/)) { + ($rolesdump =~ /^error:/)) { return \%userroles; } my %allroles=(); @@ -9861,6 +10009,14 @@ sub get_dns { my @uniq = grep(!$seen{$_}++, values(%hostdom)); return @uniq; } + + sub internet_dom { + &load_hosts_tab() if (!$loaded); + + my ($lonid) = @_; + return $internetdom{$lonid}; + } + } { @@ -9978,6 +10134,40 @@ sub get_dns { return undef; } + sub get_internet_names { + my ($lonid) = @_; + return if ($lonid eq ''); + my ($idnref,$cached)= + &Apache::lonnet::is_cached_new('internetnames',$lonid); + if ($cached) { + return $idnref; + } + my $ip = &get_host_ip($lonid); + my @hosts = &get_hosts_from_ip($ip); + my %iphost = &get_iphost(); + my (@idns,%seen); + foreach my $id (@hosts) { + my $dom = &host_domain($id); + my $prim_id = &domain($dom,'primary'); + my $prim_ip = &get_host_ip($prim_id); + next if ($seen{$prim_ip}); + if (ref($iphost{$prim_ip}) eq 'ARRAY') { + foreach my $id (@{$iphost{$prim_ip}}) { + my $intdom = &internet_dom($id); + unless (grep(/^\Q$intdom\E$/,@idns)) { + push(@idns,$intdom); + } + } + } + $seen{$prim_ip} = 1; + } + return &Apache::lonnet::do_cache_new('internetnames',$lonid,\@idns,12*60*60); + } + +} + +sub all_loncaparevs { + return qw(1.1 1.2 1.3 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10); } BEGIN { @@ -10055,6 +10245,34 @@ BEGIN { close($config); } +# ---------------------------------------------------------- Read loncaparev table +{ + if (-e "$perlvar{'lonTabDir'}/loncaparevs.tab") { + if (open(my $config,"<$perlvar{'lonTabDir'}/loncaparevs.tab")) { + while (my $configline=<$config>) { + chomp($configline); + my ($hostid,$loncaparev)=split(/:/,$configline); + $loncaparevs{$hostid}=$loncaparev; + } + close($config); + } + } +} + +# ---------------------------------------------------------- Read serverhostID table +{ + if (-e "$perlvar{'lonTabDir'}/serverhomeIDs.tab") { + if (open(my $config,"<$perlvar{'lonTabDir'}/serverhomeIDs.tab")) { + while (my $configline=<$config>) { + chomp($configline); + my ($name,$id)=split(/:/,$configline); + $serverhomeIDs{$name}=$id; + } + close($config); + } + } +} + # ------------- set up temporary directory { $tmpdir = $perlvar{'lonDaemons'}.'/tmp/'; @@ -10285,9 +10503,14 @@ authentication scheme =item * X -B: try to +B: try to authenticate user from domain's lib servers (first use the current one). C<$upass> should be the users password. +$checkdefauth is optional (value is 1 if a check should be made to + authenticate user using default authentication method, and allow + account creation if username does not have account in the domain). +$clientcancheckhost is optional (value is 1 if checking whether the + server can host will occur on the client side in lonauth.pm). =item * X