--- loncom/interface/domainprefs.pm 2020/09/17 00:35:04 1.372 +++ loncom/interface/domainprefs.pm 2021/04/18 02:08:46 1.381 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.372 2020/09/17 00:35:04 raeburn Exp $ +# $Id: domainprefs.pm,v 1.381 2021/04/18 02:08:46 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -176,6 +176,7 @@ use Locale::Language; use DateTime::TimeZone; use DateTime::Locale; use Time::HiRes qw( sleep ); +use Net::CIDR; my $registered_cleanup; my $modified_urls; @@ -220,7 +221,7 @@ sub handler { 'coursedefaults','usersessions','loadbalancing', 'requestauthor','selfenrollment','inststatus', 'ltitools','ssl','trust','lti','privacy','passwords', - 'proctoring'],$dom); + 'proctoring','wafproxy'],$dom); my %encconfig = &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring'],$dom); if (ref($domconfig{'ltitools'}) eq 'HASH') { @@ -259,11 +260,11 @@ sub handler { } } } - my @prefs_order = ('rolecolors','login','defaults','passwords','quotas','autoenroll', - 'autoupdate','autocreate','directorysrch','contacts','privacy', - 'usercreation','selfcreation','usermodification','scantron', - 'requestcourses','requestauthor','coursecategories', - 'serverstatuses','helpsettings','coursedefaults', + my @prefs_order = ('rolecolors','login','defaults','wafproxy','passwords','quotas', + 'autoenroll','autoupdate','autocreate','directorysrch', + 'contacts','privacy','usercreation','selfcreation', + 'usermodification','scantron','requestcourses','requestauthor', + 'coursecategories','serverstatuses','helpsettings','coursedefaults', 'ltitools','proctoring','selfenrollment','usersessions','ssl', 'trust','lti'); my %existing; @@ -310,6 +311,17 @@ sub handler { print => \&print_defaults, modify => \&modify_defaults, }, + 'wafproxy' => + { text => 'Web Application Firewall/Reverse Proxy', + help => 'Domain_Configuration_WAF_Proxy', + header => [{col1 => 'Domain(s)', + col2 => 'Servers and WAF/Reverse Proxy alias(es)', + }, + {col1 => 'Domain(s)', + col2 => 'WAF Configuration',}], + print => \&print_wafproxy, + modify => \&modify_wafproxy, + }, 'passwords' => { text => 'Passwords (Internal authentication)', help => 'Domain_Configuration_Passwords', @@ -805,6 +817,8 @@ sub process_changes { $output = &modify_privacy($dom,%domconfig); } elsif ($action eq 'passwords') { $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig); + } elsif ($action eq 'wafproxy') { + $output = &modify_wafproxy($dom,$action,$lastactref,%domconfig); } return $output; } @@ -841,6 +855,8 @@ sub print_config_box { $output .= <i_javascript($settings); } elsif ($action eq 'proctoring') { $output .= &proctoring_javascript($settings); + } elsif ($action eq 'wafproxy') { + $output .= &wafproxy_javascript($dom); } $output .= ' @@ -882,7 +898,7 @@ sub print_config_box { ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') || ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl') || ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') || - ($action eq 'contacts') || ($action eq 'privacy')) { + ($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'wafproxy')) { $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal); } elsif ($action eq 'passwords') { $output .= $item->{'print'}->('top',$dom,$confname,$settings,\$rowtotal); @@ -1005,7 +1021,7 @@ sub print_config_box { $rowtotal ++; } elsif (($action eq 'usermodification') || ($action eq 'coursedefaults') || ($action eq 'defaults') || ($action eq 'directorysrch') || - ($action eq 'helpsettings')) { + ($action eq 'helpsettings') || ($action eq 'wafproxy')) { $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); } elsif ($action eq 'scantron') { $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal); @@ -2830,6 +2846,121 @@ function toggleLTITools(form,setting,ite ENDSCRIPT } +sub wafproxy_javascript { + my ($dom) = @_; + return <<"ENDSCRIPT"; + + +ENDSCRIPT +} + sub proctoring_javascript { my ($settings) = @_; my (%ordered,$total,%jstext); @@ -3767,18 +3898,17 @@ sub print_contacts { \%choices,$rownum); $datatable .= $reports; } elsif ($position eq 'lower') { - $css_class = $rownum%2?' class="LC_odd_row"':''; - my ($threshold,$sysmail,%excluded,%weights); + my (%current,%excluded,%weights); my ($defaults,$names) = &Apache::loncommon::lon_status_items(); if ($lonstatus{'threshold'} =~ /^\d+$/) { - $threshold = $lonstatus{'threshold'}; + $current{'errorthreshold'} = $lonstatus{'threshold'}; } else { - $threshold = $defaults->{'threshold'}; + $current{'errorthreshold'} = $defaults->{'threshold'}; } if ($lonstatus{'sysmail'} =~ /^\d+$/) { - $sysmail = $lonstatus{'sysmail'}; + $current{'errorsysmail'} = $lonstatus{'sysmail'}; } else { - $sysmail = $defaults->{'sysmail'}; + $current{'errorsysmail'} = $defaults->{'sysmail'}; } if (ref($lonstatus{'weights'}) eq 'HASH') { foreach my $type ('E','W','N','U') { @@ -3798,13 +3928,16 @@ sub print_contacts { map {$excluded{$_} = 1; } @{$lonstatus{'excluded'}}; } } - $datatable .= ''. - ''; - $rownum ++; + foreach my $item ('errorthreshold','errorsysmail') { + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''; + $rownum ++; + } $css_class = $rownum%2?' class="LC_odd_row"':''; $datatable .= ''. '
'. - $titles->{'errorthreshold'}. - ''. - '
'. + $titles->{$item}. + ''. + '
'. @@ -3850,14 +3983,6 @@ sub print_contacts { } $datatable .= '
'; $rownum ++; - $css_class = $rownum%2?' class="LC_odd_row"':''; - $datatable .= ''. - ''. - $titles->{'errorsysmail'}. - ''. - ''; - $rownum ++; } elsif ($position eq 'bottom') { my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); my (@posstypes,%usertypeshash); @@ -6147,7 +6272,7 @@ sub print_coursedefaults { my $currcanclone = 'none'; my $onclick; my @cloneoptions = ('none','domain'); - my %clonetitles = ( + my %clonetitles = &Apache::lonlocal::texthash ( none => 'No additional course requesters', domain => "Any course requester in course's domain", instcode => 'Course requests for official courses ...', @@ -7164,6 +7289,262 @@ sub print_passwords { return $datatable; } +sub print_wafproxy { + my ($position,$dom,$settings,$rowtotal) = @_; + my $css_class; + my $itemcount = 0; + my $datatable; + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my (%othercontrol,%otherdoms,%aliases,%values,$setdom,$showdom); + my %lt = &wafproxy_titles(); + foreach my $server (sort(keys(%servers))) { + my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server}); + next if ($serverhome eq ''); + my $serverdom; + if ($serverhome ne $server) { + $serverdom = &Apache::lonnet::host_domain($serverhome); + if (($serverdom ne '') && (&Apache::lonnet::domain($serverdom) ne '')) { + $othercontrol{$server} = $serverdom; + } + } else { + $serverdom = &Apache::lonnet::host_domain($server); + next if (($serverdom eq '') || (&Apache::lonnet::domain($serverdom) eq '')); + if ($serverdom ne $dom) { + $othercontrol{$server} = $serverdom; + } else { + $setdom = 1; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'alias'}) eq 'HASH') { + $aliases{$dom} = $settings->{'alias'}; + if ($aliases{$dom} ne '') { + $showdom = 1; + } + } + } + } + } + } + if ($setdom) { + %{$values{$dom}} = (); + if (ref($settings) eq 'HASH') { + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { + $values{$dom}{$item} = $settings->{$item}; + } + } + } + if (keys(%othercontrol)) { + %otherdoms = reverse(%othercontrol); + foreach my $domain (keys(%otherdoms)) { + %{$values{$domain}} = (); + my %config = &Apache::lonnet::get_dom('configuration',['wafproxy'],$domain); + if (ref($config{$domain}) eq 'HASH') { + if (ref($config{$domain}{'wafproxy'}) eq 'HASH') { + $aliases{$domain} = $config{$domain}{'wafproxy'}{'alias'}; + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { + $values{$domain}{$item} = $config{$domain}{'wafproxy'}{$item}; + } + } + } + } + } + if ($position eq 'top') { + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %aliasinfo; + foreach my $server (sort(keys(%servers))) { + $itemcount ++; + my $dom_in_effect; + my $aliasrows = ''. + ''.&mt('Hostname').': '. + &Apache::lonnet::hostname($server).''; + if ($othercontrol{$server}) { + $dom_in_effect = $othercontrol{$server}; + my $current; + if (ref($aliases{$othercontrol{$server}}) eq 'HASH') { + $current = $aliases{$othercontrol{$server}{$server}}; + } + if ($current) { + $aliasrows .= $current; + } else { + $aliasrows .= &mt('None in effect'); + } + $aliasrows .= '('. + &mt('WAF/Reverse Proxy controlled by domain: [_1]', + ''.$othercontrol{$server}.'').''; + } else { + $dom_in_effect = $dom; + my $current; + if (ref($aliases{$dom}) eq 'HASH') { + if ($aliases{$dom}{$server}) { + $current = $aliases{$dom}{$server}; + } + } + $aliasrows .= ''.&mt('WAF/Reverse Proxy Alias').': '. + ''; + } + $aliasrows .= ''; + $aliasinfo{$dom_in_effect} .= $aliasrows; + } + if ($aliasinfo{$dom}) { + my ($onclick,$wafon,$wafoff,$showtable); + $onclick = ' onclick="javascript:toggleWAF();"'; + $wafoff = ' checked="checked"'; + $showtable = ' style="display:none";'; + if ($showdom) { + $wafon = $wafoff; + $wafoff = ''; + $showtable = ' style="display:inline;"'; + } + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable = ''. + ''.&mt('Domain: [_1]',''.$dom.'').'
'. + ''.&mt('WAF in use?').' '.(' 'x2).''. + ''. + ''.$aliasinfo{$dom}. + '
'; + $itemcount++; + } + if (keys(%othercontrol)) { + foreach my $key (sort(keys(%othercontrol))) { + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable = ''. + ''.&mt('Domain: [_1]',''.$key.'').''. + ''.$aliasinfo{$key}. + '
'; + $itemcount++; + } + } + } else { + if ($setdom) { + $itemcount ++; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + my ($nowafstyle,$wafstyle,$curr_remotip,$currwafdisplay,$vpndircheck,$vpnaliascheck, + $currwafvpn,$wafrangestyle); + $wafstyle = ' style="display:none;"'; + $nowafstyle = ' style="display:table-row;"'; + $currwafdisplay = ' style="display: none"'; + $wafrangestyle = ' style="display: none"'; + $curr_remotip = 'n'; + if ($showdom) { + $wafstyle = ' style="display:table-row;"'; + $nowafstyle = ' style="display:none;"'; + if (keys(%{$values{$dom}})) { + if ($values{$dom}{remoteip} =~ /^[nmh]$/) { + $curr_remotip = $values{$dom}{remoteip}; + } + if ($curr_remotip eq 'h') { + $currwafdisplay = ' style="display:table-row"'; + $wafrangestyle = ' style="display:inline-block;"'; + } + } + if (($values{$dom}{'vpnint'} ne '') || ($values{$dom}{'vpnext'} ne '')) { + $vpndircheck = ' checked="checked"'; + $currwafvpn = ' style="display:table-row;"'; + $wafrangestyle = ' style="display:inline-block;"'; + } else { + $vpnaliascheck = ' checked="checked"'; + $currwafvpn = ' style="display:none;"'; + } + } + $datatable .= ''. + ''.&mt('Domain: [_1]',''.$dom.'').''. + ''.&mt('WAF not in use, nothing to set').''. + ''. + ''. + ''.&mt('Domain: [_1]',''.$dom.'').'

'. + '
'.&mt('Format for comma separated IP ranges').':
'. + &mt('A.B.C.D/N or A.B.C.D-E.F.G.H').'
'. + ''. + ''. + ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''. + ''; + foreach my $item ('vpnint','vpnext') { + $datatable .= ''. + ''."\n"; + } + $datatable .= '
'.$lt{'remoteip'}.': '. + '
'. + $lt{'ipheader'}.': '. + ''. + '
'. + $lt{'trusted'}.': '. + ''. + '

'.$lt{'vpnaccess'}.':
'. + ''.(' 'x2). + '
'.$lt{$item}.': '. + ''. + '
'; + } + if (keys(%otherdoms)) { + foreach my $domain (sort(keys(%otherdoms))) { + $itemcount ++; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + ''.&mt('Domain: [_1]',$domain).''. + ''; + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { + my $showval = &mt('None'); + if ($values{$domain}{$item}) { + $showval = $values{$domain}{$item}; + } + $datatable .= ''. + ''; + } + $datatable .= '
'.$lt{$item}.': '.$showval.'
'; + } + } + } + $$rowtotal += $itemcount; + return $datatable; +} + +sub wafproxy_titles { + return &Apache::lonlocal::texthash( + remoteip => "Method for determining user's IP", + ipheader => 'Request header containing remote IP', + trusted => 'Trusted IP range(s)', + vpnaccess => 'Access from institutional VPN', + vpndirect => 'via regular hostname (no WAF)', + vpnaliased => 'via aliased hostname (WAF)', + vpnint => 'Internal IP Range(s) for VPN sessions', + vpnext => 'IP Range(s) for backend WAF connections', + ssloptions => 'Forwarding http/https', + alltossl => 'WAF forwards both http and https requests to https', + ssltossl => 'WAF forwards http requests to http and https to https', + ); +} + +sub remoteip_methods { + return &Apache::lonlocal::texthash( + m => 'Use Apache mod_remoteip', + h => 'Use headers parsed by LON-CAPA', + n => 'Not in use', + ); +} + sub print_usersessions { my ($position,$dom,$settings,$rowtotal) = @_; my ($css_class,$datatable,$itemcount,%checked,%choices); @@ -8199,8 +8580,8 @@ sub contact_titles { 'updatesmail' => 'E-mail from nightly check of LON-CAPA module integrity/updates', 'idconflictsmail' => 'E-mail from bi-nightly check for multiple users sharing same student/employee ID', 'hostipmail' => 'E-mail from nightly check of hostname/IP network changes', - 'errorthreshold' => 'Error/warning threshold for status e-mail', - 'errorsysmail' => 'Error threshold for e-mail to core group', + 'errorthreshold' => 'Error count threshold for status e-mail to admin(s)', + 'errorsysmail' => 'Error count threshold for e-mail to developer group', 'errorweights' => 'Weights used to compute error count', 'errorexcluded' => 'Servers with unsent updates excluded from count', ); @@ -9250,7 +9631,7 @@ sub print_defaults { $datatable .= ' '.&mt('Internal ID:').' '.$item.' '. ''. &mt('delete').''. - ''.&mt('Name displayed:'). + ''.&mt('Name displayed').':'. ''. ''; } @@ -9270,7 +9651,7 @@ sub print_defaults { ''. ' '.&mt('(new)'). ''. - &mt('Name displayed:'). + &mt('Name displayed').':'. ''. ''."\n"; $rownum ++; @@ -10279,7 +10660,7 @@ ENDSCRIPT sub initialize_categories { my ($itemcount) = @_; my ($datatable,$css_class,$chgstr); - my %default_names = ( + my %default_names = &Apache::lonlocal::texthash ( instcode => 'Official courses (with institutional codes)', communities => 'Communities', placement => 'Placement Tests', @@ -15054,7 +15435,7 @@ sub modify_contacts { $contacts_hash{'contacts'}{'lonstatus'}{$item} = \@excluded; } } elsif ($item eq 'weights') { - foreach my $type ('E','W','N') { + foreach my $type ('E','W','N','U') { $env{'form.error'.$item.'_'.$type} =~ s/^\s+|\s+$//g; if ($env{'form.error'.$item.'_'.$type} =~ /^\d+$/) { unless ($env{'form.error'.$item.'_'.$type} == $lonstatus_defs->{$type}) { @@ -16221,7 +16602,7 @@ sub modify_passwords { $resulttext .= '
  • '.&mt('E-mail address(es) in LON-CAPA used for verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'
  • '; } } else { - $resulttext .= '
  • '.&mt('E-mail address(es) in LON-CAPA usedfor verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'
  • '; + $resulttext .= '
  • '.&mt('E-mail address(es) in LON-CAPA used for verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'
  • '; } if ($confighash{'passwords'}{'resetremove'}) { $resulttext .= '
  • '.&mt('Preamble to "Forgot Password" web form not shown').'
  • '; @@ -19426,6 +19807,267 @@ sub modify_selfenrollment { return $resulttext; } +sub modify_wafproxy { + my ($dom,$action,$lastactref,%domconfig) = @_; + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my (%othercontrol,%canset,%values,%curralias,%currvalue,@warnings,%wafproxy, + %changes,%expirecache); + foreach my $server (sort(keys(%servers))) { + my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server}); + if ($serverhome eq $server) { + my $serverdom = &Apache::lonnet::host_domain($server); + if ($serverdom eq $dom) { + $canset{$server} = 1; + } + } + } + if (ref($domconfig{'wafproxy'}) eq 'HASH') { + %{$values{$dom}} = (); + if (ref($domconfig{'wafproxy'}{'alias'}) eq 'HASH') { + %curralias = %{$domconfig{'wafproxy'}{'alias'}}; + } + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { + $currvalue{$item} = $domconfig{'wafproxy'}{$item}; + } + } + my $output; + if (keys(%canset)) { + %{$wafproxy{'alias'}} = (); + foreach my $key (sort(keys(%canset))) { + if ($env{'form.wafproxy_'.$dom}) { + $wafproxy{'alias'}{$key} = $env{'form.wafproxy_alias_'.$key}; + $wafproxy{'alias'}{$key} =~ s/^\s+|\s+$//g; + if ($wafproxy{'alias'}{$key} ne $curralias{$key}) { + $changes{'alias'} = 1; + } + } else { + $wafproxy{'alias'}{$key} = ''; + if ($curralias{$key}) { + $changes{'alias'} = 1; + } + } + if ($wafproxy{'alias'}{$key} eq '') { + if ($curralias{$key}) { + $expirecache{$key} = 1; + } + delete($wafproxy{'alias'}{$key}); + } + } + unless (keys(%{$wafproxy{'alias'}})) { + delete($wafproxy{'alias'}); + } + # Localization for values in %warn occus in &mt() calls separately. + my %warn = ( + trusted => 'trusted IP range(s)', + vpnint => 'internal IP range(s) for VPN sessions(s)', + vpnext => 'IP range(s) for backend WAF connections', + ); + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { + my $possible = $env{'form.wafproxy_'.$item}; + $possible =~ s/^\s+|\s+$//g; + if ($possible ne '') { + if ($item eq 'remoteip') { + if ($possible =~ /^[mhn]$/) { + $wafproxy{$item} = $possible; + } + } elsif ($item eq 'ipheader') { + if ($wafproxy{'remoteip'} eq 'h') { + $wafproxy{$item} = $possible; + } + } else { + my (@ok,$count); + if (($item eq 'vpnint') || ($item eq 'vpnext')) { + unless ($env{'form.wafproxy_vpnaccess'}) { + $possible = ''; + } + } elsif ($item eq 'trusted') { + unless ($wafproxy{'remoteip'} eq 'h') { + $possible = ''; + } + } + unless ($possible eq '') { + $possible =~ s/[\r\n]+/\s/g; + $possible =~ s/\s*-\s*/-/g; + $possible =~ s/\s+/,/g; + } + $count = 0; + if ($possible ne '') { + foreach my $poss (split(/\,/,$possible)) { + $count ++; + if (&validate_ip_pattern($poss)) { + push(@ok,$poss); + } + } + if (@ok) { + $wafproxy{$item} = join(',',@ok); + } + my $diff = $count - scalar(@ok); + if ($diff) { + push(@warnings,'
  • '. + &mt('[quant,_1,IP] invalid and excluded from saved value for [_2]', + $diff,$warn{$item}). + '
  • '); + } + } + } + if ($wafproxy{$item} ne $currvalue{$item}) { + $changes{$item} = 1; + } + } elsif ($currvalue{$item}) { + $changes{$item} = 1; + } + } + } else { + if (keys(%curralias)) { + $changes{'alias'} = 1; + } + if (keys(%currvalue)) { + foreach my $key (keys(%currvalue)) { + $changes{$key} = 1; + } + } + } + if (keys(%changes)) { + my %defaultshash = ( + wafproxy => \%wafproxy, + ); + my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, + $dom); + if ($putresult eq 'ok') { + my $cachetime = 24*60*60; + my (%domdefaults,$updatedomdefs); + foreach my $item ('ipheader','trusted','vpnint','vpnext') { + if ($changes{$item}) { + unless ($updatedomdefs) { + %domdefaults = &Apache::lonnet::get_domain_defaults($dom); + $updatedomdefs = 1; + } + if ($wafproxy{$item}) { + $domdefaults{'waf_'.$item} = $wafproxy{$item}; + } elsif (exists($domdefaults{'waf_'.$item})) { + delete($domdefaults{'waf_'.$item}); + } + } + } + if ($updatedomdefs) { + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } + if ((exists($wafproxy{'alias'})) || (keys(%expirecache))) { + my %updates = %expirecache; + foreach my $key (keys(%expirecache)) { + &Apache::lonnet::devalidate_cache_new('proxyalias',$key); + } + if (ref($wafproxy{'alias'}) eq 'HASH') { + my $cachetime = 24*60*60; + foreach my $key (keys(%{$wafproxy{'alias'}})) { + $updates{$key} = 1; + &Apache::lonnet::do_cache_new('proxyalias',$key,$wafproxy{'alias'}{$key}, + $cachetime); + } + } + if (ref($lastactref) eq 'HASH') { + $lastactref->{'proxyalias'} = \%updates; + } + } + $output = &mt('Changes were made to Web Application Firewall/Reverse Proxy').'
      '; + foreach my $item ('alias','remoteip','ipheader','trusted','vpnint','vpnext') { + if ($changes{$item}) { + if ($item eq 'alias') { + my $numaliased = 0; + if (ref($wafproxy{'alias'}) eq 'HASH') { + my $shown; + if (keys(%{$wafproxy{'alias'}})) { + foreach my $server (sort(keys(%{$wafproxy{'alias'}}))) { + $shown .= '
    • '.&mt('[_1] aliased by [_2]', + &Apache::lonnet::hostname($server), + $wafproxy{'alias'}{$server}).'
    • '; + $numaliased ++; + } + if ($numaliased) { + $output .= '
    • '.&mt('Aliases for hostnames set to: [_1]', + '
        '.$shown.'
      ').'
    • '; + } + } + } + unless ($numaliased) { + $output .= '
    • '.&mt('Aliases deleted for hostnames').'
    • '; + } + } else { + if ($item eq 'remoteip') { + my %ip_methods = &remoteip_methods(); + if ($wafproxy{$item} =~ /^[mh]$/) { + $output .= '
    • '.&mt("Method for determining user's IP set to: [_1]", + $ip_methods{$wafproxy{$item}}).'
    • '; + } else { + if (($env{'form.wafproxy_'.$dom}) && (ref($wafproxy{'alias'}) eq 'HASH')) { + $output .= '
    • '.&mt("No method in use to get user's real IP (will report IP used by WAF)."). + '
    • '; + } else { + $output .= '
    • '.&mt('WAF/Reverse Proxy not in use').'
    • '; + } + } + } elsif ($item eq 'ipheader') { + if ($wafproxy{$item}) { + $output .= '
    • '.&mt('Request header with remote IP set to: [_1]', + $wafproxy{$item}).'
    • '; + } else { + $output .= '
    • '.&mt('Request header with remote IP deleted').'
    • '; + } + } elsif ($item eq 'trusted') { + if ($wafproxy{$item}) { + $output .= '
    • '.&mt('Trusted IP range(s) set to: [_1]', + $wafproxy{$item}).'
    • '; + } else { + $output .= '
    • '.&mt('Trusted IP range(s) deleted').'
    • '; + } + } elsif ($item eq 'vpnint') { + if ($wafproxy{$item}) { + $output .= '
    • '.&mt('Internal IP Range(s) for VPN sessions set to: [_1]', + $wafproxy{$item}).'
    • '; + } else { + $output .= '
    • '.&mt('Internal IP Range(s) for VPN sessions deleted').'
    • '; + } + } elsif ($item eq 'vpnext') { + if ($wafproxy{$item}) { + $output .= '
    • '.&mt('IP Range(s) for backend WAF connections set to: [_1]', + $wafproxy{$item}).'
    • '; + } else { + $output .= '
    • '.&mt('IP Range(s) for backend WAF connections deleted').'
    • '; + } + } + } + } + } + } else { + $output = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + } elsif (keys(%canset)) { + $output = &mt('No changes made to Web Application Firewall/Reverse Proxy settings'); + } + if (@warnings) { + $output .= '
      '.&mt('Warnings:').'
        '. + join("\n",@warnings).'
      '; + } + return $output; +} + +sub validate_ip_pattern { + my ($pattern) = @_; + if ($pattern =~ /^([^-]+)\-([^-]+)$/) { + my ($start,$end) = ($1,$2); + if ((&Net::CIDR::cidrvalidate($start)) && (&Net::CIDR::cidrvalidate($end))) { + return 1; + } + } elsif (&Net::CIDR::cidrvalidate($pattern)) { + return 1; + } + return +} + sub modify_usersessions { my ($dom,$lastactref,%domconfig) = @_; my @hostingtypes = ('version','excludedomain','includedomain'); @@ -20362,24 +21004,24 @@ sub modify_loadbalancing { $resulttext .= '
    • '.&mt('Load Balancer: [_1] -- cookie use enabled', $balancer).'
    • '; } - if (keys(%toupdate)) { - my %thismachine; - my $updatedhere; - my $cachetime = 60*60*24; - map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); - foreach my $lonhost (keys(%toupdate)) { - if ($thismachine{$lonhost}) { - unless ($updatedhere) { - &Apache::lonnet::do_cache_new('loadbalancing',$dom, - $defaultshash{'loadbalancing'}, - $cachetime); - $updatedhere = 1; - } - } else { - my $cachekey = &escape('loadbalancing').':'.&escape($dom); - &Apache::lonnet::remote_devalidate_cache($lonhost,[$cachekey]); - } + } + } + if (keys(%toupdate)) { + my %thismachine; + my $updatedhere; + my $cachetime = 60*60*24; + map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); + foreach my $lonhost (keys(%toupdate)) { + if ($thismachine{$lonhost}) { + unless ($updatedhere) { + &Apache::lonnet::do_cache_new('loadbalancing',$dom, + $defaultshash{'loadbalancing'}, + $cachetime); + $updatedhere = 1; } + } else { + my $cachekey = &escape('loadbalancing').':'.&escape($dom); + &Apache::lonnet::remote_devalidate_cache($lonhost,[$cachekey]); } } } @@ -21140,14 +21782,22 @@ sub devalidate_remote_domconfs { my %thismachine; map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); my @posscached = ('domainconfig','domdefaults','ltitools','usersessions', - 'directorysrch','passwdconf','cats'); + 'directorysrch','passwdconf','cats','proxyalias'); if (keys(%servers)) { foreach my $server (keys(%servers)) { next if ($thismachine{$server}); my @cached; foreach my $name (@posscached) { if ($cachekeys->{$name}) { - push(@cached,&escape($name).':'.&escape($dom)); + if ($name eq 'proxyalias') { + if (ref($cachekeys->{$name}) eq 'HASH') { + foreach my $key (keys(%{$cachekeys->{$name}})) { + push(@cached,&escape($name).':'.&escape($key)); + } + } + } else { + push(@cached,&escape($name).':'.&escape($dom)); + } } } if (@cached) {