Преглед изворни кода

stub pw expire notice email

Jason Rivard пре 8 година
родитељ
комит
ca7784f151
100 измењених фајлова са 1562 додато и 884 уклоњено
  1. 0 6
      pom.xml
  2. 7 0
      src/main/java/password/pwm/AppProperty.java
  3. 2 7
      src/main/java/password/pwm/PwmConstants.java
  4. 2 2
      src/main/java/password/pwm/config/Configuration.java
  5. 2 1
      src/main/java/password/pwm/health/CertificateChecker.java
  6. 19 2
      src/main/java/password/pwm/health/HealthMonitor.java
  7. 2 2
      src/main/java/password/pwm/http/JspUtility.java
  8. 59 122
      src/main/java/password/pwm/http/PwmRequest.java
  9. 89 0
      src/main/java/password/pwm/http/PwmRequestAttribute.java
  10. 3 3
      src/main/java/password/pwm/http/PwmResponse.java
  11. 3 1
      src/main/java/password/pwm/http/PwmURL.java
  12. 10 0
      src/main/java/password/pwm/http/bean/AdminBean.java
  13. 2 1
      src/main/java/password/pwm/http/filter/ApplicationModeFilter.java
  14. 4 3
      src/main/java/password/pwm/http/filter/ConfigAccessFilter.java
  15. 4 3
      src/main/java/password/pwm/http/filter/RequestInitializationFilter.java
  16. 3 2
      src/main/java/password/pwm/http/servlet/AbstractPwmServlet.java
  17. 2 1
      src/main/java/password/pwm/http/servlet/AccountInformationServlet.java
  18. 2 1
      src/main/java/password/pwm/http/servlet/DeleteAccountServlet.java
  19. 2 1
      src/main/java/password/pwm/http/servlet/ForgottenUsernameServlet.java
  20. 4 3
      src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java
  21. 4 3
      src/main/java/password/pwm/http/servlet/LogoutServlet.java
  22. 4 1
      src/main/java/password/pwm/http/servlet/PwmServletDefinition.java
  23. 2 1
      src/main/java/password/pwm/http/servlet/SetupOtpServlet.java
  24. 5 4
      src/main/java/password/pwm/http/servlet/SetupResponsesServlet.java
  25. 2 1
      src/main/java/password/pwm/http/servlet/ShortcutServlet.java
  26. 3 2
      src/main/java/password/pwm/http/servlet/UpdateProfileServlet.java
  27. 49 7
      src/main/java/password/pwm/http/servlet/admin/AdminServlet.java
  28. 1 0
      src/main/java/password/pwm/http/servlet/admin/UserDebugDataBean.java
  29. 18 3
      src/main/java/password/pwm/http/servlet/admin/UserDebugDataReader.java
  30. 74 14
      src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java
  31. 5 26
      src/main/java/password/pwm/http/servlet/command/CommandServlet.java
  32. 39 0
      src/main/java/password/pwm/http/servlet/command/PrivateCommandServlet.java
  33. 39 0
      src/main/java/password/pwm/http/servlet/command/PublicCommandServlet.java
  34. 2 1
      src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java
  35. 2 1
      src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerCertificatesServlet.java
  36. 13 8
      src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerServlet.java
  37. 37 1
      src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java
  38. 46 417
      src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java
  39. 443 0
      src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordUtil.java
  40. 7 6
      src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java
  41. 3 2
      src/main/java/password/pwm/http/servlet/newuser/NewUserServlet.java
  42. 4 3
      src/main/java/password/pwm/http/state/CryptoCookieBeanImpl.java
  43. 2 1
      src/main/java/password/pwm/http/tag/ErrorMessageTag.java
  44. 2 1
      src/main/java/password/pwm/http/tag/SuccessMessageTag.java
  45. 25 4
      src/main/java/password/pwm/http/tag/conditional/PwmIfTest.java
  46. 58 6
      src/main/java/password/pwm/ldap/LdapOperationsHelper.java
  47. 6 3
      src/main/java/password/pwm/ldap/UserStatusReader.java
  48. 2 30
      src/main/java/password/pwm/ldap/auth/LDAPAuthenticationRequest.java
  49. 138 26
      src/main/java/password/pwm/svc/pwnotify/PasswordExpireNotificationEngine.java
  50. 12 15
      src/main/java/password/pwm/svc/report/ReportService.java
  51. 17 1
      src/main/java/password/pwm/svc/sessiontrack/SessionTrackService.java
  52. 4 3
      src/main/java/password/pwm/util/CaptchaUtility.java
  53. 1 1
      src/main/java/password/pwm/util/LocaleHelper.java
  54. 2 1
      src/main/java/password/pwm/util/cli/MainClass.java
  55. 52 0
      src/main/java/password/pwm/util/cli/commands/PasswordExpireNotificationCommand.java
  56. 2 0
      src/main/java/password/pwm/util/db/DatabaseAccessor.java
  57. 21 0
      src/main/java/password/pwm/util/db/DatabaseAccessorImpl.java
  58. 4 0
      src/main/java/password/pwm/util/java/TimeDuration.java
  59. 2 2
      src/main/java/password/pwm/util/macro/StandardMacros.java
  60. 7 8
      src/main/java/password/pwm/util/operations/PasswordUtility.java
  61. 1 1
      src/main/java/password/pwm/ws/server/rest/RestAppDataServer.java
  62. 7 0
      src/main/resources/password/pwm/AppProperty.properties
  63. 4 3
      src/main/webapp/WEB-INF/jsp/accountinformation.jsp
  64. 56 19
      src/main/webapp/WEB-INF/jsp/admin-user-debug.jsp
  65. 3 2
      src/main/webapp/WEB-INF/jsp/application-unavailable.jsp
  66. 2 1
      src/main/webapp/WEB-INF/jsp/changepassword-agreement.jsp
  67. 4 3
      src/main/webapp/WEB-INF/jsp/changepassword-complete.jsp
  68. 3 2
      src/main/webapp/WEB-INF/jsp/changepassword-wait.jsp
  69. 12 14
      src/main/webapp/WEB-INF/jsp/changepassword.jsp
  70. 2 1
      src/main/webapp/WEB-INF/jsp/configmanager-certificates.jsp
  71. 3 2
      src/main/webapp/WEB-INF/jsp/configmanager-login.jsp
  72. 2 1
      src/main/webapp/WEB-INF/jsp/configmanager-summary.jsp
  73. 6 5
      src/main/webapp/WEB-INF/jsp/configmanager.jsp
  74. 2 1
      src/main/webapp/WEB-INF/jsp/deleteaccount-agreement.jsp
  75. 2 2
      src/main/webapp/WEB-INF/jsp/error-http.jsp
  76. 4 3
      src/main/webapp/WEB-INF/jsp/error.jsp
  77. 2 1
      src/main/webapp/WEB-INF/jsp/forgottenpassword-attributes.jsp
  78. 3 2
      src/main/webapp/WEB-INF/jsp/forgottenpassword-enterotp.jsp
  79. 2 1
      src/main/webapp/WEB-INF/jsp/forgottenpassword-entertoken.jsp
  80. 2 1
      src/main/webapp/WEB-INF/jsp/forgottenpassword-method.jsp
  81. 4 3
      src/main/webapp/WEB-INF/jsp/forgottenpassword-remote.jsp
  82. 3 2
      src/main/webapp/WEB-INF/jsp/forgottenpassword-responses.jsp
  83. 4 3
      src/main/webapp/WEB-INF/jsp/forgottenusername-complete.jsp
  84. 2 2
      src/main/webapp/WEB-INF/jsp/fragment/cancel-form.jsp
  85. 3 2
      src/main/webapp/WEB-INF/jsp/fragment/captcha-embed.jsp
  86. 1 6
      src/main/webapp/WEB-INF/jsp/fragment/footer.jsp
  87. 5 4
      src/main/webapp/WEB-INF/jsp/fragment/form.jsp
  88. 2 1
      src/main/webapp/WEB-INF/jsp/fragment/ldap-permissions.jsp
  89. 2 2
      src/main/webapp/WEB-INF/jsp/fragment/message.jsp
  90. 4 4
      src/main/webapp/WEB-INF/jsp/guest-create.jsp
  91. 4 4
      src/main/webapp/WEB-INF/jsp/guest-update.jsp
  92. 5 5
      src/main/webapp/WEB-INF/jsp/helpdesk-detail.jsp
  93. 2 1
      src/main/webapp/WEB-INF/jsp/helpdesk.jsp
  94. 3 3
      src/main/webapp/WEB-INF/jsp/login.jsp
  95. 3 3
      src/main/webapp/WEB-INF/jsp/logout-public.jsp
  96. 3 3
      src/main/webapp/WEB-INF/jsp/logout.jsp
  97. 2 1
      src/main/webapp/WEB-INF/jsp/newuser-agreement.jsp
  98. 2 2
      src/main/webapp/WEB-INF/jsp/newuser-profilechoice.jsp
  99. 2 1
      src/main/webapp/WEB-INF/jsp/newuser.jsp
  100. 4 3
      src/main/webapp/WEB-INF/jsp/setupotpsecret.jsp

+ 0 - 6
pom.xml

@@ -140,12 +140,6 @@
                     </excludes>
                     </excludes>
                 </configuration>
                 </configuration>
             </plugin>
             </plugin>
-            <plugin>
-                <!-- This allows us to run: "mvn tomcat7:run", then we can open a browser to: http://localhost:8080/pwm -->
-                <groupId>org.apache.tomcat.maven</groupId>
-                <artifactId>tomcat7-maven-plugin</artifactId>
-                <version>2.2</version>
-            </plugin>
             <plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-source-plugin</artifactId>
                 <artifactId>maven-source-plugin</artifactId>

+ 7 - 0
src/main/java/password/pwm/AppProperty.java

@@ -70,6 +70,12 @@ public enum     AppProperty {
     CONFIG_MANAGER_ZIPDEBUG_MAXLOGLINES             ("configManager.zipDebug.maxLogLines"),
     CONFIG_MANAGER_ZIPDEBUG_MAXLOGLINES             ("configManager.zipDebug.maxLogLines"),
     CONFIG_MANAGER_ZIPDEBUG_MAXLOGSECONDS           ("configManager.zipDebug.maxLogSeconds"),
     CONFIG_MANAGER_ZIPDEBUG_MAXLOGSECONDS           ("configManager.zipDebug.maxLogSeconds"),
     DB_JDBC_LOAD_STRATEGY                           ("db.jdbcLoadStrategy"),
     DB_JDBC_LOAD_STRATEGY                           ("db.jdbcLoadStrategy"),
+    DOWNLOAD_FILENAME_STATISTICS_CSV                ("download.filename.statistics.csv"),
+    DOWNLOAD_FILENAME_USER_REPORT_SUMMARY_CSV       ("download.filename.reportSummary.csv"),
+    DOWNLOAD_FILENAME_USER_REPORT_RECORDS_CSV       ("download.filename.reportRecords.csv"),
+    DOWNLOAD_FILENAME_AUDIT_RECORDS_CSV             ("download.filename.auditRecords.csv"),
+    DOWNLOAD_FILENAME_LDAP_PERMISSION_CSV           ("download.filename.ldapPermission.csv"),
+    DOWNLOAD_FILENAME_USER_DEBUG_JSON               ("download.filename.userDebug.json"),
     FORM_EMAIL_REGEX                                ("form.email.regexTest"),
     FORM_EMAIL_REGEX                                ("form.email.regexTest"),
     HTTP_RESOURCES_MAX_CACHE_ITEMS                  ("http.resources.maxCacheItems"),
     HTTP_RESOURCES_MAX_CACHE_ITEMS                  ("http.resources.maxCacheItems"),
     HTTP_RESOURCES_MAX_CACHE_BYTES                  ("http.resources.maxCacheBytes"),
     HTTP_RESOURCES_MAX_CACHE_BYTES                  ("http.resources.maxCacheBytes"),
@@ -147,6 +153,7 @@ public enum     AppProperty {
     INTRUDER_MAX_DELAY_PENALTY_MS                   ("intruder.maximumDelayPenaltyMS"),
     INTRUDER_MAX_DELAY_PENALTY_MS                   ("intruder.maximumDelayPenaltyMS"),
     INTRUDER_DELAY_PER_COUNT_MS                     ("intruder.delayPerCountMS"),
     INTRUDER_DELAY_PER_COUNT_MS                     ("intruder.delayPerCountMS"),
     INTRUDER_DELAY_MAX_JITTER_MS                    ("intruder.delayMaxJitterMS"),
     INTRUDER_DELAY_MAX_JITTER_MS                    ("intruder.delayMaxJitterMS"),
+    HEALTHCHECK_ENABLED                             ("healthCheck.enabled"),
     HEALTHCHECK_NOMINAL_CHECK_INTERVAL              ("healthCheck.nominalCheckIntervalSeconds"),
     HEALTHCHECK_NOMINAL_CHECK_INTERVAL              ("healthCheck.nominalCheckIntervalSeconds"),
     HEALTHCHECK_MIN_CHECK_INTERVAL                  ("healthCheck.minimumCheckIntervalSeconds"),
     HEALTHCHECK_MIN_CHECK_INTERVAL                  ("healthCheck.minimumCheckIntervalSeconds"),
     HEALTHCHECK_MAX_RECORD_AGE                      ("healthCheck.maximumRecordAgeSeconds"),
     HEALTHCHECK_MAX_RECORD_AGE                      ("healthCheck.maximumRecordAgeSeconds"),

+ 2 - 7
src/main/java/password/pwm/PwmConstants.java

@@ -114,6 +114,8 @@ public abstract class PwmConstants {
     public static final SessionLabel HEALTH_SESSION_LABEL = new SessionLabel(SESSION_LABEL_SESSION_ID ,null,"health",null,null);
     public static final SessionLabel HEALTH_SESSION_LABEL = new SessionLabel(SESSION_LABEL_SESSION_ID ,null,"health",null,null);
     public static final SessionLabel CLI_SESSION_LABEL= new SessionLabel(SESSION_LABEL_SESSION_ID ,null,"cli",null,null);
     public static final SessionLabel CLI_SESSION_LABEL= new SessionLabel(SESSION_LABEL_SESSION_ID ,null,"cli",null,null);
     public static final SessionLabel TOKEN_SESSION_LABEL = new SessionLabel(SESSION_LABEL_SESSION_ID ,null,"token",null,null);
     public static final SessionLabel TOKEN_SESSION_LABEL = new SessionLabel(SESSION_LABEL_SESSION_ID ,null,"token",null,null);
+    public static final SessionLabel PW_EXP_NOTICE_LABEL = new SessionLabel(SESSION_LABEL_SESSION_ID ,null,"pwExpireNotice",null,null);
+
 
 
     public static final int DATABASE_ACCESSOR_KEY_LENGTH = Integer.parseInt(readPwmConstantsBundle("databaseAccessor.keyLength"));
     public static final int DATABASE_ACCESSOR_KEY_LENGTH = Integer.parseInt(readPwmConstantsBundle("databaseAccessor.keyLength"));
 
 
@@ -140,13 +142,6 @@ public abstract class PwmConstants {
     public static final PwmHashAlgorithm SETTING_CHECKSUM_HASH_METHOD = PwmHashAlgorithm.SHA256;
     public static final PwmHashAlgorithm SETTING_CHECKSUM_HASH_METHOD = PwmHashAlgorithm.SHA256;
 
 
 
 
-    public static final String DOWNLOAD_FILENAME_STATISTICS_CSV = "Statistics.csv";
-    public static final String DOWNLOAD_FILENAME_USER_REPORT_SUMMARY_CSV = "UserReportSummary.csv";
-    public static final String DOWNLOAD_FILENAME_USER_REPORT_RECORDS_CSV = "UserReportRecords.csv";
-    public static final String DOWNLOAD_FILENAME_AUDIT_RECORDS_CSV = "AuditRecords.csv";
-    public static final String DOWNLOAD_FILENAME_LDAP_PERMISSION_CSV = "LDAPPermissionRecommendations.csv";
-
-
     public static final String LOG_REMOVED_VALUE_REPLACEMENT = readPwmConstantsBundle("log.removedValue");
     public static final String LOG_REMOVED_VALUE_REPLACEMENT = readPwmConstantsBundle("log.removedValue");
 
 
     public static final Collection<Locale> INCLUDED_LOCALES;
     public static final Collection<Locale> INCLUDED_LOCALES;

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

@@ -105,7 +105,7 @@ public class Configuration implements Serializable, SettingReader {
     public String toDebugString() {
     public String toDebugString() {
         final StringBuilder outputText = new StringBuilder();
         final StringBuilder outputText = new StringBuilder();
         outputText.append("  ");
         outputText.append("  ");
-        outputText.append(JsonUtil.serialize(StoredConfigurationUtil.toJsonDebugObject(storedConfiguration)));
+        outputText.append(JsonUtil.serialize(StoredConfigurationUtil.toJsonDebugObject(storedConfiguration), JsonUtil.Flag.PrettyPrint));
         return outputText.toString().replaceAll("\n","\n  ");
         return outputText.toString().replaceAll("\n","\n  ");
     }
     }
 
 
@@ -409,7 +409,7 @@ public class Configuration implements Serializable, SettingReader {
 
 
         final PwmPasswordPolicy policy = initPasswordPolicy(profile,locale);
         final PwmPasswordPolicy policy = initPasswordPolicy(profile,locale);
         if (!dataCache.cachedPasswordPolicy.containsKey(profile)) {
         if (!dataCache.cachedPasswordPolicy.containsKey(profile)) {
-            dataCache.cachedPasswordPolicy.put(profile,new LinkedHashMap<Locale,PwmPasswordPolicy>());
+            dataCache.cachedPasswordPolicy.put(profile,new LinkedHashMap<>());
         }
         }
         dataCache.cachedPasswordPolicy.get(profile).put(locale,policy);
         dataCache.cachedPasswordPolicy.get(profile).put(locale,policy);
         return policy;
         return policy;

+ 2 - 1
src/main/java/password/pwm/health/CertificateChecker.java

@@ -38,6 +38,7 @@ 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.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
+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;
 
 
@@ -150,7 +151,7 @@ public class CertificateChecker implements HealthChecker {
             errorMsg.append("certificate for subject ");
             errorMsg.append("certificate for subject ");
             errorMsg.append(certificate.getSubjectDN().getName());
             errorMsg.append(certificate.getSubjectDN().getName());
             errorMsg.append(" will expire on: ");
             errorMsg.append(" will expire on: ");
-            errorMsg.append(PwmConstants.DEFAULT_DATETIME_FORMAT.format(expireDate));
+            errorMsg.append(JavaHelper.toIsoDate(expireDate));
             errorMsg.append(" (").append(durationUntilExpire.asCompactString()).append(" from now)");
             errorMsg.append(" (").append(durationUntilExpire.asCompactString()).append(" from now)");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_CERTIFICATE_ERROR, errorMsg.toString(), new String[]{errorMsg.toString()});
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_CERTIFICATE_ERROR, errorMsg.toString(), new String[]{errorMsg.toString()});
             throw new PwmOperationalException(errorInformation);
             throw new PwmOperationalException(errorInformation);

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

@@ -22,8 +22,8 @@
 
 
 package password.pwm.health;
 package password.pwm.health;
 
 
+import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
-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.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -96,10 +96,16 @@ public class HealthMonitor implements PwmService {
     }
     }
 
 
     public Instant getLastHealthCheckTime() {
     public Instant getLastHealthCheckTime() {
+        if (status != STATUS.OPEN) {
+            return null;
+        }
         return lastHealthCheckTime;
         return lastHealthCheckTime;
     }
     }
 
 
     public HealthStatus getMostSevereHealthStatus(final CheckTimeliness timeliness) {
     public HealthStatus getMostSevereHealthStatus(final CheckTimeliness timeliness) {
+        if (status != STATUS.OPEN) {
+            return HealthStatus.GOOD;
+        }
         return getMostSevereHealthStatus(getHealthRecords(timeliness));
         return getMostSevereHealthStatus(getHealthRecords(timeliness));
     }
     }
 
 
@@ -124,6 +130,13 @@ public class HealthMonitor implements PwmService {
         this.pwmApplication = pwmApplication;
         this.pwmApplication = pwmApplication;
         settings = HealthMonitorSettings.fromConfiguration(pwmApplication.getConfig());
         settings = HealthMonitorSettings.fromConfiguration(pwmApplication.getConfig());
 
 
+        if (!Boolean.parseBoolean(pwmApplication.getConfig().readAppProperty(AppProperty.HEALTHCHECK_ENABLED))) {
+            LOGGER.debug("health monitor will remain inactive due to AppProperty " + AppProperty.HEALTHCHECK_ENABLED.getKey());
+            status = STATUS.CLOSED;
+            return;
+        }
+
+
         executorService = Executors.newSingleThreadScheduledExecutor(
         executorService = Executors.newSingleThreadScheduledExecutor(
                 JavaHelper.makePwmThreadFactory(
                 JavaHelper.makePwmThreadFactory(
                         JavaHelper.makeThreadName(pwmApplication, this.getClass()) + "-",
                         JavaHelper.makeThreadName(pwmApplication, this.getClass()) + "-",
@@ -137,6 +150,10 @@ public class HealthMonitor implements PwmService {
     }
     }
 
 
     public Set<HealthRecord> getHealthRecords(final CheckTimeliness timeliness) {
     public Set<HealthRecord> getHealthRecords(final CheckTimeliness timeliness) {
+        if (status != STATUS.OPEN) {
+            return Collections.emptySet();
+        }
+
         lastRequestedUpdateTime = Instant.now();
         lastRequestedUpdateTime = Instant.now();
 
 
         {
         {
@@ -215,7 +232,7 @@ public class HealthMonitor implements PwmService {
 
 
     public ServiceInfo serviceInfo()
     public ServiceInfo serviceInfo()
     {
     {
-        return new ServiceInfo(Collections.<DataStorageMethod>emptyList());
+        return new ServiceInfo(Collections.emptyList());
     }
     }
 
 
     public Map<HealthMonitorFlag, Serializable> getHealthProperties()
     public Map<HealthMonitorFlag, Serializable> getHealthProperties()

+ 2 - 2
src/main/java/password/pwm/http/JspUtility.java

@@ -48,7 +48,7 @@ public abstract class JspUtility {
             final ServletRequest request
             final ServletRequest request
     )
     )
     {
     {
-        final PwmRequest pwmRequest = (PwmRequest)request.getAttribute(PwmRequest.Attribute.PwmRequest.toString());
+        final PwmRequest pwmRequest = (PwmRequest)request.getAttribute(PwmRequestAttribute.PwmRequest.toString());
         if (pwmRequest == null) {
         if (pwmRequest == null) {
             LOGGER.warn("unable to load pwmRequest object during jsp execution");
             LOGGER.warn("unable to load pwmRequest object during jsp execution");
         }
         }
@@ -65,7 +65,7 @@ public abstract class JspUtility {
         return null;
         return null;
     }
     }
 
 
-    public static Serializable getAttribute(final PageContext pageContext, final PwmRequest.Attribute requestAttr) {
+    public static Serializable getAttribute(final PageContext pageContext, final PwmRequestAttribute requestAttr) {
         final PwmRequest pwmRequest = forRequest(pageContext.getRequest());
         final PwmRequest pwmRequest = forRequest(pageContext.getRequest());
         return pwmRequest.getAttribute(requestAttr);
         return pwmRequest.getAttribute(requestAttr);
     }
     }

+ 59 - 122
src/main/java/password/pwm/http/PwmRequest.java

@@ -40,8 +40,8 @@ import password.pwm.config.PwmSetting;
 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.http.servlet.CommandServlet;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.http.servlet.PwmServletDefinition;
+import password.pwm.http.servlet.command.CommandServlet;
 import password.pwm.util.Validator;
 import password.pwm.util.Validator;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -57,6 +57,7 @@ import java.io.InputStream;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
@@ -70,12 +71,17 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(PwmRequest.class);
     private static final PwmLogger LOGGER = PwmLogger.forClass(PwmRequest.class);
 
 
-    private static final Set<String> HTTP_DEBUG_STRIP_VALUES = new HashSet<>(
-            Arrays.asList(new String[] {
+    private static final Set<String> HTTP_PARAM_DEBUG_STRIP_VALUES =
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new String[] {
                     "password",
                     "password",
                     PwmConstants.PARAM_TOKEN,
                     PwmConstants.PARAM_TOKEN,
                     PwmConstants.PARAM_RESPONSE_PREFIX,
                     PwmConstants.PARAM_RESPONSE_PREFIX,
-            }));
+            })));
+
+    private static final Set<String> HTTP_HEADER_DEBUG_STRIP_VALUES =
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new String[] {
+                    HttpHeader.Authorization.getHttpName(),
+            })));
 
 
     private final PwmResponse pwmResponse;
     private final PwmResponse pwmResponse;
     private transient PwmApplication pwmApplication;
     private transient PwmApplication pwmApplication;
@@ -89,12 +95,12 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        PwmRequest pwmRequest = (PwmRequest) request.getAttribute(PwmRequest.Attribute.PwmRequest.toString());
+        PwmRequest pwmRequest = (PwmRequest) request.getAttribute(PwmRequestAttribute.PwmRequest.toString());
         if (pwmRequest == null) {
         if (pwmRequest == null) {
             final PwmSession pwmSession = PwmSessionWrapper.readPwmSession(request);
             final PwmSession pwmSession = PwmSessionWrapper.readPwmSession(request);
             final PwmApplication pwmApplication = ContextManager.getPwmApplication(request);
             final PwmApplication pwmApplication = ContextManager.getPwmApplication(request);
             pwmRequest = new PwmRequest(request, response, pwmApplication, pwmSession);
             pwmRequest = new PwmRequest(request, response, pwmApplication, pwmSession);
-            request.setAttribute(PwmRequest.Attribute.PwmRequest.toString(), pwmRequest);
+            request.setAttribute(PwmRequestAttribute.PwmRequest.toString(), pwmRequest);
         }
         }
         return pwmRequest;
         return pwmRequest;
     }
     }
@@ -183,7 +189,7 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
     public void sendRedirectToContinue()
     public void sendRedirectToContinue()
             throws PwmUnrecoverableException, IOException
             throws PwmUnrecoverableException, IOException
     {
     {
-        String redirectURL = this.getContextPath() + PwmServletDefinition.Command.servletUrl();
+        String redirectURL = this.getContextPath() + PwmServletDefinition.PublicCommand.servletUrl();
         redirectURL = PwmURL.appendAndEncodeUrlParameters(redirectURL, Collections.singletonMap(PwmConstants.PARAM_ACTION_REQUEST, CommandServlet.CommandAction.next.toString()));
         redirectURL = PwmURL.appendAndEncodeUrlParameters(redirectURL, Collections.singletonMap(PwmConstants.PARAM_ACTION_REQUEST, CommandServlet.CommandAction.next.toString()));
         sendRedirect(redirectURL);
         sendRedirect(redirectURL);
     }
     }
@@ -262,71 +268,6 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
         return Collections.unmodifiableMap(returnObj);
         return Collections.unmodifiableMap(returnObj);
     }
     }
 
 
-    public enum Attribute {
-        PwmErrorInfo,
-        SuccessMessage,
-        PwmRequest,
-        OriginalUri,
-        AgreementText,
-        CompleteText,
-        AvailableAuthMethods,
-        ConfigurationSummaryOutput,
-        LdapPermissionItems,
-        PageTitle,
-        ModuleBean,
-        ModuleBean_String,
-        CspNonce,
-
-        FormConfiguration,
-        FormReadOnly,
-        FormShowPasswordFields,
-        FormData,
-
-        SetupResponses_ResponseInfo,
-
-        SetupOtp_QrCodeValue,
-
-        HelpdeskDetail,
-        HelpdeskObfuscatedDN,
-        HelpdeskUsername,
-        HelpdeskVerificationEnabled,
-
-        ConfigFilename,
-        ConfigLastModified,
-        ConfigHasPassword,
-        ConfigPasswordRememberTime,
-        ConfigLoginHistory,
-        ApplicationPath,
-
-        ConfigHasCertificates,
-
-        CaptchaClientUrl,
-        CaptchaIframeUrl,
-        CaptchaPublicKey,
-
-        ChangePassword_MaxWaitSeconds,
-        ChangePassword_CheckIntervalSeconds,
-
-        ForgottenPasswordChallengeSet,
-        ForgottenPasswordOptionalPageView,
-        ForgottenPasswordPrompts,
-        ForgottenPasswordInstructions,
-        ForgottenPasswordUserInfo,
-
-        GuestCurrentExpirationDate,
-        GuestMaximumExpirationDate,
-        GuestMaximumValidDays,
-
-        NewUser_FormShowBackButton,
-
-        CookieBeanStorage,
-
-        ShortcutItems,
-        NextUrl,
-
-        UserDebugData,
-    }
-
     public static class FileUploadItem {
     public static class FileUploadItem {
         private final String name;
         private final String name;
         private final String type;
         private final String type;
@@ -418,11 +359,11 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
         return true;
         return true;
     }
     }
 
 
-    public void setAttribute(final PwmRequest.Attribute name, final Serializable value) {
+    public void setAttribute(final PwmRequestAttribute name, final Serializable value) {
         this.getHttpServletRequest().setAttribute(name.toString(),value);
         this.getHttpServletRequest().setAttribute(name.toString(),value);
     }
     }
 
 
-    public Serializable getAttribute(final PwmRequest.Attribute name) {
+    public Serializable getAttribute(final PwmRequestAttribute name) {
         return (Serializable)this.getHttpServletRequest().getAttribute(name.toString());
         return (Serializable)this.getHttpServletRequest().getAttribute(name.toString());
     }
     }
 
 
@@ -460,29 +401,7 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
             }
             }
             sb.append("\n");
             sb.append("\n");
 
 
-            final Map<String,List<String>> valueMap = this.readMultiParametersAsMap();
-            for (final String paramName : valueMap.keySet()) {
-                for (final String paramValue : valueMap.get(paramName)) {
-                    sb.append("  ").append(paramName).append("=");
-                    boolean strip = false;
-                    for (final String stripValue : HTTP_DEBUG_STRIP_VALUES) {
-                        if (paramName.toLowerCase().contains(stripValue.toLowerCase())) {
-                            strip = true;
-                        }
-                    }
-                    if (strip) {
-                        sb.append(PwmConstants.LOG_REMOVED_VALUE_REPLACEMENT);
-                    } else {
-                        sb.append("'");
-                        sb.append(paramValue);
-                        sb.append("'");
-                    }
-
-                    sb.append("\n");
-                }
-            }
-
-            sb.deleteCharAt(sb.length() - 1);
+            sb.append(debugOutputMapToString(this.readMultiParametersAsMap(), HTTP_PARAM_DEBUG_STRIP_VALUES));
         }
         }
         LOGGER.trace(this.getSessionLabel(), sb.toString());
         LOGGER.trace(this.getSessionLabel(), sb.toString());
     }
     }
@@ -558,13 +477,13 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
 
 
     public synchronized String getCspNonce()
     public synchronized String getCspNonce()
     {
     {
-        if (getAttribute(PwmRequest.Attribute.CspNonce) == null) {
+        if (getAttribute(PwmRequestAttribute.CspNonce) == null) {
             final int nonceLength = Integer.parseInt(getConfig().readAppProperty(AppProperty.HTTP_HEADER_CSP_NONCE_BYTES));
             final int nonceLength = Integer.parseInt(getConfig().readAppProperty(AppProperty.HTTP_HEADER_CSP_NONCE_BYTES));
             final byte[] cspNonce = PwmRandom.getInstance().newBytes(nonceLength);
             final byte[] cspNonce = PwmRandom.getInstance().newBytes(nonceLength);
             final String cspString = StringUtil.base64Encode(cspNonce);
             final String cspString = StringUtil.base64Encode(cspNonce);
-            setAttribute(PwmRequest.Attribute.CspNonce, cspString);
+            setAttribute(PwmRequestAttribute.CspNonce, cspString);
         }
         }
-        return (String)getAttribute(PwmRequest.Attribute.CspNonce);
+        return (String)getAttribute(PwmRequestAttribute.CspNonce);
     }
     }
 
 
     public <T extends Serializable> T readEncryptedCookie(final String cookieName, final Class<T> returnClass)
     public <T extends Serializable> T readEncryptedCookie(final String cookieName, final Class<T> returnClass)
@@ -606,10 +525,10 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
                 ? new LinkedHashMap<FormConfiguration,String>()
                 ? new LinkedHashMap<FormConfiguration,String>()
                 : new LinkedHashMap<>(formDataMap);
                 : new LinkedHashMap<>(formDataMap);
 
 
-        this.setAttribute(PwmRequest.Attribute.FormConfiguration, new ArrayList<>(formConfiguration));
-        this.setAttribute(PwmRequest.Attribute.FormData, formDataMapValue);
-        this.setAttribute(PwmRequest.Attribute.FormReadOnly, readOnly);
-        this.setAttribute(PwmRequest.Attribute.FormShowPasswordFields, showPasswordFields);
+        this.setAttribute(PwmRequestAttribute.FormConfiguration, new ArrayList<>(formConfiguration));
+        this.setAttribute(PwmRequestAttribute.FormData, formDataMapValue);
+        this.setAttribute(PwmRequestAttribute.FormReadOnly, readOnly);
+        this.setAttribute(PwmRequestAttribute.FormShowPasswordFields, showPasswordFields);
     }
     }
 
 
     public void invalidateSession() {
     public void invalidateSession() {
@@ -636,24 +555,7 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
         sb.append("http").append(getHttpServletRequest().isSecure() ? "s " : " non-").append("secure request headers: ");
         sb.append("http").append(getHttpServletRequest().isSecure() ? "s " : " non-").append("secure request headers: ");
         sb.append(LINE_SEPERATOR);
         sb.append(LINE_SEPERATOR);
 
 
-        final Map<String,List<String>> headerMap = readHeaderValuesMap();
-        for (final String headerName : headerMap.keySet()) {
-            for (final String value : headerMap.get(headerName)) {
-                sb.append("  ");
-                sb.append(headerName);
-                sb.append("=");
-                if (headerName.contains("Authorization")) {
-                    sb.append(PwmConstants.LOG_REMOVED_VALUE_REPLACEMENT);
-                } else {
-                    sb.append(value);
-                }
-                sb.append(LINE_SEPERATOR);
-            }
-        }
-
-        if (LINE_SEPERATOR.equals(sb.substring(sb.length() - LINE_SEPERATOR.length(), sb.length()))) {
-            sb.delete(sb.length() - LINE_SEPERATOR.length(), sb.length());
-        }
+        sb.append(debugOutputMapToString(readHeaderValuesMap(), HTTP_HEADER_DEBUG_STRIP_VALUES));
 
 
         return sb.toString();
         return sb.toString();
     }
     }
@@ -671,4 +573,39 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
         }
         }
         return false;
         return false;
     }
     }
+
+    private static String debugOutputMapToString(
+            final Map<String,List<String>> input,
+            final Collection<String> stripValues
+
+    ) {
+        final String LINE_SEPARATOR = "\n";
+        final StringBuilder sb = new StringBuilder();
+        for (final String paramName : input.keySet()) {
+            for (final String paramValue : input.get(paramName)) {
+                sb.append("  ").append(paramName).append("=");
+                boolean strip = false;
+                for (final String stripValue : stripValues) {
+                    if (paramName.toLowerCase().contains(stripValue.toLowerCase())) {
+                        strip = true;
+                    }
+                }
+                if (strip) {
+                    sb.append(PwmConstants.LOG_REMOVED_VALUE_REPLACEMENT);
+                } else {
+                    sb.append("'");
+                    sb.append(paramValue);
+                    sb.append("'");
+                }
+
+                sb.append(LINE_SEPARATOR);
+            }
+        }
+
+        if (LINE_SEPARATOR.equals(sb.substring(sb.length() - LINE_SEPARATOR.length(), sb.length()))) {
+            sb.delete(sb.length() - LINE_SEPARATOR.length(), sb.length());
+        }
+
+        return sb.toString();
+    }
 }
 }

+ 89 - 0
src/main/java/password/pwm/http/PwmRequestAttribute.java

@@ -0,0 +1,89 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2017 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;
+
+public enum PwmRequestAttribute {
+    PwmErrorInfo,
+    SuccessMessage,
+    PwmRequest,
+    OriginalUri,
+    AgreementText,
+    CompleteText,
+    AvailableAuthMethods,
+    ConfigurationSummaryOutput,
+    LdapPermissionItems,
+    PageTitle,
+    ModuleBean,
+    ModuleBean_String,
+    CspNonce,
+
+    FormConfiguration,
+    FormReadOnly,
+    FormShowPasswordFields,
+    FormData,
+
+    SetupResponses_ResponseInfo,
+
+    SetupOtp_QrCodeValue,
+
+    HelpdeskDetail,
+    HelpdeskObfuscatedDN,
+    HelpdeskUsername,
+    HelpdeskVerificationEnabled,
+
+    ConfigFilename,
+    ConfigLastModified,
+    ConfigHasPassword,
+    ConfigPasswordRememberTime,
+    ConfigLoginHistory,
+    ApplicationPath,
+
+    ConfigHasCertificates,
+
+    CaptchaClientUrl,
+    CaptchaIframeUrl,
+    CaptchaPublicKey,
+
+    ChangePassword_MaxWaitSeconds,
+    ChangePassword_CheckIntervalSeconds,
+    ChangePassword_PasswordPolicyChangeMessage,
+
+    ForgottenPasswordChallengeSet,
+    ForgottenPasswordOptionalPageView,
+    ForgottenPasswordPrompts,
+    ForgottenPasswordInstructions,
+    ForgottenPasswordUserInfo,
+
+    GuestCurrentExpirationDate,
+    GuestMaximumExpirationDate,
+    GuestMaximumValidDays,
+
+    NewUser_FormShowBackButton,
+
+    CookieBeanStorage,
+
+    ShortcutItems,
+    NextUrl,
+
+    UserDebugData,
+}

+ 3 - 3
src/main/java/password/pwm/http/PwmResponse.java

@@ -116,7 +116,7 @@ public class PwmResponse extends PwmHttpResponseWrapper {
     {
     {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
-        this.pwmRequest.setAttribute(PwmRequest.Attribute.SuccessMessage, message);
+        this.pwmRequest.setAttribute(PwmRequestAttribute.SuccessMessage, message);
 
 
         final boolean showMessage = !pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.DISPLAY_SUCCESS_PAGES)
         final boolean showMessage = !pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.DISPLAY_SUCCESS_PAGES)
                 && !Arrays.asList(flags).contains(Flag.AlwaysShowMessage);
                 && !Arrays.asList(flags).contains(Flag.AlwaysShowMessage);
@@ -124,7 +124,7 @@ public class PwmResponse extends PwmHttpResponseWrapper {
         if (showMessage) {
         if (showMessage) {
             LOGGER.trace(pwmSession, "skipping success page due to configuration setting.");
             LOGGER.trace(pwmSession, "skipping success page due to configuration setting.");
             final String redirectUrl = pwmRequest.getContextPath()
             final String redirectUrl = pwmRequest.getContextPath()
-                    +  PwmServletDefinition.Command.servletUrl()
+                    +  PwmServletDefinition.PublicCommand.servletUrl()
                     + "?processAction=next";
                     + "?processAction=next";
             sendRedirect(redirectUrl);
             sendRedirect(redirectUrl);
             return;
             return;
@@ -145,7 +145,7 @@ public class PwmResponse extends PwmHttpResponseWrapper {
     {
     {
         LOGGER.error(pwmRequest.getSessionLabel(), errorInformation);
         LOGGER.error(pwmRequest.getSessionLabel(), errorInformation);
 
 
-        pwmRequest.setAttribute(PwmRequest.Attribute.PwmErrorInfo, errorInformation);
+        pwmRequest.setAttribute(PwmRequestAttribute.PwmErrorInfo, errorInformation);
 
 
         if (JavaHelper.enumArrayContainsValue(flags, Flag.ForceLogout)) {
         if (JavaHelper.enumArrayContainsValue(flags, Flag.ForceLogout)) {
             LOGGER.debug(pwmRequest, "forcing logout due to error " + errorInformation.toDebugStr());
             LOGGER.debug(pwmRequest, "forcing logout due to error " + errorInformation.toDebugStr());

+ 3 - 1
src/main/java/password/pwm/http/PwmURL.java

@@ -149,7 +149,9 @@ public class PwmURL {
     }
     }
 
 
     public boolean isCommandServletURL() {
     public boolean isCommandServletURL() {
-        return isPwmServletURL(PwmServletDefinition.Command);
+        return isPwmServletURL(PwmServletDefinition.PublicCommand)
+                || isPwmServletURL(PwmServletDefinition.PrivateCommand);
+
     }
     }
 
 
     public boolean isWebServiceURL() {
     public boolean isWebServiceURL() {

+ 10 - 0
src/main/java/password/pwm/http/bean/AdminBean.java

@@ -23,6 +23,10 @@
 package password.pwm.http.bean;
 package password.pwm.http.bean;
 
 
 
 
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.Setter;
+import password.pwm.bean.UserIdentity;
 import password.pwm.config.option.SessionBeanMode;
 import password.pwm.config.option.SessionBeanMode;
 
 
 import java.util.Arrays;
 import java.util.Arrays;
@@ -31,6 +35,12 @@ import java.util.HashSet;
 import java.util.Set;
 import java.util.Set;
 
 
 public class AdminBean extends PwmSessionBean {
 public class AdminBean extends PwmSessionBean {
+
+    @Getter
+    @Setter
+    @SerializedName("l")
+    private UserIdentity lastUserDebug;
+
     @Override
     @Override
     public Type getType()
     public Type getType()
     {
     {

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

@@ -31,6 +31,7 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.ContextManager;
 import password.pwm.http.ContextManager;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -51,7 +52,7 @@ public class ApplicationModeFilter extends AbstractPwmFilter {
             throws IOException, ServletException
             throws IOException, ServletException
     {
     {
         // add request url to request attribute
         // add request url to request attribute
-        pwmRequest.setAttribute(PwmRequest.Attribute.OriginalUri, pwmRequest.getHttpServletRequest().getRequestURI());
+        pwmRequest.setAttribute(PwmRequestAttribute.OriginalUri, pwmRequest.getHttpServletRequest().getRequestURI());
 
 
         // ignore if resource request
         // ignore if resource request
         final PwmURL pwmURL = pwmRequest.getURL();
         final PwmURL pwmURL = pwmRequest.getURL();

+ 4 - 3
src/main/java/password/pwm/http/filter/ConfigAccessFilter.java

@@ -43,6 +43,7 @@ import password.pwm.http.HttpHeader;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.http.bean.ConfigManagerBean;
 import password.pwm.http.bean.ConfigManagerBean;
@@ -256,8 +257,8 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
 
 
         final ConfigLoginHistory configLoginHistory = readConfigLoginHistory(pwmRequest);
         final ConfigLoginHistory configLoginHistory = readConfigLoginHistory(pwmRequest);
 
 
-        pwmRequest.setAttribute(PwmRequest.Attribute.ConfigLoginHistory, configLoginHistory);
-        pwmRequest.setAttribute(PwmRequest.Attribute.ConfigPasswordRememberTime,time);
+        pwmRequest.setAttribute(PwmRequestAttribute.ConfigLoginHistory, configLoginHistory);
+        pwmRequest.setAttribute(PwmRequestAttribute.ConfigPasswordRememberTime,time);
         pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_LOGIN);
         pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_LOGIN);
 
 
     }
     }
@@ -397,7 +398,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
     private static ProcessStatus denyAndError(final PwmRequest pwmRequest, final ErrorInformation errorInformation)
     private static ProcessStatus denyAndError(final PwmRequest pwmRequest, final ErrorInformation errorInformation)
             throws ServletException, PwmUnrecoverableException, IOException
             throws ServletException, PwmUnrecoverableException, IOException
     {
     {
-        pwmRequest.setAttribute(PwmRequest.Attribute.PwmErrorInfo, errorInformation);
+        pwmRequest.setAttribute(PwmRequestAttribute.PwmErrorInfo, errorInformation);
         forwardToJsp(pwmRequest);
         forwardToJsp(pwmRequest);
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }

+ 4 - 3
src/main/java/password/pwm/http/filter/RequestInitializationFilter.java

@@ -38,6 +38,7 @@ import password.pwm.http.HttpHeader;
 import password.pwm.http.IdleTimeoutCalculator;
 import password.pwm.http.IdleTimeoutCalculator;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmResponse;
 import password.pwm.http.PwmResponse;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSessionWrapper;
 import password.pwm.http.PwmSessionWrapper;
@@ -109,7 +110,7 @@ public class RequestInitializationFilter implements Filter {
                     final ContextManager contextManager = ContextManager.getContextManager(req.getServletContext());
                     final ContextManager contextManager = ContextManager.getContextManager(req.getServletContext());
                     if (contextManager != null) {
                     if (contextManager != null) {
                         final ErrorInformation startupError = contextManager.getStartupErrorInformation();
                         final ErrorInformation startupError = contextManager.getStartupErrorInformation();
-                        servletRequest.setAttribute(PwmRequest.Attribute.PwmErrorInfo.toString(), startupError);
+                        servletRequest.setAttribute(PwmRequestAttribute.PwmErrorInfo.toString(), startupError);
                     }
                     }
                 } catch (Exception e) {
                 } catch (Exception e) {
                     if (pwmURL.isResourceURL()) {
                     if (pwmURL.isResourceURL()) {
@@ -153,7 +154,7 @@ public class RequestInitializationFilter implements Filter {
                 } catch (Throwable e2) {
                 } catch (Throwable e2) {
                     e2.getMessage();
                     e2.getMessage();
                 }
                 }
-                req.setAttribute(PwmRequest.Attribute.PwmErrorInfo.toString(),errorInformation);
+                req.setAttribute(PwmRequestAttribute.PwmErrorInfo.toString(),errorInformation);
                 final String url = JspUrl.APP_UNAVAILABLE.getPath();
                 final String url = JspUrl.APP_UNAVAILABLE.getPath();
                 req.getServletContext().getRequestDispatcher(url).forward(req, resp);
                 req.getServletContext().getRequestDispatcher(url).forward(req, resp);
             }
             }
@@ -199,7 +200,7 @@ public class RequestInitializationFilter implements Filter {
                 } catch (Throwable e2) {
                 } catch (Throwable e2) {
                     e2.getMessage();
                     e2.getMessage();
                 }
                 }
-                req.setAttribute(PwmRequest.Attribute.PwmErrorInfo.toString(),errorInformation);
+                req.setAttribute(PwmRequestAttribute.PwmErrorInfo.toString(),errorInformation);
                 final String url = JspUrl.APP_UNAVAILABLE.getPath();
                 final String url = JspUrl.APP_UNAVAILABLE.getPath();
                 req.getServletContext().getRequestDispatcher(url).forward(req, resp);
                 req.getServletContext().getRequestDispatcher(url).forward(req, resp);
             }
             }

+ 3 - 2
src/main/java/password/pwm/http/servlet/AbstractPwmServlet.java

@@ -32,6 +32,7 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.ContextManager;
 import password.pwm.http.ContextManager;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSessionWrapper;
 import password.pwm.http.PwmSessionWrapper;
 import password.pwm.http.bean.PwmSessionBean;
 import password.pwm.http.bean.PwmSessionBean;
@@ -284,7 +285,7 @@ public abstract class AbstractPwmServlet extends HttpServlet implements PwmServl
         if (beanClass != null) {
         if (beanClass != null) {
             final PwmSessionBean pwmSessionBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, beanClass);
             final PwmSessionBean pwmSessionBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, beanClass);
             pwmSessionBean.setLastError(errorInformation);
             pwmSessionBean.setLastError(errorInformation);
-            pwmRequest.setAttribute(PwmRequest.Attribute.PwmErrorInfo, errorInformation);
+            pwmRequest.setAttribute(PwmRequestAttribute.PwmErrorInfo, errorInformation);
         }
         }
     }
     }
 
 
@@ -292,7 +293,7 @@ public abstract class AbstractPwmServlet extends HttpServlet implements PwmServl
         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);
         final PwmSessionBean pwmSessionBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, beanClass);
         if (pwmSessionBean != null && pwmSessionBean.getLastError() != null) {
         if (pwmSessionBean != null && pwmSessionBean.getLastError() != null) {
-            pwmRequest.setAttribute(PwmRequest.Attribute.PwmErrorInfo, pwmSessionBean.getLastError());
+            pwmRequest.setAttribute(PwmRequestAttribute.PwmErrorInfo, pwmSessionBean.getLastError());
             pwmSessionBean.setLastError(null);
             pwmSessionBean.setLastError(null);
         }
         }
     }
     }

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

@@ -33,6 +33,7 @@ import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
@@ -70,7 +71,7 @@ public class AccountInformationServlet extends AbstractPwmServlet {
                         pwmRequest.getPwmSession().getSessionManager().getUserDataReader(pwmRequest.getPwmApplication()),
                         pwmRequest.getPwmSession().getSessionManager().getUserDataReader(pwmRequest.getPwmApplication()),
                         FormUtility.Flag.ReturnEmptyValues
                         FormUtility.Flag.ReturnEmptyValues
                 );
                 );
-                pwmRequest.setAttribute(PwmRequest.Attribute.FormData, new LinkedHashMap<>(ldapValues));
+                pwmRequest.setAttribute(PwmRequestAttribute.FormData, new LinkedHashMap<>(ldapValues));
             }
             }
         } catch (PwmException e) {
         } catch (PwmException e) {
             LOGGER.error(pwmRequest, "error reading user form data: " + e.getMessage());
             LOGGER.error(pwmRequest, "error reading user form data: " + e.getMessage());

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

@@ -42,6 +42,7 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.bean.DeleteAccountBean;
 import password.pwm.http.bean.DeleteAccountBean;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecord;
@@ -138,7 +139,7 @@ public class DeleteAccountServlet extends ControlledPwmServlet {
             if (!bean.isAgreementPassed()) {
             if (!bean.isAgreementPassed()) {
                 final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
                 final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
                 final String expandedText = macroMachine.expandMacros(selfDeleteAgreementText);
                 final String expandedText = macroMachine.expandMacros(selfDeleteAgreementText);
-                pwmRequest.setAttribute(PwmRequest.Attribute.AgreementText, expandedText);
+                pwmRequest.setAttribute(PwmRequestAttribute.AgreementText, expandedText);
                 pwmRequest.forwardToJsp(JspUrl.SELF_DELETE_AGREE);
                 pwmRequest.forwardToJsp(JspUrl.SELF_DELETE_AGREE);
                 return;
                 return;
             }
             }

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

@@ -42,6 +42,7 @@ 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.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.ldap.LdapUserDataReader;
 import password.pwm.ldap.LdapUserDataReader;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.SearchConfiguration;
@@ -368,7 +369,7 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet {
         final String completeMessage = pwmRequest.getConfig().readSettingAsLocalizedString(PwmSetting.FORGOTTEN_USERNAME_MESSAGE,locale);
         final String completeMessage = pwmRequest.getConfig().readSettingAsLocalizedString(PwmSetting.FORGOTTEN_USERNAME_MESSAGE,locale);
         final MacroMachine macroMachine = MacroMachine.forUser(pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getSessionLabel(), userIdentity);
         final MacroMachine macroMachine = MacroMachine.forUser(pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getSessionLabel(), userIdentity);
         final String expandedText = macroMachine.expandMacros(completeMessage);
         final String expandedText = macroMachine.expandMacros(completeMessage);
-        pwmRequest.setAttribute(PwmRequest.Attribute.CompleteText, expandedText);
+        pwmRequest.setAttribute(PwmRequestAttribute.CompleteText, expandedText);
         pwmRequest.forwardToJsp(JspUrl.FORGOTTEN_USERNAME_COMPLETE);
         pwmRequest.forwardToJsp(JspUrl.FORGOTTEN_USERNAME_COMPLETE);
     }
     }
 
 

+ 4 - 3
src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java

@@ -47,6 +47,7 @@ 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.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.GuestRegistrationBean;
 import password.pwm.http.bean.GuestRegistrationBean;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
@@ -625,7 +626,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
         final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
         final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
 
 
         final long maxValidDays = pwmRequest.getConfig().readSettingAsLong(PwmSetting.GUEST_MAX_VALID_DAYS);
         final long maxValidDays = pwmRequest.getConfig().readSettingAsLong(PwmSetting.GUEST_MAX_VALID_DAYS);
-        pwmRequest.setAttribute(PwmRequest.Attribute.GuestMaximumValidDays, String.valueOf(maxValidDays));
+        pwmRequest.setAttribute(PwmRequestAttribute.GuestMaximumValidDays, String.valueOf(maxValidDays));
 
 
 
 
         final String maxExpirationDate;
         final String maxExpirationDate;
@@ -655,8 +656,8 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
             }
             }
         }
         }
 
 
-        pwmRequest.setAttribute(PwmRequest.Attribute.GuestCurrentExpirationDate, currentExpirationDate);
-        pwmRequest.setAttribute(PwmRequest.Attribute.GuestMaximumExpirationDate, maxExpirationDate);
+        pwmRequest.setAttribute(PwmRequestAttribute.GuestCurrentExpirationDate, currentExpirationDate);
+        pwmRequest.setAttribute(PwmRequestAttribute.GuestMaximumExpirationDate, maxExpirationDate);
     }
     }
 }
 }
 
 

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

@@ -31,6 +31,7 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -81,14 +82,14 @@ public class LogoutServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action = "showLogout")
     @ActionHandler(action = "showLogout")
-    public ProcessStatus processLogoutAction(
+    public ProcessStatus processShowLogout(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
             throws ServletException, PwmUnrecoverableException, IOException
             throws ServletException, PwmUnrecoverableException, IOException
     {
     {
         final Optional<String> nextUrl = readAndValidateNextUrlParameter(pwmRequest);
         final Optional<String> nextUrl = readAndValidateNextUrlParameter(pwmRequest);
         if (nextUrl.isPresent()) {
         if (nextUrl.isPresent()) {
-            pwmRequest.setAttribute(PwmRequest.Attribute.NextUrl, nextUrl.get());
+            pwmRequest.setAttribute(PwmRequestAttribute.NextUrl, nextUrl.get());
         }
         }
         pwmRequest.forwardToJsp(JspUrl.LOGOUT);
         pwmRequest.forwardToJsp(JspUrl.LOGOUT);
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
@@ -96,7 +97,7 @@ public class LogoutServlet extends ControlledPwmServlet {
 
 
 
 
     @ActionHandler(action = "showTimeout")
     @ActionHandler(action = "showTimeout")
-    public ProcessStatus processTimeoutAction(
+    public ProcessStatus processShowTimeout(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
             throws ServletException, PwmUnrecoverableException, IOException
             throws ServletException, PwmUnrecoverableException, IOException

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

@@ -42,6 +42,8 @@ import password.pwm.http.bean.UpdateProfileBean;
 import password.pwm.http.servlet.admin.AdminServlet;
 import password.pwm.http.servlet.admin.AdminServlet;
 import password.pwm.http.servlet.changepw.PrivateChangePasswordServlet;
 import password.pwm.http.servlet.changepw.PrivateChangePasswordServlet;
 import password.pwm.http.servlet.changepw.PublicChangePasswordServlet;
 import password.pwm.http.servlet.changepw.PublicChangePasswordServlet;
+import password.pwm.http.servlet.command.PrivateCommandServlet;
+import password.pwm.http.servlet.command.PublicCommandServlet;
 import password.pwm.http.servlet.configeditor.ConfigEditorServlet;
 import password.pwm.http.servlet.configeditor.ConfigEditorServlet;
 import password.pwm.http.servlet.configguide.ConfigGuideServlet;
 import password.pwm.http.servlet.configguide.ConfigGuideServlet;
 import password.pwm.http.servlet.configmanager.ConfigManagerCertificatesServlet;
 import password.pwm.http.servlet.configmanager.ConfigManagerCertificatesServlet;
@@ -60,7 +62,7 @@ public enum PwmServletDefinition {
     Login(password.pwm.http.servlet.LoginServlet.class, LoginServletBean.class),
     Login(password.pwm.http.servlet.LoginServlet.class, LoginServletBean.class),
     Logout(password.pwm.http.servlet.LogoutServlet.class, null),
     Logout(password.pwm.http.servlet.LogoutServlet.class, null),
     OAuthConsumer(OAuthConsumerServlet.class, null),
     OAuthConsumer(OAuthConsumerServlet.class, null),
-    Command(password.pwm.http.servlet.CommandServlet.class, null),
+    PublicCommand(PublicCommandServlet.class, null),
     PublicPeopleSearch(PublicPeopleSearchServlet.class, null),
     PublicPeopleSearch(PublicPeopleSearchServlet.class, null),
     PublicChangePassword(PublicChangePasswordServlet.class, ChangePasswordBean.class),
     PublicChangePassword(PublicChangePasswordServlet.class, ChangePasswordBean.class),
     //Resource(password.pwm.http.servlet.ResourceFileServlet.class),
     //Resource(password.pwm.http.servlet.ResourceFileServlet.class),
@@ -72,6 +74,7 @@ public enum PwmServletDefinition {
     SetupOtp(password.pwm.http.servlet.SetupOtpServlet.class, SetupOtpBean.class),
     SetupOtp(password.pwm.http.servlet.SetupOtpServlet.class, SetupOtpBean.class),
     Helpdesk(password.pwm.http.servlet.helpdesk.HelpdeskServlet.class, null),
     Helpdesk(password.pwm.http.servlet.helpdesk.HelpdeskServlet.class, null),
     Shortcuts(password.pwm.http.servlet.ShortcutServlet.class, ShortcutsBean.class),
     Shortcuts(password.pwm.http.servlet.ShortcutServlet.class, ShortcutsBean.class),
+    PrivateCommand(PrivateCommandServlet.class, null),
     PrivatePeopleSearch(PrivatePeopleSearchServlet.class, null),
     PrivatePeopleSearch(PrivatePeopleSearchServlet.class, null),
     GuestRegistration(password.pwm.http.servlet.GuestRegistrationServlet.class, null),
     GuestRegistration(password.pwm.http.servlet.GuestRegistrationServlet.class, null),
     SelfDelete(DeleteAccountServlet.class, DeleteAccountBean.class),
     SelfDelete(DeleteAccountServlet.class, DeleteAccountBean.class),

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

@@ -42,6 +42,7 @@ 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.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.SetupOtpBean;
 import password.pwm.http.bean.SetupOtpBean;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationType;
@@ -253,7 +254,7 @@ public class SetupOtpServlet extends AbstractPwmServlet {
             }
             }
         } else {
         } else {
             final String qrCodeValue = makeQrCodeDataImageUrl(pwmRequest, otpBean.getOtpUserRecord());
             final String qrCodeValue = makeQrCodeDataImageUrl(pwmRequest, otpBean.getOtpUserRecord());
-            pwmRequest.setAttribute(PwmRequest.Attribute.SetupOtp_QrCodeValue, qrCodeValue);
+            pwmRequest.setAttribute(PwmRequestAttribute.SetupOtp_QrCodeValue, qrCodeValue);
             pwmRequest.forwardToJsp(JspUrl.SETUP_OTP_SECRET);
             pwmRequest.forwardToJsp(JspUrl.SETUP_OTP_SECRET);
         }
         }
     }
     }

+ 5 - 4
src/main/java/password/pwm/http/servlet/SetupResponsesServlet.java

@@ -47,6 +47,7 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.SetupResponsesBean;
 import password.pwm.http.bean.SetupResponsesBean;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
@@ -182,7 +183,7 @@ public class SetupResponsesServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action = "clearExisting")
     @ActionHandler(action = "clearExisting")
-    private ProcessStatus handleClearResponses(
+    private ProcessStatus handleClearExisting(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException
@@ -276,9 +277,9 @@ public class SetupResponsesServlet extends ControlledPwmServlet {
 
 
         initializeBean(pwmRequest, setupResponsesBean);
         initializeBean(pwmRequest, setupResponsesBean);
 
 
-        pwmRequest.setAttribute(PwmRequest.Attribute.ModuleBean, setupResponsesBean);
-        pwmRequest.setAttribute(PwmRequest.Attribute.ModuleBean_String, pwmRequest.getPwmApplication().getSecureService().encryptObjectToString(setupResponsesBean));
-        pwmRequest.setAttribute(PwmRequest.Attribute.SetupResponses_ResponseInfo, pwmRequest.getPwmSession().getUserInfoBean().getResponseInfoBean());
+        pwmRequest.setAttribute(PwmRequestAttribute.ModuleBean, setupResponsesBean);
+        pwmRequest.setAttribute(PwmRequestAttribute.ModuleBean_String, pwmRequest.getPwmApplication().getSecureService().encryptObjectToString(setupResponsesBean));
+        pwmRequest.setAttribute(PwmRequestAttribute.SetupResponses_ResponseInfo, pwmRequest.getPwmSession().getUserInfoBean().getResponseInfoBean());
 
 
         if (setupResponsesBean.isHasExistingResponses() && !pwmRequest.getPwmSession().getUserInfoBean().isRequiresResponseConfig()) {
         if (setupResponsesBean.isHasExistingResponses() && !pwmRequest.getPwmSession().getUserInfoBean().isRequiresResponseConfig()) {
             pwmRequest.forwardToJsp(JspUrl.SETUP_RESPONSES_EXISTING);
             pwmRequest.forwardToJsp(JspUrl.SETUP_RESPONSES_EXISTING);

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

@@ -33,6 +33,7 @@ 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.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.ShortcutsBean;
 import password.pwm.http.bean.ShortcutsBean;
 import password.pwm.ldap.LdapPermissionTester;
 import password.pwm.ldap.LdapPermissionTester;
@@ -121,7 +122,7 @@ public class ShortcutServlet extends AbstractPwmServlet {
             throws ServletException, PwmUnrecoverableException, IOException {
             throws ServletException, PwmUnrecoverableException, IOException {
         final ArrayList<ShortcutItem> shortcutItems = new ArrayList<>();
         final ArrayList<ShortcutItem> shortcutItems = new ArrayList<>();
         shortcutItems.addAll(shortcutsBean.getVisibleItems().values());
         shortcutItems.addAll(shortcutsBean.getVisibleItems().values());
-        pwmRequest.setAttribute(PwmRequest.Attribute.ShortcutItems, shortcutItems);
+        pwmRequest.setAttribute(PwmRequestAttribute.ShortcutItems, shortcutItems);
         pwmRequest.forwardToJsp(JspUrl.SHORTCUT);
         pwmRequest.forwardToJsp(JspUrl.SHORTCUT);
     }
     }
 
 

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

@@ -49,6 +49,7 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.UpdateProfileBean;
 import password.pwm.http.bean.UpdateProfileBean;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
@@ -269,7 +270,7 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action = "updateProfile")
     @ActionHandler(action = "updateProfile")
-    ProcessStatus handleUpdateRequest(
+    ProcessStatus handleUpdateProfileRequest(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
@@ -307,7 +308,7 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
             if (!updateProfileBean.isAgreementPassed()) {
             if (!updateProfileBean.isAgreementPassed()) {
                 final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
                 final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
                 final String expandedText = macroMachine.expandMacros(updateProfileAgreementText);
                 final String expandedText = macroMachine.expandMacros(updateProfileAgreementText);
-                pwmRequest.setAttribute(PwmRequest.Attribute.AgreementText, expandedText);
+                pwmRequest.setAttribute(PwmRequestAttribute.AgreementText, expandedText);
                 pwmRequest.forwardToJsp(JspUrl.UPDATE_ATTRIBUTES_AGREEMENT);
                 pwmRequest.forwardToJsp(JspUrl.UPDATE_ATTRIBUTES_AGREEMENT);
                 return;
                 return;
             }
             }

+ 49 - 7
src/main/java/password/pwm/http/servlet/admin/AdminServlet.java

@@ -23,6 +23,7 @@
 package password.pwm.http.servlet.admin;
 package password.pwm.http.servlet.admin;
 
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
+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.PwmConstants;
@@ -35,7 +36,9 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
+import password.pwm.http.bean.AdminBean;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.http.servlet.PwmServletDefinition;
@@ -45,6 +48,7 @@ import password.pwm.svc.report.ReportCsvUtility;
 import password.pwm.svc.report.ReportService;
 import password.pwm.svc.report.ReportService;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -82,6 +86,7 @@ public class AdminServlet extends ControlledPwmServlet {
         reportCommand(HttpMethod.POST),
         reportCommand(HttpMethod.POST),
         reportStatus(HttpMethod.GET),
         reportStatus(HttpMethod.GET),
         reportSummary(HttpMethod.GET),
         reportSummary(HttpMethod.GET),
+        downloadUserDebug(HttpMethod.GET),
 
 
         ;
         ;
 
 
@@ -128,7 +133,7 @@ public class AdminServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action =  "viewLogWindow")
     @ActionHandler(action =  "viewLogWindow")
-    private ProcessStatus processViewLog(
+    private ProcessStatus processViewLogWindow(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
             throws PwmUnrecoverableException, IOException, ServletException
             throws PwmUnrecoverableException, IOException, ServletException
@@ -145,7 +150,10 @@ public class AdminServlet extends ControlledPwmServlet {
     {
     {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
 
-        pwmRequest.getPwmResponse().markAsDownload(PwmConstants.ContentTypeValue.csv, PwmConstants.DOWNLOAD_FILENAME_AUDIT_RECORDS_CSV);
+        pwmRequest.getPwmResponse().markAsDownload(
+                PwmConstants.ContentTypeValue.csv,
+                pwmApplication.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_AUDIT_RECORDS_CSV)
+        );
 
 
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         try {
         try {
@@ -167,7 +175,10 @@ public class AdminServlet extends ControlledPwmServlet {
     {
     {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
 
-        pwmRequest.getPwmResponse().markAsDownload(PwmConstants.ContentTypeValue.csv, PwmConstants.DOWNLOAD_FILENAME_USER_REPORT_RECORDS_CSV);
+        pwmRequest.getPwmResponse().markAsDownload(
+                PwmConstants.ContentTypeValue.csv,
+                pwmApplication.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_USER_REPORT_RECORDS_CSV)
+        );
 
 
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         try {
         try {
@@ -193,7 +204,10 @@ public class AdminServlet extends ControlledPwmServlet {
     {
     {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
 
-        pwmRequest.getPwmResponse().markAsDownload(PwmConstants.ContentTypeValue.csv, PwmConstants.DOWNLOAD_FILENAME_USER_REPORT_SUMMARY_CSV);
+        pwmRequest.getPwmResponse().markAsDownload(
+                PwmConstants.ContentTypeValue.csv,
+                pwmApplication.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_USER_REPORT_SUMMARY_CSV)
+        );
 
 
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         try {
         try {
@@ -214,7 +228,10 @@ public class AdminServlet extends ControlledPwmServlet {
     {
     {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
 
-        pwmRequest.getPwmResponse().markAsDownload(PwmConstants.ContentTypeValue.csv, PwmConstants.DOWNLOAD_FILENAME_STATISTICS_CSV);
+        pwmRequest.getPwmResponse().markAsDownload(
+                PwmConstants.ContentTypeValue.csv,
+                pwmRequest.getPwmApplication().getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_STATISTICS_CSV)
+        );
 
 
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         try {
         try {
@@ -298,6 +315,29 @@ public class AdminServlet extends ControlledPwmServlet {
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }
 
 
+    @ActionHandler(action = "downloadUserDebug")
+    private ProcessStatus processDownloadUserDebug(final PwmRequest pwmRequest)
+
+            throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
+    {
+        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
+        final AdminBean adminBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, AdminBean.class);
+        final UserIdentity userIdentity = adminBean.getLastUserDebug();
+        if (userIdentity != null) {
+            pwmRequest.getPwmResponse().markAsDownload(
+                    PwmConstants.ContentTypeValue.json,
+                    pwmApplication.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_USER_DEBUG_JSON)
+            );
+            final UserDebugDataBean userDebugData = UserDebugDataReader.readUserDebugData(pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getSessionLabel(), userIdentity);
+            final String output = JsonUtil.serialize(userDebugData, JsonUtil.Flag.PrettyPrint);
+            pwmRequest.getPwmResponse().getOutputStream().write(output.getBytes(PwmConstants.DEFAULT_CHARSET));
+        } else {
+            pwmRequest.respondWithError(new ErrorInformation(PwmError.ERROR_UNKNOWN, "no previously searched user available for download"));
+        }
+
+        return ProcessStatus.Halt;
+    }
+
     private void processDebugUserSearch(final PwmRequest pwmRequest)
     private void processDebugUserSearch(final PwmRequest pwmRequest)
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
@@ -310,6 +350,8 @@ public class AdminServlet extends ControlledPwmServlet {
         final UserIdentity userIdentity;
         final UserIdentity userIdentity;
         try {
         try {
             userIdentity = userSearchEngine.resolveUsername(username,null,null, pwmRequest.getSessionLabel());
             userIdentity = userSearchEngine.resolveUsername(username,null,null, pwmRequest.getSessionLabel());
+            final AdminBean adminBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, AdminBean.class);
+            adminBean.setLastUserDebug(userIdentity);
         } catch (ChaiUnavailableException e) {
         } catch (ChaiUnavailableException e) {
             setLastError(pwmRequest, PwmUnrecoverableException.fromChaiException(e).getErrorInformation());
             setLastError(pwmRequest, PwmUnrecoverableException.fromChaiException(e).getErrorInformation());
             return;
             return;
@@ -319,7 +361,7 @@ public class AdminServlet extends ControlledPwmServlet {
         }
         }
 
 
         final UserDebugDataBean userDebugData = UserDebugDataReader.readUserDebugData(pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getSessionLabel(), userIdentity);
         final UserDebugDataBean userDebugData = UserDebugDataReader.readUserDebugData(pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getSessionLabel(), userIdentity);
-        pwmRequest.setAttribute(PwmRequest.Attribute.UserDebugData, userDebugData);
+        pwmRequest.setAttribute(PwmRequestAttribute.UserDebugData, userDebugData);
     }
     }
 
 
     private void forwardToJsp(final PwmRequest pwmRequest) throws ServletException, PwmUnrecoverableException, IOException {
     private void forwardToJsp(final PwmRequest pwmRequest) throws ServletException, PwmUnrecoverableException, IOException {
@@ -342,7 +384,7 @@ public class AdminServlet extends ControlledPwmServlet {
         tokenLookup(JspUrl.ADMIN_TOKEN_LOOKUP,"/tokens"),
         tokenLookup(JspUrl.ADMIN_TOKEN_LOOKUP,"/tokens"),
         viewLog(JspUrl.ADMIN_LOGVIEW,"/logs"),
         viewLog(JspUrl.ADMIN_LOGVIEW,"/logs"),
         urlReference(JspUrl.ADMIN_URLREFERENCE,"/urls"),
         urlReference(JspUrl.ADMIN_URLREFERENCE,"/urls"),
-        debugUser(JspUrl.ADMIN_DEBUG,"/debug"),
+        debugUser(JspUrl.ADMIN_DEBUG,"/userdebug"),
 
 
         ;
         ;
 
 

+ 1 - 0
src/main/java/password/pwm/http/servlet/admin/UserDebugDataBean.java

@@ -36,6 +36,7 @@ import java.util.Map;
 @Builder
 @Builder
 public class UserDebugDataBean implements Serializable {
 public class UserDebugDataBean implements Serializable {
     private final UserInfoBean userInfoBean;
     private final UserInfoBean userInfoBean;
+    private final boolean passwordReadable;
     private final Map<Permission,String> permissions;
     private final Map<Permission,String> permissions;
     private final PwmPasswordPolicy ldapPasswordPolicy;
     private final PwmPasswordPolicy ldapPasswordPolicy;
     private final PwmPasswordPolicy configuredPasswordPolicy;
     private final PwmPasswordPolicy configuredPasswordPolicy;

+ 18 - 3
src/main/java/password/pwm/http/servlet/admin/UserDebugDataReader.java

@@ -22,6 +22,7 @@
 
 
 package password.pwm.http.servlet.admin;
 package password.pwm.http.servlet.admin;
 
 
+import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.Permission;
 import password.pwm.Permission;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SessionLabel;
@@ -33,6 +34,7 @@ import password.pwm.config.profile.ProfileType;
 import password.pwm.config.profile.ProfileUtility;
 import password.pwm.config.profile.ProfileUtility;
 import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.ldap.LdapOperationsHelper;
 import password.pwm.ldap.LdapPermissionTester;
 import password.pwm.ldap.LdapPermissionTester;
 import password.pwm.ldap.UserStatusReader;
 import password.pwm.ldap.UserStatusReader;
 import password.pwm.util.operations.PasswordUtility;
 import password.pwm.util.operations.PasswordUtility;
@@ -43,8 +45,8 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.TreeMap;
 
 
-class UserDebugDataReader {
-    static UserDebugDataBean readUserDebugData(
+public class UserDebugDataReader {
+    public static UserDebugDataBean readUserDebugData(
             final PwmApplication pwmApplication,
             final PwmApplication pwmApplication,
             final Locale locale,
             final Locale locale,
             final SessionLabel sessionLabel,
             final SessionLabel sessionLabel,
@@ -62,7 +64,19 @@ class UserDebugDataReader {
 
 
         final PwmPasswordPolicy ldapPasswordPolicy = PasswordUtility.readLdapPasswordPolicy(pwmApplication, pwmApplication.getProxiedChaiUser(userIdentity));
         final PwmPasswordPolicy ldapPasswordPolicy = PasswordUtility.readLdapPasswordPolicy(pwmApplication, pwmApplication.getProxiedChaiUser(userIdentity));
 
 
-        final PwmPasswordPolicy configPasswordPolicy = pwmApplication.getConfig().getPasswordPolicy(userIdentity.getLdapProfileID(), locale);
+        final PwmPasswordPolicy configPasswordPolicy = PasswordUtility.determineConfiguredPolicyProfileForUser(
+                pwmApplication,
+                sessionLabel,
+                userIdentity,
+                locale
+        );
+
+        boolean readablePassword = false;
+        try {
+            readablePassword = null != LdapOperationsHelper.readLdapPassword(pwmApplication, sessionLabel, userIdentity);
+        } catch (ChaiUnavailableException e) {
+            /* disregard */
+        }
 
 
         final UserDebugDataBean userDebugData = UserDebugDataBean.builder()
         final UserDebugDataBean userDebugData = UserDebugDataBean.builder()
                 .userInfoBean(userInfoBean)
                 .userInfoBean(userInfoBean)
@@ -70,6 +84,7 @@ class UserDebugDataReader {
                 .profiles(profiles)
                 .profiles(profiles)
                 .ldapPasswordPolicy(ldapPasswordPolicy)
                 .ldapPasswordPolicy(ldapPasswordPolicy)
                 .configuredPasswordPolicy(configPasswordPolicy)
                 .configuredPasswordPolicy(configPasswordPolicy)
+                .passwordReadable(readablePassword)
                 .build();
                 .build();
 
 
         return userDebugData;
         return userDebugData;

+ 74 - 14
src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java

@@ -49,6 +49,7 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.ChangePasswordBean;
 import password.pwm.http.bean.ChangePasswordBean;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
@@ -61,12 +62,14 @@ import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PwmPasswordRuleValidator;
 import password.pwm.util.PwmPasswordRuleValidator;
+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;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.operations.PasswordUtility;
 import password.pwm.util.operations.PasswordUtility;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
+import password.pwm.ws.server.rest.RestCheckPasswordServer;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import java.io.IOException;
 import java.io.IOException;
@@ -95,6 +98,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         agree(HttpMethod.POST),
         agree(HttpMethod.POST),
         warnResponse(HttpMethod.POST),
         warnResponse(HttpMethod.POST),
         reset(HttpMethod.POST),
         reset(HttpMethod.POST),
+        checkPassword(HttpMethod.POST),
 
 
         ;
         ;
 
 
@@ -112,6 +116,11 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
+    enum WarnResponseValue {
+        skip,
+        change,
+    }
+
     @Override
     @Override
     public Class<? extends ProcessAction> getProcessActionsClass() {
     public Class<? extends ProcessAction> getProcessActionsClass() {
         return ChangePasswordServlet.ChangePasswordAction.class;
         return ChangePasswordServlet.ChangePasswordAction.class;
@@ -133,16 +142,26 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action = "warnResponse")
     @ActionHandler(action = "warnResponse")
-    public ProcessStatus processWarnAction(final PwmRequest pwmRequest) throws ServletException, PwmUnrecoverableException, IOException {
+    public ProcessStatus processWarnResponse(final PwmRequest pwmRequest) throws ServletException, PwmUnrecoverableException, IOException {
         final ChangePasswordBean changePasswordBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, ChangePasswordBean.class);
         final ChangePasswordBean changePasswordBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, ChangePasswordBean.class);
 
 
         if (pwmRequest.getPwmSession().getUserInfoBean().getPasswordState().isWarnPeriod()) {
         if (pwmRequest.getPwmSession().getUserInfoBean().getPasswordState().isWarnPeriod()) {
-            final String warnResponse = pwmRequest.readParameterAsString("warnResponse");
-            if ("skip".equalsIgnoreCase(warnResponse)) {
-                pwmRequest.getPwmSession().getLoginInfoBean().setFlag(LoginInfoBean.LoginFlag.skipNewPw);
-                pwmRequest.sendRedirectToContinue();
-            } else if ("change".equalsIgnoreCase(warnResponse)) {
-                changePasswordBean.setWarnPassed(true);
+            final String warnResponseStr = pwmRequest.readParameterAsString("warnResponse");
+            final WarnResponseValue warnResponse = JavaHelper.readEnumFromString(WarnResponseValue.class, null, warnResponseStr);
+            if (warnResponse != null) {
+                switch (warnResponse) {
+                    case skip:
+                        pwmRequest.getPwmSession().getLoginInfoBean().setFlag(LoginInfoBean.LoginFlag.skipNewPw);
+                        pwmRequest.sendRedirectToContinue();
+                        return ProcessStatus.Halt;
+
+                    case change:
+                        changePasswordBean.setWarnPassed(true);
+                        break;
+
+                    default:
+                        JavaHelper.unhandledSwitchStatement(warnResponse);
+                }
             }
             }
         }
         }
 
 
@@ -179,7 +198,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         if (PasswordUtility.PasswordCheckInfo.MatchStatus.MATCH != PasswordUtility.figureMatchStatus(caseSensitive,
         if (PasswordUtility.PasswordCheckInfo.MatchStatus.MATCH != PasswordUtility.figureMatchStatus(caseSensitive,
                 password1, password2)) {
                 password1, password2)) {
             setLastError(pwmRequest, PwmError.PASSWORD_DOESNOTMATCH.toInfo());
             setLastError(pwmRequest, PwmError.PASSWORD_DOESNOTMATCH.toInfo());
-            pwmRequest.forwardToJsp(JspUrl.PASSWORD_CHANGE);
+            forwardToChangePage(pwmRequest);
             return ProcessStatus.Continue;
             return ProcessStatus.Continue;
         }
         }
 
 
@@ -330,7 +349,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
             if (completeMessage != null && !completeMessage.isEmpty()) {
             if (completeMessage != null && !completeMessage.isEmpty()) {
                 final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
                 final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
                 final String expandedText = macroMachine.expandMacros(completeMessage);
                 final String expandedText = macroMachine.expandMacros(completeMessage);
-                pwmRequest.setAttribute(PwmRequest.Attribute.CompleteText, expandedText);
+                pwmRequest.setAttribute(PwmRequestAttribute.CompleteText, expandedText);
                 pwmRequest.forwardToJsp(JspUrl.PASSWORD_COMPLETE);
                 pwmRequest.forwardToJsp(JspUrl.PASSWORD_COMPLETE);
             } else {
             } else {
                 pwmRequest.getPwmResponse().forwardToSuccessPage(Message.Success_PasswordChange);
                 pwmRequest.getPwmResponse().forwardToSuccessPage(Message.Success_PasswordChange);
@@ -341,6 +360,33 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }
 
 
+    @ActionHandler(action = "checkPassword")
+    private ProcessStatus processCheckPasswordAction(final PwmRequest pwmRequest) throws IOException, PwmUnrecoverableException, ChaiUnavailableException
+    {
+        final RestCheckPasswordServer.JsonInput jsonInput = JsonUtil.deserialize(
+                pwmRequest.readRequestBodyAsString(),
+                RestCheckPasswordServer.JsonInput.class
+        );
+
+        final UserInfoBean userInfoBean = pwmRequest.getPwmSession().getUserInfoBean();
+        final RestCheckPasswordServer.PasswordCheckRequest passwordCheckRequest = new RestCheckPasswordServer.PasswordCheckRequest(
+                userInfoBean.getUserIdentity(),
+                PasswordData.forStringValue(jsonInput.getPassword1()),
+                PasswordData.forStringValue(jsonInput.getPassword2()),
+                userInfoBean
+        );
+
+        final RestCheckPasswordServer.JsonData checkResult = RestCheckPasswordServer.doPasswordRuleCheck(
+                pwmRequest.getPwmApplication(),
+                pwmRequest.getPwmSession(),
+                passwordCheckRequest);
+
+        final RestResultBean restResultBean = new RestResultBean(checkResult);
+        pwmRequest.outputJsonResult(restResultBean);
+
+        return ProcessStatus.Halt;
+    }
+
     private void executeChangePassword(
     private void executeChangePassword(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final PasswordData newPassword
             final PasswordData newPassword
@@ -401,7 +447,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         if (agreementMsg != null && agreementMsg.length() > 0 && !changePasswordBean.isAgreementPassed()) {
         if (agreementMsg != null && agreementMsg.length() > 0 && !changePasswordBean.isAgreementPassed()) {
             final MacroMachine macroMachine = pwmSession.getSessionManager().getMacroMachine(pwmApplication);
             final MacroMachine macroMachine = pwmSession.getSessionManager().getMacroMachine(pwmApplication);
             final String expandedText = macroMachine.expandMacros(agreementMsg);
             final String expandedText = macroMachine.expandMacros(agreementMsg);
-            pwmRequest.setAttribute(PwmRequest.Attribute.AgreementText,expandedText);
+            pwmRequest.setAttribute(PwmRequestAttribute.AgreementText,expandedText);
             pwmRequest.forwardToJsp(JspUrl.PASSWORD_AGREEMENT);
             pwmRequest.forwardToJsp(JspUrl.PASSWORD_AGREEMENT);
             return;
             return;
         }
         }
@@ -417,7 +463,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         }
         }
 
 
         changePasswordBean.setAllChecksPassed(true);
         changePasswordBean.setAllChecksPassed(true);
-        pwmRequest.forwardToJsp(JspUrl.PASSWORD_CHANGE);
+        forwardToChangePage(pwmRequest);
     }
     }
 
 
     private static boolean determineIfCurrentPasswordRequired(
     private static boolean determineIfCurrentPasswordRequired(
@@ -566,7 +612,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         return true;
         return true;
     }
     }
 
 
-    protected void forwardToFormPage(final PwmRequest pwmRequest)
+    private void forwardToFormPage(final PwmRequest pwmRequest)
             throws ServletException, PwmUnrecoverableException, IOException
             throws ServletException, PwmUnrecoverableException, IOException
     {
     {
         pwmRequest.addFormInfoToRequestAttr(PwmSetting.PASSWORD_REQUIRE_FORM,false,false);
         pwmRequest.addFormInfoToRequestAttr(PwmSetting.PASSWORD_REQUIRE_FORM,false,false);
@@ -579,12 +625,12 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         final ChangePasswordBean changePasswordBean = pwmApplication.getSessionStateService().getBean(pwmRequest, ChangePasswordBean.class);
         final ChangePasswordBean changePasswordBean = pwmApplication.getSessionStateService().getBean(pwmRequest, ChangePasswordBean.class);
         final Instant maxCompleteTime = changePasswordBean.getChangePasswordMaxCompletion();
         final Instant maxCompleteTime = changePasswordBean.getChangePasswordMaxCompletion();
         pwmRequest.setAttribute(
         pwmRequest.setAttribute(
-                PwmRequest.Attribute.ChangePassword_MaxWaitSeconds,
+                PwmRequestAttribute.ChangePassword_MaxWaitSeconds,
                 maxCompleteTime == null ? 30 : TimeDuration.fromCurrent(maxCompleteTime).getTotalSeconds()
                 maxCompleteTime == null ? 30 : TimeDuration.fromCurrent(maxCompleteTime).getTotalSeconds()
         );
         );
 
 
         pwmRequest.setAttribute(
         pwmRequest.setAttribute(
-                PwmRequest.Attribute.ChangePassword_CheckIntervalSeconds,
+                PwmRequestAttribute.ChangePassword_CheckIntervalSeconds,
                 Long.parseLong(pwmRequest.getConfig().readAppProperty(AppProperty.CLIENT_AJAX_PW_WAIT_CHECK_SECONDS))
                 Long.parseLong(pwmRequest.getConfig().readAppProperty(AppProperty.CLIENT_AJAX_PW_WAIT_CHECK_SECONDS))
         );
         );
 
 
@@ -626,4 +672,18 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
 
 
         return ProcessStatus.Continue;
         return ProcessStatus.Continue;
     }
     }
+
+    private void forwardToChangePage(final PwmRequest pwmRequest) throws ServletException, PwmUnrecoverableException, IOException
+    {
+        final String passwordPolicyChangeMessage = pwmRequest.getPwmSession().getUserInfoBean().getPasswordPolicy().getRuleHelper().getChangeMessage();
+        if (passwordPolicyChangeMessage.length() > 1) {
+            final MacroMachine macroMachine = pwmRequest.getPwmSession().getSessionManager().getMacroMachine(pwmRequest.getPwmApplication());
+            macroMachine.expandMacros(passwordPolicyChangeMessage);
+            pwmRequest.setAttribute(PwmRequestAttribute.ChangePassword_PasswordPolicyChangeMessage, passwordPolicyChangeMessage);
+        }
+
+        pwmRequest.forwardToJsp(JspUrl.PASSWORD_CHANGE);
+    }
 }
 }
+
+

+ 5 - 26
src/main/java/password/pwm/http/servlet/CommandServlet.java → src/main/java/password/pwm/http/servlet/command/CommandServlet.java

@@ -20,7 +20,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
 
 
-package password.pwm.http.servlet;
+package password.pwm.http.servlet.command;
 
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
@@ -37,40 +37,19 @@ import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.filter.AuthenticationFilter;
 import password.pwm.http.filter.AuthenticationFilter;
+import password.pwm.http.servlet.ControlledPwmServlet;
+import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
-import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
 import java.io.IOException;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Map;
 
 
-/**
- * Processes a variety of different commands sent in an HTTP Request, including logoff.
- *
- * @author Jason D. Rivard
- */
-
-@WebServlet(
-        name="CommandServlet",
-        urlPatterns = {
-                PwmConstants.URL_PREFIX_PUBLIC + "/command",
-                PwmConstants.URL_PREFIX_PRIVATE + "/command",
-                PwmConstants.URL_PREFIX_PUBLIC + "/command/*",
-                PwmConstants.URL_PREFIX_PRIVATE + "/command/*",
-                PwmConstants.URL_PREFIX_PUBLIC + "/CommandServlet",
-                PwmConstants.URL_PREFIX_PRIVATE + "/CommandServlet",
-                PwmConstants.URL_PREFIX_PUBLIC + "/CommandServlet/*",
-                PwmConstants.URL_PREFIX_PRIVATE + "/CommandServlet/*",
-        }
-)
-
-
-public class CommandServlet extends ControlledPwmServlet {
-// ------------------------------ FIELDS ------------------------------
+public abstract class CommandServlet extends ControlledPwmServlet {
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(CommandServlet.class);
     private static final PwmLogger LOGGER = PwmLogger.forClass(CommandServlet.class);
 
 
@@ -140,7 +119,7 @@ public class CommandServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action = "next")
     @ActionHandler(action = "next")
-    private ProcessStatus processContinue(
+    private ProcessStatus processNext(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
             throws IOException, PwmUnrecoverableException, ServletException
             throws IOException, PwmUnrecoverableException, ServletException

+ 39 - 0
src/main/java/password/pwm/http/servlet/command/PrivateCommandServlet.java

@@ -0,0 +1,39 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2017 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.command;
+
+import password.pwm.PwmConstants;
+
+import javax.servlet.annotation.WebServlet;
+
+@WebServlet(
+        name="PrivateCommandServlet",
+        urlPatterns = {
+                PwmConstants.URL_PREFIX_PRIVATE + "/command",
+                PwmConstants.URL_PREFIX_PRIVATE + "/command/*",
+                PwmConstants.URL_PREFIX_PRIVATE + "/CommandServlet",
+                PwmConstants.URL_PREFIX_PRIVATE + "/CommandServlet/*",
+        }
+)
+public class PrivateCommandServlet extends CommandServlet {
+}

+ 39 - 0
src/main/java/password/pwm/http/servlet/command/PublicCommandServlet.java

@@ -0,0 +1,39 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2017 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.command;
+
+import password.pwm.PwmConstants;
+
+import javax.servlet.annotation.WebServlet;
+
+@WebServlet(
+        name="PublicCommandServlet",
+        urlPatterns = {
+                PwmConstants.URL_PREFIX_PUBLIC + "/command",
+                PwmConstants.URL_PREFIX_PUBLIC + "/command/*",
+                PwmConstants.URL_PREFIX_PUBLIC + "/CommandServlet",
+                PwmConstants.URL_PREFIX_PUBLIC + "/CommandServlet/*",
+        }
+)
+public class PublicCommandServlet extends CommandServlet {
+}

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

@@ -58,6 +58,7 @@ import password.pwm.http.ContextManager;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.http.bean.ConfigGuideBean;
 import password.pwm.http.bean.ConfigGuideBean;
@@ -587,7 +588,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
 
 
         if (configGuideBean.getStep() == GuideStep.LDAP_PERMISSIONS) {
         if (configGuideBean.getStep() == GuideStep.LDAP_PERMISSIONS) {
             final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(ConfigGuideForm.generateStoredConfig(configGuideBean));
             final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(ConfigGuideForm.generateStoredConfig(configGuideBean));
-            pwmRequest.setAttribute(PwmRequest.Attribute.LdapPermissionItems,ldapPermissionCalculator);
+            pwmRequest.setAttribute(PwmRequestAttribute.LdapPermissionItems,ldapPermissionCalculator);
         }
         }
 
 
         final HttpServletRequest req = pwmRequest.getHttpServletRequest();
         final HttpServletRequest req = pwmRequest.getHttpServletRequest();

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

@@ -36,6 +36,7 @@ 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.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.util.secure.X509Utils;
 import password.pwm.util.secure.X509Utils;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -98,7 +99,7 @@ public class ConfigManagerCertificatesServlet extends AbstractPwmServlet {
             pwmRequest.outputJsonResult(restResultBean);
             pwmRequest.outputJsonResult(restResultBean);
             return;
             return;
         }
         }
-        pwmRequest.setAttribute(PwmRequest.Attribute.ConfigHasCertificates, !certificateDebugDataItems.isEmpty());
+        pwmRequest.setAttribute(PwmRequestAttribute.ConfigHasCertificates, !certificateDebugDataItems.isEmpty());
         pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_CERTIFICATES);
         pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_CERTIFICATES);
     }
     }
 
 

+ 13 - 8
src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerServlet.java

@@ -25,6 +25,7 @@ package password.pwm.http.servlet.configmanager;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import org.apache.commons.csv.CSVPrinter;
 import org.apache.commons.csv.CSVPrinter;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.IOUtils;
+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.PwmConstants;
@@ -40,6 +41,7 @@ import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmResponse;
 import password.pwm.http.PwmResponse;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.ConfigManagerBean;
 import password.pwm.http.bean.ConfigManagerBean;
@@ -169,17 +171,17 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         final ConfigurationReader configurationReader = pwmRequest.getContextManager().getConfigReader();
         final ConfigurationReader configurationReader = pwmRequest.getContextManager().getConfigReader();
-        pwmRequest.setAttribute(PwmRequest.Attribute.PageTitle,LocaleHelper.getLocalizedMessage(Config.Title_ConfigManager, pwmRequest));
-        pwmRequest.setAttribute(PwmRequest.Attribute.ApplicationPath, pwmRequest.getPwmApplication().getPwmEnvironment().getApplicationPath().getAbsolutePath());
-        pwmRequest.setAttribute(PwmRequest.Attribute.ConfigFilename, configurationReader.getConfigFile().getAbsolutePath());
+        pwmRequest.setAttribute(PwmRequestAttribute.PageTitle,LocaleHelper.getLocalizedMessage(Config.Title_ConfigManager, pwmRequest));
+        pwmRequest.setAttribute(PwmRequestAttribute.ApplicationPath, pwmRequest.getPwmApplication().getPwmEnvironment().getApplicationPath().getAbsolutePath());
+        pwmRequest.setAttribute(PwmRequestAttribute.ConfigFilename, configurationReader.getConfigFile().getAbsolutePath());
         {
         {
             final Date lastModifyTime = configurationReader.getStoredConfiguration().modifyTime();
             final Date lastModifyTime = configurationReader.getStoredConfiguration().modifyTime();
             final String output = lastModifyTime == null
             final String output = lastModifyTime == null
                     ? LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable,pwmRequest)
                     ? LocaleHelper.getLocalizedMessage(Display.Value_NotApplicable,pwmRequest)
                     : JavaHelper.toIsoDate(lastModifyTime);
                     : JavaHelper.toIsoDate(lastModifyTime);
-            pwmRequest.setAttribute(PwmRequest.Attribute.ConfigLastModified, output);
+            pwmRequest.setAttribute(PwmRequestAttribute.ConfigLastModified, output);
         }
         }
-        pwmRequest.setAttribute(PwmRequest.Attribute.ConfigHasPassword, LocaleHelper.booleanString(configurationReader.getStoredConfiguration().hasPassword(), pwmRequest.getLocale(), pwmRequest.getConfig()));
+        pwmRequest.setAttribute(PwmRequestAttribute.ConfigHasPassword, LocaleHelper.booleanString(configurationReader.getStoredConfiguration().hasPassword(), pwmRequest.getLocale(), pwmRequest.getConfig()));
     }
     }
 
 
 
 
@@ -362,7 +364,7 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
     {
     {
         final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest);
         final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest);
         final LinkedHashMap<String,Object> outputMap = new LinkedHashMap<>(storedConfiguration.toOutputMap(pwmRequest.getLocale()));
         final LinkedHashMap<String,Object> outputMap = new LinkedHashMap<>(storedConfiguration.toOutputMap(pwmRequest.getLocale()));
-        pwmRequest.setAttribute(PwmRequest.Attribute.ConfigurationSummaryOutput,outputMap);
+        pwmRequest.setAttribute(PwmRequestAttribute.ConfigurationSummaryOutput,outputMap);
         pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_EDITOR_SUMMARY);
         pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_EDITOR_SUMMARY);
     }
     }
 
 
@@ -371,7 +373,7 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
     {
     {
         final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest);
         final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest);
         final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(storedConfiguration);
         final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(storedConfiguration);
-        pwmRequest.setAttribute(PwmRequest.Attribute.LdapPermissionItems,ldapPermissionCalculator);
+        pwmRequest.setAttribute(PwmRequestAttribute.LdapPermissionItems,ldapPermissionCalculator);
         pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_PERMISSIONS);
         pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_PERMISSIONS);
     }
     }
 
 
@@ -381,7 +383,10 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
     )
     )
             throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
             throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
     {
     {
-        pwmRequest.getPwmResponse().markAsDownload(PwmConstants.ContentTypeValue.csv, PwmConstants.DOWNLOAD_FILENAME_LDAP_PERMISSION_CSV);
+        pwmRequest.getPwmResponse().markAsDownload(
+                PwmConstants.ContentTypeValue.csv,
+                pwmRequest.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_LDAP_PERMISSION_CSV)
+        );
 
 
         final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(pwmRequest.getPwmResponse().getOutputStream());
         final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter(pwmRequest.getPwmResponse().getOutputStream());
         try {
         try {

+ 37 - 1
src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java

@@ -27,6 +27,7 @@ import password.pwm.AppProperty;
 import password.pwm.PwmAboutProperty;
 import password.pwm.PwmAboutProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.bean.UserIdentity;
 import password.pwm.bean.pub.SessionStateInfoBean;
 import password.pwm.bean.pub.SessionStateInfoBean;
 import password.pwm.config.Configuration;
 import password.pwm.config.Configuration;
 import password.pwm.config.stored.StoredConfigurationImpl;
 import password.pwm.config.stored.StoredConfigurationImpl;
@@ -34,6 +35,8 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthMonitor;
 import password.pwm.health.HealthMonitor;
 import password.pwm.health.HealthRecord;
 import password.pwm.health.HealthRecord;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.servlet.admin.UserDebugDataBean;
+import password.pwm.http.servlet.admin.UserDebugDataReader;
 import password.pwm.ldap.LdapDebugDataGenerator;
 import password.pwm.ldap.LdapDebugDataGenerator;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.util.LDAPPermissionCalculator;
 import password.pwm.util.LDAPPermissionCalculator;
@@ -94,7 +97,8 @@ public class DebugItemGenerator {
             LdapDebugItemGenerator.class,
             LdapDebugItemGenerator.class,
             LDAPPermissionItemGenerator.class,
             LDAPPermissionItemGenerator.class,
             LocalDBDebugGenerator.class,
             LocalDBDebugGenerator.class,
-            SessionDataGenerator.class
+            SessionDataGenerator.class,
+            LdapRecentUserDebugGenerator.class
     ));
     ));
 
 
     static void outputZipDebugFile(
     static void outputZipDebugFile(
@@ -575,6 +579,38 @@ public class DebugItemGenerator {
         }
         }
     }
     }
 
 
+    static class LdapRecentUserDebugGenerator implements Generator {
+        @Override
+        public String getFilename() {
+            return "recentUserDebugData.json";
+        }
+
+        @Override
+        public void outputItem(
+                final PwmApplication pwmApplication,
+                final PwmRequest pwmRequest,
+                final OutputStream outputStream
+        )
+                throws Exception
+        {
+            final List<UserIdentity> recentUsers = pwmApplication.getSessionTrackService().getRecentLogins();
+            final List<UserDebugDataBean> recentDebugBeans = new ArrayList<>();
+
+            for (final UserIdentity userIdentity : recentUsers) {
+                final UserDebugDataBean dataBean = UserDebugDataReader.readUserDebugData(
+                        pwmApplication,
+                        pwmRequest.getLocale(),
+                        pwmRequest.getSessionLabel(),
+                        userIdentity
+                );
+                recentDebugBeans.add(dataBean);
+            }
+
+            outputStream.write(JsonUtil.serializeCollection(recentDebugBeans, JsonUtil.Flag.PrettyPrint).getBytes(PwmConstants.DEFAULT_CHARSET));
+        }
+    }
+
+
     interface Generator {
     interface Generator {
 
 
         String getFilename();
         String getFilename();

+ 46 - 417
src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java

@@ -29,12 +29,10 @@ import com.novell.ldapchai.cr.ResponseSet;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiValidationException;
 import com.novell.ldapchai.exception.ChaiValidationException;
-import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.VerificationMethodSystem;
 import password.pwm.VerificationMethodSystem;
-import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.PasswordStatus;
 import password.pwm.bean.PasswordStatus;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
@@ -61,9 +59,9 @@ import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 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.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.ForgottenPasswordBean;
 import password.pwm.http.bean.ForgottenPasswordBean;
-import password.pwm.http.filter.AuthenticationFilter;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.http.servlet.PwmServletDefinition;
@@ -73,20 +71,18 @@ import password.pwm.http.servlet.oauth.OAuthSettings;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
 import password.pwm.ldap.LdapOperationsHelper;
 import password.pwm.ldap.LdapOperationsHelper;
 import password.pwm.ldap.LdapUserDataReader;
 import password.pwm.ldap.LdapUserDataReader;
-import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.UserDataReader;
 import password.pwm.ldap.UserDataReader;
-import password.pwm.ldap.search.UserSearchEngine;
-import password.pwm.ldap.UserStatusReader;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationUtility;
 import password.pwm.ldap.auth.AuthenticationUtility;
 import password.pwm.ldap.auth.PwmAuthenticationSource;
 import password.pwm.ldap.auth.PwmAuthenticationSource;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.auth.SessionAuthenticator;
+import password.pwm.ldap.search.SearchConfiguration;
+import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 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.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenPayload;
-import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenType;
 import password.pwm.svc.token.TokenType;
 import password.pwm.util.CaptchaUtility;
 import password.pwm.util.CaptchaUtility;
 import password.pwm.util.LocaleHelper;
 import password.pwm.util.LocaleHelper;
@@ -96,18 +92,15 @@ import password.pwm.util.RandomPasswordGenerator;
 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;
-import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.operations.ActionExecutor;
 import password.pwm.util.operations.ActionExecutor;
 import password.pwm.util.operations.PasswordUtility;
 import password.pwm.util.operations.PasswordUtility;
 import password.pwm.util.operations.cr.NMASCrOperator;
 import password.pwm.util.operations.cr.NMASCrOperator;
 import password.pwm.util.operations.otp.OTPUserRecord;
 import password.pwm.util.operations.otp.OTPUserRecord;
-import password.pwm.ws.client.rest.RestTokenDataClient;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
 import java.io.IOException;
-import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collection;
@@ -191,8 +184,8 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
 
         final Configuration config = pwmApplication.getConfig();
         final Configuration config = pwmApplication.getConfig();
-        final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
 
 
+        final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
         if (!config.readSettingAsBoolean(PwmSetting.FORGOTTEN_PASSWORD_ENABLE)) {
         if (!config.readSettingAsBoolean(PwmSetting.FORGOTTEN_PASSWORD_ENABLE)) {
             pwmRequest.respondWithError(PwmError.ERROR_SERVICE_NOT_AVAILABLE.toInfo());
             pwmRequest.respondWithError(PwmError.ERROR_SERVICE_NOT_AVAILABLE.toInfo());
             return ProcessStatus.Halt;
             return ProcessStatus.Halt;
@@ -306,9 +299,9 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
         final String requestedChoiceStr = pwmRequest.readParameterAsString("choice");
         final String requestedChoiceStr = pwmRequest.readParameterAsString("choice");
         final LinkedHashSet<IdentityVerificationMethod> remainingAvailableOptionalMethods = new LinkedHashSet<>(
         final LinkedHashSet<IdentityVerificationMethod> remainingAvailableOptionalMethods = new LinkedHashSet<>(
-                figureRemainingAvailableOptionalAuthMethods(pwmRequest, forgottenPasswordBean)
+                ForgottenPasswordUtil.figureRemainingAvailableOptionalAuthMethods(pwmRequest, forgottenPasswordBean)
         );
         );
-        pwmRequest.setAttribute(PwmRequest.Attribute.AvailableAuthMethods, remainingAvailableOptionalMethods);
+        pwmRequest.setAttribute(PwmRequestAttribute.AvailableAuthMethods, remainingAvailableOptionalMethods);
 
 
         IdentityVerificationMethod requestedChoice = null;
         IdentityVerificationMethod requestedChoice = null;
         if (requestedChoiceStr != null && !requestedChoiceStr.isEmpty()) {
         if (requestedChoiceStr != null && !requestedChoiceStr.isEmpty()) {
@@ -325,7 +318,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
 
 
         if (remainingAvailableOptionalMethods.contains(requestedChoice)) {
         if (remainingAvailableOptionalMethods.contains(requestedChoice)) {
             forgottenPasswordBean.getProgress().setInProgressVerificationMethod(requestedChoice);
             forgottenPasswordBean.getProgress().setInProgressVerificationMethod(requestedChoice);
-            pwmRequest.setAttribute(PwmRequest.Attribute.ForgottenPasswordOptionalPageView,"true");
+            pwmRequest.setAttribute(PwmRequestAttribute.ForgottenPasswordOptionalPageView,"true");
             forwardUserBasedOnRecoveryMethod(pwmRequest, requestedChoice);
             forwardUserBasedOnRecoveryMethod(pwmRequest, requestedChoice);
             return ProcessStatus.Continue;
             return ProcessStatus.Continue;
         } else if (requestedChoice != null) {
         } else if (requestedChoice != null) {
@@ -428,7 +421,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action = "enterCode")
     @ActionHandler(action = "enterCode")
-    private ProcessStatus processEnterToken(final PwmRequest pwmRequest)
+    private ProcessStatus processEnterCode(final PwmRequest pwmRequest)
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
     {
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
@@ -471,7 +464,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action = "enterRemoteResponse")
     @ActionHandler(action = "enterRemoteResponse")
-    private ProcessStatus processEnterRemote(final PwmRequest pwmRequest)
+    private ProcessStatus processEnterRemoteResponse(final PwmRequest pwmRequest)
             throws PwmUnrecoverableException, IOException, ServletException
             throws PwmUnrecoverableException, IOException, ServletException
     {
     {
         final String PREFIX = "remote-";
         final String PREFIX = "remote-";
@@ -520,7 +513,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         final String userEnteredCode = pwmRequest.readParameterAsString(PwmConstants.PARAM_TOKEN);
         final String userEnteredCode = pwmRequest.readParameterAsString(PwmConstants.PARAM_TOKEN);
         LOGGER.debug(pwmRequest, String.format("entered OTP: %s", userEnteredCode));
         LOGGER.debug(pwmRequest, String.format("entered OTP: %s", userEnteredCode));
 
 
-        final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
+        final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
         final OTPUserRecord otpUserRecord = userInfoBean.getOtpUserRecord();
         final OTPUserRecord otpUserRecord = userInfoBean.getOtpUserRecord();
 
 
         final boolean otpPassed;
         final boolean otpPassed;
@@ -622,7 +615,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         }
         }
         final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
         final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
 
 
-        final ResponseSet responseSet = readResponseSet(pwmRequest, forgottenPasswordBean);
+        final ResponseSet responseSet = ForgottenPasswordUtil.readResponseSet(pwmRequest, forgottenPasswordBean);
         if (responseSet == null) {
         if (responseSet == null) {
             final String errorMsg = "attempt to check responses, but responses are not loaded into session bean";
             final String errorMsg = "attempt to check responses, but responses are not loaded into session bean";
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
@@ -631,7 +624,10 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
 
 
         try {
         try {
             // read the supplied responses from the user
             // read the supplied responses from the user
-            final Map<Challenge, String> crMap = readResponsesFromHttpRequest(pwmRequest, forgottenPasswordBean.getPresentableChallengeSet());
+            final Map<Challenge, String> crMap = ForgottenPasswordUtil.readResponsesFromHttpRequest(
+                    pwmRequest,
+                    forgottenPasswordBean.getPresentableChallengeSet()
+            );
 
 
             final boolean responsesPassed;
             final boolean responsesPassed;
             try {
             try {
@@ -696,9 +692,9 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         }
         }
 
 
         {
         {
-            final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
+            final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
             final MessageSendMethod tokenSendMethod = forgottenPasswordBean.getProgress().getTokenSendChoice();
             final MessageSendMethod tokenSendMethod = forgottenPasswordBean.getProgress().getTokenSendChoice();
-            initializeAndSendToken(pwmRequest, userInfoBean, tokenSendMethod);
+            ForgottenPasswordUtil.initializeAndSendToken(pwmRequest, userInfoBean, tokenSendMethod);
         }
         }
 
 
         final RestResultBean restResultBean = new RestResultBean();
         final RestResultBean restResultBean = new RestResultBean();
@@ -769,7 +765,8 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
 
 
         // check for identified user;
         // check for identified user;
         if (forgottenPasswordBean.getUserIdentity() == null) {
         if (forgottenPasswordBean.getUserIdentity() == null) {
-            forwardToSearchPage(pwmRequest);
+            pwmRequest.addFormInfoToRequestAttr(PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM,false,false);
+            pwmRequest.forwardToJsp(JspUrl.RECOVER_PASSWORD_SEARCH);
             return;
             return;
         }
         }
 
 
@@ -789,13 +786,12 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_NO_PROFILE_ASSIGNED));
             throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_NO_PROFILE_ASSIGNED));
         }
         }
 
 
-
         // check for previous authentication
         // check for previous authentication
         if (recoveryFlags.getRequiredAuthMethods().contains(IdentityVerificationMethod.PREVIOUS_AUTH) || recoveryFlags.getOptionalAuthMethods().contains(IdentityVerificationMethod.PREVIOUS_AUTH)) {
         if (recoveryFlags.getRequiredAuthMethods().contains(IdentityVerificationMethod.PREVIOUS_AUTH) || recoveryFlags.getOptionalAuthMethods().contains(IdentityVerificationMethod.PREVIOUS_AUTH)) {
             if (!progress.getSatisfiedMethods().contains(IdentityVerificationMethod.PREVIOUS_AUTH)) {
             if (!progress.getSatisfiedMethods().contains(IdentityVerificationMethod.PREVIOUS_AUTH)) {
                 final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
                 final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
                 final String userGuid = LdapOperationsHelper.readLdapGuidValue(pwmApplication, pwmRequest.getSessionLabel(), userIdentity, true);
                 final String userGuid = LdapOperationsHelper.readLdapGuidValue(pwmApplication, pwmRequest.getSessionLabel(), userIdentity, true);
-                if (checkAuthRecord(pwmRequest, userGuid)) {
+                if (ForgottenPasswordUtil.checkAuthRecord(pwmRequest, userGuid)) {
                     LOGGER.debug(pwmRequest, "marking " + IdentityVerificationMethod.PREVIOUS_AUTH + " method as satisfied");
                     LOGGER.debug(pwmRequest, "marking " + IdentityVerificationMethod.PREVIOUS_AUTH + " method as satisfied");
                     progress.getSatisfiedMethods().add(IdentityVerificationMethod.PREVIOUS_AUTH);
                     progress.getSatisfiedMethods().add(IdentityVerificationMethod.PREVIOUS_AUTH);
                 }
                 }
@@ -815,7 +811,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             if (progress.getSatisfiedMethods().contains(progress.getInProgressVerificationMethod())) {
             if (progress.getSatisfiedMethods().contains(progress.getInProgressVerificationMethod())) {
                 progress.setInProgressVerificationMethod(null);
                 progress.setInProgressVerificationMethod(null);
             } else {
             } else {
-                pwmRequest.setAttribute(PwmRequest.Attribute.ForgottenPasswordOptionalPageView,"true");
+                pwmRequest.setAttribute(PwmRequestAttribute.ForgottenPasswordOptionalPageView,"true");
                 forwardUserBasedOnRecoveryMethod(pwmRequest, progress.getInProgressVerificationMethod());
                 forwardUserBasedOnRecoveryMethod(pwmRequest, progress.getInProgressVerificationMethod());
                 return;
                 return;
             }
             }
@@ -823,9 +819,9 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
 
 
         // check if more optional methods required
         // check if more optional methods required
         if (recoveryFlags.getMinimumOptionalAuthMethods() > 0) {
         if (recoveryFlags.getMinimumOptionalAuthMethods() > 0) {
-            final Set<IdentityVerificationMethod> satisfiedOptionalMethods = figureSatisfiedOptionalAuthMethods(recoveryFlags,progress);
+            final Set<IdentityVerificationMethod> satisfiedOptionalMethods = ForgottenPasswordUtil.figureSatisfiedOptionalAuthMethods(recoveryFlags,progress);
             if (satisfiedOptionalMethods.size() < recoveryFlags.getMinimumOptionalAuthMethods()) {
             if (satisfiedOptionalMethods.size() < recoveryFlags.getMinimumOptionalAuthMethods()) {
-                final Set<IdentityVerificationMethod> remainingAvailableOptionalMethods = figureRemainingAvailableOptionalAuthMethods(pwmRequest, forgottenPasswordBean);
+                final Set<IdentityVerificationMethod> remainingAvailableOptionalMethods = ForgottenPasswordUtil.figureRemainingAvailableOptionalAuthMethods(pwmRequest, forgottenPasswordBean);
                 if (remainingAvailableOptionalMethods.isEmpty()) {
                 if (remainingAvailableOptionalMethods.isEmpty()) {
                     final String errorMsg = "additional optional verification methods are needed, however all available optional verification methods have been satisified by user";
                     final String errorMsg = "additional optional verification methods are needed, however all available optional verification methods have been satisified by user";
                     final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INVALID_CONFIG,errorMsg);
                     final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INVALID_CONFIG,errorMsg);
@@ -857,7 +853,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             StatisticsManager.incrementStat(pwmRequest, Statistic.RECOVERY_SUCCESSES);
             StatisticsManager.incrementStat(pwmRequest, Statistic.RECOVERY_SUCCESSES);
         }
         }
 
 
-        final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
+        final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
         try {
         try {
             final boolean enforceFromForgotten = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.CHALLENGE_ENFORCE_MINIMUM_PASSWORD_LIFETIME);
             final boolean enforceFromForgotten = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.CHALLENGE_ENFORCE_MINIMUM_PASSWORD_LIFETIME);
             if (enforceFromForgotten) {
             if (enforceFromForgotten) {
@@ -866,7 +862,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
                         theUser,
                         theUser,
                         pwmRequest.getSessionLabel(),
                         pwmRequest.getSessionLabel(),
                         userInfoBean.getPasswordPolicy(),
                         userInfoBean.getPasswordPolicy(),
-                        userInfoBean.getLastLdapLoginTime(),
+                        userInfoBean.getPasswordLastModifiedTime(),
                         userInfoBean.getPasswordState()
                         userInfoBean.getPasswordState()
                 );
                 );
             }
             }
@@ -876,7 +872,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
 
 
         LOGGER.trace(pwmRequest, "all recovery checks passed, proceeding to configured recovery action");
         LOGGER.trace(pwmRequest, "all recovery checks passed, proceeding to configured recovery action");
 
 
-        final RecoveryAction recoveryAction = getRecoveryAction(config, forgottenPasswordBean);
+        final RecoveryAction recoveryAction = ForgottenPasswordUtil.getRecoveryAction(config, forgottenPasswordBean);
         if (recoveryAction == RecoveryAction.SENDNEWPW || recoveryAction == RecoveryAction.SENDNEWPW_AND_EXPIRE) {
         if (recoveryAction == RecoveryAction.SENDNEWPW || recoveryAction == RecoveryAction.SENDNEWPW_AND_EXPIRE) {
             processSendNewPassword(pwmRequest);
             processSendNewPassword(pwmRequest);
             return;
             return;
@@ -915,10 +911,10 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             theUser.unlockPassword();
             theUser.unlockPassword();
 
 
             // mark the event log
             // mark the event log
-            final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
+            final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
             pwmApplication.getAuditManager().submit(AuditEvent.UNLOCK_PASSWORD, userInfoBean, pwmSession);
             pwmApplication.getAuditManager().submit(AuditEvent.UNLOCK_PASSWORD, userInfoBean, pwmSession);
 
 
-            sendUnlockNoticeEmail(pwmRequest, forgottenPasswordBean);
+            ForgottenPasswordUtil.sendUnlockNoticeEmail(pwmRequest, forgottenPasswordBean);
 
 
             pwmRequest.getPwmResponse().forwardToSuccessPage(Message.Success_UnlockAccount);
             pwmRequest.getPwmResponse().forwardToSuccessPage(Message.Success_UnlockAccount);
         } catch (ChaiOperationException e) {
         } catch (ChaiOperationException e) {
@@ -977,7 +973,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             pwmSession.getUserInfoBean().setRequiresNewPassword(true);
             pwmSession.getUserInfoBean().setRequiresNewPassword(true);
 
 
             // redirect user to change password screen.
             // redirect user to change password screen.
-            pwmRequest.sendRedirect(PwmServletDefinition.PrivateChangePassword.servletUrlName());
+            pwmRequest.sendRedirect(PwmServletDefinition.PublicChangePassword.servletUrlName());
         } catch (PwmUnrecoverableException e) {
         } catch (PwmUnrecoverableException e) {
             LOGGER.warn(pwmSession,
             LOGGER.warn(pwmSession,
                     "unexpected error authenticating during forgotten password recovery process user: " + e.getMessage());
                     "unexpected error authenticating during forgotten password recovery process user: " + e.getMessage());
@@ -994,7 +990,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
         final ForgottenPasswordProfile forgottenPasswordProfile = pwmRequest.getConfig().getForgottenPasswordProfiles().get(forgottenPasswordBean.getForgottenPasswordProfileID());
         final ForgottenPasswordProfile forgottenPasswordProfile = pwmRequest.getConfig().getForgottenPasswordProfiles().get(forgottenPasswordBean.getForgottenPasswordProfileID());
-        final RecoveryAction recoveryAction = getRecoveryAction(pwmApplication.getConfig(), forgottenPasswordBean);
+        final RecoveryAction recoveryAction = ForgottenPasswordUtil.getRecoveryAction(pwmApplication.getConfig(), forgottenPasswordBean);
 
 
         LOGGER.trace(pwmRequest,"beginning process to send new password to user");
         LOGGER.trace(pwmRequest,"beginning process to send new password to user");
 
 
@@ -1017,14 +1013,17 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         }
         }
 
 
         try {
         try {
+            /*
             final SessionAuthenticator sessionAuthenticator = new SessionAuthenticator(
             final SessionAuthenticator sessionAuthenticator = new SessionAuthenticator(
                     pwmApplication,
                     pwmApplication,
                     pwmSession,
                     pwmSession,
                     PwmAuthenticationSource.FORGOTTEN_PASSWORD
                     PwmAuthenticationSource.FORGOTTEN_PASSWORD
             );
             );
             sessionAuthenticator.authUserWithUnknownPassword(userIdentity,AuthenticationType.AUTH_FROM_PUBLIC_MODULE);
             sessionAuthenticator.authUserWithUnknownPassword(userIdentity,AuthenticationType.AUTH_FROM_PUBLIC_MODULE);
+            */
+            pwmSession.getLoginInfoBean().setAuthenticated(true);
             pwmSession.getLoginInfoBean().getAuthFlags().add(AuthenticationType.AUTH_FROM_PUBLIC_MODULE);
             pwmSession.getLoginInfoBean().getAuthFlags().add(AuthenticationType.AUTH_FROM_PUBLIC_MODULE);
-
+            pwmSession.getLoginInfoBean().setUserIdentity(userIdentity);
 
 
             LOGGER.info(pwmRequest, "user successfully supplied password recovery responses, emailing new password to: " + theUser.getEntryDN());
             LOGGER.info(pwmRequest, "user successfully supplied password recovery responses, emailing new password to: " + theUser.getEntryDN());
 
 
@@ -1073,103 +1072,6 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
-    public static Map<Challenge, String> readResponsesFromHttpRequest(
-            final PwmRequest req,
-            final ChallengeSet challengeSet
-    )
-            throws ChaiValidationException, ChaiUnavailableException, PwmUnrecoverableException
-    {
-        final Map<Challenge, String> responses = new LinkedHashMap<>();
-
-        int counter = 0;
-        for (final Challenge loopChallenge : challengeSet.getChallenges()) {
-            counter++;
-            final String answer = req.readParameterAsString(PwmConstants.PARAM_RESPONSE_PREFIX + counter);
-
-            responses.put(loopChallenge, answer.length() > 0 ? answer : "");
-        }
-
-        return responses;
-    }
-
-    private static String initializeAndSendToken(
-            final PwmRequest pwmRequest,
-            final UserInfoBean userInfoBean,
-            final MessageSendMethod tokenSendMethod
-
-    )
-            throws PwmUnrecoverableException
-    {
-        final Configuration config = pwmRequest.getConfig();
-        final UserIdentity userIdentity = userInfoBean.getUserIdentity();
-        final Map<String,String> tokenMapData = new LinkedHashMap<>();
-
-
-        try {
-            final Instant userLastPasswordChange = PasswordUtility.determinePwdLastModified(
-                    pwmRequest.getPwmApplication(),
-                    pwmRequest.getSessionLabel(),
-                    userIdentity
-            );
-            if (userLastPasswordChange != null) {
-                final String userChangeString = JavaHelper.toIsoDate(userLastPasswordChange);
-                tokenMapData.put(PwmConstants.TOKEN_KEY_PWD_CHG_DATE, userChangeString);
-            }
-        } catch (ChaiUnavailableException e) {
-            LOGGER.error(pwmRequest, "unexpected error reading user's last password change time");
-        }
-
-        final EmailItemBean emailItemBean = config.readSettingAsEmail(PwmSetting.EMAIL_CHALLENGE_TOKEN, pwmRequest.getLocale());
-        final MacroMachine macroMachine = MacroMachine.forUser(pwmRequest, userIdentity);
-
-        final RestTokenDataClient.TokenDestinationData inputDestinationData = new RestTokenDataClient.TokenDestinationData(
-                macroMachine.expandMacros(emailItemBean.getTo()),
-                userInfoBean.getUserSmsNumber(),
-                null
-        );
-
-        final RestTokenDataClient restTokenDataClient = new RestTokenDataClient(pwmRequest.getPwmApplication());
-        final RestTokenDataClient.TokenDestinationData outputDestrestTokenDataClient = restTokenDataClient.figureDestTokenDisplayString(
-                pwmRequest.getSessionLabel(),
-                inputDestinationData,
-                userIdentity,
-                pwmRequest.getLocale());
-
-        final String tokenDestinationAddress = outputDestrestTokenDataClient.getDisplayValue();
-        final Set<String> destinationValues = new LinkedHashSet<>();
-        if (outputDestrestTokenDataClient.getEmail() != null) {
-            destinationValues.add(outputDestrestTokenDataClient.getEmail());
-        }
-        if (outputDestrestTokenDataClient.getSms() != null) {
-            destinationValues.add(outputDestrestTokenDataClient.getSms());
-        }
-
-        final String tokenKey;
-        final TokenPayload tokenPayload;
-        try {
-            tokenPayload = pwmRequest.getPwmApplication().getTokenService().createTokenPayload(TokenType.FORGOTTEN_PW, tokenMapData, userIdentity, destinationValues);
-            tokenKey = pwmRequest.getPwmApplication().getTokenService().generateNewToken(tokenPayload, pwmRequest.getSessionLabel());
-        } catch (PwmOperationalException e) {
-            throw new PwmUnrecoverableException(e.getErrorInformation());
-        }
-
-        final String smsMessage = config.readSettingAsLocalizedString(PwmSetting.SMS_CHALLENGE_TOKEN_TEXT, pwmRequest.getLocale());
-
-        TokenService.TokenSender.sendToken(
-                pwmRequest.getPwmApplication(),
-                userInfoBean,
-                macroMachine,
-                emailItemBean,
-                tokenSendMethod,
-                outputDestrestTokenDataClient.getEmail(),
-                outputDestrestTokenDataClient.getSms(),
-                smsMessage,
-                tokenKey
-        );
-
-        StatisticsManager.incrementStat(pwmRequest, Statistic.RECOVERY_TOKENS_SENT);
-        return tokenDestinationAddress;
-    }
 
 
     private static List<FormConfiguration> figureAttributeForm(
     private static List<FormConfiguration> figureAttributeForm(
             final PwmApplication pwmApplication,
             final PwmApplication pwmApplication,
@@ -1255,75 +1157,6 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         pwmRequest.getPwmSession().getUserSessionDataCacheBean().addPostChangePasswordActions("forgottenPasswordPostActions", postAction);
         pwmRequest.getPwmSession().getUserSessionDataCacheBean().addPostChangePasswordActions("forgottenPasswordPostActions", postAction);
     }
     }
 
 
-    private static void verifyRequirementsForAuthMethod(
-            final PwmRequest pwmRequest,
-            final ForgottenPasswordBean forgottenPasswordBean,
-            final IdentityVerificationMethod recoveryVerificationMethods
-    )
-            throws PwmUnrecoverableException
-    {
-        switch (recoveryVerificationMethods) {
-            case TOKEN: {
-                final MessageSendMethod tokenSendMethod = forgottenPasswordBean.getRecoveryFlags().getTokenSendMethod();
-                if (tokenSendMethod == null || tokenSendMethod == MessageSendMethod.NONE) {
-                    final String errorMsg = "user is required to complete token validation, yet there is not a token send method configured";
-                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INVALID_CONFIG, errorMsg);
-                    throw new PwmUnrecoverableException(errorInformation);
-                }
-            }
-            break;
-
-            case ATTRIBUTES: {
-                final List<FormConfiguration> formConfiguration = forgottenPasswordBean.getAttributeForm();
-                if (formConfiguration == null || formConfiguration.isEmpty()) {
-                    final String errorMsg = "user is required to complete LDAP attribute check, yet there are no LDAP attribute form items configured";
-                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INVALID_CONFIG, errorMsg);
-                    throw new PwmUnrecoverableException(errorInformation);
-                }
-            }
-            break;
-
-            case OTP: {
-                final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
-                if (userInfoBean.getOtpUserRecord() == null) {
-                    final String errorMsg = "could not find a one time password configuration for " + userInfoBean.getUserIdentity();
-                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_NO_OTP_CONFIGURATION, errorMsg);
-                    throw new PwmUnrecoverableException(errorInformation);
-                }
-            }
-            break;
-
-            case CHALLENGE_RESPONSES: {
-                final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
-                final ResponseSet responseSet = readResponseSet(pwmRequest, forgottenPasswordBean);
-                if (responseSet == null) {
-                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_RESPONSES_NORESPONSES);
-                    throw new PwmUnrecoverableException(errorInformation);
-                }
-
-                final ChallengeSet challengeSet = userInfoBean.getChallengeProfile().getChallengeSet();
-
-                try {
-                    if (responseSet.meetsChallengeSetRequirements(challengeSet)) {
-                        if (challengeSet.getRequiredChallenges().isEmpty() && (challengeSet.getMinRandomRequired() <= 0)) {
-                            final String errorMsg = "configured challenge set policy for " + userInfoBean.getUserIdentity().toString() + " is empty, user not qualified to recover password";
-                            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_NO_CHALLENGES, errorMsg);
-                            throw new PwmUnrecoverableException(errorInformation);
-                        }
-                    }
-                } catch (ChaiValidationException e) {
-                    final String errorMsg = "stored response set for user '" + userInfoBean.getUserIdentity() + "' do not meet current challenge set requirements: " + e.getLocalizedMessage();
-                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_RESPONSES_NORESPONSES, errorMsg);
-                    throw new PwmUnrecoverableException(errorInformation);
-                }
-            }
-            break;
-
-            default:
-                // continue, assume no data requirements for method.
-                break;
-        }
-    }
 
 
     private static void initForgottenPasswordBean(
     private static void initForgottenPasswordBean(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
@@ -1339,7 +1172,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
 
 
         forgottenPasswordBean.setUserIdentity(userIdentity);
         forgottenPasswordBean.setUserIdentity(userIdentity);
 
 
-        final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
+        final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
 
 
         final String forgottenProfileID = ProfileUtility.discoverProfileIDforUser(pwmApplication, sessionLabel, userIdentity, ProfileType.ForgottenPassword);
         final String forgottenProfileID = ProfileUtility.discoverProfileIDforUser(pwmApplication, sessionLabel, userIdentity, ProfileType.ForgottenPassword);
         if (forgottenProfileID == null || forgottenProfileID.isEmpty()) {
         if (forgottenProfileID == null || forgottenProfileID.isEmpty()) {
@@ -1408,7 +1241,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         forgottenPasswordBean.setProgress(new ForgottenPasswordBean.Progress());
         forgottenPasswordBean.setProgress(new ForgottenPasswordBean.Progress());
 
 
         for (final IdentityVerificationMethod recoveryVerificationMethods : recoveryFlags.getRequiredAuthMethods()) {
         for (final IdentityVerificationMethod recoveryVerificationMethods : recoveryFlags.getRequiredAuthMethods()) {
-            verifyRequirementsForAuthMethod(pwmRequest, forgottenPasswordBean, recoveryVerificationMethods);
+            ForgottenPasswordUtil.verifyRequirementsForAuthMethod(pwmRequest, forgottenPasswordBean, recoveryVerificationMethods);
         }
         }
     }
     }
 
 
@@ -1491,79 +1324,6 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         }
         }
     }
     }
 
 
-    private static MessageSendMethod figureTokenSendPreference(
-            final PwmRequest pwmRequest,
-            final ForgottenPasswordBean forgottenPasswordBean
-    )
-            throws PwmUnrecoverableException
-    {
-        final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
-        final MessageSendMethod tokenSendMethod = forgottenPasswordBean.getRecoveryFlags().getTokenSendMethod();
-        if (tokenSendMethod == null || tokenSendMethod.equals(MessageSendMethod.NONE)) {
-            return MessageSendMethod.NONE;
-        }
-
-        if (!tokenSendMethod.equals(MessageSendMethod.CHOICE_SMS_EMAIL)) {
-            return tokenSendMethod;
-        }
-
-        final String emailAddress = userInfoBean.getUserEmailAddress();
-        final String smsAddress = userInfoBean.getUserSmsNumber();
-
-        final boolean hasEmail = emailAddress != null && emailAddress.length() > 1;
-        final boolean hasSms = smsAddress != null && smsAddress.length() > 1;
-
-        if (hasEmail && hasSms) {
-            return MessageSendMethod.CHOICE_SMS_EMAIL;
-        } else if (hasEmail) {
-            LOGGER.debug(pwmRequest, "though token send method is " + MessageSendMethod.CHOICE_SMS_EMAIL + ", no sms address is available for user so defaulting to email method");
-            return MessageSendMethod.EMAILONLY;
-        } else if (hasSms) {
-            LOGGER.debug(pwmRequest, "though token send method is " + MessageSendMethod.CHOICE_SMS_EMAIL + ", no email address is available for user so defaulting to sms method");
-            return MessageSendMethod.SMSONLY;
-        }
-
-        throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_TOKEN_MISSING_CONTACT));
-    }
-
-    private static Set<IdentityVerificationMethod> figureSatisfiedOptionalAuthMethods(
-            final ForgottenPasswordBean.RecoveryFlags recoveryFlags,
-            final ForgottenPasswordBean.Progress progress)
-    {
-        final Set<IdentityVerificationMethod> result = new LinkedHashSet<>();
-        result.addAll(recoveryFlags.getOptionalAuthMethods());
-        result.retainAll(progress.getSatisfiedMethods());
-        return Collections.unmodifiableSet(result);
-    }
-
-    private static Set<IdentityVerificationMethod> figureRemainingAvailableOptionalAuthMethods(
-            final PwmRequest pwmRequest,
-            final ForgottenPasswordBean forgottenPasswordBean
-    )
-    {
-        final ForgottenPasswordBean.RecoveryFlags recoveryFlags = forgottenPasswordBean.getRecoveryFlags();
-        final ForgottenPasswordBean.Progress progress = forgottenPasswordBean.getProgress();
-        final Set<IdentityVerificationMethod> result = new LinkedHashSet<>();
-        result.addAll(recoveryFlags.getOptionalAuthMethods());
-        result.removeAll(progress.getSatisfiedMethods());
-
-        for (final IdentityVerificationMethod recoveryVerificationMethods : new LinkedHashSet<>(result)) {
-            try {
-                verifyRequirementsForAuthMethod(pwmRequest, forgottenPasswordBean, recoveryVerificationMethods);
-            } catch (PwmUnrecoverableException e) {
-                result.remove(recoveryVerificationMethods);
-            }
-        }
-
-        return Collections.unmodifiableSet(result);
-    }
-
-    public static RecoveryAction getRecoveryAction(final Configuration configuration, final ForgottenPasswordBean forgottenPasswordBean) {
-        final ForgottenPasswordProfile forgottenPasswordProfile = configuration.getForgottenPasswordProfiles().get(forgottenPasswordBean.getForgottenPasswordProfileID());
-        return forgottenPasswordProfile.readSettingAsEnum(PwmSetting.RECOVERY_ACTION, RecoveryAction.class);
-    }
-
-
     private void forwardUserBasedOnRecoveryMethod(
     private void forwardUserBasedOnRecoveryMethod(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final IdentityVerificationMethod method
             final IdentityVerificationMethod method
@@ -1572,7 +1332,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
     {
     {
         LOGGER.debug(pwmRequest,"attempting to forward request to handle verification method " + method.toString());
         LOGGER.debug(pwmRequest,"attempting to forward request to handle verification method " + method.toString());
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean(pwmRequest);
-        verifyRequirementsForAuthMethod(pwmRequest,forgottenPasswordBean,method);
+        ForgottenPasswordUtil.verifyRequirementsForAuthMethod(pwmRequest,forgottenPasswordBean,method);
         switch (method) {
         switch (method) {
             case PREVIOUS_AUTH: {
             case PREVIOUS_AUTH: {
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"previous authentication is required, but user has not previously authenticated"));
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"previous authentication is required, but user has not previously authenticated"));
@@ -1585,14 +1345,14 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             break;
             break;
 
 
             case CHALLENGE_RESPONSES: {
             case CHALLENGE_RESPONSES: {
-                pwmRequest.setAttribute(PwmRequest.Attribute.ForgottenPasswordChallengeSet, forgottenPasswordBean.getPresentableChallengeSet());
+                pwmRequest.setAttribute(PwmRequestAttribute.ForgottenPasswordChallengeSet, forgottenPasswordBean.getPresentableChallengeSet());
                 pwmRequest.forwardToJsp(JspUrl.RECOVER_PASSWORD_RESPONSES);
                 pwmRequest.forwardToJsp(JspUrl.RECOVER_PASSWORD_RESPONSES);
             }
             }
             break;
             break;
 
 
             case OTP: {
             case OTP: {
-                final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
-                pwmRequest.setAttribute(PwmRequest.Attribute.ForgottenPasswordUserInfo, userInfoBean);
+                final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
+                pwmRequest.setAttribute(PwmRequestAttribute.ForgottenPasswordUserInfo, userInfoBean);
                 pwmRequest.forwardToJsp(JspUrl.RECOVER_PASSWORD_ENTER_OTP);
                 pwmRequest.forwardToJsp(JspUrl.RECOVER_PASSWORD_ENTER_OTP);
             }
             }
             break;
             break;
@@ -1600,7 +1360,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             case TOKEN: {
             case TOKEN: {
                 final ForgottenPasswordBean.Progress progress = forgottenPasswordBean.getProgress();
                 final ForgottenPasswordBean.Progress progress = forgottenPasswordBean.getProgress();
                 if (progress.getTokenSendChoice() == null) {
                 if (progress.getTokenSendChoice() == null) {
-                    progress.setTokenSendChoice(figureTokenSendPreference(pwmRequest, forgottenPasswordBean));
+                    progress.setTokenSendChoice(ForgottenPasswordUtil.figureTokenSendPreference(pwmRequest, forgottenPasswordBean));
                 }
                 }
 
 
                 if (progress.getTokenSendChoice() == MessageSendMethod.CHOICE_SMS_EMAIL) {
                 if (progress.getTokenSendChoice() == MessageSendMethod.CHOICE_SMS_EMAIL) {
@@ -1609,8 +1369,8 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
                 }
                 }
 
 
                 if (!progress.isTokenSent()) {
                 if (!progress.isTokenSent()) {
-                    final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
-                    final String destAddress = initializeAndSendToken(pwmRequest, userInfoBean, progress.getTokenSendChoice());
+                    final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
+                    final String destAddress = ForgottenPasswordUtil.initializeAndSendToken(pwmRequest, userInfoBean, progress.getTokenSendChoice());
                     progress.setTokenSentAddress(destAddress);
                     progress.setTokenSentAddress(destAddress);
                     progress.setTokenSent(true);
                     progress.setTokenSent(true);
                 }
                 }
@@ -1623,7 +1383,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             break;
             break;
 
 
             case REMOTE_RESPONSES: {
             case REMOTE_RESPONSES: {
-                final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
+                final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
                 final VerificationMethodSystem remoteMethod;
                 final VerificationMethodSystem remoteMethod;
                 if (forgottenPasswordBean.getProgress().getRemoteRecoveryMethod() == null) {
                 if (forgottenPasswordBean.getProgress().getRemoteRecoveryMethod() == null) {
                     remoteMethod = new RemoteVerificationMethod();
                     remoteMethod = new RemoteVerificationMethod();
@@ -1641,8 +1401,8 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
                 final List<VerificationMethodSystem.UserPrompt> prompts = remoteMethod.getCurrentPrompts();
                 final List<VerificationMethodSystem.UserPrompt> prompts = remoteMethod.getCurrentPrompts();
                 final String displayInstructions = remoteMethod.getCurrentDisplayInstructions();
                 final String displayInstructions = remoteMethod.getCurrentDisplayInstructions();
 
 
-                pwmRequest.setAttribute(PwmRequest.Attribute.ForgottenPasswordPrompts, new ArrayList<>(prompts));
-                pwmRequest.setAttribute(PwmRequest.Attribute.ForgottenPasswordInstructions, displayInstructions);
+                pwmRequest.setAttribute(PwmRequestAttribute.ForgottenPasswordPrompts, new ArrayList<>(prompts));
+                pwmRequest.setAttribute(PwmRequestAttribute.ForgottenPasswordInstructions, displayInstructions);
                 pwmRequest.forwardToJsp(JspUrl.RECOVER_PASSWORD_REMOTE);
                 pwmRequest.forwardToJsp(JspUrl.RECOVER_PASSWORD_REMOTE);
             }
             }
             break;
             break;
@@ -1663,137 +1423,6 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         }
         }
 
 
     }
     }
-
-    private boolean checkAuthRecord(final PwmRequest pwmRequest, final String userGuid)
-            throws PwmUnrecoverableException
-    {
-        if (userGuid == null || userGuid.isEmpty()) {
-            return false;
-        }
-
-        try {
-            final String cookieName = pwmRequest.getConfig().readAppProperty(AppProperty.HTTP_COOKIE_AUTHRECORD_NAME);
-            if (cookieName == null || cookieName.isEmpty()) {
-                LOGGER.trace(pwmRequest, "skipping auth record cookie read, cookie name parameter is blank");
-                return false;
-            }
-
-            final AuthenticationFilter.AuthRecord authRecord = pwmRequest.readEncryptedCookie(cookieName, AuthenticationFilter.AuthRecord.class);
-            if (authRecord != null) {
-                if (authRecord.getGuid() != null && !authRecord.getGuid().isEmpty() && authRecord.getGuid().equals(userGuid)) {
-                    LOGGER.debug(pwmRequest, "auth record cookie validated");
-                    return true;
-                }
-            }
-        } catch (Exception e) {
-            LOGGER.error(pwmRequest, "unexpected error while examining cookie auth record: " + e.getMessage());
-        }
-        return false;
-    }
-
-    protected void forwardToSearchPage(final PwmRequest pwmRequest)
-            throws ServletException, PwmUnrecoverableException, IOException
-    {
-        pwmRequest.addFormInfoToRequestAttr(PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM,false,false);
-        pwmRequest.forwardToJsp(JspUrl.RECOVER_PASSWORD_SEARCH);
-    }
-
-
-    private static UserInfoBean readUserInfoBean(final PwmRequest pwmRequest, final ForgottenPasswordBean forgottenPasswordBean) throws PwmUnrecoverableException {
-        if (forgottenPasswordBean.getUserIdentity() == null) {
-            return null;
-        }
-
-        final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
-
-        {
-            final UserInfoBean beanInRequest = (UserInfoBean)pwmRequest.getAttribute(PwmRequest.Attribute.ForgottenPasswordUserInfo);
-            if (beanInRequest != null) {
-                if (userIdentity.equals(beanInRequest.getUserIdentity())) {
-                    LOGGER.trace(pwmRequest, "using request cached UserInfoBean");
-                    return beanInRequest;
-                } else {
-                    LOGGER.trace(pwmRequest, "request cached userInfoBean is not for current user, clearing.");
-                    pwmRequest.setAttribute(PwmRequest.Attribute.ForgottenPasswordUserInfo, null);
-                }
-            }
-        }
-
-        final ChaiProvider chaiProvider = pwmRequest.getPwmApplication().getProxyChaiProvider(userIdentity.getLdapProfileID());
-        final UserStatusReader userStatusReader = new UserStatusReader(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
-        final UserInfoBean userInfoBean = new UserInfoBean();
-        userStatusReader.populateUserInfoBean(
-                userInfoBean,
-                pwmRequest.getLocale(),
-                userIdentity,
-                chaiProvider
-        );
-
-        pwmRequest.setAttribute(PwmRequest.Attribute.ForgottenPasswordUserInfo, userInfoBean);
-
-        return userInfoBean;
-    }
-
-    private static ResponseSet readResponseSet(final PwmRequest pwmRequest, final ForgottenPasswordBean forgottenPasswordBean)
-            throws PwmUnrecoverableException
-    {
-
-        if (forgottenPasswordBean.getUserIdentity() == null) {
-            return null;
-        }
-
-        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
-        final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
-        final ResponseSet responseSet;
-
-        try {
-            final ChaiUser theUser = pwmApplication.getProxiedChaiUser(userIdentity);
-            responseSet = pwmApplication.getCrService().readUserResponseSet(
-                    pwmRequest.getSessionLabel(),
-                    userIdentity,
-                    theUser
-            );
-        } catch (ChaiUnavailableException e) {
-            throw PwmUnrecoverableException.fromChaiException(e);
-        }
-
-        return responseSet;
-    }
-
-    private static void sendUnlockNoticeEmail(
-            final PwmRequest pwmRequest,
-            final ForgottenPasswordBean forgottenPasswordBean
-    )
-            throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
-    {
-        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
-        final Configuration config = pwmRequest.getConfig();
-        final Locale locale = pwmRequest.getLocale();
-        final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
-        final EmailItemBean configuredEmailSetting = config.readSettingAsEmail(PwmSetting.EMAIL_UNLOCK, locale);
-
-        if (configuredEmailSetting == null) {
-            LOGGER.debug(pwmRequest, "skipping send unlock notice email for '" + userIdentity + "' no email configured");
-            return;
-        }
-
-        final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
-        final MacroMachine macroMachine = new MacroMachine(
-                pwmApplication,
-                pwmRequest.getSessionLabel(),
-                userInfoBean,
-                null,
-                LdapUserDataReader.appProxiedReader(pwmApplication, userIdentity)
-        );
-
-        pwmApplication.getEmailQueue().submitEmail(
-                configuredEmailSetting,
-                userInfoBean,
-                macroMachine
-        );
-    }
-
-
 }
 }
 
 
 
 

+ 443 - 0
src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordUtil.java

@@ -0,0 +1,443 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2017 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.forgottenpw;
+
+import com.novell.ldapchai.ChaiUser;
+import com.novell.ldapchai.cr.Challenge;
+import com.novell.ldapchai.cr.ChallengeSet;
+import com.novell.ldapchai.cr.ResponseSet;
+import com.novell.ldapchai.exception.ChaiUnavailableException;
+import com.novell.ldapchai.exception.ChaiValidationException;
+import com.novell.ldapchai.provider.ChaiProvider;
+import password.pwm.AppProperty;
+import password.pwm.PwmApplication;
+import password.pwm.PwmConstants;
+import password.pwm.bean.EmailItemBean;
+import password.pwm.bean.UserIdentity;
+import password.pwm.bean.UserInfoBean;
+import password.pwm.config.Configuration;
+import password.pwm.config.FormConfiguration;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.option.IdentityVerificationMethod;
+import password.pwm.config.option.MessageSendMethod;
+import password.pwm.config.option.RecoveryAction;
+import password.pwm.config.profile.ForgottenPasswordProfile;
+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.PwmRequestAttribute;
+import password.pwm.http.bean.ForgottenPasswordBean;
+import password.pwm.http.filter.AuthenticationFilter;
+import password.pwm.ldap.LdapUserDataReader;
+import password.pwm.ldap.UserStatusReader;
+import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.token.TokenPayload;
+import password.pwm.svc.token.TokenService;
+import password.pwm.svc.token.TokenType;
+import password.pwm.util.java.JavaHelper;
+import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.macro.MacroMachine;
+import password.pwm.util.operations.PasswordUtility;
+import password.pwm.ws.client.rest.RestTokenDataClient;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+class ForgottenPasswordUtil {
+    private static final PwmLogger LOGGER = PwmLogger.forClass(ForgottenPasswordUtil.class);
+
+    static Set<IdentityVerificationMethod> figureRemainingAvailableOptionalAuthMethods(
+            final PwmRequest pwmRequest,
+            final ForgottenPasswordBean forgottenPasswordBean
+    )
+    {
+        final ForgottenPasswordBean.RecoveryFlags recoveryFlags = forgottenPasswordBean.getRecoveryFlags();
+        final ForgottenPasswordBean.Progress progress = forgottenPasswordBean.getProgress();
+        final Set<IdentityVerificationMethod> result = new LinkedHashSet<>();
+        result.addAll(recoveryFlags.getOptionalAuthMethods());
+        result.removeAll(progress.getSatisfiedMethods());
+
+        for (final IdentityVerificationMethod recoveryVerificationMethods : new LinkedHashSet<>(result)) {
+            try {
+                verifyRequirementsForAuthMethod(pwmRequest, forgottenPasswordBean, recoveryVerificationMethods);
+            } catch (PwmUnrecoverableException e) {
+                result.remove(recoveryVerificationMethods);
+            }
+        }
+
+        return Collections.unmodifiableSet(result);
+    }
+
+    public static RecoveryAction getRecoveryAction(final Configuration configuration, final ForgottenPasswordBean forgottenPasswordBean) {
+        final ForgottenPasswordProfile forgottenPasswordProfile = configuration.getForgottenPasswordProfiles().get(forgottenPasswordBean.getForgottenPasswordProfileID());
+        return forgottenPasswordProfile.readSettingAsEnum(PwmSetting.RECOVERY_ACTION, RecoveryAction.class);
+    }
+
+
+    static Set<IdentityVerificationMethod> figureSatisfiedOptionalAuthMethods(
+            final ForgottenPasswordBean.RecoveryFlags recoveryFlags,
+            final ForgottenPasswordBean.Progress progress)
+    {
+        final Set<IdentityVerificationMethod> result = new LinkedHashSet<>();
+        result.addAll(recoveryFlags.getOptionalAuthMethods());
+        result.retainAll(progress.getSatisfiedMethods());
+        return Collections.unmodifiableSet(result);
+    }
+
+    static UserInfoBean readUserInfoBean(final PwmRequest pwmRequest, final ForgottenPasswordBean forgottenPasswordBean) throws PwmUnrecoverableException
+    {
+        if (forgottenPasswordBean.getUserIdentity() == null) {
+            return null;
+        }
+
+        final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
+
+        {
+            final UserInfoBean beanInRequest = (UserInfoBean)pwmRequest.getAttribute(PwmRequestAttribute.ForgottenPasswordUserInfo);
+            if (beanInRequest != null) {
+                if (userIdentity.equals(beanInRequest.getUserIdentity())) {
+                    LOGGER.trace(pwmRequest, "using request cached UserInfoBean");
+                    return beanInRequest;
+                } else {
+                    LOGGER.trace(pwmRequest, "request cached userInfoBean is not for current user, clearing.");
+                    pwmRequest.setAttribute(PwmRequestAttribute.ForgottenPasswordUserInfo, null);
+                }
+            }
+        }
+
+        final ChaiProvider chaiProvider = pwmRequest.getPwmApplication().getProxyChaiProvider(userIdentity.getLdapProfileID());
+        final UserStatusReader userStatusReader = new UserStatusReader(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
+        final UserInfoBean userInfoBean = new UserInfoBean();
+        userStatusReader.populateUserInfoBean(
+                userInfoBean,
+                pwmRequest.getLocale(),
+                userIdentity,
+                chaiProvider
+        );
+
+        pwmRequest.setAttribute(PwmRequestAttribute.ForgottenPasswordUserInfo, userInfoBean);
+
+        return userInfoBean;
+    }
+
+    static ResponseSet readResponseSet(final PwmRequest pwmRequest, final ForgottenPasswordBean forgottenPasswordBean)
+            throws PwmUnrecoverableException
+    {
+
+        if (forgottenPasswordBean.getUserIdentity() == null) {
+            return null;
+        }
+
+        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
+        final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
+        final ResponseSet responseSet;
+
+        try {
+            final ChaiUser theUser = pwmApplication.getProxiedChaiUser(userIdentity);
+            responseSet = pwmApplication.getCrService().readUserResponseSet(
+                    pwmRequest.getSessionLabel(),
+                    userIdentity,
+                    theUser
+            );
+        } catch (ChaiUnavailableException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
+        }
+
+        return responseSet;
+    }
+
+    static void sendUnlockNoticeEmail(
+            final PwmRequest pwmRequest,
+            final ForgottenPasswordBean forgottenPasswordBean
+    )
+            throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
+    {
+        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
+        final Configuration config = pwmRequest.getConfig();
+        final Locale locale = pwmRequest.getLocale();
+        final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
+        final EmailItemBean configuredEmailSetting = config.readSettingAsEmail(PwmSetting.EMAIL_UNLOCK, locale);
+
+        if (configuredEmailSetting == null) {
+            LOGGER.debug(pwmRequest, "skipping send unlock notice email for '" + userIdentity + "' no email configured");
+            return;
+        }
+
+        final UserInfoBean userInfoBean = readUserInfoBean(pwmRequest, forgottenPasswordBean);
+        final MacroMachine macroMachine = new MacroMachine(
+                pwmApplication,
+                pwmRequest.getSessionLabel(),
+                userInfoBean,
+                null,
+                LdapUserDataReader.appProxiedReader(pwmApplication, userIdentity)
+        );
+
+        pwmApplication.getEmailQueue().submitEmail(
+                configuredEmailSetting,
+                userInfoBean,
+                macroMachine
+        );
+    }
+
+    static boolean checkAuthRecord(final PwmRequest pwmRequest, final String userGuid)
+            throws PwmUnrecoverableException
+    {
+        if (userGuid == null || userGuid.isEmpty()) {
+            return false;
+        }
+
+        try {
+            final String cookieName = pwmRequest.getConfig().readAppProperty(AppProperty.HTTP_COOKIE_AUTHRECORD_NAME);
+            if (cookieName == null || cookieName.isEmpty()) {
+                LOGGER.trace(pwmRequest, "skipping auth record cookie read, cookie name parameter is blank");
+                return false;
+            }
+
+            final AuthenticationFilter.AuthRecord authRecord = pwmRequest.readEncryptedCookie(cookieName, AuthenticationFilter.AuthRecord.class);
+            if (authRecord != null) {
+                if (authRecord.getGuid() != null && !authRecord.getGuid().isEmpty() && authRecord.getGuid().equals(userGuid)) {
+                    LOGGER.debug(pwmRequest, "auth record cookie validated");
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            LOGGER.error(pwmRequest, "unexpected error while examining cookie auth record: " + e.getMessage());
+        }
+        return false;
+    }
+
+    static MessageSendMethod figureTokenSendPreference(
+            final PwmRequest pwmRequest,
+            final ForgottenPasswordBean forgottenPasswordBean
+    )
+            throws PwmUnrecoverableException
+    {
+        final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
+        final MessageSendMethod tokenSendMethod = forgottenPasswordBean.getRecoveryFlags().getTokenSendMethod();
+        if (tokenSendMethod == null || tokenSendMethod.equals(MessageSendMethod.NONE)) {
+            return MessageSendMethod.NONE;
+        }
+
+        if (!tokenSendMethod.equals(MessageSendMethod.CHOICE_SMS_EMAIL)) {
+            return tokenSendMethod;
+        }
+
+        final String emailAddress = userInfoBean.getUserEmailAddress();
+        final String smsAddress = userInfoBean.getUserSmsNumber();
+
+        final boolean hasEmail = emailAddress != null && emailAddress.length() > 1;
+        final boolean hasSms = smsAddress != null && smsAddress.length() > 1;
+
+        if (hasEmail && hasSms) {
+            return MessageSendMethod.CHOICE_SMS_EMAIL;
+        } else if (hasEmail) {
+            LOGGER.debug(pwmRequest, "though token send method is " + MessageSendMethod.CHOICE_SMS_EMAIL + ", no sms address is available for user so defaulting to email method");
+            return MessageSendMethod.EMAILONLY;
+        } else if (hasSms) {
+            LOGGER.debug(pwmRequest, "though token send method is " + MessageSendMethod.CHOICE_SMS_EMAIL + ", no email address is available for user so defaulting to sms method");
+            return MessageSendMethod.SMSONLY;
+        }
+
+        throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_TOKEN_MISSING_CONTACT));
+    }
+
+    static void verifyRequirementsForAuthMethod(
+            final PwmRequest pwmRequest,
+            final ForgottenPasswordBean forgottenPasswordBean,
+            final IdentityVerificationMethod recoveryVerificationMethods
+    )
+            throws PwmUnrecoverableException
+    {
+        switch (recoveryVerificationMethods) {
+            case TOKEN: {
+                final MessageSendMethod tokenSendMethod = forgottenPasswordBean.getRecoveryFlags().getTokenSendMethod();
+                if (tokenSendMethod == null || tokenSendMethod == MessageSendMethod.NONE) {
+                    final String errorMsg = "user is required to complete token validation, yet there is not a token send method configured";
+                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INVALID_CONFIG, errorMsg);
+                    throw new PwmUnrecoverableException(errorInformation);
+                }
+            }
+            break;
+
+            case ATTRIBUTES: {
+                final List<FormConfiguration> formConfiguration = forgottenPasswordBean.getAttributeForm();
+                if (formConfiguration == null || formConfiguration.isEmpty()) {
+                    final String errorMsg = "user is required to complete LDAP attribute check, yet there are no LDAP attribute form items configured";
+                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INVALID_CONFIG, errorMsg);
+                    throw new PwmUnrecoverableException(errorInformation);
+                }
+            }
+            break;
+
+            case OTP: {
+                final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
+                if (userInfoBean.getOtpUserRecord() == null) {
+                    final String errorMsg = "could not find a one time password configuration for " + userInfoBean.getUserIdentity();
+                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_NO_OTP_CONFIGURATION, errorMsg);
+                    throw new PwmUnrecoverableException(errorInformation);
+                }
+            }
+            break;
+
+            case CHALLENGE_RESPONSES: {
+                final UserInfoBean userInfoBean = ForgottenPasswordUtil.readUserInfoBean(pwmRequest, forgottenPasswordBean);
+                final ResponseSet responseSet = ForgottenPasswordUtil.readResponseSet(pwmRequest, forgottenPasswordBean);
+                if (responseSet == null) {
+                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_RESPONSES_NORESPONSES);
+                    throw new PwmUnrecoverableException(errorInformation);
+                }
+
+                final ChallengeSet challengeSet = userInfoBean.getChallengeProfile().getChallengeSet();
+
+                try {
+                    if (responseSet.meetsChallengeSetRequirements(challengeSet)) {
+                        if (challengeSet.getRequiredChallenges().isEmpty() && (challengeSet.getMinRandomRequired() <= 0)) {
+                            final String errorMsg = "configured challenge set policy for " + userInfoBean.getUserIdentity().toString() + " is empty, user not qualified to recover password";
+                            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_NO_CHALLENGES, errorMsg);
+                            throw new PwmUnrecoverableException(errorInformation);
+                        }
+                    }
+                } catch (ChaiValidationException e) {
+                    final String errorMsg = "stored response set for user '" + userInfoBean.getUserIdentity() + "' do not meet current challenge set requirements: " + e.getLocalizedMessage();
+                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_RESPONSES_NORESPONSES, errorMsg);
+                    throw new PwmUnrecoverableException(errorInformation);
+                }
+            }
+            break;
+
+            default:
+                // continue, assume no data requirements for method.
+                break;
+        }
+    }
+
+    static Map<Challenge, String> readResponsesFromHttpRequest(
+            final PwmRequest req,
+            final ChallengeSet challengeSet
+    )
+            throws ChaiValidationException, ChaiUnavailableException, PwmUnrecoverableException
+    {
+        final Map<Challenge, String> responses = new LinkedHashMap<>();
+
+        int counter = 0;
+        for (final Challenge loopChallenge : challengeSet.getChallenges()) {
+            counter++;
+            final String answer = req.readParameterAsString(PwmConstants.PARAM_RESPONSE_PREFIX + counter);
+
+            responses.put(loopChallenge, answer.length() > 0 ? answer : "");
+        }
+
+        return responses;
+    }
+
+    static String initializeAndSendToken(
+            final PwmRequest pwmRequest,
+            final UserInfoBean userInfoBean,
+            final MessageSendMethod tokenSendMethod
+
+    )
+            throws PwmUnrecoverableException
+    {
+        final Configuration config = pwmRequest.getConfig();
+        final UserIdentity userIdentity = userInfoBean.getUserIdentity();
+        final Map<String,String> tokenMapData = new LinkedHashMap<>();
+
+
+        try {
+            final Instant userLastPasswordChange = PasswordUtility.determinePwdLastModified(
+                    pwmRequest.getPwmApplication(),
+                    pwmRequest.getSessionLabel(),
+                    userIdentity
+            );
+            if (userLastPasswordChange != null) {
+                final String userChangeString = JavaHelper.toIsoDate(userLastPasswordChange);
+                tokenMapData.put(PwmConstants.TOKEN_KEY_PWD_CHG_DATE, userChangeString);
+            }
+        } catch (ChaiUnavailableException e) {
+            LOGGER.error(pwmRequest, "unexpected error reading user's last password change time");
+        }
+
+        final EmailItemBean emailItemBean = config.readSettingAsEmail(PwmSetting.EMAIL_CHALLENGE_TOKEN, pwmRequest.getLocale());
+        final MacroMachine macroMachine = MacroMachine.forUser(pwmRequest, userIdentity);
+
+        final RestTokenDataClient.TokenDestinationData inputDestinationData = new RestTokenDataClient.TokenDestinationData(
+                macroMachine.expandMacros(emailItemBean.getTo()),
+                userInfoBean.getUserSmsNumber(),
+                null
+        );
+
+        final RestTokenDataClient restTokenDataClient = new RestTokenDataClient(pwmRequest.getPwmApplication());
+        final RestTokenDataClient.TokenDestinationData outputDestrestTokenDataClient = restTokenDataClient.figureDestTokenDisplayString(
+                pwmRequest.getSessionLabel(),
+                inputDestinationData,
+                userIdentity,
+                pwmRequest.getLocale());
+
+        final String tokenDestinationAddress = outputDestrestTokenDataClient.getDisplayValue();
+        final Set<String> destinationValues = new LinkedHashSet<>();
+        if (outputDestrestTokenDataClient.getEmail() != null) {
+            destinationValues.add(outputDestrestTokenDataClient.getEmail());
+        }
+        if (outputDestrestTokenDataClient.getSms() != null) {
+            destinationValues.add(outputDestrestTokenDataClient.getSms());
+        }
+
+        final String tokenKey;
+        final TokenPayload tokenPayload;
+        try {
+            tokenPayload = pwmRequest.getPwmApplication().getTokenService().createTokenPayload(TokenType.FORGOTTEN_PW, tokenMapData, userIdentity, destinationValues);
+            tokenKey = pwmRequest.getPwmApplication().getTokenService().generateNewToken(tokenPayload, pwmRequest.getSessionLabel());
+        } catch (PwmOperationalException e) {
+            throw new PwmUnrecoverableException(e.getErrorInformation());
+        }
+
+        final String smsMessage = config.readSettingAsLocalizedString(PwmSetting.SMS_CHALLENGE_TOKEN_TEXT, pwmRequest.getLocale());
+
+        TokenService.TokenSender.sendToken(
+                pwmRequest.getPwmApplication(),
+                userInfoBean,
+                macroMachine,
+                emailItemBean,
+                tokenSendMethod,
+                outputDestrestTokenDataClient.getEmail(),
+                outputDestrestTokenDataClient.getSms(),
+                smsMessage,
+                tokenKey
+        );
+
+        StatisticsManager.incrementStat(pwmRequest, Statistic.RECOVERY_TOKENS_SENT);
+        return tokenDestinationAddress;
+    }
+
+}

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

@@ -54,6 +54,7 @@ import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 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.PwmRequestAttribute;
 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.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
@@ -159,7 +160,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
             throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
     {
     {
         final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
         final HelpdeskProfile helpdeskProfile = getHelpdeskRProfile(pwmRequest);
-        pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskVerificationEnabled, !helpdeskProfile.readRequiredVerificationMethods().isEmpty());
+        pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskVerificationEnabled, !helpdeskProfile.readRequiredVerificationMethods().isEmpty());
         pwmRequest.forwardToJsp(JspUrl.HELPDESK_SEARCH);
         pwmRequest.forwardToJsp(JspUrl.HELPDESK_SEARCH);
     }
     }
 
 
@@ -456,16 +457,16 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         }
         }
 
 
         final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
         final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
-        pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskDetail, helpdeskDetailInfoBean);
+        pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskDetail, helpdeskDetailInfoBean);
 
 
         if (helpdeskDetailInfoBean != null && helpdeskDetailInfoBean.getUserInfoBean() != null) {
         if (helpdeskDetailInfoBean != null && helpdeskDetailInfoBean.getUserInfoBean() != null) {
             final String obfuscatedDN = helpdeskDetailInfoBean.getUserInfoBean().getUserIdentity().toObfuscatedKey(pwmRequest.getPwmApplication());
             final String obfuscatedDN = helpdeskDetailInfoBean.getUserInfoBean().getUserIdentity().toObfuscatedKey(pwmRequest.getPwmApplication());
-            pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskObfuscatedDN, obfuscatedDN);
-            pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskUsername, helpdeskDetailInfoBean.getUserInfoBean().getUsername());
+            pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskObfuscatedDN, obfuscatedDN);
+            pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskUsername, helpdeskDetailInfoBean.getUserInfoBean().getUsername());
         }
         }
 
 
         StatisticsManager.incrementStat(pwmRequest, Statistic.HELPDESK_USER_LOOKUP);
         StatisticsManager.incrementStat(pwmRequest, Statistic.HELPDESK_USER_LOOKUP);
-        pwmRequest.setAttribute(PwmRequest.Attribute.HelpdeskVerificationEnabled, !helpdeskProfile.readOptionalVerificationMethods().isEmpty());
+        pwmRequest.setAttribute(PwmRequestAttribute.HelpdeskVerificationEnabled, !helpdeskProfile.readOptionalVerificationMethods().isEmpty());
         pwmRequest.forwardToJsp(JspUrl.HELPDESK_DETAIL);
         pwmRequest.forwardToJsp(JspUrl.HELPDESK_DETAIL);
     }
     }
 
 
@@ -542,7 +543,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
     }
     }
 
 
     @ActionHandler(action = "unlockIntruder")
     @ActionHandler(action = "unlockIntruder")
-    private ProcessStatus restUnlockPassword(
+    private ProcessStatus restUnlockIntruder(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException, ServletException

+ 3 - 2
src/main/java/password/pwm/http/servlet/newuser/NewUserServlet.java

@@ -42,6 +42,7 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.ProcessStatus;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.http.bean.NewUserBean;
 import password.pwm.http.bean.NewUserBean;
@@ -233,7 +234,7 @@ public class NewUserServlet extends ControlledPwmServlet {
                         newUserBean.getNewUserForm()
                         newUserBean.getNewUserForm()
                 );
                 );
                 final String expandedText = macroMachine.expandMacros(newUserAgreementText);
                 final String expandedText = macroMachine.expandMacros(newUserAgreementText);
-                pwmRequest.setAttribute(PwmRequest.Attribute.AgreementText, expandedText);
+                pwmRequest.setAttribute(PwmRequestAttribute.AgreementText, expandedText);
                 pwmRequest.forwardToJsp(JspUrl.NEW_USER_AGREEMENT);
                 pwmRequest.forwardToJsp(JspUrl.NEW_USER_AGREEMENT);
                 return;
                 return;
             }
             }
@@ -600,7 +601,7 @@ public class NewUserServlet extends ControlledPwmServlet {
         {
         {
             final boolean showBack = !newUserBean.isUrlSpecifiedProfile()
             final boolean showBack = !newUserBean.isUrlSpecifiedProfile()
                     && pwmRequest.getConfig().getNewUserProfiles().keySet().size() > 1;
                     && pwmRequest.getConfig().getNewUserProfiles().keySet().size() > 1;
-            pwmRequest.setAttribute(PwmRequest.Attribute.NewUser_FormShowBackButton, showBack);
+            pwmRequest.setAttribute(PwmRequestAttribute.NewUser_FormShowBackButton, showBack);
         }
         }
 
 
         pwmRequest.forwardToJsp(JspUrl.NEW_USER);
         pwmRequest.forwardToJsp(JspUrl.NEW_USER);

+ 4 - 3
src/main/java/password/pwm/http/state/CryptoCookieBeanImpl.java

@@ -27,6 +27,7 @@ import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.PwmHttpResponseWrapper;
 import password.pwm.http.PwmHttpResponseWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.bean.PwmSessionBean;
 import password.pwm.http.bean.PwmSessionBean;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
@@ -64,7 +65,7 @@ class CryptoCookieBeanImpl implements SessionBeanProvider {
                 return cookieBean;
                 return cookieBean;
             }
             }
         } catch (PwmException e) {
         } catch (PwmException e) {
-            LOGGER.error(pwmRequest, "error reading existing " + cookieName + " cookie bean: " + e.getMessage());
+            LOGGER.debug(pwmRequest, "ignoring existing existing " + cookieName + " cookie bean due to error: " + e.getMessage());
         }
         }
 
 
         final E newBean = SessionStateService.newBean(sessionGuid, theClass);
         final E newBean = SessionStateService.newBean(sessionGuid, theClass);
@@ -142,10 +143,10 @@ class CryptoCookieBeanImpl implements SessionBeanProvider {
     }
     }
 
 
     private static Map<Class<? extends PwmSessionBean>,PwmSessionBean> getRequestBeanMap(final PwmRequest pwmRequest) {
     private static Map<Class<? extends PwmSessionBean>,PwmSessionBean> getRequestBeanMap(final PwmRequest pwmRequest) {
-        Serializable sessionBeans = pwmRequest.getAttribute(PwmRequest.Attribute.CookieBeanStorage);
+        Serializable sessionBeans = pwmRequest.getAttribute(PwmRequestAttribute.CookieBeanStorage);
         if (sessionBeans == null) {
         if (sessionBeans == null) {
             sessionBeans = new HashMap<>();
             sessionBeans = new HashMap<>();
-            pwmRequest.setAttribute(PwmRequest.Attribute.CookieBeanStorage, sessionBeans);
+            pwmRequest.setAttribute(PwmRequestAttribute.CookieBeanStorage, sessionBeans);
         }
         }
         return (Map<Class<? extends PwmSessionBean>,PwmSessionBean>)sessionBeans;
         return (Map<Class<? extends PwmSessionBean>,PwmSessionBean>)sessionBeans;
     }
     }

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

@@ -29,6 +29,7 @@ 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.http.PwmRequestAttribute;
 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 +65,7 @@ public class ErrorMessageTag extends PwmAbstractTag {
                 return EVAL_PAGE;
                 return EVAL_PAGE;
             }
             }
 
 
-            final ErrorInformation error = (ErrorInformation)pwmRequest.getAttribute(PwmRequest.Attribute.PwmErrorInfo);
+            final ErrorInformation error = (ErrorInformation)pwmRequest.getAttribute(PwmRequestAttribute.PwmErrorInfo);
 
 
             if (error != null) {
             if (error != null) {
                 final boolean allowHtml = Boolean.parseBoolean(pwmRequest.getConfig().readAppProperty(AppProperty.HTTP_ERRORS_ALLOW_HTML));
                 final boolean allowHtml = Boolean.parseBoolean(pwmRequest.getConfig().readAppProperty(AppProperty.HTTP_ERRORS_ALLOW_HTML));

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

@@ -23,6 +23,7 @@
 package password.pwm.http.tag;
 package password.pwm.http.tag;
 
 
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.macro.MacroMachine;
 
 
@@ -45,7 +46,7 @@ public class SuccessMessageTag extends PwmAbstractTag {
             final HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
             final HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
             final PwmRequest pwmRequest = PwmRequest.forRequest(req, (HttpServletResponse) pageContext.getResponse());
             final PwmRequest pwmRequest = PwmRequest.forRequest(req, (HttpServletResponse) pageContext.getResponse());
 
 
-            final String successMsg = (String)pwmRequest.getAttribute(PwmRequest.Attribute.SuccessMessage);
+            final String successMsg = (String)pwmRequest.getAttribute(PwmRequestAttribute.SuccessMessage);
 
 
             final String outputMsg;
             final String outputMsg;
             if (successMsg == null || successMsg.isEmpty()) {
             if (successMsg == null || successMsg.isEmpty()) {

+ 25 - 4
src/main/java/password/pwm/http/tag/conditional/PwmIfTest.java

@@ -28,6 +28,8 @@ import password.pwm.Permission;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.PwmEnvironment;
 import password.pwm.PwmEnvironment;
+import password.pwm.bean.PasswordStatus;
+import password.pwm.bean.UserInfoBean;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.ProfileType;
 import password.pwm.config.profile.ProfileType;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
@@ -41,7 +43,7 @@ import password.pwm.svc.PwmService;
 public enum PwmIfTest {
 public enum PwmIfTest {
     authenticated(new AuthenticatedTest()),
     authenticated(new AuthenticatedTest()),
     configurationOpen(new ConfigurationOpen()),
     configurationOpen(new ConfigurationOpen()),
-    endUserFunctionalityAvaiable(new EndUserFunctionalityTest()),
+    endUserFunctionalityAvailable(new EndUserFunctionalityTest()),
     showIcons(new BooleanAppPropertyTest(AppProperty.CLIENT_JSP_SHOW_ICONS)),
     showIcons(new BooleanAppPropertyTest(AppProperty.CLIENT_JSP_SHOW_ICONS)),
     showCancel(new BooleanPwmSettingTest(PwmSetting.DISPLAY_CANCEL_BUTTON)),
     showCancel(new BooleanPwmSettingTest(PwmSetting.DISPLAY_CANCEL_BUTTON)),
     maskTokenInput(new BooleanPwmSettingTest(PwmSetting.DISPLAY_MASK_TOKEN_FIELDS)),
     maskTokenInput(new BooleanPwmSettingTest(PwmSetting.DISPLAY_MASK_TOKEN_FIELDS)),
@@ -59,6 +61,7 @@ public enum PwmIfTest {
     shortcutsEnabled(new BooleanPwmSettingTest(PwmSetting.SHORTCUT_ENABLE)),
     shortcutsEnabled(new BooleanPwmSettingTest(PwmSetting.SHORTCUT_ENABLE)),
     peopleSearchEnabled(new BooleanPwmSettingTest(PwmSetting.PEOPLE_SEARCH_ENABLE)),
     peopleSearchEnabled(new BooleanPwmSettingTest(PwmSetting.PEOPLE_SEARCH_ENABLE)),
     orgChartEnabled(new OrgChartEnabled()),
     orgChartEnabled(new OrgChartEnabled()),
+    passwordExpired(new PasswordExpired()),
 
 
     accountInfoEnabled(new BooleanPwmSettingTest(PwmSetting.ACCOUNT_INFORMATION_ENABLED)),
     accountInfoEnabled(new BooleanPwmSettingTest(PwmSetting.ACCOUNT_INFORMATION_ENABLED)),
 
 
@@ -69,7 +72,7 @@ public enum PwmIfTest {
 
 
     updateProfileAvailable(new BooleanPwmSettingTest(PwmSetting.UPDATE_PROFILE_ENABLE), new ActorHasProfileTest(ProfileType.UpdateAttributes)),
     updateProfileAvailable(new BooleanPwmSettingTest(PwmSetting.UPDATE_PROFILE_ENABLE), new ActorHasProfileTest(ProfileType.UpdateAttributes)),
     helpdeskAvailable(new BooleanPwmSettingTest(PwmSetting.HELPDESK_ENABLE), new ActorHasProfileTest(ProfileType.Helpdesk)),
     helpdeskAvailable(new BooleanPwmSettingTest(PwmSetting.HELPDESK_ENABLE), new ActorHasProfileTest(ProfileType.Helpdesk)),
-    DeleteAccountAvailalable(new BooleanPwmSettingTest(PwmSetting.DELETE_ACCOUNT_ENABLE), new ActorHasProfileTest(ProfileType.DeleteAccount)),
+    DeleteAccountAvailable(new BooleanPwmSettingTest(PwmSetting.DELETE_ACCOUNT_ENABLE), new ActorHasProfileTest(ProfileType.DeleteAccount)),
     guestRegistrationAvailable(new BooleanPwmSettingTest(PwmSetting.GUEST_ENABLE), new BooleanPermissionTest(Permission.GUEST_REGISTRATION)),
     guestRegistrationAvailable(new BooleanPwmSettingTest(PwmSetting.GUEST_ENABLE), new BooleanPermissionTest(Permission.GUEST_REGISTRATION)),
 
 
     booleanSetting(new BooleanPwmSettingTest(null)),
     booleanSetting(new BooleanPwmSettingTest(null)),
@@ -81,7 +84,7 @@ public enum PwmIfTest {
     trialMode(new TrialModeTest()),
     trialMode(new TrialModeTest()),
     appliance(new EnvironmentFlagTest(PwmEnvironment.ApplicationFlag.Appliance)),
     appliance(new EnvironmentFlagTest(PwmEnvironment.ApplicationFlag.Appliance)),
 
 
-    healthWarningsVisible(new HealthWarningsVisibileTest()),
+    healthWarningsVisible(new HealthWarningsVisibleTest()),
 
 
     headerMenuIsVisible(new HeaderMenuIsVisibleTest()),
     headerMenuIsVisible(new HeaderMenuIsVisibleTest()),
 
 
@@ -304,7 +307,7 @@ public enum PwmIfTest {
     }
     }
 
 
 
 
-    private static class HealthWarningsVisibileTest implements Test {
+    private static class HealthWarningsVisibleTest implements Test {
         @Override
         @Override
         public boolean test(final PwmRequest pwmRequest, final PwmIfOptions options) throws ChaiUnavailableException, PwmUnrecoverableException {
         public boolean test(final PwmRequest pwmRequest, final PwmIfOptions options) throws ChaiUnavailableException, PwmUnrecoverableException {
             if (pwmRequest.isFlag(PwmRequestFlag.HIDE_HEADER_WARNINGS)) {
             if (pwmRequest.isFlag(PwmRequestFlag.HIDE_HEADER_WARNINGS)) {
@@ -346,6 +349,10 @@ public enum PwmIfTest {
                 return true;
                 return true;
             }
             }
 
 
+            if (pwmRequest.isForcedPageView()) {
+                return false;
+            }
+
             if (pwmRequest.isAuthenticated()) {
             if (pwmRequest.isAuthenticated()) {
                 if (pwmRequest.getPwmSession().getSessionManager().checkPermission(pwmRequest.getPwmApplication(), Permission.PWMADMIN)) {
                 if (pwmRequest.getPwmSession().getSessionManager().checkPermission(pwmRequest.getPwmApplication(), Permission.PWMADMIN)) {
                     return true;
                     return true;
@@ -412,4 +419,18 @@ public enum PwmIfTest {
             return new PeopleSearchConfiguration(pwmRequest.getConfig()).isOrgChartEnabled();
             return new PeopleSearchConfiguration(pwmRequest.getConfig()).isOrgChartEnabled();
         }
         }
     }
     }
+
+    private static class PasswordExpired implements Test {
+        @Override
+        public boolean test(final PwmRequest pwmRequest, final PwmIfOptions options) throws ChaiUnavailableException, PwmUnrecoverableException
+        {
+            if (!pwmRequest.isAuthenticated()) {
+                return false;
+            }
+
+            final UserInfoBean userInfoBean = pwmRequest.getPwmSession().getUserInfoBean();
+            final PasswordStatus passwordStatus = userInfoBean.getPasswordState();
+            return passwordStatus.isExpired() || passwordStatus.isPreExpired() || passwordStatus.isViolatesPolicy();
+        }
+    }
 }
 }

+ 58 - 6
src/main/java/password/pwm/ldap/LdapOperationsHelper.java

@@ -36,7 +36,6 @@ import com.novell.ldapchai.provider.ChaiSetting;
 import com.novell.ldapchai.util.SearchHelper;
 import com.novell.ldapchai.util.SearchHelper;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
-import password.pwm.PwmConstants;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
 import password.pwm.config.Configuration;
@@ -65,14 +64,16 @@ import password.pwm.util.secure.X509Utils;
 import javax.net.ssl.X509TrustManager;
 import javax.net.ssl.X509TrustManager;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
 import java.time.Instant;
 import java.time.Instant;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.Queue;
 import java.util.Set;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 
 
@@ -631,7 +632,7 @@ public class LdapOperationsHelper {
         return Collections.emptyMap();
         return Collections.emptyMap();
     }
     }
 
 
-    public static List<UserIdentity> readAllUsersFromLdap(
+    public static Iterator<UserIdentity> readAllUsersFromLdap(
             final PwmApplication pwmApplication,
             final PwmApplication pwmApplication,
             final SessionLabel sessionLabel,
             final SessionLabel sessionLabel,
             final String searchFilter,
             final String searchFilter,
@@ -663,11 +664,26 @@ public class LdapOperationsHelper {
                 searchConfiguration,
                 searchConfiguration,
                 maxResults,
                 maxResults,
                 Collections.emptyList(),
                 Collections.emptyList(),
-                PwmConstants.REPORTING_SESSION_LABEL
+                sessionLabel
 
 
         );
         );
-        LOGGER.debug(sessionLabel,"user search found " + searchResults.size() + " users for reporting");
-        return new ArrayList<>(searchResults.keySet());
+        LOGGER.debug(sessionLabel,"user search found " + searchResults.size() + " users");
+
+        final Queue<UserIdentity> tempQueue = new LinkedList<>(searchResults.keySet());
+
+        return new Iterator<UserIdentity>() {
+            @Override
+            public boolean hasNext()
+            {
+                return tempQueue.peek() != null;
+            }
+
+            @Override
+            public UserIdentity next()
+            {
+                return tempQueue.poll();
+            }
+        };
     }
     }
 
 
     public static Instant readPasswordExpirationTime(final ChaiUser theUser) {
     public static Instant readPasswordExpirationTime(final ChaiUser theUser) {
@@ -687,4 +703,40 @@ public class LdapOperationsHelper {
         return null;
         return null;
     }
     }
 
 
+    public static PasswordData readLdapPassword(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final UserIdentity userIdentity
+    )
+            throws ChaiUnavailableException,  PwmUnrecoverableException
+    {
+        if (userIdentity == null || userIdentity.getUserDN() == null || userIdentity.getUserDN().length() < 1) {
+            throw new NullPointerException("invalid user (null)");
+        }
+
+        final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
+        final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
+
+        // use chai (nmas) to retrieve user password
+        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.EDIRECTORY_READ_USER_PWD)) {
+            String currentPass = null;
+            try {
+                final String readPassword = chaiUser.readPassword();
+                if (readPassword != null && readPassword.length() > 0) {
+                    currentPass = readPassword;
+                    LOGGER.debug(sessionLabel,"successfully retrieved user's current password from ldap, now conducting standard authentication");
+                }
+            } catch (Exception e) {
+                LOGGER.debug(sessionLabel, "unable to retrieve user password from ldap: " + e.getMessage());
+            }
+
+            // actually do the authentication since we have user pw.
+            if (currentPass != null && currentPass.length() > 0) {
+                return new PasswordData(currentPass);
+            }
+        } else {
+            LOGGER.trace(sessionLabel, "skipping attempt to read user password, option disabled");
+        }
+        return null;
+    }
 }
 }

+ 6 - 3
src/main/java/password/pwm/ldap/UserStatusReader.java

@@ -52,6 +52,7 @@ import password.pwm.error.PwmDataValidationException;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
+import password.pwm.svc.PwmService;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PwmPasswordRuleValidator;
 import password.pwm.util.PwmPasswordRuleValidator;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -300,9 +301,11 @@ public class UserStatusReader {
         //populate OTP data
         //populate OTP data
         if (config.readSettingAsBoolean(PwmSetting.OTP_ENABLED)){
         if (config.readSettingAsBoolean(PwmSetting.OTP_ENABLED)){
             final OtpService otpService = pwmApplication.getOtpService();
             final OtpService otpService = pwmApplication.getOtpService();
-            final OTPUserRecord otpUserRecord = otpService.readOTPUserConfiguration(sessionLabel,userIdentity);
-            uiBean.setOtpUserRecord(otpUserRecord);
-            uiBean.setRequiresOtpConfig(checkIfOtpUpdateNeeded(uiBean, otpUserRecord));
+            if (otpService != null && otpService.status() ==  PwmService.STATUS.OPEN) {
+                final OTPUserRecord otpUserRecord = otpService.readOTPUserConfiguration(sessionLabel, userIdentity);
+                uiBean.setOtpUserRecord(otpUserRecord);
+                uiBean.setRequiresOtpConfig(checkIfOtpUpdateNeeded(uiBean, otpUserRecord));
+            }
         }
         }
 
 
         //populate cached password rule attributes
         //populate cached password rule attributes

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

@@ -274,6 +274,7 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
                 sessionLabel.getSrcHostname()
                 sessionLabel.getSrcHostname()
         );
         );
         pwmApplication.getAuditManager().submit(auditRecord);
         pwmApplication.getAuditManager().submit(auditRecord);
+        pwmApplication.getSessionTrackService().addRecentLogin(userIdentity);
 
 
         return authenticationResult;
         return authenticationResult;
     }
     }
@@ -351,40 +352,11 @@ class LDAPAuthenticationRequest implements AuthenticationRequest {
         }
         }
     }
     }
 
 
-
     private PasswordData learnUserPassword()
     private PasswordData learnUserPassword()
             throws ChaiUnavailableException,  PwmUnrecoverableException
             throws ChaiUnavailableException,  PwmUnrecoverableException
     {
     {
         log(PwmLogLevel.TRACE, "beginning auth processes for user with unknown password");
         log(PwmLogLevel.TRACE, "beginning auth processes for user with unknown password");
-
-        if (userIdentity == null || userIdentity.getUserDN() == null || userIdentity.getUserDN().length() < 1) {
-            throw new NullPointerException("invalid user (null)");
-        }
-
-        final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
-        final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
-
-        // use chai (nmas) to retrieve user password
-        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.EDIRECTORY_READ_USER_PWD)) {
-            String currentPass = null;
-            try {
-                final String readPassword = chaiUser.readPassword();
-                if (readPassword != null && readPassword.length() > 0) {
-                    currentPass = readPassword;
-                    log(PwmLogLevel.DEBUG, "successfully retrieved user's current password from ldap, now conducting standard authentication");
-                }
-            } catch (Exception e) {
-                log(PwmLogLevel.ERROR, "unable to retrieve user password from ldap: " + e.getMessage());
-            }
-
-            // actually do the authentication since we have user pw.
-            if (currentPass != null && currentPass.length() > 0) {
-                return new PasswordData(currentPass);
-            }
-        } else {
-            log(PwmLogLevel.TRACE, "skipping attempt to read user password, option disabled");
-        }
-        return null;
+        return LdapOperationsHelper.readLdapPassword(pwmApplication, sessionLabel, userIdentity);
     }
     }
 
 
     private PasswordData setTempUserPassword(
     private PasswordData setTempUserPassword(

+ 138 - 26
src/main/java/password/pwm/svc/pwnotify/PasswordExpireNotificationEngine.java

@@ -25,61 +25,74 @@ package password.pwm.svc.pwnotify;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
+import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.Getter;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
+import password.pwm.PwmConstants;
+import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
+import password.pwm.bean.UserInfoBean;
+import password.pwm.config.Configuration;
+import password.pwm.config.PwmSetting;
 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.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.ldap.LdapOperationsHelper;
 import password.pwm.ldap.LdapOperationsHelper;
+import password.pwm.ldap.UserStatusReader;
 import password.pwm.util.db.DatabaseException;
 import password.pwm.util.db.DatabaseException;
 import password.pwm.util.db.DatabaseTable;
 import password.pwm.util.db.DatabaseTable;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.TimeDuration;
+import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.macro.MacroMachine;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
 import java.time.Instant;
 import java.time.Instant;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.List;
-import java.util.Queue;
+import java.util.Locale;
 
 
 public class PasswordExpireNotificationEngine {
 public class PasswordExpireNotificationEngine {
 
 
+    private static final PwmLogger LOGGER = PwmLogger.forClass(PasswordExpireNotificationEngine.class);
+
+    private static final SessionLabel SESSION_LABEL = PwmConstants.PW_EXP_NOTICE_LABEL;
+
     private final Settings settings;
     private final Settings settings;
     private final PwmApplication pwmApplication;
     private final PwmApplication pwmApplication;
 
 
+
     public PasswordExpireNotificationEngine(final PwmApplication pwmApplication)
     public PasswordExpireNotificationEngine(final PwmApplication pwmApplication)
     {
     {
         this.pwmApplication = pwmApplication;
         this.pwmApplication = pwmApplication;
-        this.settings = new Settings();
+        this.settings = Settings.fromConfiguration(pwmApplication.getConfig());
     }
     }
 
 
     public void executeJob()
     public void executeJob()
             throws ChaiUnavailableException, ChaiOperationException, PwmOperationalException, PwmUnrecoverableException
             throws ChaiUnavailableException, ChaiOperationException, PwmOperationalException, PwmUnrecoverableException
     {
     {
-        final Queue<UserIdentity> workQueue;
-        {
-            final List<UserIdentity> users = LdapOperationsHelper.readAllUsersFromLdap(
-                    pwmApplication,
-                    null,
-                    null,
-                    1_000_000
-            );
-            workQueue = new LinkedList<>(users);
-        }
+        final Iterator<UserIdentity> workQueue = LdapOperationsHelper.readAllUsersFromLdap(
+                pwmApplication,
+                null,
+                null,
+                1_000_000
+        );
 
 
-        while (!workQueue.isEmpty()) {
-            final UserIdentity userIdentity = workQueue.poll();
-            final StoredState storedState = new DbStorage(pwmApplication).getStoredState(userIdentity, null);
+        while (workQueue.hasNext()) {
+            final UserIdentity userIdentity = workQueue.next();
+            processUserIdentity(userIdentity);
         }
         }
     }
     }
 
 
-    static void processUserIdentity(
-            final PwmApplication pwmApplication,
+    private void processUserIdentity(
             final UserIdentity userIdentity
             final UserIdentity userIdentity
-            )
+    )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         final ChaiUser theUser = pwmApplication.getProxiedChaiUser(userIdentity);
         final ChaiUser theUser = pwmApplication.getProxiedChaiUser(userIdentity);
@@ -87,28 +100,102 @@ public class PasswordExpireNotificationEngine {
         if (passwordExpirationTime == null || passwordExpirationTime.isBefore(Instant.now())) {
         if (passwordExpirationTime == null || passwordExpirationTime.isBefore(Instant.now())) {
             return;
             return;
         }
         }
+
+        final Instant previousNotice;
+        {
+            final DbStorage dbStorage = new DbStorage(pwmApplication);
+            final NotificationState storedState = dbStorage.readStoredState(userIdentity, SESSION_LABEL);
+            if (storedState == null || storedState.getExpireTime() == null || !storedState.getExpireTime().equals(passwordExpirationTime)) {
+                previousNotice = null;
+            } else {
+                previousNotice = storedState.getLastNotice();
+            }
+        }
+        final int currentDayInterval = daysUntilInstant(passwordExpirationTime);
+        final int previousDays = previousNotice == null
+                ? Integer.MAX_VALUE
+                : daysUntilInstant(previousNotice);
+
+        int nextDayInterval = -1;
+        for (final int configuredDayInterval : settings.getDayIntervals()) {
+            if (currentDayInterval <= configuredDayInterval) {
+                if  (configuredDayInterval != previousDays) {
+                    nextDayInterval = configuredDayInterval;
+                }
+            }
+        }
+
+        if (nextDayInterval < 1) {
+            return;
+        }
+
+        System.out.println(userIdentity + " next=" +nextDayInterval);
+        {
+            final DbStorage dbStorage = new DbStorage(pwmApplication);
+            dbStorage.writeStoredState(userIdentity, SESSION_LABEL, new NotificationState(passwordExpirationTime, Instant.now()));
+        }
+
+        sendNoticeEmail(userIdentity);
+    }
+
+    void sendNoticeEmail(final UserIdentity userIdentity)
+            throws PwmUnrecoverableException
+    {
+        final Locale userLocale = PwmConstants.DEFAULT_LOCALE;
+        final EmailItemBean emailItemBean = pwmApplication.getConfig().readSettingAsEmail(
+                PwmSetting.EMAIL_PW_EXPIRATION_NOTICE,
+                userLocale
+        );
+        final MacroMachine macroMachine = MacroMachine.forUser(pwmApplication, userLocale, SESSION_LABEL, userIdentity);
+        final UserStatusReader userStatusReader = new UserStatusReader(pwmApplication, SESSION_LABEL);
+        final UserInfoBean userInfoBean = userStatusReader.populateUserInfoBean(userLocale, userIdentity);
+        pwmApplication.getEmailQueue().submitEmail(emailItemBean, userInfoBean, macroMachine);
+    }
+
+    static int daysUntilInstant(final Instant instant) {
+        final TimeDuration timeDuration = TimeDuration.fromCurrent(instant);
+        return (int)timeDuration.getTotalDays();
+
     }
     }
 
 
     @Getter
     @Getter
     static class Settings implements Serializable {
     static class Settings implements Serializable {
-        private List<Integer> dayIntervals;
+        private List<Integer> dayIntervals = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(new Integer[]{8,5,3})));
+
+        static Settings fromConfiguration(final Configuration configuration)
+        {
+            final Settings settings = new Settings();
+
+            final List<Integer> tempList = new ArrayList<>(Arrays.asList(new Integer[]{8,5,3}));
+            Collections.sort(tempList);
+            Collections.reverse(tempList);
+            settings.dayIntervals = Collections.unmodifiableList(tempList);
+
+            return settings;
+        }
     }
     }
 
 
     @Getter
     @Getter
-    static class StoredState implements Serializable {
-        private Instant lastSendTimestamp;
+    @AllArgsConstructor
+    static class NotificationState implements Serializable {
+        private Instant expireTime;
+        private Instant lastNotice;
     }
     }
 
 
     interface PwExpireStorageEngine {
     interface PwExpireStorageEngine {
 
 
-        StoredState getStoredState(
+        NotificationState readStoredState(
                 UserIdentity userIdentity,
                 UserIdentity userIdentity,
                 SessionLabel sessionLabel
                 SessionLabel sessionLabel
         )
         )
                 throws PwmUnrecoverableException;
                 throws PwmUnrecoverableException;
+
+        void writeStoredState(UserIdentity userIdentity, SessionLabel sessionLabel, NotificationState notificationState) throws PwmUnrecoverableException;
+
     }
     }
 
 
     static class DbStorage implements PwExpireStorageEngine {
     static class DbStorage implements PwExpireStorageEngine {
+        private static final DatabaseTable TABLE = DatabaseTable.PW_NOTIFY;
         private final PwmApplication pwmApplication;
         private final PwmApplication pwmApplication;
 
 
         DbStorage(final PwmApplication pwmApplication)
         DbStorage(final PwmApplication pwmApplication)
@@ -117,7 +204,7 @@ public class PasswordExpireNotificationEngine {
         }
         }
 
 
         @Override
         @Override
-        public StoredState getStoredState(
+        public NotificationState readStoredState(
                 final UserIdentity userIdentity,
                 final UserIdentity userIdentity,
                 final SessionLabel sessionLabel
                 final SessionLabel sessionLabel
         )
         )
@@ -135,12 +222,37 @@ public class PasswordExpireNotificationEngine {
 
 
             final String rawDbValue;
             final String rawDbValue;
             try {
             try {
-                rawDbValue = pwmApplication.getDatabaseAccessor().get(DatabaseTable.PW_NOTIFY, guid);
+                rawDbValue = pwmApplication.getDatabaseAccessor().get(TABLE, guid);
             } catch (DatabaseException e) {
             } catch (DatabaseException e) {
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DB_UNAVAILABLE,e.getMessage()));
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DB_UNAVAILABLE,e.getMessage()));
             }
             }
 
 
-            return JsonUtil.deserialize(rawDbValue, StoredState.class);
+            return JsonUtil.deserialize(rawDbValue, NotificationState.class);
+        }
+
+        public void writeStoredState(
+                final UserIdentity userIdentity,
+                final SessionLabel sessionLabel,
+                final NotificationState notificationState
+        )
+                throws PwmUnrecoverableException
+        {
+            final String guid;
+            try {
+                guid = LdapOperationsHelper.readLdapGuidValue(pwmApplication, sessionLabel, userIdentity, true);
+            } catch (ChaiUnavailableException e) {
+                throw new PwmUnrecoverableException(PwmUnrecoverableException.fromChaiException(e).getErrorInformation());
+            }
+            if (StringUtil.isEmpty(guid)) {
+                throw new PwmUnrecoverableException(PwmError.ERROR_MISSING_GUID);
+            }
+
+            final String rawDbValue = JsonUtil.serialize(notificationState);
+            try {
+                pwmApplication.getDatabaseAccessor().put(TABLE, guid, rawDbValue);
+            } catch (DatabaseException e) {
+                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DB_UNAVAILABLE,e.getMessage()));
+            }
         }
         }
     }
     }
 }
 }

+ 12 - 15
src/main/java/password/pwm/svc/report/ReportService.java

@@ -59,6 +59,7 @@ import java.math.MathContext;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.List;
 import java.util.Queue;
 import java.util.Queue;
@@ -388,17 +389,13 @@ public class ReportService implements PwmService {
             resetJobStatus();
             resetJobStatus();
             clearWorkQueue();
             clearWorkQueue();
 
 
-            final Queue<UserIdentity> memQueue;
-            {
-                final List<UserIdentity> userIdentities = LdapOperationsHelper.readAllUsersFromLdap(
-                        pwmApplication,
-                        PwmConstants.REPORTING_SESSION_LABEL,
-                        settings.getSearchFilter(),
-                        settings.getMaxSearchSize()
-                );
-                Collections.shuffle(userIdentities);
-                memQueue = new LinkedList<>(userIdentities);
-            }
+            final Iterator<UserIdentity> memQueue = LdapOperationsHelper.readAllUsersFromLdap(
+                    pwmApplication,
+                    PwmConstants.REPORTING_SESSION_LABEL,
+                    settings.getSearchFilter(),
+                    settings.getMaxSearchSize()
+            );
+
 
 
             LOGGER.trace("completed ldap search process, transferring search results to work queue");
             LOGGER.trace("completed ldap search process, transferring search results to work queue");
 
 
@@ -410,12 +407,12 @@ public class ReportService implements PwmService {
                             .createSettings()
                             .createSettings()
             );
             );
 
 
-            while (status == STATUS.OPEN && !cancelFlag && !memQueue.isEmpty()) {
+            while (status == STATUS.OPEN && !cancelFlag && memQueue.hasNext()) {
                 final Instant loopStart = Instant.now();
                 final Instant loopStart = Instant.now();
                 final List<String> bufferList = new ArrayList<>();
                 final List<String> bufferList = new ArrayList<>();
                 final int loopCount = transactionCalculator.getTransactionSize();
                 final int loopCount = transactionCalculator.getTransactionSize();
-                for (int i = 0; i < loopCount && !memQueue.isEmpty(); i++) {
-                    bufferList.add(memQueue.poll().toDelimitedKey());
+                for (int i = 0; i < loopCount && memQueue.hasNext(); i++) {
+                    bufferList.add(memQueue.next().toDelimitedKey());
                 }
                 }
                 dnQueue.addAll(bufferList);
                 dnQueue.addAll(bufferList);
                 transactionCalculator.recordLastTransactionDuration(TimeDuration.fromCurrent(loopStart));
                 transactionCalculator.recordLastTransactionDuration(TimeDuration.fromCurrent(loopStart));
@@ -603,7 +600,7 @@ public class ReportService implements PwmService {
                         final TimeDuration progressDuration = TimeDuration.fromCurrent(startTime);
                         final TimeDuration progressDuration = TimeDuration.fromCurrent(startTime);
                         LOGGER.trace(PwmConstants.REPORTING_SESSION_LABEL,
                         LOGGER.trace(PwmConstants.REPORTING_SESSION_LABEL,
                                 "cache review process in progress, examined " + examinedRecords
                                 "cache review process in progress, examined " + examinedRecords
-                                + " in " + progressDuration.asCompactString());
+                                        + " in " + progressDuration.asCompactString());
                         lastLogOutputTime = Instant.now();
                         lastLogOutputTime = Instant.now();
                     }
                     }
                 }
                 }

+ 17 - 1
src/main/java/password/pwm/svc/sessiontrack/SessionTrackService.java

@@ -22,9 +22,12 @@
 
 
 package password.pwm.svc.sessiontrack;
 package password.pwm.svc.sessiontrack;
 
 
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.LoginInfoBean;
+import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.bean.pub.SessionStateInfoBean;
 import password.pwm.bean.pub.SessionStateInfoBean;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmException;
@@ -33,6 +36,7 @@ import password.pwm.http.PwmSession;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashMap;
@@ -48,6 +52,10 @@ public class SessionTrackService implements PwmService {
 
 
     private final transient Map<PwmSession,Boolean> pwmSessions = new ConcurrentHashMap<>();
     private final transient Map<PwmSession,Boolean> pwmSessions = new ConcurrentHashMap<>();
 
 
+    private final Cache<UserIdentity,Object> recentLoginCache = Caffeine.newBuilder()
+            .maximumSize(10)
+            .build();
+
     private PwmApplication pwmApplication;
     private PwmApplication pwmApplication;
 
 
     @Override
     @Override
@@ -188,7 +196,7 @@ public class SessionTrackService implements PwmService {
         sessionStateInfoBean.setCreateTime(loopSession.getSessionStateBean().getSessionCreationTime());
         sessionStateInfoBean.setCreateTime(loopSession.getSessionStateBean().getSessionCreationTime());
         sessionStateInfoBean.setLastTime(loopSession.getSessionStateBean().getSessionLastAccessedTime());
         sessionStateInfoBean.setLastTime(loopSession.getSessionStateBean().getSessionLastAccessedTime());
         sessionStateInfoBean.setIdle(loopSession.getIdleTime().asCompactString());
         sessionStateInfoBean.setIdle(loopSession.getIdleTime().asCompactString());
-        sessionStateInfoBean.setLocale(loopSsBean.getLocale() == null ? null : loopSsBean.getLocale());
+        sessionStateInfoBean.setLocale(loopSsBean.getLocale());
         sessionStateInfoBean.setSrcAddress(loopSsBean.getSrcAddress());
         sessionStateInfoBean.setSrcAddress(loopSsBean.getSrcAddress());
         sessionStateInfoBean.setSrcHost(loopSsBean.getSrcHostname());
         sessionStateInfoBean.setSrcHost(loopSsBean.getSrcHostname());
         sessionStateInfoBean.setLastUrl(loopSsBean.getLastRequestURL());
         sessionStateInfoBean.setLastUrl(loopSsBean.getLastRequestURL());
@@ -207,4 +215,12 @@ public class SessionTrackService implements PwmService {
     public int sessionCount() {
     public int sessionCount() {
         return currentValidSessionSet().size();
         return currentValidSessionSet().size();
     }
     }
+
+    public void addRecentLogin(final UserIdentity userIdentity) {
+        recentLoginCache.put(userIdentity,this);
+    }
+
+    public List<UserIdentity> getRecentLogins() {
+        return Collections.unmodifiableList(new ArrayList<>(recentLoginCache.asMap().keySet()));
+    }
 }
 }

+ 4 - 3
src/main/java/password/pwm/util/CaptchaUtility.java

@@ -36,6 +36,7 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.http.client.PwmHttpClient;
 import password.pwm.http.client.PwmHttpClient;
 import password.pwm.http.client.PwmHttpClientRequest;
 import password.pwm.http.client.PwmHttpClientRequest;
@@ -256,15 +257,15 @@ public class CaptchaUtility {
         StatisticsManager.incrementStat(pwmRequest, Statistic.CAPTCHA_PRESENTATIONS);
         StatisticsManager.incrementStat(pwmRequest, Statistic.CAPTCHA_PRESENTATIONS);
 
 
         final String reCaptchaPublicKey = pwmRequest.getConfig().readSettingAsString(PwmSetting.RECAPTCHA_KEY_PUBLIC);
         final String reCaptchaPublicKey = pwmRequest.getConfig().readSettingAsString(PwmSetting.RECAPTCHA_KEY_PUBLIC);
-        pwmRequest.setAttribute(PwmRequest.Attribute.CaptchaPublicKey, reCaptchaPublicKey);
+        pwmRequest.setAttribute(PwmRequestAttribute.CaptchaPublicKey, reCaptchaPublicKey);
         {
         {
             final String urlValue = pwmRequest.getConfig().readAppProperty(AppProperty.RECAPTCHA_CLIENT_JS_URL);
             final String urlValue = pwmRequest.getConfig().readAppProperty(AppProperty.RECAPTCHA_CLIENT_JS_URL);
-            pwmRequest.setAttribute(PwmRequest.Attribute.CaptchaClientUrl, urlValue);
+            pwmRequest.setAttribute(PwmRequestAttribute.CaptchaClientUrl, urlValue);
         }
         }
         {
         {
             final String configuredUrl =pwmRequest.getConfig().readAppProperty(AppProperty.RECAPTCHA_CLIENT_IFRAME_URL);
             final String configuredUrl =pwmRequest.getConfig().readAppProperty(AppProperty.RECAPTCHA_CLIENT_IFRAME_URL);
             final String url = configuredUrl + "?k=" + reCaptchaPublicKey + "&hl=" + pwmRequest.getLocale().toString();
             final String url = configuredUrl + "?k=" + reCaptchaPublicKey + "&hl=" + pwmRequest.getLocale().toString();
-            pwmRequest.setAttribute(PwmRequest.Attribute.CaptchaIframeUrl,url);
+            pwmRequest.setAttribute(PwmRequestAttribute.CaptchaIframeUrl,url);
         }
         }
     }
     }
 
 

+ 1 - 1
src/main/java/password/pwm/util/LocaleHelper.java

@@ -131,7 +131,7 @@ public class LocaleHelper {
                 returnValue = bundle.getString(key);
                 returnValue = bundle.getString(key);
             } catch (MissingResourceException e) {
             } catch (MissingResourceException e) {
                 final String errorMsg = "missing key '" + key + "' for " + bundleClass.getName();
                 final String errorMsg = "missing key '" + key + "' for " + bundleClass.getName();
-                if (config.isDevDebugMode()) {
+                if (config != null && config.isDevDebugMode()) {
                     LOGGER.warn(errorMsg);
                     LOGGER.warn(errorMsg);
                 }
                 }
                 returnValue = key;
                 returnValue = key;

+ 2 - 1
src/main/java/password/pwm/util/cli/MainClass.java

@@ -37,7 +37,6 @@ import password.pwm.config.stored.ConfigurationReader;
 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.FileSystemUtility;
 import password.pwm.util.cli.commands.ClearResponsesCommand;
 import password.pwm.util.cli.commands.ClearResponsesCommand;
 import password.pwm.util.cli.commands.CliCommand;
 import password.pwm.util.cli.commands.CliCommand;
 import password.pwm.util.cli.commands.ConfigDeleteCommand;
 import password.pwm.util.cli.commands.ConfigDeleteCommand;
@@ -64,6 +63,7 @@ import password.pwm.util.cli.commands.ShellCommand;
 import password.pwm.util.cli.commands.TokenInfoCommand;
 import password.pwm.util.cli.commands.TokenInfoCommand;
 import password.pwm.util.cli.commands.UserReportCommand;
 import password.pwm.util.cli.commands.UserReportCommand;
 import password.pwm.util.cli.commands.VersionCommand;
 import password.pwm.util.cli.commands.VersionCommand;
+import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.localdb.LocalDBException;
@@ -123,6 +123,7 @@ public class MainClass {
         commandList.add(new ShellCommand());
         commandList.add(new ShellCommand());
         commandList.add(new ConfigResetHttpsCommand());
         commandList.add(new ConfigResetHttpsCommand());
         commandList.add(new HelpCommand());
         commandList.add(new HelpCommand());
+        //commandList.add(new PasswordExpireNotificationCommand());
 
 
         final Map<String,CliCommand> sortedMap = new TreeMap<>();
         final Map<String,CliCommand> sortedMap = new TreeMap<>();
         for (final CliCommand command : commandList) {
         for (final CliCommand command : commandList) {

+ 52 - 0
src/main/java/password/pwm/util/cli/commands/PasswordExpireNotificationCommand.java

@@ -0,0 +1,52 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2017 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.cli.commands;
+
+import password.pwm.PwmApplication;
+import password.pwm.svc.pwnotify.PasswordExpireNotificationEngine;
+import password.pwm.util.cli.CliParameters;
+
+import java.util.Collections;
+
+public class PasswordExpireNotificationCommand extends AbstractCliCommand {
+    public void doCommand()
+            throws Exception
+    {
+        final PwmApplication pwmApplication = cliEnvironment.getPwmApplication();
+        final PasswordExpireNotificationEngine engine = new PasswordExpireNotificationEngine(pwmApplication);
+        engine.executeJob();
+    }
+
+    @Override
+    public CliParameters getCliParameters()
+    {
+        final CliParameters cliParameters = new CliParameters();
+        cliParameters.commandName = "PasswordExpirationNotification";
+        cliParameters.description = "Run the password expiration notification batch process";
+        cliParameters.options = Collections.emptyList();
+        cliParameters.needsPwmApplication = true;
+        cliParameters.needsLocalDB = true;
+        cliParameters.readOnly = false;
+        return cliParameters;
+    }
+}

+ 2 - 0
src/main/java/password/pwm/util/db/DatabaseAccessor.java

@@ -86,4 +86,6 @@ public interface DatabaseAccessor {
     @DbOperation
     @DbOperation
     int size(DatabaseTable table) throws
     int size(DatabaseTable table) throws
             DatabaseException;
             DatabaseException;
+
+    boolean isMasterServer();
 }
 }

+ 21 - 0
src/main/java/password/pwm/util/db/DatabaseAccessorImpl.java

@@ -61,6 +61,8 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Properties;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 
 /**
 /**
  * @author Jason D. Rivard
  * @author Jason D. Rivard
@@ -88,6 +90,9 @@ public class DatabaseAccessorImpl implements PwmService, DatabaseAccessor {
 
 
     private JDBCDriverLoader.DriverLoader jdbcDriverLoader;
     private JDBCDriverLoader.DriverLoader jdbcDriverLoader;
 
 
+    private ExecutorService masterStatusService;
+    private final AtomicBoolean masterStatus = new AtomicBoolean(false);
+
 // --------------------------- CONSTRUCTORS ---------------------------
 // --------------------------- CONSTRUCTORS ---------------------------
 
 
     public DatabaseAccessorImpl()
     public DatabaseAccessorImpl()
@@ -144,6 +149,8 @@ public class DatabaseAccessorImpl implements PwmService, DatabaseAccessor {
             status = PwmService.STATUS.CLOSED;
             status = PwmService.STATUS.CLOSED;
             LOGGER.debug("skipping database connection open, no connection parameters configured");
             LOGGER.debug("skipping database connection open, no connection parameters configured");
         }
         }
+
+        masterStatusService = JavaHelper.makeSingleThreadExecutorService(pwmApplication, DatabaseAccessorImpl.class);
     }
     }
 
 
     public List<HealthRecord> healthCheck() {
     public List<HealthRecord> healthCheck() {
@@ -728,4 +735,18 @@ public class DatabaseAccessorImpl implements PwmService, DatabaseAccessor {
         }
         }
         return Collections.emptyMap();
         return Collections.emptyMap();
     }
     }
+
+    @Override
+    public boolean isMasterServer()
+    {
+        return false;
+    }
+
+    private class MasterCheckTask implements Runnable {
+        @Override
+        public void run()
+        {
+
+        }
+    }
 }
 }

+ 4 - 0
src/main/java/password/pwm/util/java/TimeDuration.java

@@ -194,6 +194,10 @@ public class TimeDuration implements Comparable, Serializable {
         return ms / (60 * 1000);
         return ms / (60 * 1000);
     }
     }
 
 
+    public long getTotalDays() {
+        return ms / (60 * 1000 * 60 * 24);
+    }
+
     public String asCompactString() {
     public String asCompactString() {
         final StringBuilder sb = new StringBuilder();
         final StringBuilder sb = new StringBuilder();
 
 

+ 2 - 2
src/main/java/password/pwm/util/macro/StandardMacros.java

@@ -671,7 +671,7 @@ public abstract class StandardMacros {
         {
         {
             final UserInfoBean userInfoBean = macroRequestInfo.getUserInfoBean();
             final UserInfoBean userInfoBean = macroRequestInfo.getUserInfoBean();
             if (userInfoBean != null && userInfoBean.getOtpUserRecord() != null && userInfoBean.getOtpUserRecord().getTimestamp() != null) {
             if (userInfoBean != null && userInfoBean.getOtpUserRecord() != null && userInfoBean.getOtpUserRecord().getTimestamp() != null) {
-                return PwmConstants.DEFAULT_DATETIME_FORMAT.format(userInfoBean.getOtpUserRecord().getTimestamp());
+                return JavaHelper.toIsoDate(userInfoBean.getOtpUserRecord().getTimestamp());
             }
             }
             return "";
             return "";
         }
         }
@@ -688,7 +688,7 @@ public abstract class StandardMacros {
         {
         {
             final UserInfoBean userInfoBean = macroRequestInfo.getUserInfoBean();
             final UserInfoBean userInfoBean = macroRequestInfo.getUserInfoBean();
             if (userInfoBean != null && userInfoBean.getResponseInfoBean() != null && userInfoBean.getResponseInfoBean().getTimestamp() != null) {
             if (userInfoBean != null && userInfoBean.getResponseInfoBean() != null && userInfoBean.getResponseInfoBean().getTimestamp() != null) {
-                return PwmConstants.DEFAULT_DATETIME_FORMAT.format(userInfoBean.getResponseInfoBean().getTimestamp());
+                return JavaHelper.toIsoDate(userInfoBean.getResponseInfoBean().getTimestamp());
             }
             }
             return "";
             return "";
         }
         }

+ 7 - 8
src/main/java/password/pwm/util/operations/PasswordUtility.java

@@ -38,7 +38,6 @@ import com.novell.ldapchai.util.ChaiUtility;
 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.LoginInfoBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.PasswordStatus;
 import password.pwm.bean.PasswordStatus;
@@ -761,7 +760,7 @@ public class PasswordUtility {
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        final long startTime = System.currentTimeMillis();
+        final Instant startTime = Instant.now();
         final PasswordPolicySource ppSource = PasswordPolicySource.valueOf(pwmApplication.getConfig().readSettingAsString(PwmSetting.PASSWORD_POLICY_SOURCE));
         final PasswordPolicySource ppSource = PasswordPolicySource.valueOf(pwmApplication.getConfig().readSettingAsString(PwmSetting.PASSWORD_POLICY_SOURCE));
 
 
         final PwmPasswordPolicy returnPolicy;
         final PwmPasswordPolicy returnPolicy;
@@ -791,7 +790,7 @@ public class PasswordUtility {
         return returnPolicy;
         return returnPolicy;
     }
     }
 
 
-    protected static PwmPasswordPolicy determineConfiguredPolicyProfileForUser(
+    public static PwmPasswordPolicy determineConfiguredPolicyProfileForUser(
             final PwmApplication pwmApplication,
             final PwmApplication pwmApplication,
             final SessionLabel pwmSession,
             final SessionLabel pwmSession,
             final UserIdentity userIdentity,
             final UserIdentity userIdentity,
@@ -1103,8 +1102,8 @@ public class PasswordUtility {
                 if (oracleDS_PrePasswordAllowChangeTime != null && !oracleDS_PrePasswordAllowChangeTime.isEmpty()) {
                 if (oracleDS_PrePasswordAllowChangeTime != null && !oracleDS_PrePasswordAllowChangeTime.isEmpty()) {
                     final Date date = OracleDSEntries.convertZuluToDate(oracleDS_PrePasswordAllowChangeTime);
                     final Date date = OracleDSEntries.convertZuluToDate(oracleDS_PrePasswordAllowChangeTime);
                     if (new Date().before(date)) {
                     if (new Date().before(date)) {
-                        LOGGER.debug("discovered oracleds allowed change time is set to: " + PwmConstants.DEFAULT_DATETIME_FORMAT.format(date) + ", won't permit password change");
-                        final String errorMsg = "change not permitted until " + PwmConstants.DEFAULT_DATETIME_FORMAT.format(date);
+                        LOGGER.debug("discovered oracleds allowed change time is set to: " + JavaHelper.toIsoDate(date) + ", won't permit password change");
+                        final String errorMsg = "change not permitted until " + JavaHelper.toIsoDate(date);
                         final ErrorInformation errorInformation = new ErrorInformation(PwmError.PASSWORD_TOO_SOON, errorMsg);
                         final ErrorInformation errorInformation = new ErrorInformation(PwmError.PASSWORD_TOO_SOON, errorMsg);
                         throw new PwmUnrecoverableException(errorInformation);
                         throw new PwmUnrecoverableException(errorInformation);
                     }
                     }
@@ -1132,7 +1131,7 @@ public class PasswordUtility {
 
 
         final TimeDuration passwordAge = TimeDuration.fromCurrent(lastModified);
         final TimeDuration passwordAge = TimeDuration.fromCurrent(lastModified);
         LOGGER.trace(sessionLabel, "beginning check for minimum lifetime, lastModified="
         LOGGER.trace(sessionLabel, "beginning check for minimum lifetime, lastModified="
-                + PwmConstants.DEFAULT_DATETIME_FORMAT.format(lastModified)
+                + JavaHelper.toIsoDate(lastModified)
                 + ", minimumLifetimeSeconds=" + minimumLifetime.asCompactString()
                 + ", minimumLifetimeSeconds=" + minimumLifetime.asCompactString()
                 + ", passwordAge=" + passwordAge.asCompactString());
                 + ", passwordAge=" + passwordAge.asCompactString());
 
 
@@ -1155,11 +1154,11 @@ public class PasswordUtility {
 
 
         final Instant allowedChangeDate = Instant.ofEpochMilli(lastModified.toEpochMilli() + minimumLifetime.getTotalMilliseconds());
         final Instant allowedChangeDate = Instant.ofEpochMilli(lastModified.toEpochMilli() + minimumLifetime.getTotalMilliseconds());
         final String errorMsg = "last password change was at "
         final String errorMsg = "last password change was at "
-                + PwmConstants.DEFAULT_DATETIME_FORMAT.format(lastModified)
+                + JavaHelper.toIsoDate(lastModified)
                 + " and is too recent (" + passwordAge.asCompactString()
                 + " and is too recent (" + passwordAge.asCompactString()
                 + " ago), password cannot be changed within minimum lifetime of "
                 + " ago), password cannot be changed within minimum lifetime of "
                 + minimumLifetime.asCompactString()
                 + minimumLifetime.asCompactString()
-                + ", next eligible time to change is after " + PwmConstants.DEFAULT_DATETIME_FORMAT.format(allowedChangeDate);
+                + ", next eligible time to change is after " + JavaHelper.toIsoDate(allowedChangeDate);
 
 
         final ErrorInformation errorInformation = new ErrorInformation(PwmError.PASSWORD_TOO_SOON,errorMsg);
         final ErrorInformation errorInformation = new ErrorInformation(PwmError.PASSWORD_TOO_SOON,errorMsg);
         throw new PwmOperationalException(errorInformation);
         throw new PwmOperationalException(errorInformation);

+ 1 - 1
src/main/java/password/pwm/ws/server/rest/RestAppDataServer.java

@@ -392,7 +392,7 @@ public class RestAppDataServer extends AbstractRestServer {
         final String contextPath = request.getContextPath();
         final String contextPath = request.getContextPath();
         settingMap.put("url-context", contextPath);
         settingMap.put("url-context", contextPath);
         settingMap.put("url-logout", contextPath + PwmServletDefinition.Logout.servletUrl());
         settingMap.put("url-logout", contextPath + PwmServletDefinition.Logout.servletUrl());
-        settingMap.put("url-command", contextPath + PwmServletDefinition.Command.servletUrl());
+        settingMap.put("url-command", contextPath + PwmServletDefinition.PublicCommand.servletUrl());
         settingMap.put("url-resources", contextPath + "/public/resources" + pwmApplication.getResourceServletService().getResourceNonce());
         settingMap.put("url-resources", contextPath + "/public/resources" + pwmApplication.getResourceServletService().getResourceNonce());
         settingMap.put("url-restservice", contextPath + "/public/rest");
         settingMap.put("url-restservice", contextPath + "/public/rest");
 
 

+ 7 - 0
src/main/resources/password/pwm/AppProperty.properties

@@ -62,7 +62,14 @@ configGuide.idleTimeoutSeconds=3600
 configManager.zipDebug.maxLogLines=100000
 configManager.zipDebug.maxLogLines=100000
 configManager.zipDebug.maxLogSeconds=30
 configManager.zipDebug.maxLogSeconds=30
 db.jdbcLoadStrategy=AppPathFileLoader,Classpath
 db.jdbcLoadStrategy=AppPathFileLoader,Classpath
+download.filename.statistics.csv=Statistics.csv
+download.filename.reportSummary.csv=UserReportSummary.csv
+download.filename.reportRecords.csv=UserReportRecords.csv
+download.filename.auditRecords.csv=AuditRecords.csv
+download.filename.ldapPermission.csv=LDAPPermissionRecommendations.csv
+download.filename.userDebug.json=userDebug.json
 form.email.regexTest=^[_+a-zA-Z0-9-]+(\\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*$
 form.email.regexTest=^[_+a-zA-Z0-9-]+(\\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*$
+healthCheck.enabled=true
 healthCheck.nominalCheckIntervalSeconds=60
 healthCheck.nominalCheckIntervalSeconds=60
 healthCheck.minimumCheckIntervalSeconds=10
 healthCheck.minimumCheckIntervalSeconds=10
 healthCheck.maximumRecordAgeSeconds=300
 healthCheck.maximumRecordAgeSeconds=300

+ 4 - 3
src/main/webapp/WEB-INF/jsp/accountinformation.jsp

@@ -27,7 +27,7 @@
 <%@ page import="password.pwm.config.PwmSetting" %>
 <%@ page import="password.pwm.config.PwmSetting" %>
 <%@ page import="password.pwm.config.option.ViewStatusFields" %>
 <%@ page import="password.pwm.config.option.ViewStatusFields" %>
 <%@ page import="password.pwm.http.JspUtility" %>
 <%@ page import="password.pwm.http.JspUtility" %>
-<%@ page import="password.pwm.http.servlet.CommandServlet" %>
+<%@ page import="password.pwm.http.servlet.command.CommandServlet" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.i18n.Display" %>
 <%@ page import="password.pwm.i18n.Display" %>
 <%@ page import="password.pwm.svc.event.UserAuditRecord" %>
 <%@ page import="password.pwm.svc.event.UserAuditRecord" %>
@@ -40,6 +40,7 @@
 <%@ page import="java.util.Locale" %>
 <%@ page import="java.util.Locale" %>
 <%@ page import="java.util.Map" %>
 <%@ page import="java.util.Map" %>
 <%@ page import="java.util.Set" %>
 <%@ page import="java.util.Set" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
@@ -336,7 +337,7 @@
         <% } %>
         <% } %>
     </table>
     </table>
 </div>
 </div>
-<% final Map<FormConfiguration, List<String>> userFormData = (Map<FormConfiguration,List<String>>)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.FormData); %>
+<% final Map<FormConfiguration, List<String>> userFormData = (Map<FormConfiguration,List<String>>)JspUtility.getAttribute(pageContext, PwmRequestAttribute.FormData); %>
 <% if (userFormData != null && !userFormData.isEmpty()) { %>
 <% if (userFormData != null && !userFormData.isEmpty()) { %>
 <div data-dojo-type="dijit.layout.ContentPane" id="UserData" title="<pwm:display key="<%=Display.Title_UserData.toString()%>"/>" class="tabContent">
 <div data-dojo-type="dijit.layout.ContentPane" id="UserData" title="<pwm:display key="<%=Display.Title_UserData.toString()%>"/>" class="tabContent">
     <div style="max-height: 400px; overflow: auto;">
     <div style="max-height: 400px; overflow: auto;">
@@ -402,7 +403,7 @@
 <% } %>
 <% } %>
 </div>
 </div>
 <div class="buttonbar">
 <div class="buttonbar">
-    <form action="<pwm:url url='<%=PwmServletDefinition.Command.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded">
+    <form action="<pwm:url url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded">
         <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
         <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
         <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>
         <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>
         <button type="submit" name="button" class="btn" id="button_continue">
         <button type="submit" name="button" class="btn" id="button_continue">

+ 56 - 19
src/main/webapp/WEB-INF/jsp/admin-user-debug.jsp

@@ -30,7 +30,8 @@
 <%@ page import="password.pwm.config.profile.PwmPasswordRule" %>
 <%@ page import="password.pwm.config.profile.PwmPasswordRule" %>
 <%@ page import="password.pwm.http.servlet.admin.UserDebugDataBean" %>
 <%@ page import="password.pwm.http.servlet.admin.UserDebugDataBean" %>
 <%@ page import="java.util.Map" %>
 <%@ page import="java.util.Map" %>
-<% final PwmRequest debug_pwmRequest = JspUtility.getPwmRequest(pageContext); %>
+<%@ page import="password.pwm.i18n.Display" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
@@ -45,7 +46,7 @@
         <div id="page-content-title">User Debug</div>
         <div id="page-content-title">User Debug</div>
         <%@ include file="fragment/admin-nav.jsp" %>
         <%@ include file="fragment/admin-nav.jsp" %>
 
 
-        <% final UserDebugDataBean userDebugDataBean = (UserDebugDataBean)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.UserDebugData); %>
+        <% final UserDebugDataBean userDebugDataBean = (UserDebugDataBean)JspUtility.getAttribute(pageContext, PwmRequestAttribute.UserDebugData); %>
         <% if (userDebugDataBean == null) { %>
         <% if (userDebugDataBean == null) { %>
         <%@ include file="/WEB-INF/jsp/fragment/message.jsp" %>
         <%@ include file="/WEB-INF/jsp/fragment/message.jsp" %>
         <div id="panel-searchbar" class="searchbar">
         <div id="panel-searchbar" class="searchbar">
@@ -62,6 +63,12 @@
                 <button type="submit" class="btn"><pwm:display key="Button_Continue"/></button>
                 <button type="submit" class="btn"><pwm:display key="Button_Continue"/></button>
             </form>
             </form>
         </div>
         </div>
+        <div class="buttonbar">
+            <form method="get">
+                <input type="hidden" name="processAction" value="<%=AdminServlet.AdminAction.downloadUserDebug.toString()%>"/>
+                <button type="submit" class="btn">Download</button>
+            </form>
+        </div>
         <% final UserInfoBean userInfoBean = userDebugDataBean.getUserInfoBean(); %>
         <% final UserInfoBean userInfoBean = userDebugDataBean.getUserInfoBean(); %>
         <% if (userInfoBean != null) { %>
         <% if (userInfoBean != null) { %>
         <table>
         <table>
@@ -150,6 +157,14 @@
                     <%= JspUtility.freindlyWrite(pageContext, userInfoBean.getPasswordState().isViolatesPolicy()) %>
                     <%= JspUtility.freindlyWrite(pageContext, userInfoBean.getPasswordState().isViolatesPolicy()) %>
                 </td>
                 </td>
             </tr>
             </tr>
+            <tr>
+                <td class="key">
+                    Password Readable From LDAP
+                </td>
+                <td id="PasswordViolatesPolicy">
+                    <%= JspUtility.freindlyWrite(pageContext, userDebugDataBean.isPasswordReadable()) %>
+                </td>
+            </tr>
             <tr>
             <tr>
                 <td class="key">
                 <td class="key">
                     Requires New Password
                     Requires New Password
@@ -232,23 +247,29 @@
             <% if (userPolicy != null) { %>
             <% if (userPolicy != null) { %>
             <% PwmPasswordPolicy configPolicy = userDebugDataBean.getConfiguredPasswordPolicy(); %>
             <% PwmPasswordPolicy configPolicy = userDebugDataBean.getConfiguredPasswordPolicy(); %>
             <% PwmPasswordPolicy ldapPolicy = userDebugDataBean.getLdapPasswordPolicy(); %>
             <% PwmPasswordPolicy ldapPolicy = userDebugDataBean.getLdapPasswordPolicy(); %>
-            <tr>
-                <td>Policy Name</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userPolicy.getDisplayName(JspUtility.locale(request)))%></td>
-            </tr>
-            <tr>
-                <td>Policy ID</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userPolicy.getIdentifier())%></td>
-            </tr>
             <tr>
             <tr>
                 <td colspan="10">
                 <td colspan="10">
                     <table>
                     <table>
                         <tr class="title">
                         <tr class="title">
-                            <td class="key">Rule</td>
-                            <td class="key">Rule Type</td>
-                            <td class="key">Configured Policy</td>
-                            <td class="key">LDAP Policy</td>
-                            <td class="key">Effective Policy</td>
+                            <td class="key" style="width: 1px;">Rule</td>
+                            <td class="key" style="width: 1px;">Rule Type</td>
+                            <td class="key" style="width: 20%;">Configured <%=PwmConstants.PWM_APP_NAME%> Policy</td>
+                            <td class="key" style="width: 20%;">LDAP Policy</td>
+                            <td class="key" style="width: 20%;">Effective Policy</td>
+                        </tr>
+                        <tr>
+                            <td>ID</td>
+                            <td><pwm:display key="<%=Display.Value_NotApplicable.toString()%>"/></td>
+                            <td><%=JspUtility.freindlyWrite(pageContext, configPolicy.getIdentifier())%></td>
+                            <td><%=JspUtility.freindlyWrite(pageContext, ldapPolicy.getIdentifier())%></td>
+                            <td><%=JspUtility.freindlyWrite(pageContext, userPolicy.getIdentifier())%></td>
+                        </tr>
+                        <tr>
+                            <td>Display Name</td>
+                            <td><pwm:display key="<%=Display.Value_NotApplicable.toString()%>"/></td>
+                            <td><%=JspUtility.freindlyWrite(pageContext, configPolicy.getDisplayName(JspUtility.locale(request)))%></td>
+                            <td><%=JspUtility.freindlyWrite(pageContext, ldapPolicy.getDisplayName(JspUtility.locale(request)))%></td>
+                            <td><%=JspUtility.freindlyWrite(pageContext, userPolicy.getDisplayName(JspUtility.locale(request)))%></td>
                         </tr>
                         </tr>
                         <% for (final PwmPasswordRule rule : PwmPasswordRule.values()) { %>
                         <% for (final PwmPasswordRule rule : PwmPasswordRule.values()) { %>
                         <tr>
                         <tr>
@@ -297,7 +318,7 @@
                 <td><%=JspUtility.freindlyWrite(pageContext, responseInfoBean.getTimestamp())%></td>
                 <td><%=JspUtility.freindlyWrite(pageContext, responseInfoBean.getTimestamp())%></td>
             </tr>
             </tr>
             <tr>
             <tr>
-                <td>Challenges</td>
+                <td>Answered Challenges</td>
                 <% final Map<Challenge,String> crMap = responseInfoBean.getCrMap(); %>
                 <% final Map<Challenge,String> crMap = responseInfoBean.getCrMap(); %>
                 <% if (crMap == null) { %>
                 <% if (crMap == null) { %>
                 <td>
                 <td>
@@ -337,7 +358,7 @@
                 </td>
                 </td>
             </tr>
             </tr>
             <tr>
             <tr>
-                <td>Helpdesk Challenges</td>
+                <td>Helpdesk Answered Challenges</td>
                 <% final Map<Challenge,String> helpdeskCrMap = responseInfoBean.getHelpdeskCrMap(); %>
                 <% final Map<Challenge,String> helpdeskCrMap = responseInfoBean.getHelpdeskCrMap(); %>
                 <% if (helpdeskCrMap == null) { %>
                 <% if (helpdeskCrMap == null) { %>
                 <td>
                 <td>
@@ -383,19 +404,35 @@
                     <table>
                     <table>
                         <tr>
                         <tr>
                             <td class="key">Type</td>
                             <td class="key">Type</td>
-                            <td class="key">Required</td>
                             <td class="key">Text</td>
                             <td class="key">Text</td>
+                            <td class="key">Required</td>
+                            <td class="key">Min Length</td>
+                            <td class="key">Max Length</td>
+                            <td class="key">Enforce Wordlist</td>
+                            <td class="key">Max Question Characters</td>
                         </tr>
                         </tr>
                         <% for (final Challenge challenge : challengeProfile.getChallengeSet().getChallenges()) { %>
                         <% for (final Challenge challenge : challengeProfile.getChallengeSet().getChallenges()) { %>
                         <tr>
                         <tr>
                             <td>
                             <td>
                                 <%= challenge.isAdminDefined() ? "Admin Defined" : "User Defined" %>
                                 <%= challenge.isAdminDefined() ? "Admin Defined" : "User Defined" %>
                             </td>
                             </td>
+                            <td>
+                                <%= JspUtility.freindlyWrite(pageContext, challenge.getChallengeText())%>
+                            </td>
                             <td>
                             <td>
                                 <%= JspUtility.freindlyWrite(pageContext, challenge.isRequired())%>
                                 <%= JspUtility.freindlyWrite(pageContext, challenge.isRequired())%>
                             </td>
                             </td>
                             <td>
                             <td>
-                                <%= JspUtility.freindlyWrite(pageContext, challenge.getChallengeText())%>
+                                <%= challenge.getMinLength() %>
+                            </td>
+                            <td>
+                                <%= challenge.getMaxLength() %>
+                            </td>
+                            <td>
+                                <%= JspUtility.freindlyWrite(pageContext, challenge.isEnforceWordlist())%>
+                            </td>
+                            <td>
+                                <%= challenge.getMaxQuestionCharsInAnswer() %>
                             </td>
                             </td>
                         </tr>
                         </tr>
                         <% } %>
                         <% } %>

+ 3 - 2
src/main/webapp/WEB-INF/jsp/application-unavailable.jsp

@@ -2,6 +2,7 @@
 <%@ page import="password.pwm.error.ErrorInformation" %>
 <%@ page import="password.pwm.error.ErrorInformation" %>
 <%@ page import="password.pwm.error.PwmError" %>
 <%@ page import="password.pwm.error.PwmError" %>
 <%@ page import="password.pwm.http.PwmRequest" %>
 <%@ page import="password.pwm.http.PwmRequest" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -27,9 +28,9 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%
 <%
-    final ErrorInformation startupError = request.getAttribute(PwmRequest.Attribute.PwmErrorInfo.toString()) == null
+    final ErrorInformation startupError = request.getAttribute(PwmRequestAttribute.PwmErrorInfo.toString()) == null
             ? new ErrorInformation(PwmError.ERROR_APP_UNAVAILABLE)
             ? new ErrorInformation(PwmError.ERROR_APP_UNAVAILABLE)
-            : (ErrorInformation)request.getAttribute(PwmRequest.Attribute.PwmErrorInfo.toString());
+            : (ErrorInformation)request.getAttribute(PwmRequestAttribute.PwmErrorInfo.toString());
 %>
 %>
 <html>
 <html>
 <head>
 <head>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/changepassword-agreement.jsp

@@ -1,6 +1,7 @@
 <%@ page import="password.pwm.bean.PasswordStatus" %>
 <%@ page import="password.pwm.bean.PasswordStatus" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -42,7 +43,7 @@
         <% } %>
         <% } %>
         <%@ include file="fragment/message.jsp" %>
         <%@ include file="fragment/message.jsp" %>
         <br/>
         <br/>
-        <div id="agreementText" class="agreementText"><%= (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.AgreementText) %></div>
+        <div id="agreementText" class="agreementText"><%= (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.AgreementText) %></div>
         <div class="buttonbar">
         <div class="buttonbar">
             <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded" autocomplete="off">
             <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded" autocomplete="off">
                 <%-- remove the next line to remove the "I Agree" checkbox --%>
                 <%-- remove the next line to remove the "I Agree" checkbox --%>

+ 4 - 3
src/main/webapp/WEB-INF/jsp/changepassword-complete.jsp

@@ -1,5 +1,6 @@
-<%@ page import="password.pwm.http.servlet.CommandServlet" %>
+<%@ page import="password.pwm.http.servlet.command.CommandServlet" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -35,11 +36,11 @@
     <div id="centerbody">
     <div id="centerbody">
         <div id="page-content-title"><pwm:display key="Title_ChangePassword" displayIfMissing="true"/></div>
         <div id="page-content-title"><pwm:display key="Title_ChangePassword" displayIfMissing="true"/></div>
         <%@ include file="fragment/message.jsp" %>
         <%@ include file="fragment/message.jsp" %>
-        <% final String expandedText = (String) JspUtility.getAttribute(pageContext, PwmRequest.Attribute.CompleteText); %>
+        <% final String expandedText = (String) JspUtility.getAttribute(pageContext, PwmRequestAttribute.CompleteText); %>
         <br/>
         <br/>
         <div id="agreementText" class="agreementText"><%= expandedText %></div>
         <div id="agreementText" class="agreementText"><%= expandedText %></div>
         <div class="buttonbar">
         <div class="buttonbar">
-            <form action="<pwm:url url='<%=PwmServletDefinition.Command.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded" class="pwm-form">
+            <form action="<pwm:url url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded" class="pwm-form">
                 <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
                 <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
                 <button type="submit" name="button" class="btn" id="submitBtn">
                 <button type="submit" name="button" class="btn" id="submitBtn">
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>

+ 3 - 2
src/main/webapp/WEB-INF/jsp/changepassword-wait.jsp

@@ -2,6 +2,7 @@
 <%@ page import="password.pwm.http.bean.ChangePasswordBean" %>
 <%@ page import="password.pwm.http.bean.ChangePasswordBean" %>
 <%@ page import="password.pwm.util.java.TimeDuration" %>
 <%@ page import="password.pwm.util.java.TimeDuration" %>
 <%@ page import="java.time.Instant" %>
 <%@ page import="java.time.Instant" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -33,8 +34,8 @@
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>
 <body class="nihilo">
 <body class="nihilo">
-<% long maxWaitSeconds = (Long)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ChangePassword_MaxWaitSeconds); %>
-<% long checkIntervalSeconds = (Long)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ChangePassword_CheckIntervalSeconds); %>
+<% long maxWaitSeconds = (Long)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ChangePassword_MaxWaitSeconds); %>
+<% long checkIntervalSeconds = (Long)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ChangePassword_CheckIntervalSeconds); %>
 <meta http-equiv="refresh" content="<%=maxWaitSeconds%>;url='ChangePassword?processAction=complete&pwmFormID=<pwm:FormID/>">
 <meta http-equiv="refresh" content="<%=maxWaitSeconds%>;url='ChangePassword?processAction=complete&pwmFormID=<pwm:FormID/>">
 <noscript>
 <noscript>
     <meta http-equiv="refresh" content="<%=checkIntervalSeconds%>;url='ChangePassword?processAction=complete&pwmFormID=<pwm:FormID/>">
     <meta http-equiv="refresh" content="<%=checkIntervalSeconds%>;url='ChangePassword?processAction=complete&pwmFormID=<pwm:FormID/>">

+ 12 - 14
src/main/webapp/WEB-INF/jsp/changepassword.jsp

@@ -25,10 +25,9 @@
 <%@ page import="password.pwm.util.macro.MacroMachine" %>
 <%@ page import="password.pwm.util.macro.MacroMachine" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
-<% final PwmRequest changepassword_pwmRequest = PwmRequest.forRequest(request,response); %>
-<% final PasswordStatus passwordStatus = changepassword_pwmRequest.getPwmSession().getUserInfoBean().getPasswordState(); %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>
 <body class="nihilo">
 <body class="nihilo">
@@ -38,9 +37,9 @@
     </jsp:include>
     </jsp:include>
     <div id="centerbody">
     <div id="centerbody">
         <div id="page-content-title"><pwm:display key="Title_ChangePassword" displayIfMissing="true"/></div>
         <div id="page-content-title"><pwm:display key="Title_ChangePassword" displayIfMissing="true"/></div>
-        <% if (passwordStatus.isExpired() || passwordStatus.isPreExpired() || passwordStatus.isViolatesPolicy()) { %>
+        <pwm:if test="<%=PwmIfTest.passwordExpired%>">
         <h1><pwm:display key="Display_PasswordExpired"/></h1><br/>
         <h1><pwm:display key="Display_PasswordExpired"/></h1><br/>
-        <% } %>
+        </pwm:if>
         <pwm:display key="Display_ChangePassword"/>
         <pwm:display key="Display_ChangePassword"/>
         <div id="PasswordRequirements">
         <div id="PasswordRequirements">
             <ul>
             <ul>
@@ -48,11 +47,10 @@
             </ul>
             </ul>
         </div>
         </div>
         <%
         <%
-            final String passwordPolicyChangeMessage = changepassword_pwmRequest.getPwmSession().getUserInfoBean().getPasswordPolicy().getRuleHelper().getChangeMessage();
-            final MacroMachine macroMachine = JspUtility.getPwmSession(pageContext).getSessionManager().getMacroMachine(ContextManager.getPwmApplication(session));
+            final String passwordPolicyChangeMessage = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ChangePassword_PasswordPolicyChangeMessage);
         %>
         %>
-        <% if (passwordPolicyChangeMessage.length() > 1) { %>
-        <p><%= macroMachine.expandMacros(passwordPolicyChangeMessage) %></p>
+        <% if (passwordPolicyChangeMessage != null) { %>
+        <p><%= passwordPolicyChangeMessage %></p>
         <% } %>
         <% } %>
         <br/>
         <br/>
         <%@ include file="fragment/message.jsp" %>
         <%@ include file="fragment/message.jsp" %>
@@ -113,12 +111,12 @@
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                     <pwm:display key="Button_ChangePassword"/>
                     <pwm:display key="Button_ChangePassword"/>
                 </button>
                 </button>
-                <% if (!passwordStatus.isExpired() && !passwordStatus.isPreExpired() && !passwordStatus.isViolatesPolicy()) { %>
-                <button id="button-reset" type="submit" name="change" class="btn" form="form-reset">
-                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
-                    <pwm:display key="Button_Cancel"/>
-                </button>
-                <% } %>
+                <pwm:if test="<%=PwmIfTest.passwordExpired%>" negate="true">
+                    <button id="button-reset" type="submit" name="change" class="btn" form="form-reset">
+                        <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
+                        <pwm:display key="Button_Cancel"/>
+                    </button>
+                </pwm:if>
             </div>
             </div>
         </form>
         </form>
         <form id="form-reset" name="form-reset" action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded" >
         <form id="form-reset" name="form-reset" action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded" >

+ 2 - 1
src/main/webapp/WEB-INF/jsp/configmanager-certificates.jsp

@@ -3,6 +3,7 @@
 <%@ page import="password.pwm.i18n.Config" %>
 <%@ page import="password.pwm.i18n.Config" %>
 <%@ page import="password.pwm.util.LocaleHelper" %>
 <%@ page import="password.pwm.util.LocaleHelper" %>
 <%@ page import="java.util.List" %>
 <%@ page import="java.util.List" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -37,7 +38,7 @@
     <jsp:include page="fragment/header-body.jsp">
     <jsp:include page="fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="<%=LocaleHelper.getLocalizedMessage(Config.Title_ConfigManager, JspUtility.getPwmRequest(pageContext))%>"/>
         <jsp:param name="pwm.PageName" value="<%=LocaleHelper.getLocalizedMessage(Config.Title_ConfigManager, JspUtility.getPwmRequest(pageContext))%>"/>
     </jsp:include>
     </jsp:include>
-    <% if ((Boolean)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ConfigHasCertificates)) { %>
+    <% if ((Boolean)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ConfigHasCertificates)) { %>
     <div id="centerbody">
     <div id="centerbody">
         <div id="page-content-title"><%=LocaleHelper.getLocalizedMessage(Config.Title_ConfigManager, JspUtility.getPwmRequest(pageContext))%></div>
         <div id="page-content-title"><%=LocaleHelper.getLocalizedMessage(Config.Title_ConfigManager, JspUtility.getPwmRequest(pageContext))%></div>
         <%@ include file="fragment/configmanager-nav.jsp" %>
         <%@ include file="fragment/configmanager-nav.jsp" %>

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

@@ -2,6 +2,7 @@
 <%@ page import="password.pwm.i18n.Config" %>
 <%@ page import="password.pwm.i18n.Config" %>
 <%@ page import="password.pwm.util.LocaleHelper" %>
 <%@ page import="password.pwm.util.LocaleHelper" %>
 <%@ page import="password.pwm.util.java.JavaHelper" %>
 <%@ page import="password.pwm.util.java.JavaHelper" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -56,7 +57,7 @@
             <div class="checkboxWrapper">
             <div class="checkboxWrapper">
                 <label>
                 <label>
                     <input type="checkbox" id="remember" name="remember"/>
                     <input type="checkbox" id="remember" name="remember"/>
-                    <pwm:display key="Display_RememberLogin" bundle="Config" value1="<%=(String)JspUtility.getAttribute(pageContext,PwmRequest.Attribute.ConfigPasswordRememberTime)%>"/>
+                    <pwm:display key="Display_RememberLogin" bundle="Config" value1="<%=(String)JspUtility.getAttribute(pageContext,PwmRequestAttribute.ConfigPasswordRememberTime)%>"/>
                 </label>
                 </label>
             </div>
             </div>
             <% } %>
             <% } %>
@@ -69,7 +70,7 @@
                 <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>" autofocus/>
                 <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>" autofocus/>
             </div>
             </div>
         </form>
         </form>
-        <% final ConfigAccessFilter.ConfigLoginHistory configLoginHistory = (ConfigAccessFilter.ConfigLoginHistory)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ConfigLoginHistory); %>
+        <% final ConfigAccessFilter.ConfigLoginHistory configLoginHistory = (ConfigAccessFilter.ConfigLoginHistory)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ConfigLoginHistory); %>
         <% if (configLoginHistory != null && !configLoginHistory.successEvents().isEmpty()) { %>
         <% if (configLoginHistory != null && !configLoginHistory.successEvents().isEmpty()) { %>
         <h2 style="margin-top: 15px;">Previous Authentications</h2>
         <h2 style="margin-top: 15px;">Previous Authentications</h2>
         <table>
         <table>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/configmanager-summary.jsp

@@ -11,6 +11,7 @@
 <%@ page import="java.util.List" %>
 <%@ page import="java.util.List" %>
 <%@ page import="java.util.Locale" %>
 <%@ page import="java.util.Locale" %>
 <%@ page import="java.util.Map" %>
 <%@ page import="java.util.Map" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -38,7 +39,7 @@
   final Map<String,Object> outputData = new HashMap<String,Object>();
   final Map<String,Object> outputData = new HashMap<String,Object>();
   try {
   try {
     final PwmRequest pwmRequest = PwmRequest.forRequest(request,response);
     final PwmRequest pwmRequest = PwmRequest.forRequest(request,response);
-    outputData.putAll((Map)pwmRequest.getAttribute(PwmRequest.Attribute.ConfigurationSummaryOutput));
+    outputData.putAll((Map)pwmRequest.getAttribute(PwmRequestAttribute.ConfigurationSummaryOutput));
 
 
     settingData.addAll((List<Map<String,String>>)outputData.get("settings"));
     settingData.addAll((List<Map<String,String>>)outputData.get("settings"));
   } catch (PwmException e) {
   } catch (PwmException e) {

+ 6 - 5
src/main/webapp/WEB-INF/jsp/configmanager.jsp

@@ -5,6 +5,7 @@
 <%@ page import="password.pwm.util.java.StringUtil" %>
 <%@ page import="password.pwm.util.java.StringUtil" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -63,7 +64,7 @@
                     Last Modified
                     Last Modified
                 </td>
                 </td>
                 <td>
                 <td>
-                    <% final String lastModified = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ConfigLastModified); %>
+                    <% final String lastModified = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ConfigLastModified); %>
                     <% if (lastModified == null) { %>
                     <% if (lastModified == null) { %>
                     <pwm:display key="Value_NotApplicable"/>
                     <pwm:display key="Value_NotApplicable"/>
                     <% } else { %>
                     <% } else { %>
@@ -76,7 +77,7 @@
                     Password Protected
                     Password Protected
                 </td>
                 </td>
                 <td>
                 <td>
-                    <%=JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ConfigHasPassword)%>
+                    <%=JspUtility.getAttribute(pageContext, PwmRequestAttribute.ConfigHasPassword)%>
                 </td>
                 </td>
             </tr>
             </tr>
             <pwm:if test="<%=PwmIfTest.appliance%>" negate="true">
             <pwm:if test="<%=PwmIfTest.appliance%>" negate="true">
@@ -86,7 +87,7 @@
                     </td>
                     </td>
                     <td>
                     <td>
                         <div style="max-width:398px; overflow-x: auto; white-space: nowrap">
                         <div style="max-width:398px; overflow-x: auto; white-space: nowrap">
-                            <%=StringUtil.escapeHtml((String) JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ApplicationPath))%>
+                            <%=StringUtil.escapeHtml((String) JspUtility.getAttribute(pageContext, PwmRequestAttribute.ApplicationPath))%>
                         </div>
                         </div>
                     </td>
                     </td>
                 </tr>
                 </tr>
@@ -96,7 +97,7 @@
                     </td>
                     </td>
                     <td>
                     <td>
                         <div style="max-width:398px; overflow-x: auto; white-space: nowrap">
                         <div style="max-width:398px; overflow-x: auto; white-space: nowrap">
-                            <%=StringUtil.escapeHtml((String) JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ConfigFilename))%>
+                            <%=StringUtil.escapeHtml((String) JspUtility.getAttribute(pageContext, PwmRequestAttribute.ConfigFilename))%>
                         </div>
                         </div>
                     </td>
                     </td>
                 </tr>
                 </tr>
@@ -121,7 +122,7 @@
             <pwm:if test="<%=PwmIfTest.configurationOpen%>">
             <pwm:if test="<%=PwmIfTest.configurationOpen%>">
             <tr class="buttonrow">
             <tr class="buttonrow">
                 <td class="buttoncell" colspan="2">
                 <td class="buttoncell" colspan="2">
-                    <% final String configFileName = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ConfigFilename); %>
+                    <% final String configFileName = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ConfigFilename); %>
                     <pwm:if test="<%=PwmIfTest.trialMode%>">
                     <pwm:if test="<%=PwmIfTest.trialMode%>">
                         <div  style="text-align: center" class="center">
                         <div  style="text-align: center" class="center">
                         <span><pwm:display key="Notice_TrialRestrictConfig" bundle="Admin"/></span>
                         <span><pwm:display key="Notice_TrialRestrictConfig" bundle="Admin"/></span>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/deleteaccount-agreement.jsp

@@ -1,5 +1,6 @@
 <%@ page import="password.pwm.http.servlet.DeleteAccountServlet" %>
 <%@ page import="password.pwm.http.servlet.DeleteAccountServlet" %>
 <%@ page import="password.pwm.i18n.Display" %>
 <%@ page import="password.pwm.i18n.Display" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -36,7 +37,7 @@
     <div id="centerbody">
     <div id="centerbody">
         <div id="page-content-title"><pwm:display key="Title_DeleteAccount" displayIfMissing="true"/></div>
         <div id="page-content-title"><pwm:display key="Title_DeleteAccount" displayIfMissing="true"/></div>
         <%@ include file="fragment/message.jsp" %>
         <%@ include file="fragment/message.jsp" %>
-        <% final String expandedText = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.AgreementText); %>
+        <% final String expandedText = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.AgreementText); %>
         <div class="agreementText"><%= expandedText %></div>
         <div class="agreementText"><%= expandedText %></div>
         <div class="buttonbar">
         <div class="buttonbar">
             <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
             <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">

+ 2 - 2
src/main/webapp/WEB-INF/jsp/error-http.jsp

@@ -1,6 +1,6 @@
 <%@ page import="password.pwm.error.PwmError" %>
 <%@ page import="password.pwm.error.PwmError" %>
 <%@ page import="password.pwm.http.JspUtility" %>
 <%@ page import="password.pwm.http.JspUtility" %>
-<%@ page import="password.pwm.http.servlet.CommandServlet" %>
+<%@ page import="password.pwm.http.servlet.command.CommandServlet" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
@@ -59,7 +59,7 @@
         <br/>
         <br/>
         <br/>
         <br/>
         <div class="buttonbar">
         <div class="buttonbar">
-            <form action="<pwm:url url='<%=PwmServletDefinition.Command.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded">
+            <form action="<pwm:url url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded">
                 <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
                 <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
                 <input type="submit" name="button" class="btn"
                 <input type="submit" name="button" class="btn"
                        value="    <pwm:display key="Button_Continue"/>    "
                        value="    <pwm:display key="Button_Continue"/>    "

+ 4 - 3
src/main/webapp/WEB-INF/jsp/error.jsp

@@ -1,7 +1,8 @@
 <%@ page import="password.pwm.error.ErrorInformation" %>
 <%@ page import="password.pwm.error.ErrorInformation" %>
 <%@ page import="password.pwm.http.JspUtility" %>
 <%@ page import="password.pwm.http.JspUtility" %>
-<%@ page import="password.pwm.http.servlet.CommandServlet" %>
+<%@ page import="password.pwm.http.servlet.command.CommandServlet" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -28,7 +29,7 @@
 <%@ page language="java" session="true" isThreadSafe="true"
 <%@ page language="java" session="true" isThreadSafe="true"
          contentType="text/html" %>
          contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
-<% final ErrorInformation errorInformation = (ErrorInformation)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.PwmErrorInfo); %>
+<% final ErrorInformation errorInformation = (ErrorInformation)JspUtility.getAttribute(pageContext, PwmRequestAttribute.PwmErrorInfo); %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <% JspUtility.setFlag(pageContext, PwmRequestFlag.HIDE_HEADER_BUTTONS); %>
 <% JspUtility.setFlag(pageContext, PwmRequestFlag.HIDE_HEADER_BUTTONS); %>
 <% JspUtility.setFlag(pageContext, PwmRequestFlag.HIDE_HEADER_WARNINGS); %>
 <% JspUtility.setFlag(pageContext, PwmRequestFlag.HIDE_HEADER_WARNINGS); %>
@@ -52,7 +53,7 @@
         <pwm:if test="<%=PwmIfTest.showErrorDetail%>">
         <pwm:if test="<%=PwmIfTest.showErrorDetail%>">
             <% if (errorInformation != null && !errorInformation.getError().isErrorIsPermanent()) { %>
             <% if (errorInformation != null && !errorInformation.getError().isErrorIsPermanent()) { %>
             <div class="buttonbar">
             <div class="buttonbar">
-                <form action="<pwm:url url='<%=PwmServletDefinition.Command.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded">
+                <form action="<pwm:url url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded">
                     <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
                     <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
                     <button type="submit" name="button" class="btn" id="button_continue" autofocus="autofocus">
                     <button type="submit" name="button" class="btn" id="button_continue" autofocus="autofocus">
                         <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                         <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/forgottenpassword-attributes.jsp

@@ -23,6 +23,7 @@
 <%@ page import="password.pwm.http.JspUtility" %>
 <%@ page import="password.pwm.http.JspUtility" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
@@ -52,7 +53,7 @@ this is handled this way so on browsers where hiding fields is not possible, the
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                     <pwm:display key="Button_RecoverPassword"/>
                     <pwm:display key="Button_RecoverPassword"/>
                 </button>
                 </button>
-                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordOptionalPageView))) { %>
+                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordOptionalPageView))) { %>
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:display key="Button_GoBack"/>
                     <pwm:display key="Button_GoBack"/>

+ 3 - 2
src/main/webapp/WEB-INF/jsp/forgottenpassword-enterotp.jsp

@@ -26,6 +26,7 @@
 <%@ page import="password.pwm.util.operations.otp.OTPUserRecord" %>
 <%@ page import="password.pwm.util.operations.otp.OTPUserRecord" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.bean.UserInfoBean" %>
 <%@ page import="password.pwm.bean.UserInfoBean" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
@@ -37,7 +38,7 @@
     <div id="centerbody">
     <div id="centerbody">
         <div id="page-content-title"><pwm:display key="Title_ForgottenPassword" displayIfMissing="true"/></div>
         <div id="page-content-title"><pwm:display key="Title_ForgottenPassword" displayIfMissing="true"/></div>
         <%
         <%
-            final UserInfoBean userInfoBean = (UserInfoBean)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordUserInfo);
+            final UserInfoBean userInfoBean = (UserInfoBean)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordUserInfo);
             final OTPUserRecord otp = userInfoBean.getOtpUserRecord();
             final OTPUserRecord otp = userInfoBean.getOtpUserRecord();
             final String identifier = otp.getIdentifier();
             final String identifier = otp.getIdentifier();
 
 
@@ -56,7 +57,7 @@
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                     <pwm:display key="Button_CheckCode"/>
                     <pwm:display key="Button_CheckCode"/>
                 </button>
                 </button>
-                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordOptionalPageView))) { %>
+                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordOptionalPageView))) { %>
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:display key="Button_GoBack"/>
                     <pwm:display key="Button_GoBack"/>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/forgottenpassword-entertoken.jsp

@@ -25,6 +25,7 @@
 <%@ page import="password.pwm.http.bean.ForgottenPasswordBean" %>
 <%@ page import="password.pwm.http.bean.ForgottenPasswordBean" %>
 <%@ page import="password.pwm.http.servlet.forgottenpw.ForgottenPasswordServlet"%>
 <%@ page import="password.pwm.http.servlet.forgottenpw.ForgottenPasswordServlet"%>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%  final boolean resendEnabled = Boolean.parseBoolean(JspUtility.getPwmRequest(pageContext).getConfig().readAppProperty(AppProperty.TOKEN_RESEND_ENABLED)); %>
 <%  final boolean resendEnabled = Boolean.parseBoolean(JspUtility.getPwmRequest(pageContext).getConfig().readAppProperty(AppProperty.TOKEN_RESEND_ENABLED)); %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>
@@ -86,7 +87,7 @@
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                     <pwm:display key="Button_CheckCode"/>
                     <pwm:display key="Button_CheckCode"/>
                 </button>
                 </button>
-                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordOptionalPageView))) { %>
+                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordOptionalPageView))) { %>
                 <button type="submit" id="button-goBack" name="button-goBack" class="btn" form="form-goBack">
                 <button type="submit" id="button-goBack" name="button-goBack" class="btn" form="form-goBack">
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:display key="Button_GoBack"/>
                     <pwm:display key="Button_GoBack"/>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/forgottenpassword-method.jsp

@@ -1,6 +1,7 @@
 <%@ page import="password.pwm.config.option.IdentityVerificationMethod" %>
 <%@ page import="password.pwm.config.option.IdentityVerificationMethod" %>
 <%@ page import="java.util.LinkedHashSet" %>
 <%@ page import="java.util.LinkedHashSet" %>
 <%@ page import="java.util.Set" %>
 <%@ page import="java.util.Set" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -28,7 +29,7 @@
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%
 <%
     final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
     final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
-    final Set<IdentityVerificationMethod> methods = new LinkedHashSet<IdentityVerificationMethod>((Set<IdentityVerificationMethod>) JspUtility.getAttribute(pageContext, PwmRequest.Attribute.AvailableAuthMethods));
+    final Set<IdentityVerificationMethod> methods = new LinkedHashSet<IdentityVerificationMethod>((Set<IdentityVerificationMethod>) JspUtility.getAttribute(pageContext, PwmRequestAttribute.AvailableAuthMethods));
 %>
 %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>

+ 4 - 3
src/main/webapp/WEB-INF/jsp/forgottenpassword-remote.jsp

@@ -25,6 +25,7 @@
 <%@ page import="password.pwm.VerificationMethodSystem" %>
 <%@ page import="password.pwm.VerificationMethodSystem" %>
 <%@ page import="java.util.List" %>
 <%@ page import="java.util.List" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
@@ -36,8 +37,8 @@
     <div id="centerbody">
     <div id="centerbody">
         <div id="page-content-title"><pwm:display key="Title_ForgottenPassword" displayIfMissing="true"/></div>
         <div id="page-content-title"><pwm:display key="Title_ForgottenPassword" displayIfMissing="true"/></div>
         <%
         <%
-            final List<VerificationMethodSystem.UserPrompt> prompts = (List<VerificationMethodSystem.UserPrompt>)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordPrompts);
-            final String instructions = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordInstructions);
+            final List<VerificationMethodSystem.UserPrompt> prompts = (List<VerificationMethodSystem.UserPrompt>)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordPrompts);
+            final String instructions = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordInstructions);
         %>
         %>
         <p><%=instructions%></p>
         <p><%=instructions%></p>
         <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded" name="search" class="pwm-form" autocomplete="off">
         <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded" name="search" class="pwm-form" autocomplete="off">
@@ -55,7 +56,7 @@
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                     <pwm:display key="Button_Continue"/>
                     <pwm:display key="Button_Continue"/>
                 </button>
                 </button>
-                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordOptionalPageView))) { %>
+                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordOptionalPageView))) { %>
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:display key="Button_GoBack"/>
                     <pwm:display key="Button_GoBack"/>

+ 3 - 2
src/main/webapp/WEB-INF/jsp/forgottenpassword-responses.jsp

@@ -24,10 +24,11 @@
 <%@ page import="com.novell.ldapchai.cr.ChallengeSet" %>
 <%@ page import="com.novell.ldapchai.cr.ChallengeSet" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
-<% final ChallengeSet challengeSet = (ChallengeSet)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordChallengeSet); %>
+<% final ChallengeSet challengeSet = (ChallengeSet)JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordChallengeSet); %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>
 <%--
 <%--
@@ -62,7 +63,7 @@ this is handled this way so on browsers where hiding fields is not possible, the
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                                                    <pwm:display key="Button_RecoverPassword"/>
                                                    <pwm:display key="Button_RecoverPassword"/>
                 </button>
                 </button>
-                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequest.Attribute.ForgottenPasswordOptionalPageView))) { %>
+                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmRequestAttribute.ForgottenPasswordOptionalPageView))) { %>
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:display key="Button_GoBack"/>
                     <pwm:display key="Button_GoBack"/>

+ 4 - 3
src/main/webapp/WEB-INF/jsp/forgottenusername-complete.jsp

@@ -1,5 +1,6 @@
-<%@ page import="password.pwm.http.servlet.CommandServlet" %>
+<%@ page import="password.pwm.http.servlet.command.CommandServlet" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -35,11 +36,11 @@
     <div id="centerbody">
     <div id="centerbody">
         <div id="page-content-title"><pwm:display key="Title_ForgottenUsername" displayIfMissing="true"/></div>
         <div id="page-content-title"><pwm:display key="Title_ForgottenUsername" displayIfMissing="true"/></div>
         <%@ include file="fragment/message.jsp" %>
         <%@ include file="fragment/message.jsp" %>
-        <% final String expandedText = (String) JspUtility.getAttribute(pageContext, PwmRequest.Attribute.CompleteText); %>
+        <% final String expandedText = (String) JspUtility.getAttribute(pageContext, PwmRequestAttribute.CompleteText); %>
         <br/>
         <br/>
         <div id="agreementText" class="agreementText"><%= expandedText %></div>
         <div id="agreementText" class="agreementText"><%= expandedText %></div>
         <div class="buttonbar">
         <div class="buttonbar">
-            <form action="<pwm:url url='<%=PwmServletDefinition.Command.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded" class="pwm-form">
+            <form action="<pwm:url url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>' addContext="true"/>" method="post" enctype="application/x-www-form-urlencoded" class="pwm-form">
                 <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
                 <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
                 <button type="submit" name="button" class="btn" id="submitBtn">
                 <button type="submit" name="button" class="btn" id="submitBtn">
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>

+ 2 - 2
src/main/webapp/WEB-INF/jsp/fragment/cancel-form.jsp

@@ -1,5 +1,5 @@
 <%@ page import="password.pwm.PwmConstants" %>
 <%@ page import="password.pwm.PwmConstants" %>
-<%@ page import="password.pwm.http.servlet.CommandServlet" %>
+<%@ page import="password.pwm.http.servlet.command.CommandServlet" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 
 
@@ -29,7 +29,7 @@
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <pwm:if test="<%=PwmIfTest.showCancel%>">
 <pwm:if test="<%=PwmIfTest.showCancel%>">
   <pwm:if test="<%=PwmIfTest.forcedPageView%>" negate="true">
   <pwm:if test="<%=PwmIfTest.forcedPageView%>" negate="true">
-    <form id="form-hidden-cancel" action="<pwm:url addContext="true" url='<%=PwmServletDefinition.Command.servletUrl()%>'/>" method="get">
+    <form id="form-hidden-cancel" action="<pwm:url addContext="true" url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>'/>" method="get">
       <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
       <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=CommandServlet.CommandAction.next.toString()%>"/>
     </form>
     </form>
   </pwm:if>
   </pwm:if>

+ 3 - 2
src/main/webapp/WEB-INF/jsp/fragment/captcha-embed.jsp

@@ -24,6 +24,7 @@
 <%@ page import="password.pwm.http.PwmRequest" %>
 <%@ page import="password.pwm.http.PwmRequest" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
 <%@ page import="password.pwm.util.CaptchaUtility" %>
 <%@ page import="password.pwm.util.CaptchaUtility" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <% if (CaptchaUtility.captchaEnabledForRequest(JspUtility.getPwmRequest(pageContext))) { %>
 <% if (CaptchaUtility.captchaEnabledForRequest(JspUtility.getPwmRequest(pageContext))) { %>
 <% CaptchaUtility.prepareCaptchaDisplay(JspUtility.getPwmRequest(pageContext)); %>
 <% CaptchaUtility.prepareCaptchaDisplay(JspUtility.getPwmRequest(pageContext)); %>
@@ -42,9 +43,9 @@
             };
             };
 
 
             console.log('reached google recaptcha onload callback');
             console.log('reached google recaptcha onload callback');
-            grecaptcha.render('recaptcha-container',{callback:recaptchaCallback,sitekey:'<%=JspUtility.getAttribute(pageContext,PwmRequest.Attribute.CaptchaPublicKey)%>'});
+            grecaptcha.render('recaptcha-container',{callback:recaptchaCallback,sitekey:'<%=JspUtility.getAttribute(pageContext,PwmRequestAttribute.CaptchaPublicKey)%>'});
         }
         }
     </script>
     </script>
 </pwm:script>
 </pwm:script>
-<script nonce="<pwm:value name="<%=PwmValue.cspNonce%>"/>" src="<%=(String)JspUtility.getAttribute(pageContext,PwmRequest.Attribute.CaptchaClientUrl)%>?onload=onloadCallback&render=explicit" defer async></script>
+<script nonce="<pwm:value name="<%=PwmValue.cspNonce%>"/>" src="<%=(String)JspUtility.getAttribute(pageContext,PwmRequestAttribute.CaptchaClientUrl)%>?onload=onloadCallback&render=explicit" defer async></script>
 <% } %>
 <% } %>

+ 1 - 6
src/main/webapp/WEB-INF/jsp/fragment/footer.jsp

@@ -70,12 +70,7 @@
         PWM_GLOBAL['startupFunctions'].push(function() {
         PWM_GLOBAL['startupFunctions'].push(function() {
             <pwm:value name="<%=PwmValue.customJavascript%>"/>
             <pwm:value name="<%=PwmValue.customJavascript%>"/>
         });
         });
-        var dojoConfig = {
-            has: {
-                "csp-restrictions":false
-            },
-            async:true
-        }
+        var dojoConfig = { has: { "csp-restrictions":false }, async:true }
     </script>
     </script>
 </pwm:script>
 </pwm:script>
 <script nonce="<pwm:value name="<%=PwmValue.cspNonce%>"/>" dojo-sync-loader="false" type="text/javascript" src="<pwm:url addContext="true" url='/public/resources/webjars/dojo/dojo.js'/>"></script>
 <script nonce="<pwm:value name="<%=PwmValue.cspNonce%>"/>" dojo-sync-loader="false" type="text/javascript" src="<pwm:url addContext="true" url='/public/resources/webjars/dojo/dojo.js'/>"></script>

+ 5 - 4
src/main/webapp/WEB-INF/jsp/fragment/form.jsp

@@ -12,6 +12,7 @@
 <%@ page import="java.util.Locale" %>
 <%@ page import="java.util.Locale" %>
 <%@ page import="java.util.Map" %>
 <%@ page import="java.util.Map" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
 <%@ page import="password.pwm.http.tag.value.PwmValue" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 
 
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
@@ -38,7 +39,7 @@
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%
 <%
     final PwmRequest formPwmRequest = PwmRequest.forRequest(request,response);
     final PwmRequest formPwmRequest = PwmRequest.forRequest(request,response);
-    final List<FormConfiguration> formConfigurationList = (List<FormConfiguration>)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.FormConfiguration);
+    final List<FormConfiguration> formConfigurationList = (List<FormConfiguration>)JspUtility.getAttribute(pageContext, PwmRequestAttribute.FormConfiguration);
 %>
 %>
 <% if (formConfigurationList == null) { %>
 <% if (formConfigurationList == null) { %>
 [ form definition is not available ]
 [ form definition is not available ]
@@ -46,9 +47,9 @@
 <!-- form contains no items ] -->
 <!-- form contains no items ] -->
 <% } else { %>
 <% } else { %>
 <%
 <%
-    final boolean forceReadOnly = (Boolean)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.FormReadOnly);
-    final boolean showPasswordFields = (Boolean)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.FormShowPasswordFields);
-    final Map<FormConfiguration,String> formDataMap = (Map<FormConfiguration,String>)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.FormData);
+    final boolean forceReadOnly = (Boolean)JspUtility.getAttribute(pageContext, PwmRequestAttribute.FormReadOnly);
+    final boolean showPasswordFields = (Boolean)JspUtility.getAttribute(pageContext, PwmRequestAttribute.FormShowPasswordFields);
+    final Map<FormConfiguration,String> formDataMap = (Map<FormConfiguration,String>)JspUtility.getAttribute(pageContext, PwmRequestAttribute.FormData);
 
 
     final PwmApplication pwmApplication = formPwmRequest.getPwmApplication();
     final PwmApplication pwmApplication = formPwmRequest.getPwmApplication();
     final Locale formLocale = formPwmRequest.getLocale();
     final Locale formLocale = formPwmRequest.getLocale();

+ 2 - 1
src/main/webapp/WEB-INF/jsp/fragment/ldap-permissions.jsp

@@ -8,6 +8,7 @@
 <%@ page import="java.util.TreeSet" %>
 <%@ page import="java.util.TreeSet" %>
 <%@ page import="password.pwm.util.LocaleHelper" %>
 <%@ page import="password.pwm.util.LocaleHelper" %>
 <%@ page import="password.pwm.i18n.Display" %>
 <%@ page import="password.pwm.i18n.Display" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 
 
@@ -33,7 +34,7 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   --%>
   --%>
 
 
-<% final LDAPPermissionCalculator outputData = (LDAPPermissionCalculator)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.LdapPermissionItems); %>
+<% final LDAPPermissionCalculator outputData = (LDAPPermissionCalculator)JspUtility.getAttribute(pageContext, PwmRequestAttribute.LdapPermissionItems); %>
 <p>
 <p>
     <pwm:display key="Display_LdapPermissionRecommendations" bundle="Config"/>
     <pwm:display key="Display_LdapPermissionRecommendations" bundle="Config"/>
 </p>
 </p>

+ 2 - 2
src/main/webapp/WEB-INF/jsp/fragment/message.jsp

@@ -1,6 +1,6 @@
 <%@ page import="password.pwm.error.ErrorInformation" %>
 <%@ page import="password.pwm.error.ErrorInformation" %>
 <%@ page import="password.pwm.http.JspUtility" %>
 <%@ page import="password.pwm.http.JspUtility" %>
-<%@ page import="password.pwm.http.PwmRequest" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 
 
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
@@ -29,7 +29,7 @@
   --%>
   --%>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <div id="message_wrapper">
 <div id="message_wrapper">
-<% final ErrorInformation requestError = (ErrorInformation)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.PwmErrorInfo); %>
+<% final ErrorInformation requestError = (ErrorInformation)JspUtility.getAttribute(pageContext, PwmRequestAttribute.PwmErrorInfo); %>
 <% if (requestError != null) { %>
 <% if (requestError != null) { %>
     <span id="message" class="message message-error"><pwm:ErrorMessage/></span>
     <span id="message" class="message message-error"><pwm:ErrorMessage/></span>
     <span id="errorCode" style="display: none"><%=requestError.getError().getErrorCode()%></span>
     <span id="errorCode" style="display: none"><%=requestError.getError().getErrorCode()%></span>

+ 4 - 4
src/main/webapp/WEB-INF/jsp/guest-create.jsp

@@ -1,4 +1,4 @@
-<%--
+<%@ page import="password.pwm.http.PwmRequestAttribute" %><%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
   ~
   ~
@@ -25,9 +25,9 @@
 <%@ page language="java" session="true" isThreadSafe="true"
 <%@ page language="java" session="true" isThreadSafe="true"
          contentType="text/html" %>
          contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
-<% final String maxValidDate = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.GuestMaximumExpirationDate); %>
-<% final String selectedDate = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.GuestCurrentExpirationDate); %>
-<% final String maxValidDays = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.GuestMaximumValidDays); %>
+<% final String maxValidDate = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.GuestMaximumExpirationDate); %>
+<% final String selectedDate = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.GuestCurrentExpirationDate); %>
+<% final String maxValidDays = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.GuestMaximumValidDays); %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>
 <body class="nihilo">
 <body class="nihilo">

+ 4 - 4
src/main/webapp/WEB-INF/jsp/guest-update.jsp

@@ -1,4 +1,4 @@
-<%--
+<%@ page import="password.pwm.http.PwmRequestAttribute" %><%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
   ~
   ~
@@ -24,9 +24,9 @@
 <%@ page language="java" session="true" isThreadSafe="true"
 <%@ page language="java" session="true" isThreadSafe="true"
          contentType="text/html" %>
          contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
-<% final String maxValidDate = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.GuestMaximumExpirationDate); %>
-<% final String selectedDate = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.GuestCurrentExpirationDate); %>
-<% final String maxValidDays = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.GuestMaximumValidDays); %>
+<% final String maxValidDate = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.GuestMaximumExpirationDate); %>
+<% final String selectedDate = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.GuestCurrentExpirationDate); %>
+<% final String maxValidDays = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.GuestMaximumValidDays); %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="fragment/header.jsp" %>
 <%@ include file="fragment/header.jsp" %>
 <body class="nihilo">
 <body class="nihilo">

+ 5 - 5
src/main/webapp/WEB-INF/jsp/helpdesk-detail.jsp

@@ -46,6 +46,7 @@
 <%@ page import="java.util.List" %>
 <%@ page import="java.util.List" %>
 <%@ page import="java.util.Set" %>
 <%@ page import="java.util.Set" %>
 <%@ page import="java.time.Instant" %>
 <%@ page import="java.time.Instant" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
@@ -54,11 +55,10 @@
     final PwmSession pwmSession = pwmRequest.getPwmSession();
     final PwmSession pwmSession = pwmRequest.getPwmSession();
     final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
     final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
     final HelpdeskProfile helpdeskProfile = pwmSession.getSessionManager().getHelpdeskProfile(pwmApplication);
     final HelpdeskProfile helpdeskProfile = pwmSession.getSessionManager().getHelpdeskProfile(pwmApplication);
-    final DateFormat dateFormatter = PwmConstants.DEFAULT_DATETIME_FORMAT;
     final HelpdeskUIMode SETTING_PW_UI_MODE = HelpdeskUIMode.valueOf(helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_SET_PASSWORD_MODE));
     final HelpdeskUIMode SETTING_PW_UI_MODE = HelpdeskUIMode.valueOf(helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_SET_PASSWORD_MODE));
 
 
     // user info
     // user info
-    final HelpdeskDetailInfoBean helpdeskDetailInfoBean = (HelpdeskDetailInfoBean)pwmRequest.getAttribute(PwmRequest.Attribute.HelpdeskDetail);
+    final HelpdeskDetailInfoBean helpdeskDetailInfoBean = (HelpdeskDetailInfoBean)pwmRequest.getAttribute(PwmRequestAttribute.HelpdeskDetail);
     final UserInfoBean searchedUserInfo = helpdeskDetailInfoBean.getUserInfoBean();
     final UserInfoBean searchedUserInfo = helpdeskDetailInfoBean.getUserInfoBean();
     final ResponseInfoBean responseInfoBean = searchedUserInfo.getResponseInfoBean();
     final ResponseInfoBean responseInfoBean = searchedUserInfo.getResponseInfoBean();
 
 
@@ -81,8 +81,8 @@
         <pwm:script>
         <pwm:script>
             <script type="text/javascript">
             <script type="text/javascript">
                 PWM_GLOBAL['startupFunctions'].push(function(){
                 PWM_GLOBAL['startupFunctions'].push(function(){
-                    PWM_VAR["helpdesk_obfuscatedDN"] = '<%=JspUtility.getAttribute(pageContext, PwmRequest.Attribute.HelpdeskObfuscatedDN)%>';
-                    PWM_VAR["helpdesk_username"] = '<%=StringUtil.escapeJS((String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.HelpdeskUsername))%>';
+                    PWM_VAR["helpdesk_obfuscatedDN"] = '<%=JspUtility.getAttribute(pageContext, PwmRequestAttribute.HelpdeskObfuscatedDN)%>';
+                    PWM_VAR["helpdesk_username"] = '<%=StringUtil.escapeJS((String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.HelpdeskUsername))%>';
                 });
                 });
             </script>
             </script>
         </pwm:script>
         </pwm:script>
@@ -585,7 +585,7 @@
                         </button>
                         </button>
                         <% } %>
                         <% } %>
                         <% } %>
                         <% } %>
-                        <% if ((Boolean)JspUtility.getPwmRequest(pageContext).getAttribute(PwmRequest.Attribute.HelpdeskVerificationEnabled) == true) { %>
+                        <% if ((Boolean)JspUtility.getPwmRequest(pageContext).getAttribute(PwmRequestAttribute.HelpdeskVerificationEnabled) == true) { %>
                         <button id="sendTokenButton" class="helpdesk-detail-btn btn">
                         <button id="sendTokenButton" class="helpdesk-detail-btn btn">
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-mobile-phone"></span></pwm:if>
                             <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-mobile-phone"></span></pwm:if>
                             <pwm:display key="Button_Verify"/>
                             <pwm:display key="Button_Verify"/>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/helpdesk.jsp

@@ -1,4 +1,5 @@
 <%@ page import="password.pwm.http.JspUtility" %>
 <%@ page import="password.pwm.http.JspUtility" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -44,7 +45,7 @@
                     <span style="color: #ffcd59;" class="pwm-icon pwm-icon-lg pwm-icon-exclamation-circle"></span>
                     <span style="color: #ffcd59;" class="pwm-icon pwm-icon-lg pwm-icon-exclamation-circle"></span>
                 </div>
                 </div>
 
 
-                <% if ((Boolean)JspUtility.getPwmRequest(pageContext).getAttribute(PwmRequest.Attribute.HelpdeskVerificationEnabled)) { %>
+                <% if ((Boolean)JspUtility.getPwmRequest(pageContext).getAttribute(PwmRequestAttribute.HelpdeskVerificationEnabled)) { %>
                 <div id="verifications-btn">
                 <div id="verifications-btn">
                     <button class="btn" id="button-show-current-verifications">
                     <button class="btn" id="button-show-current-verifications">
                         <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>
                         <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-check"></span></pwm:if>

+ 3 - 3
src/main/webapp/WEB-INF/jsp/login.jsp

@@ -42,10 +42,10 @@
             <%@ include file="/WEB-INF/jsp/fragment/ldap-selector.jsp" %>
             <%@ include file="/WEB-INF/jsp/fragment/ldap-selector.jsp" %>
             <div class="sign-in">
             <div class="sign-in">
                 <div class="formFieldWrapper">
                 <div class="formFieldWrapper">
-                    <input type="text" name="username" id="username" placeholder="<pwm:display key="Field_Username"/>" class="inputfield" <pwm:autofocus/> required="required">
+                    <input type="text" name="username" id="username" title="<pwm:display key="Field_Username"/>" placeholder="<pwm:display key="Field_Username"/>" class="inputfield" <pwm:autofocus/> required="required">
                 </div>
                 </div>
                 <div class="formFieldWrapper">
                 <div class="formFieldWrapper">
-                    <input type="<pwm:value name="<%=PwmValue.passwordFieldType%>"/>" name="password" id="password" placeholder="<pwm:display key="Field_Password"/>" required="required" class="inputfield passwordfield"/>
+                    <input type="<pwm:value name="<%=PwmValue.passwordFieldType%>"/>" name="password" id="password" title="<pwm:display key="Field_Password"/>" placeholder="<pwm:display key="Field_Password"/>" required="required" class="inputfield passwordfield"/>
                 </div>
                 </div>
                 <%@ include file="/WEB-INF/jsp/fragment/captcha-embed.jsp"%>
                 <%@ include file="/WEB-INF/jsp/fragment/captcha-embed.jsp"%>
                 <div class="buttonbar">
                 <div class="buttonbar">
@@ -61,7 +61,7 @@
                 </div>
                 </div>
             </div>
             </div>
         </form>
         </form>
-        <pwm:if test="<%=PwmIfTest.endUserFunctionalityAvaiable%>">
+        <pwm:if test="<%=PwmIfTest.endUserFunctionalityAvailable%>">
             <pwm:if test="<%=PwmIfTest.showLoginOptions%>">
             <pwm:if test="<%=PwmIfTest.showLoginOptions%>">
                 <table class="noborder">
                 <table class="noborder">
                     <pwm:if test="<%=PwmIfTest.forgottenPasswordEnabled%>">
                     <pwm:if test="<%=PwmIfTest.forgottenPasswordEnabled%>">

+ 3 - 3
src/main/webapp/WEB-INF/jsp/logout-public.jsp

@@ -1,4 +1,4 @@
-<%--
+<%@ page import="password.pwm.http.PwmRequestAttribute" %><%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
   ~
   ~
@@ -40,8 +40,8 @@
         <br/>
         <br/>
         <br/>
         <br/>
         <p>
         <p>
-            <% if (JspUtility.getAttribute(pageContext,PwmRequest.Attribute.NextUrl) != null) {%>
-            <form action="<%=JspUtility.getAttribute(pageContext, PwmRequest.Attribute.NextUrl)%>" method="GET">
+            <% if (JspUtility.getAttribute(pageContext,PwmRequestAttribute.NextUrl) != null) {%>
+            <form action="<%=JspUtility.getAttribute(pageContext, PwmRequestAttribute.NextUrl)%>" method="GET">
                 <button type="submit" name="button" class="btn" <pwm:autofocus/> id="submitBtn">
                 <button type="submit" name="button" class="btn" <pwm:autofocus/> id="submitBtn">
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                     <pwm:display key="Button_Continue"/>
                     <pwm:display key="Button_Continue"/>

+ 3 - 3
src/main/webapp/WEB-INF/jsp/logout.jsp

@@ -1,4 +1,4 @@
-<%--
+<%@ page import="password.pwm.http.PwmRequestAttribute" %><%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
   ~
   ~
@@ -40,8 +40,8 @@
         <br/>
         <br/>
         <br/>
         <br/>
         <p>
         <p>
-        <% if (JspUtility.getAttribute(pageContext,PwmRequest.Attribute.NextUrl) != null) {%>
-        <form action="<%=JspUtility.getAttribute(pageContext, PwmRequest.Attribute.NextUrl)%>" method="GET">
+        <% if (JspUtility.getAttribute(pageContext,PwmRequestAttribute.NextUrl) != null) {%>
+        <form action="<%=JspUtility.getAttribute(pageContext, PwmRequestAttribute.NextUrl)%>" method="GET">
             <button type="submit" name="button" class="btn" <pwm:autofocus/> id="submitBtn">
             <button type="submit" name="button" class="btn" <pwm:autofocus/> id="submitBtn">
                 <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                 <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-forward"></span></pwm:if>
                 <pwm:display key="Button_Continue"/>
                 <pwm:display key="Button_Continue"/>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/newuser-agreement.jsp

@@ -1,4 +1,5 @@
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -36,7 +37,7 @@
     <div id="centerbody">
     <div id="centerbody">
         <div id="page-content-title"><pwm:display key="Title_NewUser" displayIfMissing="true"/></div>
         <div id="page-content-title"><pwm:display key="Title_NewUser" displayIfMissing="true"/></div>
         <%@ include file="fragment/message.jsp" %>
         <%@ include file="fragment/message.jsp" %>
-        <% final String expandedText = (String)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.AgreementText); %>
+        <% final String expandedText = (String)JspUtility.getAttribute(pageContext, PwmRequestAttribute.AgreementText); %>
         <br/><br/>
         <br/><br/>
         <div id="agreementText" class="agreementText"><%= expandedText %></div>
         <div id="agreementText" class="agreementText"><%= expandedText %></div>
         <div class="buttonbar">
         <div class="buttonbar">

+ 2 - 2
src/main/webapp/WEB-INF/jsp/newuser-profilechoice.jsp

@@ -3,7 +3,7 @@
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="java.util.Map" %>
 <%@ page import="java.util.Map" %>
-<%@ page import="password.pwm.http.servlet.CommandServlet" %>
+<%@ page import="password.pwm.http.servlet.command.CommandServlet" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -70,7 +70,7 @@
         <br/>
         <br/>
         <div class="buttonbar">
         <div class="buttonbar">
             <% if (ContextManager.getPwmApplication(session).getConfig().readSettingAsBoolean(password.pwm.config.PwmSetting.DISPLAY_CANCEL_BUTTON)) { %>
             <% if (ContextManager.getPwmApplication(session).getConfig().readSettingAsBoolean(password.pwm.config.PwmSetting.DISPLAY_CANCEL_BUTTON)) { %>
-            <form action="<pwm:url url='<%=PwmServletDefinition.Command.servletUrl()%>' addContext="true"/>" method="get"
+            <form action="<pwm:url url='<%=PwmServletDefinition.PublicCommand.servletUrl()%>' addContext="true"/>" method="get"
                   enctype="application/x-www-form-urlencoded" name="search" class="pwm-form">
                   enctype="application/x-www-form-urlencoded" name="search" class="pwm-form">
                 <button class="btn" type="submit" name="submitBtn">
                 <button class="btn" type="submit" name="submitBtn">
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-times"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-times"></span></pwm:if>

+ 2 - 1
src/main/webapp/WEB-INF/jsp/newuser.jsp

@@ -1,6 +1,7 @@
 <%@ page import="password.pwm.http.servlet.newuser.NewUserServlet" %>
 <%@ page import="password.pwm.http.servlet.newuser.NewUserServlet" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.servlet.PwmServletDefinition" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%--
 <%--
   ~ Password Management Servlets (PWM)
   ~ Password Management Servlets (PWM)
   ~ http://www.pwm-project.org
   ~ http://www.pwm-project.org
@@ -53,7 +54,7 @@
                 </button>
                 </button>
                 <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
                 <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
 
 
-                <% if ((Boolean)JspUtility.getAttribute(pageContext, PwmRequest.Attribute.NewUser_FormShowBackButton)) { %>
+                <% if ((Boolean)JspUtility.getAttribute(pageContext, PwmRequestAttribute.NewUser_FormShowBackButton)) { %>
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                 <button type="button" id="button-goBack" name="button-goBack" class="btn" >
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-backward"></span></pwm:if>
                     <pwm:display key="Button_GoBack"/>
                     <pwm:display key="Button_GoBack"/>

+ 4 - 3
src/main/webapp/WEB-INF/jsp/setupotpsecret.jsp

@@ -24,6 +24,7 @@
 <%@page import="password.pwm.http.bean.SetupOtpBean"%>
 <%@page import="password.pwm.http.bean.SetupOtpBean"%>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
 <%@ page import="password.pwm.util.operations.otp.OTPUserRecord" %>
 <%@ page import="password.pwm.util.operations.otp.OTPUserRecord" %>
+<%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true"
 <%@ page language="java" session="true" isThreadSafe="true"
          contentType="text/html" %>
          contentType="text/html" %>
@@ -57,15 +58,15 @@
         <div data-dojo-type="dijit.layout.TabContainer" data-dojo-props="doLayout: false, persist: true">
         <div data-dojo-type="dijit.layout.TabContainer" data-dojo-props="doLayout: false, persist: true">
             <div data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Display_SetupOtp_Android_Title"/>">
             <div data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Display_SetupOtp_Android_Title"/>">
                 <pwm:display key="Display_SetupOtp_Android_Steps"/>
                 <pwm:display key="Display_SetupOtp_Android_Steps"/>
-                <img class="qrcodeimage" src="<%=JspUtility.getAttribute(pageContext,PwmRequest.Attribute.SetupOtp_QrCodeValue)%>" alt="QR Code"/>
+                <img class="qrcodeimage" src="<%=JspUtility.getAttribute(pageContext,PwmRequestAttribute.SetupOtp_QrCodeValue)%>" alt="QR Code"/>
             </div>
             </div>
             <div data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Display_SetupOtp_iPhone_Title"/>">
             <div data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Display_SetupOtp_iPhone_Title"/>">
                 <pwm:display key="Display_SetupOtp_iPhone_Steps"/>
                 <pwm:display key="Display_SetupOtp_iPhone_Steps"/>
-                <img class="qrcodeimage" src="<%=JspUtility.getAttribute(pageContext,PwmRequest.Attribute.SetupOtp_QrCodeValue)%>" alt="QR Code"/>
+                <img class="qrcodeimage" src="<%=JspUtility.getAttribute(pageContext,PwmRequestAttribute.SetupOtp_QrCodeValue)%>" alt="QR Code"/>
             </div>
             </div>
             <div data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Display_SetupOtp_Other_Title"/>">
             <div data-dojo-type="dijit.layout.ContentPane" title="<pwm:display key="Display_SetupOtp_Other_Title"/>">
                 <pwm:display key="Display_SetupOtp_Other_Steps"/>
                 <pwm:display key="Display_SetupOtp_Other_Steps"/>
-                <img class="qrcodeimage" src="<%=JspUtility.getAttribute(pageContext,PwmRequest.Attribute.SetupOtp_QrCodeValue)%>" alt="QR Code"/>
+                <img class="qrcodeimage" src="<%=JspUtility.getAttribute(pageContext,PwmRequestAttribute.SetupOtp_QrCodeValue)%>" alt="QR Code"/>
                 <table border="0" style="width: 300px; margin-right: auto; margin-left: auto">
                 <table border="0" style="width: 300px; margin-right: auto; margin-left: auto">
                     <tr valign="top">
                     <tr valign="top">
                         <td><b><pwm:display key="Field_OTP_Identifier"/></b></td>
                         <td><b><pwm:display key="Field_OTP_Identifier"/></b></td>

Неке датотеке нису приказане због велике количине промена