--- loncom/interface/domainprefs.pm 2021/09/01 00:21:52 1.385 +++ loncom/interface/domainprefs.pm 2021/10/07 15:51:15 1.388 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.385 2021/09/01 00:21:52 raeburn Exp $ +# $Id: domainprefs.pm,v 1.388 2021/10/07 15:51:15 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -297,7 +297,10 @@ sub handler { {col1 => 'Log-in Help', col2 => 'Value'}, {col1 => 'Custom HTML in document head', - col2 => 'Value'}], + col2 => 'Value'}, + {col1 => 'SSO', + col2 => 'Dual login: SSO and non-SSO options'}, + ], print => \&print_login, modify => \&modify_login, }, @@ -632,7 +635,10 @@ sub handler { {col1 => 'Log-in Help', col2 => 'Value'}, {col1 => 'Custom HTML in document head', - col2 => 'Value'}], + col2 => 'Value'}, + {col1 => 'SSO', + col2 => 'Dual login: SSO and non-SSO options'}, + ], print => \&print_login, modify => \&modify_login, }; @@ -859,6 +865,8 @@ sub print_config_box { $output .= &wafproxy_javascript($dom); } elsif ($action eq 'autoupdate') { $output .= &autoupdate_javascript(); + } elsif ($action eq 'login') { + $output .= &saml_javascript(); } $output .= ' @@ -878,7 +886,7 @@ sub print_config_box { my $leftnobr = ''; if (($action eq 'rolecolors') || ($action eq 'defaults') || ($action eq 'directorysrch') || - (($action eq 'login') && ($numheaders < 4))) { + (($action eq 'login') && ($numheaders < 5))) { $colspan = ' colspan="2"'; } if ($action eq 'usersessions') { @@ -909,7 +917,7 @@ sub print_config_box { } elsif ($action eq 'scantron') { $output .= $item->{'print'}->($r,'top',$dom,$confname,$settings,\$rowtotal); } elsif ($action eq 'login') { - if ($numheaders == 4) { + if ($numheaders == 5) { $colspan = ' colspan="2"'; $output .= &print_login('service',$dom,$confname,$phase,$settings,\$rowtotal); } else { @@ -1050,7 +1058,7 @@ sub print_config_box { '. $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); } elsif ($action eq 'login') { - if ($numheaders == 4) { + if ($numheaders == 5) { $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'
'.&mt($item->{'header'}->[3]->{'col2'}).'
@@ -1074,7 +1082,7 @@ sub print_config_box { '; - if ($numheaders == 4) { + if ($numheaders == 5) { $output .= ' @@ -1086,7 +1094,27 @@ sub print_config_box { '; } $rowtotal ++; - $output .= &print_login('headtag',$dom,$confname,$phase,$settings,\$rowtotal); + $output .= &print_login('headtag',$dom,$confname,$phase,$settings,\$rowtotal).' +
'.&mt($item->{'header'}->[3]->{'col1'}).' '.&mt($item->{'header'}->[3]->{'col2'}).'
+ + + + + + '; + if ($numheaders == 5) { + $output .= ' + + + '; + } else { + $output .= ' + + + '; + } + $rowtotal ++; + $output .= &print_login('saml',$dom,$confname,$phase,$settings,\$rowtotal); } elsif ($action eq 'requestcourses') { $output .= &print_requestmail($dom,$action,$settings,\$rowtotal); $rowtotal ++; @@ -1221,9 +1249,12 @@ sub print_config_box { sub print_login { my ($caller,$dom,$confname,$phase,$settings,$rowtotal) = @_; - my ($css_class,$datatable); + my ($css_class,$datatable,$switchserver,%lt); my %choices = &login_choices(); - + if (($caller eq 'help') || ($caller eq 'headtag') || ($caller eq 'saml')) { + %lt = &login_file_options(); + $switchserver = &check_switchserver($dom,$confname); + } if ($caller eq 'service') { my %servers = &Apache::lonnet::internet_dom_servers($dom); my $choice = $choices{'disallowlogin'}; @@ -1417,18 +1448,10 @@ sub print_login { $datatable .= &display_color_options($dom,$confname,$phase,'login',$itemcount,\%choices,\%is_custom,\%defaults,\%designs,\@images,\@bgs,\@links,\%alt_text,$rowtotal,\@logintext); $datatable .= '
'.&mt($item->{'header'}->[4]->{'col1'}).''.&mt($item->{'header'}->[4]->{'col2'}).'
'.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col2'}).'
'; } elsif ($caller eq 'help') { - my ($defaulturl,$defaulttype,%url,%type,%lt,%langchoices); - my $switchserver = &check_switchserver($dom,$confname); + my ($defaulturl,$defaulttype,%url,%type,%langchoices); my $itemcount = 1; $defaulturl = '/adm/loginproblems.html'; $defaulttype = 'default'; - %lt = &Apache::lonlocal::texthash ( - del => 'Delete?', - rep => 'Replace:', - upl => 'Upload:', - default => 'Default', - custom => 'Custom', - ); %langchoices = &Apache::lonlocal::texthash(&get_languages_hash()); my @currlangs; if (ref($settings) eq 'HASH') { @@ -1525,14 +1548,6 @@ sub print_login { } } } - my %lt = &Apache::lonlocal::texthash( - del => 'Delete?', - rep => 'Replace:', - upl => 'Upload:', - curr => 'View contents', - none => 'None', - ); - my $switchserver = &check_switchserver($dom,$confname); foreach my $lonhost (sort(keys(%domservers))) { my $exempt = &check_exempt_addresses($currexempt{$lonhost}); $datatable .= ''.$domservers{$lonhost}.''; @@ -1556,6 +1571,88 @@ sub print_login { $datatable .= ''; } $datatable .= ''; + } elsif ($caller eq 'saml') { + my %domservers = &Apache::lonnet::get_servers($dom); + $datatable .= ''. + ''. + ''. + ''."\n"; + my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso,%styleon,%styleoff); + foreach my $lonhost (keys(%domservers)) { + $samlurl{$lonhost} = '/adm/sso'; + $styleon{$lonhost} = 'display:none'; + $styleoff{$lonhost} = ''; + } + if (ref($settings->{'saml'}) eq 'HASH') { + foreach my $lonhost (keys(%{$settings->{'saml'}})) { + if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') { + $saml{$lonhost} = 1; + $samltext{$lonhost} = $settings->{'saml'}{$lonhost}{'text'}; + $samlimg{$lonhost} = $settings->{'saml'}{$lonhost}{'img'}; + $samlalt{$lonhost} = $settings->{'saml'}{$lonhost}{'alt'}; + $samlurl{$lonhost} = $settings->{'saml'}{$lonhost}{'url'}; + $samltitle{$lonhost} = $settings->{'saml'}{$lonhost}{'title'}; + $samlnotsso{$lonhost} = $settings->{'saml'}{$lonhost}{'notsso'}; + $styleon{$lonhost} = ''; + $styleoff{$lonhost} = 'display:none'; + } else { + $styleon{$lonhost} = 'display:none'; + $styleoff{$lonhost} = ''; + } + } + } + my $itemcount = 1; + foreach my $lonhost (sort(keys(%domservers))) { + my $samlon = ' '; + my $samloff = ' checked="checked" '; + if ($saml{$lonhost}) { + $samlon = $samloff; + $samloff = ' '; + } + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''. + ''. + ''; + $itemcount ++; + } + $datatable .= '
'.$choices{'hostid'}.''.$choices{'samllanding'}.''.$choices{'samloptions'}.'
'.$domservers{$lonhost}.''.(' 'x2). + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '
'.&mt('SSO').''. + ''.&mt('Non-SSO').'
'.&mt('Text').''.&mt('Image').''.&mt('Alt Text').''.&mt('URL').''.&mt('Tool Tip').''.&mt('Text').'
'; + if ($samlimg{$lonhost}) { + $datatable .= '
'. + ' '.$lt{'rep'}.''; + } else { + $datatable .= $lt{'upl'}; + } + $datatable .='
'; + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= ''; + } + $datatable .= '
 
'; } return $datatable; } @@ -1592,10 +1689,24 @@ sub login_choices { headtag => "Custom markup", action => "Action", current => "Current", + samllanding => "Dual login?", + samloptions => "Options", ); return %choices; } +sub login_file_options { + return &Apache::lonlocal::texthash( + del => 'Delete?', + rep => 'Replace:', + upl => 'Upload:', + curr => 'View contents', + default => 'Default', + custom => 'Custom', + none => 'None', + ); +} + sub print_rolecolors { my ($phase,$role,$dom,$confname,$settings,$rowtotal) = @_; my %choices = &color_font_choices(); @@ -3348,6 +3459,48 @@ function toggleLastActiveDays(form) { ENDSCRIPT } +sub saml_javascript { + return <<"ENDSCRIPT"; + + +ENDSCRIPT +} + sub print_autoenroll { my ($dom,$settings,$rowtotal) = @_; my $autorun = &Apache::lonnet::auto_run(undef,$dom), @@ -7365,7 +7518,7 @@ sub print_wafproxy { my $itemcount = 0; my $datatable; my %servers = &Apache::lonnet::internet_dom_servers($dom); - my (%othercontrol,%otherdoms,%aliases,%values,$setdom,$showdom); + my (%othercontrol,%otherdoms,%aliases,%saml,%values,$setdom,$showdom); my %lt = &wafproxy_titles(); foreach my $server (sort(keys(%servers))) { my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server}); @@ -7390,6 +7543,9 @@ sub print_wafproxy { $showdom = 1; } } + if (ref($settings->{'saml'}) eq 'HASH') { + $saml{$dom} = $settings->{'saml'}; + } } } } @@ -7409,6 +7565,9 @@ sub print_wafproxy { my %config = &Apache::lonnet::get_dom('configuration',['wafproxy'],$domain); if (ref($config{'wafproxy'}) eq 'HASH') { $aliases{$domain} = $config{'wafproxy'}{'alias'}; + if (exists($config{'wafproxy'}{'saml'})) { + $saml{$domain} = $config{'wafproxy'}{'saml'}; + } foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { $values{$domain}{$item} = $config{'wafproxy'}{$item}; } @@ -7427,14 +7586,22 @@ sub print_wafproxy { ''.&Apache::lonnet::hostname($server).' '; if ($othercontrol{$server}) { $dom_in_effect = $othercontrol{$server}; - my $current; + my ($current,$forsaml); if (ref($aliases{$dom_in_effect}) eq 'HASH') { $current = $aliases{$dom_in_effect}{$server}; } + if (ref($saml{$dom_in_effect}) eq 'HASH') { + if ($saml{$dom_in_effect}{$server}) { + $forsaml = 1; + } + } $aliasrows .= ''. &mt('Alias').': '; if ($current) { $aliasrows .= $current; + if ($forsaml) { + $aliasrows .= ' ('.&mt('also for Shibboleth').')'; + } } else { $aliasrows .= &mt('None'); } @@ -7443,16 +7610,30 @@ sub print_wafproxy { ''.$dom_in_effect.'').')'; } else { $dom_in_effect = $dom; - my $current; + my ($current,$samlon,$samloff); + $samloff = ' checked="checked"'; if (ref($aliases{$dom}) eq 'HASH') { if ($aliases{$dom}{$server}) { $current = $aliases{$dom}{$server}; } } + if (ref($saml{$dom}) eq 'HASH') { + if ($saml{$dom}{$server}) { + $samlon = $samloff; + undef($samloff); + } + } $aliasrows .= ''. &mt('Alias').': '. ''; + 'value="'.$current.'" size="30" />'. + (' 'x2).''. + &mt('Alias used for Shibboleth').':  '. + ''; } $aliasrows .= ''; $aliasinfo{$dom_in_effect} .= $aliasrows; @@ -11251,12 +11432,14 @@ sub usertype_update_row { sub modify_login { my ($r,$dom,$confname,$lastactref,%domconfig) = @_; my ($resulttext,$errors,$colchgtext,%changes,%colchanges,%newfile,%newurl, - %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon); + %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon, + %currsaml,%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso); %title = ( coursecatalog => 'Display course catalog', adminmail => 'Display administrator E-mail address', helpdesk => 'Display "Contact Helpdesk" link', newuser => 'Link for visitors to create a user account', - loginheader => 'Log-in box header'); + loginheader => 'Log-in box header', + saml => 'Dual SSO and non-SSO login'); @offon = ('off','on'); if (ref($domconfig{login}) eq 'HASH') { if (ref($domconfig{login}{loginvia}) eq 'HASH') { @@ -11264,6 +11447,20 @@ sub modify_login { $curr_loginvia{$lonhost} = $domconfig{login}{loginvia}{$lonhost}; } } + if (ref($domconfig{login}{'saml'}) eq 'HASH') { + foreach my $lonhost (keys(%{$domconfig{login}{'saml'}})) { + if (ref($domconfig{login}{'saml'}{$lonhost}) eq 'HASH') { + $currsaml{$lonhost} = $domconfig{login}{'saml'}{$lonhost}; + $saml{$lonhost} = 1; + $samltext{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'text'}; + $samlurl{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'url'}; + $samlalt{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'alt'}; + $samlimg{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'img'}; + $samltitle{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'title'}; + $samlnotsso{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'notsso'}; + } + } + } } ($errors,%colchanges) = &modify_colors($r,$dom,$confname,['login'], \%domconfig,\%loginhash); @@ -11510,6 +11707,86 @@ sub modify_login { $errors .= '
  • '.$error.'
  • '; } } + my @delsamlimg = &Apache::loncommon::get_env_multiple('form.saml_img_del'); + my @newsamlimgs; + foreach my $lonhost (keys(%domservers)) { + if ($env{'form.saml_'.$lonhost}) { + if ($env{'form.saml_img_'.$lonhost.'.filename'}) { + push(@newsamlimgs,$lonhost); + } + foreach my $item ('text','alt','url','title','notsso') { + $env{'form.saml_'.$item.'_'.$lonhost} =~ s/^\s+|\s+$//g; + } + if ($saml{$lonhost}) { + if (grep(/^\Q$lonhost\E$/,@delsamlimg)) { +#FIXME Need to obsolete published image + delete($currsaml{$lonhost}{'img'}); + $changes{'saml'}{$lonhost} = 1; + } + if ($env{'form.saml_alt_'.$lonhost} ne $samlalt{$lonhost}) { + $changes{'saml'}{$lonhost} = 1; + } + if ($env{'form.saml_text_'.$lonhost} ne $samltext{$lonhost}) { + $changes{'saml'}{$lonhost} = 1; + } + if ($env{'form.saml_url_'.$lonhost} ne $samlurl{$lonhost}) { + $changes{'saml'}{$lonhost} = 1; + } + if ($env{'form.saml_title_'.$lonhost} ne $samltitle{$lonhost}) { + $changes{'saml'}{$lonhost} = 1; + } + if ($env{'form.saml_notsso_'.$lonhost} ne $samlnotsso{$lonhost}) { + $changes{'saml'}{$lonhost} = 1; + } + } else { + $changes{'saml'}{$lonhost} = 1; + } + foreach my $item ('text','alt','url','title','notsso') { + $currsaml{$lonhost}{$item} = $env{'form.saml_'.$item.'_'.$lonhost}; + } + } else { + delete($currsaml{$lonhost}); + } + } + foreach my $posshost (keys(%currsaml)) { + unless (exists($domservers{$posshost})) { + delete($currsaml{$posshost}); + } + } + %{$loginhash{'login'}{'saml'}} = %currsaml; + if (@newsamlimgs) { + my $error; + my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); + if ($configuserok eq 'ok') { + if ($switchserver) { + $error = &mt("Upload of SSO Button Image is not permitted to this server: [_1].",$switchserver); + } elsif ($author_ok eq 'ok') { + foreach my $lonhost (@newsamlimgs) { + my $formelem = 'saml_img_'.$lonhost; + my ($result,$imgurl) = &publishlogo($r,'upload',$formelem,$dom,$confname, + "login/saml/$lonhost",'','', + $env{'form.saml_img_'.$lonhost.'.filename'}); + if ($result eq 'ok') { + $currsaml{$lonhost}{'img'} = $imgurl; + $loginhash{'login'}{'saml'}{$lonhost}{'img'} = $imgurl; + $changes{'saml'}{$lonhost} = 1; + } else { + my $puberror = &mt("Upload of SSO button image failed for [_1] because an error occurred publishing the file in RES space. Error was: [_2].", + $lonhost,$result); + $errors .= '
  • '.$puberror.'
  • '; + } + } + } else { + $error = &mt("Upload of SSO button image file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2]. Error was: [_3].",$confname,$dom,$author_ok); + } + } else { + $error = &mt("Upload of SSO button image file(s) failed because a Domain Configuration user ([_1]) could not be created in domain: [_2]. Error was: [_3].",$confname,$dom,$configuserok); + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
  • '.$error.'
  • '; + } + } &process_captcha('login',\%changes,$loginhash{'login'},$domconfig{'login'}); my $defaulthelpfile = '/adm/loginproblems.html'; @@ -11550,6 +11827,31 @@ sub modify_login { } if (keys(%changes) > 0 || $colchgtext) { &Apache::loncommon::devalidate_domconfig_cache($dom); + if (exists($changes{'saml'})) { + my $hostid_in_use; + my @hosts = &Apache::lonnet::current_machine_ids(); + if (@hosts > 1) { + foreach my $hostid (@hosts) { + if (&Apache::lonnet::host_domain($hostid) eq $dom) { + $hostid_in_use = $hostid; + last; + } + } + } else { + $hostid_in_use = $r->dir_config('lonHostID'); + } + if (($hostid_in_use) && + (&Apache::lonnet::host_domain($hostid_in_use) eq $dom)) { + &Apache::lonnet::devalidate_cache_new('samllanding',$hostid_in_use); + } + if (ref($lastactref) eq 'HASH') { + if (ref($changes{'saml'}) eq 'HASH') { + my %updates; + map { $updates{$_} = 1; } keys(%{$changes{'saml'}}); + $lastactref->{'samllanding'} = \%updates; + } + } + } if (ref($lastactref) eq 'HASH') { $lastactref->{'domainconfig'} = 1; } @@ -11629,6 +11931,38 @@ sub modify_login { } } } + } elsif ($item eq 'saml') { + if (ref($changes{$item}) eq 'HASH') { + my %notlt = ( + text => 'Text for log-in by SSO', + img => 'SSO button image', + alt => 'Alt text for button image', + url => 'SSO URL', + title => 'Tooltip for SSO link', + notsso => 'Text for non-SSO log-in', + ); + foreach my $lonhost (sort(keys(%{$changes{$item}}))) { + if (ref($currsaml{$lonhost}) eq 'HASH') { + $resulttext .= '
  • '.&mt("$title{$item} in use for [_1]","$lonhost"). + '
  • '; + } else { + $resulttext .= '
  • '.&mt("$title{$item} not in use for [_1]",$lonhost).'
  • '; + } + } + } } elsif ($item eq 'captcha') { if (ref($loginhash{'login'}) eq 'HASH') { my $chgtxt; @@ -19930,8 +20264,8 @@ sub modify_selfenrollment { 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); + my (%othercontrol,%canset,%values,%curralias,%currsaml,%currvalue,@warnings, + %wafproxy,%changes,%expirecache,%expiresaml); foreach my $server (sort(keys(%servers))) { my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server}); if ($serverhome eq $server) { @@ -19946,6 +20280,9 @@ sub modify_wafproxy { if (ref($domconfig{'wafproxy'}{'alias'}) eq 'HASH') { %curralias = %{$domconfig{'wafproxy'}{'alias'}}; } + if (ref($domconfig{'wafproxy'}{'saml'}) eq 'HASH') { + %currsaml = %{$domconfig{'wafproxy'}{'saml'}}; + } foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext','sslopt') { $currvalue{$item} = $domconfig{'wafproxy'}{$item}; } @@ -19953,6 +20290,7 @@ sub modify_wafproxy { my $output; if (keys(%canset)) { %{$wafproxy{'alias'}} = (); + %{$wafproxy{'saml'}} = (); foreach my $key (sort(keys(%canset))) { if ($env{'form.wafproxy_'.$dom}) { $wafproxy{'alias'}{$key} = $env{'form.wafproxy_alias_'.$key}; @@ -19960,11 +20298,21 @@ sub modify_wafproxy { if ($wafproxy{'alias'}{$key} ne $curralias{$key}) { $changes{'alias'} = 1; } + if ($env{'form.wafproxy_alias_saml_'.$key}) { + $wafproxy{'saml'}{$key} = 1; + } + if ($wafproxy{'saml'}{$key} ne $currsaml{$key}) { + $changes{'saml'} = 1; + } } else { $wafproxy{'alias'}{$key} = ''; + $wafproxy{'saml'}{$key} = ''; if ($curralias{$key}) { $changes{'alias'} = 1; } + if ($currsaml{$key}) { + $changes{'saml'} = 1; + } } if ($wafproxy{'alias'}{$key} eq '') { if ($curralias{$key}) { @@ -19972,11 +20320,20 @@ sub modify_wafproxy { } delete($wafproxy{'alias'}{$key}); } + if ($wafproxy{'saml'}{$key} eq '') { + if ($currsaml{$key}) { + $expiresaml{$key} = 1; + } + delete($wafproxy{'saml'}{$key}); + } } unless (keys(%{$wafproxy{'alias'}})) { delete($wafproxy{'alias'}); } - # Localization for values in %warn occus in &mt() calls separately. + unless (keys(%{$wafproxy{'saml'}})) { + delete($wafproxy{'saml'}); + } + # Localization for values in %warn occurs in &mt() calls separately. my %warn = ( trusted => 'trusted IP range(s)', vpnint => 'internal IP range(s) for VPN sessions(s)', @@ -20044,6 +20401,9 @@ sub modify_wafproxy { } else { if (keys(%curralias)) { $changes{'alias'} = 1; + } + if (keys(%currsaml)) { + $changes{'saml'} = 1; } if (keys(%currvalue)) { foreach my $key (keys(%currvalue)) { @@ -20096,6 +20456,23 @@ sub modify_wafproxy { $lastactref->{'proxyalias'} = \%updates; } } + if ((exists($wafproxy{'saml'})) || (keys(%expiresaml))) { + my %samlupdates = %expiresaml; + foreach my $key (keys(%expiresaml)) { + &Apache::lonnet::devalidate_cache_new('proxysaml',$key); + } + if (ref($wafproxy{'saml'}) eq 'HASH') { + my $cachetime = 24*60*60; + foreach my $key (keys(%{$wafproxy{'saml'}})) { + $samlupdates{$key} = 1; + &Apache::lonnet::do_cache_new('proxysaml',$key,$wafproxy{'saml'}{$key}, + $cachetime); + } + } + if (ref($lastactref) eq 'HASH') { + $lastactref->{'proxysaml'} = \%samlupdates; + } + } $output = &mt('Changes were made to Web Application Firewall/Reverse Proxy').'