浏览代码

Update intruder client references.

Jason Rivard 4 年之前
父节点
当前提交
d575c073a9
共有 36 个文件被更改,包括 452 次插入242 次删除
  1. 17 5
      server/src/main/java/password/pwm/PwmApplication.java
  2. 26 2
      server/src/main/java/password/pwm/health/HealthService.java
  3. 16 1
      server/src/main/java/password/pwm/http/HttpEventManager.java
  4. 2 1
      server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java
  5. 9 8
      server/src/main/java/password/pwm/http/servlet/ForgottenUsernameServlet.java
  6. 8 7
      server/src/main/java/password/pwm/http/servlet/activation/ActivateUserServlet.java
  7. 4 4
      server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java
  8. 2 2
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java
  9. 2 3
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java
  10. 3 2
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java
  11. 9 9
      server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java
  12. 7 8
      server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordStateMachine.java
  13. 3 7
      server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java
  14. 8 5
      server/src/main/java/password/pwm/http/servlet/resource/ResourceFileRequest.java
  15. 3 39
      server/src/main/java/password/pwm/http/servlet/resource/ResourceFileServlet.java
  16. 46 30
      server/src/main/java/password/pwm/ldap/LdapBrowser.java
  17. 2 1
      server/src/main/java/password/pwm/ldap/auth/LDAPAuthenticationRequest.java
  18. 5 4
      server/src/main/java/password/pwm/ldap/auth/SessionAuthenticator.java
  19. 1 1
      server/src/main/java/password/pwm/svc/cr/NMASCrOperator.java
  20. 0 7
      server/src/main/java/password/pwm/svc/event/AuditServiceClient.java
  21. 0 7
      server/src/main/java/password/pwm/svc/intruder/IntruderDomainService.java
  22. 49 48
      server/src/main/java/password/pwm/svc/intruder/IntruderServiceClient.java
  23. 0 7
      server/src/main/java/password/pwm/svc/otp/LocalDbOtpOperator.java
  24. 2 1
      server/src/main/java/password/pwm/svc/token/TokenService.java
  25. 7 5
      server/src/main/java/password/pwm/svc/wordlist/AbstractWordlist.java
  26. 3 3
      server/src/main/java/password/pwm/svc/wordlist/AbstractWordlistBucket.java
  27. 0 5
      server/src/main/java/password/pwm/svc/wordlist/SeedlistService.java
  28. 2 2
      server/src/main/java/password/pwm/util/java/CopyingInputStream.java
  29. 44 0
      server/src/main/java/password/pwm/util/java/LongIncrementer.java
  30. 6 7
      server/src/main/java/password/pwm/util/java/Percent.java
  31. 5 1
      server/src/test/java/password/pwm/health/HealthMessageTest.java
  32. 144 0
      server/src/test/java/password/pwm/util/java/CopyingInputStreamTest.java
  33. 2 1
      webapp/src/main/webapp/public/resources/js/configeditor-settings-stringarray.js
  34. 2 0
      webapp/src/main/webapp/public/resources/js/configeditor.js
  35. 9 9
      webapp/src/main/webapp/public/resources/js/uilibrary.js
  36. 4 0
      webapp/src/main/webapp/public/resources/themes/pwm/configStyle.css

+ 17 - 5
server/src/main/java/password/pwm/PwmApplication.java

@@ -306,6 +306,9 @@ public class PwmApplication
     {
         final Instant startTime = Instant.now();
 
+        getPwmScheduler().immediateExecuteRunnableInNewThread( UserAgentUtils::initializeCache, "initialize useragent cache" );
+        getPwmScheduler().immediateExecuteRunnableInNewThread( PwmSettingMetaDataReader::initCache, "initialize PwmSetting cache" );
+        
         if ( Boolean.parseBoolean( getConfig().readAppProperty( AppProperty.LOGGING_OUTPUT_CONFIGURATION ) ) )
         {
             outputConfigurationToLog( this );
@@ -355,8 +358,14 @@ public class PwmApplication
             }
         }
 
-        getPwmScheduler().immediateExecuteRunnableInNewThread( UserAgentUtils::initializeCache, "initialize useragent cache" );
-        getPwmScheduler().immediateExecuteRunnableInNewThread( PwmSettingMetaDataReader::initCache, "initialize PwmSetting cache" );
+        if ( Boolean.parseBoolean( getConfig().readAppProperty( AppProperty.LOGGING_OUTPUT_CONFIGURATION ) ) )
+        {
+            getPwmScheduler().immediateExecuteRunnableInNewThread( () ->
+            {
+                outputConfigurationToLog( this );
+                outputNonDefaultPropertiesToLog( this );
+            }, "output configuration to log" );
+        }
 
         MBeanUtility.registerMBean( this );
         LOGGER.trace( () -> "completed post init tasks", () -> TimeDuration.fromCurrent( startTime ) );
@@ -607,11 +616,14 @@ public class PwmApplication
                 PwmConstants.DEFAULT_LOCALE );
 
         LOGGER.trace( () -> "--begin current configuration output--" );
-        debugStrings.entrySet().stream()
+        final long itemCount = debugStrings.entrySet().stream()
                 .map( valueFormatter )
                 .map( s -> ( Supplier<CharSequence> ) () -> s )
-                .forEach( LOGGER::trace );
-        LOGGER.trace( () -> "--end current configuration output--", () -> TimeDuration.fromCurrent( startTime ) );
+                .peek( LOGGER::trace )
+                .count();
+
+        LOGGER.trace( () -> "--end current configuration output of " + itemCount + " items --",
+                () -> TimeDuration.fromCurrent( startTime ) );
     }
 
     private static void outputNonDefaultPropertiesToLog( final PwmApplication pwmApplication )

+ 26 - 2
server/src/main/java/password/pwm/health/HealthService.java

@@ -34,6 +34,8 @@ import password.pwm.util.debug.DebugItemGenerator;
 import password.pwm.util.java.AtomicLoopIntIncrementer;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.StatisticAverageBundle;
+import password.pwm.util.java.StatisticCounterBundle;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogLevel;
@@ -49,6 +51,7 @@ import java.nio.file.Files;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -81,8 +84,21 @@ public class HealthService extends AbstractPwmService implements PwmService
     private final Map<HealthMonitorFlag, Serializable> healthProperties = new ConcurrentHashMap<>();
     private final AtomicInteger healthCheckCount = new AtomicInteger( 0 );
 
+    private final StatisticCounterBundle<CounterStatKey> counterStats = new StatisticCounterBundle<>( CounterStatKey.class );
+    private final StatisticAverageBundle<AverageStatKey> averageStats = new StatisticAverageBundle<>( AverageStatKey.class );
+
     private volatile HealthData healthData = emptyHealthData();
 
+    enum CounterStatKey
+    {
+        checks,
+    }
+
+    enum AverageStatKey
+    {
+        checkProcessTime,
+    }
+
     enum HealthMonitorFlag
     {
         LdapVendorSameCheck,
@@ -151,7 +167,10 @@ public class HealthService extends AbstractPwmService implements PwmService
             LOGGER.trace( () ->  "begin force immediate check" );
             final Future future = getPwmApplication().getPwmScheduler().scheduleJob( new ImmediateJob(), executorService, TimeDuration.ZERO );
             settings.getMaximumForceCheckWait().pause( future::isDone );
-            LOGGER.trace( () ->  "exit force immediate check, done=" + future.isDone(), () -> TimeDuration.fromCurrent( startTime ) );
+            final TimeDuration checkDuration = TimeDuration.fromCurrent( startTime );
+            averageStats.update( AverageStatKey.checkProcessTime, checkDuration );
+            counterStats.increment( CounterStatKey.checks );
+            LOGGER.trace( () ->  "exit force immediate check, done=" + future.isDone(), () -> checkDuration );
         }
 
         getPwmApplication().getPwmScheduler().scheduleJob( new UpdateJob(), executorService, settings.getNominalCheckInterval() );
@@ -267,7 +286,12 @@ public class HealthService extends AbstractPwmService implements PwmService
     @Override
     public ServiceInfoBean serviceInfo( )
     {
-        return ServiceInfoBean.builder().build();
+        final Map<String, String> debugData = new HashMap<>();
+        debugData.putAll( averageStats.debugStats() );
+        debugData.putAll( counterStats.debugStats() );
+        return ServiceInfoBean.builder()
+                .debugProperties( Collections.unmodifiableMap( debugData ) )
+                .build();
     }
 
     Map<HealthMonitorFlag, Serializable> getHealthProperties( )

+ 16 - 1
server/src/main/java/password/pwm/http/HttpEventManager.java

@@ -33,6 +33,8 @@ import password.pwm.util.logging.PwmLogger;
 
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
 import javax.servlet.http.HttpSession;
 import javax.servlet.http.HttpSessionActivationListener;
 import javax.servlet.http.HttpSessionEvent;
@@ -51,7 +53,8 @@ import java.util.logging.Logger;
 public class HttpEventManager implements
         ServletContextListener,
         HttpSessionListener,
-        HttpSessionActivationListener
+        HttpSessionActivationListener,
+        ServletRequestListener
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( HttpEventManager.class );
 
@@ -182,5 +185,17 @@ public class HttpEventManager implements
         debugItems.put( "avgRequestDuration", avgReqDuration.asCompactString() );
         return StringHelper.stringMapToString( debugItems, "," );
     }
+
+    @Override
+    public void requestDestroyed( final ServletRequestEvent sre )
+    {
+        ServletRequestListener.super.requestDestroyed( sre );
+    }
+
+    @Override
+    public void requestInitialized( final ServletRequestEvent sre )
+    {
+        ServletRequestListener.super.requestInitialized( sre );
+    }
 }
 

+ 2 - 1
server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java

@@ -43,6 +43,7 @@ import password.pwm.http.PwmResponse;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSessionFactory;
 import password.pwm.http.PwmURL;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsService;
@@ -621,7 +622,7 @@ public class RequestInitializationFilter implements Filter
         checkTrial( pwmRequest );
 
         // check intruder
-        pwmRequest.getPwmDomain().getIntruderService().client().checkAddressAndSession( pwmRequest.getPwmSession() );
+        IntruderServiceClient.checkAddressAndSession( pwmRequest.getPwmDomain(), pwmRequest.getPwmSession() );
     }
 
     private static void checkIfSourceAddressChanged( final PwmRequest pwmRequest )

+ 9 - 8
server/src/main/java/password/pwm/http/servlet/ForgottenUsernameServlet.java

@@ -20,8 +20,8 @@
 
 package password.pwm.http.servlet;
 
-import password.pwm.PwmDomain;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.SessionLabel;
@@ -43,6 +43,7 @@ import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.CaptchaUtility;
@@ -163,7 +164,7 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
                     forgottenUsernameForm, ssBean.getLocale() );
 
             // check for intruder search
-            pwmDomain.getIntruderService().client().checkAttributes( formValues );
+            IntruderServiceClient.checkAttributes( pwmDomain, formValues );
 
             // see if the values meet the configured form requirements.
             FormUtility.validateFormValues( pwmRequest.getDomainConfig(), formValues, ssBean.getLocale() );
@@ -196,7 +197,7 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
 
             if ( userIdentity == null )
             {
-                pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
+                IntruderServiceClient.markAddressAndSession( pwmDomain, pwmSession );
                 StatisticsClient.incrementStat( pwmRequest, Statistic.FORGOTTEN_USERNAME_FAILURES );
                 setLastError( pwmRequest, PwmError.ERROR_CANT_MATCH_USER.toInfo() );
                 forwardToFormJsp( pwmRequest );
@@ -204,7 +205,7 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
             }
 
             // make sure the user isn't locked.
-            pwmDomain.getIntruderService().client().checkUserIdentity( userIdentity );
+            IntruderServiceClient.checkUserIdentity( pwmDomain, userIdentity );
 
             final UserInfo forgottenUserInfo = UserInfoFactory.newUserInfoUsingProxy(
                     pwmRequest.getPwmApplication(),
@@ -215,8 +216,8 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
             // send username
             sendUsername( pwmDomain, pwmRequest, forgottenUserInfo );
 
-            pwmDomain.getIntruderService().client().clearAddressAndSession( pwmSession );
-            pwmDomain.getIntruderService().client().clearAttributes( formValues );
+            IntruderServiceClient.clearAddressAndSession( pwmDomain, pwmSession );
+            IntruderServiceClient.clearAttributes( pwmDomain, formValues );
 
             StatisticsClient.incrementStat( pwmRequest, Statistic.FORGOTTEN_USERNAME_SUCCESSES );
 
@@ -233,8 +234,8 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
                     e.getErrorInformation().getFieldValues() )
                     : e.getErrorInformation();
             setLastError( pwmRequest, errorInfo );
-            pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
-            pwmDomain.getIntruderService().client().markAttributes( formValues, pwmRequest.getLabel() );
+            IntruderServiceClient.markAddressAndSession( pwmDomain, pwmSession );
+            IntruderServiceClient.markAttributes( pwmDomain, formValues, pwmRequest.getLabel() );
         }
 
         StatisticsClient.incrementStat( pwmRequest, Statistic.FORGOTTEN_USERNAME_FAILURES );

+ 8 - 7
server/src/main/java/password/pwm/http/servlet/activation/ActivateUserServlet.java

@@ -55,6 +55,7 @@ import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditServiceClient;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenType;
@@ -243,7 +244,7 @@ public class ActivateUserServlet extends ControlledPwmServlet
                     ssBean.getLocale() );
 
             // check for intruders
-            pwmDomain.getIntruderService().client().checkAttributes( formValues );
+            IntruderServiceClient.checkAttributes( pwmDomain, formValues );
 
             // read the context attr
             final String contextParam = pwmRequest.readParameterAsString( PwmConstants.PARAM_CONTEXT );
@@ -273,13 +274,13 @@ public class ActivateUserServlet extends ControlledPwmServlet
             ActivateUserUtils.validateParamsAgainstLDAP( pwmRequest, formValues, userIdentity );
 
             ActivateUserUtils.initUserActivationBean( pwmRequest, userIdentity );
-            pwmDomain.getIntruderService().client().clearAttributes( formValues );
-            pwmDomain.getIntruderService().client().clearAddressAndSession( pwmSession );
+            IntruderServiceClient.clearAttributes( pwmDomain, formValues );
+            IntruderServiceClient.clearAddressAndSession( pwmRequest.getPwmDomain(), pwmSession );
         }
         catch ( final PwmOperationalException e )
         {
-            pwmDomain.getIntruderService().client().markAttributes( formValues, pwmRequest.getLabel() );
-            pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
+            IntruderServiceClient.markAttributes( pwmRequest, formValues );
+            IntruderServiceClient.markAddressAndSession( pwmDomain, pwmSession );
             setLastError( pwmRequest, e.getErrorInformation() );
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
         }
@@ -478,8 +479,8 @@ public class ActivateUserServlet extends ControlledPwmServlet
         catch ( final PwmOperationalException e )
         {
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
-            pwmDomain.getIntruderService().client().markUserIdentity( activateUserBean.getUserIdentity(), pwmRequest );
-            pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
+            IntruderServiceClient.markUserIdentity( pwmRequest, activateUserBean.getUserIdentity() );
+            IntruderServiceClient.markAddressAndSession( pwmDomain, pwmSession );
             pwmRequest.respondWithError( e.getErrorInformation() );
         }
     }

+ 4 - 4
server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java

@@ -51,6 +51,7 @@ import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditServiceClient;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.stats.AvgStatistic;
 import password.pwm.util.PasswordData;
 import password.pwm.util.form.FormUtility;
@@ -296,8 +297,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
 
             if ( !passed )
             {
-                pwmRequest.getPwmDomain().getIntruderService().client().markUserIdentity(
-                        userInfo.getUserIdentity(), pwmRequest.getLabel() );
+                IntruderServiceClient.markUserIdentity( pwmRequest, userInfo.getUserIdentity() );
                 LOGGER.debug( pwmRequest, () -> "failed password validation check: currentPassword value is incorrect" );
                 setLastError( pwmRequest, new ErrorInformation( PwmError.ERROR_BAD_CURRENT_PASSWORD ) );
                 return ProcessStatus.Continue;
@@ -320,8 +320,8 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
         }
         catch ( final PwmOperationalException e )
         {
-            pwmRequest.getPwmDomain().getIntruderService().client().markAddressAndSession( pwmRequest );
-            pwmRequest.getPwmDomain().getIntruderService().client().markUserIdentity( userInfo.getUserIdentity(), pwmRequest.getLabel() );
+            IntruderServiceClient.markAddressAndSession( pwmRequest.getPwmDomain(), pwmRequest.getPwmSession() );
+            IntruderServiceClient.markUserIdentity( pwmRequest, userInfo.getUserIdentity() );
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
             setLastError( pwmRequest, e.getErrorInformation() );
             return ProcessStatus.Continue;

+ 2 - 2
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java

@@ -878,8 +878,8 @@ public class ConfigEditorServlet extends ControlledPwmServlet
         final Instant startTime = Instant.now();
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
         final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap( PwmHttpRequestWrapper.Flag.BypassValidation );
-        final String profile = inputMap.get( "profile" );
-        final String dn = inputMap.getOrDefault( "dn", "" );
+        final String profile = inputMap.get( LdapBrowser.PARAM_PROFILE );
+        final String dn = inputMap.getOrDefault( LdapBrowser.PARAM_DN, "" );
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForDomainSetting(  );
 
         final LdapBrowser ldapBrowser = new LdapBrowser(

+ 2 - 3
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java

@@ -361,15 +361,14 @@ public class ConfigGuideServlet extends ControlledPwmServlet
 
         final Instant startTime = Instant.now();
         final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap( PwmHttpRequestWrapper.Flag.BypassValidation );
-        final String profile = inputMap.get( "profile" );
-        final String dn = inputMap.getOrDefault( "dn", "" );
+        final String dn = inputMap.getOrDefault( LdapBrowser.PARAM_DN, "" );
 
         final LdapBrowser ldapBrowser = new LdapBrowser(
                 pwmRequest.getLabel(),
                 pwmRequest.getPwmDomain().getLdapConnectionService().getChaiProviderFactory(),
                 storedConfiguration
         );
-        final LdapBrowser.LdapBrowseResult result = ldapBrowser.doBrowse( domainID, profile, dn );
+        final LdapBrowser.LdapBrowseResult result = ldapBrowser.doBrowse( domainID, ConfigGuideForm.LDAP_PROFILE_NAME, dn );
         ldapBrowser.close();
 
         LOGGER.trace( pwmRequest, () -> "performed ldapBrowse operation in "

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java

@@ -48,6 +48,7 @@ import password.pwm.http.bean.ConfigManagerBean;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.svc.intruder.IntruderRecordType;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
@@ -150,7 +151,7 @@ public class ConfigManagerLoginServlet extends AbstractPwmServlet
             else
             {
                 LOGGER.trace( pwmRequest, () -> "configuration password is not correct" );
-                pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
+                IntruderServiceClient.markAddressAndSession( pwmDomain, pwmRequest.getPwmSession() );
                 pwmDomain.getIntruderService().mark( IntruderRecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME, pwmRequest.getLabel() );
                 final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_PASSWORD_ONLY_BAD );
                 updateLoginHistory( pwmRequest, pwmRequest.getUserInfoIfLoggedIn(), false );
@@ -257,7 +258,7 @@ public class ConfigManagerLoginServlet extends AbstractPwmServlet
         final PwmSession pwmSession = pwmRequest.getPwmSession();
 
         configManagerBean.setPasswordVerified( true );
-        pwmDomain.getIntruderService().client().clearAddressAndSession( pwmSession );
+        IntruderServiceClient.clearAddressAndSession( pwmDomain, pwmSession );
         pwmDomain.getIntruderService().clear( IntruderRecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME );
         pwmRequest.getPwmSession().getSessionStateBean().setSessionIdRecycleNeeded( true );
         if ( persistentLoginEnabled && "on".equals( pwmRequest.readParameterAsString( "remember" ) ) )

+ 9 - 9
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java

@@ -73,6 +73,7 @@ import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditServiceClient;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.svc.token.TokenPayload;
@@ -198,7 +199,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
 
         if ( forgottenPasswordBean.getUserIdentity() != null )
         {
-            pwmDomain.getIntruderService().client().checkUserIdentity( forgottenPasswordBean.getUserIdentity() );
+            IntruderServiceClient.checkUserIdentity( pwmDomain, forgottenPasswordBean.getUserIdentity() );
         }
 
         checkForLocaleSwitch( pwmRequest, forgottenPasswordBean );
@@ -424,7 +425,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
             formValues = FormUtility.readFormValuesFromRequest( pwmRequest, forgottenPasswordForm, userLocale );
 
             // check for intruder search values
-            pwmDomain.getIntruderService().client().checkAttributes( formValues );
+            IntruderServiceClient.checkAttributes( pwmDomain, formValues );
 
             // see if the values meet the configured form requirements.
             FormUtility.validateFormValues( pwmRequest.getDomainConfig(), formValues, userLocale );
@@ -468,7 +469,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
             ForgottenPasswordUtil.initForgottenPasswordBean( pwmRequest.getPwmRequestContext(), userIdentity, forgottenPasswordBean );
 
             // clear intruder search values
-            pwmDomain.getIntruderService().client().clearAttributes( formValues );
+            IntruderServiceClient.clearAttributes( pwmDomain, formValues );
 
             return ProcessStatus.Continue;
         }
@@ -483,8 +484,8 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
 
                 StatisticsClient.incrementStat( pwmRequest, Statistic.RECOVERY_FAILURES );
 
-                pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
-                pwmDomain.getIntruderService().client().markAttributes( formValues, pwmRequest.getLabel() );
+                IntruderServiceClient.markAddressAndSession( pwmDomain, pwmRequest.getPwmSession() );
+                IntruderServiceClient.markAttributes( pwmRequest, formValues );
 
                 LOGGER.debug( pwmRequest, errorInfo );
                 setLastError( pwmRequest, errorInfo );
@@ -836,7 +837,6 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
     private ProcessStatus processCheckAttributes( final PwmRequest pwmRequest )
             throws ChaiUnavailableException, IOException, ServletException, PwmUnrecoverableException
     {
-        //final SessionStateBean ssBean = pwmRequest.getPwmSession().getSessionStateBean();
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean( pwmRequest );
 
         if ( forgottenPasswordBean.isBogusUser() )
@@ -847,7 +847,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
             {
                 final List<FormConfiguration> formConfigurations = pwmRequest.getDomainConfig().readSettingAsForm( PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM );
                 final Map<FormConfiguration, String> formMap = FormUtility.asFormConfigurationMap( formConfigurations, forgottenPasswordBean.getUserSearchValues() );
-                pwmRequest.getPwmDomain().getIntruderService().client().markAttributes( formMap, pwmRequest.getLabel() );
+                IntruderServiceClient.markAttributes( pwmRequest, formMap );
             }
 
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_INCORRECT_RESPONSE,
@@ -1278,10 +1278,10 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
                     PwmAuthenticationSource.FORGOTTEN_PASSWORD
             );
             sessionAuthenticator.simulateBadPassword( userIdentity );
-            pwmRequest.getPwmDomain().getIntruderService().client().markUserIdentity( userIdentity, pwmRequest );
+            IntruderServiceClient.markUserIdentity( pwmRequest, userIdentity );
         }
 
-        pwmRequest.getPwmDomain().getIntruderService().client().markAddressAndSession( pwmRequest );
+        IntruderServiceClient.markAddressAndSession( pwmRequest.getPwmDomain(), pwmRequest.getPwmSession() );
 
         StatisticsClient.incrementStat( pwmRequest, Statistic.RECOVERY_FAILURES );
     }

+ 7 - 8
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordStateMachine.java

@@ -60,6 +60,8 @@ import password.pwm.ldap.auth.AuthenticationUtility;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
+import password.pwm.svc.intruder.IntruderServiceClient;
+import password.pwm.svc.otp.OTPUserRecord;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.svc.token.TokenPayload;
@@ -75,7 +77,6 @@ import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroRequest;
-import password.pwm.svc.otp.OTPUserRecord;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.ws.server.PresentableForm;
 import password.pwm.ws.server.PresentableFormRow;
@@ -745,7 +746,7 @@ public class ForgottenPasswordStateMachine
                     {
                         final List<FormConfiguration> formConfigurations = pwmDomain.getConfig().readSettingAsForm( PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM );
                         final Map<FormConfiguration, String> formMap = FormUtility.asFormConfigurationMap( formConfigurations, forgottenPasswordBean.getUserSearchValues() );
-                        pwmDomain.getIntruderService().client().markAttributes( formMap, forgottenPasswordStateMachine.getRequestContext().getSessionLabel() );
+                        IntruderServiceClient.markAttributes( pwmDomain, formMap, forgottenPasswordStateMachine.getRequestContext().getSessionLabel() );
                     }
 
                     final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_INCORRECT_RESPONSE,
@@ -970,7 +971,7 @@ public class ForgottenPasswordStateMachine
                 formValues = FormUtility.readFormValuesFromMap( values, forgottenPasswordForm, pwmRequestContext.getLocale() );
 
                 // check for intruder search values
-                pwmRequestContext.getPwmDomain().getIntruderService().client().checkAttributes( formValues );
+                IntruderServiceClient.checkAttributes( pwmRequestContext.getPwmDomain(), formValues );
 
                 // see if the values meet the configured form requirements.
                 FormUtility.validateFormValues( pwmRequestContext.getDomainConfig(), formValues, pwmRequestContext.getLocale() );
@@ -1013,7 +1014,7 @@ public class ForgottenPasswordStateMachine
                 ForgottenPasswordUtil.initForgottenPasswordBean( pwmRequestContext, userIdentity, forgottenPasswordStateMachine.getForgottenPasswordBean() );
 
                 // clear intruder search values
-                pwmRequestContext.getPwmDomain().getIntruderService().client().clearAttributes( formValues );
+                IntruderServiceClient.clearAttributes( pwmRequestContext.getPwmDomain(), formValues );
 
                 return;
             }
@@ -1028,7 +1029,7 @@ public class ForgottenPasswordStateMachine
 
                     StatisticsClient.incrementStat( pwmRequestContext.getPwmApplication(), Statistic.RECOVERY_FAILURES );
 
-                    pwmRequestContext.getPwmDomain().getIntruderService().client().markAttributes( formValues, pwmRequestContext.getSessionLabel() );
+                    IntruderServiceClient.markAttributes( pwmRequestContext.getPwmDomain(), formValues, pwmRequestContext.getSessionLabel() );
 
                     LOGGER.debug( pwmRequestContext.getSessionLabel(), errorInfo );
                     forgottenPasswordStateMachine.clear();
@@ -1118,9 +1119,7 @@ public class ForgottenPasswordStateMachine
         {
             SessionAuthenticator.simulateBadPassword( pwmRequestContext, userIdentity );
 
-
-            pwmRequestContext.getPwmDomain().getIntruderService().client().markUserIdentity( userIdentity,
-                    pwmRequestContext.getSessionLabel() );
+            IntruderServiceClient.markUserIdentity( pwmRequestContext.getPwmDomain(), pwmRequestContext.getSessionLabel(), userIdentity );
         }
 
         StatisticsClient.incrementStat( pwmRequestContext.getPwmDomain(), Statistic.RECOVERY_FAILURES );

+ 3 - 7
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java

@@ -72,7 +72,7 @@ import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.event.HelpdeskAuditRecord;
-import password.pwm.svc.intruder.IntruderDomainService;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.otp.OTPUserRecord;
 import password.pwm.svc.otp.OtpService;
 import password.pwm.svc.secure.DomainSecureService;
@@ -609,11 +609,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
         }
 
         //clear pwm intruder setting.
-        {
-            final IntruderDomainService intruderManager = pwmRequest.getPwmDomain().getIntruderService();
-            intruderManager.client().clearUserIdentity( userIdentity );
-        }
-
+        IntruderServiceClient.clearUserIdentity( pwmRequest, userIdentity );
 
         try
         {
@@ -951,7 +947,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
         }
 
         //clear pwm intruder setting.
-        pwmRequest.getPwmDomain().getIntruderService().client().clearUserIdentity( userIdentity );
+        IntruderServiceClient.clearUserIdentity( pwmRequest, userIdentity );
 
         try
         {

+ 8 - 5
server/src/main/java/password/pwm/http/servlet/resource/ResourceFileRequest.java

@@ -20,6 +20,7 @@
 
 package password.pwm.http.servlet.resource;
 
+import edu.umd.cs.findbugs.annotations.NonNull;
 import lombok.Value;
 import org.webjars.WebJarAssetLocator;
 import password.pwm.config.DomainConfig;
@@ -58,16 +59,16 @@ class ResourceFileRequest
     /** Contains a list of all resources (files) found inside the resources folder of all JARs in the WAR's classpath. **/
     private static final Collection<String> WEB_JAR_ASSET_LIST = Collections.unmodifiableCollection( new ArrayList<>( new WebJarAssetLocator().listAssets() ) );
 
-    private final HttpServletRequest httpServletRequest;
     private final DomainConfig domainConfig;
+    private final HttpServletRequest httpServletRequest;
     private final ResourceServletConfiguration resourceServletConfiguration;
 
     private final Optional<FileResource> fileResource;
 
     ResourceFileRequest(
-            final DomainConfig domainConfig,
-            final ResourceServletConfiguration resourceServletConfiguration,
-            final HttpServletRequest httpServletRequest
+            @NonNull final DomainConfig domainConfig,
+            @NonNull final ResourceServletConfiguration resourceServletConfiguration,
+            @NonNull final HttpServletRequest httpServletRequest
     )
             throws PwmUnrecoverableException
     {
@@ -145,7 +146,9 @@ class ResourceFileRequest
             final String contentType = getRawMimeType();
             if ( contentType.startsWith( "text" ) || contentType.contains( "javascript" ) )
             {
-                final PwmHttpRequestWrapper pwmHttpRequestWrapper = new PwmHttpRequestWrapper( httpServletRequest, domainConfig.getAppConfig() );
+                final PwmHttpRequestWrapper pwmHttpRequestWrapper = new PwmHttpRequestWrapper(
+                        httpServletRequest,
+                        domainConfig.getAppConfig() );
                 final String acceptEncoding = pwmHttpRequestWrapper.readHeaderValueAsString( HttpHeader.AcceptEncoding );
                 return acceptEncoding != null && accepts( acceptEncoding, "gzip" );
             }

+ 3 - 39
server/src/main/java/password/pwm/http/servlet/resource/ResourceFileServlet.java

@@ -84,51 +84,15 @@ public class ResourceFileServlet extends HttpServlet implements PwmServlet
         try
         {
             pwmRequest = PwmRequest.forRequest( req, resp );
+            processAction( pwmRequest );
+            return;
         }
         catch ( final PwmUnrecoverableException e )
         {
             LOGGER.error( () -> "unable to satisfy request using standard mechanism, reverting to raw resource server" );
         }
 
-        if ( pwmRequest != null )
-        {
-            try
-            {
-                processAction( pwmRequest );
-            }
-            catch ( final PwmUnrecoverableException e )
-            {
-                LOGGER.error( pwmRequest, () -> "error during resource servlet request processing: " + e.getMessage() );
-            }
-        }
-        else
-        {
-            try
-            {
-                rawRequestProcessor( req, resp );
-            }
-            catch ( final PwmUnrecoverableException e )
-            {
-                LOGGER.error( () -> "error serving raw resource request: " + e.getMessage() );
-            }
-        }
-    }
-
-    private void rawRequestProcessor( final HttpServletRequest req, final HttpServletResponse resp )
-            throws IOException, PwmUnrecoverableException
-    {
-
-        final ResourceFileRequest resourceFileRequest = new ResourceFileRequest( null, ResourceServletConfiguration.defaultConfiguration(), req );
-
-        final Optional<FileResource> file = resourceFileRequest.getRequestedFileResource();
-
-        if ( file.isEmpty() )
-        {
-            resp.sendError( HttpServletResponse.SC_NOT_FOUND );
-            return;
-        }
-
-        handleUncachedResponse( resp, file.get(), false );
+        resp.sendError( 500, "unable to initialize request for resource url" );
     }
 
     protected void processAction( final PwmRequest pwmRequest )

+ 46 - 30
server/src/main/java/password/pwm/ldap/LdapBrowser.java

@@ -56,6 +56,9 @@ import java.util.TreeMap;
 
 public class LdapBrowser
 {
+    public static final String PARAM_DN = "dn";
+    public static final String PARAM_PROFILE = "profile";
+
     private static final PwmLogger LOGGER = PwmLogger.forClass( LdapBrowser.class );
     private final StoredConfiguration storedConfiguration;
 
@@ -63,13 +66,17 @@ public class LdapBrowser
     private final ChaiProviderFactory chaiProviderFactory;
     private final Map<String, ChaiProvider> providerCache = new HashMap<>();
 
+    private enum DnType
+    {
+        navigable,
+        selectable,
+    }
 
     public LdapBrowser(
             final SessionLabel sessionLabel,
             final ChaiProviderFactory chaiProviderFactory,
             final StoredConfiguration storedConfiguration
     )
-            throws PwmUnrecoverableException
     {
         this.sessionLabel = sessionLabel;
         this.chaiProviderFactory = chaiProviderFactory;
@@ -115,33 +122,12 @@ public class LdapBrowser
     {
         final LdapBrowseResult.LdapBrowseResultBuilder result = LdapBrowseResult.builder();
 
-        {
-            final Map<String, Boolean> childDNs = new TreeMap<>( getChildEntries( domainID, profileID, dn ) );
+        updateBrowseResultChildren( domainID, profileID, dn, result );
 
-            final List<DNInformation> navigableDNs = new ArrayList<>();
-            final List<DNInformation> selectableDNs = new ArrayList<>();
-            for ( final Map.Entry<String, Boolean> entry : childDNs.entrySet() )
-            {
-                final String childDN = entry.getKey();
-                final DNInformation dnInformation = new DNInformation( childDN, entryNameFromDN( childDN ) );
-
-                if ( entry.getValue() )
-                {
-                    navigableDNs.add( dnInformation );
-                }
-                else
-                {
-                    selectableDNs.add( dnInformation );
-                }
-            }
-            result.navigableDNlist( navigableDNs );
-            result.selectableDNlist( selectableDNs );
-            result.maxResults( childDNs.size() >= getMaxSizeLimit() );
-
-        }
         result.dn( dn );
         result.profileID( profileID );
         final DomainConfig domainConfig = new AppConfig( storedConfiguration ).getDomainConfigs().get( domainID );
+
         if ( domainConfig.getLdapProfiles().size() > 1 )
         {
             result.profileList( new ArrayList<>( domainConfig.getLdapProfiles().keySet() ) );
@@ -168,6 +154,37 @@ public class LdapBrowser
         return result.build();
     }
 
+    private void updateBrowseResultChildren(
+            final DomainID domainID,
+            final String profileID,
+            final String dn,
+            final LdapBrowseResult.LdapBrowseResultBuilder result
+    )
+            throws ChaiUnavailableException, PwmUnrecoverableException, ChaiOperationException
+    {
+        final Map<String, DnType> childDNs = new TreeMap<>( getChildEntries( domainID, profileID, dn ) );
+
+        final List<DNInformation> navigableDNs = new ArrayList<>();
+        final List<DNInformation> selectableDNs = new ArrayList<>();
+        for ( final Map.Entry<String, DnType> entry : childDNs.entrySet() )
+        {
+            final String childDN = entry.getKey();
+            final DNInformation dnInformation = new DNInformation( entryNameFromDN( childDN ), childDN );
+
+            if ( entry.getValue() == DnType.navigable )
+            {
+                navigableDNs.add( dnInformation );
+            }
+            else
+            {
+                selectableDNs.add( dnInformation );
+            }
+        }
+        result.navigableDNlist( navigableDNs );
+        result.selectableDNlist( selectableDNs );
+        result.maxResults( childDNs.size() >= getMaxSizeLimit() );
+    }
+
     private ChaiProvider getChaiProvider( final DomainID domainID, final String profile ) throws PwmUnrecoverableException
     {
         if ( !providerCache.containsKey( profile ) )
@@ -186,7 +203,7 @@ public class LdapBrowser
         return Integer.parseInt( appConfig.readAppProperty( AppProperty.LDAP_BROWSER_MAX_ENTRIES ) );
     }
 
-    private Map<String, Boolean> getChildEntries(
+    private Map<String, DnType> getChildEntries(
             final DomainID domainID,
             final String profile,
             final String dn
@@ -194,14 +211,14 @@ public class LdapBrowser
             throws ChaiUnavailableException, PwmUnrecoverableException, ChaiOperationException
     {
 
-        final HashMap<String, Boolean> returnMap = new HashMap<>();
+        final HashMap<String, DnType> returnMap = new HashMap<>();
         final ChaiProvider chaiProvider = getChaiProvider( domainID, profile );
         if ( StringUtil.isEmpty( dn ) && chaiProvider.getDirectoryVendor() == DirectoryVendor.ACTIVE_DIRECTORY )
         {
             final Set<String> adRootDNList = adRootDNList( domainID, profile );
             for ( final String rootDN : adRootDNList )
             {
-                returnMap.put( rootDN, true );
+                returnMap.put( rootDN, DnType.navigable );
             }
         }
         else
@@ -215,7 +232,6 @@ public class LdapBrowser
                 searchHelper.setAttributes( "subordinateCount" );
                 searchHelper.setSearchScope( SearchScope.ONE );
                 results = chaiProvider.searchMultiValues( dn, searchHelper );
-
             }
 
             for ( final Map.Entry<String, Map<String, List<String>>> entry : results.entrySet() )
@@ -246,10 +262,10 @@ public class LdapBrowser
                         LOGGER.debug( sessionLabel, () -> "error during subordinate entry count of " + dn + ", error: " + e.getMessage() );
                     }
                 }
-                returnMap.put( resultDN, hasSubs );
+                returnMap.put( resultDN, hasSubs ? DnType.navigable : DnType.selectable );
             }
         }
-        return returnMap;
+        return Collections.unmodifiableMap( returnMap );
     }
 
     private Set<String> adRootDNList( final DomainID domainID, final String profile ) throws ChaiUnavailableException, ChaiOperationException, PwmUnrecoverableException

+ 2 - 1
server/src/main/java/password/pwm/ldap/auth/LDAPAuthenticationRequest.java

@@ -51,6 +51,7 @@ import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.intruder.IntruderDomainService;
 import password.pwm.svc.intruder.IntruderRecordType;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.stats.AvgStatistic;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.Statistic;
@@ -212,7 +213,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest
         log( PwmLogLevel.DEBUG, () -> "preparing to authenticate user using authenticationType=" + this.requestedAuthType + " using strategy " + this.strategy );
 
         final IntruderDomainService intruderManager = pwmDomain.getIntruderService();
-        intruderManager.client().checkUserIdentity( userIdentity );
+        IntruderServiceClient.checkUserIdentity( pwmDomain, userIdentity );
         intruderManager.check( IntruderRecordType.ADDRESS, sessionLabel.getSourceAddress() );
 
         // verify user is not account disabled

+ 5 - 4
server/src/main/java/password/pwm/ldap/auth/SessionAuthenticator.java

@@ -48,6 +48,7 @@ import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.intruder.IntruderDomainService;
 import password.pwm.svc.intruder.IntruderRecordType;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.PasswordData;
@@ -343,7 +344,7 @@ public class SessionAuthenticator
         final IntruderDomainService intruderManager = pwmDomain.getIntruderService();
         if ( intruderManager != null )
         {
-            intruderManager.client().markAddressAndSession( pwmRequest );
+            IntruderServiceClient.markAddressAndSession( pwmRequest.getPwmDomain(), pwmRequest.getPwmSession() );
 
             if ( username != null )
             {
@@ -352,7 +353,7 @@ public class SessionAuthenticator
 
             if ( userIdentity != null )
             {
-                intruderManager.client().markUserIdentity( userIdentity, sessionLabel );
+                IntruderServiceClient.markUserIdentity( pwmRequest, userIdentity );
             }
         }
     }
@@ -415,8 +416,8 @@ public class SessionAuthenticator
 
         //notify the intruder manager with a successful login
         intruderManager.clear( IntruderRecordType.USERNAME, pwmSession.getUserInfo().getUsername() );
-        intruderManager.client().clearUserIdentity( userIdentity );
-        intruderManager.client().clearAddressAndSession( pwmSession );
+        IntruderServiceClient.clearUserIdentity( pwmRequest, userIdentity );
+        IntruderServiceClient.clearAddressAndSession( pwmDomain, pwmSession );
 
         if ( pwmSession.getUserInfo().getPasswordStatus().isWarnPeriod() )
         {

+ 1 - 1
server/src/main/java/password/pwm/svc/cr/NMASCrOperator.java

@@ -595,7 +595,7 @@ public class NMASCrOperator implements CrOperator
                 try
                 {
                     cycle();
-                    pwmDomain.getIntruderService().client().checkUserIdentity( userIdentity );
+                    IntruderServiceClient.checkUserIdentity( pwmDomain, userIdentity );
                     if ( challengeSet == null )
                     {
                         final String errorMsg = "unable to load next challenge set";

+ 0 - 7
server/src/main/java/password/pwm/svc/event/AuditServiceClient.java

@@ -21,7 +21,6 @@
 package password.pwm.svc.event;
 
 import password.pwm.PwmApplication;
-import password.pwm.PwmApplicationMode;
 import password.pwm.PwmDomain;
 import password.pwm.bean.SessionLabel;
 import password.pwm.error.PwmUnrecoverableException;
@@ -43,12 +42,6 @@ public class AuditServiceClient
         Objects.requireNonNull( pwmApplication );
         Objects.requireNonNull( auditRecord );
 
-        if ( pwmApplication.getApplicationMode() != PwmApplicationMode.RUNNING || pwmApplication.getApplicationMode() != PwmApplicationMode.CONFIGURATION )
-        {
-            LOGGER.trace( sessionLabel, () -> "discarding event, application not running: " + JsonUtil.serialize( auditRecord ) );
-            return;
-        }
-
         final AuditService auditService = pwmApplication.getAuditService();
         try
         {

+ 0 - 7
server/src/main/java/password/pwm/svc/intruder/IntruderDomainService.java

@@ -400,13 +400,6 @@ public class IntruderDomainService extends AbstractPwmService implements PwmServ
         }
     }
 
-
-
-    public IntruderServiceClient client( )
-    {
-        return new IntruderServiceClient( pwmDomain, this );
-    }
-
     private static void sendIntruderNoticeEmail(
             final PwmDomain pwmDomain,
             final SessionLabel sessionLabel,

+ 49 - 48
server/src/main/java/password/pwm/svc/intruder/IntruderServiceClient.java

@@ -29,65 +29,53 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmSession;
-import password.pwm.svc.PwmService;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 public class IntruderServiceClient
 {
-    private final PwmDomain pwmDomain;
-    private final IntruderDomainService intruderService;
-
-    protected IntruderServiceClient( final PwmDomain pwmDomain, final IntruderDomainService intruderService )
+    private IntruderServiceClient()
     {
-        this.pwmDomain = Objects.requireNonNull( pwmDomain );
-        this.intruderService = Objects.requireNonNull( intruderService );
     }
 
-    public static void checkUserIdentity( final PwmDomain pwmDomain, final UserIdentity userIdentity ) throws PwmUnrecoverableException
+    public static void checkAddressAndSession( final PwmDomain pwmDomain, final PwmSession pwmSession )
+            throws PwmUnrecoverableException
     {
-        if ( pwmDomain != null )
+        final IntruderDomainService intruderService = pwmDomain.getIntruderService();
+
+        if ( pwmSession != null )
         {
-            final IntruderDomainService intruderService = pwmDomain.getIntruderService();
-            if ( intruderService != null && intruderService.status() == PwmService.STATUS.OPEN )
+            final String subject = pwmSession.getSessionStateBean().getSrcAddress();
+            intruderService.check( IntruderRecordType.ADDRESS, subject );
+            final int maxAllowedAttempts = ( int ) pwmDomain.getConfig().readSettingAsLong( PwmSetting.INTRUDER_SESSION_MAX_ATTEMPTS );
+            if ( maxAllowedAttempts != 0 && pwmSession.getSessionStateBean().getIntruderAttempts().get() > maxAllowedAttempts )
             {
-                intruderService.client().checkUserIdentity( userIdentity );
+                throw new PwmUnrecoverableException( PwmError.ERROR_INTRUDER_SESSION );
             }
         }
     }
 
-    public void markAddressAndSession( final PwmRequest pwmRequest )
+    public static void markAddressAndSession( final PwmDomain pwmDomain, final PwmSession pwmSession )
             throws PwmUnrecoverableException
     {
-        if ( pwmRequest != null )
-        {
-            final String subject = pwmRequest.getPwmSession().getSessionStateBean().getSrcAddress();
-            pwmRequest.getPwmSession().getSessionStateBean().incrementIntruderAttempts();
-            intruderService.mark( IntruderRecordType.ADDRESS, subject, pwmRequest.getLabel() );
-        }
-    }
+        final IntruderDomainService intruderService = pwmDomain.getIntruderService();
 
-    public void checkAddressAndSession( final PwmSession pwmSession )
-            throws PwmUnrecoverableException
-    {
         if ( pwmSession != null )
         {
             final String subject = pwmSession.getSessionStateBean().getSrcAddress();
-            intruderService.check( IntruderRecordType.ADDRESS, subject );
-            final int maxAllowedAttempts = ( int ) pwmDomain.getConfig().readSettingAsLong( PwmSetting.INTRUDER_SESSION_MAX_ATTEMPTS );
-            if ( maxAllowedAttempts != 0 && pwmSession.getSessionStateBean().getIntruderAttempts().get() > maxAllowedAttempts )
-            {
-                throw new PwmUnrecoverableException( PwmError.ERROR_INTRUDER_SESSION );
-            }
+            pwmSession.getSessionStateBean().incrementIntruderAttempts();
+            intruderService.mark( IntruderRecordType.ADDRESS, subject, pwmSession.getLabel() );
         }
     }
 
-    public void clearAddressAndSession( final PwmSession pwmSession )
+    public static void clearAddressAndSession( final PwmDomain pwmDomain, final PwmSession pwmSession )
             throws PwmUnrecoverableException
     {
+        final IntruderDomainService intruderService = pwmDomain.getIntruderService();
+
         if ( pwmSession != null )
         {
             final String subject = pwmSession.getSessionStateBean().getSrcAddress();
@@ -97,39 +85,41 @@ public class IntruderServiceClient
         }
     }
 
-    public void markUserIdentity( final UserIdentity userIdentity, final SessionLabel sessionLabel )
+    public static void checkUserIdentity( final PwmDomain pwmDomain, final UserIdentity userIdentity )
             throws PwmUnrecoverableException
     {
+        final IntruderDomainService intruderService = pwmDomain.getIntruderService();
+
         if ( userIdentity != null )
         {
             final String subject = userIdentity.toDelimitedKey();
-            intruderService.mark( IntruderRecordType.USER_ID, subject, sessionLabel );
+            intruderService.check( IntruderRecordType.USER_ID, subject );
         }
     }
 
-    public void markUserIdentity( final UserIdentity userIdentity, final PwmRequest pwmRequest )
+    public static void markUserIdentity( final PwmRequest pwmRequest, final UserIdentity userIdentity )
             throws PwmUnrecoverableException
     {
-        if ( userIdentity != null )
-        {
-            final String subject = userIdentity.toDelimitedKey();
-            intruderService.mark( IntruderRecordType.USER_ID, subject, pwmRequest.getLabel() );
-        }
+        markUserIdentity( pwmRequest.getPwmDomain(), pwmRequest.getLabel(), userIdentity );
     }
 
-    public void checkUserIdentity( final UserIdentity userIdentity )
+    public static void markUserIdentity( final PwmDomain pwmDomain, final SessionLabel sessionLabel, final UserIdentity userIdentity )
             throws PwmUnrecoverableException
     {
+        final IntruderDomainService intruderService = pwmDomain.getIntruderService();
+
         if ( userIdentity != null )
         {
             final String subject = userIdentity.toDelimitedKey();
-            intruderService.check( IntruderRecordType.USER_ID, subject );
+            intruderService.mark( IntruderRecordType.USER_ID, subject, sessionLabel );
         }
     }
 
-    public void clearUserIdentity( final UserIdentity userIdentity )
+    public static void clearUserIdentity( final PwmRequest pwmRequest, final UserIdentity userIdentity )
             throws PwmUnrecoverableException
     {
+        final IntruderDomainService intruderService = pwmRequest.getPwmDomain().getIntruderService();
+
         if ( userIdentity != null )
         {
             final String subject = userIdentity.toDelimitedKey();
@@ -137,9 +127,17 @@ public class IntruderServiceClient
         }
     }
 
-    public void markAttributes( final Map<FormConfiguration, String> formValues, final SessionLabel sessionLabel )
+    public static void markAttributes( final PwmRequest pwmRequest, final Map<FormConfiguration, String> formValues )
+            throws PwmUnrecoverableException
+    {
+        markAttributes( pwmRequest.getPwmDomain(), formValues, pwmRequest.getLabel() );
+    }
+
+    public static void markAttributes( final PwmDomain pwmDomain, final Map<FormConfiguration, String> formValues, final SessionLabel sessionLabel )
             throws PwmUnrecoverableException
     {
+        final IntruderDomainService intruderService = pwmDomain.getIntruderService();
+
         final List<String> subjects = attributeFormToList( formValues );
         for ( final String subject : subjects )
         {
@@ -147,9 +145,11 @@ public class IntruderServiceClient
         }
     }
 
-    public void clearAttributes( final Map<FormConfiguration, String> formValues )
+    public static void clearAttributes( final PwmDomain pwmDomain, final Map<FormConfiguration, String> formValues )
             throws PwmUnrecoverableException
     {
+        final IntruderDomainService intruderService = pwmDomain.getIntruderService();
+
         final List<String> subjects = attributeFormToList( formValues );
         for ( final String subject : subjects )
         {
@@ -157,9 +157,11 @@ public class IntruderServiceClient
         }
     }
 
-    public void checkAttributes( final Map<FormConfiguration, String> formValues )
+    public static void checkAttributes( final PwmDomain pwmDomain, final Map<FormConfiguration, String> formValues )
             throws PwmUnrecoverableException
     {
+        final IntruderDomainService intruderService = pwmDomain.getIntruderService();
+
         final List<String> subjects = attributeFormToList( formValues );
         for ( final String subject : subjects )
         {
@@ -167,7 +169,7 @@ public class IntruderServiceClient
         }
     }
 
-    private List<String> attributeFormToList( final Map<FormConfiguration, String> formValues )
+    private static List<String> attributeFormToList( final Map<FormConfiguration, String> formValues )
     {
         final List<String> returnList = new ArrayList<>();
         if ( formValues != null )
@@ -182,7 +184,6 @@ public class IntruderServiceClient
                 }
             }
         }
-        return returnList;
+        return Collections.unmodifiableList( returnList );
     }
-
 }

+ 0 - 7
server/src/main/java/password/pwm/svc/otp/LocalDbOtpOperator.java

@@ -146,13 +146,6 @@ public class LocalDbOtpOperator extends AbstractOtpOperator
             localDB.put( LocalDB.DB.OTP_SECRET, userGUID, value );
             LOGGER.info( pwmRequest, () -> "saved OTP secret for user in LocalDB" );
         }
-        catch ( final LocalDBException ex )
-        {
-            final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_WRITING_OTP_SECRET, "unexpected LocalDB error saving otp to localDB: " + ex.getMessage() );
-            final PwmUnrecoverableException pwmOE = new PwmUnrecoverableException( errorInfo );
-            pwmOE.initCause( ex );
-            throw pwmOE;
-        }
         catch ( final PwmOperationalException ex )
         {
             final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_WRITING_OTP_SECRET, "unexpected error saving otp to localDB: " + ex.getMessage() );

+ 2 - 1
server/src/main/java/password/pwm/svc/token/TokenService.java

@@ -60,6 +60,7 @@ import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.intruder.IntruderRecordType;
+import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.DataStore;
@@ -611,7 +612,7 @@ public class TokenService extends AbstractPwmService implements PwmService
             if ( sessionUserIdentity != null && tokenEntryType == TokenEntryType.unauthenticated )
             {
                 SessionAuthenticator.simulateBadPassword( pwmRequestContext, sessionUserIdentity );
-                pwmDomain.getIntruderService().client().markUserIdentity( sessionUserIdentity, sessionLabel );
+                IntruderServiceClient.markUserIdentity( pwmRequestContext.getPwmDomain(), pwmRequestContext.getSessionLabel(), sessionUserIdentity );
             }
             StatisticsClient.incrementStat( pwmDomain, Statistic.RECOVERY_FAILURES );
             stats.increment( StatsKey.tokenValidationsFailed );

+ 7 - 5
server/src/main/java/password/pwm/svc/wordlist/AbstractWordlist.java

@@ -130,8 +130,6 @@ abstract class AbstractWordlist extends AbstractPwmService implements Wordlist,
 
         getLogger().trace( getSessionLabel(), () -> "opening with configuration: " + JsonUtil.serialize( wordlistConfiguration ) );
 
-        warmup();
-
         return STATUS.OPEN;
     }
 
@@ -139,8 +137,6 @@ abstract class AbstractWordlist extends AbstractPwmService implements Wordlist,
 
     protected abstract PwmLogger getLogger();
 
-    protected abstract void warmup();
-
     private void startTestInstance( final WordlistType wordlistType )
     {
         this.wordlistBucket = new MemoryWordlistBucket( getPwmApplication(), wordlistConfiguration, wordlistType );
@@ -301,6 +297,7 @@ abstract class AbstractWordlist extends AbstractPwmService implements Wordlist,
 
         setStatus( STATUS.CLOSED );
         inhibitBackgroundImportFlag.set( true );
+
         if ( executorService != null )
         {
             executorService.shutdown();
@@ -369,6 +366,11 @@ abstract class AbstractWordlist extends AbstractPwmService implements Wordlist,
 
     void writeWordlistStatus( final WordlistStatus wordlistStatus )
     {
+        if ( status() == STATUS.CLOSED )
+        {
+            return;
+        }
+
         wordTypesCache = null;
         wordlistBucket.writeWordlistStatus( wordlistStatus );
     }
@@ -559,6 +561,6 @@ abstract class AbstractWordlist extends AbstractPwmService implements Wordlist,
     private BooleanSupplier makeProcessCancelSupplier( )
     {
         return () -> inhibitBackgroundImportFlag.get()
-                || !STATUS.OPEN.equals( status() );
+                || STATUS.OPEN != status();
     }
 }

+ 3 - 3
server/src/main/java/password/pwm/svc/wordlist/AbstractWordlistBucket.java

@@ -23,8 +23,8 @@ package password.pwm.svc.wordlist;
 import password.pwm.PwmApplication;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.util.java.AtomicLoopLongIncrementer;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.LongIncrementer;
 import password.pwm.util.java.StringUtil;
 
 import java.util.Collection;
@@ -52,7 +52,7 @@ public abstract class AbstractWordlistBucket implements WordlistBucket
 
     private Map<String, String> getWriteTxnForValue(
             final Collection<String> words,
-            final AtomicLoopLongIncrementer valueIncrementer
+            final LongIncrementer valueIncrementer
     )
     {
         switch ( type )
@@ -98,7 +98,7 @@ public abstract class AbstractWordlistBucket implements WordlistBucket
             throws PwmUnrecoverableException
     {
         final WordlistStatus initialStatus = abstractWordlist.readWordlistStatus();
-        final AtomicLoopLongIncrementer valueIncrementer = AtomicLoopLongIncrementer.builder().initial( initialStatus.getValueCount() ).build();
+        final LongIncrementer valueIncrementer = new LongIncrementer( initialStatus.getValueCount() );
         this.putValues( getWriteTxnForValue( words, valueIncrementer ) );
 
         if ( initialStatus.getValueCount() != valueIncrementer.get() )

+ 0 - 5
server/src/main/java/password/pwm/svc/wordlist/SeedlistService.java

@@ -48,9 +48,4 @@ public class SeedlistService extends AbstractWordlist implements Wordlist
     {
         return super.randomSeed();
     }
-
-    @Override
-    protected void warmup()
-    {
-    }
 }

+ 2 - 2
server/src/main/java/password/pwm/util/java/CopyingInputStream.java

@@ -66,7 +66,7 @@ public class CopyingInputStream extends InputStream
         {
             consumer.accept( b );
         }
-        if ( bytesRead >= 1 )
+        else if ( bytesRead >= 1 )
         {
             final byte[] tempBytes = new byte[bytesRead];
             System.arraycopy( b, off, tempBytes, 0, bytesRead );
@@ -105,7 +105,7 @@ public class CopyingInputStream extends InputStream
         {
             consumer.accept( b );
         }
-        if ( bytesRead >= 1 )
+        else if ( bytesRead >= 1 )
         {
             final byte[] tempBytes = new byte[len];
             System.arraycopy( b, off, tempBytes, 0, bytesRead );

+ 44 - 0
server/src/main/java/password/pwm/util/java/LongIncrementer.java

@@ -0,0 +1,44 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2020 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.java;
+
+/**
+ * Simple long incrementer.  Not thread safe.
+ */
+public class LongIncrementer
+{
+    private long value;
+
+    public LongIncrementer( final long value )
+    {
+        this.value = value;
+    }
+
+    public long next()
+    {
+        return value++;
+    }
+
+    public long get()
+    {
+        return value;
+    }
+}

+ 6 - 7
server/src/main/java/password/pwm/util/java/Percent.java

@@ -37,19 +37,18 @@ public class Percent
 
     private Percent( final BigDecimal numerator, final BigDecimal denominator )
     {
-        if ( numerator == null )
+        if ( numerator == null || denominator == null )
         {
-            throw new NullPointerException( "numerator cannot be null" );
+            percentage = BigDecimal.ZERO;
         }
-        if ( denominator == null )
+        else if ( denominator.compareTo( BigDecimal.ZERO ) <= 0 )
         {
-            throw new NullPointerException( "denominator cannot be null" );
+            percentage = BigDecimal.ZERO;
         }
-        if ( denominator.compareTo( BigDecimal.ZERO ) <= 0 )
+        else
         {
-            throw new NullPointerException( "denominator must be greater than zero" );
+            percentage = numerator.divide( denominator, MathContext.DECIMAL32 ).multiply( BIG_DECIMAL_ONE_HUNDRED );
         }
-        percentage = numerator.divide( denominator, MathContext.DECIMAL32 ).multiply( BIG_DECIMAL_ONE_HUNDRED );
     }
 
     public static Percent of( final BigDecimal numerator, final long denominator )

+ 5 - 1
server/src/test/java/password/pwm/health/HealthMessageTest.java

@@ -56,7 +56,11 @@ public class HealthMessageTest
         {
             for ( final HealthMessage healthMessage : HealthMessage.values() )
             {
-                healthMessage.getDescription( locale, domainConfig, new String[]{"field1", "field2"} );
+                healthMessage.getDescription( locale, domainConfig, new String[]
+                        {
+                                "field1",
+                                "field2",
+                        } );
             }
         }
     }

+ 144 - 0
server/src/test/java/password/pwm/util/java/CopyingInputStreamTest.java

@@ -0,0 +1,144 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2020 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.java;
+
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.function.Consumer;
+
+
+public class CopyingInputStreamTest
+{
+    private static final String ASCII = "US-ASCII";
+
+    private InputStream copyingStream;
+    private OutputTester output;
+
+    private static class OutputTester implements Consumer<byte[]>
+    {
+        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+        @Override
+        public void accept( final byte[] bytes )
+        {
+            try
+            {
+                byteArrayOutputStream.write( bytes );
+            }
+            catch ( final IOException e )
+            {
+                throw new RuntimeException( e );
+            }
+        }
+
+        public byte[] bytes()
+        {
+            return byteArrayOutputStream.toByteArray();
+        }
+    }
+
+
+
+    @Before
+    public void setUp() throws Exception
+    {
+        final InputStream input = new ByteArrayInputStream( "abc".getBytes( ASCII ) );
+        output = new OutputTester();
+        copyingStream = new CopyingInputStream( input, output );
+    }
+
+    @Test
+    public void testReadNothing() throws Exception
+    {
+        Assert.assertEquals( "", new String( output.bytes(), ASCII ) );
+    }
+
+    @Test
+    public void testReadOneByte() throws Exception
+    {
+        Assert.assertEquals( 'a', copyingStream.read() );
+        Assert.assertEquals( "a", new String( output.bytes(), ASCII ) );
+    }
+
+    @Test
+    public void testReadEverything() throws Exception
+    {
+        Assert.assertEquals( 'a', copyingStream.read() );
+        Assert.assertEquals( 'b', copyingStream.read() );
+        Assert.assertEquals( 'c', copyingStream.read() );
+        Assert.assertEquals( -1, copyingStream.read() );
+        Assert.assertEquals( "abc", new String( output.bytes(), ASCII ) );
+    }
+
+    @Test
+    public void testReadToArray() throws Exception
+    {
+        final byte[] buffer = new byte[8];
+        Assert.assertEquals( 3, copyingStream.read( buffer ) );
+        Assert.assertEquals( 'a', buffer[0] );
+        Assert.assertEquals( 'b', buffer[1] );
+        Assert.assertEquals( 'c', buffer[2] );
+        Assert.assertEquals( -1, copyingStream.read( buffer ) );
+        Assert.assertEquals( "abc", new String( output.bytes(), ASCII ) );
+    }
+
+    @Test
+    public void testReadToArrayWithOffset() throws Exception
+    {
+        final byte[] buffer = new byte[8];
+        Assert.assertEquals( 3, copyingStream.read( buffer, 4, 4 ) );
+        Assert.assertEquals( 'a', buffer[4] );
+        Assert.assertEquals( 'b', buffer[5] );
+        Assert.assertEquals( 'c', buffer[6] );
+        Assert.assertEquals( -1, copyingStream.read( buffer, 4, 4 ) );
+        Assert.assertEquals( "abc", new String( output.bytes(), ASCII ) );
+    }
+
+    @Test
+    public void testSkip() throws Exception
+    {
+        Assert.assertEquals( 'a', copyingStream.read() );
+        Assert.assertEquals( 1, copyingStream.skip( 1 ) );
+        Assert.assertEquals( 'c', copyingStream.read() );
+        Assert.assertEquals( -1, copyingStream.read() );
+        Assert.assertEquals( "ac", new String( output.bytes(), ASCII ) );
+    }
+
+    @Test
+    public void testMarkReset() throws Exception
+    {
+        Assert.assertEquals( 'a', copyingStream.read() );
+        copyingStream.mark( 1 );
+        Assert.assertEquals( 'b', copyingStream.read() );
+        copyingStream.reset();
+        Assert.assertEquals( 'b', copyingStream.read() );
+        Assert.assertEquals( 'c', copyingStream.read() );
+        Assert.assertEquals( -1, copyingStream.read() );
+        Assert.assertEquals( "abbc", new String( output.bytes(), ASCII ) );
+    }
+}

+ 2 - 1
webapp/src/main/webapp/public/resources/js/configeditor-settings-stringarray.js

@@ -177,7 +177,8 @@ StringArrayValueHandler.drawRow = function(settingKey, iteration, value, itemCou
                 var actionName = syntax === 'PROFILE' ? 'copyProfile' : 'copyDomain';
                 PWM_MAIN.showWaitDialog({
                     loadFunction: function () {
-                        PWM_MAIN.ajaxRequest("editor?processAction=" + actionName, resultFunction, {content: options});
+                        var url = PWM_MAIN.addParamToUrl(window.location.pathname, 'processAction',actionName);
+                        PWM_MAIN.ajaxRequest(url, resultFunction, {content: options});
                     }
                 });
             };

+ 2 - 0
webapp/src/main/webapp/public/resources/js/configeditor.js

@@ -948,9 +948,11 @@ PWM_CFGEDIT.selectTemplate = function(newTemplate) {
 PWM_CFGEDIT.loadMainPageBody = function() {
 
     var drawSettingsFunction = function () {
+        var dispatched = false;
         var lastSelected = PWM_MAIN.Preferences.readSessionStorage('configEditor-lastSelected', null);
         if (lastSelected) {
                 PWM_CFGEDIT.dispatchNavigationItem(lastSelected);
+                dispatched = true;
         }
 
         if (!dispatched)

+ 9 - 9
webapp/src/main/webapp/public/resources/js/uilibrary.js

@@ -346,26 +346,26 @@ UILibrary.editLdapDN = function(nextFunction, options) {
             var entryName = dnInformation['entryName'];
             var out = '';
             if (navigable) {
-                out += '<tr><td style="width:10px" class="navigableDN" data-dn="' + loopDN + '"><span class="pwm-icon pwm-icon-level-down"></span></td>';
+                out += '<tr><td class="navigableDN" data-dn="' + loopDN + '"><span class="pwm-icon pwm-icon-level-down"></span></td>';
             } else {
-                out += '<tr><td style="width:10px"></td>';
+                out += '<tr><td></td>';
             }
-            out += '<td class="selectableDN" data-dn="' + loopDN + '" title="' + loopDN + '"><a><code>' + entryName + '</code></a></td>';
+            out += '<td class="selectableDN" data-dn="' + loopDN + '" title="' + loopDN + '">' + entryName + '</td>';
             out += '</tr>';
             return out;
         };
 
         if (data['data']['navigableDNlist'] && !PWM_MAIN.JSLibrary.isEmpty(data['data']['navigableDNlist'])) {
             var navigableDNlist = data['data']['navigableDNlist'];
-            for (var i in navigableDNlist) {
-                body += makeEntryHtml(navigableDNlist[i],true);
-            }
+            PWM_MAIN.JSLibrary.forEachInArray(navigableDNlist,function (item) {
+                body += makeEntryHtml(item,true);
+            });
         }
         if (data['data']['selectableDNlist'] && !PWM_MAIN.JSLibrary.isEmpty(data['data']['selectableDNlist'])) {
             var selectableDNlist = data['data']['selectableDNlist'];
-            for (var i in selectableDNlist) {
-                body += makeEntryHtml(selectableDNlist[i],false);
-            }
+            PWM_MAIN.JSLibrary.forEachInArray(selectableDNlist,function (item){
+                body += makeEntryHtml(item,false);
+            });
         }
         body += '</table></div>';
 

+ 4 - 0
webapp/src/main/webapp/public/resources/themes/pwm/configStyle.css

@@ -594,10 +594,14 @@ select {
 
 .navigableDN {
     cursor: pointer;
+    overflow-y: auto;
+    font-family: "Lucida Console", "Courier New", monospace;
 }
 
 .selectableDN {
     cursor: pointer;
+    overflow-y: auto;
+    font-family: "Lucida Console", "Courier New", monospace;
 }
 
 .setting-challenge-question-summary {