浏览代码

misc fixes

jrivard 10 年之前
父节点
当前提交
74c64941de
共有 64 个文件被更改,包括 1017 次插入862 次删除
  1. 1 0
      pwm/servlet/src/password/pwm/AppProperty.java
  2. 1 0
      pwm/servlet/src/password/pwm/AppProperty.properties
  3. 1 1
      pwm/servlet/src/password/pwm/PwmConstants.java
  4. 7 6
      pwm/servlet/src/password/pwm/config/ActionConfiguration.java
  5. 2 11
      pwm/servlet/src/password/pwm/config/Configuration.java
  6. 3 3
      pwm/servlet/src/password/pwm/config/ConfigurationReader.java
  7. 9 9
      pwm/servlet/src/password/pwm/config/FormConfiguration.java
  8. 5 8
      pwm/servlet/src/password/pwm/config/FormUtility.java
  9. 3 3
      pwm/servlet/src/password/pwm/config/PwmSetting.xml
  10. 3 3
      pwm/servlet/src/password/pwm/config/StoredConfiguration.java
  11. 0 2
      pwm/servlet/src/password/pwm/http/ContextManager.java
  12. 1 0
      pwm/servlet/src/password/pwm/http/PwmRequest.java
  13. 0 15
      pwm/servlet/src/password/pwm/http/bean/HelpdeskBean.java
  14. 7 5
      pwm/servlet/src/password/pwm/http/servlet/ConfigEditorServlet.java
  15. 3 3
      pwm/servlet/src/password/pwm/http/servlet/ConfigManagerServlet.java
  16. 46 10
      pwm/servlet/src/password/pwm/http/servlet/ForgottenPasswordServlet.java
  17. 2 1
      pwm/servlet/src/password/pwm/http/servlet/GuestRegistrationServlet.java
  18. 23 10
      pwm/servlet/src/password/pwm/http/servlet/HelpdeskServlet.java
  19. 13 10
      pwm/servlet/src/password/pwm/http/servlet/NewUserServlet.java
  20. 39 3
      pwm/servlet/src/password/pwm/http/servlet/PeopleSearchServlet.java
  21. 14 10
      pwm/servlet/src/password/pwm/http/servlet/ResourceFileServlet.java
  22. 7 5
      pwm/servlet/src/password/pwm/http/servlet/UpdateProfileServlet.java
  23. 1 1
      pwm/servlet/src/password/pwm/i18n/Admin.properties
  24. 1 1
      pwm/servlet/src/password/pwm/i18n/Error.properties
  25. 6 494
      pwm/servlet/src/password/pwm/ldap/auth/AuthenticationRequest.java
  26. 10 0
      pwm/servlet/src/password/pwm/ldap/auth/AuthenticationUtility.java
  27. 512 0
      pwm/servlet/src/password/pwm/ldap/auth/LDAPAuthenticationRequest.java
  28. 4 4
      pwm/servlet/src/password/pwm/ldap/auth/SessionAuthenticator.java
  29. 2 2
      pwm/servlet/src/password/pwm/token/TokenService.java
  30. 6 3
      pwm/servlet/src/password/pwm/util/PwmPasswordRuleValidator.java
  31. 1 1
      pwm/servlet/src/password/pwm/util/XmlUtil.java
  32. 1 0
      pwm/servlet/src/password/pwm/ws/server/rest/RestAppDataServer.java
  33. 1 1
      pwm/servlet/src/password/pwm/ws/server/rest/RestStatusServer.java
  34. 1 1
      pwm/servlet/web/WEB-INF/jsp/admin-logview-window.jsp
  35. 5 1
      pwm/servlet/web/WEB-INF/jsp/admin-tokenlookup.jsp
  36. 16 21
      pwm/servlet/web/WEB-INF/jsp/captcha.jsp
  37. 3 3
      pwm/servlet/web/WEB-INF/jsp/changepassword-wait.jsp
  38. 1 1
      pwm/servlet/web/WEB-INF/jsp/configguide-password.jsp
  39. 1 1
      pwm/servlet/web/WEB-INF/jsp/configguide-start.jsp
  40. 7 0
      pwm/servlet/web/WEB-INF/jsp/fragment/footer.jsp
  41. 15 15
      pwm/servlet/web/WEB-INF/jsp/fragment/setupresponses-form.jsp
  42. 1 1
      pwm/servlet/web/WEB-INF/jsp/helpdesk-detail.jsp
  43. 3 13
      pwm/servlet/web/WEB-INF/jsp/helpdesk.jsp
  44. 2 9
      pwm/servlet/web/WEB-INF/jsp/logout.jsp
  45. 4 3
      pwm/servlet/web/WEB-INF/jsp/newuser-wait.jsp
  46. 2 34
      pwm/servlet/web/WEB-INF/jsp/peoplesearch.jsp
  47. 6 4
      pwm/servlet/web/WEB-INF/jsp/setupresponses-helpdesk.jsp
  48. 4 4
      pwm/servlet/web/WEB-INF/jsp/setupresponses.jsp
  49. 3 3
      pwm/servlet/web/public/health.jsp
  50. 30 15
      pwm/servlet/web/public/index.jsp
  51. 1 1
      pwm/servlet/web/public/reference/license.jsp
  52. 2 8
      pwm/servlet/web/public/reference/localeinfo.jsp
  53. 1 7
      pwm/servlet/web/public/reference/referencedoc.jsp
  54. 3 6
      pwm/servlet/web/public/reference/rest.jsp
  55. 二进制
      pwm/servlet/web/public/resources/dojo.zip
  56. 1 1
      pwm/servlet/web/public/resources/js/changepassword.js
  57. 2 1
      pwm/servlet/web/public/resources/js/configeditor-settings.js
  58. 1 4
      pwm/servlet/web/public/resources/js/configeditor.js
  59. 1 9
      pwm/servlet/web/public/resources/js/configmanager.js
  60. 33 13
      pwm/servlet/web/public/resources/js/helpdesk.js
  61. 44 4
      pwm/servlet/web/public/resources/js/main.js
  62. 31 14
      pwm/servlet/web/public/resources/js/peoplesearch.js
  63. 53 39
      pwm/servlet/web/public/resources/js/responses.js
  64. 5 1
      pwm/servlet/web/public/resources/style.css

+ 1 - 0
pwm/servlet/src/password/pwm/AppProperty.java

@@ -42,6 +42,7 @@ public enum AppProperty {
     CLIENT_FORM_NONCE_LENGTH                        ("client.formNonce.length"),
     CLIENT_WARNING_HEADER_SHOW                      ("client.warningHeader.show"),
     CLIENT_PW_SHOW_REVERT_TIMEOUT                   ("client.pwShowRevertTimeout"),
+    CLIENT_JS_ENABLE_HTML5DIALOG                    ("client.js.enableHtml5Dialog"),
     CLIENT_JSP_SHOW_ICONS                           ("client.jsp.showIcons"),
     CONFIG_MAX_JDBC_JAR_SIZE                        ("config.maxJdbcJarSize"),
     CONFIG_RELOAD_ON_CHANGE                         ("config.reloadOnChange"),

+ 1 - 0
pwm/servlet/src/password/pwm/AppProperty.properties

@@ -36,6 +36,7 @@ client.formNonce.enable=true
 client.formNonce.length=10
 client.warningHeader.show=true
 client.pwShowRevertTimeout=45000
+client.js.enableHtml5Dialog=true
 client.jsp.showIcons=true
 config.reloadOnChange=true
 config.maxJdbcJarSize=10240000

+ 1 - 1
pwm/servlet/src/password/pwm/PwmConstants.java

@@ -109,7 +109,7 @@ public abstract class PwmConstants {
     public static final String LDAP_AD_PASSWORD_POLICY_CONTROL_ASN = "1.2.840.113556.1.4.2066";
     public static final String PROFILE_ID_ALL = "all";
 
-    public static final String TOKEN_KEY_PWD_CHG_DATE = "lastPwdChange";
+    public static final String TOKEN_KEY_PWD_CHG_DATE = "_lastPwdChange";
     public static final float JAVA_MINIMUM_VERSION = (float)1.6;
 
     public static final String HTTP_BASIC_AUTH_PREFIX = readPwmConstantsBundle("httpHeaderAuthorizationBasic");

+ 7 - 6
pwm/servlet/src/password/pwm/config/ActionConfiguration.java

@@ -22,6 +22,7 @@
 
 package password.pwm.config;
 
+import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 
@@ -103,26 +104,26 @@ public class ActionConfiguration implements Serializable {
 
     public void validate() throws PwmOperationalException {
         if (this.getName() == null || this.getName().length() < 1) {
-            throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," form field name is required");
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null, new String[]{" form field name is required"}));
         }
 
         if (this.getType() == null) {
-            throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," type is required for field " + this.getName());
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null, new String[]{" type is required for field " + this.getName()}));
         }
 
         if (this.getType() == Type.webservice) {
             if (this.getMethod() == null) {
-                throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," method for webservice action " + this.getName() + " is required");
+                throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null, new String[]{" method for webservice action " + this.getName() + " is required"}));
             }
             if (this.getUrl() == null || this.getUrl().length() < 1) {
-                throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," url for webservice action " + this.getName() + " is required");
+                throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null, new String[]{" url for webservice action " + this.getName() + " is required"}));
             }
         } else if (this.getType() == Type.ldap) {
             if (this.getAttributeName() == null || this.getAttributeName().length() < 1) {
-                throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," attribute name for ldap action " + this.getName() + " is required");
+                throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null, new String[]{" attribute name for ldap action " + this.getName() + " is required"}));
             }
             if (this.getAttributeValue() == null || this.getAttributeValue().length() < 1) {
-                throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," attribute value for ldap action " + this.getName() + " is required");
+                throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, null, new String[]{" attribute value for ldap action " + this.getName() + " is required"}));
             }
         }
     }

+ 2 - 11
pwm/servlet/src/password/pwm/config/Configuration.java

@@ -356,16 +356,8 @@ public class Configuration implements Serializable, SettingReader {
             throw new IllegalArgumentException("unknown challenge profileID specified: " + profile);
         }
 
-        if (!dataCache.challengeProfile.containsKey(profile)) {
-            dataCache.challengeProfile.put(profile,new HashMap<Locale,ChallengeProfile>());
-        }
-
-        if (dataCache.challengeProfile.get(profile).containsKey(locale)) {
-            return dataCache.challengeProfile.get(profile).get(locale);
-        }
-
+        // challengeProfile challengeSet's are mutable (question text) and can not be cached.
         final ChallengeProfile challengeProfile = ChallengeProfile.readChallengeProfileFromConfig(profile, locale, storedConfiguration);
-        dataCache.challengeProfile.get(profile).put(locale,challengeProfile);
         return challengeProfile;
     }
 
@@ -560,7 +552,7 @@ public class Configuration implements Serializable, SettingReader {
 
     public LdapProfile getDefaultLdapProfile() throws PwmUnrecoverableException {
         if (getLdapProfiles().isEmpty()) {
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"no ldap profiles are defined"));
+            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"no ldap profiles are defined"}));
         }
         return getLdapProfiles().values().iterator().next();
     }
@@ -733,7 +725,6 @@ public class Configuration implements Serializable, SettingReader {
 
     private static class DataCache implements Serializable {
         private final Map<String,Map<Locale,PwmPasswordPolicy>> cachedPasswordPolicy = new HashMap<>();
-        private final Map<String,Map<Locale,ChallengeProfile>> challengeProfile = new HashMap<>();
         private Map<Locale,String> localeFlagMap = null;
         private Map<String,LdapProfile> ldapProfiles;
         private final Map<PwmSetting, StoredValue> settings = new EnumMap<>(PwmSetting.class);

+ 3 - 3
pwm/servlet/src/password/pwm/config/ConfigurationReader.java

@@ -112,7 +112,7 @@ public class ConfigurationReader {
             theFileData = new FileInputStream(configFile);
         } catch (Exception e) {
             final String errorMsg = "unable to read configuration file: " + e.getMessage();
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,errorMsg);
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorMsg});
             this.configMode = PwmApplication.MODE.ERROR;
             throw new PwmUnrecoverableException(errorInformation);
         }
@@ -122,7 +122,7 @@ public class ConfigurationReader {
             storedConfiguration = StoredConfiguration.fromXml(theFileData);
         } catch (PwmUnrecoverableException e) {
             final String errorMsg = "unable to parse configuration file: " + e.getMessage();
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,errorMsg);
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorMsg});
             this.configMode = PwmApplication.MODE.ERROR;
             throw new PwmUnrecoverableException(errorInformation);
         }
@@ -130,7 +130,7 @@ public class ConfigurationReader {
         final List<String> validationErrorMsgs = storedConfiguration.validateValues();
         if (validationErrorMsgs != null && !validationErrorMsgs.isEmpty()) {
             final String errorMsg = "value error in config file, please investigate: " + validationErrorMsgs.get(0);
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,errorMsg);
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorMsg});
             this.configMode = PwmApplication.MODE.ERROR;
             throw new PwmUnrecoverableException(errorInformation);
         }

+ 9 - 9
pwm/servlet/src/password/pwm/config/FormConfiguration.java

@@ -65,7 +65,7 @@ public class FormConfiguration implements Serializable {
             throws PwmOperationalException
     {
         if (config == null) {
-            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"input cannot be null"));
+            throw new NullPointerException("config can not be null");
         }
 
         final FormConfiguration formItem = new FormConfiguration();
@@ -83,7 +83,7 @@ public class FormConfiguration implements Serializable {
             try {
                 formItem.type = Type.valueOf(typeStr.toLowerCase());
             } catch (IllegalArgumentException e) {
-                throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"unknown type for form config: " + typeStr));
+                throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"unknown type for form config: " + typeStr}));
             }
         }
 
@@ -91,14 +91,14 @@ public class FormConfiguration implements Serializable {
         try {
             formItem.minimumLength = Integer.parseInt(st.nextToken());
         } catch (NumberFormatException e) {
-            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"invalid minimum length type for form config: " + e.getMessage()));
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"invalid minimum length type for form config: " + e.getMessage()}));
         }
 
         //maximum length
         try {
             formItem.maximumLength = Integer.parseInt(st.nextToken());
         } catch (NumberFormatException e) {
-            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"invalid maximum length type for form config: " + e.getMessage()));
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"invalid maximum length type for form config: " + e.getMessage()}));
         }
 
         //required
@@ -112,28 +112,28 @@ public class FormConfiguration implements Serializable {
 
     public void validate() throws PwmOperationalException {
         if (this.getName() == null || this.getName().length() < 1) {
-            throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," form field name is required");
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" form field name is required"}));
         }
 
         if (this.getType() == null) {
-            throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," type is required for field " + this.getName());
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" type is required for field " + this.getName()}));
         }
 
         if (labels == null || this.labels.isEmpty() || this.getLabel(PwmConstants.DEFAULT_LOCALE) == null || this.getLabel(PwmConstants.DEFAULT_LOCALE).length() < 1) {
-            throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," a default label value is required for " + this.getName());
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" a default label value is required for " + this.getName()}));
         }
 
         if (this.getRegex() != null && this.getRegex().length() > 0) {
             try {
                 Pattern.compile(this.getRegex());
             } catch (PatternSyntaxException e) {
-                throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," regular expression for '" + this.getName() + " ' is not valid: " + e.getMessage());
+                throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" regular expression for '" + this.getName() + " ' is not valid: " + e.getMessage()}));
             }
         }
 
         if (this.getType() == Type.select) {
             if (this.getSelectOptions() == null || this.getSelectOptions().isEmpty()) {
-                throw new PwmOperationalException(PwmError.CONFIG_FORMAT_ERROR," field '" + this.getName() + " ' is type select, but no select options are defined");
+                throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{" field '" + this.getName() + " ' is type select, but no select options are defined"}));
             }
         }
     }

+ 5 - 8
pwm/servlet/src/password/pwm/config/FormUtility.java

@@ -117,17 +117,16 @@ public class FormUtility {
             final PwmApplication pwmApplication,
             final Map<FormConfiguration, String> formValues,
             final Locale locale,
-            final Collection<UserIdentity> excludeDN
+            final Collection<UserIdentity> excludeDN,
+            final boolean allowResultCaching
     )
             throws PwmDataValidationException, PwmUnrecoverableException
     {
         final Map<String, String> filterClauses = new HashMap<>();
         final Map<String,String> labelMap = new HashMap<>();
-        final List<String> uniqueAttributes = new ArrayList();
         for (final FormConfiguration formItem : formValues.keySet()) {
             if (formItem.isUnique() && !formItem.isReadonly()) {
                 if (formItem.getType() != FormConfiguration.Type.hidden) {
-                    uniqueAttributes.add(formItem.getName());
                     final String value = formValues.get(formItem);
                     if (value != null && value.length() > 0) {
                         filterClauses.put(formItem.getName(), value);
@@ -167,7 +166,7 @@ public class FormUtility {
         final CacheKey cacheKey = CacheKey.makeCacheKey(
                 Validator.class, null, "attr_unique_check_" + filter.toString()
         );
-        if (cacheService != null) {
+        if (allowResultCaching && cacheService != null) {
             final String cacheValue = cacheService.get(cacheKey);
             if (cacheValue != null) {
                 if (NEGATIVE_CACHE_HIT.equals(cacheValue)) {
@@ -244,7 +243,7 @@ public class FormUtility {
             }
             throw new PwmDataValidationException(e.getErrorInformation());
         }
-        if (cacheService != null) {
+        if (allowResultCaching && cacheService != null) {
             cacheService.put(cacheKey, cachePolicy, NEGATIVE_CACHE_HIT);
         }
     }
@@ -256,8 +255,6 @@ public class FormUtility {
      *
      * @param formValues - a Map containing String keys of parameter names and ParamConfigs as values
      * @throws password.pwm.error.PwmDataValidationException - If there is a problem with any of the fields
-     * @throws com.novell.ldapchai.exception.ChaiUnavailableException
-     *                             if ldap server becomes unavailable
      * @throws password.pwm.error.PwmUnrecoverableException
      *                             if an unexpected error occurs
      */
@@ -279,7 +276,7 @@ public class FormUtility {
     {
         if (formElements == null || formElements.isEmpty()) {
             final String errorMsg = "can not auto-generate ldap search filter for form with no required form items";
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,errorMsg);
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorMsg});
             throw new PwmUnrecoverableException(errorInformation);
         }
 

+ 3 - 3
pwm/servlet/src/password/pwm/config/PwmSetting.xml

@@ -2980,7 +2980,7 @@
         <label>PeopleSearch Result Limit</label>
         <description><![CDATA[Maximum number of records to return while searching.]]></description>
         <default>
-            <value>100</value>
+            <value>200</value>
         </default>
         <options>
             <option value="min">1</option>
@@ -3036,9 +3036,9 @@
     </setting>
     <setting key="peopleSearch.idleTimeout" level="1">
         <label>Idle Timeout Seconds for PeopleSearch Users</label>
-        <description><![CDATA[Number of seconds after which an authenticated session becomes unauthenticated.   Session Idle timeout will be set to this value once a user successfully access the people search module.]]></description>
+        <description><![CDATA[Number of seconds after which an authenticated session becomes unauthenticated.   Session idle timeout will be set to this value while a user user is accessing the people search module.  If the value is set to 0, then the application wide idle timeout value will be used.  If a user is using the PeopleSearch module without authenticating, then no timeout is applied.]]></description>
         <default>
-            <value>3600</value>
+            <value>0</value>
         </default>
     </setting>
     <setting key="ldap.edirectory.enableNmas" level="1" required="true">

+ 3 - 3
pwm/servlet/src/password/pwm/config/StoredConfiguration.java

@@ -146,7 +146,7 @@ public class StoredConfiguration implements Serializable {
             ConfigurationCleaner.cleanup(newConfiguration);
         } catch (Exception e) {
             final String errorMsg = "error reading configuration file format, error=" + e.getMessage();
-            final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,errorMsg);
+            final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorMsg});
             throw new PwmUnrecoverableException(errorInfo);
         }
 
@@ -817,11 +817,11 @@ public class StoredConfiguration implements Serializable {
             throws PwmOperationalException
     {
         if (password == null || password.isEmpty()) {
-            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"can not set blank password"));
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"can not set blank password"}));
         }
         final String trimmedPassword = password.trim();
         if (trimmedPassword.length() < 1) {
-            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"can not set blank password"));
+            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"can not set blank password"}));
         }
 
 

+ 0 - 2
pwm/servlet/src/password/pwm/http/ContextManager.java

@@ -32,7 +32,6 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.servlet.ResourceFileServlet;
 import password.pwm.util.Helper;
 import password.pwm.util.PwmRandom;
 import password.pwm.util.logging.PwmLogger;
@@ -331,7 +330,6 @@ public class ContextManager implements Serializable {
 
             LOGGER.info("beginning application restart");
             try {
-                ResourceFileServlet.clearCache(servletContext);
                 shutdown();
             } catch (Exception e) {
                 LOGGER.fatal("unexpected error during shutdown: " + e.getMessage(),e);

+ 1 - 0
pwm/servlet/src/password/pwm/http/PwmRequest.java

@@ -83,6 +83,7 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
         HIDE_HEADER_BUTTONS,
         HIDE_HEADER_WARNINGS,
         NO_REQ_COUNTER,
+        NO_IDLE_TIMEOUT,
         ALWAYS_EXPAND_MESSAGE_TEXT,
     }
 

+ 0 - 15
pwm/servlet/src/password/pwm/http/bean/HelpdeskBean.java

@@ -27,14 +27,12 @@ import password.pwm.config.FormConfiguration;
 import password.pwm.event.UserAuditRecord;
 
 import java.io.Serializable;
-import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
 public class HelpdeskBean implements PwmSessionBean {
     private String searchString;
-    private Map<String,String> searchColumnHeaders = Collections.emptyMap();
     private HelpdeskDetailInfo heldpdeskDetailInfo = new HelpdeskDetailInfo();
 
     public static class HelpdeskDetailInfo implements Serializable {
@@ -140,17 +138,4 @@ public class HelpdeskBean implements PwmSessionBean {
     public void setHeldpdeskDetailInfo(HelpdeskDetailInfo heldpdeskDetailInfo) {
         this.heldpdeskDetailInfo = heldpdeskDetailInfo;
     }
-
-    public Map<String, String> getSearchColumnHeaders()
-    {
-        return searchColumnHeaders;
-    }
-
-    public void setSearchColumnHeaders(Map<String, String> searchColumnHeaders)
-    {
-        this.searchColumnHeaders = searchColumnHeaders;
-    }
-
-
-    
 }

+ 7 - 5
pwm/servlet/src/password/pwm/http/servlet/ConfigEditorServlet.java

@@ -461,15 +461,19 @@ public class ConfigEditorServlet extends PwmServlet {
         final HashMap<String, String> resultData = new HashMap<>();
         restResultBean.setData(resultData);
 
-        if (!configManagerBean.getStoredConfiguration().validateValues().isEmpty()) {
-            final String errorString = configManagerBean.getStoredConfiguration().validateValues().get(0);
-            final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, errorString);
+        final List<String> validationErrors = configManagerBean.getStoredConfiguration().validateValues();
+        if (!validationErrors.isEmpty()) {
+            final String errorString = validationErrors.get(0);
+            final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, errorString, new String[]{errorString});
             restResultBean = RestResultBean.fromError(errorInfo, pwmRequest);
+            LOGGER.error(pwmSession, "save configuration aborted, error: " + errorString);
         } else {
             try {
                 ConfigManagerServlet.saveConfiguration(pwmRequest, configManagerBean.getStoredConfiguration());
                 configManagerBean.setConfiguration(null);
                 restResultBean.setError(false);
+                configManagerBean.setConfiguration(null);
+                LOGGER.debug(pwmSession, "save configuration operation completed");
             } catch (PwmUnrecoverableException e) {
                 final ErrorInformation errorInfo = e.getErrorInformation();
                 restResultBean = RestResultBean.fromError(errorInfo, pwmRequest);
@@ -477,8 +481,6 @@ public class ConfigEditorServlet extends PwmServlet {
             }
         }
 
-        configManagerBean.setConfiguration(null);
-        LOGGER.debug(pwmSession, "save configuration operation completed");
         pwmRequest.outputJsonResult(restResultBean);
     }
 

+ 3 - 3
pwm/servlet/src/password/pwm/http/servlet/ConfigManagerServlet.java

@@ -429,7 +429,7 @@ public class ConfigManagerServlet extends PwmServlet {
         try {
             final StoredConfiguration storedConfiguration = readCurrentConfiguration(pwmRequest);
             if (!storedConfiguration.hasPassword()) {
-                final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"Please set a configuration password before locking the configuration");
+                final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"Please set a configuration password before locking the configuration"});
                 final RestResultBean restResultBean = RestResultBean.fromError(errorInfo, pwmRequest);
                 LOGGER.debug(pwmSession, errorInfo);
                 pwmRequest.outputJsonResult(restResultBean);
@@ -468,7 +468,7 @@ public class ConfigManagerServlet extends PwmServlet {
             final List<String> errorStrings = storedConfiguration.validateValues();
             if (errorStrings != null && !errorStrings.isEmpty()) {
                 final String errorString = errorStrings.get(0);
-                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, errorString));
+                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorString}));
             }
         }
 
@@ -479,7 +479,7 @@ public class ConfigManagerServlet extends PwmServlet {
         } catch (Exception e) {
             final String errorString = "error saving file: " + e.getMessage();
             LOGGER.error(pwmRequest, errorString);
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, errorString));
+            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{errorString}));
         }
 
     }

+ 46 - 10
pwm/servlet/src/password/pwm/http/servlet/ForgottenPasswordServlet.java

@@ -33,7 +33,6 @@ import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
-import password.pwm.Validator;
 import password.pwm.bean.*;
 import password.pwm.config.*;
 import password.pwm.config.option.MessageSendMethod;
@@ -166,7 +165,6 @@ public class ForgottenPasswordServlet extends PwmServlet {
         }
 
         if (processAction != null) {
-            Validator.validatePwmFormID(pwmRequest);
 
             switch (processAction) {
                 case search:
@@ -380,7 +378,7 @@ public class ForgottenPasswordServlet extends PwmServlet {
         try {
             final TokenPayload tokenPayload = pwmRequest.getPwmApplication().getTokenService().processUserEnteredCode(
                     pwmRequest.getPwmSession(),
-                    forgottenPasswordBean.getUserInfo().getUserIdentity(),
+                    forgottenPasswordBean.getUserInfo() == null ? null : forgottenPasswordBean.getUserInfo().getUserIdentity(),
                     TOKEN_NAME,
                     userEnteredCode
             );
@@ -567,7 +565,7 @@ public class ForgottenPasswordServlet extends PwmServlet {
             forwardToSearchPage(pwmRequest);
             return;
         }
-        
+
         if (!progress.getSatisfiedMethods().contains(RecoveryVerificationMethod.PREVIOUS_AUTH)) {
             if (checkAuthRecord(pwmRequest, forgottenPasswordBean.getUserInfo().getUserGuid())) {
                 LOGGER.debug(pwmRequest, "marking " + RecoveryVerificationMethod.PREVIOUS_AUTH + " method as satisfied");
@@ -1185,8 +1183,9 @@ public class ForgottenPasswordServlet extends PwmServlet {
         LOGGER.debug(pwmRequest, errorInformation);
         pwmRequest.setResponseError(errorInformation);
 
-        final UserIdentity userIdentity = forgottenPasswordBean.getUserInfo().getUserIdentity();
-        StatisticsManager.incrementStat(pwmRequest, Statistic.RECOVERY_FAILURES);
+        final UserIdentity userIdentity = forgottenPasswordBean == null || forgottenPasswordBean.getUserInfo() == null
+                ? null
+                : forgottenPasswordBean.getUserInfo().getUserIdentity();
         if (userIdentity != null) {
             SessionAuthenticator sessionAuthenticator = new SessionAuthenticator(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession());
             sessionAuthenticator.simulateBadPassword(userIdentity);
@@ -1195,6 +1194,7 @@ public class ForgottenPasswordServlet extends PwmServlet {
             pwmRequest.getPwmApplication().getIntruderManager().convenience().markAddressAndSession(
                     pwmRequest.getPwmSession());
         }
+        StatisticsManager.incrementStat(pwmRequest, Statistic.RECOVERY_FAILURES);
     }
 
     private void checkForLocaleSwitch(final PwmRequest pwmRequest, final ForgottenPasswordBean forgottenPasswordBean)
@@ -1367,7 +1367,7 @@ public class ForgottenPasswordServlet extends PwmServlet {
                 LOGGER.trace(pwmRequest, "skipping auth record cookie read, cookie name parameter is blank");
                 return false;
             }
-            
+
             final AuthenticationFilter.AuthRecord authRecord = pwmRequest.readCookie(cookieName, AuthenticationFilter.AuthRecord.class);
             if (authRecord != null) {
                 if (authRecord.getGuid() != null && !authRecord.getGuid().isEmpty() && authRecord.getGuid().equals(userGuid)) {
@@ -1380,13 +1380,49 @@ public class ForgottenPasswordServlet extends PwmServlet {
         }
         return false;
     }
-    
-    protected void forwardToSearchPage(final PwmRequest pwmRequest) 
-            throws ServletException, PwmUnrecoverableException, IOException 
+
+    protected void forwardToSearchPage(final PwmRequest pwmRequest)
+            throws ServletException, PwmUnrecoverableException, IOException
     {
         pwmRequest.addFormInfoToRequestAttr(PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM,false,false);
         pwmRequest.forwardToJsp(PwmConstants.JSP_URL.RECOVER_PASSWORD_SEARCH);
     }
+
+
+    private void verifyTokenPayload(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final UserIdentity sessionUserIdentity,
+            final TokenPayload tokenPayload
+    )
+            throws PwmOperationalException
+    {
+        if (tokenPayload.getData().containsKey(PwmConstants.TOKEN_KEY_PWD_CHG_DATE)) {
+            return;
+        }
+
+        try {
+            final Date userLastPasswordChange = PasswordUtility.determinePwdLastModified(
+                    pwmApplication,
+                    sessionLabel,
+                    sessionUserIdentity);
+            final String dateStringInToken = tokenPayload.getData().get(PwmConstants.TOKEN_KEY_PWD_CHG_DATE);
+            if (userLastPasswordChange != null && dateStringInToken != null) {
+                final String userChangeString = PwmConstants.DEFAULT_DATETIME_FORMAT.format(userLastPasswordChange);
+                if (!dateStringInToken.equalsIgnoreCase(userChangeString)) {
+                    final String errorString = "user password has changed since token issued, token rejected";
+                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_EXPIRED, errorString);
+                    throw new PwmOperationalException(errorInformation);
+                }
+            }
+        } catch (ChaiUnavailableException | PwmUnrecoverableException e) {
+            final String errorMsg = "unexpected error reading user's last password change time while validating token: " + e.getMessage();
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_TOKEN_INCORRECT, errorMsg);
+            throw new PwmOperationalException(errorInformation);
+        }
+    }
+
+
 }
 
 

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

@@ -199,7 +199,8 @@ public class GuestRegistrationServlet extends PwmServlet {
                     pwmApplication,
                     formValues,
                     ssBean.getLocale(),
-                    Collections.singletonList(guBean.getUpdateUserIdentity())
+                    Collections.singletonList(guBean.getUpdateUserIdentity()),
+                    false
             );
 
             final Date expirationDate = readExpirationFromRequest(pwmRequest);

+ 23 - 10
pwm/servlet/src/password/pwm/http/servlet/HelpdeskServlet.java

@@ -94,6 +94,7 @@ public class HelpdeskServlet extends PwmServlet {
         deleteUser(HttpMethod.POST),
         validateOtpCode(HttpMethod.POST),
         sendVerificationToken(HttpMethod.POST),
+        clientData(HttpMethod.GET),
 
         ;
 
@@ -139,17 +140,10 @@ public class HelpdeskServlet extends PwmServlet {
             return;
         }
 
-        {
-            final List<FormConfiguration> searchForm = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_SEARCH_FORM);
-            final Map<String,String> searchColumns = new LinkedHashMap<>();
-            for (final FormConfiguration formConfiguration : searchForm) {
-                searchColumns.put(formConfiguration.getName(),formConfiguration.getLabel(pwmSession.getSessionStateBean().getLocale()));
-            }
-            helpdeskBean.setSearchColumnHeaders(searchColumns);
-        }
-
         final int helpdeskIdleTimeout = (int)helpdeskProfile.readSettingAsLong(PwmSetting.HELPDESK_IDLE_TIMEOUT_SECONDS);
-        pwmSession.setSessionTimeout(pwmRequest.getHttpServletRequest().getSession(),helpdeskIdleTimeout);
+        if (helpdeskIdleTimeout > 0) {
+            pwmSession.setSessionTimeout(pwmRequest.getHttpServletRequest().getSession(), helpdeskIdleTimeout);
+        }
 
         final HelpdeskAction action = readProcessAction(pwmRequest);
         if (action != null) {
@@ -187,12 +181,31 @@ public class HelpdeskServlet extends PwmServlet {
                 case sendVerificationToken:
                     restSendVerificationTokenRequest(pwmRequest, helpdeskBean, helpdeskProfile);
                     return;
+
+                case clientData:
+                    restClientData(pwmRequest, helpdeskProfile);
+                    return;
             }
         }
 
         pwmRequest.forwardToJsp(PwmConstants.JSP_URL.HELPDESK_SEARCH);
     }
 
+    private void restClientData(final PwmRequest pwmRequest, final HelpdeskProfile helpdeskProfile)
+            throws IOException
+    {
+        final List<FormConfiguration> searchForm = helpdeskProfile.readSettingAsForm(PwmSetting.HELPDESK_SEARCH_FORM);
+        final Map<String,String> searchColumns = new LinkedHashMap<>();
+        for (final FormConfiguration formConfiguration : searchForm) {
+            searchColumns.put(formConfiguration.getName(),formConfiguration.getLabel(pwmRequest.getLocale()));
+        }
+        final HashMap<String,Object> returnValues = new HashMap<>();
+        returnValues.put("helpdesk_search_columns",searchColumns);
+        final RestResultBean restResultBean = new RestResultBean(returnValues);
+        LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
+        pwmRequest.outputJsonResult(restResultBean);
+    }
+
     private void processExecuteActionRequest(
             final PwmRequest pwmRequest,
             final HelpdeskBean helpdeskBean,

+ 13 - 10
pwm/servlet/src/password/pwm/http/servlet/NewUserServlet.java

@@ -303,7 +303,7 @@ public class NewUserServlet extends PwmServlet {
 
         try {
             final NewUserBean.NewUserForm newUserForm = NewUserFormUtils.readFromJsonRequest(pwmRequest);
-            PasswordUtility.PasswordCheckInfo passwordCheckInfo = verifyForm(pwmRequest, newUserForm);
+            PasswordUtility.PasswordCheckInfo passwordCheckInfo = verifyForm(pwmRequest, newUserForm, true);
             if (passwordCheckInfo.isPassed() && passwordCheckInfo.getMatch() == PasswordUtility.PasswordCheckInfo.MATCH_STATUS.MATCH) {
                 passwordCheckInfo = new PasswordUtility.PasswordCheckInfo(
                         Message.getLocalizedMessage(locale,
@@ -328,7 +328,8 @@ public class NewUserServlet extends PwmServlet {
 
     static PasswordUtility.PasswordCheckInfo verifyForm(
             final PwmRequest pwmRequest,
-            final NewUserBean.NewUserForm newUserForm
+            final NewUserBean.NewUserForm newUserForm,
+            final boolean allowResultCaching
     )
             throws PwmDataValidationException, PwmUnrecoverableException, ChaiUnavailableException
     {
@@ -339,7 +340,8 @@ public class NewUserServlet extends PwmServlet {
                 pwmApplication,
                 newUserForm.getFormData(),
                 locale,
-                Collections.<UserIdentity>emptyList()
+                Collections.<UserIdentity>emptyList(),
+                allowResultCaching
         );
         final UserInfoBean uiBean = new UserInfoBean();
         final NewUserProfile newUserProfile = getNewUserProfile(pwmRequest);
@@ -397,9 +399,9 @@ public class NewUserServlet extends PwmServlet {
                     LOGGER.debug(pwmRequest, "email token passed");
 
                     try {
-                        verifyForm(pwmRequest, newUserFormFromToken);
+                        verifyForm(pwmRequest, newUserFormFromToken, false);
                     } catch (PwmUnrecoverableException | PwmOperationalException e) {
-                        LOGGER.error(pwmRequest,"while reading stored form data in token payload, form validation error occured: " + e.getMessage());
+                        LOGGER.error(pwmRequest,"while reading stored form data in token payload, form validation error occurred: " + e.getMessage());
                         throw e;
                     }
 
@@ -467,7 +469,7 @@ public class NewUserServlet extends PwmServlet {
 
         try {
             NewUserBean.NewUserForm newUserForm = NewUserFormUtils.readFromRequest(pwmRequest);
-            final PasswordUtility.PasswordCheckInfo passwordCheckInfo = verifyForm(pwmRequest, newUserForm);
+            final PasswordUtility.PasswordCheckInfo passwordCheckInfo = verifyForm(pwmRequest, newUserForm, true);
             passwordCheckInfoToException(passwordCheckInfo);
             newUserBean.setNewUserForm(newUserForm);
             newUserBean.setFormPassed(true);
@@ -490,17 +492,18 @@ public class NewUserServlet extends PwmServlet {
         final PwmSession pwmSession = pwmRequest.getPwmSession();
 
         final long startTime = System.currentTimeMillis();
-        LOGGER.debug(pwmSession, "beginning createUser process for " + newUserDN);
 
         // re-perform verification before proceeding
         {
             final PasswordUtility.PasswordCheckInfo passwordCheckInfo = verifyForm(
                     pwmRequest,
-                    newUserForm
+                    newUserForm,
+                    false
             );
             passwordCheckInfoToException(passwordCheckInfo);
         }
 
+        LOGGER.debug(pwmSession, "beginning createUser process for " + newUserDN);
         final PasswordData userPassword = newUserForm.getNewUserPassword();
 
         // set up the user creation attributes
@@ -788,8 +791,8 @@ public class NewUserServlet extends PwmServlet {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         
         if (pwmApplication.getConfig().getTokenStorageMethod() == TokenStorageMethod.STORE_LDAP) {
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,
-                    "cannot generate new user tokens when storage type is configured as STORE_LDAP."));
+            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{
+                    "cannot generate new user tokens when storage type is configured as STORE_LDAP."}));
         }
 
         final NewUserBean newUserBean = pwmRequest.getPwmSession().getNewUserBean();

+ 39 - 3
pwm/servlet/src/password/pwm/http/servlet/PeopleSearchServlet.java

@@ -163,7 +163,10 @@ public class PeopleSearchServlet extends PwmServlet {
     public enum PeopleSearchActions implements ProcessAction {
         search(HttpMethod.POST),
         detail(HttpMethod.POST),
-        photo(HttpMethod.GET),;
+        photo(HttpMethod.GET),
+        clientData(HttpMethod.GET),
+
+        ;
 
         private final HttpMethod method;
 
@@ -189,7 +192,7 @@ public class PeopleSearchServlet extends PwmServlet {
     protected void processAction(
             final PwmRequest pwmRequest
     )
-            throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException 
+            throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException
     {
         final boolean publicAccessEnabled = pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_ENABLE_PUBLIC);
         if (!publicAccessEnabled) {
@@ -205,7 +208,9 @@ public class PeopleSearchServlet extends PwmServlet {
         }
 
         final int peopleSearchIdleTimeout = (int)pwmRequest.getConfig().readSettingAsLong(PwmSetting.PEOPLE_SEARCH_IDLE_TIMEOUT_SECONDS);
-        pwmRequest.getPwmSession().setSessionTimeout(pwmRequest.getHttpServletRequest().getSession(),peopleSearchIdleTimeout);
+        if (peopleSearchIdleTimeout > 0 && pwmRequest.getURL().isPrivateUrl()) {
+            pwmRequest.getPwmSession().setSessionTimeout(pwmRequest.getHttpServletRequest().getSession(), peopleSearchIdleTimeout);
+        }
 
         final PeopleSearchActions peopleSearchAction = this.readProcessAction(pwmRequest);
         if (peopleSearchAction != null) {
@@ -221,12 +226,43 @@ public class PeopleSearchServlet extends PwmServlet {
                 case photo:
                     processUserPhotoImageRequest(pwmRequest);
                     return;
+
+                case clientData:
+                    restLoadClientData(pwmRequest);
+                    return;
             }
         }
 
+        if (pwmRequest.getURL().isPublicUrl()) {
+            pwmRequest.setFlag(PwmRequest.Flag.HIDE_IDLE, true);
+            pwmRequest.setFlag(PwmRequest.Flag.NO_IDLE_TIMEOUT, true);
+        }
         pwmRequest.forwardToJsp(PwmConstants.JSP_URL.PEOPLE_SEARCH);
     }
 
+    private void restLoadClientData(
+            final PwmRequest pwmRequest
+    )
+            throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
+    {
+
+        final Map<String, String> searchColumns = new LinkedHashMap<>();
+        final String photoStyle = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_STYLE_ATTR);
+        final List<FormConfiguration> searchForm = pwmRequest.getConfig().readSettingAsForm(PwmSetting.PEOPLE_SEARCH_RESULT_FORM);
+        for (final FormConfiguration formConfiguration : searchForm) {
+            searchColumns.put(formConfiguration.getName(),
+                    formConfiguration.getLabel(pwmRequest.getLocale()));
+        }
+
+        final HashMap<String,Object> returnValues = new HashMap<>();
+        returnValues.put("peoplesearch_search_columns",searchColumns);
+        returnValues.put("photo_style_attribute",photoStyle);
+        final RestResultBean restResultBean = new RestResultBean(returnValues);
+        LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
+        pwmRequest.outputJsonResult(restResultBean);
+    };
+
+
     private void restSearchRequest(
             final PwmRequest pwmRequest
     )

+ 14 - 10
pwm/servlet/src/password/pwm/http/servlet/ResourceFileServlet.java

@@ -51,6 +51,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.*;
 import java.math.BigDecimal;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
@@ -77,12 +78,12 @@ public class ResourceFileServlet extends HttpServlet {
     private final Map<String, FileResource> customFileBundle = new HashMap<>();
     private Pattern noncePattern;
     private String nonceValue;
-    private boolean pwmApplicationInitialized = false;
+    private Date pwmAppStartupTime = null;
 
     public void init()
             throws ServletException {
         this.getServletContext().setAttribute(PwmConstants.CONTEXT_ATTR_RESOURCE_HIT_AVG, new EventRateMeter.MovingAverage(60 * 60 * 1000));
-
+        this.clearCache(getServletContext());
         final String zipFileResourceParam = this.getInitParameter("zipFileResources");
         if (zipFileResourceParam != null) {
             for (final String loopInitParam : zipFileResourceParam.split(";")) {
@@ -109,6 +110,8 @@ public class ResourceFileServlet extends HttpServlet {
     }
 
     void init(final PwmApplication pwmApplication) {
+        LOGGER.trace("initializing");
+        clearCache(getServletContext());
         final int setting_maxCacheItems = Integer.parseInt(pwmApplication.getConfig().readAppProperty(AppProperty.HTTP_RESOURCES_MAX_CACHE_ITEMS));
         setting_expireSeconds = Long.parseLong(pwmApplication.getConfig().readAppProperty(AppProperty.HTTP_RESOURCES_EXPIRATION_SECONDS));
         setting_enableGzip = Boolean.parseBoolean(pwmApplication.getConfig().readAppProperty(AppProperty.HTTP_RESOURCES_ENABLE_GZIP));
@@ -136,7 +139,7 @@ public class ResourceFileServlet extends HttpServlet {
                 e.printStackTrace();
             }
         }
-        pwmApplicationInitialized = true;
+        pwmAppStartupTime = pwmApplication.getStartupTime();
     }
 
 
@@ -151,8 +154,8 @@ public class ResourceFileServlet extends HttpServlet {
     }
 
     private static Map<CacheKey, CacheEntry> getCache(final ServletContext servletContext) {
-        Map<CacheKey, CacheEntry> cacheMap = (Map) servletContext.getAttribute(PwmConstants.CONTEXT_ATTR_RESOURCE_CACHE);
-        return cacheMap == null ? new HashMap() : cacheMap;
+        Map<CacheKey, CacheEntry> cacheMap = (Map<CacheKey, CacheEntry>) servletContext.getAttribute(PwmConstants.CONTEXT_ATTR_RESOURCE_CACHE);
+        return cacheMap == null ? new HashMap<CacheKey,CacheEntry>() : cacheMap;
     }
 
     private static EventRateMeter.MovingAverage getCacheHitRatio(final ServletContext servletContext) {
@@ -165,9 +168,10 @@ public class ResourceFileServlet extends HttpServlet {
         return cacheHitRatio;
     }
 
-    public static void clearCache(final ServletContext servletContext) {
-        final Map cache = getCache(servletContext);
-        if (cache != null) {
+    private void clearCache(final ServletContext servletContext) {
+        final Map<CacheKey, CacheEntry> cache = getCache(servletContext);
+        if (cache != null && !cache.isEmpty()) {
+            LOGGER.trace("clearing cacheMap of " + cache.size() + " items");
             cache.clear();
         }
     }
@@ -185,7 +189,7 @@ public class ResourceFileServlet extends HttpServlet {
             pwmRequest = PwmRequest.forRequest(request, response);
             sessionLabel = pwmRequest.getSessionLabel();
             pwmApplication = pwmRequest.getPwmApplication();
-            if (!pwmApplicationInitialized) {
+            if (pwmAppStartupTime == null || !pwmAppStartupTime.equals(pwmApplication.getStartupTime())) {
                 init(pwmApplication);
             }
         } catch (PwmException e) {
@@ -639,7 +643,7 @@ public class ResourceFileServlet extends HttpServlet {
         }
     }
 
-    static interface FileResource {
+    interface FileResource {
         InputStream getInputStream() throws IOException;
 
         long length();

+ 7 - 5
pwm/servlet/src/password/pwm/http/servlet/UpdateProfileServlet.java

@@ -157,7 +157,7 @@ public class UpdateProfileServlet extends PwmServlet {
             readFromJsonRequest(pwmRequest, updateProfileBean);
 
             // verify form meets the form requirements
-            verifyFormAttributes(pwmRequest, formValues);
+            verifyFormAttributes(pwmRequest, formValues, true);
         } catch (PwmOperationalException e) {
             success = false;
             userMessage = e.getErrorInformation().toUserStr(pwmRequest.getPwmSession(), pwmRequest.getPwmApplication());
@@ -220,7 +220,7 @@ public class UpdateProfileServlet extends PwmServlet {
         // validate the form data.
         try {
             // verify form meets the form requirements
-            verifyFormAttributes(pwmRequest, formValues);
+            verifyFormAttributes(pwmRequest, formValues, true);
         } catch (PwmOperationalException e) {
             LOGGER.error(pwmSession, e.getMessage());
             pwmRequest.setResponseError(e.getErrorInformation());
@@ -360,7 +360,7 @@ public class UpdateProfileServlet extends PwmServlet {
         final UserInfoBean uiBean = pwmRequest.getPwmSession().getUserInfoBean();
 
         // verify form meets the form requirements (may be redundant, but shouldn't hurt)
-        verifyFormAttributes(pwmRequest, formValues);
+        verifyFormAttributes(pwmRequest, formValues, false);
 
         // write values.
         LOGGER.info("updating profile for " + pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity());
@@ -411,7 +411,8 @@ public class UpdateProfileServlet extends PwmServlet {
 
     private static void verifyFormAttributes(
             final PwmRequest pwmRequest,
-            final Map<FormConfiguration, String> formValues
+            final Map<FormConfiguration, String> formValues,
+            final boolean allowResultCaching
     )
             throws PwmOperationalException, PwmUnrecoverableException
     {
@@ -425,7 +426,8 @@ public class UpdateProfileServlet extends PwmServlet {
                     pwmRequest.getPwmApplication(),
                     formValues,
                     userLocale,
-                    Collections.singletonList(pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity())
+                    Collections.singletonList(pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity()),
+                    allowResultCaching
             );
     }
 

+ 1 - 1
pwm/servlet/src/password/pwm/i18n/Admin.properties

@@ -25,7 +25,7 @@
 Header_AdminUser=%1%  You are logged in as administrator.
 Header_ConfigModeActive=%1% is in open configuration mode and is not secure.
 Header_TrialMode=%1% Trial.
-Header_ConfigWarningsPresent=Configuration warnings exist, click to view.
+Header_HealthWarningsPresent=System warnings exist, click to view.
 IntruderRecordType_ADDRESS=Address
 IntruderRecordType_USERNAME=Username
 IntruderRecordType_USER_ID=User DN

+ 1 - 1
pwm/servlet/src/password/pwm/i18n/Error.properties

@@ -158,7 +158,7 @@ Error_NoProfileAssigned=No profile is assigned for this operation.
 Error_ConfigUploadSuccess=File uploaded successfully
 Error_ConfigUploadFailure=File failed to upload.
 Error_ConfigSaveSuccess=Configuration saved successfully.  Application restart has been requested.  The application may be unavailable while restarting.  If the restart request fails you may need to restart the application server manually.
-Error_ConfigFormatError=Configuration format error.
+Error_ConfigFormatError=Configuration format error:  %1%
 Error_ConfigLdapFailure=Unable to connect to LDAP directory server.
 Error_ConfigLdapSuccess=Successfully connected to LDAP directory server
 

+ 6 - 494
pwm/servlet/src/password/pwm/ldap/auth/AuthenticationRequest.java

@@ -1,502 +1,14 @@
-/*
- * Password Management Servlets (PWM)
- * http://code.google.com/p/pwm/
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2015 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.ldap.auth;
 
-import com.novell.ldapchai.ChaiConstant;
-import com.novell.ldapchai.ChaiFactory;
-import com.novell.ldapchai.ChaiUser;
-import com.novell.ldapchai.exception.*;
-import com.novell.ldapchai.impl.oracleds.entry.OracleDSEntries;
-import com.novell.ldapchai.provider.ChaiProvider;
-import password.pwm.PwmApplication;
-import password.pwm.PwmConstants;
-import password.pwm.bean.SessionLabel;
-import password.pwm.bean.UserIdentity;
-import password.pwm.config.PwmSetting;
-import password.pwm.config.profile.LdapProfile;
-import password.pwm.config.profile.PwmPasswordPolicy;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
+import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.event.AuditEvent;
-import password.pwm.ldap.LdapOperationsHelper;
 import password.pwm.util.PasswordData;
-import password.pwm.util.RandomPasswordGenerator;
-import password.pwm.util.TimeDuration;
-import password.pwm.util.intruder.IntruderManager;
-import password.pwm.util.intruder.RecordType;
-import password.pwm.util.logging.PwmLogLevel;
-import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.operations.PasswordUtility;
-import password.pwm.util.stats.Statistic;
-import password.pwm.util.stats.StatisticsManager;
-
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
-
-class AuthenticationRequest {
-    private static final PwmLogger LOGGER = PwmLogger.forClass(AuthenticationRequest.class);
-    private static final String ORACLE_ATTR_PW_ALLOW_CHG_TIME = "passwordAllowChangeTime";
-
-    private final PwmApplication pwmApplication;
-    private final SessionLabel sessionLabel;
-    private final UserIdentity userIdentity;
-    private final AuthenticationType requestedAuthType;
-
-    private ChaiProvider userProvider;
-    private AuthenticationStrategy strategy = AuthenticationStrategy.BIND;
-    private Date startTime;
-
-    private static int counter = 0;
-    private int operationNumber = 0;
-
-
-    AuthenticationRequest(
-            PwmApplication pwmApplication,
-            SessionLabel sessionLabel,
-            UserIdentity userIdentity,
-            AuthenticationType requestedAuthType
-    )
-    {
-        this.pwmApplication = pwmApplication;
-        this.sessionLabel = sessionLabel;
-        this.userIdentity = userIdentity;
-        this.requestedAuthType = requestedAuthType;
-        this.operationNumber = counter++;
-    }
-
-    public AuthenticationResult authUsingUnknownPw()
-            throws ChaiUnavailableException, PwmUnrecoverableException
-    {
-        initialize();
-
-        log(PwmLogLevel.TRACE, "beginning authentication using unknown password procedure");
-
-        PasswordData userPassword = null;
-        final boolean configAlwaysUseProxy = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN);
-        if (configAlwaysUseProxy) {
-            strategy = AuthenticationStrategy.ADMIN_PROXY;
-        } else {
-            userPassword = learnUserPassword();
-            if (userPassword != null) {
-                strategy = AuthenticationStrategy.READ_THEN_BIND;
-            } else {
-                userPassword = setTempUserPassword();
-                if (userPassword != null) {
-                    strategy = AuthenticationStrategy.WRITE_THEN_BIND;
-                }
-            }
-            if (userPassword == null) {
-                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"no available unknown-pw authentication method"));
-            }
-        }
-
-        try {
-            return authenticateUserImpl(userPassword);
-        } catch (PwmOperationalException e) {
-            if (strategy == AuthenticationStrategy.READ_THEN_BIND) {
-                final String errorStr = "unable to authenticate with password read from directory, check proxy rights, ldap logs; error: " + e.getMessage();
-                throw new PwmUnrecoverableException(
-                        new ErrorInformation(PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr));
-            } else if (strategy == AuthenticationStrategy.WRITE_THEN_BIND) {
-                final String errorStr = "unable to authenticate with temporary password, check proxy rights, ldap logs; error: " + e.getMessage();
-                throw new PwmUnrecoverableException(
-                        new ErrorInformation(PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr));
-            }
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"unable to authenticate via authWithUnknownPw method: " + e.getMessage()));
-        }
-    }
-
-    public AuthenticationResult authenticateUser(final PasswordData password)
-            throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
-    {
-        initialize();
-        return authenticateUserImpl(password);
-    }
-
-    private AuthenticationResult authenticateUserImpl(
-            final PasswordData password
-    )
-            throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException
-    {
-        if (startTime == null) {
-            startTime = new Date();
-        }
-
-        final StatisticsManager statisticsManager = pwmApplication.getStatisticsManager();
-        final IntruderManager intruderManager = pwmApplication.getIntruderManager();
-        intruderManager.convenience().checkUserIdentity(userIdentity);
-        intruderManager.check(RecordType.ADDRESS, sessionLabel.getSrcAddress());
-
-        boolean allowBindAsUser = true;
-        if (strategy == AuthenticationStrategy.ADMIN_PROXY) {
-            allowBindAsUser = false;
-        }
-
-        if (allowBindAsUser) {
-            try {
-                testCredentials(userIdentity, password);
-            } catch (PwmOperationalException e) {
-                boolean permitAuthDespiteError = false;
-                final ChaiProvider.DIRECTORY_VENDOR vendor = pwmApplication.getProxyChaiProvider(
-                        userIdentity.getLdapProfileID()).getDirectoryVendor();
-                if (PwmError.PASSWORD_NEW_PASSWORD_REQUIRED == e.getError()) {
-                    if (vendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
-                        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
-                            log(PwmLogLevel.INFO,
-                                    "auth bind failed, but will allow login due to 'must change password on next login AD error', error: " + e.getErrorInformation().toDebugStr());
-                            allowBindAsUser = false;
-                            permitAuthDespiteError = true;
-                        }
-                    } else if (vendor == ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS) {
-                        if (pwmApplication.getConfig().readSettingAsBoolean(
-                                PwmSetting.ORACLE_DS_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
-                            log(PwmLogLevel.INFO,
-                                    "auth bind failed, but will allow login due to 'pwdReset' user attribute, error: " + e.getErrorInformation().toDebugStr());
-                            allowBindAsUser = false;
-                            permitAuthDespiteError = true;
-                        }
-                    }
-                } else if (PwmError.PASSWORD_EXPIRED == e.getError()) { // handle ad case where password is expired
-                    if (vendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
-                        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
-                            if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_EXPIRED)) {
-                                throw e;
-                            }
-                            log(PwmLogLevel.INFO,
-                                    "auth bind failed, but will allow login due to 'password expired AD error', error: " + e.getErrorInformation().toDebugStr());
-                            allowBindAsUser = false;
-                            permitAuthDespiteError = true;
-                        }
-                    }
-                }
-
-                if (!permitAuthDespiteError) {    // auth failed, presumably due to wrong password.
-                    statisticsManager.incrementValue(Statistic.AUTHENTICATION_FAILURES);
-                    throw e;
-                }
-            }
-        } else {
-            // verify user is not account disabled
-            AuthenticationUtility.checkIfUserEligibleToAuthentication(pwmApplication, userIdentity);
-        }
-
-        statisticsManager.incrementValue(Statistic.AUTHENTICATIONS);
-        statisticsManager.updateEps(Statistic.EpsType.AUTHENTICATION, 1);
-        statisticsManager.updateAverageValue(Statistic.AVG_AUTHENTICATION_TIME,
-                TimeDuration.fromCurrent(startTime).getTotalMilliseconds());
-
-        final AuthenticationType returnAuthType;
-        if (!allowBindAsUser) {
-            returnAuthType = AuthenticationType.AUTH_BIND_INHIBIT;
-        } else {
-            if (requestedAuthType == null) {
-                returnAuthType = AuthenticationType.AUTHENTICATED;
-            } else {
-                if (requestedAuthType == AuthenticationType.AUTH_WITHOUT_PASSWORD) {
-                    returnAuthType = AuthenticationType.AUTHENTICATED;
-                } else if (requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE) {
-                    returnAuthType =  AuthenticationType.AUTH_FROM_PUBLIC_MODULE;
-                }  else {
-                    returnAuthType = requestedAuthType;
-                }
-            }
-        }
-
-        final StringBuilder debugMsg = new StringBuilder();
-        debugMsg.append("successful ldap authentication for ").append(userIdentity);
-        debugMsg.append(" (").append(TimeDuration.fromCurrent(startTime).asCompactString()).append(")");
-        debugMsg.append(" type: ").append(returnAuthType).append(", using strategy ").append(strategy);
-        log(PwmLogLevel.INFO, debugMsg);
-        pwmApplication.getAuditManager().submit(pwmApplication.getAuditManager().createUserAuditRecord(
-                AuditEvent.AUTHENTICATE,
-                this.userIdentity,
-                returnAuthType.toString(),
-                sessionLabel.getSrcAddress(),
-                sessionLabel.getSrcHostname()
-        ));
-
-        final ChaiProvider returnProvider = determineUserProvider(returnAuthType, password);
-        return new AuthenticationResult(returnProvider, returnAuthType, password);
-    }
-
-    private void initialize() {
-        if (startTime != null) {
-            throw new IllegalStateException("AuthenticationRequest can not be used more than once");
-        }
-        log(PwmLogLevel.DEBUG, "preparing to authenticate user using authenticationType=" + this.requestedAuthType + " using strategy " + this.strategy);
-        startTime = new Date();
-    }
-
-    private void testCredentials(
-            final UserIdentity userIdentity,
-            final PasswordData password
-    )
-            throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException
-    {
-        log(PwmLogLevel.TRACE, "beginning testCredentials process");
-
-        if (userIdentity == null || userIdentity.getUserDN() == null || userIdentity.getUserDN().length() < 1) {
-            final String errorMsg = "attempt to authenticate with null userDN";
-            log(PwmLogLevel.DEBUG, errorMsg);
-            throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_WRONGPASSWORD,errorMsg));
-        }
-
-        if (password == null) {
-            final String errorMsg = "attempt to authenticate with null password";
-            log(PwmLogLevel.DEBUG, errorMsg);
-            throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_WRONGPASSWORD,errorMsg));
-        }
-
-        //try authenticating the user using a normal ldap BIND operation.
-        log(PwmLogLevel.TRACE, "attempting authentication using ldap BIND");
-
-        boolean bindSucceeded = false;
-        try {
-            //read a provider using the user's DN and password.
-            userProvider = LdapOperationsHelper.createChaiProvider(
-                    sessionLabel,
-                    userIdentity.getLdapProfile(pwmApplication.getConfig()),
-                    pwmApplication.getConfig(),
-                    userIdentity.getUserDN(),
-                    password
-            );
-
-            //issue a read operation to trigger a bind.
-            userProvider.readStringAttribute(userIdentity.getUserDN(), ChaiConstant.ATTR_LDAP_OBJECTCLASS);
-
-            bindSucceeded = true;
-        } catch (ChaiException e) {
-            if (e.getErrorCode() != null && e.getErrorCode() == ChaiError.INTRUDER_LOCKOUT) {
-                final String errorMsg = "intruder lockout detected for user " + userIdentity + " marking session as locked out: " + e.getMessage();
-                final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INTRUDER_LDAP, errorMsg);
-                log(PwmLogLevel.WARN, errorInformation.toDebugStr());
-                throw new PwmUnrecoverableException(errorInformation);
-            }
-            final PwmError pwmError = PwmError.forChaiError(e.getErrorCode());
-            final ErrorInformation errorInformation;
-            if (pwmError != null && PwmError.ERROR_UNKNOWN != pwmError) {
-                errorInformation = new ErrorInformation(pwmError, e.getMessage());
-            } else {
-                errorInformation = new ErrorInformation(PwmError.ERROR_WRONGPASSWORD, "ldap error during password check: " + e.getMessage());
-            }
-            log(PwmLogLevel.DEBUG, errorInformation.toDebugStr());
-            throw new PwmOperationalException(errorInformation);
-        } finally {
-            if (!bindSucceeded && userProvider != null){
-                try {
-                    userProvider.close();
-                    userProvider = null;
-                } catch (Throwable e) {
-                    log(PwmLogLevel.ERROR, "unexpected error closing invalid ldap connection after failed login attempt: " + e.getMessage());
-                }
-            }
-        }
-    }
-
-
-    private PasswordData learnUserPassword()
-            throws ChaiUnavailableException,  PwmUnrecoverableException
-    {
-        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;
-    }
-
-    private PasswordData setTempUserPassword(
-    )
-            throws ChaiUnavailableException, ImpossiblePasswordPolicyException, PwmUnrecoverableException
-    {
-
-        final boolean configAlwaysUseProxy = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN);
-
-        final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
-        final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
-
-        // try setting a random password on the account to authenticate.
-        if (!configAlwaysUseProxy && requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE) {
-            log(PwmLogLevel.DEBUG, "attempting to set temporary random password");
-
-            PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
-                    pwmApplication,
-                    sessionLabel,
-                    userIdentity,
-                    chaiUser,
-                    PwmConstants.DEFAULT_LOCALE
-            );
-
-            // create random password for user
-            RandomPasswordGenerator.RandomGeneratorConfig randomGeneratorConfig = new RandomPasswordGenerator.RandomGeneratorConfig();
-            randomGeneratorConfig.setSeedlistPhrases(RandomPasswordGenerator.DEFAULT_SEED_PHRASES);
-            randomGeneratorConfig.setPasswordPolicy(passwordPolicy);
-
-            final PasswordData currentPass = RandomPasswordGenerator.createRandomPassword(sessionLabel, randomGeneratorConfig, pwmApplication);
-
-            try {
-                final String oracleDS_PrePasswordAllowChangeTime = oraclePreTemporaryPwHandler(chaiProvider,
-                        chaiUser);
-
-                // write the random password for the user.
-                chaiUser.setPassword(currentPass.getStringValue());
-
-                oraclePostTemporaryPwHandler(chaiProvider, chaiUser, oracleDS_PrePasswordAllowChangeTime);
-
-                log(PwmLogLevel.INFO, "user " + userIdentity + " password has been set to random value to use for user authentication");
-            } catch (ChaiOperationException e) {
-                final String errorStr = "error setting random password for user " + userIdentity + " " + e.getMessage();
-                log(PwmLogLevel.ERROR, errorStr);
-                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr));
-            }
-
-            return currentPass;
-        }
-        return null;
-    }
-
-    private String oraclePreTemporaryPwHandler(
-            final ChaiProvider chaiProvider,
-            final ChaiUser chaiUser
-    )
-            throws PwmUnrecoverableException, ChaiUnavailableException, ChaiOperationException
-    {
-        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.ORACLE_DS_ENABLE_MANIP_ALLOWCHANGETIME)) {
-            return null;
-        }
-
-        if (ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor()) {
-            return null;
-        }
-
-        // oracle DS special case: passwordAllowChangeTime handler
-        final String oracleDS_PrePasswordAllowChangeTime = chaiProvider.readStringAttribute(
-                chaiUser.getEntryDN(),
-                ORACLE_ATTR_PW_ALLOW_CHG_TIME);
-        log(PwmLogLevel.TRACE,"read OracleDS value of passwordAllowChangeTime value=" + oracleDS_PrePasswordAllowChangeTime);
-
-        if (oracleDS_PrePasswordAllowChangeTime != null && !oracleDS_PrePasswordAllowChangeTime.isEmpty()) {
-            final Date date = OracleDSEntries.convertZuluToDate(oracleDS_PrePasswordAllowChangeTime);
-            if (new Date().before(date)) {
-                final String errorMsg = "change not permitted until " + PwmConstants.DEFAULT_DATETIME_FORMAT.format(
-                        date);
-                throw new PwmUnrecoverableException(
-                        new ErrorInformation(PwmError.PASSWORD_TOO_SOON, errorMsg));
-            }
-        }
-
-        return oracleDS_PrePasswordAllowChangeTime;
-    }
-
-    private void oraclePostTemporaryPwHandler(
-            final ChaiProvider chaiProvider,
-            final ChaiUser chaiUser,
-            final String oracleDS_PrePasswordAllowChangeTime
-    )
-            throws ChaiUnavailableException, ChaiOperationException
-    {
-        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.ORACLE_DS_ENABLE_MANIP_ALLOWCHANGETIME)) {
-            return;
-        }
-
-        // oracle DS special case: passwordAllowChangeTime handler
-        if (ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor()) {
-            return;
-        }
-
-        if (oracleDS_PrePasswordAllowChangeTime != null && !oracleDS_PrePasswordAllowChangeTime.isEmpty()) {
-            // write back the original pre-password allow change time.
-            final Set<String> values = new HashSet<>(
-                    Collections.singletonList(oracleDS_PrePasswordAllowChangeTime));
-            chaiProvider.writeStringAttribute(chaiUser.getEntryDN(), ORACLE_ATTR_PW_ALLOW_CHG_TIME,
-                    values,
-                    true);
-            log(PwmLogLevel.TRACE,"re-wrote passwordAllowChangeTime attribute to user " + chaiUser.getEntryDN() + ", value=" + oracleDS_PrePasswordAllowChangeTime);
-        } else {
-            final String oracleDS_PostPasswordAllowChangeTime = chaiProvider.readStringAttribute(
-                    chaiUser.getEntryDN(),
-                    ORACLE_ATTR_PW_ALLOW_CHG_TIME);
-            if (oracleDS_PostPasswordAllowChangeTime != null && !oracleDS_PostPasswordAllowChangeTime.isEmpty()) {
-                // password allow change time has appeared, but wasn't present previously, so delete it.
-                log(PwmLogLevel.TRACE, "a new value for passwordAllowChangeTime attribute to user " + chaiUser.getEntryDN() + " has appeared, will remove");
-                chaiProvider.deleteStringAttributeValue(chaiUser.getEntryDN(), ORACLE_ATTR_PW_ALLOW_CHG_TIME,
-                        oracleDS_PostPasswordAllowChangeTime);
-                log(PwmLogLevel.TRACE, "deleted attribute value for passwordAllowChangeTime attribute on user " + chaiUser.getEntryDN());
-            }
-        }
-    }
-
-    private ChaiProvider determineUserProvider(final AuthenticationType authenticationType, final PasswordData userPassword)
-            throws ChaiUnavailableException, PwmUnrecoverableException
-    {
-        final boolean authIsBindInhibit = authenticationType == AuthenticationType.AUTH_BIND_INHIBIT;
-        if (authIsBindInhibit) {
-            return null;
-        }
-
-        final boolean authIsFromForgottenPw = authenticationType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE;
-        final boolean alwaysUseProxyIsEnabled = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN);
-        final boolean passwordNotPresent = userPassword == null;
-
-        if (authIsFromForgottenPw && (alwaysUseProxyIsEnabled || passwordNotPresent)) {
-            final LdapProfile profile = pwmApplication.getConfig().getLdapProfiles().get(userIdentity.getLdapProfileID());
-            final String proxyDN = profile.readSettingAsString(PwmSetting.LDAP_PROXY_USER_DN);
-            final PasswordData proxyPassword = profile.readSettingAsPassword(PwmSetting.LDAP_PROXY_USER_PASSWORD);
-            return LdapOperationsHelper.createChaiProvider(sessionLabel, profile, pwmApplication.getConfig(), proxyDN, proxyPassword);
-        }
 
-        return userProvider;
-    }
+public interface AuthenticationRequest {
+    AuthenticationResult authUsingUnknownPw()
+            throws ChaiUnavailableException, PwmUnrecoverableException;
 
-    private void log(final PwmLogLevel level, final CharSequence message) {
-        LOGGER.log(level, sessionLabel,"authID=" + operationNumber + ", " + message);
-    }
+    AuthenticationResult authenticateUser(PasswordData password)
+                    throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException;
 }

+ 10 - 0
pwm/servlet/src/password/pwm/ldap/auth/AuthenticationUtility.java

@@ -26,6 +26,7 @@ import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.PwmApplication;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
@@ -60,4 +61,13 @@ public abstract class AuthenticationUtility {
             throw new PwmUnrecoverableException(PwmError.ERROR_ACCOUNT_EXPIRED);
         }
     }
+
+    static LDAPAuthenticationRequest createLDAPAuthenticationRequest(
+            PwmApplication pwmApplication,
+            SessionLabel sessionLabel,
+            UserIdentity userIdentity,
+            AuthenticationType requestedAuthType
+    ) {
+        return new LDAPAuthenticationRequest(pwmApplication, sessionLabel, userIdentity, requestedAuthType);
+    }
 }

+ 512 - 0
pwm/servlet/src/password/pwm/ldap/auth/LDAPAuthenticationRequest.java

@@ -0,0 +1,512 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://code.google.com/p/pwm/
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2015 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.ldap.auth;
+
+import com.novell.ldapchai.ChaiConstant;
+import com.novell.ldapchai.ChaiFactory;
+import com.novell.ldapchai.ChaiUser;
+import com.novell.ldapchai.exception.*;
+import com.novell.ldapchai.impl.oracleds.entry.OracleDSEntries;
+import com.novell.ldapchai.provider.ChaiProvider;
+import password.pwm.PwmApplication;
+import password.pwm.PwmConstants;
+import password.pwm.bean.SessionLabel;
+import password.pwm.bean.UserIdentity;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.profile.LdapProfile;
+import password.pwm.config.profile.PwmPasswordPolicy;
+import password.pwm.error.ErrorInformation;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmOperationalException;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.event.AuditEvent;
+import password.pwm.ldap.LdapOperationsHelper;
+import password.pwm.util.PasswordData;
+import password.pwm.util.RandomPasswordGenerator;
+import password.pwm.util.TimeDuration;
+import password.pwm.util.intruder.IntruderManager;
+import password.pwm.util.intruder.RecordType;
+import password.pwm.util.logging.PwmLogLevel;
+import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.operations.PasswordUtility;
+import password.pwm.util.stats.Statistic;
+import password.pwm.util.stats.StatisticsManager;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+class LDAPAuthenticationRequest implements AuthenticationRequest {
+    private static final PwmLogger LOGGER = PwmLogger.forClass(LDAPAuthenticationRequest.class);
+    private static final String ORACLE_ATTR_PW_ALLOW_CHG_TIME = "passwordAllowChangeTime";
+
+    private final PwmApplication pwmApplication;
+    private final SessionLabel sessionLabel;
+    private final UserIdentity userIdentity;
+    private final AuthenticationType requestedAuthType;
+
+    private ChaiProvider userProvider;
+    private AuthenticationStrategy strategy = AuthenticationStrategy.BIND;
+    private Date startTime;
+
+    private static int counter = 0;
+    private int operationNumber = 0;
+
+
+    LDAPAuthenticationRequest(
+            PwmApplication pwmApplication,
+            SessionLabel sessionLabel,
+            UserIdentity userIdentity,
+            AuthenticationType requestedAuthType
+    )
+    {
+        this.pwmApplication = pwmApplication;
+        this.sessionLabel = sessionLabel;
+        this.userIdentity = userIdentity;
+        this.requestedAuthType = requestedAuthType;
+        this.operationNumber = counter++;
+    }
+
+    @Override
+    public AuthenticationResult authUsingUnknownPw()
+            throws ChaiUnavailableException, PwmUnrecoverableException
+    {
+        initialize();
+
+        log(PwmLogLevel.TRACE, "beginning authentication using unknown password procedure");
+
+        PasswordData userPassword = null;
+        final boolean configAlwaysUseProxy = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN);
+        if (configAlwaysUseProxy) {
+            strategy = AuthenticationStrategy.ADMIN_PROXY;
+        } else {
+            userPassword = learnUserPassword();
+            if (userPassword != null) {
+                strategy = AuthenticationStrategy.READ_THEN_BIND;
+            } else {
+                userPassword = setTempUserPassword();
+                if (userPassword != null) {
+                    strategy = AuthenticationStrategy.WRITE_THEN_BIND;
+                }
+            }
+            if (userPassword == null) {
+                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"no available unknown-pw authentication method"));
+            }
+        }
+
+        try {
+            return authenticateUserImpl(userPassword);
+        } catch (PwmOperationalException e) {
+            if (strategy == AuthenticationStrategy.READ_THEN_BIND) {
+                final String errorStr = "unable to authenticate with password read from directory, check proxy rights, ldap logs; error: " + e.getMessage();
+                throw new PwmUnrecoverableException(
+                        new ErrorInformation(PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr));
+            } else if (strategy == AuthenticationStrategy.WRITE_THEN_BIND) {
+                final String errorStr = "unable to authenticate with temporary password, check proxy rights, ldap logs; error: " + e.getMessage();
+                throw new PwmUnrecoverableException(
+                        new ErrorInformation(PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr));
+            }
+            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"unable to authenticate via authWithUnknownPw method: " + e.getMessage()));
+        }
+    }
+
+    @Override
+    public AuthenticationResult authenticateUser(final PasswordData password)
+            throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
+    {
+        initialize();
+        return authenticateUserImpl(password);
+    }
+
+    private AuthenticationResult authenticateUserImpl(
+            final PasswordData password
+    )
+            throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException
+    {
+        if (startTime == null) {
+            startTime = new Date();
+        }
+
+        log(PwmLogLevel.DEBUG, "preparing to authenticate user using authenticationType=" + this.requestedAuthType + " using strategy " + this.strategy);
+
+        final StatisticsManager statisticsManager = pwmApplication.getStatisticsManager();
+        final IntruderManager intruderManager = pwmApplication.getIntruderManager();
+        intruderManager.convenience().checkUserIdentity(userIdentity);
+        intruderManager.check(RecordType.ADDRESS, sessionLabel.getSrcAddress());
+
+        boolean allowBindAsUser = true;
+        if (strategy == AuthenticationStrategy.ADMIN_PROXY) {
+            allowBindAsUser = false;
+        }
+
+        if (allowBindAsUser) {
+            try {
+                testCredentials(userIdentity, password);
+            } catch (PwmOperationalException e) {
+                boolean permitAuthDespiteError = false;
+                final ChaiProvider.DIRECTORY_VENDOR vendor = pwmApplication.getProxyChaiProvider(
+                        userIdentity.getLdapProfileID()).getDirectoryVendor();
+                if (PwmError.PASSWORD_NEW_PASSWORD_REQUIRED == e.getError()) {
+                    if (vendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
+                        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
+                            log(PwmLogLevel.INFO,
+                                    "auth bind failed, but will allow login due to 'must change password on next login AD error', error: " + e.getErrorInformation().toDebugStr());
+                            allowBindAsUser = false;
+                            permitAuthDespiteError = true;
+                        }
+                    } else if (vendor == ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS) {
+                        if (pwmApplication.getConfig().readSettingAsBoolean(
+                                PwmSetting.ORACLE_DS_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
+                            log(PwmLogLevel.INFO,
+                                    "auth bind failed, but will allow login due to 'pwdReset' user attribute, error: " + e.getErrorInformation().toDebugStr());
+                            allowBindAsUser = false;
+                            permitAuthDespiteError = true;
+                        }
+                    }
+                } else if (PwmError.PASSWORD_EXPIRED == e.getError()) { // handle ad case where password is expired
+                    if (vendor == ChaiProvider.DIRECTORY_VENDOR.MICROSOFT_ACTIVE_DIRECTORY) {
+                        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_REQUIRE_NEW_PWD)) {
+                            if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_ALLOW_AUTH_EXPIRED)) {
+                                throw e;
+                            }
+                            log(PwmLogLevel.INFO,
+                                    "auth bind failed, but will allow login due to 'password expired AD error', error: " + e.getErrorInformation().toDebugStr());
+                            allowBindAsUser = false;
+                            permitAuthDespiteError = true;
+                        }
+                    }
+                }
+
+                if (!permitAuthDespiteError) {    // auth failed, presumably due to wrong password.
+                    statisticsManager.incrementValue(Statistic.AUTHENTICATION_FAILURES);
+                    throw e;
+                }
+            }
+        } else {
+            // verify user is not account disabled
+            AuthenticationUtility.checkIfUserEligibleToAuthentication(pwmApplication, userIdentity);
+        }
+
+        statisticsManager.incrementValue(Statistic.AUTHENTICATIONS);
+        statisticsManager.updateEps(Statistic.EpsType.AUTHENTICATION, 1);
+        statisticsManager.updateAverageValue(Statistic.AVG_AUTHENTICATION_TIME,
+                TimeDuration.fromCurrent(startTime).getTotalMilliseconds());
+
+        final AuthenticationType returnAuthType;
+        if (!allowBindAsUser) {
+            returnAuthType = AuthenticationType.AUTH_BIND_INHIBIT;
+        } else {
+            if (requestedAuthType == null) {
+                returnAuthType = AuthenticationType.AUTHENTICATED;
+            } else {
+                if (requestedAuthType == AuthenticationType.AUTH_WITHOUT_PASSWORD) {
+                    returnAuthType = AuthenticationType.AUTHENTICATED;
+                } else if (requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE) {
+                    returnAuthType =  AuthenticationType.AUTH_FROM_PUBLIC_MODULE;
+                }  else {
+                    returnAuthType = requestedAuthType;
+                }
+            }
+        }
+
+        final boolean useProxy = determineIfLdapProxyNeeded(returnAuthType, password);
+        final ChaiProvider returnProvider = useProxy ? makeProxyProvider() : userProvider;
+        final AuthenticationResult authenticationResult = new AuthenticationResult(returnProvider, returnAuthType, password);
+
+        final StringBuilder debugMsg = new StringBuilder();
+        debugMsg.append("successful ldap authentication for ").append(userIdentity);
+        debugMsg.append(" (").append(TimeDuration.fromCurrent(startTime).asCompactString()).append(")");
+        debugMsg.append(" type: ").append(returnAuthType).append(", using strategy ").append(strategy);
+        debugMsg.append(", using proxy connection: ").append(useProxy);
+        log(PwmLogLevel.INFO, debugMsg);
+        pwmApplication.getAuditManager().submit(pwmApplication.getAuditManager().createUserAuditRecord(
+                AuditEvent.AUTHENTICATE,
+                this.userIdentity,
+                returnAuthType.toString(),
+                sessionLabel.getSrcAddress(),
+                sessionLabel.getSrcHostname()
+        ));
+
+        return authenticationResult;
+    }
+
+    private void initialize() {
+        if (startTime != null) {
+            throw new IllegalStateException("AuthenticationRequest can not be used more than once");
+        }
+        startTime = new Date();
+    }
+
+    private void testCredentials(
+            final UserIdentity userIdentity,
+            final PasswordData password
+    )
+            throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException
+    {
+        log(PwmLogLevel.TRACE, "beginning testCredentials process");
+
+        if (userIdentity == null || userIdentity.getUserDN() == null || userIdentity.getUserDN().length() < 1) {
+            final String errorMsg = "attempt to authenticate with null userDN";
+            log(PwmLogLevel.DEBUG, errorMsg);
+            throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_WRONGPASSWORD,errorMsg));
+        }
+
+        if (password == null) {
+            final String errorMsg = "attempt to authenticate with null password";
+            log(PwmLogLevel.DEBUG, errorMsg);
+            throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_WRONGPASSWORD,errorMsg));
+        }
+
+        //try authenticating the user using a normal ldap BIND operation.
+        log(PwmLogLevel.TRACE, "attempting authentication using ldap BIND");
+
+        boolean bindSucceeded = false;
+        try {
+            //read a provider using the user's DN and password.
+            userProvider = LdapOperationsHelper.createChaiProvider(
+                    sessionLabel,
+                    userIdentity.getLdapProfile(pwmApplication.getConfig()),
+                    pwmApplication.getConfig(),
+                    userIdentity.getUserDN(),
+                    password
+            );
+
+            //issue a read operation to trigger a bind.
+            userProvider.readStringAttribute(userIdentity.getUserDN(), ChaiConstant.ATTR_LDAP_OBJECTCLASS);
+
+            bindSucceeded = true;
+        } catch (ChaiException e) {
+            if (e.getErrorCode() != null && e.getErrorCode() == ChaiError.INTRUDER_LOCKOUT) {
+                final String errorMsg = "intruder lockout detected for user " + userIdentity + " marking session as locked out: " + e.getMessage();
+                final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INTRUDER_LDAP, errorMsg);
+                log(PwmLogLevel.WARN, errorInformation.toDebugStr());
+                throw new PwmUnrecoverableException(errorInformation);
+            }
+            final PwmError pwmError = PwmError.forChaiError(e.getErrorCode());
+            final ErrorInformation errorInformation;
+            if (pwmError != null && PwmError.ERROR_UNKNOWN != pwmError) {
+                errorInformation = new ErrorInformation(pwmError, e.getMessage());
+            } else {
+                errorInformation = new ErrorInformation(PwmError.ERROR_WRONGPASSWORD, "ldap error during password check: " + e.getMessage());
+            }
+            log(PwmLogLevel.DEBUG, errorInformation.toDebugStr());
+            throw new PwmOperationalException(errorInformation);
+        } finally {
+            if (!bindSucceeded && userProvider != null){
+                try {
+                    userProvider.close();
+                    userProvider = null;
+                } catch (Throwable e) {
+                    log(PwmLogLevel.ERROR, "unexpected error closing invalid ldap connection after failed login attempt: " + e.getMessage());
+                }
+            }
+        }
+    }
+
+
+    private PasswordData learnUserPassword()
+            throws ChaiUnavailableException,  PwmUnrecoverableException
+    {
+        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;
+    }
+
+    private PasswordData setTempUserPassword(
+    )
+            throws ChaiUnavailableException, ImpossiblePasswordPolicyException, PwmUnrecoverableException
+    {
+
+        final boolean configAlwaysUseProxy = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN);
+
+        final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID());
+        final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
+
+        // try setting a random password on the account to authenticate.
+        if (!configAlwaysUseProxy && requestedAuthType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE) {
+            log(PwmLogLevel.DEBUG, "attempting to set temporary random password");
+
+            PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
+                    pwmApplication,
+                    sessionLabel,
+                    userIdentity,
+                    chaiUser,
+                    PwmConstants.DEFAULT_LOCALE
+            );
+
+            // create random password for user
+            RandomPasswordGenerator.RandomGeneratorConfig randomGeneratorConfig = new RandomPasswordGenerator.RandomGeneratorConfig();
+            randomGeneratorConfig.setSeedlistPhrases(RandomPasswordGenerator.DEFAULT_SEED_PHRASES);
+            randomGeneratorConfig.setPasswordPolicy(passwordPolicy);
+
+            final PasswordData currentPass = RandomPasswordGenerator.createRandomPassword(sessionLabel, randomGeneratorConfig, pwmApplication);
+
+            try {
+                final String oracleDS_PrePasswordAllowChangeTime = oraclePreTemporaryPwHandler(chaiProvider,
+                        chaiUser);
+
+                // write the random password for the user.
+                chaiUser.setPassword(currentPass.getStringValue());
+
+                oraclePostTemporaryPwHandler(chaiProvider, chaiUser, oracleDS_PrePasswordAllowChangeTime);
+
+                log(PwmLogLevel.INFO, "user " + userIdentity + " password has been set to random value to use for user authentication");
+            } catch (ChaiOperationException e) {
+                final String errorStr = "error setting random password for user " + userIdentity + " " + e.getMessage();
+                log(PwmLogLevel.ERROR, errorStr);
+                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_BAD_SESSION_PASSWORD, errorStr));
+            }
+
+            return currentPass;
+        }
+        return null;
+    }
+
+    private String oraclePreTemporaryPwHandler(
+            final ChaiProvider chaiProvider,
+            final ChaiUser chaiUser
+    )
+            throws PwmUnrecoverableException, ChaiUnavailableException, ChaiOperationException
+    {
+        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.ORACLE_DS_ENABLE_MANIP_ALLOWCHANGETIME)) {
+            return null;
+        }
+
+        if (ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor()) {
+            return null;
+        }
+
+        // oracle DS special case: passwordAllowChangeTime handler
+        final String oracleDS_PrePasswordAllowChangeTime = chaiProvider.readStringAttribute(
+                chaiUser.getEntryDN(),
+                ORACLE_ATTR_PW_ALLOW_CHG_TIME);
+        log(PwmLogLevel.TRACE,"read OracleDS value of passwordAllowChangeTime value=" + oracleDS_PrePasswordAllowChangeTime);
+
+        if (oracleDS_PrePasswordAllowChangeTime != null && !oracleDS_PrePasswordAllowChangeTime.isEmpty()) {
+            final Date date = OracleDSEntries.convertZuluToDate(oracleDS_PrePasswordAllowChangeTime);
+            if (new Date().before(date)) {
+                final String errorMsg = "change not permitted until " + PwmConstants.DEFAULT_DATETIME_FORMAT.format(
+                        date);
+                throw new PwmUnrecoverableException(
+                        new ErrorInformation(PwmError.PASSWORD_TOO_SOON, errorMsg));
+            }
+        }
+
+        return oracleDS_PrePasswordAllowChangeTime;
+    }
+
+    private void oraclePostTemporaryPwHandler(
+            final ChaiProvider chaiProvider,
+            final ChaiUser chaiUser,
+            final String oracleDS_PrePasswordAllowChangeTime
+    )
+            throws ChaiUnavailableException, ChaiOperationException
+    {
+        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.ORACLE_DS_ENABLE_MANIP_ALLOWCHANGETIME)) {
+            return;
+        }
+
+        // oracle DS special case: passwordAllowChangeTime handler
+        if (ChaiProvider.DIRECTORY_VENDOR.ORACLE_DS != chaiUser.getChaiProvider().getDirectoryVendor()) {
+            return;
+        }
+
+        if (oracleDS_PrePasswordAllowChangeTime != null && !oracleDS_PrePasswordAllowChangeTime.isEmpty()) {
+            // write back the original pre-password allow change time.
+            final Set<String> values = new HashSet<>(
+                    Collections.singletonList(oracleDS_PrePasswordAllowChangeTime));
+            chaiProvider.writeStringAttribute(chaiUser.getEntryDN(), ORACLE_ATTR_PW_ALLOW_CHG_TIME,
+                    values,
+                    true);
+            log(PwmLogLevel.TRACE,"re-wrote passwordAllowChangeTime attribute to user " + chaiUser.getEntryDN() + ", value=" + oracleDS_PrePasswordAllowChangeTime);
+        } else {
+            final String oracleDS_PostPasswordAllowChangeTime = chaiProvider.readStringAttribute(
+                    chaiUser.getEntryDN(),
+                    ORACLE_ATTR_PW_ALLOW_CHG_TIME);
+            if (oracleDS_PostPasswordAllowChangeTime != null && !oracleDS_PostPasswordAllowChangeTime.isEmpty()) {
+                // password allow change time has appeared, but wasn't present previously, so delete it.
+                log(PwmLogLevel.TRACE, "a new value for passwordAllowChangeTime attribute to user " + chaiUser.getEntryDN() + " has appeared, will remove");
+                chaiProvider.deleteStringAttributeValue(chaiUser.getEntryDN(), ORACLE_ATTR_PW_ALLOW_CHG_TIME,
+                        oracleDS_PostPasswordAllowChangeTime);
+                log(PwmLogLevel.TRACE, "deleted attribute value for passwordAllowChangeTime attribute on user " + chaiUser.getEntryDN());
+            }
+        }
+    }
+
+    private boolean determineIfLdapProxyNeeded(final AuthenticationType authenticationType, final PasswordData userPassword)
+            throws ChaiUnavailableException, PwmUnrecoverableException
+    {
+        if (userProvider != null) {
+            return true;
+        }
+
+        final boolean authIsBindInhibit = authenticationType == AuthenticationType.AUTH_BIND_INHIBIT;
+        final boolean authIsFromForgottenPw = authenticationType == AuthenticationType.AUTH_FROM_PUBLIC_MODULE;
+        final boolean alwaysUseProxyIsEnabled = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.AD_USE_PROXY_FOR_FORGOTTEN);
+        final boolean passwordNotPresent = userPassword == null;
+
+        return authIsBindInhibit || authIsFromForgottenPw && (alwaysUseProxyIsEnabled || passwordNotPresent);
+
+    }
+
+    private ChaiProvider makeProxyProvider()
+            throws ChaiUnavailableException, PwmUnrecoverableException
+    {
+        final LdapProfile profile = pwmApplication.getConfig().getLdapProfiles().get(userIdentity.getLdapProfileID());
+        final String proxyDN = profile.readSettingAsString(PwmSetting.LDAP_PROXY_USER_DN);
+        final PasswordData proxyPassword = profile.readSettingAsPassword(PwmSetting.LDAP_PROXY_USER_PASSWORD);
+        return LdapOperationsHelper.createChaiProvider(sessionLabel, profile, pwmApplication.getConfig(), proxyDN, proxyPassword);
+    }
+
+    private void log(final PwmLogLevel level, final CharSequence message) {
+        LOGGER.log(level, sessionLabel,"authID=" + operationNumber + ", " + message);
+    }
+}

+ 4 - 4
pwm/servlet/src/password/pwm/ldap/auth/SessionAuthenticator.java

@@ -81,7 +81,7 @@ public class SessionAuthenticator {
             final UserSearchEngine userSearchEngine = new UserSearchEngine(pwmApplication, sessionLabel);
             userIdentity = userSearchEngine.resolveUsername(username, context, ldapProfile);
 
-            AuthenticationRequest authEngine = new AuthenticationRequest(pwmApplication, sessionLabel, userIdentity, AuthenticationType.AUTHENTICATED);
+            AuthenticationRequest authEngine = AuthenticationUtility.createLDAPAuthenticationRequest(pwmApplication, sessionLabel, userIdentity, AuthenticationType.AUTHENTICATED);
             AuthenticationResult authResult = authEngine.authenticateUser(password);
             postAuthenticationSequence(userIdentity, authResult);
         } catch (PwmOperationalException e) {
@@ -99,7 +99,7 @@ public class SessionAuthenticator {
             throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
     {
         try {
-            AuthenticationRequest authEngine = new AuthenticationRequest(pwmApplication, sessionLabel, userIdentity, AuthenticationType.AUTHENTICATED);
+            AuthenticationRequest authEngine = AuthenticationUtility.createLDAPAuthenticationRequest(pwmApplication, sessionLabel, userIdentity, AuthenticationType.AUTHENTICATED);
             AuthenticationResult authResult = authEngine.authenticateUser(password);
             postAuthenticationSequence(userIdentity, authResult);
         } catch (PwmOperationalException e) {
@@ -122,7 +122,7 @@ public class SessionAuthenticator {
             final UserSearchEngine userSearchEngine = new UserSearchEngine(pwmApplication, sessionLabel);
             userIdentity = userSearchEngine.resolveUsername(username, null, null);
 
-            AuthenticationRequest authEngine = new AuthenticationRequest(pwmApplication, sessionLabel, userIdentity, requestedAuthType);
+            AuthenticationRequest authEngine = AuthenticationUtility.createLDAPAuthenticationRequest(pwmApplication, sessionLabel, userIdentity, requestedAuthType);
             AuthenticationResult authResult = authEngine.authUsingUnknownPw();
             postAuthenticationSequence(userIdentity, authResult);
         } catch (PwmOperationalException e) {
@@ -137,7 +137,7 @@ public class SessionAuthenticator {
     )
             throws PwmUnrecoverableException, ChaiUnavailableException
     {
-        AuthenticationRequest authEngine = new AuthenticationRequest(pwmApplication, sessionLabel, userIdentity, requestedAuthType);
+        AuthenticationRequest authEngine = AuthenticationUtility.createLDAPAuthenticationRequest(pwmApplication, sessionLabel, userIdentity, requestedAuthType);
         AuthenticationResult authResult = authEngine.authUsingUnknownPw();
         postAuthenticationSequence(userIdentity, authResult);
     }

+ 2 - 2
pwm/servlet/src/password/pwm/token/TokenService.java

@@ -573,12 +573,12 @@ public class TokenService implements PwmService {
         }
 
         // check if password-last-modified is same as when tried to read it before.
-        if (sessionUserIdentity != null && tokenPayload.getUserIdentity() != null && tokenPayload.getData().containsKey(PwmConstants.TOKEN_KEY_PWD_CHG_DATE)) {
+        if (tokenPayload.getUserIdentity() != null && tokenPayload.getData() != null && tokenPayload.getData().containsKey(PwmConstants.TOKEN_KEY_PWD_CHG_DATE)) {
             try {
                 final Date userLastPasswordChange = PasswordUtility.determinePwdLastModified(
                         pwmApplication,
                         pwmSession.getLabel(),
-                        sessionUserIdentity);
+                        tokenPayload.getUserIdentity());
                 final String dateStringInToken = tokenPayload.getData().get(PwmConstants.TOKEN_KEY_PWD_CHG_DATE);
                 if (userLastPasswordChange != null && dateStringInToken != null) {
                     final String userChangeString = PwmConstants.DEFAULT_DATETIME_FORMAT.format(userLastPasswordChange);

+ 6 - 3
pwm/servlet/src/password/pwm/util/PwmPasswordRuleValidator.java

@@ -142,7 +142,7 @@ public class PwmPasswordRuleValidator {
     )
             throws PwmUnrecoverableException
     {
-        final String passwordString = password.getStringValue();
+        final String passwordString = password == null ? "" : password.getStringValue();
         final String oldPasswordString = oldPassword == null ? null : oldPassword.getStringValue();
         return internalPwmPolicyValidator(passwordString, oldPasswordString, uiBean, failFast);
     }
@@ -518,13 +518,16 @@ public class PwmPasswordRuleValidator {
         final boolean haltOnError = Boolean.parseBoolean(config.readAppProperty(AppProperty.WS_REST_CLIENT_PWRULE_HALTONERROR));
         final Map<String,Object> sendData = new LinkedHashMap<>();
 
-        final PasswordCharCounter passwordCharCounter = new PasswordCharCounter(password.getStringValue());
 
         if (restURL == null || restURL.isEmpty()) {
             return Collections.emptyList();
         }
 
-        sendData.put("password",password.getStringValue());
+        {
+            final String passwordStr = password == null ? "" : password.getStringValue();
+            sendData.put("password", passwordStr);
+        }
+
         if (pwmPasswordPolicy != null) {
             final LinkedHashMap<String,Object> policyData = new LinkedHashMap<>();
             for (final PwmPasswordRule rule : PwmPasswordRule.values()) {

+ 1 - 1
pwm/servlet/src/password/pwm/util/XmlUtil.java

@@ -51,7 +51,7 @@ public class XmlUtil {
         try {
             inputDocument = builder.build(inputStream);
         } catch (Exception e) {
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,"error parsing xml data: " + e.getMessage()));
+            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,null,new String[]{"error parsing xml data: " + e.getMessage()}));
         }
         return inputDocument;
     }

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

@@ -352,6 +352,7 @@ public class RestAppDataServer extends AbstractRestServer {
         settingMap.put("client.ajaxTypingTimeout", Integer.parseInt(config.readAppProperty(AppProperty.CLIENT_AJAX_TYPING_TIMEOUT)));
         settingMap.put("client.ajaxTypingWait", Integer.parseInt(config.readAppProperty(AppProperty.CLIENT_AJAX_TYPING_WAIT)));
         settingMap.put("client.activityMaxEpsRate", Integer.parseInt(config.readAppProperty(AppProperty.CLIENT_ACTIVITY_MAX_EPS_RATE)));
+        settingMap.put("client.js.enableHtml5Dialog", Boolean.parseBoolean(config.readAppProperty(AppProperty.CLIENT_JS_ENABLE_HTML5DIALOG)));
         settingMap.put("client.pwShowRevertTimeout", Integer.parseInt(config.readAppProperty(AppProperty.CLIENT_PW_SHOW_REVERT_TIMEOUT)));
         settingMap.put("enableIdleTimeout", config.readSettingAsBoolean(PwmSetting.DISPLAY_IDLE_TIMEOUT));
         settingMap.put("pageLeaveNotice", config.readSettingAsLong(PwmSetting.SECURITY_PAGE_LEAVE_NOTICE_TIMEOUT));

+ 1 - 1
pwm/servlet/src/password/pwm/ws/server/rest/RestStatusServer.java

@@ -164,7 +164,7 @@ public class RestStatusServer extends AbstractRestServer {
             }
 
             final TimeDuration timeDuration = TimeDuration.fromCurrent(startTime);
-            LOGGER.debug(restRequestBean.getPwmSession(),"completed REST status request in " + timeDuration.asCompactString() + ", result=" + JsonUtil.serialize(restRequestBean));
+            LOGGER.debug(restRequestBean.getPwmSession(),"completed REST status request in " + timeDuration.asCompactString() + ", result=" + JsonUtil.serialize(restResultBean));
             return restResultBean.asJsonResponse();
         } catch (PwmException e) {
             return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();

+ 1 - 1
pwm/servlet/web/WEB-INF/jsp/admin-logview-window.jsp

@@ -31,6 +31,7 @@
 <%@ taglib uri="pwm" prefix="pwm" %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_REQ_COUNTER); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
 <html dir="<pwm:LocaleOrientation/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <% final PwmRequest pwmRequest = PwmRequest.forRequest(request,response); %>
@@ -86,7 +87,6 @@
 <script type="text/javascript">
     PWM_GLOBAL['startupFunctions'].push(function(){
         var refreshFunction = function(){
-            PWM_GLOBAL['idle_suspendTimeout'] = true;
             var levelSelectElement = PWM_MAIN.getObject('select-level');
             var level=levelSelectElement.options[levelSelectElement.selectedIndex].value;
             PWM_MAIN.showWaitDialog({loadFunction:function(){PWM_CONFIG.openLogViewer(level)}});

+ 5 - 1
pwm/servlet/web/WEB-INF/jsp/admin-tokenlookup.jsp

@@ -163,8 +163,12 @@
         <form id="tokenForm" action="Administration" method="post">
             <textarea name="token" id="token" style="width: 580px; height: 150px"></textarea>
             <div class="buttonbar">
-                <input name="submitBtn" class="btn" type="submit" value="Lookup Token"/>
+                <button type="submit" name="submitBtn" class="btn" type="submit">
+                    <pwm:if test="showIcons"><span class="btn-icon fa fa-search"></span></pwm:if>
+                    Lookup Token
+                </button>
             </div>
+            <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>
         </form>
     </div>
     <div class="push"></div>

+ 16 - 21
pwm/servlet/web/WEB-INF/jsp/captcha.jsp

@@ -29,13 +29,19 @@
 <%-- begin reCaptcha section (http://code.google.com/apis/recaptcha/docs/display.html) --%>
 <pwm:script>
     <script type="text/javascript">
-        function recaptchaCallback() {
-            console.log('captcha completed, submitting form');
-            PWM_MAIN.handleFormSubmit(PWM_MAIN.getObject('verifyCaptcha'));
+        function onloadCallback() {
+            var recaptchaCallback = function() {
+                console.log('captcha completed, submitting form');
+                PWM_MAIN.handleFormSubmit(PWM_MAIN.getObject('verifyCaptcha'));
+            };
+
+            console.log('reached google recaptcha onload callback');
+            PWM_MAIN.setStyle('captcha-loading','display','none');
+            grecaptcha.render('recaptcha-container',{callback:recaptchaCallback,sitekey:'<%=JspUtility.getAttribute(pageContext,PwmConstants.REQUEST_ATTR.CaptchaPublicKey)%>'});
         }
     </script>
 </pwm:script>
-<pwm:script-ref url="<%=(String)JspUtility.getAttribute(pageContext,PwmConstants.REQUEST_ATTR.CaptchaClientUrl)%>"/>
+<script nonce="<pwm:value name="cspNonce"/>" src="<%=(String)JspUtility.getAttribute(pageContext,PwmConstants.REQUEST_ATTR.CaptchaClientUrl)%>?onload=onloadCallback&render=explicit" defer async></script>
 <div id="wrapper">
     <jsp:include page="fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="Title_Captcha"/>
@@ -44,29 +50,18 @@
         <p><pwm:display key="Display_Captcha"/></p>
         <%@ include file="fragment/message.jsp" %>
         <br/>
+        <div id="captcha-loading" class="WaitDialogBlank"></div>
         <form action="<pwm:url url='Captcha'/>" method="post" enctype="application/x-www-form-urlencoded" id="verifyCaptcha" name="verifyCaptcha" class="pwm-form">
             <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>
+
             <center>
-                <div data-callback="recaptchaCallback" style="margin: auto !important" class="g-recaptcha" data-sitekey="<%=JspUtility.getAttribute(pageContext,PwmConstants.REQUEST_ATTR.CaptchaPublicKey)%>"></div>
+                <div id="recaptcha-container">
+                </div>
             </center>
-            <%--
             <noscript>
-                <div style="width: 302px; height: 352px;">
-                    <div style="width: 302px; height: 352px; position: relative;">
-                        <div style="width: 302px; height: 352px; position: absolute;">
-                            <iframe src="https://www.google.com/recaptcha/api/fallback?k=your_site_key"
-                                    frameborder="0" scrolling="no"
-                                    style="width: 302px; height:352px; border-style: none;">
-                            </iframe>
-                        </div>
-                        <div style="width: 250px; height: 80px; position: absolute; border-style: none; bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;">
-                            <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px; height: 80px; border: 1px solid #c1c1c1; margin: 0px; padding: 0px; resize: none;" value="">
-                            </textarea>
-                        </div>
-                    </div>
-                </div>
+                <span><pwm:display key="Display_JavascriptRequired"/></span>
+                <a href="<pwm:context/>"><pwm:display key="Title_MainPage"/></a>
             </noscript>
-            --%>
             <div class="buttonbar">
                 <input type="hidden" name="processAction" value="doVerify"/>
                 <button type="submit" name="verify" class="btn" id="verify_button">

+ 3 - 3
pwm/servlet/web/WEB-INF/jsp/changepassword-wait.jsp

@@ -23,6 +23,9 @@
   --%>
 
 <!DOCTYPE html>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS);%>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT);%>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT);%>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html dir="<pwm:LocaleOrientation/>">
@@ -45,7 +48,6 @@
     <meta http-equiv="refresh" content="<%=checkIntervalSeconds%>;url='ChangePassword?processAction=complete&pwmFormID=<pwm:FormID/>">
 </noscript>
 <div id="wrapper">
-    <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS);%>
     <jsp:include page="fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="Title_PleaseWait"/>
     </jsp:include>
@@ -70,7 +72,6 @@
 <pwm:script>
 <script type="text/javascript">
     PWM_GLOBAL['startupFunctions'].push(function(){
-        PWM_GLOBAL['idle_suspendTimeout'] = true;
         require(["dojo/parser", "dijit/ProgressBar","dojo/ready"], function(parser){
             parser.parse();
             PWM_CHANGEPW.refreshCreateStatus(<%=checkIntervalSeconds * 1000%>);
@@ -79,7 +80,6 @@
 </script>
 </pwm:script>
 <pwm:script-ref url="/public/resources/js/changepassword.js"/>
-<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT);%>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 1 - 1
pwm/servlet/web/WEB-INF/jsp/configguide-password.jsp

@@ -58,7 +58,7 @@
                         <b>Configuration Password</b>
                         <br/>
                         <span class="fa fa-chevron-circle-right"></span>
-                        <input type="<pwm:value name="passwordFieldType"/>" id="<%=ConfigGuideServlet.PARAM_CONFIG_PASSWORD%>" name="<%=ConfigGuideServlet.PARAM_CONFIG_PASSWORD%>" class="configStringInput passwordfield" style="width:200px" autofocus/>
+                        <input type="<pwm:value name="passwordFieldType"/>" id="<%=ConfigGuideServlet.PARAM_CONFIG_PASSWORD%>" name="<%=ConfigGuideServlet.PARAM_CONFIG_PASSWORD%>" class="configStringInput passwordfield" style="width:200px" <pwm:autofocus/>/>
                     </div>
                     <div class="setting_item">
                         <b>Verify Configuration Password</b>

+ 1 - 1
pwm/servlet/web/WEB-INF/jsp/configguide-start.jsp

@@ -22,6 +22,7 @@
 
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_LOCALE); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_THEME); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
@@ -84,7 +85,6 @@
 <pwm:script>
     <script type="text/javascript">
         PWM_GLOBAL['startupFunctions'].push(function() {
-            PWM_GLOBAL['idle_suspendTimeout'] = true;
             PWM_MAIN.addEventHandler('button-startConfigGuide', 'click', function () {
                 if (PWM_GLOBAL['setting-displayEula']) {
                     PWM_MAIN.showEula(true, function () {

+ 7 - 0
pwm/servlet/web/WEB-INF/jsp/fragment/footer.jsp

@@ -81,6 +81,13 @@
     </div>
 </div>
 <% } %>
+<% if (footer_pwmRequest.isFlag(PwmRequest.Flag.NO_IDLE_TIMEOUT)) { %>
+<pwm:script>
+    <script type="text/javascript">
+        PWM_GLOBAL['idle_suspendTimeout'] = true;
+    </script>
+</pwm:script>
+<% } %>
 <pwm:script>
     <script type="text/javascript">
         PWM_GLOBAL['startupFunctions'].push(function() {

+ 15 - 15
pwm/servlet/web/WEB-INF/jsp/fragment/setupresponses-form.jsp

@@ -1,6 +1,9 @@
 <%@ page import="com.novell.ldapchai.cr.Challenge" %>
 <%@ page import="password.pwm.http.bean.SetupResponsesBean" %>
+<%@ page import="password.pwm.util.JsonUtil" %>
 <%@ page import="password.pwm.util.StringUtil" %>
+<%@ page import="java.util.ArrayList" %>
+<%@ page import="java.util.List" %>
 <%--
   ~ Password Management Servlets (PWM)
   ~ http://code.google.com/p/pwm/
@@ -55,22 +58,26 @@
 <% } %>
 <%---------------------- display fields for RANDOM challenges using SIMPLE mode ----------------------------------------%>
 <% if (setupData.isSimpleMode()) {  %>
+<% // need to output all the random options for the javascript functions.
+    final List<String> questionTexts = new ArrayList<String>();
+    for (final String indexKey : setupData.getIndexedChallenges().keySet()) {
+        final Challenge challenge = setupData.getIndexedChallenges().get(indexKey);
+        if (!challenge.isRequired()) {
+            questionTexts.add(challenge.getChallengeText());
+        }
+    }
+%>
 <p><pwm:display key="Display_SetupRandomResponses" value1="<%= String.valueOf(setupData.getChallengeSet().getMinRandomRequired()) %>"/></p>
 <% for (int index = 0; index < setupData.getMinRandomSetup(); index++) { %>
 <h2>
     <select name="PwmResponse_Q_Random_<%=index%>" id="PwmResponse_Q_Random_<%=index%>" style="width:70%" <pwm:autofocus/> class="simpleModeResponseSelection"
             data-response-id="PwmResponse_R_Random_<%=index%>">
         <option value="UNSELECTED" data-unselected-option="true">&nbsp;&mdash;&nbsp;<pwm:display key="Display_SelectionIndicator"/>&nbsp;&mdash;&nbsp;</option>
-        <%
-            for (final String indexKey : setupData.getIndexedChallenges().keySet()) {
-                final Challenge challenge = setupData.getIndexedChallenges().get(indexKey);
-                if (!challenge.isRequired()) {
-
+        <% for (final String questionText : questionTexts) {
         %>
-        <option value="<%=StringUtil.escapeHtml(challenge.getChallengeText())%>"><%=StringUtil.escapeHtml(challenge.getChallengeText())%>
+        <option value="<%=StringUtil.escapeHtml(questionText)%>"><%=StringUtil.escapeHtml(questionText)%>
         </option>
         <% } %>
-        <% } %>
     </select>
 </h2>
 <p>
@@ -81,16 +88,9 @@
 <% } %>
 <pwm:script>
 <script type="text/javascript">
-    <% // need to output all the random options for the javascript functions.
-        for (final String indexKey : setupData.getIndexedChallenges().keySet()) {
-        final Challenge challenge = setupData.getIndexedChallenges().get(indexKey);
-        if (!challenge.isRequired()) {
-    %>
     PWM_GLOBAL['startupFunctions'].push(function(){
-        PWM_VAR['simpleRandomOptions'].push('<%=StringUtil.escapeJS(challenge.getChallengeText())%>')
+        PWM_VAR['simpleRandomOptions'] = <%=JsonUtil.serializeCollection(questionTexts)%>;
     });
-    <% } %>
-    <% } %>
 </script>
 </pwm:script>
 <% } else { %>

+ 1 - 1
pwm/servlet/web/WEB-INF/jsp/helpdesk-detail.jsp

@@ -755,8 +755,8 @@
         });
     </script>
 </pwm:script>
+<jsp:include page="/WEB-INF/jsp/fragment/footer.jsp"/>
 <pwm:script-ref url="/public/resources/js/helpdesk.js"/>
 <pwm:script-ref url="/public/resources/js/changepassword.js"/>
-<jsp:include page="/WEB-INF/jsp/fragment/footer.jsp"/>
 </body>
 </html>

+ 3 - 13
pwm/servlet/web/WEB-INF/jsp/helpdesk.jsp

@@ -20,14 +20,12 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   --%>
 
-<%@ page import="password.pwm.http.PwmSessionWrapper" %>
 <%@ page import="password.pwm.http.bean.HelpdeskBean" %>
-<%@ page import="password.pwm.util.JsonUtil" %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
-<% final PwmSession pwmSession = PwmSessionWrapper.readPwmSession(request); %>
-<% final HelpdeskBean helpdeskBean = pwmSession.getHelpdeskBean(); %>
+<% final PwmRequest pwmRequest = JspUtility.getPwmRequest(pageContext); %>
+<% final HelpdeskBean helpdeskBean = pwmRequest.getPwmSession().getHelpdeskBean(); %>
 <html dir="<pwm:LocaleOrientation/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <body class="nihilo">
@@ -74,15 +72,7 @@
     <input type="hidden" name="userKey" id="userKey" value=""/>
     <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>
 </form>
-<pwm:script>
-<script>
-    PWM_GLOBAL['startupFunctions'].push(function(){
-        PWM_VAR['helpdesk_search_columns'] = <%=JsonUtil.serializeMap(helpdeskBean.getSearchColumnHeaders())%>;
-        PWM_HELPDESK.initHelpdeskSearchPage();
-    });
-</script>
-</pwm:script>
-<pwm:script-ref url="/public/resources/js/helpdesk.js"/>
 <jsp:include page="/WEB-INF/jsp/fragment/footer.jsp"/>
+<pwm:script-ref url="/public/resources/js/helpdesk.js"/>
 </body>
 </html>

+ 2 - 9
pwm/servlet/web/WEB-INF/jsp/logout.jsp

@@ -1,4 +1,3 @@
-<%@ page import="password.pwm.http.JspUtility" %>
 <%--
   ~ Password Management Servlets (PWM)
   ~ http://code.google.com/p/pwm/
@@ -22,6 +21,8 @@
   --%>
 
 <!DOCTYPE html>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
 <%@ page language="java" session="true" isThreadSafe="true"
          contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
@@ -38,14 +39,6 @@
     </div>
     <div class="push"></div>
 </div>
-<pwm:script>
-<script type="text/javascript">
-    PWM_GLOBAL['startupFunctions'].push(function(){
-        PWM_GLOBAL['idle_suspendTimeout'] = true;
-    });
-</script>
-</pwm:script>
-<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 4 - 3
pwm/servlet/web/WEB-INF/jsp/newuser-wait.jsp

@@ -23,6 +23,9 @@
   --%>
 
 <!DOCTYPE html>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html dir="<pwm:LocaleOrientation/>">
@@ -41,7 +44,7 @@
 %>
 <meta http-equiv="refresh" content="<%=refreshSeconds%>;url=NewUser?processAction=complete&pwmFormID=<pwm:FormID/>">
 <div id="wrapper">
-    <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS); %>
+
     <jsp:include page="fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="Title_PleaseWait"/>
     </jsp:include>
@@ -65,7 +68,6 @@
 <pwm:script>
 <script type="text/javascript">
     PWM_GLOBAL['startupFunctions'].push(function(){
-        PWM_GLOBAL['idle_suspendTimeout'] = true;
         require(["dojo/parser", "dijit/ProgressBar","dojo/ready"], function(parser,registry){
             parser.parse();
             PWM_NEWUSER.refreshCreateStatus(<%=checkIntervalSeconds * 1000%>);
@@ -74,7 +76,6 @@
 </script>
 </pwm:script>
 <pwm:script-ref url="/public/resources/js/newuser.js"/>
-<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 2 - 34
pwm/servlet/web/WEB-INF/jsp/peoplesearch.jsp

@@ -20,40 +20,17 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   --%>
 
-<%@ page import="password.pwm.config.FormConfiguration" %>
-<%@ page import="password.pwm.error.PwmException" %>
-<%@ page import="password.pwm.util.JsonUtil" %>
-<%@ page import="password.pwm.util.StringUtil" %>
-<%@ page import="java.util.LinkedHashMap" %>
-<%@ page import="java.util.List" %>
-<%@ page import="java.util.Map" %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html dir="<pwm:LocaleOrientation/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
-<%
-    final Map<String, String> searchColumns = new LinkedHashMap<String, String>();
-    String photoStyle = "";
-    try {
-        final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
-        photoStyle = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_STYLE_ATTR);
-        final List<FormConfiguration> searchForm = pwmRequest.getConfig().readSettingAsForm(PwmSetting.PEOPLE_SEARCH_RESULT_FORM);
-        for (final FormConfiguration formConfiguration : searchForm) {
-            searchColumns.put(formConfiguration.getName(),
-                    formConfiguration.getLabel(pwmRequest.getLocale()));
-        }
-    } catch (PwmException e) {
-            /* noop */
-    }
-
-%>
 <body class="nihilo">
 <div id="wrapper">
     <jsp:include page="/WEB-INF/jsp/fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="Title_PeopleSearch"/>
     </jsp:include>
-    <div id="centerbody" class="wide">
+    <div id="centerbody" class="wide" style="height:100%">
         <%@ include file="/WEB-INF/jsp/fragment/message.jsp" %>
 
         <div id="searchControlPanel" style="position: relative; margin-left: auto; margin-right: auto; width: 100%; text-align: center;">
@@ -88,16 +65,7 @@
     </div>
     <div class="push"></div>
 </div>
-<pwm:script>
-    <script>
-        PWM_GLOBAL['startupFunctions'].push(function(){
-            PWM_VAR['peoplesearch_search_columns'] = <%=JsonUtil.serializeMap(searchColumns)%>;
-            PWM_VAR['photo_style_attribute'] = '<%=StringUtil.escapeJS(photoStyle)%>';
-            PWM_PS.initPeopleSearchPage();
-        });
-    </script>
-</pwm:script>
-<pwm:script-ref url="/public/resources/js/peoplesearch.js"/>
 <%@ include file="fragment/footer.jsp" %>
+<pwm:script-ref url="/public/resources/js/peoplesearch.js"/>
 </body>
 </html>

+ 6 - 4
pwm/servlet/web/WEB-INF/jsp/setupresponses-helpdesk.jsp

@@ -41,13 +41,15 @@
         <form action="<pwm:url url='SetupResponses'/>" method="post" name="form-setupResponses"
               enctype="application/x-www-form-urlencoded" id="form-setupResponses" class="pwm-form">
             <%@ include file="fragment/message.jsp" %>
-            <% request.setAttribute("setupData",responseBean.getHelpdeskResponseData()); %>
-            <jsp:include page="fragment/setupresponses-form.jsp"/>
+            <div id="pwm-setupResponsesDiv">
+                <% request.setAttribute("setupData",responseBean.getHelpdeskResponseData()); %>
+                <jsp:include page="fragment/setupresponses-form.jsp"/>
+            </div>
             <div class="buttonbar">
                 <input type="hidden" name="processAction" value="setHelpdeskResponses"/>
                 <button type="submit" name="setResponses" class="btn" id="button-setResponses">
-                <pwm:if test="showIcons"><span class="btn-icon fa fa-forward"></span></pwm:if>
-                <pwm:display key="Button_SetResponses"/>
+                    <pwm:if test="showIcons"><span class="btn-icon fa fa-forward"></span></pwm:if>
+                    <pwm:display key="Button_SetResponses"/>
                 </button>
                 <%@ include file="/WEB-INF/jsp/fragment/button-reset.jsp" %>
                 <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>

+ 4 - 4
pwm/servlet/web/WEB-INF/jsp/setupresponses.jsp

@@ -22,8 +22,7 @@
 
 <%@ page import="password.pwm.http.bean.SetupResponsesBean" %>
 <!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" %>
 <% final SetupResponsesBean responseBean = JspUtility.getPwmSession(pageContext).getSetupResponseBean(); %>
 <html dir="<pwm:LocaleOrientation/>">
@@ -35,11 +34,12 @@
     </jsp:include>
     <div id="centerbody">
         <p><pwm:display key="Display_SetupResponses"/></p>
-        <form action="<pwm:url url='SetupResponses'/>" method="post" name="form-setupResponses"
-              enctype="application/x-www-form-urlencoded" id="form-setupResponses" class="pwm-form">
+        <form action="<pwm:url url='SetupResponses'/>" method="post" name="form-setupResponses" enctype="application/x-www-form-urlencoded" id="form-setupResponses" class="pwm-form">
             <%@ include file="fragment/message.jsp" %>
+            <div id="pwm-setupResponsesDiv">
             <% request.setAttribute("setupData",responseBean.getResponseData()); %>
             <jsp:include page="fragment/setupresponses-form.jsp"/>
+            </div>
             <div class="buttonbar">
                 <input type="hidden" name="processAction" value="setResponses"/>
                 <button type="submit" name="setResponses" class="btn" id="button-setResponses">

+ 3 - 3
pwm/servlet/web/public/health.jsp

@@ -26,11 +26,13 @@
   --%>
 
 <!DOCTYPE html>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT);%>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_THEME); %>
 <%@ page language="java" session="true" isThreadSafe="true"
          contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html dir="<pwm:LocaleOrientation/>">
-<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_THEME); %>
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <% try { JspUtility.getPwmSession(pageContext).unauthenticateUser(); } catch (Exception e) { }%>
 <%
@@ -233,12 +235,10 @@
 
     PWM_GLOBAL['startupFunctions'].push(function(){
         startupHealthPage();
-        PWM_GLOBAL['idle_suspendTimeout'] = true;
     });
 </script>
 </pwm:script>
 <pwm:script-ref url="/public/resources/js/admin.js"/>
-<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 30 - 15
pwm/servlet/web/public/index.jsp

@@ -1,3 +1,4 @@
+<%@ page import="password.pwm.http.JspUtility" %>
 <%--
   ~ Password Management Servlets (PWM)
   ~ http://code.google.com/p/pwm/
@@ -25,6 +26,7 @@
          contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html dir="<pwm:LocaleOrientation/>">
+<% final PwmRequest index_pwmRequest = JspUtility.getPwmRequest(pageContext); %>
 <%@ include file="../WEB-INF/jsp/fragment/header.jsp" %>
 <body class="nihilo">
 <div id="wrapper">
@@ -32,9 +34,9 @@
         <jsp:param name="pwm.PageName" value="Title_MainPage"/>
     </jsp:include>
     <div id="centerbody">
-        <table style="border:0">
-            <tr style="border:0">
-                <td style="border:0" class="menubutton_key">
+        <table class="noborder">
+            <tr>
+                <td class="menubutton_key">
                     <a class="menubutton" id="Button_Login" href="<pwm:context/><pwm:url url='/private'/>">
                         <pwm:if test="showIcons"><span class="btn-icon fa fa-sign-in"></span></pwm:if>
                         <pwm:display key="Button_Login"/>
@@ -44,9 +46,9 @@
                     <p><pwm:display key="Display_Login"/></p>
                 </td>
             </tr>
-            <% if (ContextManager.getPwmApplication(session).getConfig() != null && ContextManager.getPwmApplication(session).getConfig().readSettingAsBoolean(PwmSetting.FORGOTTEN_PASSWORD_ENABLE)) { %>
-            <tr style="border:0">
-                <td style="border:0" class="menubutton_key">
+            <% if (index_pwmRequest.getConfig() != null && index_pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.FORGOTTEN_PASSWORD_ENABLE)) { %>
+            <tr>
+                <td class="menubutton_key">
                     <a class="menubutton" id="Title_ForgottenPassword" href="<pwm:context/><pwm:url url='/public/ForgottenPassword'/>">
                         <pwm:if test="showIcons"><span class="btn-icon fa fa-unlock"></span></pwm:if>
                         <pwm:display key="Title_ForgottenPassword"/>
@@ -57,9 +59,9 @@
                 </td>
             </tr>
             <% } %>
-            <% if (ContextManager.getPwmApplication(session).getConfig() != null && ContextManager.getPwmApplication(session).getConfig().readSettingAsBoolean(PwmSetting.FORGOTTEN_USERNAME_ENABLE)) { %>
-            <tr style="border:0">
-                <td style="border:0" class="menubutton_key">
+            <% if (index_pwmRequest.getConfig() != null && index_pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.FORGOTTEN_USERNAME_ENABLE)) { %>
+            <tr>
+                <td class="menubutton_key">
                     <a class="menubutton" id="Title_ForgottenUsername" href="<pwm:context/><pwm:url url='/public/ForgottenUsername'/>">
                         <pwm:if test="showIcons"><span class="btn-icon fa fa-unlock"></span></pwm:if>
                         <pwm:display key="Title_ForgottenUsername"/>
@@ -70,9 +72,9 @@
                 </td>
             </tr>
             <% } %>
-            <% if (ContextManager.getPwmApplication(session).getConfig() != null && ContextManager.getPwmApplication(session).getConfig().readSettingAsBoolean(PwmSetting.ACTIVATE_USER_ENABLE)) { %>
-            <tr style="border:0">
-                <td style="border:0" class="menubutton_key">
+            <% if (index_pwmRequest.getConfig() != null && index_pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.ACTIVATE_USER_ENABLE)) { %>
+            <tr>
+                <td class="menubutton_key">
                     <a class="menubutton" id="Title_ActivateUser" href="<pwm:context/><pwm:url url='/public/ActivateUser'/>">
                         <pwm:if test="showIcons"><span class="btn-icon fa fa-graduation-cap"></span></pwm:if>
                         <pwm:display key="Title_ActivateUser"/>
@@ -83,9 +85,9 @@
                 </td>
             </tr>
             <% } %>
-            <% if (ContextManager.getPwmApplication(session).getConfig() != null && ContextManager.getPwmApplication(session).getConfig().readSettingAsBoolean(PwmSetting.NEWUSER_ENABLE)) { %>
-            <tr style="border:0">
-                <td style="border:0" class="menubutton_key">
+            <% if (index_pwmRequest.getConfig() != null && index_pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.NEWUSER_ENABLE)) { %>
+            <tr>
+                <td class="menubutton_key">
                     <a class="menubutton" id="Title_NewUser" href="<pwm:context/><pwm:url url='/public/NewUser'/>">
                         <pwm:if test="showIcons"><span class="btn-icon fa fa-file-text-o"></span></pwm:if>
                         <pwm:display key="Title_NewUser"/>
@@ -96,6 +98,19 @@
                 </td>
             </tr>
             <% } %>
+            <% if (index_pwmRequest.getConfig() != null && index_pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_ENABLE_PUBLIC)) { %>
+            <tr>
+                <td class="menubutton_key">
+                    <a class="menubutton" href="<pwm:url url='PeopleSearch'/>">
+                        <pwm:if test="showIcons"><span class="btn-icon fa fa-search"></span></pwm:if>
+                        <pwm:display key="Title_PeopleSearch"/>
+                    </a>
+                </td>
+                <td>
+                    <p><pwm:display key="Long_Title_PeopleSearch"/></p>
+                </td>
+            </tr>
+            <% } %>
         </table>
     </div>
     <div class="push"></div>

+ 1 - 1
pwm/servlet/web/public/reference/license.jsp

@@ -28,6 +28,7 @@
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_THEME); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_REQ_COUNTER); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <html dir="<pwm:LocaleOrientation/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
@@ -266,7 +267,6 @@
 </div>
 <pwm:script>
 <script type="text/javascript">
-    PWM_GLOBAL['idle_suspendTimeout'] = true;
     PWM_GLOBAL['startupFunctions'].push(function(){
         require(["dojo/parser","dijit/TitlePane"],function(dojoParser){
             dojoParser.parse();

+ 2 - 8
pwm/servlet/web/public/reference/localeinfo.jsp

@@ -32,7 +32,9 @@
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_WARNINGS); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_THEME); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_REQ_COUNTER); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%
@@ -400,14 +402,6 @@
         <br/><br/><br/><br/>
     </div>
 </div>
-<pwm:script>
-    <script type="text/javascript">
-        PWM_GLOBAL['startupFunctions'].push(function(){
-            PWM_GLOBAL['idle_suspendTimeout'] = true;
-        });
-    </script>
-</pwm:script>
-<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 1 - 7
pwm/servlet/web/public/reference/referencedoc.jsp

@@ -35,6 +35,7 @@
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_WARNINGS); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_THEME); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_REQ_COUNTER); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
@@ -392,13 +393,6 @@
         <div class="push"></div>
     </div>
 </div>
-<pwm:script>
-    <script type="text/javascript">
-        PWM_GLOBAL['startupFunctions'].push(function(){
-            PWM_GLOBAL['idle_suspendTimeout'] = true;
-        });
-    </script>
-</pwm:script>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 3 - 6
pwm/servlet/web/public/reference/rest.jsp

@@ -27,6 +27,8 @@
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_REQ_COUNTER); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
+<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html; charset=UTF-8" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html dir="<pwm:LocaleOrientation/>">
@@ -1802,17 +1804,12 @@ Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
 <pwm:script>
 <script type="text/javascript">
     PWM_GLOBAL['startupFunctions'].push(function(){
-        PWM_GLOBAL['idle_suspendTimeout'] = true;
-        require(["dojo/parser","dojo/domReady!","dijit/layout/TabContainer","dijit/layout/ContentPane","dijit/Dialog"],function(dojoParser){
-            dojoParser.parse();
-        });
-        require(["dojo/parser","dijit/TitlePane"],function(dojoParser){
+        require(["dojo/parser","dojo/domReady!","dijit/layout/TabContainer","dijit/layout/ContentPane","dijit/Dialog","dijit/TitlePane"],function(dojoParser){
             dojoParser.parse();
         });
     });
 </script>
 </pwm:script>
-<% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

二进制
pwm/servlet/web/public/resources/dojo.zip


+ 1 - 1
pwm/servlet/web/public/resources/js/changepassword.js

@@ -266,7 +266,7 @@ PWM_CHANGEPW.doRandomGeneration=function(randomConfig) {
 
     eventHandlers.push(function(){
         PWM_MAIN.addEventHandler('cancelRandomsButton','click',function(){
-            PWM_MAIN.clearDijitWidget('dialogPopup');
+            PWM_MAIN.closeWaitDialog('dialogPopup');
         });
         PWM_MAIN.addEventHandler('moreRandomsButton','click',function(){
             PWM_CHANGEPW.beginFetchRandoms(randomConfig);

+ 2 - 1
pwm/servlet/web/public/resources/js/configeditor-settings.js

@@ -717,8 +717,9 @@ FormTableHandler.addRow = function(keyName) {
         });
     },okAction:function(){
         var currentSize = PWM_MAIN.itemCount(PWM_VAR['clientSettingCache'][keyName]);
-        PWM_VAR['clientSettingCache'][keyName][currentSize + 1] = FormTableHandler.newRowValue
+        PWM_VAR['clientSettingCache'][keyName][currentSize + 1] = FormTableHandler.newRowValue;
         PWM_VAR['clientSettingCache'][keyName][currentSize + 1].name = PWM_VAR['newFormFieldName'];
+        PWM_VAR['clientSettingCache'][keyName][currentSize + 1].labels = {'':PWM_VAR['newFormFieldName']};
         FormTableHandler.writeFormSetting(keyName,function(){
             FormTableHandler.init(keyName);
         });

+ 1 - 4
pwm/servlet/web/public/resources/js/configeditor.js

@@ -255,10 +255,7 @@ PWM_CFGEDIT.saveConfiguration = function() {
             var url = "ConfigEditor?processAction=finishEditing";
             var loadFunction = function(data) {
                 if (data['error'] == true) {
-                    PWM_MAIN.showDialog({
-                        title: PWM_MAIN.showString('Title_Error'),
-                        text: data['errorDetail']
-                    })
+                    PWM_MAIN.showErrorDialog(data);
                 } else {
                     console.log('save completed');
                     PWM_MAIN.showWaitDialog({title:'Save complete, restarting application...',loadFunction:function(){

+ 1 - 9
pwm/servlet/web/public/resources/js/configmanager.js

@@ -167,15 +167,7 @@ PWM_CONFIG.showHeaderHealth = function() {
                 }
                 if (hasWarnTopics) {
                     PWM_MAIN.openHeaderWarningPanel();
-                    parentDiv.innerHTML = '<div id="panel-healthHeaderErrors" class="header-error"><span class="fa fa-warning"></span> ' + PWM_ADMIN.showString('Header_ConfigWarningsPresent') + '</div>';
-                    var tooltipBody = PWM_ADMIN.makeHealthHtml(data['data'],true,false);
-                    /*
-                    PWM_MAIN.showTooltip({
-                        position:'below',
-                        id:'panel-healthHeaderErrors',
-                        text:tooltipBody
-                    });
-                    */
+                    parentDiv.innerHTML = '<div id="panel-healthHeaderErrors" class="header-error"><span class="fa fa-warning"></span> ' + PWM_ADMIN.showString('Header_HealthWarningsPresent') + '</div>';
                 }
                 setTimeout(function () {
                     PWM_CONFIG.showHeaderHealth()

+ 33 - 13
pwm/servlet/web/public/resources/js/helpdesk.js

@@ -273,18 +273,6 @@ PWM_HELPDESK.makeSearchGrid = function(nextAction) {
     });
 };
 
-PWM_HELPDESK.initHelpdeskSearchPage = function() {
-    PWM_HELPDESK.makeSearchGrid(function(){
-        PWM_MAIN.addEventHandler('username', "keyup, input", function(){
-            PWM_HELPDESK.processHelpdeskSearch();
-        });
-        if (PWM_MAIN.getObject('username').value && PWM_MAIN.getObject('username').value.length > 0) {
-            PWM_HELPDESK.processHelpdeskSearch();
-        }
-    });
-};
-
-
 PWM_HELPDESK.deleteUser = function(userKey) {
     PWM_MAIN.showConfirmDialog({
         text:PWM_MAIN.showString('Confirm_DeleteUser'),
@@ -404,4 +392,36 @@ PWM_HELPDESK.sendVerificationToken = function(userKey,choiceFlag) {
         },
         loadFunction:dialoagLoadFunction
     });
-};
+};
+
+PWM_HELPDESK.initHelpdeskSearchPage = function() {
+    PWM_HELPDESK.makeSearchGrid(function(){
+        PWM_MAIN.addEventHandler('username', "keyup, input", function(){
+            PWM_HELPDESK.processHelpdeskSearch();
+        });
+        if (PWM_MAIN.getObject('username').value && PWM_MAIN.getObject('username').value.length > 0) {
+            PWM_HELPDESK.processHelpdeskSearch();
+        }
+    });
+};
+
+PWM_HELPDESK.initPage = function() {
+    var applicationData = PWM_MAIN.getObject("application-info");
+    var jspName = applicationData ? applicationData.getAttribute("data-jsp-name") : "";
+    if ("helpdesk.jsp" == jspName) {
+        PWM_HELPDESK.initHelpdeskSearchPage();
+        PWM_MAIN.ajaxRequest("Helpdesk?processAction=clientData",function(data){
+            if (data['error']) {
+                PWM_MAIN.showErrorDialog(data);
+                return;
+            }
+            for (var keyName in data['data']) {
+                PWM_VAR[keyName] = data['data'][keyName];
+            }
+            console.log('loaded helpdesk clientData');
+            PWM_HELPDESK.initHelpdeskSearchPage();
+        },{method:"GET"});
+    }
+};
+
+PWM_HELPDESK.initPage();

+ 44 - 4
pwm/servlet/web/public/resources/js/main.js

@@ -674,6 +674,7 @@ PWM_MAIN.initLocaleSelectorMenu = function(attachNode) {
 
 PWM_MAIN.showErrorDialog = function(error, options) {
     options = options === undefined ? {} : options;
+    var forceReload = false;
     var body = '';
     var logMsg = '';
     var titleMsg = PWM_MAIN.showString('Title_Error');
@@ -683,6 +684,10 @@ PWM_MAIN.showErrorDialog = function(error, options) {
         logMsg += options['text'];
     }
     if (error && error['error']) {
+        var code = error['errorCode'];
+        if (code == 5028 || code == 5035) {
+            forceReload = true;
+        }
         titleMsg += ' ' + error['errorCode'];
         logMsg += ' ' + error['errorCode'];
 
@@ -694,12 +699,23 @@ PWM_MAIN.showErrorDialog = function(error, options) {
         }
     } else {
         body += error;
-        logMsg + error;
+        logMsg += error;
+    }
+
+    if (forceReload) {
+        logMsg += 'due to error code type, reloading page.';
     }
 
     console.log('displaying error message: ' + logMsg);
     options['title'] = titleMsg;
     options['text'] = body;
+    options['okAction'] = function() {
+        if (forceReload) { // incorrect page sequence;
+            var newURL = window.location.pathname;
+            PWM_MAIN.goto(newURL);
+            PWM_MAIN.showWaitDialog();
+        }
+    };
     PWM_MAIN.showDialog(options);
 };
 
@@ -736,9 +752,12 @@ PWM_MAIN.showWaitDialog = function(options) {
 };
 
 PWM_MAIN.html5DialogSupport = function() {
-    var testdialog=document.createElement("dialog");
-    testdialog.setAttribute("open", "");
-    return (testdialog.open==true);
+    if (PWM_GLOBAL['client.js.enableHtml5Dialog']) {
+        var testdialog = document.createElement("dialog");
+        testdialog.setAttribute("open", "");
+        return (testdialog.open == true);
+    }
+    return false;
 };
 
 PWM_MAIN.showDialog = function(options) {
@@ -1914,5 +1933,26 @@ PWM_MAIN.submitPostAction = function(buttonID,actionUrl,actionValue) {
     });
 };
 
+PWM_MAIN.doQuery = function(queryString, resultFunction) {
+    require(["dojo/query"], function (query) {
+        var results = query(queryString);
+        for (var i = 0; i < results.length; i++) {
+            (function(iterator){
+                var result = results[iterator];
+                resultFunction(result)
+            })(i);
+        }
+    });
+};
+
+PWM_MAIN.doIfQueryHasResults = function(queryString, trueFunction) {
+    require(["dojo/query"], function (query) {
+        var results = query(queryString);
+        if (results && results.length > 0) {
+            trueFunction();
+        }
+    });
+};
+
 PWM_MAIN.pageLoadHandler();
 

+ 31 - 14
pwm/servlet/web/public/resources/js/peoplesearch.js

@@ -43,8 +43,8 @@ PWM_PS.processPeopleSearch = function() {
     validationProps['processResultsFunction'] = function(data) {
         var grid = PWM_VAR['peoplesearch_search_grid'];
         if (data['error']) {
-            PWM_MAIN.showError("error: " + data['errorMessage']);
             grid.refresh();
+            PWM_MAIN.showErrorDialog(data);
         } else {
 
             var gridData = data['data']['searchResults'];
@@ -178,7 +178,6 @@ PWM_PS.showUserDetail = function(userKey) {
             var url = "PeopleSearch?processAction=detail";
             var loadFunction = function(data) {
                 if (data['error'] == true) {
-                    console.error('unable to load people detail, error: ' + data['errorDetail']);
                     PWM_MAIN.closeWaitDialog();
                     PWM_MAIN.showErrorDialog(data);
                     return;
@@ -191,7 +190,7 @@ PWM_PS.showUserDetail = function(userKey) {
                     text:htmlBody,
                     showClose:true,
                     loadFunction:function(){
-                        var photoURL = PWM_MAIN.addPwmFormIDtoURL(data['data']['photoURL']);
+                        var photoURL = data['data']['photoURL'];
                         if (photoURL) {
                             PWM_PS.loadPicture(PWM_MAIN.getObject("userPhotoParentDiv"),photoURL);
                         }
@@ -224,18 +223,10 @@ PWM_PS.makeSearchGrid = function(nextFunction) {
             });
 };
 
-PWM_PS.initPeopleSearchPage = function() {
-    PWM_PS.makeSearchGrid(function(){
-        PWM_MAIN.addEventHandler('username', "keyup, input", function(){
-            PWM_PS.processPeopleSearch();
-        });
-        if (PWM_MAIN.getObject('username').value && PWM_MAIN.getObject('username').value.length > 0) {
-            PWM_PS.processPeopleSearch();
-        }
-    });
-};
-
 PWM_PS.loadPicture = function(parentDiv,url) {
+    if (url.lastIndexOf('http', 0) !== 0) {
+        url = PWM_MAIN.addPwmFormIDtoURL(url);
+    }
     require(["dojo/on"], function(on){
         var image = new Image();
         image.setAttribute('id',"userPhotoImage");
@@ -250,3 +241,29 @@ PWM_PS.loadPicture = function(parentDiv,url) {
     });
 };
 
+PWM_PS.initPeopleSearchPage = function() {
+    var applicationData = PWM_MAIN.getObject("application-info");
+    var jspName = applicationData ? applicationData.getAttribute("data-jsp-name") : "";
+    if ("peoplesearch.jsp" == jspName) {
+        PWM_MAIN.ajaxRequest("PeopleSearch?processAction=clientData",function(data){
+            if (data['error']) {
+                PWM_MAIN.showErrorDialog(data);
+                return;
+            }
+            for (var keyName in data['data']) {
+                PWM_VAR[keyName] = data['data'][keyName];
+            }
+            console.log('loaded peoplesearch jsClientData');
+            PWM_PS.makeSearchGrid(function () {
+                PWM_MAIN.addEventHandler('username', "keyup, input", function () {
+                    PWM_PS.processPeopleSearch();
+                });
+                if (PWM_MAIN.getObject('username').value && PWM_MAIN.getObject('username').value.length > 0) {
+                    PWM_PS.processPeopleSearch();
+                }
+            });
+        },{method:"GET"});
+    }
+};
+
+PWM_PS.initPeopleSearchPage();

+ 53 - 39
pwm/servlet/web/public/resources/js/responses.js

@@ -20,6 +20,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+"use strict";
+
 var PWM_RESPONSES = PWM_RESPONSES || {};
 var PWM_VAR = PWM_VAR || {};
 var require;
@@ -104,16 +106,11 @@ PWM_RESPONSES.makeSelectOptionsDistinct=function() {
             var responseID = selectedElement.getAttribute('data-response-id');
             selectedElement.innerHTML = '';
             if (selectedValue == 'UNSELECTED') {
-                PWM_MAIN.getObject(responseID).disabled = true;
-                PWM_MAIN.getObject(responseID).readonly = true;
                 var unselectedOption = document.createElement('option');
                 unselectedOption.value = 'UNSELECTED';
                 unselectedOption.innerHTML = '&nbsp;&mdash;&nbsp;' + initialChoiceText + '&nbsp;&mdash;&nbsp;';
                 unselectedOption.selected = true;
                 selectedElement.appendChild(unselectedOption);
-            } else {
-                PWM_MAIN.getObject(responseID).disabled = false;
-                PWM_MAIN.getObject(responseID).readonly = false;
             }
 
             for (var i = 0; i < allPossibleTexts.length; i++) {
@@ -138,48 +135,65 @@ PWM_RESPONSES.makeSelectOptionsDistinct=function() {
 };
 
 PWM_RESPONSES.startupResponsesPage=function() {
-    var initialPrompt = PWM_MAIN.showString('Display_ResponsesPrompt');
-    if (initialPrompt != null && initialPrompt.length > 1) {
-        var messageElement = PWM_MAIN.getObject("message");
-        if (messageElement.firstChild.nodeValue.length < 2) {
-            PWM_MAIN.showInfo(initialPrompt);
+    PWM_MAIN.doIfQueryHasResults('#pwm-setupResponsesDiv',function(){
+        var initialPrompt = PWM_MAIN.showString('Display_ResponsesPrompt');
+        if (initialPrompt != null && initialPrompt.length > 1) {
+            var messageElement = PWM_MAIN.getObject("message");
+            if (messageElement.firstChild.nodeValue.length < 2) {
+                PWM_MAIN.showInfo(initialPrompt);
+            }
         }
-    }
-    PWM_MAIN.addEventHandler('form-setupResponses','input',function(){
-        console.log('form-setupResponses input event handler');
-        PWM_RESPONSES.validateResponses();
+        PWM_MAIN.addEventHandler('form-setupResponses','input',function(){
+            console.log('form-setupResponses input event handler');
+            PWM_RESPONSES.validateResponses();
+        });
+        PWM_MAIN.getObject("button-setResponses").disabled = true;
+        PWM_RESPONSES.initSimpleRandomElements();
     });
-    PWM_MAIN.getObject("button-setResponses").disabled = true;
-    PWM_RESPONSES.initSimpleRandomElements();
 };
 
 
 PWM_RESPONSES.initSimpleRandomElements = function() {
+    console.log('entering initSimpleRandomElements');
     PWM_VAR['simpleRandomSelectElements'] = [];
     PWM_VAR['focusInValues'] = {};
-    require(["dojo/query","dojo/on"], function(query,on){
-        var results = query('.simpleModeResponseSelection');
-        for (var i = 0; i < results.length; i++) {
-            (function(itemIterator){
-                var element = results[itemIterator];
-                PWM_VAR['simpleRandomSelectElements'].push(element.id);
-                on(element, "focusin", function(){
-                    PWM_VAR['focusInValues'][element.id] = element.selectedIndex;
-                });
-                on(element, "click,blur", function(){
-                    if (PWM_VAR['focusInValues'][element.id] != element.selectedIndex) {
-                        var selectedIndex = element.selectedIndex;
-                        var selectedValue = element.options[selectedIndex].value;
-                        if (selectedValue != 'UNSELECTED') {
-                            PWM_RESPONSES.makeSelectOptionsDistinct();
-                            var responseID = element.getAttribute('data-response-id');
-                            PWM_MAIN.getObject(responseID).value = '';
-                            PWM_MAIN.getObject(responseID).disabled = false;
-                            PWM_MAIN.getObject(responseID).focus();
-                        }
-                    }
-                });
-            })(i);
+
+    var updateResponseInputField = function(element) {
+        var responseID = element.getAttribute('data-response-id');
+        if (element.value == 'UNSELECTED') {
+            PWM_MAIN.getObject(responseID).disabled = true;
+            PWM_MAIN.getObject(responseID).readonly = true;
+        } else {
+            PWM_MAIN.getObject(responseID).disabled = false;
+            PWM_MAIN.getObject(responseID).readonly = false;
         }
+
+    };
+
+    PWM_MAIN.doQuery('.simpleModeResponseSelection',function(element){
+        PWM_VAR['simpleRandomSelectElements'].push(element.id);
+        updateResponseInputField(element);
+        PWM_MAIN.addEventHandler(element.id,"keypress,keyup,keydown",function(){
+            updateResponseInputField(element);
+        });
+        PWM_MAIN.addEventHandler(element.id,"focusin",function(){
+            PWM_VAR['focusInValues'][element.id] = element.selectedIndex;
+        });
+        PWM_MAIN.addEventHandler(element.id,"click,blur",function(){
+            if (PWM_VAR['focusInValues'][element.id] != element.selectedIndex) {
+                var selectedIndex = element.selectedIndex;
+                var selectedValue = element.options[selectedIndex].value;
+                if (selectedValue != 'UNSELECTED') {
+                    var responseID = element.getAttribute('data-response-id');
+                    var responseElement = PWM_MAIN.getObject(responseID);
+                    responseElement.value = '';
+                    responseElement.disabled = false;
+                    responseElement.readonly = false;
+                    responseElement.focus();
+                    PWM_RESPONSES.makeSelectOptionsDistinct();
+                }
+            }
+        });
     });
+    PWM_RESPONSES.makeSelectOptionsDistinct();
 };

+ 5 - 1
pwm/servlet/web/public/resources/style.css

@@ -540,7 +540,7 @@ img.qrcodeimage {
 
 #peoplesearch-searchResultsGrid {
     min-height: 400px;
-    height: 60vh;
+    height: 70vh;
 }
 
 #helpdesk-searchResultsGrid {
@@ -688,3 +688,7 @@ progress:not([value]) {
     margin-right: 10%;
     padding-top: 70px;
 }
+
+.dgrid-row-odd {
+    background: #fdfdfd;
+}