Просмотр исходного кода

- controlledpwmservlet test case
- remove Helper.java
- code refactoring

Jason Rivard 8 лет назад
Родитель
Сommit
d30816d12e
51 измененных файлов с 769 добавлено и 782 удалено
  1. 6 0
      pom.xml
  2. 3 3
      src/main/java/password/pwm/PwmAboutProperty.java
  3. 16 2
      src/main/java/password/pwm/PwmApplication.java
  4. 1 1
      src/main/java/password/pwm/config/Configuration.java
  5. 2 3
      src/main/java/password/pwm/health/HealthMonitor.java
  6. 1 2
      src/main/java/password/pwm/http/ContextManager.java
  7. 1 2
      src/main/java/password/pwm/http/PwmResponse.java
  8. 99 4
      src/main/java/password/pwm/http/filter/SessionFilter.java
  9. 25 22
      src/main/java/password/pwm/http/servlet/ControlledPwmServlet.java
  10. 30 46
      src/main/java/password/pwm/http/servlet/LoginServlet.java
  11. 3 8
      src/main/java/password/pwm/http/servlet/LogoutServlet.java
  12. 2 1
      src/main/java/password/pwm/http/servlet/PwmServletDefinition.java
  13. 3 8
      src/main/java/password/pwm/http/servlet/SetupResponsesServlet.java
  14. 3 63
      src/main/java/password/pwm/http/servlet/UpdateProfileServlet.java
  15. 4 9
      src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java
  16. 1 2
      src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerServlet.java
  17. 6 7
      src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java
  18. 5 10
      src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java
  19. 109 1
      src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskDetailInfoBean.java
  20. 112 225
      src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java
  21. 4 11
      src/main/java/password/pwm/http/servlet/newuser/NewUserServlet.java
  22. 1 2
      src/main/java/password/pwm/http/tag/ErrorMessageTag.java
  23. 22 2
      src/main/java/password/pwm/http/tag/PwmFormIDTag.java
  24. 1 2
      src/main/java/password/pwm/http/tag/conditional/PwmIfTest.java
  25. 1 2
      src/main/java/password/pwm/ldap/auth/SessionAuthenticator.java
  26. 2 2
      src/main/java/password/pwm/svc/cache/LocalDBCacheStore.java
  27. 1 2
      src/main/java/password/pwm/svc/event/AuditService.java
  28. 3 3
      src/main/java/password/pwm/svc/event/LocalDbAuditVault.java
  29. 1 2
      src/main/java/password/pwm/svc/intruder/IntruderManager.java
  30. 2 3
      src/main/java/password/pwm/svc/report/ReportCsvUtility.java
  31. 2 3
      src/main/java/password/pwm/svc/report/ReportService.java
  32. 2 3
      src/main/java/password/pwm/svc/stats/StatisticsManager.java
  33. 2 3
      src/main/java/password/pwm/svc/token/TokenService.java
  34. 3 4
      src/main/java/password/pwm/svc/wordlist/AbstractWordlist.java
  35. 2 3
      src/main/java/password/pwm/svc/wordlist/SharedHistoryManager.java
  36. 0 272
      src/main/java/password/pwm/util/Helper.java
  37. 2 2
      src/main/java/password/pwm/util/cli/commands/LocalDBInfoCommand.java
  38. 1 3
      src/main/java/password/pwm/util/java/BlockingThreadPool.java
  39. 68 9
      src/main/java/password/pwm/util/java/JavaHelper.java
  40. 31 0
      src/main/java/password/pwm/util/java/StringUtil.java
  41. 5 5
      src/main/java/password/pwm/util/localdb/Derby_LocalDB.java
  42. 2 3
      src/main/java/password/pwm/util/localdb/LocalDBFactory.java
  43. 1 2
      src/main/java/password/pwm/util/localdb/LocalDBUtility.java
  44. 1 2
      src/main/java/password/pwm/util/localdb/WorkQueueProcessor.java
  45. 6 6
      src/main/java/password/pwm/util/logging/LocalDBLogger.java
  46. 1 2
      src/main/java/password/pwm/ws/server/RestResultBean.java
  47. 2 3
      src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp
  48. 3 3
      src/main/webapp/WEB-INF/jsp/configmanager-localdb.jsp
  49. 1 0
      src/test/java/password/pwm/error/PwmErrorTest.java
  50. 160 0
      src/test/java/password/pwm/http/servlet/ControlledPwmServletTest.java
  51. 4 4
      src/test/java/password/pwm/manual/LocalDBLoggerTest.java

+ 6 - 0
pom.xml

@@ -540,6 +540,12 @@
             <version>1.58</version>
             <version>1.58</version>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>org.reflections</groupId>
+            <artifactId>reflections</artifactId>
+            <version>0.9.10</version>
+            <scope>test</scope>
+        </dependency>
 
 
         <!-- container dependencies -->
         <!-- container dependencies -->
         <dependency>
         <dependency>

+ 3 - 3
src/main/java/password/pwm/PwmAboutProperty.java

@@ -24,10 +24,10 @@ package password.pwm;
 
 
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.Display;
-import password.pwm.util.Helper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.db.DatabaseAccessor;
 import password.pwm.util.db.DatabaseAccessor;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmRandom;
 import password.pwm.util.secure.PwmRandom;
 
 
@@ -160,8 +160,8 @@ public enum PwmAboutProperty {
             aboutMap.put(app_localDbLogSize,       Integer.toString(pwmApplication.getLocalDBLogger().getStoredEventCount()));
             aboutMap.put(app_localDbLogSize,       Integer.toString(pwmApplication.getLocalDBLogger().getStoredEventCount()));
             aboutMap.put(app_localDbLogOldestTime, dateFormatForInfoBean(pwmApplication.getLocalDBLogger().getTailDate()));
             aboutMap.put(app_localDbLogOldestTime, dateFormatForInfoBean(pwmApplication.getLocalDBLogger().getTailDate()));
 
 
-            aboutMap.put(app_localDbStorageSize,   Helper.formatDiskSize(FileSystemUtility.getFileDirectorySize(pwmApplication.getLocalDB().getFileLocation())));
-            aboutMap.put(app_localDbFreeSpace,     Helper.formatDiskSize(FileSystemUtility.diskSpaceRemaining(pwmApplication.getLocalDB().getFileLocation())));
+            aboutMap.put(app_localDbStorageSize,   StringUtil.formatDiskSize(FileSystemUtility.getFileDirectorySize(pwmApplication.getLocalDB().getFileLocation())));
+            aboutMap.put(app_localDbFreeSpace,     StringUtil.formatDiskSize(FileSystemUtility.diskSpaceRemaining(pwmApplication.getLocalDB().getFileLocation())));
         }
         }
 
 
 
 

+ 16 - 2
src/main/java/password/pwm/PwmApplication.java

@@ -56,7 +56,6 @@ import password.pwm.svc.token.TokenService;
 import password.pwm.svc.wordlist.SeedlistManager;
 import password.pwm.svc.wordlist.SeedlistManager;
 import password.pwm.svc.wordlist.SharedHistoryManager;
 import password.pwm.svc.wordlist.SharedHistoryManager;
 import password.pwm.svc.wordlist.WordlistManager;
 import password.pwm.svc.wordlist.WordlistManager;
-import password.pwm.util.Helper;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.VersionChecker;
 import password.pwm.util.VersionChecker;
 import password.pwm.util.cli.commands.ExportHttpsTomcatConfigCommand;
 import password.pwm.util.cli.commands.ExportHttpsTomcatConfigCommand;
@@ -272,7 +271,7 @@ public class PwmApplication {
                 }
                 }
             };
             };
             postInitThread.setDaemon(true);
             postInitThread.setDaemon(true);
-            postInitThread.setName(Helper.makeThreadName(this, PwmApplication.class));
+            postInitThread.setName(JavaHelper.makeThreadName(this, PwmApplication.class));
             postInitThread.start();
             postInitThread.start();
         }
         }
     }
     }
@@ -780,6 +779,21 @@ public class PwmApplication {
         }
         }
         return tempDirectory;
         return tempDirectory;
     }
     }
+
+    public boolean determineIfDetailErrorMsgShown() {
+        final PwmApplicationMode mode = this.getApplicationMode();
+        if (mode == PwmApplicationMode.CONFIGURATION || mode == PwmApplicationMode.NEW) {
+            return true;
+        }
+        if (mode == PwmApplicationMode.RUNNING) {
+            if (this.getConfig() != null) {
+                if (this.getConfig().readSettingAsBoolean(PwmSetting.DISPLAY_SHOW_DETAILED_ERRORS)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 }
 }
 
 
 
 

+ 1 - 1
src/main/java/password/pwm/config/Configuration.java

@@ -59,9 +59,9 @@ import password.pwm.config.value.UserPermissionValue;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.util.java.JsonUtil;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
+import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogLevel;
 import password.pwm.util.logging.PwmLogLevel;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;

+ 2 - 3
src/main/java/password/pwm/health/HealthMonitor.java

@@ -26,7 +26,6 @@ import password.pwm.PwmApplication;
 import password.pwm.config.option.DataStorageMethod;
 import password.pwm.config.option.DataStorageMethod;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmException;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.Helper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -126,8 +125,8 @@ public class HealthMonitor implements PwmService {
         settings = HealthMonitorSettings.fromConfiguration(pwmApplication.getConfig());
         settings = HealthMonitorSettings.fromConfiguration(pwmApplication.getConfig());
 
 
         executorService = Executors.newSingleThreadScheduledExecutor(
         executorService = Executors.newSingleThreadScheduledExecutor(
-                Helper.makePwmThreadFactory(
-                        Helper.makeThreadName(pwmApplication, this.getClass()) + "-",
+                JavaHelper.makePwmThreadFactory(
+                        JavaHelper.makeThreadName(pwmApplication, this.getClass()) + "-",
                         true
                         true
                 ));
                 ));
 
 

+ 1 - 2
src/main/java/password/pwm/http/ContextManager.java

@@ -35,7 +35,6 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.util.Helper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmRandom;
 import password.pwm.util.secure.PwmRandom;
@@ -205,7 +204,7 @@ public class ContextManager implements Serializable {
             handleStartupError("unable to initialize application: ", e);
             handleStartupError("unable to initialize application: ", e);
         }
         }
 
 
-        final String threadName = Helper.makeThreadName(pwmApplication, this.getClass()) + " timer";
+        final String threadName = JavaHelper.makeThreadName(pwmApplication, this.getClass()) + " timer";
         taskMaster = new Timer(threadName, true);
         taskMaster = new Timer(threadName, true);
         taskMaster.schedule(new RestartFlagWatcher(), 1031, 1031);
         taskMaster.schedule(new RestartFlagWatcher(), 1031, 1031);
 
 

+ 1 - 2
src/main/java/password/pwm/http/PwmResponse.java

@@ -30,7 +30,6 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
-import password.pwm.util.Helper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -162,7 +161,7 @@ public class PwmResponse extends PwmHttpResponseWrapper {
                 LOGGER.error("unexpected error sending user to error page: " + e.toString());
                 LOGGER.error("unexpected error sending user to error page: " + e.toString());
             }
             }
         } else {
         } else {
-            final boolean showDetail = Helper.determineIfDetailErrorMsgShown(pwmRequest.getPwmApplication());
+            final boolean showDetail = pwmRequest.getPwmApplication().determineIfDetailErrorMsgShown();
             final String errorStatusText = showDetail
             final String errorStatusText = showDetail
                     ? errorInformation.toDebugStr()
                     ? errorInformation.toDebugStr()
                     : errorInformation.toUserStr(pwmRequest.getPwmSession(),pwmRequest.getPwmApplication());
                     : errorInformation.toUserStr(pwmRequest.getPwmSession(),pwmRequest.getPwmApplication());

+ 99 - 4
src/main/java/password/pwm/http/filter/SessionFilter.java

@@ -28,6 +28,7 @@ import password.pwm.PwmApplicationMode;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.LoginInfoBean;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.Configuration;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.SessionVerificationMode;
 import password.pwm.config.option.SessionVerificationMode;
@@ -45,7 +46,6 @@ import password.pwm.http.PwmResponse;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.util.Helper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -53,11 +53,15 @@ import password.pwm.util.logging.PwmLogger;
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
 import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.List;
+import java.util.regex.Pattern;
 
 
 /**
 /**
  * This session filter (invoked by the container through the web.xml descriptor) wraps all calls to the
  * This session filter (invoked by the container through the web.xml descriptor) wraps all calls to the
@@ -69,7 +73,6 @@ import java.util.List;
  * @author Jason D. Rivard
  * @author Jason D. Rivard
  */
  */
 public class SessionFilter extends AbstractPwmFilter {
 public class SessionFilter extends AbstractPwmFilter {
-// ------------------------------ FIELDS ------------------------------
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(SessionFilter.class);
     private static final PwmLogger LOGGER = PwmLogger.forClass(SessionFilter.class);
 
 
@@ -185,7 +188,7 @@ public class SessionFilter extends AbstractPwmFilter {
             final String forwardURL = pwmRequest.readParameterAsString(forwardURLParamName);
             final String forwardURL = pwmRequest.readParameterAsString(forwardURLParamName);
             if (forwardURL != null && forwardURL.length() > 0) {
             if (forwardURL != null && forwardURL.length() > 0) {
                 try {
                 try {
-                    Helper.checkUrlAgainstWhitelist(pwmApplication, pwmRequest.getSessionLabel(), forwardURL);
+                    checkUrlAgainstWhitelist(pwmApplication, pwmRequest.getSessionLabel(), forwardURL);
                 } catch (PwmOperationalException e) {
                 } catch (PwmOperationalException e) {
                     LOGGER.error(pwmRequest, e.getErrorInformation());
                     LOGGER.error(pwmRequest, e.getErrorInformation());
                     pwmRequest.respondWithError(e.getErrorInformation());
                     pwmRequest.respondWithError(e.getErrorInformation());
@@ -201,7 +204,7 @@ public class SessionFilter extends AbstractPwmFilter {
             final String logoutURL = pwmRequest.readParameterAsString(logoutURLParamName);
             final String logoutURL = pwmRequest.readParameterAsString(logoutURLParamName);
             if (logoutURL != null && logoutURL.length() > 0) {
             if (logoutURL != null && logoutURL.length() > 0) {
                 try {
                 try {
-                    Helper.checkUrlAgainstWhitelist(pwmApplication, pwmRequest.getSessionLabel(), logoutURL);
+                    checkUrlAgainstWhitelist(pwmApplication, pwmRequest.getSessionLabel(), logoutURL);
                 } catch (PwmOperationalException e) {
                 } catch (PwmOperationalException e) {
                     LOGGER.error(pwmRequest, e.getErrorInformation());
                     LOGGER.error(pwmRequest, e.getErrorInformation());
                     pwmRequest.respondWithError(e.getErrorInformation());
                     pwmRequest.respondWithError(e.getErrorInformation());
@@ -423,4 +426,96 @@ public class SessionFilter extends AbstractPwmFilter {
             }
             }
         }
         }
     }
     }
+
+    private static void checkUrlAgainstWhitelist(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final String inputURL
+    )
+            throws PwmOperationalException
+    {
+        LOGGER.trace(sessionLabel, "beginning test of requested redirect URL: " + inputURL);
+        if (inputURL == null || inputURL.isEmpty()) {
+            return;
+        }
+
+        final URI inputURI;
+        try {
+            inputURI = URI.create(inputURL);
+        } catch (IllegalArgumentException e) {
+            LOGGER.error(sessionLabel, "unable to parse requested redirect url '" + inputURL + "', error: " + e.getMessage());
+            // dont put input uri in error response
+            final String errorMsg = "unable to parse url: " + e.getMessage();
+            throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
+        }
+
+        { // check to make sure we werent handed a non-http uri.
+            final String scheme = inputURI.getScheme();
+            if (scheme != null && !scheme.isEmpty() && !scheme.equalsIgnoreCase("http") && !scheme.equals("https")) {
+                final String errorMsg = "unsupported url scheme";
+                throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
+            }
+        }
+
+        if (inputURI.getHost() != null && !inputURI.getHost().isEmpty()) { // disallow localhost uri
+            try {
+                final InetAddress inetAddress = InetAddress.getByName(inputURI.getHost());
+                if (inetAddress.isLoopbackAddress()) {
+                    final String errorMsg = "redirect to loopback host is not permitted";
+                    throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
+                }
+            } catch (UnknownHostException e) {
+                /* noop */
+            }
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        if (inputURI.getScheme() != null) {
+            sb.append(inputURI.getScheme());
+            sb.append("://");
+        }
+        if (inputURI.getHost() != null) {
+            sb.append(inputURI.getHost());
+        }
+        if (inputURI.getPort() != -1) {
+            sb.append(":");
+            sb.append(inputURI.getPort());
+        }
+        if (inputURI.getPath() != null) {
+            sb.append(inputURI.getPath());
+        }
+
+        final String testURI = sb.toString();
+        LOGGER.trace(sessionLabel, "preparing to whitelist test parsed and decoded URL: " + testURI);
+
+        final String REGEX_PREFIX = "regex:";
+        final List<String> whiteList = pwmApplication.getConfig().readSettingAsStringArray(PwmSetting.SECURITY_REDIRECT_WHITELIST);
+        for (final String loopFragment : whiteList) {
+            if (loopFragment.startsWith(REGEX_PREFIX)) {
+                try {
+                    final String strPattern = loopFragment.substring(REGEX_PREFIX.length(), loopFragment.length());
+                    final Pattern pattern = Pattern.compile(strPattern);
+                    if (pattern.matcher(testURI).matches()) {
+                        LOGGER.debug(sessionLabel, "positive URL match for regex pattern: " + strPattern);
+                        return;
+                    } else {
+                        LOGGER.trace(sessionLabel, "negative URL match for regex pattern: " + strPattern);
+                    }
+                } catch (Exception e) {
+                    LOGGER.error(sessionLabel, "error while testing URL match for regex pattern: '" + loopFragment + "', error: " + e.getMessage());;
+                }
+
+            } else {
+                if (testURI.startsWith(loopFragment)) {
+                    LOGGER.debug(sessionLabel, "positive URL match for pattern: " + loopFragment);
+                    return;
+                } else {
+                    LOGGER.trace(sessionLabel, "negative URL match for pattern: " + loopFragment);
+                }
+            }
+        }
+
+        final String errorMsg = testURI + " is not a match for any configured redirect whitelist, see setting: " + PwmSetting.SECURITY_REDIRECT_WHITELIST.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE);
+        throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
+    }
 }
 }

+ 25 - 22
src/main/java/password/pwm/http/servlet/ControlledPwmServlet.java

@@ -23,6 +23,7 @@
 package password.pwm.http.servlet;
 package password.pwm.http.servlet;
 
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
+import password.pwm.PwmConstants;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
@@ -30,6 +31,7 @@ import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmResponse;
 import password.pwm.http.PwmResponse;
 import password.pwm.http.bean.PwmSessionBean;
 import password.pwm.http.bean.PwmSessionBean;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
@@ -38,10 +40,7 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Method;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
 
 
 public abstract class ControlledPwmServlet extends AbstractPwmServlet implements PwmServlet {
 public abstract class ControlledPwmServlet extends AbstractPwmServlet implements PwmServlet {
 
 
@@ -70,6 +69,22 @@ public abstract class ControlledPwmServlet extends AbstractPwmServlet implements
         throw new IllegalStateException("unable to determine PwmServletDefinition for class " + this.getClass().getName());
         throw new IllegalStateException("unable to determine PwmServletDefinition for class " + this.getClass().getName());
     }
     }
 
 
+    public abstract Class<? extends ProcessAction> getProcessActionsClass();
+
+    protected ProcessAction readProcessAction(final PwmRequest request)
+            throws PwmUnrecoverableException
+    {
+        try {
+            final String inputParameter = request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST);
+            final Class processStatusClass = getProcessActionsClass();
+            final Enum answer = JavaHelper.readEnumFromString(processStatusClass, null, inputParameter);
+            return (ProcessAction)answer;
+        } catch (Exception e) {
+            LOGGER.error("error",e);
+        }
+        return null;
+    }
+
     private ProcessStatus dispatchMethod(
     private ProcessStatus dispatchMethod(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
@@ -156,9 +171,9 @@ public abstract class ControlledPwmServlet extends AbstractPwmServlet implements
         String action();
         String action();
     }
     }
 
 
-    private static Method discoverMethodForAction(final Class clazz, final ProcessAction action) {
+    public static Method discoverMethodForAction(final Class clazz, final ProcessAction action) {
         Method interestedMethod = null;
         Method interestedMethod = null;
-        final Collection<Method> methods = getAllMethods(clazz);
+        final Collection<Method> methods = JavaHelper.getAllMethodsForClass(clazz);
         for (final Method method : methods) {
         for (final Method method : methods) {
             if (method.getAnnotation(ActionHandler.class) != null) {
             if (method.getAnnotation(ActionHandler.class) != null) {
                 final String actionName = method.getAnnotation(ActionHandler.class).action();
                 final String actionName = method.getAnnotation(ActionHandler.class).action();
@@ -171,25 +186,13 @@ public abstract class ControlledPwmServlet extends AbstractPwmServlet implements
         return interestedMethod;
         return interestedMethod;
     }
     }
 
 
-    private static Collection<Method> getAllMethods(final Class clazz) {
-        final LinkedHashSet<Method> methods = new LinkedHashSet<>();
-
-        // add local methods;
-        methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
-
-        final Class superClass = clazz.getSuperclass();
-        if (superClass != null) {
-            methods.addAll(getAllMethods(superClass));
-        }
-
-        return Collections.unmodifiableSet(methods);
-    }
-
     protected void setLastError(final PwmRequest pwmRequest, final ErrorInformation errorInformation) throws PwmUnrecoverableException {
     protected void setLastError(final PwmRequest pwmRequest, final ErrorInformation errorInformation) throws PwmUnrecoverableException {
         final Class<? extends PwmSessionBean> beanClass = this.getServletDefinition().getPwmSessionBeanClass();
         final Class<? extends PwmSessionBean> beanClass = this.getServletDefinition().getPwmSessionBeanClass();
-        final PwmSessionBean pwmSessionBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, beanClass);
-        pwmSessionBean.setLastError(errorInformation);
-        pwmRequest.setResponseError(errorInformation);
+        if (beanClass != null) {
+            final PwmSessionBean pwmSessionBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, beanClass);
+            pwmSessionBean.setLastError(errorInformation);
+            pwmRequest.setResponseError(errorInformation);
+        }
     }
     }
 
 
     private void examineLastError(final PwmRequest pwmRequest) throws PwmUnrecoverableException {
     private void examineLastError(final PwmRequest pwmRequest) throws PwmUnrecoverableException {

+ 30 - 46
src/main/java/password/pwm/http/servlet/LoginServlet.java

@@ -31,6 +31,7 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
+import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.http.bean.LoginServletBean;
 import password.pwm.http.bean.LoginServletBean;
@@ -39,7 +40,6 @@ import password.pwm.ldap.auth.PwmAuthenticationSource;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.util.CaptchaUtility;
 import password.pwm.util.CaptchaUtility;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
-import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
@@ -67,8 +67,7 @@ import java.util.Map;
                 PwmConstants.URL_PREFIX_PRIVATE + "/Login"
                 PwmConstants.URL_PREFIX_PRIVATE + "/Login"
         }
         }
 )
 )
-public class LoginServlet extends AbstractPwmServlet {
-// ------------------------------ FIELDS ------------------------------
+public class LoginServlet extends ControlledPwmServlet {
 
 
     private static final PwmLogger LOGGER = PwmLogger.getLogger(LoginServlet.class.getName());
     private static final PwmLogger LOGGER = PwmLogger.getLogger(LoginServlet.class.getName());
 
 
@@ -92,76 +91,58 @@ public class LoginServlet extends AbstractPwmServlet {
         }
         }
     }
     }
 
 
-    protected LoginServletAction readProcessAction(final PwmRequest request)
-            throws PwmUnrecoverableException
-    {
-        try {
-            return LoginServletAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return LoginServletAction.class;
     }
     }
 
 
-
-    public void processAction(
-            final PwmRequest pwmRequest
-    )
-            throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException
-    {
-        final boolean passwordOnly = pwmRequest.isAuthenticated() &&
+    private boolean passwordOnly(final PwmRequest pwmRequest) {
+        return pwmRequest.isAuthenticated() &&
                 pwmRequest.getPwmSession().getLoginInfoBean().getType() == AuthenticationType.AUTH_WITHOUT_PASSWORD;
                 pwmRequest.getPwmSession().getLoginInfoBean().getType() == AuthenticationType.AUTH_WITHOUT_PASSWORD;
 
 
-        final LoginServletAction action = readProcessAction(pwmRequest);
-
-        if (action != null) {
-            switch (action) {
-                case login:
-                    processLogin(pwmRequest, passwordOnly);
-                    break;
-
-                case restLogin:
-                    processRestLogin(pwmRequest, passwordOnly);
-                    break;
-
-                case receiveUrl:
-                    processReceiveUrl(pwmRequest);
-                    break;
-
-                default:
-                    JavaHelper.unhandledSwitchStatement(action);
-            }
-
-            return;
-        }
+    }
 
 
+    @Override
+    protected void nextStep(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException {
+        final boolean passwordOnly = passwordOnly(pwmRequest);
         forwardToJSP(pwmRequest, passwordOnly);
         forwardToJSP(pwmRequest, passwordOnly);
     }
     }
 
 
-    private void processLogin(final PwmRequest pwmRequest, final boolean passwordOnly)
+    @Override
+    public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
+        return ProcessStatus.Continue;
+    }
+
+    @ActionHandler(action = "login")
+    private ProcessStatus processLogin(final PwmRequest pwmRequest)
             throws PwmUnrecoverableException, ServletException, IOException, ChaiUnavailableException
             throws PwmUnrecoverableException, ServletException, IOException, ChaiUnavailableException
     {
     {
+        final boolean passwordOnly = passwordOnly(pwmRequest);
         final Map<String,String> valueMap = pwmRequest.readParametersAsMap();
         final Map<String,String> valueMap = pwmRequest.readParametersAsMap();
         try {
         try {
             handleLoginRequest(pwmRequest, valueMap, passwordOnly);
             handleLoginRequest(pwmRequest, valueMap, passwordOnly);
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
             pwmRequest.setResponseError(e.getErrorInformation());
             pwmRequest.setResponseError(e.getErrorInformation());
             forwardToJSP(pwmRequest, passwordOnly);
             forwardToJSP(pwmRequest, passwordOnly);
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         // login has succeeded
         // login has succeeded
         pwmRequest.sendRedirect(determinePostLoginUrl(pwmRequest));
         pwmRequest.sendRedirect(determinePostLoginUrl(pwmRequest));
+        return ProcessStatus.Halt;
     }
     }
 
 
-    private void processRestLogin(final PwmRequest pwmRequest, final boolean passwordOnly)
+    @ActionHandler(action = "restLogin")
+    private ProcessStatus processRestLogin(final PwmRequest pwmRequest)
             throws PwmUnrecoverableException, ServletException, IOException, ChaiUnavailableException
             throws PwmUnrecoverableException, ServletException, IOException, ChaiUnavailableException
     {
     {
+        final boolean passwordOnly = passwordOnly(pwmRequest);
         final Map<String, String> valueMap = pwmRequest.readBodyAsJsonStringMap();
         final Map<String, String> valueMap = pwmRequest.readBodyAsJsonStringMap();
 
 
         if (valueMap == null || valueMap.isEmpty()) {
         if (valueMap == null || valueMap.isEmpty()) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"missing json request body");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"missing json request body");
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         try {
         try {
@@ -170,7 +151,7 @@ public class LoginServlet extends AbstractPwmServlet {
             final ErrorInformation errorInformation = e.getErrorInformation();
             final ErrorInformation errorInformation = e.getErrorInformation();
             LOGGER.trace(pwmRequest, "returning rest login error to client: " + errorInformation.toDebugStr());
             LOGGER.trace(pwmRequest, "returning rest login error to client: " + errorInformation.toDebugStr());
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         pwmRequest.readParametersAsMap();
         pwmRequest.readParametersAsMap();
@@ -182,9 +163,11 @@ public class LoginServlet extends AbstractPwmServlet {
         restResultBean.setData(resultMap);
         restResultBean.setData(resultMap);
         LOGGER.debug(pwmRequest, "rest login succeeded");
         LOGGER.debug(pwmRequest, "rest login succeeded");
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
-    private void processReceiveUrl(final PwmRequest pwmRequest)
+    @ActionHandler(action = "receiveUrl")
+    private ProcessStatus processReceiveUrl(final PwmRequest pwmRequest)
             throws PwmUnrecoverableException, IOException
             throws PwmUnrecoverableException, IOException
     {
     {
         final String encryptedNextUrl = pwmRequest.readParameterAsString(PwmConstants.PARAM_POST_LOGIN_URL);
         final String encryptedNextUrl = pwmRequest.readParameterAsString(PwmConstants.PARAM_POST_LOGIN_URL);
@@ -198,6 +181,7 @@ public class LoginServlet extends AbstractPwmServlet {
         }
         }
 
 
         pwmRequest.sendRedirect(PwmServletDefinition.Login);
         pwmRequest.sendRedirect(PwmServletDefinition.Login);
+        return ProcessStatus.Halt;
     }
     }
 
 
     private void handleLoginRequest(
     private void handleLoginRequest(

+ 3 - 8
src/main/java/password/pwm/http/servlet/LogoutServlet.java

@@ -75,16 +75,11 @@ public class LogoutServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
-    protected LogoutAction readProcessAction(final PwmRequest request)
-            throws PwmUnrecoverableException {
-        try {
-            return LogoutAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return LogoutAction.class;
     }
     }
 
 
-
     @ActionHandler(action = "showLogout")
     @ActionHandler(action = "showLogout")
     public ProcessStatus processLogoutAction(
     public ProcessStatus processLogoutAction(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest

+ 2 - 1
src/main/java/password/pwm/http/servlet/PwmServletDefinition.java

@@ -27,6 +27,7 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.bean.ForgottenPasswordBean;
 import password.pwm.http.bean.ForgottenPasswordBean;
 import password.pwm.http.bean.LoginServletBean;
 import password.pwm.http.bean.LoginServletBean;
+import password.pwm.http.bean.NewUserBean;
 import password.pwm.http.bean.PwmSessionBean;
 import password.pwm.http.bean.PwmSessionBean;
 import password.pwm.http.bean.UpdateProfileBean;
 import password.pwm.http.bean.UpdateProfileBean;
 import password.pwm.http.servlet.admin.AdminServlet;
 import password.pwm.http.servlet.admin.AdminServlet;
@@ -74,7 +75,7 @@ public enum PwmServletDefinition {
     ConfigManager_LocalDB(ConfigManagerLocalDBServlet.class, null),
     ConfigManager_LocalDB(ConfigManagerLocalDBServlet.class, null),
     ConfigManager_Certificates(ConfigManagerCertificatesServlet.class, null),
     ConfigManager_Certificates(ConfigManagerCertificatesServlet.class, null),
 
 
-    NewUser(NewUserServlet.class, null),
+    NewUser(NewUserServlet.class, NewUserBean.class),
     ActivateUser(password.pwm.http.servlet.ActivateUserServlet.class, null),
     ActivateUser(password.pwm.http.servlet.ActivateUserServlet.class, null),
     ForgottenPassword(password.pwm.http.servlet.forgottenpw.ForgottenPasswordServlet.class, ForgottenPasswordBean.class),
     ForgottenPassword(password.pwm.http.servlet.forgottenpw.ForgottenPasswordServlet.class, ForgottenPasswordBean.class),
     ForgottenUsername(password.pwm.http.servlet.ForgottenUsernameServlet.class, null),
     ForgottenUsername(password.pwm.http.servlet.ForgottenUsernameServlet.class, null),

+ 3 - 8
src/main/java/password/pwm/http/servlet/SetupResponsesServlet.java

@@ -111,14 +111,9 @@ public class SetupResponsesServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
-    protected SetupResponsesAction readProcessAction(final PwmRequest request)
-            throws PwmUnrecoverableException
-    {
-        try {
-            return SetupResponsesAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return SetupResponsesAction.class;
     }
     }
 
 
     private SetupResponsesBean getSetupResponseBean(final PwmRequest pwmRequest) throws PwmUnrecoverableException {
     private SetupResponsesBean getSetupResponseBean(final PwmRequest pwmRequest) throws PwmUnrecoverableException {

+ 3 - 63
src/main/java/password/pwm/http/servlet/UpdateProfileServlet.java

@@ -118,14 +118,9 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
-    protected UpdateProfileAction readProcessAction(final PwmRequest request)
-            throws PwmUnrecoverableException
-    {
-        try {
-            return UpdateProfileAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return UpdateProfileAction.class;
     }
     }
 
 
     private static UpdateAttributesProfile getProfile(final PwmRequest pwmRequest) {
     private static UpdateAttributesProfile getProfile(final PwmRequest pwmRequest) {
@@ -136,61 +131,6 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
         return pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, UpdateProfileBean.class);
         return pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, UpdateProfileBean.class);
     }
     }
 
 
-    /*
-    protected void processAction(final PwmRequest pwmRequest)
-            throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException
-    {
-        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
-        final UpdateProfileBean updateProfileBean = getBean(pwmRequest);
-        final UpdateAttributesProfile updateAttributesProfile = getProfile(pwmRequest);
-
-        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_ENABLE)) {
-            pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "Setting " + PwmSetting.UPDATE_PROFILE_ENABLE.toMenuLocationDebug(null,null) + " is not enabled."));
-            return;
-        }
-
-        if (updateAttributesProfile == null) {
-            pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_NO_PROFILE_ASSIGNED));
-            return;
-        }
-
-        final UpdateProfileAction action = readProcessAction(pwmRequest);
-        if (action != null) {
-            pwmRequest.validatePwmFormID();
-            switch(action) {
-                case updateProfile:
-                    handleUpdateRequest(pwmRequest, updateAttributesProfile, updateProfileBean);
-                    break;
-
-                case agree:
-                    handleAgreeRequest(pwmRequest, updateProfileBean);
-                    break;
-
-                case confirm:
-                    updateProfileBean.setConfirmationPassed(true);
-                    break;
-
-                case unConfirm:
-                    handleUnconfirm(updateProfileBean);
-                    break;
-
-                case validate:
-                    restValidateForm(pwmRequest, updateAttributesProfile, updateProfileBean);
-                    return;
-
-                case enterCode:
-                    handleEnterCodeRequest(pwmRequest, updateProfileBean);
-                    break;
-
-                default:
-                    JavaHelper.unhandledSwitchStatement(action);
-            }
-        }
-
-        nextStep(pwmRequest);
-    }
-    */
-
     @ActionHandler(action = "enterCode")
     @ActionHandler(action = "enterCode")
     ProcessStatus handleEnterCodeRequest(
     ProcessStatus handleEnterCodeRequest(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest

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

@@ -29,7 +29,6 @@ import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.Permission;
 import password.pwm.Permission;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
-import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.LoginInfoBean;
@@ -113,16 +112,12 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
-    protected ChangePasswordAction readProcessAction(final PwmRequest request)
-            throws PwmUnrecoverableException
-    {
-        try {
-            return ChangePasswordAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return ChangePasswordServlet.ChangePasswordAction.class;
     }
     }
 
 
+
     @ActionHandler(action = "reset")
     @ActionHandler(action = "reset")
     ProcessStatus processResetAction(final PwmRequest pwmRequest) throws ServletException, PwmUnrecoverableException, IOException {
     ProcessStatus processResetAction(final PwmRequest pwmRequest) throws ServletException, PwmUnrecoverableException, IOException {
 
 

+ 1 - 2
src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerServlet.java

@@ -53,7 +53,6 @@ import password.pwm.svc.PwmService;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
-import password.pwm.util.Helper;
 import password.pwm.util.LDAPPermissionCalculator;
 import password.pwm.util.LDAPPermissionCalculator;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -384,7 +383,7 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
     {
     {
         pwmRequest.getPwmResponse().markAsDownload(PwmConstants.ContentTypeValue.csv, PwmConstants.DOWNLOAD_FILENAME_LDAP_PERMISSION_CSV);
         pwmRequest.getPwmResponse().markAsDownload(PwmConstants.ContentTypeValue.csv, PwmConstants.DOWNLOAD_FILENAME_LDAP_PERMISSION_CSV);
 
 
-        final CSVPrinter csvPrinter = Helper.makeCsvPrinter(pwmRequest.getPwmResponse().getOutputStream());
+        final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(pwmRequest.getPwmResponse().getOutputStream());
         try {
         try {
 
 
             final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest);
             final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest);

+ 6 - 7
src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java

@@ -36,7 +36,6 @@ import password.pwm.health.HealthRecord;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.ldap.LdapDebugDataGenerator;
 import password.pwm.ldap.LdapDebugDataGenerator;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.Helper;
 import password.pwm.util.LDAPPermissionCalculator;
 import password.pwm.util.LDAPPermissionCalculator;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -109,7 +108,7 @@ public class DebugItemGenerator {
         final String DEBUG_FILENAME = "zipDebugGeneration.csv";
         final String DEBUG_FILENAME = "zipDebugGeneration.csv";
 
 
         final ByteArrayOutputStream debugGeneratorLogBaos = new ByteArrayOutputStream();
         final ByteArrayOutputStream debugGeneratorLogBaos = new ByteArrayOutputStream();
-        final CSVPrinter debugGeneratorLogFile = Helper.makeCsvPrinter(debugGeneratorLogBaos);
+        final CSVPrinter debugGeneratorLogFile = JavaHelper.makeCsvPrinter(debugGeneratorLogBaos);
 
 
         for (final Class<? extends DebugItemGenerator.Generator> serviceClass : DEBUG_ZIP_ITEM_GENERATORS) {
         for (final Class<? extends DebugItemGenerator.Generator> serviceClass : DEBUG_ZIP_ITEM_GENERATORS) {
             try {
             try {
@@ -248,7 +247,7 @@ public class DebugItemGenerator {
         @Override
         @Override
         public void outputItem(final PwmApplication pwmApplication, final PwmRequest pwmRequest, final OutputStream outputStream) throws Exception
         public void outputItem(final PwmApplication pwmApplication, final PwmRequest pwmRequest, final OutputStream outputStream) throws Exception
         {
         {
-            final Properties outputProps = Helper.newSortedProperties();
+            final Properties outputProps = JavaHelper.newSortedProperties();
 
 
             // java threads
             // java threads
             final Map<String,String> envProps = System.getenv();
             final Map<String,String> envProps = System.getenv();
@@ -272,7 +271,7 @@ public class DebugItemGenerator {
         {
         {
 
 
             final Configuration config = pwmRequest.getConfig();
             final Configuration config = pwmRequest.getConfig();
-            final Properties outputProps = Helper.newSortedProperties();
+            final Properties outputProps = JavaHelper.newSortedProperties();
 
 
             for (final AppProperty appProperty : AppProperty.values()) {
             for (final AppProperty appProperty : AppProperty.values()) {
                 outputProps.setProperty(appProperty.getKey(), config.readAppProperty(appProperty));
                 outputProps.setProperty(appProperty.getKey(), config.readAppProperty(appProperty));
@@ -403,7 +402,7 @@ public class DebugItemGenerator {
             }
             }
 
 
             {
             {
-                final CSVPrinter csvPrinter = Helper.makeCsvPrinter(outputStream);
+                final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(outputStream);
                 {
                 {
                     final List<String> headerRow = new ArrayList<>();
                     final List<String> headerRow = new ArrayList<>();
                     headerRow.add("Filepath");
                     headerRow.add("Filepath");
@@ -482,7 +481,7 @@ public class DebugItemGenerator {
             final StoredConfigurationImpl storedConfiguration = ConfigManagerServlet.readCurrentConfiguration(pwmRequest);
             final StoredConfigurationImpl storedConfiguration = ConfigManagerServlet.readCurrentConfiguration(pwmRequest);
             final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(storedConfiguration);
             final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(storedConfiguration);
 
 
-            final CSVPrinter csvPrinter = Helper.makeCsvPrinter(outputStream);
+            final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(outputStream);
             {
             {
                 final List<String> headerRow = new ArrayList<>();
                 final List<String> headerRow = new ArrayList<>();
                 headerRow.add("Attribute");
                 headerRow.add("Attribute");
@@ -538,7 +537,7 @@ public class DebugItemGenerator {
         ) throws Exception {
         ) throws Exception {
 
 
 
 
-            final CSVPrinter csvPrinter = Helper.makeCsvPrinter(outputStream);
+            final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(outputStream);
             {
             {
                 final List<String> headerRow = new ArrayList<>();
                 final List<String> headerRow = new ArrayList<>();
                 headerRow.add("Label");
                 headerRow.add("Label");

+ 5 - 10
src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java

@@ -169,6 +169,11 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return ForgottenPasswordAction.class;
+    }
+
     public enum ActionChoice {
     public enum ActionChoice {
         unlock,
         unlock,
         resetPassword,
         resetPassword,
@@ -179,16 +184,6 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         sms,
         sms,
     }
     }
 
 
-    protected ForgottenPasswordAction readProcessAction(final PwmRequest request)
-            throws PwmUnrecoverableException
-    {
-        try {
-            return ForgottenPasswordAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
-    }
-
     @Override
     @Override
     public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
     public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
 
 

+ 109 - 1
src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskDetailInfoBean.java

@@ -22,16 +22,40 @@
 
 
 package password.pwm.http.servlet.helpdesk;
 package password.pwm.http.servlet.helpdesk;
 
 
+import com.novell.ldapchai.ChaiUser;
+import com.novell.ldapchai.exception.ChaiUnavailableException;
+import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.config.FormConfiguration;
 import password.pwm.config.FormConfiguration;
+import password.pwm.config.FormUtility;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.profile.HelpdeskProfile;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.PwmRequest;
+import password.pwm.i18n.Display;
+import password.pwm.ldap.LdapUserDataReader;
+import password.pwm.ldap.UserDataReader;
+import password.pwm.ldap.UserStatusReader;
 import password.pwm.svc.event.UserAuditRecord;
 import password.pwm.svc.event.UserAuditRecord;
-
+import password.pwm.util.LocaleHelper;
+import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.TimeDuration;
+import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.macro.MacroMachine;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.time.Instant;
 import java.time.Instant;
+import java.util.Date;
 import java.util.List;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
 
 
 public class HelpdeskDetailInfoBean implements Serializable {
 public class HelpdeskDetailInfoBean implements Serializable {
+    private static final PwmLogger LOGGER = PwmLogger.forClass(HelpdeskDetailInfoBean.class);
+
+
     private UserInfoBean userInfoBean = new UserInfoBean();
     private UserInfoBean userInfoBean = new UserInfoBean();
     private String userDisplayName;
     private String userDisplayName;
 
 
@@ -44,6 +68,90 @@ public class HelpdeskDetailInfoBean implements Serializable {
     private Map<FormConfiguration, List<String>> searchDetails;
     private Map<FormConfiguration, List<String>> searchDetails;
     private String passwordSetDelta;
     private String passwordSetDelta;
 
 
+    static HelpdeskDetailInfoBean makeHelpdeskDetailInfo(
+            final PwmRequest pwmRequest,
+            final HelpdeskProfile helpdeskProfile,
+            final UserIdentity userIdentity
+    )
+            throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
+    {
+        final Instant startTime = Instant.now();
+        LOGGER.trace(pwmRequest, "beginning to assemble detail data report for user " + userIdentity);
+        final Locale actorLocale = pwmRequest.getLocale();
+        final ChaiUser theUser = HelpdeskServlet.getChaiUser(pwmRequest, helpdeskProfile, userIdentity);
+
+        if (!theUser.isValid()) {
+            return null;
+        }
+
+        final HelpdeskDetailInfoBean detailInfo = new HelpdeskDetailInfoBean();
+        final UserInfoBean uiBean = detailInfo.getUserInfoBean();
+        final UserStatusReader userStatusReader = new UserStatusReader(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
+        userStatusReader.populateUserInfoBean(uiBean, actorLocale, userIdentity, theUser.getChaiProvider());
+
+        try {
+            detailInfo.setIntruderLocked(theUser.isPasswordLocked());
+        } catch (Exception e) {
+            LOGGER.error(pwmRequest, "unexpected error reading intruder lock status for user '" + userIdentity + "', " + e.getMessage());
+        }
+
+        try {
+            detailInfo.setAccountEnabled(theUser.isAccountEnabled());
+        } catch (Exception e) {
+            LOGGER.error(pwmRequest, "unexpected error reading account enabled status for user '" + userIdentity + "', " + e.getMessage());
+        }
+
+        try {
+            detailInfo.setAccountExpired(theUser.isAccountExpired());
+        } catch (Exception e) {
+            LOGGER.error(pwmRequest, "unexpected error reading account expired status for user '" + userIdentity + "', " + e.getMessage());
+        }
+
+        try {
+            final Date lastLoginTime = theUser.readLastLoginTime();
+            detailInfo.setLastLoginTime(lastLoginTime == null ? null : lastLoginTime.toInstant());
+        } catch (Exception e) {
+            LOGGER.error(pwmRequest, "unexpected error reading last login time for user '" + userIdentity + "', " + e.getMessage());
+        }
+
+        try {
+            detailInfo.setUserHistory(pwmRequest.getPwmApplication().getAuditManager().readUserHistory(uiBean));
+        } catch (Exception e) {
+            LOGGER.error(pwmRequest, "unexpected error reading userHistory for user '" + userIdentity + "', " + e.getMessage());
+        }
+
+        if (uiBean.getPasswordLastModifiedTime() != null) {
+            final TimeDuration passwordSetDelta = TimeDuration.fromCurrent(uiBean.getPasswordLastModifiedTime());
+            detailInfo.setPasswordSetDelta(passwordSetDelta.asLongString(pwmRequest.getLocale()));
+        } else {
+            detailInfo.setPasswordSetDelta(LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable, pwmRequest));
+        }
+
+        final UserDataReader userDataReader = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY)
+                ? LdapUserDataReader.appProxiedReader(pwmRequest.getPwmApplication(), userIdentity)
+                : LdapUserDataReader.selfProxiedReader(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
+
+        {
+            final List<FormConfiguration> detailFormConfig = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_DETAIL_FORM);
+            final Map<FormConfiguration,List<String>> formData = FormUtility.populateFormMapFromLdap(detailFormConfig, pwmRequest.getPwmSession().getLabel(), userDataReader);
+            detailInfo.setSearchDetails(formData);
+        }
+
+        final String configuredDisplayName = helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_DETAIL_DISPLAY_NAME);
+        if (configuredDisplayName != null && !configuredDisplayName.isEmpty()) {
+            final MacroMachine macroMachine = new MacroMachine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), detailInfo.getUserInfoBean(), null, userDataReader);
+            final String displayName = macroMachine.expandMacros(configuredDisplayName);
+            detailInfo.setUserDisplayName(displayName);
+        }
+
+        final TimeDuration timeDuration = TimeDuration.fromCurrent(startTime);
+        if (pwmRequest.getConfig().isDevDebugMode()) {
+            LOGGER.trace(pwmRequest, "completed assembly of detail data report for user " + userIdentity
+                    + " in " + timeDuration.asCompactString() + ", contents: " + JsonUtil.serialize(detailInfo));
+        }
+        return detailInfo;
+    }
+
     public String getUserDisplayName() {
     public String getUserDisplayName() {
         return userDisplayName;
         return userDisplayName;
     }
     }

+ 112 - 225
src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java

@@ -38,7 +38,6 @@ import password.pwm.bean.UserInfoBean;
 import password.pwm.config.ActionConfiguration;
 import password.pwm.config.ActionConfiguration;
 import password.pwm.config.Configuration;
 import password.pwm.config.Configuration;
 import password.pwm.config.FormConfiguration;
 import password.pwm.config.FormConfiguration;
-import password.pwm.config.FormUtility;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.HelpdeskClearResponseMode;
 import password.pwm.config.option.HelpdeskClearResponseMode;
 import password.pwm.config.option.HelpdeskUIMode;
 import password.pwm.config.option.HelpdeskUIMode;
@@ -52,18 +51,18 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
+import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.AbstractPwmServlet;
-import password.pwm.i18n.Display;
+import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
 import password.pwm.ldap.LdapOperationsHelper;
 import password.pwm.ldap.LdapOperationsHelper;
 import password.pwm.ldap.LdapPermissionTester;
 import password.pwm.ldap.LdapPermissionTester;
 import password.pwm.ldap.LdapUserDataReader;
 import password.pwm.ldap.LdapUserDataReader;
 import password.pwm.ldap.UserDataReader;
 import password.pwm.ldap.UserDataReader;
 import password.pwm.ldap.UserSearchEngine;
 import password.pwm.ldap.UserSearchEngine;
-import password.pwm.ldap.UserStatusReader;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.HelpdeskAuditRecord;
 import password.pwm.svc.event.HelpdeskAuditRecord;
@@ -71,7 +70,6 @@ import password.pwm.svc.intruder.IntruderManager;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenService;
-import password.pwm.util.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
@@ -112,7 +110,7 @@ import java.util.Map;
                 PwmConstants.URL_PREFIX_PRIVATE + "/Helpdesk",
                 PwmConstants.URL_PREFIX_PRIVATE + "/Helpdesk",
         }
         }
 )
 )
-public class HelpdeskServlet extends AbstractPwmServlet {
+public class HelpdeskServlet extends ControlledPwmServlet {
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(HelpdeskServlet.class);
     private static final PwmLogger LOGGER = PwmLogger.forClass(HelpdeskServlet.class);
 
 
@@ -145,105 +143,51 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         }
         }
     }
     }
 
 
-    protected HelpdeskAction readProcessAction(final PwmRequest request)
-            throws PwmUnrecoverableException
-    {
-        try {
-            return HelpdeskAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return HelpdeskAction.class;
+    }
+
+
+    private HelpdeskProfile getHelpdeskRProfile(final PwmRequest pwmRequest) {
+        return pwmRequest.getPwmSession().getSessionManager().getHelpdeskProfile(pwmRequest.getPwmApplication());
     }
     }
 
 
-    protected void processAction(final PwmRequest pwmRequest)
-            throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException
+    @Override
+    protected void nextStep(final PwmRequest pwmRequest)
+            throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
+        pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskVerificationEnabled, !helpdeskProfile.readRequiredVerificationMethods().isEmpty());
+        pwmRequest.forwardToJsp(JspUrl.HELPDESK_SEARCH);
+    }
+
+    @Override
+    public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
 
         if (!pwmRequest.isAuthenticated()) {
         if (!pwmRequest.isAuthenticated()) {
             pwmRequest.respondWithError(PwmError.ERROR_AUTHENTICATION_REQUIRED.toInfo());
             pwmRequest.respondWithError(PwmError.ERROR_AUTHENTICATION_REQUIRED.toInfo());
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.HELPDESK_ENABLE)) {
         if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.HELPDESK_ENABLE)) {
             pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "Setting " + PwmSetting.HELPDESK_ENABLE.toMenuLocationDebug(null,null) + " is not enabled."));
             pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "Setting " + PwmSetting.HELPDESK_ENABLE.toMenuLocationDebug(null,null) + " is not enabled."));
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         final HelpdeskProfile helpdeskProfile = pwmRequest.getPwmSession().getSessionManager().getHelpdeskProfile(pwmApplication);
         final HelpdeskProfile helpdeskProfile = pwmRequest.getPwmSession().getSessionManager().getHelpdeskProfile(pwmApplication);
         if (helpdeskProfile == null) {
         if (helpdeskProfile == null) {
             pwmRequest.respondWithError(PwmError.ERROR_UNAUTHORIZED.toInfo());
             pwmRequest.respondWithError(PwmError.ERROR_UNAUTHORIZED.toInfo());
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
-        final HelpdeskAction action = readProcessAction(pwmRequest);
-        if (action != null) {
-            pwmRequest.validatePwmFormID();
-
-            switch (action) {
-                case search:
-                    restSearchRequest(pwmRequest, helpdeskProfile);
-                    return;
-
-                case detail:
-                    processDetailRequest(pwmRequest, helpdeskProfile);
-                    return;
-
-                case executeAction:
-                    processExecuteActionRequest(pwmRequest, helpdeskProfile);
-                    return;
-
-                case deleteUser:
-                    restDeleteUserRequest(pwmRequest, helpdeskProfile);
-                    return;
-
-                case validateOtpCode:
-                    restValidateOtpCodeRequest(pwmRequest, helpdeskProfile);
-                    return;
-
-                case unlockIntruder:
-                    restUnlockPassword(pwmRequest, helpdeskProfile);
-                    return;
-
-                case clearOtpSecret:
-                    restClearOtpSecret(pwmRequest, helpdeskProfile);
-                    return;
-
-                case sendVerificationToken:
-                    restSendVerificationTokenRequest(pwmRequest, helpdeskProfile);
-                    return;
-
-                case verifyVerificationToken:
-                    restVerifyVerificationTokenRequest(pwmRequest);
-                    return;
-
-                case clientData:
-                    restClientData(pwmRequest, helpdeskProfile);
-                    return;
-
-                case checkVerification:
-                    restCheckVerification(pwmRequest, helpdeskProfile);
-                    return;
-
-                case showVerifications:
-                    restShowVerifications(pwmRequest);
-                    return;
-
-                case validateAttributes:
-                    restValidateAttributes(pwmRequest, helpdeskProfile);
-                    return;
-
-                default:
-                    JavaHelper.unhandledSwitchStatement(action);
-            }
-        }
-
-        pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskVerificationEnabled, !helpdeskProfile.readRequiredVerificationMethods().isEmpty());
-        pwmRequest.forwardToJsp(JspUrl.HELPDESK_SEARCH);
+        return ProcessStatus.Continue;
     }
     }
 
 
-    private void restClientData(final PwmRequest pwmRequest, final HelpdeskProfile helpdeskProfile)
+    @ActionHandler(action = "clientData")
+    private ProcessStatus restClientData(final PwmRequest pwmRequest)
             throws IOException, PwmUnrecoverableException {
             throws IOException, PwmUnrecoverableException {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final HelpdeskClientDataBean returnValues = new HelpdeskClientDataBean();
         final HelpdeskClientDataBean returnValues = new HelpdeskClientDataBean();
         { // search page
         { // search page
             final List<FormConfiguration> searchForm = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_SEARCH_FORM);
             final List<FormConfiguration> searchForm = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_SEARCH_FORM);
@@ -295,20 +239,22 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         final RestResultBean restResultBean = new RestResultBean(returnValues);
         final RestResultBean restResultBean = new RestResultBean(returnValues);
         LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
         LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
-    private void processExecuteActionRequest(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile
+    @ActionHandler(action = "executeAction")
+    private ProcessStatus processExecuteActionRequest(
+            final PwmRequest pwmRequest
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final String userKey = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation).get("userKey");
         final String userKey = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation).get("userKey");
         if (userKey == null || userKey.length() < 1) {
         if (userKey == null || userKey.length() < 1) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
-            pwmRequest.setResponseError(errorInformation);
+            setLastError(pwmRequest, errorInformation);
             pwmRequest.respondWithError(errorInformation, false);
             pwmRequest.respondWithError(errorInformation, false);
-            return;
+            return ProcessStatus.Halt;
         }
         }
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
         LOGGER.debug(pwmRequest, "received executeAction request for user " + userIdentity.toString());
         LOGGER.debug(pwmRequest, "received executeAction request for user " + userIdentity.toString());
@@ -328,7 +274,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             LOGGER.debug(pwmRequest, errorInformation.toDebugStr());
             LOGGER.debug(pwmRequest, errorInformation.toDebugStr());
             final RestResultBean restResultBean = RestResultBean.fromError(errorInformation, pwmRequest);
             final RestResultBean restResultBean = RestResultBean.fromError(errorInformation, pwmRequest);
             pwmRequest.outputJsonResult(restResultBean);
             pwmRequest.outputJsonResult(restResultBean);
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         // check if user should be seen by actor
         // check if user should be seen by actor
@@ -364,28 +310,31 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest.getLocale(), pwmRequest.getConfig(), Message.Success_Action, action.getName());
             final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest.getLocale(), pwmRequest.getConfig(), Message.Success_Action, action.getName());
 
 
             pwmRequest.outputJsonResult(restResultBean);
             pwmRequest.outputJsonResult(restResultBean);
+            return ProcessStatus.Halt;
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
             LOGGER.error(pwmRequest, e.getErrorInformation().toDebugStr());
             LOGGER.error(pwmRequest, e.getErrorInformation().toDebugStr());
             final RestResultBean restResultBean = RestResultBean.fromError(e.getErrorInformation(), pwmRequest);
             final RestResultBean restResultBean = RestResultBean.fromError(e.getErrorInformation(), pwmRequest);
             pwmRequest.outputJsonResult(restResultBean);
             pwmRequest.outputJsonResult(restResultBean);
+            return ProcessStatus.Halt;
         }
         }
     }
     }
 
 
-    private void restDeleteUserRequest(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile
+    @ActionHandler(action = "deleteUser")
+    private ProcessStatus restDeleteUserRequest(
+            final PwmRequest pwmRequest
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
 
 
         final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
         final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
         if (userKey.length() < 1) {
         if (userKey.length() < 1) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
-            pwmRequest.setResponseError(errorInformation);
+            setLastError(pwmRequest, errorInformation);
             pwmRequest.respondWithError(errorInformation, false);
             pwmRequest.respondWithError(errorInformation, false);
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmApplication);
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmApplication);
@@ -415,7 +364,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
             LOGGER.debug(pwmRequest, errorMsg);
             LOGGER.debug(pwmRequest, errorMsg);
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         // mark the event log
         // mark the event log
@@ -442,19 +391,21 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         final RestResultBean restResultBean = new RestResultBean();
         final RestResultBean restResultBean = new RestResultBean();
         restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmSession.getSessionStateBean().getLocale(),Message.Success_Unknown,pwmApplication.getConfig()));
         restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmSession.getSessionStateBean().getLocale(),Message.Success_Unknown,pwmApplication.getConfig()));
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
-    private void processDetailRequest(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile
+    @ActionHandler(action = "detail")
+    private ProcessStatus processDetailRequest(
+            final PwmRequest pwmRequest
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
         final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
         if (userKey.length() < 1) {
         if (userKey.length() < 1) {
             pwmRequest.respondWithError(
             pwmRequest.respondWithError(
                     new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing"));
                     new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing"));
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication()).canonicalized(pwmRequest.getPwmApplication());
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication()).canonicalized(pwmRequest.getPwmApplication());
@@ -468,10 +419,9 @@ public class HelpdeskServlet extends AbstractPwmServlet {
                 pwmRequest.getSessionLabel().getSrcHostname()
                 pwmRequest.getSessionLabel().getSrcHostname()
         );
         );
         pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
         pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
-
+        return ProcessStatus.Halt;
     }
     }
 
 
-
     private void processDetailRequest(
     private void processDetailRequest(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final HelpdeskProfile helpdeskProfile,
             final HelpdeskProfile helpdeskProfile,
@@ -503,7 +453,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             return;
             return;
         }
         }
 
 
-        final HelpdeskDetailInfoBean helpdeskDetailInfoBean = makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
+        final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
         pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskDetail, helpdeskDetailInfoBean);
         pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskDetail, helpdeskDetailInfoBean);
 
 
         if (helpdeskDetailInfoBean != null && helpdeskDetailInfoBean.getUserInfoBean() != null) {
         if (helpdeskDetailInfoBean != null && helpdeskDetailInfoBean.getUserInfoBean() != null) {
@@ -517,12 +467,13 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         pwmRequest.forwardToJsp(JspUrl.HELPDESK_DETAIL);
         pwmRequest.forwardToJsp(JspUrl.HELPDESK_DETAIL);
     }
     }
 
 
-    private void restSearchRequest(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile
+    @ActionHandler(action = "search")
+    private ProcessStatus restSearchRequest(
+            final PwmRequest pwmRequest
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final Map<String, String> valueMap = pwmRequest.readBodyAsJsonStringMap();
         final Map<String, String> valueMap = pwmRequest.readBodyAsJsonStringMap();
         final String username = valueMap.get("username");
         final String username = valueMap.get("username");
 
 
@@ -532,12 +483,12 @@ public class HelpdeskServlet extends AbstractPwmServlet {
 
 
         if (username == null ||username.isEmpty()) {
         if (username == null ||username.isEmpty()) {
             final HelpdeskSearchResultsBean emptyResults = new HelpdeskSearchResultsBean();
             final HelpdeskSearchResultsBean emptyResults = new HelpdeskSearchResultsBean();
-            emptyResults.setSearchResults(new ArrayList<Map<String,Object>>());
+            emptyResults.setSearchResults(new ArrayList<>());
             emptyResults.setSizeExceeded(false);
             emptyResults.setSizeExceeded(false);
             final RestResultBean restResultBean = new RestResultBean();
             final RestResultBean restResultBean = new RestResultBean();
             restResultBean.setData(emptyResults);
             restResultBean.setData(emptyResults);
             pwmRequest.outputJsonResult(restResultBean);
             pwmRequest.outputJsonResult(restResultBean);
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         final UserSearchEngine userSearchEngine = new UserSearchEngine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
         final UserSearchEngine userSearchEngine = new UserSearchEngine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
@@ -568,7 +519,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             final RestResultBean restResultBean = RestResultBean.fromError(errorInformation, pwmRequest);
             final RestResultBean restResultBean = RestResultBean.fromError(errorInformation, pwmRequest);
             restResultBean.setData(new ArrayList<Map<String, String>>());
             restResultBean.setData(new ArrayList<Map<String, String>>());
             pwmRequest.outputJsonResult(restResultBean);
             pwmRequest.outputJsonResult(restResultBean);
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         final RestResultBean restResultBean = new RestResultBean();
         final RestResultBean restResultBean = new RestResultBean();
@@ -577,105 +528,21 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         outputData.setSizeExceeded(sizeExceeded);
         outputData.setSizeExceeded(sizeExceeded);
         restResultBean.setData(outputData);
         restResultBean.setData(outputData);
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
-
-
-    private static HelpdeskDetailInfoBean makeHelpdeskDetailInfo(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile,
-            final UserIdentity userIdentity
-    )
-            throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
-    {
-        final Instant startTime = Instant.now();
-        LOGGER.trace(pwmRequest, "beginning to assemble detail data report for user " + userIdentity);
-        final Locale actorLocale = pwmRequest.getLocale();
-        final ChaiUser theUser = getChaiUser(pwmRequest, helpdeskProfile, userIdentity);
-
-        if (!theUser.isValid()) {
-            return null;
-        }
-
-        final HelpdeskDetailInfoBean detailInfo = new HelpdeskDetailInfoBean();
-        final UserInfoBean uiBean = detailInfo.getUserInfoBean();
-        final UserStatusReader userStatusReader = new UserStatusReader(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
-        userStatusReader.populateUserInfoBean(uiBean, actorLocale, userIdentity, theUser.getChaiProvider());
-
-        try {
-            detailInfo.setIntruderLocked(theUser.isPasswordLocked());
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading intruder lock status for user '" + userIdentity + "', " + e.getMessage());
-        }
-
-        try {
-            detailInfo.setAccountEnabled(theUser.isAccountEnabled());
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading account enabled status for user '" + userIdentity + "', " + e.getMessage());
-        }
-
-        try {
-            detailInfo.setAccountExpired(theUser.isAccountExpired());
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading account expired status for user '" + userIdentity + "', " + e.getMessage());
-        }
-
-        try {
-            final Date lastLoginTime = theUser.readLastLoginTime();
-            detailInfo.setLastLoginTime(lastLoginTime == null ? null : lastLoginTime.toInstant());
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading last login time for user '" + userIdentity + "', " + e.getMessage());
-        }
-
-        try {
-            detailInfo.setUserHistory(pwmRequest.getPwmApplication().getAuditManager().readUserHistory(uiBean));
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error reading userHistory for user '" + userIdentity + "', " + e.getMessage());
-        }
-
-        if (uiBean.getPasswordLastModifiedTime() != null) {
-            final TimeDuration passwordSetDelta = TimeDuration.fromCurrent(uiBean.getPasswordLastModifiedTime());
-            detailInfo.setPasswordSetDelta(passwordSetDelta.asLongString(pwmRequest.getLocale()));
-        } else {
-            detailInfo.setPasswordSetDelta(LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable, pwmRequest));
-        }
-
-        final UserDataReader userDataReader = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY)
-                ? LdapUserDataReader.appProxiedReader(pwmRequest.getPwmApplication(), userIdentity)
-                : LdapUserDataReader.selfProxiedReader(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
-
-        {
-            final List<FormConfiguration> detailFormConfig = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_DETAIL_FORM);
-            final Map<FormConfiguration,List<String>> formData = FormUtility.populateFormMapFromLdap(detailFormConfig, pwmRequest.getPwmSession().getLabel(), userDataReader);
-            detailInfo.setSearchDetails(formData);
-        }
-
-        final String configuredDisplayName = helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_DETAIL_DISPLAY_NAME);
-        if (configuredDisplayName != null && !configuredDisplayName.isEmpty()) {
-            final MacroMachine macroMachine = new MacroMachine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), detailInfo.getUserInfoBean(), null, userDataReader);
-            final String displayName = macroMachine.expandMacros(configuredDisplayName);
-            detailInfo.setUserDisplayName(displayName);
-        }
-
-        final TimeDuration timeDuration = TimeDuration.fromCurrent(startTime);
-        if (pwmRequest.getConfig().isDevDebugMode()) {
-            LOGGER.trace(pwmRequest, "completed assembly of detail data report for user " + userIdentity
-                    + " in " + timeDuration.asCompactString() + ", contents: " + JsonUtil.serialize(detailInfo));
-        }
-        return detailInfo;
-    }
-
-    private void restUnlockPassword(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile
+    @ActionHandler(action = "unlockIntruder")
+    private ProcessStatus restUnlockPassword(
+            final PwmRequest pwmRequest
     )
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
         final String userKey = pwmRequest.readParameterAsString("userKey", PwmHttpRequestWrapper.Flag.BypassValidation);
         if (userKey.length() < 1) {
         if (userKey.length() < 1) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
             pwmRequest.respondWithError(errorInformation, false);
             pwmRequest.respondWithError(errorInformation, false);
-            return;
+            return ProcessStatus.Halt;
         }
         }
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
 
 
@@ -683,7 +550,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "password unlock request, but helpdesk unlock is not enabled");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "password unlock request, but helpdesk unlock is not enabled");
             LOGGER.error(pwmRequest, errorInformation);
             LOGGER.error(pwmRequest, errorInformation);
             pwmRequest.respondWithError(errorInformation);
             pwmRequest.respondWithError(errorInformation);
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         //clear pwm intruder setting.
         //clear pwm intruder setting.
@@ -718,26 +585,29 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             final PwmError pwmError = PwmError.forChaiError(passwordError);
             final PwmError pwmError = PwmError.forChaiError(passwordError);
             pwmRequest.respondWithError(new ErrorInformation(pwmError == null ? PwmError.PASSWORD_UNKNOWN_VALIDATION : pwmError));
             pwmRequest.respondWithError(new ErrorInformation(pwmError == null ? PwmError.PASSWORD_UNKNOWN_VALIDATION : pwmError));
             LOGGER.trace(pwmRequest, "ChaiPasswordPolicyException was thrown while resetting password: " + e.toString());
             LOGGER.trace(pwmRequest, "ChaiPasswordPolicyException was thrown while resetting password: " + e.toString());
-            return;
+            return ProcessStatus.Halt;
         } catch (ChaiOperationException e) {
         } catch (ChaiOperationException e) {
             final PwmError returnMsg = PwmError.forChaiError(e.getErrorCode()) == null ? PwmError.ERROR_UNKNOWN : PwmError.forChaiError(e.getErrorCode());
             final PwmError returnMsg = PwmError.forChaiError(e.getErrorCode()) == null ? PwmError.ERROR_UNKNOWN : PwmError.forChaiError(e.getErrorCode());
             final ErrorInformation error = new ErrorInformation(returnMsg, e.getMessage());
             final ErrorInformation error = new ErrorInformation(returnMsg, e.getMessage());
             pwmRequest.respondWithError(error);
             pwmRequest.respondWithError(error);
             LOGGER.warn(pwmRequest, "error resetting password for user '" + userIdentity.toDisplayString() + "'' " + error.toDebugStr() + ", " + e.getMessage());
             LOGGER.warn(pwmRequest, "error resetting password for user '" + userIdentity.toDisplayString() + "'' " + error.toDebugStr() + ", " + e.getMessage());
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         final RestResultBean restResultBean = new RestResultBean();
         final RestResultBean restResultBean = new RestResultBean();
         restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(),Message.Success_Unknown,pwmRequest.getConfig()));
         restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(),Message.Success_Unknown,pwmRequest.getConfig()));
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
-    private void restValidateOtpCodeRequest(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile
+    @ActionHandler(action = "validateOtpCode")
+    private ProcessStatus restValidateOtpCodeRequest(
+            final PwmRequest pwmRequest
     )
     )
             throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
             throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
+
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();
 
 
         final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(
         final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(
@@ -748,7 +618,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         if (userKey == null || userKey.isEmpty()) {
         if (userKey == null || userKey.isEmpty()) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"userKey parameter is missing");
             pwmRequest.respondWithError(errorInformation, false);
             pwmRequest.respondWithError(errorInformation, false);
-            return;
+            return ProcessStatus.Halt;
         }
         }
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication());
 
 
@@ -756,7 +626,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "password otp verification request, but otp verify is not enabled");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "password otp verification request, but otp verify is not enabled");
             LOGGER.error(pwmRequest, errorInformation);
             LOGGER.error(pwmRequest, errorInformation);
             pwmRequest.respondWithError(errorInformation);
             pwmRequest.respondWithError(errorInformation);
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         final String code = helpdeskVerificationRequestBean.getCode();
         final String code = helpdeskVerificationRequestBean.getCode();
@@ -811,14 +681,17 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
             pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
             pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
         }
         }
+        return ProcessStatus.Halt;
     }
     }
 
 
-    private void restSendVerificationTokenRequest(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile
+    @ActionHandler(action = "sendVerificationToken")
+    private ProcessStatus restSendVerificationTokenRequest(
+            final PwmRequest pwmRequest
     )
     )
             throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
             throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
+
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();
         final Configuration config = pwmRequest.getConfig();
         final Configuration config = pwmRequest.getConfig();
         final Map<String,String> bodyParams = pwmRequest.readBodyAsJsonStringMap();
         final Map<String,String> bodyParams = pwmRequest.readBodyAsJsonStringMap();
@@ -844,18 +717,18 @@ public class HelpdeskServlet extends AbstractPwmServlet {
                 final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_MISSING_CONTACT, "unable to determine appropriate send method, missing method parameter indication from operator");
                 final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_MISSING_CONTACT, "unable to determine appropriate send method, missing method parameter indication from operator");
                 LOGGER.error(pwmRequest,errorInformation);
                 LOGGER.error(pwmRequest,errorInformation);
                 pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation,pwmRequest));
                 pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation,pwmRequest));
-                return;
+                return ProcessStatus.Halt;
             }
             }
         }
         }
 
 
         final UserIdentity userIdentity = UserIdentity.fromKey(bodyParams.get("userKey"), pwmRequest.getPwmApplication());
         final UserIdentity userIdentity = UserIdentity.fromKey(bodyParams.get("userKey"), pwmRequest.getPwmApplication());
 
 
-        final HelpdeskDetailInfoBean helpdeskDetailInfoBean = makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
+        final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
         if (helpdeskDetailInfoBean == null) {
         if (helpdeskDetailInfoBean == null) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "unable to read helpdesk detail data for specified user");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "unable to read helpdesk detail data for specified user");
             LOGGER.error(pwmRequest,errorInformation);
             LOGGER.error(pwmRequest,errorInformation);
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation,pwmRequest));
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation,pwmRequest));
-            return;
+            return ProcessStatus.Halt;
         }
         }
         final UserInfoBean userInfoBean = helpdeskDetailInfoBean.getUserInfoBean();
         final UserInfoBean userInfoBean = helpdeskDetailInfoBean.getUserInfoBean();
         final UserDataReader userDataReader = LdapUserDataReader.appProxiedReader(pwmRequest.getPwmApplication(), userIdentity);
         final UserDataReader userDataReader = LdapUserDataReader.appProxiedReader(pwmRequest.getPwmApplication(), userIdentity);
@@ -899,7 +772,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         } catch (PwmException e) {
         } catch (PwmException e) {
             LOGGER.error(pwmRequest, e.getErrorInformation());
             LOGGER.error(pwmRequest, e.getErrorInformation());
             pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(),pwmRequest));
             pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(),pwmRequest));
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         StatisticsManager.incrementStat(pwmRequest,Statistic.HELPDESK_TOKENS_SENT);
         StatisticsManager.incrementStat(pwmRequest,Statistic.HELPDESK_TOKENS_SENT);
@@ -923,9 +796,11 @@ public class HelpdeskServlet extends AbstractPwmServlet {
                 + " sent to destination(s) "
                 + " sent to destination(s) "
                 + destDisplayString
                 + destDisplayString
                 + " (" + TimeDuration.fromCurrent(startTime).asCompactString() + ")");
                 + " (" + TimeDuration.fromCurrent(startTime).asCompactString() + ")");
+        return ProcessStatus.Halt;
     }
     }
 
 
-    private void restVerifyVerificationTokenRequest(
+    @ActionHandler(action = "verifyVerificationToken")
+    private ProcessStatus restVerifyVerificationTokenRequest(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
             throws IOException, PwmUnrecoverableException, ServletException
             throws IOException, PwmUnrecoverableException, ServletException
@@ -995,14 +870,17 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
         final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
         final RestResultBean restResultBean = new RestResultBean(responseBean);
         final RestResultBean restResultBean = new RestResultBean(responseBean);
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
-    private void restClearOtpSecret(
-            final PwmRequest pwmRequest,
-            final HelpdeskProfile helpdeskProfile
+    @ActionHandler(action = "clearOtpSecret")
+    private ProcessStatus restClearOtpSecret(
+            final PwmRequest pwmRequest
     )
     )
             throws ServletException, IOException, PwmUnrecoverableException, ChaiUnavailableException
             throws ServletException, IOException, PwmUnrecoverableException, ChaiUnavailableException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
+
         final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
         final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
         final UserIdentity userIdentity = userIdentityFromMap(pwmRequest, bodyMap);
         final UserIdentity userIdentity = userIdentityFromMap(pwmRequest, bodyMap);
 
 
@@ -1011,7 +889,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, errorMsg);
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, errorMsg);
             LOGGER.error(pwmRequest, errorMsg);
             LOGGER.error(pwmRequest, errorMsg);
             pwmRequest.respondWithError(errorInformation);
             pwmRequest.respondWithError(errorInformation);
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         //clear pwm intruder setting.
         //clear pwm intruder setting.
@@ -1038,12 +916,13 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             final ErrorInformation error = new ErrorInformation(returnMsg, e.getMessage());
             final ErrorInformation error = new ErrorInformation(returnMsg, e.getMessage());
             pwmRequest.respondWithError(error);
             pwmRequest.respondWithError(error);
             LOGGER.warn(pwmRequest, "error clearing OTP secret for user '" + userIdentity + "'' " + error.toDebugStr() + ", " + e.getMessage());
             LOGGER.warn(pwmRequest, "error clearing OTP secret for user '" + userIdentity + "'' " + error.toDebugStr() + ", " + e.getMessage());
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
         final RestResultBean restResultBean = new RestResultBean();
         final RestResultBean restResultBean = new RestResultBean();
         restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(),Message.Success_Unknown,pwmRequest.getConfig()));
         restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(),Message.Success_Unknown,pwmRequest.getConfig()));
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
     private static String getSearchFilter(final Configuration configuration, final HelpdeskProfile helpdeskProfile) {
     private static String getSearchFilter(final Configuration configuration, final HelpdeskProfile helpdeskProfile) {
@@ -1072,7 +951,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
     }
     }
 
 
 
 
-    private static ChaiUser getChaiUser(
+    static ChaiUser getChaiUser(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final HelpdeskProfile helpdeskProfile,
             final HelpdeskProfile helpdeskProfile,
             final UserIdentity userIdentity
             final UserIdentity userIdentity
@@ -1103,8 +982,11 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         }
         }
     }
     }
 
 
-    private void restCheckVerification(final PwmRequest pwmRequest, final HelpdeskProfile helpdeskProfile)
+    @ActionHandler(action = "checkVerification")
+    private ProcessStatus restCheckVerification(final PwmRequest pwmRequest)
             throws IOException, PwmUnrecoverableException, ServletException {
             throws IOException, PwmUnrecoverableException, ServletException {
+
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
         final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
 
 
         final UserIdentity userIdentity = userIdentityFromMap(pwmRequest, bodyMap);
         final UserIdentity userIdentity = userIdentityFromMap(pwmRequest, bodyMap);
@@ -1117,6 +999,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         results.put("passed",passed);
         results.put("passed",passed);
         final RestResultBean restResultBean = new RestResultBean(results);
         final RestResultBean restResultBean = new RestResultBean(results);
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
     private boolean checkIfRequiredVerificationPassed(final UserIdentity userIdentity, final HelpdeskVerificationStateBean verificationStateBean, final HelpdeskProfile helpdeskProfile) {
     private boolean checkIfRequiredVerificationPassed(final UserIdentity userIdentity, final HelpdeskVerificationStateBean verificationStateBean, final HelpdeskProfile helpdeskProfile) {
@@ -1132,7 +1015,8 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         return false;
         return false;
     }
     }
 
 
-    private void restShowVerifications(final PwmRequest pwmRequest)
+    @ActionHandler(action = "showVerifications")
+    private ProcessStatus restShowVerifications(final PwmRequest pwmRequest)
             throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
             throws IOException, PwmUnrecoverableException, ServletException, ChaiUnavailableException
     {
     {
         final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
         final Map<String,String> bodyMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
@@ -1146,6 +1030,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         }
         }
         final RestResultBean restResultBean = new RestResultBean(results);
         final RestResultBean restResultBean = new RestResultBean(results);
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
     private UserIdentity userIdentityFromMap(final PwmRequest pwmRequest, final Map<String,String> bodyMap) throws PwmUnrecoverableException {
     private UserIdentity userIdentityFromMap(final PwmRequest pwmRequest, final Map<String,String> bodyMap) throws PwmUnrecoverableException {
@@ -1158,9 +1043,11 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         return UserIdentity.fromObfuscatedKey(userKey, pwmRequest.getPwmApplication());
         return UserIdentity.fromObfuscatedKey(userKey, pwmRequest.getPwmApplication());
     }
     }
 
 
-    private void restValidateAttributes(final PwmRequest pwmRequest, final HelpdeskProfile helpdeskProfile)
+    @ActionHandler(action = "validateAttributes")
+    private ProcessStatus restValidateAttributes(final PwmRequest pwmRequest)
             throws IOException, PwmUnrecoverableException, ServletException
             throws IOException, PwmUnrecoverableException, ServletException
     {
     {
+        final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();
         final String bodyString = pwmRequest.readRequestBodyAsString();
         final String bodyString = pwmRequest.readRequestBodyAsString();
         final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(
         final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = JsonUtil.deserialize(
@@ -1239,6 +1126,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
         final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
         final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
         final RestResultBean restResultBean = new RestResultBean(responseBean);
         final RestResultBean restResultBean = new RestResultBean(responseBean);
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
     }
     }
 
 
     private static void sendUnlockNoticeEmail(
     private static void sendUnlockNoticeEmail(
@@ -1257,7 +1145,7 @@ public class HelpdeskServlet extends AbstractPwmServlet {
             return;
             return;
         }
         }
 
 
-        final HelpdeskDetailInfoBean helpdeskDetailInfoBean = makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
+        final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
         final MacroMachine macroMachine = new MacroMachine(
         final MacroMachine macroMachine = new MacroMachine(
                 pwmApplication,
                 pwmApplication,
                 pwmRequest.getSessionLabel(),
                 pwmRequest.getSessionLabel(),
@@ -1272,5 +1160,4 @@ public class HelpdeskServlet extends AbstractPwmServlet {
                 macroMachine
                 macroMachine
         );
         );
     }
     }
-
 }
 }

+ 4 - 11
src/main/java/password/pwm/http/servlet/newuser/NewUserServlet.java

@@ -120,14 +120,9 @@ public class NewUserServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
-    protected NewUserAction readProcessAction(final PwmRequest request)
-            throws PwmUnrecoverableException
-    {
-        try {
-            return NewUserAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
+    @Override
+    public Class<? extends ProcessAction> getProcessActionsClass() {
+        return NewUserAction.class;
     }
     }
 
 
 
 
@@ -135,8 +130,6 @@ public class NewUserServlet extends ControlledPwmServlet {
         return pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, NewUserBean.class);
         return pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, NewUserBean.class);
     }
     }
 
 
-
-
     @Override
     @Override
     public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
     public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) throws PwmUnrecoverableException, IOException, ServletException {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
@@ -154,7 +147,7 @@ public class NewUserServlet extends ControlledPwmServlet {
             return ProcessStatus.Halt;
             return ProcessStatus.Halt;
         }
         }
 
 
-        final NewUserAction action = this.readProcessAction(pwmRequest);
+        final ProcessAction action = this.readProcessAction(pwmRequest);
 
 
         // convert a url command like /public/newuser/12321321 to redirect with a process action.
         // convert a url command like /public/newuser/12321321 to redirect with a process action.
         if (action == null) {
         if (action == null) {

+ 1 - 2
src/main/java/password/pwm/http/tag/ErrorMessageTag.java

@@ -29,7 +29,6 @@ import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.ContextManager;
 import password.pwm.http.ContextManager;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
-import password.pwm.util.Helper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.macro.MacroMachine;
@@ -64,7 +63,7 @@ public class ErrorMessageTag extends PwmAbstractTag {
             final ErrorInformation error = (ErrorInformation)pwmRequest.getAttribute(PwmRequest.Attribute.PwmErrorInfo);
             final ErrorInformation error = (ErrorInformation)pwmRequest.getAttribute(PwmRequest.Attribute.PwmErrorInfo);
 
 
             if (error != null) {
             if (error != null) {
-                final boolean showErrorDetail = Helper.determineIfDetailErrorMsgShown(pwmApplication);
+                final boolean showErrorDetail = pwmApplication.determineIfDetailErrorMsgShown();
 
 
                 String outputMsg;
                 String outputMsg;
                 if (showErrorDetail) {
                 if (showErrorDetail) {

+ 22 - 2
src/main/java/password/pwm/http/tag/PwmFormIDTag.java

@@ -22,21 +22,41 @@
 
 
 package password.pwm.http.tag;
 package password.pwm.http.tag;
 
 
+import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmApplicationMode;
+import password.pwm.bean.FormNonce;
+import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.JspUtility;
 import password.pwm.http.JspUtility;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
-import password.pwm.util.Helper;
+import password.pwm.http.state.SessionStateService;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.jsp.tagext.TagSupport;
 import javax.servlet.jsp.tagext.TagSupport;
 import java.io.IOException;
 import java.io.IOException;
+import java.time.Instant;
 
 
 public class PwmFormIDTag extends TagSupport {
 public class PwmFormIDTag extends TagSupport {
 // --------------------- Interface Tag ---------------------
 // --------------------- Interface Tag ---------------------
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(PwmFormIDTag.class);
     private static final PwmLogger LOGGER = PwmLogger.forClass(PwmFormIDTag.class);
 
 
+    private static String buildPwmFormID(final PwmRequest pwmRequest) throws PwmUnrecoverableException {
+        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
+        if (pwmApplication == null) {
+            return "";
+        }
+        final SessionStateService sessionStateService = pwmApplication.getSessionStateService();
+        final String value = sessionStateService.getSessionStateInfo(pwmRequest);
+        final FormNonce formID = new FormNonce(
+                pwmRequest.getPwmSession().getLoginInfoBean().getGuid(),
+                Instant.now(),
+                pwmRequest.getPwmSession().getLoginInfoBean().getReqCounter(),
+                value
+        );
+        return pwmRequest.getPwmApplication().getSecureService().encryptObjectToString(formID);
+    }
+
     public int doEndTag()
     public int doEndTag()
             throws javax.servlet.jsp.JspTagException
             throws javax.servlet.jsp.JspTagException
     {
     {
@@ -46,7 +66,7 @@ public class PwmFormIDTag extends TagSupport {
 
 
         try {
         try {
             final PwmRequest pwmRequest = JspUtility.getPwmRequest(pageContext);
             final PwmRequest pwmRequest = JspUtility.getPwmRequest(pageContext);
-            final String pwmFormID = Helper.buildPwmFormID(pwmRequest);
+            final String pwmFormID = buildPwmFormID(pwmRequest);
 
 
             pageContext.getOut().write(pwmFormID);
             pageContext.getOut().write(pwmFormID);
         } catch (Exception e) {
         } catch (Exception e) {

+ 1 - 2
src/main/java/password/pwm/http/tag/conditional/PwmIfTest.java

@@ -37,7 +37,6 @@ import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestFlag;
 import password.pwm.http.PwmRequestFlag;
 import password.pwm.http.servlet.peoplesearch.PeopleSearchConfiguration;
 import password.pwm.http.servlet.peoplesearch.PeopleSearchConfiguration;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.Helper;
 
 
 public enum PwmIfTest {
 public enum PwmIfTest {
     authenticated(new AuthenticatedTest()),
     authenticated(new AuthenticatedTest()),
@@ -269,7 +268,7 @@ public enum PwmIfTest {
         )
         )
                 throws ChaiUnavailableException, PwmUnrecoverableException
                 throws ChaiUnavailableException, PwmUnrecoverableException
         {
         {
-            return Helper.determineIfDetailErrorMsgShown(pwmRequest.getPwmApplication());
+            return pwmRequest.getPwmApplication().determineIfDetailErrorMsgShown();
         }
         }
     }
     }
 
 

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

@@ -50,7 +50,6 @@ import password.pwm.svc.intruder.IntruderManager;
 import password.pwm.svc.intruder.RecordType;
 import password.pwm.svc.intruder.RecordType;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.svc.stats.StatisticsManager;
-import password.pwm.util.Helper;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
@@ -108,7 +107,7 @@ public class SessionAuthenticator {
             postFailureSequence(e, username, userIdentity);
             postFailureSequence(e, username, userIdentity);
 
 
             if (readHiddenErrorTypes().contains(e.getError())) {
             if (readHiddenErrorTypes().contains(e.getError())) {
-                if (Helper.determineIfDetailErrorMsgShown(pwmApplication)) {
+                if (pwmApplication.determineIfDetailErrorMsgShown()) {
                     LOGGER.debug(pwmSession, "allowing error " + e.getError() + " to be returned though it is configured as a hidden type; "
                     LOGGER.debug(pwmSession, "allowing error " + e.getError() + " to be returned though it is configured as a hidden type; "
                             + "app is currently permitting detailed error messages");
                             + "app is currently permitting detailed error messages");
                 } else {
                 } else {

+ 2 - 2
src/main/java/password/pwm/svc/cache/LocalDBCacheStore.java

@@ -24,7 +24,7 @@ package password.pwm.svc.cache;
 
 
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.util.Helper;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.localdb.LocalDBException;
@@ -60,7 +60,7 @@ public class LocalDBCacheStore implements CacheStore {
         } catch (LocalDBException e) {
         } catch (LocalDBException e) {
             LOGGER.error("error while clearing LocalDB CACHE DB during init: " + e.getMessage());
             LOGGER.error("error while clearing LocalDB CACHE DB during init: " + e.getMessage());
         }
         }
-        timer = new Timer(Helper.makeThreadName(pwmApplication,LocalDBCacheStore.class),true);
+        timer = new Timer(JavaHelper.makeThreadName(pwmApplication,LocalDBCacheStore.class),true);
     }
     }
 
 
     @Override
     @Override

+ 1 - 2
src/main/java/password/pwm/svc/event/AuditService.java

@@ -44,7 +44,6 @@ import password.pwm.health.HealthStatus;
 import password.pwm.health.HealthTopic;
 import password.pwm.health.HealthTopic;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.Helper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
@@ -358,7 +357,7 @@ public class AuditService implements PwmService {
     {
     {
         final Configuration config = null;
         final Configuration config = null;
 
 
-        final CSVPrinter csvPrinter = Helper.makeCsvPrinter(outputStream);
+        final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(outputStream);
 
 
         csvPrinter.printComment(" " + PwmConstants.PWM_APP_NAME + " audit record output ");
         csvPrinter.printComment(" " + PwmConstants.PWM_APP_NAME + " audit record output ");
         csvPrinter.printComment(" " + JavaHelper.toIsoDate(Instant.now()));
         csvPrinter.printComment(" " + JavaHelper.toIsoDate(Instant.now()));

+ 3 - 3
src/main/java/password/pwm/svc/event/LocalDbAuditVault.java

@@ -26,8 +26,8 @@ import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmException;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.Helper;
 import password.pwm.util.TransactionSizeCalculator;
 import password.pwm.util.TransactionSizeCalculator;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.Percent;
 import password.pwm.util.java.Percent;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
@@ -76,8 +76,8 @@ public class LocalDbAuditVault implements AuditVault {
         readOldestRecord();
         readOldestRecord();
 
 
         executorService = Executors.newSingleThreadScheduledExecutor(
         executorService = Executors.newSingleThreadScheduledExecutor(
-                Helper.makePwmThreadFactory(
-                        Helper.makeThreadName(pwmApplication,this.getClass()) + "-",
+                JavaHelper.makePwmThreadFactory(
+                        JavaHelper.makeThreadName(pwmApplication,this.getClass()) + "-",
                         true
                         true
                 ));
                 ));
 
 

+ 1 - 2
src/main/java/password/pwm/svc/intruder/IntruderManager.java

@@ -54,7 +54,6 @@ import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.DataStore;
 import password.pwm.util.DataStore;
 import password.pwm.util.DataStoreFactory;
 import password.pwm.util.DataStoreFactory;
-import password.pwm.util.Helper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.db.DatabaseDataStore;
 import password.pwm.util.db.DatabaseDataStore;
 import password.pwm.util.db.DatabaseTable;
 import password.pwm.util.db.DatabaseTable;
@@ -165,7 +164,7 @@ public class IntruderManager implements Serializable, PwmService {
         final RecordStore recordStore;
         final RecordStore recordStore;
         {
         {
             recordStore = new DataStoreRecordStore(dataStore, this);
             recordStore = new DataStoreRecordStore(dataStore, this);
-            final String threadName = Helper.makeThreadName(pwmApplication, this.getClass()) + " timer";
+            final String threadName = JavaHelper.makeThreadName(pwmApplication, this.getClass()) + " timer";
             timer = new Timer(threadName, true);
             timer = new Timer(threadName, true);
             final long maxRecordAge = Long.parseLong(pwmApplication.getConfig().readAppProperty(AppProperty.INTRUDER_RETENTION_TIME_MS));
             final long maxRecordAge = Long.parseLong(pwmApplication.getConfig().readAppProperty(AppProperty.INTRUDER_RETENTION_TIME_MS));
             final long cleanerRunFrequency = Long.parseLong(pwmApplication.getConfig().readAppProperty(AppProperty.INTRUDER_CLEANUP_FREQUENCY_MS));
             final long cleanerRunFrequency = Long.parseLong(pwmApplication.getConfig().readAppProperty(AppProperty.INTRUDER_CLEANUP_FREQUENCY_MS));

+ 2 - 3
src/main/java/password/pwm/svc/report/ReportCsvUtility.java

@@ -30,7 +30,6 @@ import password.pwm.config.Configuration;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.Display;
-import password.pwm.util.Helper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.java.ClosableIterator;
 import password.pwm.util.java.ClosableIterator;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -54,7 +53,7 @@ public class ReportCsvUtility {
     public void outputSummaryToCsv(final OutputStream outputStream, final Locale locale)
     public void outputSummaryToCsv(final OutputStream outputStream, final Locale locale)
             throws IOException {
             throws IOException {
         final List<ReportSummaryData.PresentationRow> outputList = reportService.getSummaryData().asPresentableCollection(pwmApplication.getConfig(), locale);
         final List<ReportSummaryData.PresentationRow> outputList = reportService.getSummaryData().asPresentableCollection(pwmApplication.getConfig(), locale);
-        final CSVPrinter csvPrinter = Helper.makeCsvPrinter(outputStream);
+        final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(outputStream);
 
 
         for (final ReportSummaryData.PresentationRow presentationRow : outputList) {
         for (final ReportSummaryData.PresentationRow presentationRow : outputList) {
             final List<String> headerRow = new ArrayList<>();
             final List<String> headerRow = new ArrayList<>();
@@ -83,7 +82,7 @@ public class ReportCsvUtility {
 
 
     public void outputToCsv(final OutputStream outputStream, final boolean includeHeader, final Locale locale, final Configuration config, final ReportColumnFilter columnFilter)
     public void outputToCsv(final OutputStream outputStream, final boolean includeHeader, final Locale locale, final Configuration config, final ReportColumnFilter columnFilter)
             throws IOException, ChaiUnavailableException, ChaiOperationException, PwmUnrecoverableException, PwmOperationalException {
             throws IOException, ChaiUnavailableException, ChaiOperationException, PwmUnrecoverableException, PwmOperationalException {
-        final CSVPrinter csvPrinter = Helper.makeCsvPrinter(outputStream);
+        final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(outputStream);
         final Class localeClass = password.pwm.i18n.Admin.class;
         final Class localeClass = password.pwm.i18n.Admin.class;
         if (includeHeader) {
         if (includeHeader) {
             final List<String> headerRow = new ArrayList<>();
             final List<String> headerRow = new ArrayList<>();

+ 2 - 3
src/main/java/password/pwm/svc/report/ReportService.java

@@ -43,7 +43,6 @@ import password.pwm.ldap.UserSearchEngine;
 import password.pwm.ldap.UserStatusReader;
 import password.pwm.ldap.UserStatusReader;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.stats.EventRateMeter;
 import password.pwm.svc.stats.EventRateMeter;
-import password.pwm.util.Helper;
 import password.pwm.util.TransactionSizeCalculator;
 import password.pwm.util.TransactionSizeCalculator;
 import password.pwm.util.java.BlockingThreadPool;
 import password.pwm.util.java.BlockingThreadPool;
 import password.pwm.util.java.ClosableIterator;
 import password.pwm.util.java.ClosableIterator;
@@ -140,8 +139,8 @@ public class ReportService implements PwmService {
         dnQueue = LocalDBStoredQueue.createLocalDBStoredQueue(pwmApplication, pwmApplication.getLocalDB(), LocalDB.DB.REPORT_QUEUE);
         dnQueue = LocalDBStoredQueue.createLocalDBStoredQueue(pwmApplication, pwmApplication.getLocalDB(), LocalDB.DB.REPORT_QUEUE);
 
 
         executorService = Executors.newSingleThreadScheduledExecutor(
         executorService = Executors.newSingleThreadScheduledExecutor(
-                Helper.makePwmThreadFactory(
-                        Helper.makeThreadName(pwmApplication,this.getClass()) + "-",
+                JavaHelper.makePwmThreadFactory(
+                        JavaHelper.makeThreadName(pwmApplication,this.getClass()) + "-",
                         true
                         true
                 ));
                 ));
 
 

+ 2 - 3
src/main/java/password/pwm/svc/stats/StatisticsManager.java

@@ -41,7 +41,6 @@ import password.pwm.http.PwmRequest;
 import password.pwm.http.client.PwmHttpClient;
 import password.pwm.http.client.PwmHttpClient;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.util.AlertHandler;
 import password.pwm.util.AlertHandler;
-import password.pwm.util.Helper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
@@ -298,7 +297,7 @@ public class StatisticsManager implements PwmService {
         localDB.put(LocalDB.DB.PWM_STATS, DB_KEY_INITIAL_DAILY_KEY, initialDailyKey.toString());
         localDB.put(LocalDB.DB.PWM_STATS, DB_KEY_INITIAL_DAILY_KEY, initialDailyKey.toString());
 
 
         { // setup a timer to roll over at 0 Zula and one to write current stats every 10 seconds
         { // setup a timer to roll over at 0 Zula and one to write current stats every 10 seconds
-            final String threadName = Helper.makeThreadName(pwmApplication, this.getClass()) + " timer";
+            final String threadName = JavaHelper.makeThreadName(pwmApplication, this.getClass()) + " timer";
             daemonTimer = new Timer(threadName, true);
             daemonTimer = new Timer(threadName, true);
             daemonTimer.schedule(new FlushTask(), 10 * 1000, DB_WRITE_FREQUENCY_MS);
             daemonTimer.schedule(new FlushTask(), 10 * 1000, DB_WRITE_FREQUENCY_MS);
             daemonTimer.schedule(new NightlyTask(), Date.from(JavaHelper.nextZuluZeroTime()));
             daemonTimer.schedule(new NightlyTask(), Date.from(JavaHelper.nextZuluZeroTime()));
@@ -555,7 +554,7 @@ public class StatisticsManager implements PwmService {
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();
 
 
         final StatisticsManager statsManger = pwmApplication.getStatisticsManager();
         final StatisticsManager statsManger = pwmApplication.getStatisticsManager();
-        final CSVPrinter csvPrinter = Helper.makeCsvPrinter(outputStream);
+        final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(outputStream);
 
 
         if (includeHeader) {
         if (includeHeader) {
             final List<String> headers = new ArrayList<>();
             final List<String> headers = new ArrayList<>();

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

@@ -54,7 +54,6 @@ import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.intruder.RecordType;
 import password.pwm.svc.intruder.RecordType;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.svc.stats.StatisticsManager;
-import password.pwm.util.Helper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
@@ -188,8 +187,8 @@ public class TokenService implements PwmService {
         }
         }
 
 
         executorService = Executors.newSingleThreadScheduledExecutor(
         executorService = Executors.newSingleThreadScheduledExecutor(
-                Helper.makePwmThreadFactory(
-                        Helper.makeThreadName(pwmApplication, this.getClass()) + "-",
+                JavaHelper.makePwmThreadFactory(
+                        JavaHelper.makeThreadName(pwmApplication, this.getClass()) + "-",
                         true
                         true
                 ));
                 ));
 
 

+ 3 - 4
src/main/java/password/pwm/svc/wordlist/AbstractWordlist.java

@@ -41,7 +41,6 @@ import password.pwm.http.ContextManager;
 import password.pwm.http.client.PwmHttpClient;
 import password.pwm.http.client.PwmHttpClient;
 import password.pwm.http.client.PwmHttpClientConfiguration;
 import password.pwm.http.client.PwmHttpClientConfiguration;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.Helper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
@@ -104,8 +103,8 @@ abstract class AbstractWordlist implements Wordlist, PwmService {
         }
         }
 
 
         executorService = Executors.newSingleThreadScheduledExecutor(
         executorService = Executors.newSingleThreadScheduledExecutor(
-                Helper.makePwmThreadFactory(
-                        Helper.makeThreadName(pwmApplication,this.getClass()) + "-",
+                JavaHelper.makePwmThreadFactory(
+                        JavaHelper.makeThreadName(pwmApplication,this.getClass()) + "-",
                         true
                         true
                 ));
                 ));
     }
     }
@@ -355,7 +354,7 @@ abstract class AbstractWordlist implements Wordlist, PwmService {
                         }
                         }
                         populator = null;
                         populator = null;
                     }
                     }
-                }, Helper.makeThreadName(pwmApplication, WordlistManager.class));
+                }, JavaHelper.makeThreadName(pwmApplication, WordlistManager.class));
                 t.setDaemon(true);
                 t.setDaemon(true);
                 t.start();
                 t.start();
             }
             }

+ 2 - 3
src/main/java/password/pwm/svc/wordlist/SharedHistoryManager.java

@@ -31,7 +31,6 @@ import password.pwm.error.PwmException;
 import password.pwm.health.HealthRecord;
 import password.pwm.health.HealthRecord;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.Helper;
 import password.pwm.util.java.Sleeper;
 import password.pwm.util.java.Sleeper;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -217,7 +216,7 @@ public class SharedHistoryManager implements PwmService {
             frequencyMs = frequencyMs < MIN_CLEANER_FREQUENCY ? MIN_CLEANER_FREQUENCY : frequencyMs;
             frequencyMs = frequencyMs < MIN_CLEANER_FREQUENCY ? MIN_CLEANER_FREQUENCY : frequencyMs;
 
 
             LOGGER.debug("scheduling cleaner task to run once every " + new TimeDuration(frequencyMs).asCompactString());
             LOGGER.debug("scheduling cleaner task to run once every " + new TimeDuration(frequencyMs).asCompactString());
-            final String threadName = Helper.makeThreadName(pwmApplication, this.getClass()) + " timer";
+            final String threadName = JavaHelper.makeThreadName(pwmApplication, this.getClass()) + " timer";
             cleanerTimer = new Timer(threadName, true);
             cleanerTimer = new Timer(threadName, true);
             cleanerTimer.schedule(new CleanerTask(), 1000, frequencyMs);
             cleanerTimer.schedule(new CleanerTask(), 1000, frequencyMs);
         }
         }
@@ -417,7 +416,7 @@ public class SharedHistoryManager implements PwmService {
                 LOGGER.debug("starting up in background thread");
                 LOGGER.debug("starting up in background thread");
                 init(pwmApplication, settings.maxAgeMs);
                 init(pwmApplication, settings.maxAgeMs);
             }
             }
-        }, Helper.makeThreadName(pwmApplication, this.getClass()) + " initializer").start();
+        }, JavaHelper.makeThreadName(pwmApplication, this.getClass()) + " initializer").start();
     }
     }
 
 
     private static class Settings {
     private static class Settings {

+ 0 - 272
src/main/java/password/pwm/util/Helper.java

@@ -1,272 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2016 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.util;
-
-import org.apache.commons.csv.CSVPrinter;
-import password.pwm.PwmApplication;
-import password.pwm.PwmApplicationMode;
-import password.pwm.PwmConstants;
-import password.pwm.bean.FormNonce;
-import password.pwm.bean.SessionLabel;
-import password.pwm.config.PwmSetting;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
-import password.pwm.error.PwmOperationalException;
-import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.PwmRequest;
-import password.pwm.http.state.SessionStateService;
-import password.pwm.util.logging.PwmLogger;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.UnknownHostException;
-import java.text.NumberFormat;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Properties;
-import java.util.TreeSet;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.regex.Pattern;
-
-/**
- * A collection of static methods used throughout PWM
- *
- * @author Jason D. Rivard
- */
-public class Helper {
-// ------------------------------ FIELDS ------------------------------
-
-    private static final PwmLogger LOGGER = PwmLogger.forClass(Helper.class);
-
-    // -------------------------- STATIC METHODS --------------------------
-
-    private Helper() {
-    }
-
-
-    public static String formatDiskSize(final long diskSize) {
-        final float COUNT = 1000;
-        if (diskSize < 1) {
-            return "n/a";
-        }
-
-        if (diskSize == 0) {
-            return "0";
-        }
-
-        final NumberFormat nf = NumberFormat.getInstance();
-        nf.setMaximumFractionDigits(2);
-
-        if (diskSize > COUNT * COUNT * COUNT) {
-            final StringBuilder sb = new StringBuilder();
-            sb.append(nf.format(diskSize / COUNT / COUNT / COUNT));
-            sb.append(" GB");
-            return sb.toString();
-        }
-
-        if (diskSize > COUNT * COUNT) {
-            final StringBuilder sb = new StringBuilder();
-            sb.append(nf.format(diskSize / COUNT / COUNT));
-            sb.append(" MB");
-            return sb.toString();
-        }
-
-        return NumberFormat.getInstance().format(diskSize) + " bytes";
-    }
-
-
-    public static String buildPwmFormID(final PwmRequest pwmRequest) throws PwmUnrecoverableException {
-        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
-        if (pwmApplication == null) {
-            return "";
-        }
-        final SessionStateService sessionStateService = pwmApplication.getSessionStateService();
-        final String value = sessionStateService.getSessionStateInfo(pwmRequest);
-        final FormNonce formID = new FormNonce(
-                pwmRequest.getPwmSession().getLoginInfoBean().getGuid(),
-                Instant.now(),
-                pwmRequest.getPwmSession().getLoginInfoBean().getReqCounter(),
-                value
-        );
-        return pwmRequest.getPwmApplication().getSecureService().encryptObjectToString(formID);
-    }
-
-
-    public static String makeThreadName(final PwmApplication pwmApplication, final Class theClass) {
-        String instanceName = "-";
-        if (pwmApplication != null && pwmApplication.getInstanceID() != null) {
-            instanceName = pwmApplication.getInstanceID();
-        }
-
-        return PwmConstants.PWM_APP_NAME + "-" + instanceName + "-" + theClass.getSimpleName();
-    }
-
-    public static void checkUrlAgainstWhitelist(
-            final PwmApplication pwmApplication,
-            final SessionLabel sessionLabel,
-            final String inputURL
-    )
-            throws PwmOperationalException
-    {
-        LOGGER.trace(sessionLabel, "beginning test of requested redirect URL: " + inputURL);
-        if (inputURL == null || inputURL.isEmpty()) {
-            return;
-        }
-
-        final URI inputURI;
-        try {
-            inputURI = URI.create(inputURL);
-        } catch (IllegalArgumentException e) {
-            LOGGER.error(sessionLabel, "unable to parse requested redirect url '" + inputURL + "', error: " + e.getMessage());
-            // dont put input uri in error response
-            final String errorMsg = "unable to parse url: " + e.getMessage();
-            throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
-        }
-
-        { // check to make sure we werent handed a non-http uri.
-            final String scheme = inputURI.getScheme();
-            if (scheme != null && !scheme.isEmpty() && !scheme.equalsIgnoreCase("http") && !scheme.equals("https")) {
-                final String errorMsg = "unsupported url scheme";
-                throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
-            }
-        }
-
-        if (inputURI.getHost() != null && !inputURI.getHost().isEmpty()) { // disallow localhost uri
-            try {
-                final InetAddress inetAddress = InetAddress.getByName(inputURI.getHost());
-                if (inetAddress.isLoopbackAddress()) {
-                    final String errorMsg = "redirect to loopback host is not permitted";
-                    throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
-                }
-            } catch (UnknownHostException e) {
-                /* noop */
-            }
-        }
-
-        final StringBuilder sb = new StringBuilder();
-        if (inputURI.getScheme() != null) {
-            sb.append(inputURI.getScheme());
-            sb.append("://");
-        }
-        if (inputURI.getHost() != null) {
-            sb.append(inputURI.getHost());
-        }
-        if (inputURI.getPort() != -1) {
-            sb.append(":");
-            sb.append(inputURI.getPort());
-        }
-        if (inputURI.getPath() != null) {
-            sb.append(inputURI.getPath());
-        }
-
-        final String testURI = sb.toString();
-        LOGGER.trace(sessionLabel, "preparing to whitelist test parsed and decoded URL: " + testURI);
-
-        final String REGEX_PREFIX = "regex:";
-        final List<String> whiteList = pwmApplication.getConfig().readSettingAsStringArray(PwmSetting.SECURITY_REDIRECT_WHITELIST);
-        for (final String loopFragment : whiteList) {
-            if (loopFragment.startsWith(REGEX_PREFIX)) {
-                try {
-                    final String strPattern = loopFragment.substring(REGEX_PREFIX.length(), loopFragment.length());
-                    final Pattern pattern = Pattern.compile(strPattern);
-                    if (pattern.matcher(testURI).matches()) {
-                        LOGGER.debug(sessionLabel, "positive URL match for regex pattern: " + strPattern);
-                        return;
-                    } else {
-                        LOGGER.trace(sessionLabel, "negative URL match for regex pattern: " + strPattern);
-                    }
-                } catch (Exception e) {
-                    LOGGER.error(sessionLabel, "error while testing URL match for regex pattern: '" + loopFragment + "', error: " + e.getMessage());;
-                }
-
-            } else {
-                if (testURI.startsWith(loopFragment)) {
-                    LOGGER.debug(sessionLabel, "positive URL match for pattern: " + loopFragment);
-                    return;
-                } else {
-                    LOGGER.trace(sessionLabel, "negative URL match for pattern: " + loopFragment);
-                }
-            }
-        }
-
-        final String errorMsg = testURI + " is not a match for any configured redirect whitelist, see setting: " + PwmSetting.SECURITY_REDIRECT_WHITELIST.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE);
-        throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
-    }
-
-    public static boolean determineIfDetailErrorMsgShown(final PwmApplication pwmApplication) {
-        if (pwmApplication == null) {
-            return false;
-        }
-        final PwmApplicationMode mode = pwmApplication.getApplicationMode();
-        if (mode == PwmApplicationMode.CONFIGURATION || mode == PwmApplicationMode.NEW) {
-            return true;
-        }
-        if (mode == PwmApplicationMode.RUNNING) {
-            if (pwmApplication.getConfig() != null) {
-                if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.DISPLAY_SHOW_DETAILED_ERRORS)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public static CSVPrinter makeCsvPrinter(final OutputStream outputStream)
-            throws IOException
-    {
-        return new CSVPrinter(new OutputStreamWriter(outputStream,PwmConstants.DEFAULT_CHARSET), PwmConstants.DEFAULT_CSV_FORMAT);
-    }
-
-
-    public static Properties newSortedProperties() {
-        return new Properties() {
-            public synchronized Enumeration<Object> keys() {
-                return Collections.enumeration(new TreeSet<>(super.keySet()));
-            }
-        };
-    }
-
-    public static ThreadFactory makePwmThreadFactory(final String namePrefix, final boolean daemon) {
-        return new ThreadFactory() {
-            private final ThreadFactory realThreadFactory = Executors.defaultThreadFactory();
-
-            @Override
-            public Thread newThread(final Runnable r) {
-                final Thread t = realThreadFactory.newThread(r);
-                t.setDaemon(daemon);
-                if (namePrefix != null) {
-                    final String newName = namePrefix + t.getName();
-                    t.setName(newName);
-                }
-                return t;
-            }
-        };
-    }
-
-}

+ 2 - 2
src/main/java/password/pwm/util/cli/commands/LocalDBInfoCommand.java

@@ -22,10 +22,10 @@
 
 
 package password.pwm.util.cli.commands;
 package password.pwm.util.cli.commands;
 
 
-import password.pwm.util.Helper;
 import password.pwm.util.cli.CliParameters;
 import password.pwm.util.cli.CliParameters;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBUtility;
 import password.pwm.util.localdb.LocalDBUtility;
@@ -40,7 +40,7 @@ public class LocalDBInfoCommand extends AbstractCliCommand {
         final LocalDB localDB = cliEnvironment.getLocalDB();
         final LocalDB localDB = cliEnvironment.getLocalDB();
         final long localDBdiskSpace = FileSystemUtility.getFileDirectorySize(localDB.getFileLocation());
         final long localDBdiskSpace = FileSystemUtility.getFileDirectorySize(localDB.getFileLocation());
         out("beginning LocalDBInfo");
         out("beginning LocalDBInfo");
-        out("LocalDB total disk space = " + NumberFormat.getInstance().format(localDBdiskSpace) + " (" + Helper.formatDiskSize(localDBdiskSpace) + ")");
+        out("LocalDB total disk space = " + NumberFormat.getInstance().format(localDBdiskSpace) + " (" + StringUtil.formatDiskSize(localDBdiskSpace) + ")");
         out("examining LocalDB, this may take a while.... ");
         out("examining LocalDB, this may take a while.... ");
         for (final LocalDB.DB db : LocalDB.DB.values()) {
         for (final LocalDB.DB db : LocalDB.DB.values()) {
             out("---" + db.toString() + "---");
             out("---" + db.toString() + "---");

+ 1 - 3
src/main/java/password/pwm/util/java/BlockingThreadPool.java

@@ -22,8 +22,6 @@
 
 
 package password.pwm.util.java;
 package password.pwm.util.java;
 
 
-import password.pwm.util.Helper;
-
 import java.util.concurrent.Future;
 import java.util.concurrent.Future;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.Semaphore;
@@ -35,7 +33,7 @@ public class BlockingThreadPool extends ThreadPoolExecutor {
     private final Semaphore semaphore;
     private final Semaphore semaphore;
 
 
     public BlockingThreadPool(final int bound, final String name) {
     public BlockingThreadPool(final int bound, final String name) {
-        super(bound, bound, 0, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(), Helper.makePwmThreadFactory(name, true));
+        super(bound, bound, 0, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(), JavaHelper.makePwmThreadFactory(name, true));
         semaphore = new Semaphore(bound);
         semaphore = new Semaphore(bound);
     }
     }
 
 

+ 68 - 9
src/main/java/password/pwm/util/java/JavaHelper.java

@@ -22,16 +22,18 @@
 
 
 package password.pwm.util.java;
 package password.pwm.util.java;
 
 
+import org.apache.commons.csv.CSVPrinter;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.IOUtils;
+import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.StringWriter;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Method;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -40,10 +42,16 @@ import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Date;
+import java.util.Enumeration;
 import java.util.GregorianCalendar;
 import java.util.GregorianCalendar;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.List;
+import java.util.Properties;
 import java.util.TimeZone;
 import java.util.TimeZone;
+import java.util.TreeSet;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 import java.util.function.Predicate;
 
 
@@ -150,18 +158,16 @@ public class JavaHelper {
     }
     }
 
 
     public static <E extends Enum<E>> E readEnumFromString(final Class<E> enumClass, final E defaultValue, final String input) {
     public static <E extends Enum<E>> E readEnumFromString(final Class<E> enumClass, final E defaultValue, final String input) {
-        if (input == null) {
+        if (StringUtil.isEmpty(input)) {
+            return defaultValue;
+        }
+
+        if (enumClass == null || !enumClass.isEnum()) {
             return defaultValue;
             return defaultValue;
         }
         }
 
 
         try {
         try {
-            final Method valueOfMethod = enumClass.getMethod("valueOf", String.class);
-            try {
-                final Object result = valueOfMethod.invoke(null, input);
-                return (E) result;
-            } catch (InvocationTargetException e) {
-                throw e.getCause();
-            }
+            return Enum.valueOf(enumClass, input);
         } catch (IllegalArgumentException e) {
         } catch (IllegalArgumentException e) {
             /* noop */
             /* noop */
             //LOGGER.trace("input=" + input + " does not exist in enumClass=" + enumClass.getSimpleName());
             //LOGGER.trace("input=" + input + " does not exist in enumClass=" + enumClass.getSimpleName());
@@ -267,4 +273,57 @@ public class JavaHelper {
         return false;
         return false;
     }
     }
 
 
+    public static String makeThreadName(final PwmApplication pwmApplication, final Class theClass) {
+        String instanceName = "-";
+        if (pwmApplication != null && pwmApplication.getInstanceID() != null) {
+            instanceName = pwmApplication.getInstanceID();
+        }
+
+        return PwmConstants.PWM_APP_NAME + "-" + instanceName + "-" + theClass.getSimpleName();
+    }
+
+    public static Properties newSortedProperties() {
+        return new Properties() {
+            public synchronized Enumeration<Object> keys() {
+                return Collections.enumeration(new TreeSet<>(super.keySet()));
+            }
+        };
+    }
+
+    public static ThreadFactory makePwmThreadFactory(final String namePrefix, final boolean daemon) {
+        return new ThreadFactory() {
+            private final ThreadFactory realThreadFactory = Executors.defaultThreadFactory();
+
+            @Override
+            public Thread newThread(final Runnable r) {
+                final Thread t = realThreadFactory.newThread(r);
+                t.setDaemon(daemon);
+                if (namePrefix != null) {
+                    final String newName = namePrefix + t.getName();
+                    t.setName(newName);
+                }
+                return t;
+            }
+        };
+    }
+
+    public static Collection<Method> getAllMethodsForClass(final Class clazz) {
+        final LinkedHashSet<Method> methods = new LinkedHashSet<>();
+
+        // add local methods;
+        methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
+
+        final Class superClass = clazz.getSuperclass();
+        if (superClass != null) {
+            methods.addAll(getAllMethodsForClass(superClass));
+        }
+
+        return Collections.unmodifiableSet(methods);
+    }
+
+    public static CSVPrinter makeCsvPrinter(final OutputStream outputStream)
+            throws IOException
+    {
+        return new CSVPrinter(new OutputStreamWriter(outputStream,PwmConstants.DEFAULT_CHARSET), PwmConstants.DEFAULT_CSV_FORMAT);
+    }
 }
 }

+ 31 - 0
src/main/java/password/pwm/util/java/StringUtil.java

@@ -33,6 +33,7 @@ import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
 import java.net.URLEncoder;
+import java.text.NumberFormat;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
@@ -157,6 +158,36 @@ public abstract class StringUtil {
         return StringUtils.join(inputs == null ? new String[]{} : inputs.toArray(), separator);
         return StringUtils.join(inputs == null ? new String[]{} : inputs.toArray(), separator);
     }
     }
 
 
+    public static String formatDiskSize(final long diskSize) {
+        final float COUNT = 1000;
+        if (diskSize < 1) {
+            return "n/a";
+        }
+
+        if (diskSize == 0) {
+            return "0";
+        }
+
+        final NumberFormat nf = NumberFormat.getInstance();
+        nf.setMaximumFractionDigits(2);
+
+        if (diskSize > COUNT * COUNT * COUNT) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(nf.format(diskSize / COUNT / COUNT / COUNT));
+            sb.append(" GB");
+            return sb.toString();
+        }
+
+        if (diskSize > COUNT * COUNT) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(nf.format(diskSize / COUNT / COUNT));
+            sb.append(" MB");
+            return sb.toString();
+        }
+
+        return NumberFormat.getInstance().format(diskSize) + " bytes";
+    }
+
     public enum Base64Options {
     public enum Base64Options {
         GZIP,
         GZIP,
         URL_SAFE,
         URL_SAFE,

+ 5 - 5
src/main/java/password/pwm/util/localdb/Derby_LocalDB.java

@@ -25,7 +25,7 @@ package password.pwm.util.localdb;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
-import password.pwm.util.Helper;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
@@ -144,16 +144,16 @@ public class Derby_LocalDB extends AbstractJDBC_LocalDB {
     private void reclaimAllSpace(final Connection dbConnection) {
     private void reclaimAllSpace(final Connection dbConnection) {
         final java.util.Date startTime = new java.util.Date();
         final java.util.Date startTime = new java.util.Date();
         final long startSize = FileSystemUtility.getFileDirectorySize(dbDirectory);
         final long startSize = FileSystemUtility.getFileDirectorySize(dbDirectory);
-        LOGGER.debug("beginning reclaim space in all tables startSize=" + Helper.formatDiskSize(startSize));
+        LOGGER.debug("beginning reclaim space in all tables startSize=" + StringUtil.formatDiskSize(startSize));
         for (final LocalDB.DB db : LocalDB.DB.values()) {
         for (final LocalDB.DB db : LocalDB.DB.values()) {
             reclaimSpace(dbConnection,db);
             reclaimSpace(dbConnection,db);
         }
         }
         final long completeSize = FileSystemUtility.getFileDirectorySize(dbDirectory);
         final long completeSize = FileSystemUtility.getFileDirectorySize(dbDirectory);
         final long sizeDifference = startSize - completeSize;
         final long sizeDifference = startSize - completeSize;
         LOGGER.debug("completed reclaim space in all tables; duration=" + TimeDuration.fromCurrent(startTime).asCompactString()
         LOGGER.debug("completed reclaim space in all tables; duration=" + TimeDuration.fromCurrent(startTime).asCompactString()
-                + ", startSize=" + Helper.formatDiskSize(startSize)
-                + ", completeSize=" + Helper.formatDiskSize(completeSize)
-                + ", sizeDifference=" + Helper.formatDiskSize(sizeDifference)
+                + ", startSize=" + StringUtil.formatDiskSize(startSize)
+                + ", completeSize=" + StringUtil.formatDiskSize(completeSize)
+                + ", sizeDifference=" + StringUtil.formatDiskSize(sizeDifference)
         );
         );
     }
     }
 
 

+ 2 - 3
src/main/java/password/pwm/util/localdb/LocalDBFactory.java

@@ -28,7 +28,6 @@ import password.pwm.config.Configuration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
-import password.pwm.util.Helper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -100,11 +99,11 @@ public class LocalDBFactory {
 
 
         final StringBuilder debugText = new StringBuilder();
         final StringBuilder debugText = new StringBuilder();
         debugText.append("LocalDB open in ").append(openTime.asCompactString());
         debugText.append("LocalDB open in ").append(openTime.asCompactString());
-        debugText.append(", db size: ").append(Helper.formatDiskSize(FileSystemUtility.getFileDirectorySize(localDB.getFileLocation())));
+        debugText.append(", db size: ").append(StringUtil.formatDiskSize(FileSystemUtility.getFileDirectorySize(localDB.getFileLocation())));
         debugText.append(" at ").append(dbDirectory.toString());
         debugText.append(" at ").append(dbDirectory.toString());
         final long freeSpace = FileSystemUtility.diskSpaceRemaining(localDB.getFileLocation());
         final long freeSpace = FileSystemUtility.diskSpaceRemaining(localDB.getFileLocation());
         if (freeSpace >= 0) {
         if (freeSpace >= 0) {
-            debugText.append(", ").append(Helper.formatDiskSize(freeSpace)).append(" free");
+            debugText.append(", ").append(StringUtil.formatDiskSize(freeSpace)).append(" free");
         }
         }
         LOGGER.info(debugText);
         LOGGER.info(debugText);
 
 

+ 1 - 2
src/main/java/password/pwm/util/localdb/LocalDBUtility.java

@@ -31,7 +31,6 @@ import password.pwm.PwmConstants;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.svc.stats.EventRateMeter;
 import password.pwm.svc.stats.EventRateMeter;
-import password.pwm.util.Helper;
 import password.pwm.util.ProgressInfo;
 import password.pwm.util.ProgressInfo;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.TransactionSizeCalculator;
 import password.pwm.util.TransactionSizeCalculator;
@@ -116,7 +115,7 @@ public class LocalDBUtility {
         },30 * 1000, 30 * 1000);
         },30 * 1000, 30 * 1000);
 
 
 
 
-        try (CSVPrinter csvPrinter = Helper.makeCsvPrinter(new GZIPOutputStream(outputStream, GZIP_BUFFER_SIZE))) {
+        try (CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(new GZIPOutputStream(outputStream, GZIP_BUFFER_SIZE))) {
             csvPrinter.printComment(PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION + " LocalDB export on " + JavaHelper.toIsoDate(new Date()));
             csvPrinter.printComment(PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION + " LocalDB export on " + JavaHelper.toIsoDate(new Date()));
             for (final LocalDB.DB loopDB : LocalDB.DB.values()) {
             for (final LocalDB.DB loopDB : LocalDB.DB.values()) {
                 if (loopDB.isBackup()) {
                 if (loopDB.isBackup()) {

+ 1 - 2
src/main/java/password/pwm/util/localdb/WorkQueueProcessor.java

@@ -27,7 +27,6 @@ import password.pwm.PwmApplication;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
-import password.pwm.util.Helper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
@@ -103,7 +102,7 @@ public class WorkQueueProcessor<W extends Serializable> {
 
 
         this.workerThread = new WorkerThread();
         this.workerThread = new WorkerThread();
         workerThread.setDaemon(true);
         workerThread.setDaemon(true);
-        workerThread.setName(Helper.makeThreadName(pwmApplication, sourceClass) + "-worker-");
+        workerThread.setName(JavaHelper.makeThreadName(pwmApplication, sourceClass) + "-worker-");
         workerThread.start();
         workerThread.start();
     }
     }
 
 

+ 6 - 6
src/main/java/password/pwm/util/logging/LocalDBLogger.java

@@ -28,9 +28,9 @@ import password.pwm.error.PwmException;
 import password.pwm.health.HealthMessage;
 import password.pwm.health.HealthMessage;
 import password.pwm.health.HealthRecord;
 import password.pwm.health.HealthRecord;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.Helper;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.localdb.LocalDBException;
@@ -113,14 +113,14 @@ public class LocalDBLogger implements PwmService {
         status = STATUS.OPEN;
         status = STATUS.OPEN;
 
 
         cleanerService = Executors.newSingleThreadScheduledExecutor(
         cleanerService = Executors.newSingleThreadScheduledExecutor(
-                Helper.makePwmThreadFactory(
-                        Helper.makeThreadName(pwmApplication, this.getClass()) + "-cleaner-",
+                JavaHelper.makePwmThreadFactory(
+                        JavaHelper.makeThreadName(pwmApplication, this.getClass()) + "-cleaner-",
                         true
                         true
                 ));
                 ));
 
 
         writerService = Executors.newSingleThreadScheduledExecutor(
         writerService = Executors.newSingleThreadScheduledExecutor(
-                Helper.makePwmThreadFactory(
-                        Helper.makeThreadName(pwmApplication, this.getClass()) + "-writer-",
+                JavaHelper.makePwmThreadFactory(
+                        JavaHelper.makeThreadName(pwmApplication, this.getClass()) + "-writer-",
                         true
                         true
                 ));
                 ));
 
 
@@ -162,7 +162,7 @@ public class LocalDBLogger implements PwmService {
         sb.append(", tailAge=").append(tailAge == null ? "n/a" : TimeDuration.fromCurrent(tailAge).asCompactString());
         sb.append(", tailAge=").append(tailAge == null ? "n/a" : TimeDuration.fromCurrent(tailAge).asCompactString());
         sb.append(", maxEvents=").append(settings.getMaxEvents());
         sb.append(", maxEvents=").append(settings.getMaxEvents());
         sb.append(", maxAge=").append(settings.getMaxAge().asCompactString());
         sb.append(", maxAge=").append(settings.getMaxAge().asCompactString());
-        sb.append(", localDBSize=").append(Helper.formatDiskSize(FileSystemUtility.getFileDirectorySize(localDB.getFileLocation())));
+        sb.append(", localDBSize=").append(StringUtil.formatDiskSize(FileSystemUtility.getFileDirectorySize(localDB.getFileLocation())));
         return sb.toString();
         return sb.toString();
     }
     }
 
 

+ 1 - 2
src/main/java/password/pwm/ws/server/RestResultBean.java

@@ -27,7 +27,6 @@ import password.pwm.config.Configuration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
-import password.pwm.util.Helper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response;
@@ -107,7 +106,7 @@ public class RestResultBean implements Serializable {
         final RestResultBean restResultBean = new RestResultBean();
         final RestResultBean restResultBean = new RestResultBean();
         restResultBean.setError(true);
         restResultBean.setError(true);
         restResultBean.setErrorMessage(errorInformation.toUserStr(locale, config));
         restResultBean.setErrorMessage(errorInformation.toUserStr(locale, config));
-        if (forceDetail || Helper.determineIfDetailErrorMsgShown(pwmApplication)) {
+        if (forceDetail || pwmApplication.determineIfDetailErrorMsgShown()) {
             restResultBean.setErrorDetail(errorInformation.toDebugStr());
             restResultBean.setErrorDetail(errorInformation.toDebugStr());
         }
         }
         restResultBean.setErrorCode(errorInformation.getError().getErrorCode());
         restResultBean.setErrorCode(errorInformation.getError().getErrorCode());

+ 2 - 3
src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp

@@ -30,7 +30,6 @@
 <%@ page import="password.pwm.svc.PwmService" %>
 <%@ page import="password.pwm.svc.PwmService" %>
 <%@ page import="password.pwm.svc.sessiontrack.SessionTrackService" %>
 <%@ page import="password.pwm.svc.sessiontrack.SessionTrackService" %>
 <%@ page import="password.pwm.svc.stats.Statistic" %>
 <%@ page import="password.pwm.svc.stats.Statistic" %>
-<%@ page import="password.pwm.util.Helper" %>
 <%@ page import="password.pwm.util.java.FileSystemUtility" %>
 <%@ page import="password.pwm.util.java.FileSystemUtility" %>
 <%@ page import="password.pwm.util.java.JavaHelper" %>
 <%@ page import="password.pwm.util.java.JavaHelper" %>
 <%@ page import="password.pwm.util.java.StringUtil" %>
 <%@ page import="password.pwm.util.java.StringUtil" %>
@@ -482,7 +481,7 @@
                                         ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                                         ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                                         : dashboard_pwmApplication.getLocalDB().getFileLocation() == null
                                         : dashboard_pwmApplication.getLocalDB().getFileLocation() == null
                                         ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                                         ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
-                                        : Helper.formatDiskSize(FileSystemUtility.getFileDirectorySize(
+                                        : StringUtil.formatDiskSize(FileSystemUtility.getFileDirectorySize(
                                         dashboard_pwmApplication.getLocalDB().getFileLocation()))
                                         dashboard_pwmApplication.getLocalDB().getFileLocation()))
                                 %>
                                 %>
                             </td>
                             </td>
@@ -510,7 +509,7 @@
                                         ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                                         ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                                         : dashboard_pwmApplication.getLocalDB().getFileLocation() == null
                                         : dashboard_pwmApplication.getLocalDB().getFileLocation() == null
                                         ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                                         ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
-                                        : Helper.formatDiskSize(FileSystemUtility.diskSpaceRemaining(dashboard_pwmApplication.getLocalDB().getFileLocation())) %>
+                                        : StringUtil.formatDiskSize(FileSystemUtility.diskSpaceRemaining(dashboard_pwmApplication.getLocalDB().getFileLocation())) %>
                             </td>
                             </td>
                         </tr>
                         </tr>
                         <tr>
                         <tr>

+ 3 - 3
src/main/webapp/WEB-INF/jsp/configmanager-localdb.jsp

@@ -4,8 +4,8 @@
 <%@ page import="password.pwm.i18n.Config" %>
 <%@ page import="password.pwm.i18n.Config" %>
 <%@ page import="password.pwm.i18n.Display" %>
 <%@ page import="password.pwm.i18n.Display" %>
 <%@ page import="password.pwm.util.java.FileSystemUtility" %>
 <%@ page import="password.pwm.util.java.FileSystemUtility" %>
-<%@ page import="password.pwm.util.Helper" %>
 <%@ page import="password.pwm.util.LocaleHelper" %>
 <%@ page import="password.pwm.util.LocaleHelper" %>
+<%@ page import="password.pwm.util.java.StringUtil" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -101,7 +101,7 @@
                             ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                             ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                             : localdb_pwmApplication.getLocalDB().getFileLocation() == null
                             : localdb_pwmApplication.getLocalDB().getFileLocation() == null
                             ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                             ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
-                            : Helper.formatDiskSize(FileSystemUtility.getFileDirectorySize(
+                            : StringUtil.formatDiskSize(FileSystemUtility.getFileDirectorySize(
                             localdb_pwmApplication.getLocalDB().getFileLocation()))
                             localdb_pwmApplication.getLocalDB().getFileLocation()))
                     %>
                     %>
                 </td>
                 </td>
@@ -115,7 +115,7 @@
                             ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                             ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                             : localdb_pwmApplication.getLocalDB().getFileLocation() == null
                             : localdb_pwmApplication.getLocalDB().getFileLocation() == null
                             ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
                             ? JspUtility.getMessage(pageContext, Display.Value_NotApplicable)
-                            : Helper.formatDiskSize(FileSystemUtility.diskSpaceRemaining(localdb_pwmApplication.getLocalDB().getFileLocation())) %>
+                            : StringUtil.formatDiskSize(FileSystemUtility.diskSpaceRemaining(localdb_pwmApplication.getLocalDB().getFileLocation())) %>
                 </td>
                 </td>
             </tr>
             </tr>
         </table>
         </table>

+ 1 - 0
src/test/java/password/pwm/error/PwmErrorTest.java

@@ -42,6 +42,7 @@ public class PwmErrorTest extends TestCase {
         }
         }
     }
     }
 
 
+    @Test
     public void testLocalizedMessage() {
     public void testLocalizedMessage() {
         for (final PwmError pwmError : PwmError.values()) {
         for (final PwmError pwmError : PwmError.values()) {
             pwmError.getLocalizedMessage(PwmConstants.DEFAULT_LOCALE, null);
             pwmError.getLocalizedMessage(PwmConstants.DEFAULT_LOCALE, null);

+ 160 - 0
src/test/java/password/pwm/http/servlet/ControlledPwmServletTest.java

@@ -0,0 +1,160 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2016 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.http.servlet;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.reflections.Reflections;
+import org.reflections.scanners.FieldAnnotationsScanner;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.scanners.TypeAnnotationsScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+import password.pwm.http.ProcessStatus;
+import password.pwm.http.PwmRequest;
+import password.pwm.util.java.JavaHelper;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ControlledPwmServletTest {
+    @Test
+    public void testProcess() throws IllegalAccessException, InstantiationException {
+        final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods();
+
+        for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) {
+            final Class<? extends AbstractPwmServlet.ProcessAction> processActionsClass = controlledPwmServlet.newInstance().getProcessActionsClass();
+            if (!processActionsClass.isEnum()) {
+                Assert.fail(controlledPwmServlet.getName() + " process action class must be an enum");
+            }
+        }
+    }
+
+    @Test
+    public void testActionHandlerReturnTypes() throws IllegalAccessException, InstantiationException {
+        final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods();
+
+        for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) {
+            final String servletName = controlledPwmServlet.getName();
+            for (final String methodName : dataMap.get(controlledPwmServlet).keySet()) {
+                final Method method = dataMap.get(controlledPwmServlet).get(methodName);
+                if (method.getReturnType() != ProcessStatus.class) {
+                    Assert.fail(servletName + ":" + method.getName() + " must have return type of " + ProcessStatus.class.getName());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testActionHandlerParameters() throws IllegalAccessException, InstantiationException {
+        final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods();
+
+        for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) {
+            final String servletName = controlledPwmServlet.getName();
+            for (final String methodName : dataMap.get(controlledPwmServlet).keySet()) {
+                final Method method = dataMap.get(controlledPwmServlet).get(methodName);
+                final Class[] returnTypes = method.getParameterTypes();
+                if (returnTypes.length != 1) {
+                    Assert.fail(servletName + ":" + method.getName() + " must have exactly one parameter");
+                }
+                if (!returnTypes[0].equals(PwmRequest.class)) {
+                    Assert.fail(servletName + ":" + method.getName() + " must have exactly one parameter of type " + PwmRequest.class.getName());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testActionHandlersExistence() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
+        final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> dataMap = getClassAndMethods();
+
+        for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : dataMap.keySet()) {
+            final String servletName = controlledPwmServlet.getName();
+
+            final Class<? extends AbstractPwmServlet.ProcessAction> processActionsClass = controlledPwmServlet.newInstance().getProcessActionsClass();
+            final List<String> names = new ArrayList<>();
+            for (Object enumObject : processActionsClass.getEnumConstants()) {
+                names.add(((Enum)enumObject).name());
+            }
+
+            {
+                final Collection<String> missingActionHandlers = new HashSet<>(names);
+                missingActionHandlers.removeAll(dataMap.get(controlledPwmServlet).keySet());
+                if (!missingActionHandlers.isEmpty()) {
+                    Assert.fail(servletName + " does not have an action handler for action " + missingActionHandlers.iterator().next());
+                }
+            }
+
+            {
+                final Collection<String> superflousActionHandlers = new HashSet<>(dataMap.get(controlledPwmServlet).keySet());
+                superflousActionHandlers.removeAll(names);
+                if (!superflousActionHandlers.isEmpty()) {
+                    Assert.fail(servletName + " has an action handler for action " + superflousActionHandlers.iterator().next() + " but no such ProcessAction exists");
+                }
+            }
+        }
+    }
+
+    private Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> getClassAndMethods() {
+        Reflections reflections = new Reflections(new ConfigurationBuilder()
+                .setUrls(ClasspathHelper.forPackage("password.pwm"))
+                .setScanners(new SubTypesScanner(),
+                        new TypeAnnotationsScanner(),
+                        new FieldAnnotationsScanner()
+                ));
+
+
+        new Reflections("password.pwm");
+
+        Set<Class<? extends ControlledPwmServlet>> classes = reflections.getSubTypesOf(ControlledPwmServlet.class);
+
+        final Map<Class<? extends ControlledPwmServlet>,Map<String,Method>> returnMap = new HashMap<>();
+
+        for (final Class<? extends ControlledPwmServlet> controlledPwmServlet : classes) {
+            if (!Modifier.isAbstract(controlledPwmServlet.getModifiers())) {
+
+                final Map<String, Method> annotatedMethods = new HashMap<>();
+
+                for (Method method : JavaHelper.getAllMethodsForClass(controlledPwmServlet)) {
+                    if (method.getAnnotation(ControlledPwmServlet.ActionHandler.class) != null) {
+                        final String actionName = method.getAnnotation(ControlledPwmServlet.ActionHandler.class).action();
+                        annotatedMethods.put(actionName, method);
+                    }
+                }
+
+                returnMap.put(controlledPwmServlet, Collections.unmodifiableMap(annotatedMethods));
+            }
+        }
+
+        return Collections.unmodifiableMap(returnMap);
+    }
+}

+ 4 - 4
src/test/java/password/pwm/manual/LocalDBLoggerTest.java

@@ -28,11 +28,11 @@ import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.stored.ConfigurationReader;
 import password.pwm.config.stored.ConfigurationReader;
 import password.pwm.svc.stats.EventRateMeter;
 import password.pwm.svc.stats.EventRateMeter;
-import password.pwm.util.Helper;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.Percent;
 import password.pwm.util.java.Percent;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBFactory;
 import password.pwm.util.localdb.LocalDBFactory;
@@ -176,9 +176,9 @@ public class LocalDBLoggerTest extends TestCase {
     private void outputDebugInfo() {
     private void outputDebugInfo() {
         final StringBuilder sb = new StringBuilder();
         final StringBuilder sb = new StringBuilder();
         sb.append("added ").append(numberFormat.format(eventsAdded.get()));
         sb.append("added ").append(numberFormat.format(eventsAdded.get()));
-        sb.append(", size: ").append(Helper.formatDiskSize(FileSystemUtility.getFileDirectorySize(localDB.getFileLocation())));
+        sb.append(", size: ").append(StringUtil.formatDiskSize(FileSystemUtility.getFileDirectorySize(localDB.getFileLocation())));
         sb.append(", eventsInDb: ").append(figureEventsInDbStat());
         sb.append(", eventsInDb: ").append(figureEventsInDbStat());
-        sb.append(", free: ").append(Helper.formatDiskSize(
+        sb.append(", free: ").append(StringUtil.formatDiskSize(
                 FileSystemUtility.diskSpaceRemaining(localDB.getFileLocation())));
                 FileSystemUtility.diskSpaceRemaining(localDB.getFileLocation())));
         sb.append(", eps: ").append(eventRateMeter.readEventRate().setScale(0, RoundingMode.UP));
         sb.append(", eps: ").append(eventRateMeter.readEventRate().setScale(0, RoundingMode.UP));
         sb.append(", remain: ").append(settings.testDuration.subtract(TimeDuration.fromCurrent(startTime)).asCompactString());
         sb.append(", remain: ").append(settings.testDuration.subtract(TimeDuration.fromCurrent(startTime)).asCompactString());
@@ -199,7 +199,7 @@ public class LocalDBLoggerTest extends TestCase {
         results.dbClass = config.readAppProperty(AppProperty.LOCALDB_IMPLEMENTATION);
         results.dbClass = config.readAppProperty(AppProperty.LOCALDB_IMPLEMENTATION);
         results.duration = TimeDuration.fromCurrent(startTime).asCompactString();
         results.duration = TimeDuration.fromCurrent(startTime).asCompactString();
         results.recordsAdded = eventsAdded.get();
         results.recordsAdded = eventsAdded.get();
-        results.dbSize = Helper.formatDiskSize(FileSystemUtility.getFileDirectorySize(localDB.getFileLocation()));
+        results.dbSize = StringUtil.formatDiskSize(FileSystemUtility.getFileDirectorySize(localDB.getFileLocation()));
         results.eventsInDb = figureEventsInDbStat();
         results.eventsInDb = figureEventsInDbStat();
         return results;
         return results;
     }
     }