jrivard преди 10 години
родител
ревизия
15ad305156
променени са 60 файла, в които са добавени 987 реда и са изтрити 306 реда
  1. 1 0
      pwm/servlet/src/password/pwm/AppProperty.java
  2. 1 0
      pwm/servlet/src/password/pwm/AppProperty.properties
  3. 7 0
      pwm/servlet/src/password/pwm/PwmApplication.java
  4. 3 0
      pwm/servlet/src/password/pwm/PwmConstants.java
  5. 36 1
      pwm/servlet/src/password/pwm/bean/UserIdentity.java
  6. 1 8
      pwm/servlet/src/password/pwm/config/Configuration.java
  7. 70 3
      pwm/servlet/src/password/pwm/config/FormUtility.java
  8. 1 1
      pwm/servlet/src/password/pwm/config/PwmSetting.java
  9. 3 3
      pwm/servlet/src/password/pwm/config/PwmSetting.xml
  10. 12 1
      pwm/servlet/src/password/pwm/config/PwmSettingCategory.java
  11. 12 0
      pwm/servlet/src/password/pwm/config/PwmSettingXml.java
  12. 39 16
      pwm/servlet/src/password/pwm/config/profile/PwmPasswordPolicy.java
  13. 62 40
      pwm/servlet/src/password/pwm/health/ConfigurationChecker.java
  14. 2 0
      pwm/servlet/src/password/pwm/health/HealthMessage.java
  15. 131 11
      pwm/servlet/src/password/pwm/health/LDAPStatusChecker.java
  16. 3 3
      pwm/servlet/src/password/pwm/http/PwmHttpRequestWrapper.java
  17. 10 1
      pwm/servlet/src/password/pwm/http/PwmResponse.java
  18. 12 0
      pwm/servlet/src/password/pwm/http/SessionManager.java
  19. 41 30
      pwm/servlet/src/password/pwm/http/filter/AuthenticationFilter.java
  20. 17 0
      pwm/servlet/src/password/pwm/http/servlet/ConfigEditorServlet.java
  21. 4 2
      pwm/servlet/src/password/pwm/http/servlet/ConfigGuideServlet.java
  22. 2 4
      pwm/servlet/src/password/pwm/http/servlet/ConfigManagerServlet.java
  23. 27 0
      pwm/servlet/src/password/pwm/http/servlet/ForgottenPasswordServlet.java
  24. 7 2
      pwm/servlet/src/password/pwm/http/servlet/NewUserServlet.java
  25. 1 1
      pwm/servlet/src/password/pwm/http/servlet/OAuthConsumerServlet.java
  26. 1 2
      pwm/servlet/src/password/pwm/http/servlet/SetupOtpServlet.java
  27. 11 40
      pwm/servlet/src/password/pwm/http/servlet/UpdateProfileServlet.java
  28. 7 8
      pwm/servlet/src/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java
  29. 1 1
      pwm/servlet/src/password/pwm/i18n/ConfigEditor.properties
  30. 3 1
      pwm/servlet/src/password/pwm/i18n/Health.properties
  31. 8 0
      pwm/servlet/src/password/pwm/i18n/LocaleHelper.java
  32. 1 1
      pwm/servlet/src/password/pwm/i18n/Message_zh_TW.properties
  33. 0 4
      pwm/servlet/src/password/pwm/ldap/LdapOperationsHelper.java
  34. 3 8
      pwm/servlet/src/password/pwm/ldap/UserStatusReader.java
  35. 2 2
      pwm/servlet/src/password/pwm/token/TokenService.java
  36. 4 2
      pwm/servlet/src/password/pwm/util/AlertHandler.java
  37. 23 2
      pwm/servlet/src/password/pwm/util/Helper.java
  38. 53 5
      pwm/servlet/src/password/pwm/util/SecureHelper.java
  39. 4 18
      pwm/servlet/src/password/pwm/util/X509Utils.java
  40. 16 0
      pwm/servlet/src/password/pwm/util/cli/AbstractCliCommand.java
  41. 3 7
      pwm/servlet/src/password/pwm/util/cli/ConfigSetPasswordCommand.java
  42. 16 1
      pwm/servlet/src/password/pwm/util/localdb/Derby_LocalDB.java
  43. 29 9
      pwm/servlet/src/password/pwm/util/logging/PwmLogManager.java
  44. 11 7
      pwm/servlet/src/password/pwm/util/stats/StatisticsManager.java
  45. 6 4
      pwm/servlet/src/password/pwm/ws/server/rest/RestCheckPasswordServer.java
  46. 3 2
      pwm/servlet/src/password/pwm/ws/server/rest/RestProfileServer.java
  47. 2 2
      pwm/servlet/web/WEB-INF/jsp/configguide-ldap.jsp
  48. 83 0
      pwm/servlet/web/WEB-INF/jsp/forgottenpassword-remote.jsp
  49. 4 2
      pwm/servlet/web/WEB-INF/jsp/fragment/form.jsp
  50. 2 0
      pwm/servlet/web/WEB-INF/jsp/fragment/header-warnings.jsp
  51. 2 9
      pwm/servlet/web/WEB-INF/jsp/setupotpsecret-test.jsp
  52. 11 1
      pwm/servlet/web/WEB-INF/jsp/updateprofile-confirm.jsp
  53. BIN
      pwm/servlet/web/WEB-INF/lib/ldapChai.jar
  54. BIN
      pwm/servlet/web/WEB-INF/lib/ldapchai-0.6.6-SNAPSHOT.jar
  55. 34 7
      pwm/servlet/web/public/reference/referencedoc.jsp
  56. 1 1
      pwm/servlet/web/public/resources/js/changepassword.js
  57. 105 23
      pwm/servlet/web/public/resources/js/configeditor-settings.js
  58. 2 2
      pwm/servlet/web/public/resources/js/helpdesk.js
  59. 21 7
      pwm/servlet/web/public/resources/js/main.js
  60. 10 1
      pwm/servlet/web/public/resources/style.css

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

@@ -81,6 +81,7 @@ public enum AppProperty {
     HTTP_PARAM_NAME_THEME                           ("http.parameter.theme"),
     HTTP_PARAM_NAME_LOCALE                          ("http.parameter.locale"),
     HTTP_PARAM_NAME_PASSWORD_EXPIRED                ("http.parameter.passwordExpired"),
+    HTTP_PARAM_NAME_SSO_ENABLE("http.parameter.ssoBypass"),
     HTTP_PARAM_MAX_READ_LENGTH                      ("http.parameter.maxReadLength"),
     HTTP_PARAM_OAUTH_ACCESS_TOKEN                   ("http.parameter.oauth.accessToken"),
     HTTP_PARAM_OAUTH_ATTRIBUTES                     ("http.parameter.oauth.attributes"),

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

@@ -85,6 +85,7 @@ http.parameter.theme=theme
 http.parameter.locale=locale
 http.parameter.passwordExpired=passwordExpired
 http.parameter.maxReadLength=10240
+http.parameter.ssoBypass=sso
 http.parameter.oauth.accessToken=access_token
 http.parameter.oauth.attributes=attributes
 http.parameter.oauth.clientID=client_id

+ 7 - 0
pwm/servlet/src/password/pwm/PwmApplication.java

@@ -44,6 +44,7 @@ import password.pwm.util.*;
 import password.pwm.util.cache.CacheService;
 import password.pwm.util.db.DatabaseAccessorImpl;
 import password.pwm.util.intruder.IntruderManager;
+import password.pwm.util.intruder.RecordType;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBFactory;
 import password.pwm.util.logging.LocalDBLogger;
@@ -328,6 +329,12 @@ public class PwmApplication {
             LOGGER.error("error generating about application bean: " + e.getMessage());
         }
 
+        try {
+            this.getIntruderManager().clear(RecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME);
+        } catch (Exception e) {
+            LOGGER.debug("error while clearing configmanager-intruder-username from intruder table: " + e.getMessage());
+        }
+
         LOGGER.trace("completed post init tasks in " + TimeDuration.fromCurrent(startTime).asCompactString());
     }
 

+ 3 - 0
pwm/servlet/src/password/pwm/PwmConstants.java

@@ -79,6 +79,8 @@ public abstract class PwmConstants {
     public static final long VERSION_CHECK_FAIL_RETRY_MS = Long.parseLong(readPwmConstantsBundle("versionCheckFailRetryMs"));
     public static final long STATISTICS_PUBLISH_FREQUENCY_MS = Long.parseLong(readPwmConstantsBundle("statisticsPublishFrequencyMs"));
 
+    public static final String CONFIGMANAGER_INTRUDER_USERNAME = "ConfigurationManagerLogin";
+
     public static final Locale DEFAULT_LOCALE = new Locale(readPwmConstantsBundle("locale.defaultLocale"));
     public static final Charset DEFAULT_CHARSET = Charset.forName("UTF8");
 
@@ -207,6 +209,7 @@ public abstract class PwmConstants {
         RECOVER_PASSWORD_ENTER_TOKEN("forgottenpassword-entertoken.jsp"),
         RECOVER_PASSWORD_ENTER_OTP("forgottenpassword-enterotp.jsp"),
         RECOVER_PASSWORD_NAAF("forgottenpassword-naaf.jsp"),
+        RECOVER_PASSWORD_REMOTE("forgottenpassword-remote.jsp"),
         SETUP_RESPONSES("setupresponses.jsp"),
         SETUP_RESPONSES_CONFIRM("setupresponses-confirm.jsp"),
         SETUP_RESPONSES_HELPDESK("setupresponses-helpdesk.jsp"),

+ 36 - 1
pwm/servlet/src/password/pwm/bean/UserIdentity.java

@@ -22,6 +22,9 @@
 
 package password.pwm.bean;
 
+import com.novell.ldapchai.ChaiUser;
+import com.novell.ldapchai.exception.ChaiException;
+import password.pwm.PwmApplication;
 import password.pwm.config.Configuration;
 import password.pwm.config.profile.LdapProfile;
 import password.pwm.error.ErrorInformation;
@@ -39,6 +42,7 @@ public class UserIdentity implements Serializable, Comparable {
     private static final String DELIM_SEPARATOR = "|";
 
     private transient String obfuscatedValue;
+    private transient boolean canonicalized;
 
     private String userDN;
     private String ldapProfile;
@@ -71,7 +75,7 @@ public class UserIdentity implements Serializable, Comparable {
     }
 
     public String toString() {
-        return "UserIdentity: " + JsonUtil.serialize(this);
+        return "UserIdentity" + JsonUtil.serialize(this);
     }
 
     public String toObfuscatedKey(final Configuration configuration)
@@ -147,6 +151,18 @@ public class UserIdentity implements Serializable, Comparable {
         return fromDelimitedKey(key);
     }
 
+    public boolean canonicalEquals(final UserIdentity otherIdentity, final PwmApplication pwmApplication)
+            throws PwmUnrecoverableException
+    {
+        if (otherIdentity == null) {
+            return false;
+        }
+
+        final UserIdentity thisCanonicalIdentity = this.canonicalized(pwmApplication);
+        final UserIdentity otherCanonicalIdentity = otherIdentity.canonicalized(pwmApplication);
+        return thisCanonicalIdentity.equals(otherCanonicalIdentity);
+    }
+
     @Override
     public boolean equals(Object o)
     {
@@ -177,4 +193,23 @@ public class UserIdentity implements Serializable, Comparable {
 
         return thisStr.compareTo(otherStr);
     }
+
+    public UserIdentity canonicalized(final PwmApplication pwmApplication)
+            throws PwmUnrecoverableException
+    {
+        if (this.canonicalized) {
+            return this;
+        }
+
+        final ChaiUser chaiUser = pwmApplication.getProxiedChaiUser(this);
+        final String userDN;
+        try {
+            userDN = chaiUser.readCanonicalDN();
+        } catch (ChaiException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
+        }
+        final UserIdentity canonicalziedIdentity = new UserIdentity(userDN, this.getLdapProfileID());
+        canonicalziedIdentity.canonicalized = true;
+        return canonicalziedIdentity;
+    }
 }

+ 1 - 8
pwm/servlet/src/password/pwm/config/Configuration.java

@@ -337,14 +337,7 @@ public class Configuration implements Serializable, SettingReader {
     }
 
     public PwmLogLevel getEventLogLocalDBLevel() {
-        final String value = readSettingAsString(PwmSetting.EVENTS_LOCALDB_LOG_LEVEL);
-        for (final PwmLogLevel logLevel : PwmLogLevel.values()) {
-            if (logLevel.toString().equalsIgnoreCase(value)) {
-                return logLevel;
-            }
-        }
-
-        return PwmLogLevel.TRACE;
+        return readSettingAsEnum(PwmSetting.EVENTS_LOCALDB_LOG_LEVEL, PwmLogLevel.class);
     }
 
     public List<String> getChallengeProfileIDs() {

+ 70 - 3
pwm/servlet/src/password/pwm/config/FormUtility.java

@@ -23,6 +23,7 @@
 package password.pwm.config;
 
 import com.novell.ldapchai.ChaiUser;
+import com.novell.ldapchai.exception.ChaiException;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.util.SearchHelper;
@@ -32,6 +33,7 @@ import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.error.*;
 import password.pwm.http.PwmRequest;
+import password.pwm.ldap.UserDataReader;
 import password.pwm.ldap.UserSearchEngine;
 import password.pwm.util.JsonUtil;
 import password.pwm.util.StringUtil;
@@ -48,7 +50,6 @@ public class FormUtility {
 
     final private static String NEGATIVE_CACHE_HIT = "NEGATIVE_CACHE_HIT";
 
-
     public static Map<FormConfiguration, String> readFormValuesFromMap(
             final Map<String,String> inputMap,
             final Collection<FormConfiguration> formItems,
@@ -86,14 +87,43 @@ public class FormUtility {
                     throw new PwmDataValidationException(error);
                 }
             }
-            if (value != null && !formItem.isReadonly()) {
-                returnMap.put(formItem,value);
+
+            if (formItem.getType() == FormConfiguration.Type.checkbox) {
+                final String parsedValue = parseInputValueToFormValue(formItem, value);
+                returnMap.put(formItem, parsedValue);
+            } else if (value != null && !formItem.isReadonly()) {
+                final String parsedValue = parseInputValueToFormValue(formItem, value);
+                returnMap.put(formItem, parsedValue);
             }
+
         }
 
         return returnMap;
     }
 
+    private static String parseInputValueToFormValue(final FormConfiguration formConfiguration, final String input) {
+        if (formConfiguration.getType() == FormConfiguration.Type.checkbox) {
+            final boolean bValue = checkboxValueIsChecked(input);
+            return bValue ? "TRUE" : "FALSE";
+        }
+
+        return input;
+    }
+
+    public static boolean checkboxValueIsChecked(final String value) {
+        boolean bValue = false;
+        if (value != null) {
+            if (Boolean.parseBoolean(value)) {
+                bValue = true;
+            } else if ("on".equalsIgnoreCase(value)) {
+                bValue = true;
+            } else if ("checked".equalsIgnoreCase(value)) {
+                bValue = true;
+            }
+        }
+        return bValue;
+    }
+
     public static Map<String,String> asStringMap(Map<FormConfiguration, String> input) {
         final Map<String,String> returnObj = new HashMap<>();
         for (final FormConfiguration formConfiguration : input.keySet()) {
@@ -314,4 +344,41 @@ public class FormUtility {
         sb.append(")");
         return sb.toString();
     }
+
+    public static void populateFormMapFromLdap(
+            final List<FormConfiguration> formFields,
+            final SessionLabel sessionLabel,
+            final Map<FormConfiguration, String> formMap,
+            final UserDataReader userDataReader
+    )
+            throws PwmUnrecoverableException
+    {
+        final List<String> formFieldNames = FormConfiguration.convertToListOfNames(formFields);
+        LOGGER.trace(sessionLabel, "preparing to load form data from ldap for fields " + JsonUtil.serializeCollection(formFieldNames));
+        final Map<String,String> userData = new LinkedHashMap<>();
+        try {
+            userData.putAll(userDataReader.readStringAttributes(formFieldNames, true));
+        } catch (Exception e) {
+            PwmError error = null;
+            if (e instanceof ChaiException) {
+                error = PwmError.forChaiError(((ChaiException) e).getErrorCode());
+            }
+            if (error == null || error == PwmError.ERROR_UNKNOWN) {
+                error = PwmError.ERROR_LDAP_DATA_ERROR;
+            }
+
+            final ErrorInformation errorInformation = new ErrorInformation(error,"error reading current profile values: " + e.getMessage());
+            LOGGER.error(sessionLabel,errorInformation.getDetailedErrorMsg());
+            throw new PwmUnrecoverableException(errorInformation);
+        }
+
+        for (final FormConfiguration formItem : formFields) {
+            final String attrName = formItem.getName();
+            if (userData.containsKey(attrName)) {
+                final String value = parseInputValueToFormValue(formItem, userData.get(attrName));
+                formMap.put(formItem, value);
+                LOGGER.trace(sessionLabel, "loaded value for form item '" + attrName + "' with value=" + value);
+            }
+        }
+    }
 }

+ 1 - 1
pwm/servlet/src/password/pwm/config/PwmSetting.java

@@ -1192,7 +1192,7 @@ public enum PwmSetting {
         if (hidden == null) {
             final Element settingElement = PwmSettingXml.readSettingXml(this);
             final Attribute requiredAttribute = settingElement.getAttribute("hidden");
-            hidden = requiredAttribute != null && "true".equalsIgnoreCase(requiredAttribute.getValue());
+            hidden = requiredAttribute != null && "true".equalsIgnoreCase(requiredAttribute.getValue()) || this.getCategory().isHidden();
         }
         return hidden;
     }

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

@@ -2065,9 +2065,9 @@
     </setting>
     <setting key="newUser.form" level="1" required="true">
         <default>
-            <value>{"name":"mail","minimumLength":1,"maximumLength":64,"type":"email","required":true,"confirmationRequired":false,"readonly":false,"unique":true,"labels":{"":"Email Address"},"regexErrors":{"":""},"description":{"":""},"placeholder":"username@example.com","selectOptions":{}}</value>
-            <value>{"name":"givenName","minimumLength":1,"maximumLength":64,"type":"text","required":true,"confirmationRequired":false,"readonly":false,"labels":{"":"First Name"},"regexErrors":{"":""},"description":{"":""},"selectOptions":{}}</value>
-            <value>{"name":"sn","minimumLength":1,"maximumLength":64,"type":"text","required":true,"confirmationRequired":false,"readonly":false,"labels":{"":"Last Name"},"regexErrors":{"":""},"description":{"":""},"selectOptions":{}}</value>
+            <value>{"name":"mail","minimumLength":1,"maximumLength":64,"type":"email","required":true,"confirmationRequired":false,"readonly":false,"unique":true,"labels":{"":"Email Address"},"regexErrors":{"":""},"description":{"":""},"placeholder":"username@example.com","selectOptions":{},regex:"^[a-zA-Z0-9 .,'@]*$"}</value>
+            <value>{"name":"givenName","minimumLength":1,"maximumLength":64,"type":"text","required":true,"confirmationRequired":false,"readonly":false,"labels":{"":"First Name"},"regexErrors":{"":""},"description":{"":""},"selectOptions":{},regex:"^[a-zA-Z0-9 .,'@]*$"}</value>
+            <value>{"name":"sn","minimumLength":1,"maximumLength":64,"type":"text","required":true,"confirmationRequired":false,"readonly":false,"labels":{"":"Last Name"},"regexErrors":{"":""},"description":{"":""},"selectOptions":{},regex:"^[a-zA-Z0-9 .,'@]*$"}</value>
         </default>
         <options>
             <option value="unique">show</option>

+ 12 - 1
pwm/servlet/src/password/pwm/config/PwmSettingCategory.java

@@ -180,7 +180,18 @@ public enum PwmSettingCategory {
         if (hidden == null) {
             final Element settingElement = PwmSettingXml.readCategoryXml(this);
             final Attribute hiddenElement = settingElement.getAttribute("hidden");
-            hidden = hiddenElement != null && "true".equalsIgnoreCase(hiddenElement.getValue());
+            if (hiddenElement != null && "true".equalsIgnoreCase(hiddenElement.getValue())) {
+                hidden = true;
+            } else {
+                for (final PwmSettingCategory parentCategory : getParents()) {
+                    if (parentCategory.isHidden()) {
+                        hidden = true;
+                    }
+                }
+            }
+            if (hidden == null) {
+                hidden = false;
+            }
         }
         return hidden;
     }

+ 12 - 0
pwm/servlet/src/password/pwm/config/PwmSettingXml.java

@@ -28,6 +28,7 @@ import org.jdom2.JDOMException;
 import org.jdom2.input.SAXBuilder;
 import org.jdom2.xpath.XPathExpression;
 import org.jdom2.xpath.XPathFactory;
+import password.pwm.util.Helper;
 
 import javax.xml.XMLConstants;
 import javax.xml.transform.stream.StreamSource;
@@ -56,6 +57,17 @@ public class PwmSettingXml {
             } catch (IOException e) {
                 throw new IllegalStateException("unable to load " + SETTING_XML_FILENAME + ": " + e.getMessage());
             }
+
+            // clear cached dom after 30 seconds.
+            final Thread t = new Thread("PwmSettingXml static cache clear thread") {
+                @Override
+                public void run() {
+                    Helper.pause(30 * 1000);
+                    xmlDocCache = null;
+                }
+            };
+            t.setDaemon(false);
+            t.start();
         }
         return xmlDocCache;
     }

+ 39 - 16
pwm/servlet/src/password/pwm/config/profile/PwmPasswordPolicy.java

@@ -29,6 +29,8 @@ import com.novell.ldapchai.util.PasswordRuleHelper;
 import com.novell.ldapchai.util.StringHelper;
 import password.pwm.config.UserPermission;
 import password.pwm.config.option.ADPolicyComplexity;
+import password.pwm.health.HealthMessage;
+import password.pwm.health.HealthRecord;
 import password.pwm.util.logging.PwmLogger;
 
 import java.io.Serializable;
@@ -58,13 +60,11 @@ public class PwmPasswordPolicy implements Profile,Serializable {
     public static PwmPasswordPolicy createPwmPasswordPolicy(
             final Map<String, String> policyMap,
             final ChaiPasswordPolicy chaiPasswordPolicy
-    )
-    {
+    ) {
         return new PwmPasswordPolicy(policyMap, chaiPasswordPolicy);
     }
 
-    public String getIdentifier()
-    {
+    public String getIdentifier() {
         return profileID;
     }
 
@@ -83,7 +83,7 @@ public class PwmPasswordPolicy implements Profile,Serializable {
             }
             newDefaultPolicy = createPwmPasswordPolicy(defaultPolicyMap, null);
         } catch (Throwable t) {
-            LOGGER.fatal("error initializing PwmPasswordPolicy class: " + t.getMessage(),t);
+            LOGGER.fatal("error initializing PwmPasswordPolicy class: " + t.getMessage(), t);
         }
         defaultPolicy = newDefaultPolicy;
     }
@@ -153,28 +153,23 @@ public class PwmPasswordPolicy implements Profile,Serializable {
         return policyMap.get(rule.getKey());
     }
 
-    public void setProfileID(String profileID)
-    {
+    public void setProfileID(String profileID) {
         this.profileID = profileID;
     }
 
-    public List<UserPermission> getUserPermissions()
-    {
+    public List<UserPermission> getUserPermissions() {
         return userPermissions;
     }
 
-    public void setUserPermissions(List<UserPermission> userPermissions)
-    {
+    public void setUserPermissions(List<UserPermission> userPermissions) {
         this.userPermissions = userPermissions;
     }
 
-    public String getRuleText()
-    {
+    public String getRuleText() {
         return ruleText;
     }
 
-    public void setRuleText(String ruleText)
-    {
+    public void setRuleText(String ruleText) {
         this.ruleText = ruleText;
     }
 
@@ -278,7 +273,6 @@ public class PwmPasswordPolicy implements Profile,Serializable {
     }
 
 
-
 // -------------------------- INNER CLASSES --------------------------
 
     public static class RuleHelper {
@@ -384,4 +378,33 @@ public class PwmPasswordPolicy implements Profile,Serializable {
     public List<UserPermission> getPermissionMatches() {
         throw new UnsupportedOperationException();
     }
+
+    public List<HealthRecord> health(final Locale locale) {
+        final RuleHelper ruleHelper = this.getRuleHelper();
+        final List<HealthRecord> returnList = new ArrayList<>();
+        final Map<PwmPasswordRule, PwmPasswordRule> rulePairs = new LinkedHashMap<>();
+        rulePairs.put(PwmPasswordRule.MinimumLength, PwmPasswordRule.MaximumLength);
+        rulePairs.put(PwmPasswordRule.MinimumLowerCase, PwmPasswordRule.MaximumLowerCase);
+        rulePairs.put(PwmPasswordRule.MinimumUpperCase, PwmPasswordRule.MaximumUpperCase);
+        rulePairs.put(PwmPasswordRule.MinimumNumeric, PwmPasswordRule.MaximumNumeric);
+        rulePairs.put(PwmPasswordRule.MinimumSpecial, PwmPasswordRule.MaximumSpecial);
+        rulePairs.put(PwmPasswordRule.MinimumAlpha, PwmPasswordRule.MaximumAlpha);
+        rulePairs.put(PwmPasswordRule.MinimumNonAlpha, PwmPasswordRule.MaximumNonAlpha);
+        rulePairs.put(PwmPasswordRule.MinimumUnique, PwmPasswordRule.MaximumUnique);
+
+        for (final PwmPasswordRule minRule : rulePairs.keySet()) {
+            final PwmPasswordRule maxRule = rulePairs.get(minRule);
+
+            final int minValue = ruleHelper.readIntValue(minRule);
+            final int maxValue = ruleHelper.readIntValue(maxRule);
+            if (maxValue > 0 && minValue > maxValue) {
+                final String detailMsg = minRule.getLabel(locale, null) + " (" + minValue + ")"
+                        + " > "
+                        + maxRule.getLabel(locale, null) + " (" + maxValue + ")";
+                returnList.add(HealthRecord.forMessage(HealthMessage.Config_PasswordPolicyProblem, profileID, detailMsg));
+            }
+        }
+
+        return Collections.unmodifiableList(returnList);
+    }
 }

+ 62 - 40
pwm/servlet/src/password/pwm/health/ConfigurationChecker.java

@@ -32,6 +32,7 @@ import password.pwm.config.option.DataStorageMethod;
 import password.pwm.config.profile.LdapProfile;
 import password.pwm.config.profile.NewUserProfile;
 import password.pwm.config.profile.Profile;
+import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.i18n.Config;
@@ -45,29 +46,51 @@ import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 
 public class ConfigurationChecker implements HealthChecker {
     private static final PwmLogger LOGGER = PwmLogger.forClass(ConfigurationChecker.class);
 
-    public List<HealthRecord> doHealthCheck(final PwmApplication pwmApplication)
-    {
+    public List<HealthRecord> doHealthCheck(final PwmApplication pwmApplication) {
         if (pwmApplication.getConfig() == null) {
             return Collections.emptyList();
         }
 
         final Configuration config = pwmApplication.getConfig();
-        final List<HealthRecord> records = new ArrayList<>();
 
-        if (config.readSettingAsBoolean(PwmSetting.HIDE_CONFIGURATION_HEALTH_WARNINGS)) {
-            return records;
-        }
+        final List<HealthRecord> records = new ArrayList<>();
 
         if (pwmApplication.getApplicationMode() == PwmApplication.MODE.CONFIGURATION) {
             records.add(HealthRecord.forMessage(HealthMessage.Config_ConfigMode));
         }
 
+        if (config.readSettingAsBoolean(PwmSetting.NEWUSER_ENABLE)) {
+            for (final NewUserProfile newUserProfile : config.getNewUserProfiles().values()) {
+                try {
+                    newUserProfile.getNewUserPasswordPolicy(pwmApplication, PwmConstants.DEFAULT_LOCALE);
+                } catch (PwmUnrecoverableException e) {
+                    records.add(new HealthRecord(HealthStatus.WARN,HealthTopic.Configuration,e.getMessage()));
+                }
+            }
+        }
+
+        records.addAll(doHealthCheck(config, PwmConstants.DEFAULT_LOCALE));
+
+        return records;
+    }
+
+    public List<HealthRecord> doHealthCheck(final Configuration config, final Locale locale)
+    {
+
+
+        final List<HealthRecord> records = new ArrayList<>();
+
+        if (config.readSettingAsBoolean(PwmSetting.HIDE_CONFIGURATION_HEALTH_WARNINGS)) {
+            return records;
+        }
+
         final String siteUrl = config.readSettingAsString(PwmSetting.PWM_SITE_URL);
-        final String SEPARATOR = LocaleHelper.getLocalizedMessage(PwmConstants.DEFAULT_LOCALE, Config.Display_SettingNavigationSeparator, null);
+        final String SEPARATOR = LocaleHelper.getLocalizedMessage(locale, Config.Display_SettingNavigationSeparator, null);
         try {
             if (siteUrl == null || siteUrl.isEmpty() || siteUrl.equals(
                     PwmSetting.PWM_SITE_URL.getDefaultValue(config.getTemplate()).toNativeObject())) {
@@ -102,27 +125,25 @@ public class ConfigurationChecker implements HealthChecker {
             }
         }
 
-        {
-            for (final LdapProfile ldapProfile : config.getLdapProfiles().values()) {
-                final List<String> ldapServerURLs = ldapProfile.readSettingAsStringArray(PwmSetting.LDAP_SERVER_URLS);
-                if (ldapServerURLs != null && !ldapServerURLs.isEmpty()) {
-                    for (final String urlStringValue : ldapServerURLs) {
-                        try {
-                            final URI url = new URI(urlStringValue);
-                            final boolean secure = "ldaps".equalsIgnoreCase(url.getScheme());
-                            if (!secure) {
-                                records.add(HealthRecord.forMessage(
-                                        HealthMessage.Config_LDAPUnsecure,
-                                        settingToOutputText(PwmSetting.LDAP_SERVER_URLS, ldapProfile)
-                                ));
-                            }
-                        } catch (URISyntaxException e) {
-                            records.add(HealthRecord.forMessage(HealthMessage.Config_ParseError,
-                                    e.getMessage(),
-                                    settingToOutputText(PwmSetting.LDAP_SERVER_URLS, ldapProfile),
-                                    urlStringValue
+        for (final LdapProfile ldapProfile : config.getLdapProfiles().values()) {
+            final List<String> ldapServerURLs = ldapProfile.readSettingAsStringArray(PwmSetting.LDAP_SERVER_URLS);
+            if (ldapServerURLs != null && !ldapServerURLs.isEmpty()) {
+                for (final String urlStringValue : ldapServerURLs) {
+                    try {
+                        final URI url = new URI(urlStringValue);
+                        final boolean secure = "ldaps".equalsIgnoreCase(url.getScheme());
+                        if (!secure) {
+                            records.add(HealthRecord.forMessage(
+                                    HealthMessage.Config_LDAPUnsecure,
+                                    settingToOutputText(PwmSetting.LDAP_SERVER_URLS, ldapProfile)
                             ));
                         }
+                    } catch (URISyntaxException e) {
+                        records.add(HealthRecord.forMessage(HealthMessage.Config_ParseError,
+                                e.getMessage(),
+                                settingToOutputText(PwmSetting.LDAP_SERVER_URLS, ldapProfile),
+                                urlStringValue
+                        ));
                     }
                 }
             }
@@ -183,6 +204,15 @@ public class ConfigurationChecker implements HealthChecker {
             }
         }
 
+        for (final String profileID : config.getPasswordProfileIDs()) {
+            try {
+                final PwmPasswordPolicy pwmPasswordPolicy = config.getPasswordPolicy(profileID, locale);
+                records.addAll(pwmPasswordPolicy.health(locale));
+            } catch (Exception e) {
+                LOGGER.error("unexpected error during password policy health check: " + e.getMessage(),e);
+            }
+        }
+
         /*
         if (config.readSettingAsBoolean(PwmSetting.FORGOTTEN_PASSWORD_ENABLE)) {
             if (!config.readSettingAsBoolean(PwmSetting.CHALLENGE_REQUIRE_RESPONSES)) {
@@ -195,18 +225,10 @@ public class ConfigurationChecker implements HealthChecker {
             }
         }
         */
-        
-        
-        if (config.readSettingAsBoolean(PwmSetting.NEWUSER_ENABLE)) {
-            for (final NewUserProfile newUserProfile : config.getNewUserProfiles().values()) {
-                try {
-                    newUserProfile.getNewUserPasswordPolicy(pwmApplication, PwmConstants.DEFAULT_LOCALE);
-                } catch (PwmUnrecoverableException e) {
-                    records.add(new HealthRecord(HealthStatus.WARN,HealthTopic.Configuration,e.getMessage()));
-                }
-            }
-        }
-        
+
+
+
+
 
         if (!config.hasDbConfigured()) {
             if (config.helper().shouldHaveDbConfigured()) {
@@ -233,13 +255,13 @@ public class ConfigurationChecker implements HealthChecker {
         return records;
     }
 
-    public static String settingToOutputText(
+    private static String settingToOutputText(
             final PwmSetting setting
     ) {
         return settingToOutputText(setting,null);
     }
 
-    public static String settingToOutputText(
+    private static String settingToOutputText(
             final PwmSetting setting,
             final Profile profile
     ) {

+ 2 - 0
pwm/servlet/src/password/pwm/health/HealthMessage.java

@@ -58,6 +58,8 @@ public enum HealthMessage {
     Config_MissingDB                        (HealthStatus.CAUTION,  HealthTopic.Configuration),
     Config_MissingLDAPResponseAttr          (HealthStatus.CAUTION,  HealthTopic.Configuration),
     Config_URLNotSecure                     (HealthStatus.CAUTION,  HealthTopic.Configuration),
+    Config_PasswordPolicyProblem            (HealthStatus.CONFIG,   HealthTopic.Configuration),
+    Config_UserPermissionValidity           (HealthStatus.CONFIG,   HealthTopic.Configuration),
     Config_NoRecoveryEnabled                (HealthStatus.CAUTION,  HealthTopic.Configuration),
     LDAP_VendorsNotSame                     (HealthStatus.CONFIG,   HealthTopic.LDAP),
     LDAP_OK                                 (HealthStatus.GOOD,     HealthTopic.LDAP),

+ 131 - 11
pwm/servlet/src/password/pwm/health/LDAPStatusChecker.java

@@ -38,6 +38,8 @@ import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
+import password.pwm.config.PwmSettingSyntax;
+import password.pwm.config.UserPermission;
 import password.pwm.config.profile.LdapProfile;
 import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.error.ErrorInformation;
@@ -112,6 +114,8 @@ public class LDAPStatusChecker implements HealthChecker {
 
         returnRecords.addAll(checkVendorSameness(pwmApplication));
 
+        //returnRecords.addAll(checkUserPermissionValues(pwmApplication));
+
         return returnRecords;
     }
 
@@ -129,8 +133,8 @@ public class LDAPStatusChecker implements HealthChecker {
 
         if (proxyUserDN.equalsIgnoreCase(testUserDN)) {
             returnRecords.add(HealthRecord.forMessage(HealthMessage.LDAP_ProxyTestSameUser,
-                    ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_TEST_USER_DN,ldapProfile),
-                    ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_PROXY_USER_DN,ldapProfile)
+                    PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE),
+                    PwmSetting.LDAP_PROXY_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE)
             ));
             return returnRecords;
         }
@@ -153,13 +157,13 @@ public class LDAPStatusChecker implements HealthChecker {
 
             } catch (ChaiUnavailableException e) {
                 returnRecords.add(HealthRecord.forMessage(HealthMessage.LDAP_TestUserUnavailable,
-                        ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_TEST_USER_DN,ldapProfile),
+                        PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE),
                         e.getMessage()
                 ));
                 return returnRecords;
             } catch (Throwable e) {
                 returnRecords.add(HealthRecord.forMessage(HealthMessage.LDAP_TestUserUnexpected,
-                        ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_TEST_USER_DN,ldapProfile),
+                        PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE),
                         e.getMessage()
                 ));
                 return returnRecords;
@@ -169,7 +173,7 @@ public class LDAPStatusChecker implements HealthChecker {
                 theUser.readObjectClass();
             } catch (ChaiException e) {
                 returnRecords.add(HealthRecord.forMessage(HealthMessage.LDAP_TestUserError,
-                        ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_TEST_USER_DN,ldapProfile),
+                        PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE),
                         e.getMessage()
                 ));
                 return returnRecords;
@@ -199,13 +203,13 @@ public class LDAPStatusChecker implements HealthChecker {
                         userPassword = newPassword;
                     } catch (ChaiPasswordPolicyException e) {
                         returnRecords.add(HealthRecord.forMessage(HealthMessage.LDAP_TestUserPolicyError,
-                                ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_TEST_USER_DN,ldapProfile),
+                                PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE),
                                 e.getMessage()
                         ));
                         return returnRecords;
                     } catch (Exception e) {
                         returnRecords.add(HealthRecord.forMessage(HealthMessage.LDAP_TestUserUnexpected,
-                                ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_TEST_USER_DN,ldapProfile),
+                                PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE),
                                 e.getMessage()
                         ));
                         return returnRecords;
@@ -215,7 +219,7 @@ public class LDAPStatusChecker implements HealthChecker {
 
             if (userPassword == null) {
                 returnRecords.add(HealthRecord.forMessage(HealthMessage.LDAP_TestUserNoTempPass,
-                        ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_TEST_USER_DN,ldapProfile)
+                        PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE)
                 ));
                 return returnRecords;
             }
@@ -395,7 +399,7 @@ public class LDAPStatusChecker implements HealthChecker {
                 returnList.add(HealthRecord.forMessage(
                         HealthMessage.Config_ParseError,
                         e.getMessage(),
-                        ConfigurationChecker.settingToOutputText(PwmSetting.LDAP_SERVER_URLS,ldapProfile),
+                        PwmSetting.LDAP_SERVER_URLS.toMenuLocationDebug(ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE),
                         loopURL
                 ));
             }
@@ -520,7 +524,7 @@ public class LDAPStatusChecker implements HealthChecker {
                         final String url = chaiConfiguration.getSetting(ChaiSetting.BIND_URLS);
                         final HealthRecord record = HealthRecord.forMessage(
                                 HealthMessage.LDAP_Ad_History_Asn_Missing,
-                                ConfigurationChecker.settingToOutputText(PwmSetting.AD_ENFORCE_PW_HISTORY_ON_SET, null),
+                                PwmSetting.AD_ENFORCE_PW_HISTORY_ON_SET.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE),
                                 url
                         );
                         healthRecords.add(record);
@@ -541,6 +545,122 @@ public class LDAPStatusChecker implements HealthChecker {
         return healthRecords;
     }
 
+    private static List<HealthRecord> checkUserPermissionValues(final PwmApplication pwmApplication) {
+        final List<HealthRecord> returnList = new ArrayList<>();
+        final Configuration config= pwmApplication.getConfig();
+        for (final PwmSetting pwmSetting : PwmSetting.values()) {
+            if (!pwmSetting.isHidden() && pwmSetting.getSyntax() == PwmSettingSyntax.USER_PERMISSION) {
+                if (!pwmSetting.getCategory().hasProfiles()) {
+                    final List<UserPermission> userPermissions = config.readSettingAsUserPermission(pwmSetting);
+                    for (final UserPermission userPermission : userPermissions) {
+                        try {
+                            returnList.addAll(checkUserPermission(pwmApplication, userPermission, pwmSetting));
+                        } catch (PwmUnrecoverableException e) {
+                            LOGGER.error(e.getMessage(),e);
+                        }
+                    }
+                }
+            }
+        }
+        return returnList;
+    }
+
+    private static List<HealthRecord> checkUserPermission(
+            final PwmApplication pwmApplication,
+            final UserPermission userPermission,
+            final PwmSetting pwmSetting
+    )
+            throws PwmUnrecoverableException
+    {
+        final String settingDebugName = pwmSetting.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE);
+        final List<HealthRecord> returnList = new ArrayList<>();
+        final Configuration config = pwmApplication.getConfig();
+        final List<String> ldapProfilesToCheck = new ArrayList<>();
+        {
+            final String configuredLdapProfileID = userPermission.getLdapProfileID();
+            if (configuredLdapProfileID == null || configuredLdapProfileID.isEmpty() || configuredLdapProfileID.equals(PwmConstants.PROFILE_ID_ALL)) {
+                ldapProfilesToCheck.addAll(config.getLdapProfiles().keySet());
+            } else {
+                if (config.getLdapProfiles().keySet().contains(configuredLdapProfileID)) {
+                    ldapProfilesToCheck.add(configuredLdapProfileID);
+                } else {
+                    return Collections.singletonList(
+                            HealthRecord.forMessage(HealthMessage.Config_UserPermissionValidity,
+                                    settingDebugName,
+                                    "specified ldap profile ID invalid: " + configuredLdapProfileID
+                            ));
+                }
+            }
+        }
+
+        for (final String ldapProfileID : ldapProfilesToCheck) {
+            switch (userPermission.getType()) {
+                case ldapGroup: {
+                    final String groupDN = userPermission.getLdapBase();
+                    if (groupDN != null && !isExampleDN(groupDN)) {
+                        final String errorMsg = validateDN(pwmApplication, groupDN, ldapProfileID);
+                        if (errorMsg != null) {
+                            returnList.add(HealthRecord.forMessage(HealthMessage.Config_UserPermissionValidity, settingDebugName, "groupDN: " + errorMsg));
+                        }
+                    }
+                }
+                break;
+
+                case ldapQuery: {
+                    final String baseDN = userPermission.getLdapBase();
+                    if (baseDN != null && !isExampleDN(baseDN)) {
+                        final String errorMsg = validateDN(pwmApplication, baseDN, ldapProfileID);
+                        if (errorMsg != null) {
+                            returnList.add(HealthRecord.forMessage(HealthMessage.Config_UserPermissionValidity, settingDebugName, "baseDN: " + errorMsg));
+                        }
+                    }
+                }
+                break;
+            }
+        }
+        return returnList;
+    }
+
+    private static String validateDN(final PwmApplication pwmApplication, final String dnValue, final String ldapProfileID)
+            throws PwmUnrecoverableException
+    {
+        final ChaiProvider chaiProvider = pwmApplication.getProxyChaiProvider(ldapProfileID);
+        try {
+            if (!isExampleDN(dnValue)) {
+                final ChaiEntry baseDNEntry = ChaiFactory.createChaiEntry(dnValue, chaiProvider);
+                if (!baseDNEntry.isValid()) {
+                    return "DN '" + dnValue + "' is invalid";
+                } else {
+                    final String canonicalDN = baseDNEntry.readCanonicalDN();
+                    if (!dnValue.equals(canonicalDN)) {
+                        return "DN '" + dnValue + "' is not the correct canonical value";
+                    }
+                }
+            }
+        } catch (ChaiUnavailableException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
+        } catch (ChaiException e) {
+            LOGGER.error("error while evaluating ldap DN '" + dnValue + "', error: " + e.getMessage());
+        }
+        return null;
+    }
+
+    private static boolean isExampleDN(final String dnValue) {
+        if (dnValue == null) {
+            return false;
+        }
+        final String[] EXAMPLE_SUFFIXES = new String[]{
+                "DC=site,DC=example,DC=net",
+                "ou=groups,o=example"
+        };
+        for (final String suffix : EXAMPLE_SUFFIXES) {
+            if (dnValue.endsWith(suffix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public static HealthData healthForNewConfiguration(
             final PwmApplication pwmApplication,
             final Configuration config,
@@ -550,7 +670,7 @@ public class LDAPStatusChecker implements HealthChecker {
             boolean fullTest
 
     )
-                throws PwmUnrecoverableException
+            throws PwmUnrecoverableException
     {
         final PwmApplication tempApplication = new PwmApplication.PwmEnvironment(config, pwmApplication.getApplicationPath())
                 .setApplicationMode(PwmApplication.MODE.NEW)

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

@@ -116,8 +116,8 @@ public abstract class PwmHttpRequestWrapper {
                     final boolean passwordType = key.toLowerCase().contains("password");
                     String value;
                     value = bypassInputValidation 
-                            ? inputMap.get(key) : 
-                            Validator.sanitizeInputValue(configuration, inputMap.get(key), maxLength);
+                            ? inputMap.get(key)
+                            : Validator.sanitizeInputValue(configuration, inputMap.get(key), maxLength);
                     value = passwordType && passwordTrim ? value.trim() : value;
                     value = !passwordType && trim ? value.trim() : value;
                     
@@ -170,7 +170,7 @@ public abstract class PwmHttpRequestWrapper {
             throws PwmUnrecoverableException 
     {
         final int maxLength = Integer.parseInt(configuration.readAppProperty(AppProperty.HTTP_PARAM_MAX_READ_LENGTH));
-        final boolean trim = Boolean.parseBoolean(configuration.readAppProperty(AppProperty.SECURITY_INPUT_TRIM));
+        final boolean trim = Boolean.parseBoolean(configuration.readAppProperty(AppProperty.SECURITY_INPUT_PASSWORD_TRIM));
         
         final String rawValue = httpServletRequest.getParameter(name);
         if (rawValue != null && !rawValue.isEmpty()) {

+ 10 - 1
pwm/servlet/src/password/pwm/http/PwmResponse.java

@@ -126,7 +126,16 @@ public class PwmResponse extends PwmHttpResponseWrapper {
         sendRedirect(loginServletURL);
     }
 
-    public void writeCookie(final String cookieName, final Serializable cookieValue, final int seconds, final boolean httpOnly, final String path)
+
+    public void writeEncryptedCookie(final String cookieName, final Serializable cookieValue, final String path)
+            throws PwmUnrecoverableException
+    {
+        final String jsonValue = JsonUtil.serialize(cookieValue);
+        final String encryptedValue = SecureHelper.encryptToString(jsonValue, pwmRequest.getConfig().getSecurityKey(), true);
+        pwmRequest.getPwmResponse().writeCookie(cookieName, encryptedValue, -1, true, path);
+    }
+
+    public void writeEncryptedCookie(final String cookieName, final Serializable cookieValue, final int seconds, final boolean httpOnly, final String path)
             throws PwmUnrecoverableException
     {
         final String jsonValue = JsonUtil.serialize(cookieValue);

+ 12 - 0
pwm/servlet/src/password/pwm/http/SessionManager.java

@@ -134,6 +134,10 @@ public class SessionManager implements Serializable {
                 LOGGER.error(pwmSession.getLabel(), "error while closing user connection: " + e.getMessage());
             }
         }
+
+        if (userDataReader != null) {
+            userDataReader = null;
+        }
     }
 
 // -------------------------- OTHER METHODS --------------------------
@@ -184,8 +188,16 @@ public class SessionManager implements Serializable {
         }
 
         if (userDataReader == null) {
+            /*
             userDataReader = LdapUserDataReader.appProxiedReader(pwmApplication,
                     pwmSession.getUserInfoBean().getUserIdentity());
+                    */
+            final UserIdentity userIdentity = pwmSession.getUserInfoBean().getUserIdentity();
+            try {
+                userDataReader = LdapUserDataReader.selfProxiedReader(pwmApplication, pwmSession, userIdentity);
+            } catch (ChaiUnavailableException e) {
+                throw PwmUnrecoverableException.fromChaiException(e);
+            }
         }
         return userDataReader;
     }

+ 41 - 30
pwm/servlet/src/password/pwm/http/filter/AuthenticationFilter.java

@@ -185,7 +185,7 @@ public class AuthenticationFilter extends AbstractPwmFilter {
         final AuthRecord authRecord = new AuthRecord(authTime, userGuid);
 
         try {
-            pwmRequest.getPwmResponse().writeCookie(cookieName, authRecord, cookieAgeSeconds, true, pwmRequest.getContextPath());
+            pwmRequest.getPwmResponse().writeEncryptedCookie(cookieName, authRecord, cookieAgeSeconds, true, pwmRequest.getContextPath());
             LOGGER.debug(pwmRequest,"wrote auth record cookie to user browser for use during forgotten password");
         } catch (PwmUnrecoverableException e) {
             LOGGER.error(pwmRequest, "error while setting authentication record cookie: " + e.getMessage());
@@ -220,42 +220,53 @@ public class AuthenticationFilter extends AbstractPwmFilter {
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final HttpServletRequest req = pwmRequest.getHttpServletRequest();
 
-        //try to authenticate user with basic auth
-        if (!pwmSession.getSessionStateBean().isAuthenticated()) {
-            final BasicAuthInfo authInfo = BasicAuthInfo.parseAuthHeader(pwmApplication, pwmRequest);
-            if (authInfo != null) {
-                try {
-                    authUserUsingBasicHeader(pwmRequest, authInfo);
-                } catch (ChaiUnavailableException e) {
-                    StatisticsManager.incrementStat(pwmRequest, Statistic.LDAP_UNAVAILABLE_COUNT);
-                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE,e.getMessage());
-                    pwmRequest.respondWithError(errorInformation);
-                    return;
-                } catch (PwmException e) {
-                    pwmRequest.respondWithError(e.getErrorInformation());
-                    return;
-                }
+        boolean bypassSSOAuth = false;
+        {
+            final String ssoBypassParamName = pwmRequest.getConfig().readAppProperty(AppProperty.HTTP_PARAM_NAME_SSO_ENABLE);
+            if (pwmRequest.hasParameter(ssoBypassParamName) && !pwmRequest.readParameterAsBoolean(ssoBypassParamName)) {
+                bypassSSOAuth = true;
+                LOGGER.trace(pwmRequest, "bypassing sso authentication due to parameter " + pwmRequest.getConfig().readAppProperty(AppProperty.HTTP_PARAM_NAME_SSO_ENABLE) + "=true");
             }
         }
 
-        // attempt sso header authentication
-        if (!pwmSession.getSessionStateBean().isAuthenticated()) {
-            if (processAuthHeader(pwmRequest)) {
-                return;
+        if (!bypassSSOAuth) {
+            //try to authenticate user with basic auth
+            if (!pwmSession.getSessionStateBean().isAuthenticated()) {
+                final BasicAuthInfo authInfo = BasicAuthInfo.parseAuthHeader(pwmApplication, pwmRequest);
+                if (authInfo != null) {
+                    try {
+                        authUserUsingBasicHeader(pwmRequest, authInfo);
+                    } catch (ChaiUnavailableException e) {
+                        StatisticsManager.incrementStat(pwmRequest, Statistic.LDAP_UNAVAILABLE_COUNT);
+                        final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, e.getMessage());
+                        pwmRequest.respondWithError(errorInformation);
+                        return;
+                    } catch (PwmException e) {
+                        pwmRequest.respondWithError(e.getErrorInformation());
+                        return;
+                    }
+                }
             }
-        }
 
-        // try to authenticate user with CAS
-        if (!pwmSession.getSessionStateBean().isAuthenticated()) {
-            if (processCASAuthentication(pwmRequest)) {
-                return;
+            // attempt sso header authentication
+            if (!pwmSession.getSessionStateBean().isAuthenticated()) {
+                if (processAuthHeader(pwmRequest)) {
+                    return;
+                }
             }
-        }
 
-        // process OAuth
-        if (!pwmSession.getSessionStateBean().isAuthenticated()) {
-            if (processOAuthAuthenticationRequest(pwmRequest)) {
-                return;
+            // try to authenticate user with CAS
+            if (!pwmSession.getSessionStateBean().isAuthenticated()) {
+                if (processCASAuthentication(pwmRequest)) {
+                    return;
+                }
+            }
+
+            // process OAuth
+            if (!pwmSession.getSessionStateBean().isAuthenticated()) {
+                if (processOAuthAuthenticationRequest(pwmRequest)) {
+                    return;
+                }
             }
         }
 

+ 17 - 0
pwm/servlet/src/password/pwm/http/servlet/ConfigEditorServlet.java

@@ -539,6 +539,23 @@ public class ConfigEditorServlet extends PwmServlet {
         returnObj.put("html", configManagerBean.getStoredConfiguration().changeLogAsDebugString(locale, true));
         returnObj.put("modified", configManagerBean.getStoredConfiguration().isModified());
 
+        /*
+        try {
+            final ConfigurationChecker configurationChecker = new ConfigurationChecker();
+            final Configuration config = new Configuration(configManagerBean.getConfiguration());
+            final List<HealthRecord> healthRecords = configurationChecker.doHealthCheck(
+                    config,
+                    pwmRequest.getLocale()
+            );
+            final List<password.pwm.ws.server.rest.bean.HealthRecord> healthRecordBeans = password.pwm.ws.server.rest.bean.HealthRecord.fromHealthRecords(healthRecords, locale,
+                    config);
+
+            returnObj.put("health", healthRecordBeans);
+        } catch (Exception e) {
+            LOGGER.error(pwmRequest, "error generating health records: " + e.getMessage());
+        }
+        */
+
         final RestResultBean restResultBean = new RestResultBean();
         restResultBean.setData(returnObj);
         pwmRequest.outputJsonResult(restResultBean);

+ 4 - 2
pwm/servlet/src/password/pwm/http/servlet/ConfigGuideServlet.java

@@ -44,6 +44,7 @@ import password.pwm.http.PwmSession;
 import password.pwm.http.bean.ConfigGuideBean;
 import password.pwm.ldap.schema.SchemaManager;
 import password.pwm.ldap.schema.SchemaOperationResult;
+import password.pwm.util.Helper;
 import password.pwm.util.PasswordData;
 import password.pwm.util.ServletHelper;
 import password.pwm.util.X509Utils;
@@ -194,7 +195,7 @@ public class ConfigGuideServlet extends PwmServlet {
             pwmSession.getSessionStateBean().setTheme(null);
         }
 
-        final ConfigGuideBean configGuideBean = (ConfigGuideBean)pwmSession.getSessionBean(ConfigGuideBean.class);
+        final ConfigGuideBean configGuideBean = pwmSession.getSessionBean(ConfigGuideBean.class);
 
         if (pwmApplication.getApplicationMode() != PwmApplication.MODE.NEW) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"ConfigGuide unavailable unless in NEW mode");
@@ -205,7 +206,8 @@ public class ConfigGuideServlet extends PwmServlet {
 
         if (!configGuideBean.getFormData().containsKey(PARAM_APP_SITEURL)) {
             final URI uri = URI.create(pwmRequest.getHttpServletRequest().getRequestURL().toString());
-            final String newUri = uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + pwmRequest.getContextPath();
+            final int port = Helper.portForUriSchema(uri);
+            final String newUri = uri.getScheme() + "://" + uri.getHost() + ":" + port + pwmRequest.getContextPath();
             configGuideBean.getFormData().put(PARAM_APP_SITEURL,newUri);
         }
 

+ 2 - 4
pwm/servlet/src/password/pwm/http/servlet/ConfigManagerServlet.java

@@ -67,8 +67,6 @@ import java.util.zip.ZipOutputStream;
 public class ConfigManagerServlet extends PwmServlet {
     final static private PwmLogger LOGGER = PwmLogger.forClass(ConfigManagerServlet.class);
 
-    private static final String CONFIGMANAGER_INTRUDER_USERNAME = "ConfigurationManagerLogin";
-
     public enum ConfigManagerAction implements ProcessAction {
         lockConfiguration(HttpMethod.POST),
         startEditing(HttpMethod.POST),
@@ -343,7 +341,7 @@ public class ConfigManagerServlet extends PwmServlet {
                 } else{
                     LOGGER.trace(pwmRequest, "configuration password is not correct");
                     pwmApplication.getIntruderManager().convenience().markAddressAndSession(pwmSession);
-                    pwmApplication.getIntruderManager().mark(RecordType.USERNAME, CONFIGMANAGER_INTRUDER_USERNAME, pwmSession.getLabel());
+                    pwmApplication.getIntruderManager().mark(RecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME, pwmSession.getLabel());
                     final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_WRONGPASSWORD);
                     pwmRequest.setResponseError(errorInformation);
                 }
@@ -354,7 +352,7 @@ public class ConfigManagerServlet extends PwmServlet {
         if ((persistentLoginAccepted || passwordAccepted)) {
             configManagerBean.setPasswordVerified(true);
             pwmApplication.getIntruderManager().convenience().clearAddressAndSession(pwmSession);
-            pwmApplication.getIntruderManager().clear(RecordType.USERNAME,CONFIGMANAGER_INTRUDER_USERNAME);
+            pwmApplication.getIntruderManager().clear(RecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME);
             if (persistentLoginEnabled && !persistentLoginAccepted && "on".equals(pwmRequest.readParameterAsString("remember"))) {
                 if (persistentSeconds > 0) {
                     final Date expirationDate = new Date(System.currentTimeMillis() + (persistentSeconds * 1000));

+ 27 - 0
pwm/servlet/src/password/pwm/http/servlet/ForgottenPasswordServlet.java

@@ -1470,6 +1470,33 @@ public class ForgottenPasswordServlet extends PwmServlet {
         pwmRequest.addFormInfoToRequestAttr(PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM,false,false);
         pwmRequest.forwardToJsp(PwmConstants.JSP_URL.RECOVER_PASSWORD_SEARCH);
     }
+
+    static class Na implements RecoveryVerificationMethod {
+        @Override
+        public List<UserPrompt> getCurrentPrompts() throws PwmUnrecoverableException {
+            return null;
+        }
+
+        @Override
+        public String getCurrentDisplayInstructions() {
+            return null;
+        }
+
+        @Override
+        public ErrorInformation respondToPrompts(Map<String, String> answers) throws PwmUnrecoverableException {
+            return null;
+        }
+
+        @Override
+        public VerificationState getVerificationState() {
+            return null;
+        }
+
+        @Override
+        public void init(PwmApplication pwmApplication, UserInfoBean userInfoBean, SessionLabel sessionLabel, Locale locale) throws PwmUnrecoverableException {
+
+        }
+    }
 }
 
 

+ 7 - 2
pwm/servlet/src/password/pwm/http/servlet/NewUserServlet.java

@@ -246,7 +246,7 @@ public class NewUserServlet extends PwmServlet {
 
         if (newUserProfile.readSettingAsBoolean(PwmSetting.NEWUSER_SMS_VERIFICATION)) {
             if (!newUserBean.isSmsTokenIssued()) {
-                initializeToken(pwmRequest, NewUserBean.NewUserVerificationPhase.EMAIL);
+                initializeToken(pwmRequest, NewUserBean.NewUserVerificationPhase.SMS);
             }
 
             if (!newUserBean.isSmsTokenPassed()) {
@@ -508,7 +508,12 @@ public class NewUserServlet extends PwmServlet {
 
         // set up the user creation attributes
         final Map<String, String> createAttributes = new LinkedHashMap<>();
-        createAttributes.putAll(FormUtility.asStringMap(newUserForm.getFormData()));
+        for (final FormConfiguration formConfiguration : newUserForm.getFormData().keySet()) {
+            final String value = newUserForm.getFormData().get(formConfiguration);
+            if (value != null && !value.isEmpty()) {
+                createAttributes.put(formConfiguration.getName(), value);
+            }
+        }
 
         // read the creation object classes from configuration
         final Set<String> createObjectClasses = new LinkedHashSet<>(

+ 1 - 1
pwm/servlet/src/password/pwm/http/servlet/OAuthConsumerServlet.java

@@ -185,7 +185,7 @@ public class OAuthConsumerServlet extends PwmServlet {
             try {
                 final UserSearchEngine userSearchEngine = new UserSearchEngine(pwmRequest);
                 final UserIdentity resolvedIdentity = userSearchEngine.resolveUsername(oauthSuppliedUsername, null, null);
-                if (resolvedIdentity != null && resolvedIdentity.equals(pwmSession.getUserInfoBean().getUserIdentity())) {
+                if (resolvedIdentity != null && resolvedIdentity.canonicalEquals(pwmSession.getUserInfoBean().getUserIdentity(),pwmApplication)) {
                     LOGGER.debug(pwmSession, "verified incoming oauth code for already authenticated session does resolve to same as logged in user");
                 } else {
                     final String errorMsg = "incoming oauth code for already authenticated session does not resolve to same as logged in user ";

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

@@ -473,10 +473,9 @@ public class SetupOtpServlet extends PwmServlet {
             throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg));
         }
 
-        //pwmRequest.getPwmResponse().setContentType(PwmConstants.ContentTypeValue.png);
-
         OutputStream outputStream = null;
         try {
+            pwmRequest.getPwmResponse().setContentType(PwmConstants.ContentTypeValue.png);
             outputStream = pwmRequest.getPwmResponse().getOutputStream();
             outputStream.write(imageBytes);
             outputStream.flush();

+ 11 - 40
pwm/servlet/src/password/pwm/http/servlet/UpdateProfileServlet.java

@@ -24,13 +24,11 @@ package password.pwm.http.servlet;
 
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiException;
-import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.Permission;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
-import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.config.*;
@@ -42,7 +40,6 @@ import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.UpdateProfileBean;
 import password.pwm.i18n.Message;
-import password.pwm.ldap.UserDataReader;
 import password.pwm.ldap.UserStatusReader;
 import password.pwm.util.Helper;
 import password.pwm.util.logging.PwmLogger;
@@ -206,7 +203,7 @@ public class UpdateProfileServlet extends PwmServlet {
         if (!updateProfileBean.isFormSubmitted()) {
             final Map<FormConfiguration,String> formMap = updateProfileBean.getFormData();
             final List<FormConfiguration> formFields = pwmApplication.getConfig().readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
-            populateFormFromLdap(formFields, pwmRequest.getSessionLabel(), formMap, pwmSession.getSessionManager().getUserDataReader(pwmApplication));
+            FormUtility.populateFormMapFromLdap(formFields, pwmRequest.getSessionLabel(), formMap, pwmSession.getSessionManager().getUserDataReader(pwmApplication));
             forwardToForm(pwmRequest);
             return;
         }
@@ -272,32 +269,6 @@ public class UpdateProfileServlet extends PwmServlet {
         }
     }
 
-    public static void populateFormFromLdap(
-            final List<FormConfiguration> formFields,
-            final SessionLabel sessionLabel,
-            final Map<FormConfiguration, String> formMap,
-            final UserDataReader userDataReader
-    )
-            throws PwmUnrecoverableException, ChaiUnavailableException
-    {
-        LOGGER.trace(sessionLabel, "loading existing user profile data from ldap");
-        final Map<String,String> userData = new LinkedHashMap<>();
-        try {
-            userData.putAll(userDataReader.readStringAttributes(FormConfiguration.convertToListOfNames(formFields), true));
-        } catch (ChaiOperationException e) {
-            LOGGER.error(sessionLabel, "unexpected error reading profile data attributes: " + e.getMessage());
-        }
-
-        for (final FormConfiguration formItem : formFields) {
-            final String attrName = formItem.getName();
-            if (!formMap.containsKey(attrName)) {
-                if (userData.containsKey(attrName)) {
-                    formMap.put(formItem, userData.get(attrName));
-                }
-            }
-        }
-    }
-
 
     private void readFormParametersFromRequest(
             final PwmRequest pwmRequest,
@@ -423,13 +394,13 @@ public class UpdateProfileServlet extends PwmServlet {
         FormUtility.validateFormValues(pwmRequest.getConfig(), formValues, userLocale);
 
         // check unique fields against ldap
-            FormUtility.validateFormValueUniqueness(
-                    pwmRequest.getPwmApplication(),
-                    formValues,
-                    userLocale,
-                    Collections.singletonList(pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity()),
-                    allowResultCaching
-            );
+        FormUtility.validateFormValueUniqueness(
+                pwmRequest.getPwmApplication(),
+                formValues,
+                userLocale,
+                Collections.singletonList(pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity()),
+                allowResultCaching
+        );
     }
 
     private static void sendProfileUpdateEmailNotice(
@@ -452,9 +423,9 @@ public class UpdateProfileServlet extends PwmServlet {
                 pwmSession.getSessionManager().getMacroMachine(pwmApplication)
         );
     }
-    
-    protected void forwardToForm(final PwmRequest pwmRequest) 
-            throws ServletException, PwmUnrecoverableException, IOException 
+
+    protected void forwardToForm(final PwmRequest pwmRequest)
+            throws ServletException, PwmUnrecoverableException, IOException
     {
         final List<FormConfiguration> form = pwmRequest.getConfig().readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
         final Map<FormConfiguration, String> formData = pwmRequest.getPwmSession().getUpdateProfileBean().getFormData();

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

@@ -35,10 +35,7 @@ import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.SessionStateBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserInfoBean;
-import password.pwm.config.ActionConfiguration;
-import password.pwm.config.Configuration;
-import password.pwm.config.FormConfiguration;
-import password.pwm.config.PwmSetting;
+import password.pwm.config.*;
 import password.pwm.config.option.HelpdeskClearResponseMode;
 import password.pwm.config.option.HelpdeskUIMode;
 import password.pwm.config.option.MessageSendMethod;
@@ -50,7 +47,6 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmSession;
 import password.pwm.http.servlet.PwmServlet;
-import password.pwm.http.servlet.UpdateProfileServlet;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.LocaleHelper;
 import password.pwm.i18n.Message;
@@ -368,7 +364,7 @@ public class HelpdeskServlet extends PwmServlet {
             return;
         }
 
-        final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getConfig());
+        final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getConfig()).canonicalized(pwmRequest.getPwmApplication());
         processDetailRequest(pwmRequest, helpdeskProfile, userIdentity);
         final HelpdeskAuditRecord auditRecord = pwmRequest.getPwmApplication().getAuditManager().createHelpdeskAuditRecord(
                 AuditEvent.HELPDESK_VIEW_DETAIL,
@@ -390,13 +386,16 @@ public class HelpdeskServlet extends PwmServlet {
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
-        if (pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity().equals(userIdentity)) {
+        final UserIdentity actorUserIdentity = pwmRequest.getUserInfoIfLoggedIn().canonicalized(pwmRequest.getPwmApplication());
+
+        if (actorUserIdentity.canonicalEquals(userIdentity, pwmRequest.getPwmApplication())) {
             final String errorMsg = "cannot select self";
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,errorMsg);
             LOGGER.debug(pwmRequest, errorInformation);
             pwmRequest.respondWithError(errorInformation, false);
             return;
         }
+        LOGGER.trace(pwmRequest, "helpdesk detail view request for user details of " + userIdentity.toString() + " by actor " + actorUserIdentity.toString());
 
         final HelpdeskDetailInfoBean helpdeskDetailInfoBean = makeHelpdeskDetailInfo(pwmRequest, helpdeskProfile, userIdentity);
         pwmRequest.setAttribute(PwmConstants.REQUEST_ATTR.HelpdeskDetail, helpdeskDetailInfoBean);
@@ -541,7 +540,7 @@ public class HelpdeskServlet extends PwmServlet {
             for (final FormConfiguration formConfiguration : detailFormConfig) {
                 formData.put(formConfiguration,"");
             }
-            UpdateProfileServlet.populateFormFromLdap(detailFormConfig, pwmRequest.getPwmSession().getLabel(), formData, userDataReader);
+            FormUtility.populateFormMapFromLdap(detailFormConfig, pwmRequest.getPwmSession().getLabel(), formData, userDataReader);
             detailInfo.setSearchDetails(formData);
         }
 

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

@@ -678,7 +678,7 @@ Setting_Label_events.pwmDB.maxEvents=Maximum LocalDB Events
 Setting_Label_events.user.storageMethod=User History Storage Location
 Setting_Label_expireCheckDuringAuth=Check Expire During Authentication
 Setting_Label_expirePreTime=Password Pre-Expire Time
-Setting_Label_expireWarnTime=Password ExpireWarn Time
+Setting_Label_expireWarnTime=Password Expire Warn Time
 Setting_Label_external.destToken.urls=External Token Destination Server URLs
 Setting_Label_external.macros.urls=External Macro REST Server URLs
 Setting_Label_external.pwcheck.urls=External Password Check REST Server URLs

+ 3 - 1
pwm/servlet/src/password/pwm/i18n/Health.properties

@@ -51,6 +51,8 @@ HealthMessage_Config_URLNotSecure=URL is not secure (https) for setting %1%
 HealthMessage_Config_NoRecoveryEnabled=No forgotten password recovery options are enabled
 HealthMessage_Config_MissingProxyDN=Missing proxy user DN for profile %1%
 HealthMessage_Config_MissingProxyPassword=Missing proxy user password for profile %1%
+HealthMessage_Config_PasswordPolicyProblem=Password policy %1% configuration anomaly: %2%
+HealthMessage_Config_UserPermissionValidity=User Permission configuration for setting %1% issue: %2%
 HealthMessage_LDAP_VendorsNotSame=LDAP directories of different vendor types are in use.  This configuration may cause undesirable side effects and is not supported.  %1%
 HealthMessage_LDAP_Ad_History_Asn_Missing=%1% is enabled, but the server at %2% does not support this feature.  Check to be sure it is upgraded to Windows Server 2008 R2 SP1 or greater.  Password changes against this server may fail until this is resolved.
 HealthMessage_LDAP_RecentlyUnreachable=LDAP profile %1% was recently unavailable (%2% ago at %3%): %4%
@@ -66,7 +68,7 @@ HealthMessage_LocalDB_NEW=LocalDB status is NEW (loading) state, until LocalDB l
 HealthMessage_LocalDB_CLOSED=LocalDB is CLOSED, statistics, online logging, wordlists and other features are disabled.  Check logs to troubleshoot
 HealthMessage_ServiceClosed_LocalDBUnavail=unable to start %1% service, LocalDB is not available
 HealthMessage_ServiceClosed_AppReadOnly=unable to start %1% service, application is in read-only mode
-HealthMessage_SMS_SendFailure=Unable to send sms due to error: %1%
+Message_SMS_SendFailure=Unable to send sms due to error: %1%
 HealthStatus_WARN=WARN
 HealthStatus_CAUTION=CAUTION
 HealthStatus_CONFIG=CONFIGURATION

+ 8 - 0
pwm/servlet/src/password/pwm/i18n/LocaleHelper.java

@@ -260,6 +260,14 @@ public class LocaleHelper {
         return locale.toLanguageTag();
     }
 
+    public static String booleanString(final boolean input, PwmRequest pwmRequest) {
+        final Display key = input ? Display.Value_True : Display.Value_False;
+
+        return pwmRequest == null
+                ? Display.getLocalizedMessage(null, key, null)
+                : Display.getLocalizedMessage(pwmRequest.getLocale(), key, pwmRequest.getConfig());
+    }
+
     public static String booleanString(final boolean input, Locale locale, Configuration configuration) {
         Display key = input ? Display.Value_True : Display.Value_False;
         return Display.getLocalizedMessage(locale, key, configuration);

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

@@ -154,7 +154,7 @@ Success_NewUserForm=\u53ef\u958b\u59cb\u5efa\u7acb\u60a8\u7684\u5e33\u6236\u3002
 Success_PasswordChange=\u5df2\u6210\u529f\u8b8a\u66f4\u60a8\u7684\u5bc6\u78bc\u3002
 Success_PasswordReset=\u5df2\u6210\u529f\u8a2d\u5b9a %1% \u7684\u5bc6\u78bc\u3002
 Success_PasswordSend=\u5df2\u5c07\u60a8\u7684\u65b0\u5bc6\u78bc\u50b3\u9001\u81f3 %1%\u3002\u8acb\u95dc\u9589\u6b64\u8996\u7a97\uff0c\u7136\u5f8c\u4f7f\u7528\u60a8\u7684\u65b0\u5bc6\u78bc\u767b\u5165\u3002
-Success_ResponsesMeetRules=\u60a8\u7684\u56de\u61c9\u7b26\u5408\u8981\u6c42\u3002\u6e96\u5099\u5c31\u7dd2\u6642\u8acb\u6309\u4e00\u4e0b\u300c\u5132\u5b58\u56de\u61c9\u300d\u3002
+Success_ResponsesMeetRules=\u60a8\u7684\u56de\u61c9\u7b26\u5408\u8981\u6c42\u3002\u6e96\u5099\u5c31\u7dd2\u6642\u8acb\u6309\u4e00\u4e0b\u300c\u5132\u5b58\u7b54\u6848\u300d\u3002
 Success_SetupResponse=\u611f\u8b1d\u60a8\u3002\u5df2\u6210\u529f\u5132\u5b58\u60a8\u7684\u6a5f\u5bc6\u554f\u984c\u548c\u7b54\u6848\u3002\u5982\u679c\u5fd8\u8a18\u5bc6\u78bc\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u9019\u4e9b\u554f\u984c\u7684\u7b54\u6848\u91cd\u8a2d\u5bc6\u78bc\u3002
 Success_Unknown=\u5df2\u6210\u529f\u5b8c\u6210\u64cd\u4f5c\u3002
 Success_UnlockAccount=\u5df2\u89e3\u9664\u9396\u5b9a\u60a8\u7684\u5e33\u6236\u3002

+ 0 - 4
pwm/servlet/src/password/pwm/ldap/LdapOperationsHelper.java

@@ -393,10 +393,6 @@ public class LdapOperationsHelper {
             chaiConfig.setSetting(ChaiSetting.AD_SET_POLICY_HINTS_ON_PW_SET,"true");
         }
 
-        // enable caching
-        //chaiConfig.setSetting(ChaiSetting.CACHE_ENABLE, "true");
-        //chaiConfig.setSetting(ChaiSetting.CACHE_MAXIMUM_SIZE, "5000");
-
         // write out any configured values;
         final String rawValue = config.readAppProperty(AppProperty.LDAP_CHAI_SETTINGS);
         final String[] rawValues = rawValue != null ? rawValue.split(AppProperty.VALUE_SEPARATOR) : new String[0];

+ 3 - 8
pwm/servlet/src/password/pwm/ldap/UserStatusReader.java

@@ -133,8 +133,8 @@ public class UserStatusReader {
             if (ldapPasswordExpirationTime != null) {
                 TimeDuration expirationInterval = TimeDuration.fromCurrent(ldapPasswordExpirationTime);
                 LOGGER.trace(sessionLabel, "read password expiration time: "
-                        + PwmConstants.DEFAULT_DATETIME_FORMAT.format(ldapPasswordExpirationTime)
-                        + ", " + expirationInterval.asCompactString() + " from now"
+                                + PwmConstants.DEFAULT_DATETIME_FORMAT.format(ldapPasswordExpirationTime)
+                                + ", " + expirationInterval.asCompactString() + " from now"
                 );
                 final long diff = ldapPasswordExpirationTime.getTime() - System.currentTimeMillis();
 
@@ -259,12 +259,7 @@ public class UserStatusReader {
         final ChaiUser theUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), provider);
         final UserDataReader userDataReader = new LdapUserDataReader(userIdentity, theUser);
 
-        try {
-            uiBean.setUserIdentity(new UserIdentity(theUser.readCanonicalDN(),userIdentity.getLdapProfileID()));
-        } catch (ChaiOperationException e) {
-            LOGGER.warn(sessionLabel, "error reading canonical DN: " + e.getMessage());
-            uiBean.setUserIdentity(userIdentity);
-        }
+        uiBean.setUserIdentity(userIdentity.canonicalized(pwmApplication));
 
         populateLocaleSpecificUserInfoBean(uiBean, userLocale);
 

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

@@ -540,7 +540,7 @@ public class TokenService implements PwmService {
             final String tokenName,
             final String userEnteredCode
     )
-            throws PwmOperationalException
+            throws PwmOperationalException, PwmUnrecoverableException
     {
         TokenPayload tokenPayload;
         try {
@@ -566,7 +566,7 @@ public class TokenService implements PwmService {
 
         // check current session identity
         if (tokenPayload.getUserIdentity() != null && sessionUserIdentity != null) {
-            if (!tokenPayload.getUserIdentity().equals(sessionUserIdentity)) {
+            if (!tokenPayload.getUserIdentity().canonicalEquals(sessionUserIdentity, pwmApplication)) {
                 final String errorMsg = "user in session '" + sessionUserIdentity + "' entered code for user '" + tokenPayload.getUserIdentity()+ "', counting as invalid attempt";
                 throw new PwmOperationalException(PwmError.ERROR_TOKEN_INCORRECT,errorMsg);
             }

+ 4 - 2
pwm/servlet/src/password/pwm/util/AlertHandler.java

@@ -28,9 +28,11 @@ import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.config.PwmSetting;
+import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthRecord;
 import password.pwm.i18n.Display;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.report.ReportSummaryData;
 
 import java.util.*;
@@ -42,7 +44,7 @@ public abstract class AlertHandler {
     public static void alertDailyStats(
             final PwmApplication pwmApplication,
             final Map<String, String> dailyStatistics
-    ) {
+    ) throws PwmUnrecoverableException {
         if (!checkIfEnabled(pwmApplication, PwmSetting.EVENTS_ALERT_DAILY_SUMMARY)) {
             return;
         }
@@ -57,7 +59,7 @@ public abstract class AlertHandler {
             makeEmailBody(pwmApplication, dailyStatistics, locale, textBody, htmlBody);
             final EmailItemBean emailItem = new EmailItemBean(toAddress, fromAddress, subject, textBody.toString(), htmlBody.toString());
             LOGGER.debug("sending daily summary email to " + toAddress);
-            pwmApplication.getEmailQueue().submitEmail(emailItem, null, null);
+            pwmApplication.getEmailQueue().submitEmail(emailItem, null, MacroMachine.forNonUserSpecific(pwmApplication,null));
         }
     }
 

+ 23 - 2
pwm/servlet/src/password/pwm/util/Helper.java

@@ -37,7 +37,6 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.health.ConfigurationChecker;
 import password.pwm.http.ContextManager;
 import password.pwm.http.PwmSession;
 import password.pwm.i18n.Display;
@@ -502,7 +501,7 @@ public class
             }
         }
 
-        final String errorMsg = testURI + " is not a match for any configured redirect whitelist, see setting: " + ConfigurationChecker.settingToOutputText(PwmSetting.SECURITY_REDIRECT_WHITELIST);
+        final String errorMsg = testURI + " is not a match for any configured redirect whitelist, see setting: " + PwmSetting.SECURITY_REDIRECT_WHITELIST.toMenuLocationDebug(null,PwmConstants.DEFAULT_LOCALE);
         throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_REDIRECT_ILLEGAL,errorMsg));
     }
 
@@ -623,4 +622,26 @@ public class
 
         return Collections.unmodifiableMap(aboutMap);
     }
+
+    public static int portForUriSchema(final URI uri) {
+        final int port = uri.getPort();
+        if (port < 1) {
+            return portForUriScheme(uri.getScheme());
+        }
+        return port;
+    }
+
+    private static int portForUriScheme(final String scheme) {
+        if (scheme == null) {
+            throw new NullPointerException("scheme cannot be null");
+        }
+        switch (scheme) {
+            case "http": return 80;
+            case "https": return 443;
+            case "ldap": return 389;
+            case "ldaps": return 636;
+        }
+        throw new IllegalArgumentException("unknown scheme: " + scheme);
+    }
+
 }

+ 53 - 5
pwm/servlet/src/password/pwm/util/SecureHelper.java

@@ -34,6 +34,7 @@ import javax.crypto.spec.SecretKeySpec;
 import java.io.*;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
 
 public class SecureHelper {
 
@@ -67,6 +68,7 @@ public class SecureHelper {
 
     public enum BlockAlgorithm {
         AES("AES"),
+        AES_SHA2("AES"),
         AES_CHECKSUM("AES/CBC/PKCS5Padding"),
         CONFIG("AES"),
 
@@ -148,7 +150,24 @@ public class SecureHelper {
 
             final Cipher cipher = Cipher.getInstance(blockAlgorithm.getAlgName());
             cipher.init(Cipher.ENCRYPT_MODE, key, cipher.getParameters());
-            return cipher.doFinal(value.getBytes(PwmConstants.DEFAULT_CHARSET));
+            final byte[] encryptedBytes = cipher.doFinal(value.getBytes(PwmConstants.DEFAULT_CHARSET));
+            if (blockAlgorithm.equals(BlockAlgorithm.AES_SHA2)) {
+                final byte[] hashChecksum;
+                {
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    baos.write(key.getEncoded());
+                    baos.write(encryptedBytes);
+                    hashChecksum = hashToBytes(new ByteArrayInputStream(baos.toByteArray()),HashAlgorithm.SHA256);
+                }
+                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                baos.write(hashChecksum);
+                baos.write(encryptedBytes);
+                return baos.toByteArray();
+
+            } else {
+                return encryptedBytes;
+            }
+
         } catch (Exception e) {
             final String errorMsg = "unexpected error performing simple crypt operation: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_CRYPT_ERROR, errorMsg);
@@ -211,7 +230,7 @@ public class SecureHelper {
     }
 
     public static String decryptBytes(
-            final byte[] value,
+            byte[] value,
             final SecretKey key,
             final BlockAlgorithm blockAlgorithm
     )
@@ -222,6 +241,26 @@ public class SecureHelper {
                 return null;
             }
 
+            if (blockAlgorithm == BlockAlgorithm.AES_SHA2) {
+                final int CHECKSUM_SIZE = 32;
+                if (value.length <= CHECKSUM_SIZE) {
+                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_CRYPT_ERROR,"incoming AES_SHA2 data is missing checksum"));
+                }
+                final byte[] suppliedChecksum = Arrays.copyOfRange(value, 0, CHECKSUM_SIZE);
+                final byte[] suppliedPayload = Arrays.copyOfRange(value, CHECKSUM_SIZE, value.length);
+                final byte[] computedChecksum;
+                {
+                    final byte[] keyBytes = key.getEncoded();
+                    final byte[] payloadPlusKeyBytes = new byte[keyBytes.length + suppliedPayload.length];
+                    System.arraycopy(keyBytes, 0, payloadPlusKeyBytes, 0, keyBytes.length);
+                    System.arraycopy(suppliedPayload, 0, payloadPlusKeyBytes, keyBytes.length, suppliedPayload.length);
+                    computedChecksum = hashToBytes(new ByteArrayInputStream(payloadPlusKeyBytes), HashAlgorithm.SHA256);
+                }
+                if (!Arrays.equals(suppliedChecksum,computedChecksum)) {
+                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_CRYPT_ERROR,"incoming AES_SHA2 data has incorrect checksum"));
+                }
+                value = suppliedPayload;
+            }
             final Cipher cipher = Cipher.getInstance(blockAlgorithm.getAlgName());
             cipher.init(Cipher.DECRYPT_MODE, key);
             final byte[] decrypted = cipher.doFinal(value);
@@ -345,6 +384,17 @@ public class SecureHelper {
     )
             throws PwmUnrecoverableException
     {
+        return Helper.byteArrayToHexString(hashToBytes(is,algorithm));
+    }
+
+
+
+    public static byte[] hashToBytes(
+            final InputStream is,
+            final HashAlgorithm algorithm
+    )
+            throws PwmUnrecoverableException
+    {
 
         final InputStream bis = is instanceof BufferedInputStream ? is : new BufferedInputStream(is);
 
@@ -370,9 +420,7 @@ public class SecureHelper {
             }
             bis.close();
 
-            final byte[] bytes = messageDigest.digest();
-
-            return Helper.byteArrayToHexString(bytes);
+            return messageDigest.digest();
         } catch (IOException e) {
             final String errorMsg = "unexepected error during hash operation: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_CRYPT_ERROR, errorMsg);

+ 4 - 18
pwm/servlet/src/password/pwm/util/X509Utils.java

@@ -45,25 +45,11 @@ public abstract class X509Utils {
             throws PwmOperationalException
     {
         final String host = uri.getHost();
-        final int port = uri.getPort() > -1
-                ? uri.getPort()
-                : portForUriScheme(uri.getScheme());
+        final int port = Helper.portForUriSchema(uri);
 
         return readRemoteCertificates(host, port);
     }
 
-    private static int portForUriScheme(final String scheme) {
-        if (scheme == null) {
-            throw new NullPointerException("scheme cannot be null");
-        }
-        switch (scheme) {
-            case "http": return 80;
-            case "https": return 443;
-            case "ldap": return 389;
-            case "ldaps": return 636;
-        }
-        throw new IllegalArgumentException("unknown scheme: " + scheme);
-    }
 
 
     public static X509Certificate[] readRemoteCertificates(final String host, final int port)
@@ -105,9 +91,9 @@ public abstract class X509Utils {
         return certs;
     }
 
-    public static boolean testIfLdapServerCertsInDefaultKeystore(final URI ldapUri) {
-        final String ldapHost = ldapUri.getHost();
-        final int ldapPort = ldapUri.getPort();
+    public static boolean testIfLdapServerCertsInDefaultKeystore(final URI serverURI) {
+        final String ldapHost = serverURI.getHost();
+        final int ldapPort = serverURI.getPort();
         try { // use default socket factory to test if certs work with it
             final SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
             final SSLSocket sslSock = (SSLSocket) factory.createSocket(ldapHost,ldapPort);

+ 16 - 0
pwm/servlet/src/password/pwm/util/cli/AbstractCliCommand.java

@@ -22,6 +22,7 @@
 
 package password.pwm.util.cli;
 
+import java.io.Console;
 import java.io.IOException;
 import java.util.Scanner;
 
@@ -79,4 +80,19 @@ public abstract class AbstractCliCommand implements CliCommand  {
     }
 
     abstract void doCommand() throws Exception;
+
+    String promptForPassword() {
+        final Console console = System.console();
+        console.writer().write("enter password:");
+        console.writer().flush();
+        final String password = new String(console.readPassword());
+        console.writer().write("verify password:");
+        console.writer().flush();
+        final String verify  = new String(console.readPassword());
+        if (!password.equals(verify)) {
+            out("verify password incorrect, exiting...");
+            System.exit(-1);
+        }
+        return password;
+    }
 }

+ 3 - 7
pwm/servlet/src/password/pwm/util/cli/ConfigSetPasswordCommand.java

@@ -26,8 +26,6 @@ import password.pwm.PwmConstants;
 import password.pwm.config.ConfigurationReader;
 import password.pwm.config.StoredConfiguration;
 
-import java.io.Console;
-import java.io.File;
 import java.util.Collections;
 
 public class ConfigSetPasswordCommand extends AbstractCliCommand {
@@ -36,16 +34,13 @@ public class ConfigSetPasswordCommand extends AbstractCliCommand {
     public void doCommand()
             throws Exception
     {
-        final ConfigurationReader configurationReader = new ConfigurationReader(new File(PwmConstants.DEFAULT_CONFIG_FILE_FILENAME));
+        final ConfigurationReader configurationReader = cliEnvironment.getConfigurationReader();
         final StoredConfiguration storedConfiguration = configurationReader.getStoredConfiguration();
         final String password;
         if (cliEnvironment.getOptions().containsKey(PASSWORD_OPTIONNAME)) {
             password = (String)cliEnvironment.getOptions().get(PASSWORD_OPTIONNAME);
         } else {
-            final Console console = System.console();
-            console.writer().write("enter password:");
-            console.writer().flush();
-            password = new String(console.readPassword());
+            password = promptForPassword();
         }
         storedConfiguration.setPassword(password);
         configurationReader.saveConfiguration(storedConfiguration, cliEnvironment.getPwmApplication(), PwmConstants.CLI_SESSION_LABEL);
@@ -77,6 +72,7 @@ public class ConfigSetPasswordCommand extends AbstractCliCommand {
         cliParameters.description = "Sets the configuration password";
         cliParameters.options = Collections.singletonList(passwordValueOption);
         cliParameters.needsPwmApplication = true;
+        cliParameters.needsLocalDB = false;
         cliParameters.readOnly = true;
         return cliParameters;
     }

+ 16 - 1
pwm/servlet/src/password/pwm/util/localdb/Derby_LocalDB.java

@@ -99,7 +99,22 @@ public class Derby_LocalDB extends AbstractJDBC_LocalDB {
 
             return connection;
         } catch (Throwable e) {
-            final String errorMsg = "error opening DB: " + e.getMessage();
+            final String errorMsg;
+            if (e instanceof SQLException) {
+                SQLException sqlException = (SQLException)e;
+                SQLException nextException = sqlException.getNextException();
+                if (nextException != null) {
+                    if ("XSDB6".equals(nextException.getSQLState())) {
+                        errorMsg = "unable to open LocalDB, the LocalDB is already opened in a different instance: " + nextException.getMessage();
+                    } else {
+                        errorMsg = "unable to open LocalDB, error=" + e.getMessage() + ", nextError=" + nextException.getMessage();
+                    }
+                } else {
+                    errorMsg = "unable to open LocalDB, error=" + e.getMessage();
+                }
+            } else {
+                errorMsg = "error opening DB: " + e.getMessage();
+            }
             LOGGER.error(errorMsg, e);
             throw new LocalDBException(new ErrorInformation(PwmError.ERROR_LOCALDB_UNAVAILABLE,errorMsg));
         }

+ 29 - 9
pwm/servlet/src/password/pwm/util/logging/PwmLogManager.java

@@ -91,9 +91,21 @@ public class PwmLogManager {
 
         deinitializeLogger();
 
-        // if we haven't yet configured log4j for whatever reason, do so using the hardcoded defaults and level (if supplied)
-        final Layout patternLayout = new PatternLayout(config.readAppProperty(AppProperty.LOGGING_PATTERN));
+        initConsoleLogger(config, consoleLogLevel);
+
+        initFileLogger(config, fileLogLevel, pwmApplicationPath);
 
+        // disable jersey warnings.
+        java.util.logging.LogManager.getLogManager().addLogger(java.util.logging.Logger.getLogger("com.sun.jersey.spi.container.servlet.WebComponent"));
+        java.util.logging.LogManager.getLogManager().getLogger("com.sun.jersey.spi.container.servlet.WebComponent").setLevel(java.util.logging.Level.OFF);
+    }
+
+    private static void initConsoleLogger(
+            final Configuration config,
+            final String consoleLogLevel
+    )
+    {
+        final Layout patternLayout = new PatternLayout(config.readAppProperty(AppProperty.LOGGING_PATTERN));
         // configure console logging
         if (consoleLogLevel != null && consoleLogLevel.length() > 0 && !"Off".equals(consoleLogLevel)) {
             final ConsoleAppender consoleAppender = new ConsoleAppender(patternLayout);
@@ -102,6 +114,7 @@ public class PwmLogManager {
             for (final Package logPackage : LOGGING_PACKAGES) {
                 if (logPackage != null) {
                     final Logger logger = Logger.getLogger(logPackage.getName());
+                    logger.setLevel(Level.TRACE);
                     logger.addAppender(consoleAppender);
                 }
             }
@@ -109,6 +122,15 @@ public class PwmLogManager {
         } else {
             LOGGER.debug("skipping stdout log4j initialization due to blank setting for log level");
         }
+    }
+
+    private static void initFileLogger(
+            final Configuration config,
+            final String fileLogLevel,
+            final File pwmApplicationPath
+    )
+    {
+        final Layout patternLayout = new PatternLayout(config.readAppProperty(AppProperty.LOGGING_PATTERN));
 
         // configure file logging
         final String logDirectorySetting = config.readAppProperty(AppProperty.LOGGING_FILE_PATH);
@@ -137,6 +159,7 @@ public class PwmLogManager {
                     if (logPackage != null) {
                         //if (!logPackage.equals(PwmApplication.class.getPackage())) {
                         final Logger logger = Logger.getLogger(logPackage.getName());
+                        logger.setLevel(Level.TRACE);
                         logger.addAppender(fileAppender);
                         //}
                     }
@@ -146,10 +169,6 @@ public class PwmLogManager {
                 LOGGER.debug("error initializing RollingFileAppender: " + e.getMessage());
             }
         }
-
-        // disable jersey warnings.
-        java.util.logging.LogManager.getLogManager().addLogger(java.util.logging.Logger.getLogger("com.sun.jersey.spi.container.servlet.WebComponent"));
-        java.util.logging.LogManager.getLogManager().getLogger("com.sun.jersey.spi.container.servlet.WebComponent").setLevel(java.util.logging.Level.OFF);
     }
 
     public static LocalDBLogger initializeLocalDBLogger(final PwmApplication pwmApplication) {
@@ -162,13 +181,13 @@ public class PwmLogManager {
 
         // initialize the localDBLogger
         final LocalDBLogger localDBLogger;
-        final PwmLogLevel localLogLevel = pwmApplication.getConfig().getEventLogLocalDBLevel();
+        final PwmLogLevel localDBLogLevel = pwmApplication.getConfig().getEventLogLocalDBLevel();
         try {
             final int maxEvents = (int) pwmApplication.getConfig().readSettingAsLong(PwmSetting.EVENTS_PWMDB_MAX_EVENTS);
             final long maxAgeMS = 1000 * pwmApplication.getConfig().readSettingAsLong(PwmSetting.EVENTS_PWMDB_MAX_AGE);
             localDBLogger = initLocalDBLogger(localDB, maxEvents, maxAgeMS, pwmApplication);
             if (localDBLogger != null) {
-                PwmLogger.setLocalDBLogger(localLogLevel, localDBLogger);
+                PwmLogger.setLocalDBLogger(localDBLogLevel, localDBLogger);
             }
         } catch (Exception e) {
             LOGGER.warn("unable to initialize localDBLogger: " + e.getMessage());
@@ -178,11 +197,12 @@ public class PwmLogManager {
         // add appender for other packages;
         try {
             final LocalDBLog4jAppender localDBLog4jAppender = new LocalDBLog4jAppender(localDBLogger);
+            localDBLog4jAppender.setThreshold(localDBLogLevel.getLog4jLevel());
             for (final Package logPackage : LOGGING_PACKAGES) {
                 if (logPackage != null && !logPackage.equals(PwmApplication.class.getPackage())) {
                     final Logger logger = Logger.getLogger(logPackage.getName());
                     logger.addAppender(localDBLog4jAppender);
-                    logger.setLevel(localLogLevel.getLog4jLevel());
+                    logger.setLevel(Level.TRACE);
                 }
             }
         } catch (Exception e) {

+ 11 - 7
pwm/servlet/src/password/pwm/util/stats/StatisticsManager.java

@@ -325,14 +325,18 @@ public class StatisticsManager implements PwmService {
     }
 
     private void resetDailyStats() {
-        final Map<String,String> emailValues = new LinkedHashMap<>();
-        for (final Statistic statistic : Statistic.values()) {
-            final String key = statistic.getLabel(PwmConstants.DEFAULT_LOCALE);
-            final String value = statsDaily.getStatistic(statistic);
-            emailValues.put(key,value);
-        }
+        try {
+            final Map<String, String> emailValues = new LinkedHashMap<>();
+            for (final Statistic statistic : Statistic.values()) {
+                final String key = statistic.getLabel(PwmConstants.DEFAULT_LOCALE);
+                final String value = statsDaily.getStatistic(statistic);
+                emailValues.put(key, value);
+            }
 
-        AlertHandler.alertDailyStats(pwmApplication, emailValues);
+            AlertHandler.alertDailyStats(pwmApplication, emailValues);
+        } catch (Exception e) {
+            LOGGER.error("error while generating daily alert statistics: " + e.getMessage());
+        }
 
         currentDailyKey = new DailyKey(new Date());
         statsDaily = new StatisticsBundle();

+ 6 - 4
pwm/servlet/src/password/pwm/ws/server/rest/RestCheckPasswordServer.java

@@ -139,6 +139,7 @@ public class RestCheckPasswordServer extends AbstractRestServer {
         } catch (PwmUnrecoverableException e) {
             return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
         }
+        LOGGER.trace("beginning check password operation for user " + restRequestBean.getPwmSession().getUserInfoBean().getUserIdentity());
 
         if (jsonInput.password1 == null || jsonInput.password1.length() < 1) {
             final String errorMessage = "missing field 'password1'";
@@ -184,10 +185,12 @@ public class RestCheckPasswordServer extends AbstractRestServer {
             LOGGER.trace(restRequestBean.getPwmSession(), "REST /checkpassword response (" + timeDuration.asCompactString() + "): " + JsonUtil.serialize(jsonData));
             return restResultBean.asJsonResponse();
         } catch (PwmException e) {
+            LOGGER.debug(restRequestBean.getPwmSession(), "REST /checkpassword error during execution: " + e.getMessage(), e);
             return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();
         } catch (Exception e) {
             final String errorMessage = "unexpected error executing web service: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
+            LOGGER.error(restRequestBean.getPwmSession(),errorInformation.toDebugStr(),e);
             return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
         }
     }
@@ -231,8 +234,9 @@ public class RestCheckPasswordServer extends AbstractRestServer {
             throws PwmUnrecoverableException, ChaiUnavailableException
     {
         final HelpdeskProfile helpdeskProfile = pwmSession.getSessionManager().getHelpdeskProfile(pwmApplication);
-        final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
-        final boolean thirdParty = !checkRequest.getUserIdentity().equals(pwmSession.getUserInfoBean().getUserIdentity());
+        final boolean useProxy = helpdeskProfile != null && helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
+        final boolean thirdParty = checkRequest.getUserIdentity() != null
+                && !checkRequest.getUserIdentity().canonicalEquals(pwmSession.getUserInfoBean().getUserIdentity(), pwmApplication);
 
         final ChaiUser user = useProxy && thirdParty
                 ? pwmApplication.getProxiedChaiUser(checkRequest.getUserIdentity())
@@ -253,5 +257,3 @@ public class RestCheckPasswordServer extends AbstractRestServer {
         return JsonData.fromPasswordCheckInfo(passwordCheckInfo);
     }
 }
-
-

+ 3 - 2
pwm/servlet/src/password/pwm/ws/server/rest/RestProfileServer.java

@@ -26,6 +26,7 @@ import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.Permission;
 import password.pwm.config.FormConfiguration;
+import password.pwm.config.FormUtility;
 import password.pwm.config.PwmSetting;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
@@ -111,9 +112,9 @@ public class RestProfileServer extends AbstractRestServer {
                 final UserDataReader userDataReader = LdapUserDataReader.selfProxiedReader(
                         restRequestBean.getPwmApplication(), restRequestBean.getPwmSession(),
                         restRequestBean.getUserIdentity());
-                UpdateProfileServlet.populateFormFromLdap(formFields,restRequestBean.getPwmSession().getLabel(),formData,userDataReader);
+                FormUtility.populateFormMapFromLdap(formFields, restRequestBean.getPwmSession().getLabel(), formData, userDataReader);
             } else {
-                UpdateProfileServlet.populateFormFromLdap(formFields,restRequestBean.getPwmSession().getLabel(),formData,restRequestBean.getPwmSession().getSessionManager().getUserDataReader(restRequestBean.getPwmApplication()));
+                FormUtility.populateFormMapFromLdap(formFields, restRequestBean.getPwmSession().getLabel(), formData, restRequestBean.getPwmSession().getSessionManager().getUserDataReader(restRequestBean.getPwmApplication()));
             }
 
             for (FormConfiguration formConfig : formData.keySet()) {

+ 2 - 2
pwm/servlet/web/WEB-INF/jsp/configguide-ldap.jsp

@@ -107,14 +107,14 @@
                         <b>Proxy/Admin LDAP DN</b>
                         <br/>
                         <span class="fa fa-chevron-circle-right"></span>
-                        <input class="configStringInput" id="<%=ConfigGuideServlet.PARAM_LDAP_ADMIN_DN%>" name="<%=ConfigGuideServlet.PARAM_LDAP_ADMIN_DN%>"/>
+                        <input class="configStringInput" id="<%=ConfigGuideServlet.PARAM_LDAP_ADMIN_DN%>" name="<%=ConfigGuideServlet.PARAM_LDAP_ADMIN_DN%>"  value="<%=configGuideBean.getFormData().get(ConfigGuideServlet.PARAM_LDAP_ADMIN_DN)%>"/>
                     </div>
                     &nbsp;<br/>
                     <div class="setting_item">
                         <b>Password</b>
                         <br/>
                         <span class="fa fa-chevron-circle-right"></span>
-                        <input class="configStringInput" type="password" id="<%=ConfigGuideServlet.PARAM_LDAP_ADMIN_PW%>" name="<%=ConfigGuideServlet.PARAM_LDAP_ADMIN_PW%>" />
+                        <input class="configStringInput" type="password" id="<%=ConfigGuideServlet.PARAM_LDAP_ADMIN_PW%>" name="<%=ConfigGuideServlet.PARAM_LDAP_ADMIN_PW%>" value="<%=configGuideBean.getFormData().get(ConfigGuideServlet.PARAM_LDAP_ADMIN_PW)%>"/>
                     </div>
                 </div>
             </div>

+ 83 - 0
pwm/servlet/web/WEB-INF/jsp/forgottenpassword-remote.jsp

@@ -0,0 +1,83 @@
+<%--
+  ~ 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
+  --%>
+
+<!DOCTYPE html>
+<%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
+<%@ page import="password.pwm.RecoveryVerificationMethod" %>
+<%@ page import="java.util.List" %>
+<%@ taglib uri="pwm" prefix="pwm" %>
+<%@ include file="fragment/header.jsp" %>
+<html dir="<pwm:LocaleOrientation/>">
+<body class="nihilo">
+<div id="wrapper">
+    <jsp:include page="fragment/header-body.jsp">
+        <jsp:param name="pwm.PageName" value="Title_ForgottenPassword"/>
+    </jsp:include>
+    <div id="centerbody">
+        <%
+            final List<RecoveryVerificationMethod.UserPrompt> prompts = (List<RecoveryVerificationMethod.UserPrompt>)JspUtility.getAttribute(pageContext,PwmConstants.REQUEST_ATTR.ForgottenPasswordPrompts);
+            final String instructions = (String)JspUtility.getAttribute(pageContext,PwmConstants.REQUEST_ATTR.ForgottenPasswordInstructions);
+        %>
+        <p><%=instructions%></p>
+        <form action="<pwm:url url='ForgottenPassword'/>" method="post" enctype="application/x-www-form-urlencoded" name="search" class="pwm-form">
+            <%@ include file="/WEB-INF/jsp/fragment/message.jsp" %>
+            <br/>
+            <% for (final RecoveryVerificationMethod.UserPrompt userPrompt : prompts) { %>
+            <div class="formFieldLabel">
+                <%= userPrompt.getDisplayPrompt() %>
+            </div>
+
+            <input type="password" id="remote-<%=userPrompt.getIdentifier()%>" name="remote-<%=userPrompt.getIdentifier()%>" class="inputfield passwordfield" required="required" autofocus/>
+            <% } %>
+            <div class="buttonbar">
+                <button type="submit" class="btn" name="submitBtn" id="submitBtn">
+                    <pwm:if test="showIcons"><span class="btn-icon fa fa-forward"></span></pwm:if>
+                    <pwm:display key="Button_Continue"/>
+                </button>
+                <% if ("true".equals(JspUtility.getAttribute(pageContext, PwmConstants.REQUEST_ATTR.ForgottenPasswordOptionalPageView))) { %>
+                <button type="button" id="button-goBack" name="button-goBack" class="btn" >
+                    <pwm:if test="showIcons"><span class="btn-icon fa fa-backward"></span></pwm:if>
+                    <pwm:display key="Button_GoBack"/>
+                </button>
+                <% } %>
+                <%@ include file="/WEB-INF/jsp/fragment/button-reset.jsp" %>
+                <%@ include file="/WEB-INF/jsp/fragment/forgottenpassword-cancel.jsp" %>
+                <input type="hidden" id="processAction" name="processAction" value="enterNaafResponse"/>
+                <input type="hidden" id="pwmFormID" name="pwmFormID" value="<pwm:FormID/>"/>
+            </div>
+        </form>
+    </div>
+    <div class="push"></div>
+</div>
+<pwm:script>
+    <script>
+        PWM_GLOBAL['startupFunctions'].push(function(){
+            PWM_MAIN.addEventHandler('button-goBack','click',function() {
+                PWM_MAIN.submitPostAction('ForgottenPassword', '<%=ForgottenPasswordServlet.ForgottenPasswordAction.verificationChoice%>');
+            });
+        });
+    </script>
+</pwm:script>
+<%@ include file="fragment/footer.jsp" %>
+</body>
+</html>
+

+ 4 - 2
pwm/servlet/web/WEB-INF/jsp/fragment/form.jsp

@@ -1,6 +1,7 @@
 <%@ page import="password.pwm.PwmApplication" %>
 <%@ page import="password.pwm.PwmConstants" %>
 <%@ page import="password.pwm.config.FormConfiguration" %>
+<%@ page import="password.pwm.config.FormUtility" %>
 <%@ page import="password.pwm.config.PwmSetting" %>
 <%@ page import="password.pwm.error.PwmError" %>
 <%@ page import="password.pwm.http.ContextManager" %>
@@ -62,8 +63,9 @@
     <input style="text-align: left;" id="<%=loopConfiguration.getName()%>" type="hidden" class="inputfield"
            name="<%=loopConfiguration.getName()%>" value="<%= currentValue %>"/>
     <% } else if (loopConfiguration.getType().equals(FormConfiguration.Type.checkbox)) { %>
+    <% final boolean checked = FormUtility.checkboxValueIsChecked(formDataMap.get(loopConfiguration)); %>
     <label class="checkboxWrapper">
-        <input id="<%=loopConfiguration.getName()%>" name="<%=loopConfiguration.getName()%>" type="checkbox" <pwm:autofocus/>/>
+        <input id="<%=loopConfiguration.getName()%>" name="<%=loopConfiguration.getName()%>" type="checkbox" <%=checked?"checked":""%> <pwm:autofocus/>/>
         <%=loopConfiguration.getLabel(formLocale)%>
     </label>
     <% } else { %>
@@ -85,7 +87,7 @@
         <%= currentValue %>
         </span>
     <% } else if (loopConfiguration.getType() == FormConfiguration.Type.select) { %>
-    <select id="<%=loopConfiguration.getName()%>" name="<%=loopConfiguration.getName()%>" style="width:20%;margin-left: 5px" <pwm:autofocus/> >
+    <select id="<%=loopConfiguration.getName()%>" name="<%=loopConfiguration.getName()%>" class="inputfield selectfield" <pwm:autofocus/> >
         <% for (final String optionName : loopConfiguration.getSelectOptions().keySet()) {%>
         <option value="<%=optionName%>" <%if(optionName.equals(currentValue)){%>selected="selected"<%}%>>
             <%=loopConfiguration.getSelectOptions().get(optionName)%>

+ 2 - 0
pwm/servlet/web/WEB-INF/jsp/fragment/header-warnings.jsp

@@ -87,12 +87,14 @@
             <pwm:if test="showIcons"><span class="btn-icon fa fa-list-alt"></span></pwm:if>
             <pwm:display key="Title_Admin"/>
         </a>
+        <pwm:if test="forcedPageView" negate="true">
         <a class="header-warning-button" id="header_openLogViewerButton">
             <pwm:if test="showIcons"><span class="btn-icon fa fa-list-alt"></span></pwm:if>
             <pwm:display key="MenuItem_ViewLog" bundle="Config"/>
             &nbsp;
             <pwm:if test="showIcons"><span class="btn-icon fa fa-external-link"></span></pwm:if>
         </a>
+        </pwm:if>
         <% } %>
     </div>
     <div id="panel-header-healthData" class="header-warning-row header-warning-healthData"></div>

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

@@ -20,13 +20,6 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   --%>
 
-<!--
-
-TODO: focus on input field
-TODO: show/hide the entered code.
-TODO: support HOTP
-
--->
 <%@ page import="password.pwm.http.bean.SetupOtpBean" %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true"
@@ -47,9 +40,9 @@ TODO: support HOTP
         <form action="<pwm:url url='SetupOtp'/>" method="post" name="setupOtpSecret"
               enctype="application/x-www-form-urlencoded" id="setupOtpSecret" class="pwm-form">
             <div style="width:100%; text-align: center">
-                <input type="text" pattern="[0-9]*" name="<%= PwmConstants.PARAM_OTP_TOKEN%>" class="inputfield" maxlength="<%=otpTokenLength%>" type="text"
+                <input type="text" pattern="^[0-9]*$" name="<%= PwmConstants.PARAM_OTP_TOKEN%>" class="inputfield passwordfield" maxlength="<%=otpTokenLength%>" type="text"
                        id="<%= PwmConstants.PARAM_OTP_TOKEN%>" required="required" style="max-width: 100px"
-                       autofocus/>
+                       autofocus title="0-9"/>
             </div>
             <div class="buttonbar">
                 <input type="hidden" name="processAction" value="testOtpSecret"/>

+ 11 - 1
pwm/servlet/web/WEB-INF/jsp/updateprofile-confirm.jsp

@@ -1,4 +1,6 @@
 <%@ page import="password.pwm.config.FormConfiguration" %>
+<%@ page import="password.pwm.i18n.LocaleHelper" %>
+<%@ page import="password.pwm.util.StringUtil" %>
 <%@ page import="java.util.Map" %>
 <%--
   ~ Password Management Servlets (PWM)
@@ -29,6 +31,7 @@
 <html dir="<pwm:LocaleOrientation/>">
 <%@ include file="fragment/header.jsp" %>
 <body class="nihilo">
+<% final PwmRequest pwmRequest = JspUtility.getPwmRequest(pageContext);%>
 <div id="wrapper">
     <jsp:include page="fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="Title_UpdateProfileConfirm"/>
@@ -45,7 +48,14 @@
                     <%=formConfiguration.getLabel(JspUtility.locale(request))%>
                 </td>
                 <td>
-                    <%=formDataMap.get(formConfiguration)%>
+                    <%
+                        final String value = formDataMap.get(formConfiguration);
+                        if (formConfiguration.getType() == FormConfiguration.Type.checkbox) {
+                    %>
+                    <%= LocaleHelper.booleanString(Boolean.parseBoolean(value),pwmRequest)%>
+                    <% } else { %>
+                    <%=StringUtil.escapeHtml(value)%>
+                    <% } %>
                 </td>
             </tr>
             <% } %>

BIN
pwm/servlet/web/WEB-INF/lib/ldapChai.jar


BIN
pwm/servlet/web/WEB-INF/lib/ldapchai-0.6.6-SNAPSHOT.jar


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

@@ -39,6 +39,7 @@
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.NO_IDLE_TIMEOUT); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_HEADER_BUTTONS); %>
 <% JspUtility.setFlag(pageContext, PwmRequest.Flag.HIDE_FOOTER_TEXT); %>
+<% final boolean advancedMode = JspUtility.getPwmRequest(pageContext).hasParameter("advanced"); %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <%
@@ -72,8 +73,9 @@
                 <% } %>
                 <% } %>
             </ol>
-            <% if (pwmRequest.readParameterAsBoolean("advanced")) { %>
-            <li><a href="#settingSummary">Setting Summary</a></li>
+            <% if (advancedMode) { %>
+            <li><a href="#settingStatistics">Setting Statistics</a></li>
+            <li><a href="#appProperties">Application Properties</a></li>
             <% } %>
             <li><a href="#displayStrings">Display Strings</a></li>
             <ol>
@@ -320,12 +322,12 @@
         <% } %>
         <% } %>
         <% } %>
-        <% if (pwmRequest.readParameterAsBoolean("advanced")) { %>
-        <h2><a id="settingSummary">Setting Summary</a></h2>
+        <% if (advancedMode) { %>
+        <h2><a id="settingStatistics">Setting Statistics</a></h2>
         <% Map<PwmSetting.SettingStat,Object> settingStats = PwmSetting.getStats(); %>
         <table>
             <tr>
-                <td class="key">
+                <td>
                     Total Settings
                 </td>
                 <td>
@@ -333,7 +335,7 @@
                 </td>
             </tr>
             <tr>
-                <td class="key">
+                <td>
                     Settings that are part of a Profile
                 </td>
                 <td>
@@ -343,7 +345,7 @@
             <% Map<PwmSettingSyntax,Integer> syntaxCounts = (Map<PwmSettingSyntax,Integer>)settingStats.get(PwmSetting.SettingStat.syntaxCounts); %>
             <% for (final PwmSettingSyntax loopSyntax : syntaxCounts.keySet()) { %>
             <tr>
-                <td class="key" style="width: auto; white-space: nowrap">
+                <td>
                     Settings with syntax type <%= loopSyntax.toString() %>
                 </td>
                 <td>
@@ -352,6 +354,31 @@
             </tr>
             <% } %>
         </table>
+
+        <h2><a id="appProperties">Application Properties</a></h2>
+        <table>
+            <tr>
+                <td class="key" style="text-align: left">
+                    Key
+                </td>
+                <td class="key" style="text-align: left">
+                    Default Value
+                </td>
+            </tr>
+            <% for (final AppProperty appProperty : AppProperty.values()) { %>
+            <tr>
+                <td style="width: auto; white-space: nowrap; text-align: left">
+                    <%=appProperty.getKey()%>
+                </td>
+                <td>
+                    <%=appProperty.getDefaultValue()%>
+                </td>
+            </tr>
+            <% } %>
+        </table>
+
+
+
         <% } %>
         <h1><a id="displayStrings">Display Strings</a></h1>
         <% for (PwmLocaleBundle bundle : PwmLocaleBundle.values()) { %>

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

@@ -244,7 +244,7 @@ PWM_CHANGEPW.doRandomGeneration=function(randomConfig) {
             i = i + j;
             (function(index) {
                 var elementID = "randomGen" + index;
-                dialogBody += '<td style="border: 0; padding-bottom: 5px;" width="20%"><a style="visibility:hidden" class="link-randomPasswordValue" href="#" id="' + elementID + '">&nbsp;</a></td>';
+                dialogBody += '<td style="border: 0; padding-bottom: 5px;" width="20%"><div style="visibility:hidden" class="link-randomPasswordValue" href="#" id="' + elementID + '">&nbsp;</div></td>';
                 eventHandlers.push(function(){
                     PWM_MAIN.addEventHandler(elementID,'click',function(){
                         var value = PWM_MAIN.getObject(elementID).innerHTML;

+ 105 - 23
pwm/servlet/web/public/resources/js/configeditor-settings.js

@@ -746,8 +746,12 @@ FormTableHandler.addRow = function(keyName) {
 };
 
 FormTableHandler.showOptionsDialog = function(keyName, iteration) {
+    var type = PWM_VAR['clientSettingCache'][keyName][iteration]['type'];
     var options = 'options' in PWM_SETTINGS['settings'][keyName] ? PWM_SETTINGS['settings'][keyName]['options'] : {};
+    var showRequired = options['required'] != 'hide' && type != 'checkbox';
+    var showConfirmation = type != 'checkbox' && type != 'select';
     var showUnique = options['unique'] == 'show';
+    var showReadOnly = options['readonly'] == 'show';
     require(["dijit/Dialog","dijit/form/Textarea","dijit/form/CheckBox","dijit/form/NumberSpinner"],function(){
         var inputID = 'value_' + keyName + '_' + iteration + "_";
         var bodyText = '<div style="max-height: 500px; overflow-y: auto"><table class="noborder">';
@@ -758,13 +762,15 @@ FormTableHandler.showOptionsDialog = function(keyName, iteration) {
         bodyText += '</td>';
 
         bodyText += '</tr><tr>';
-        if (options['required'] != 'hide') {
+        if (showRequired) {
             bodyText += '<td id="' + inputID + '-label-required" class="key" title="' + PWM_CONFIG.showString('Tooltip_FormOptions_Required') + '">Required</td><td><input type="checkbox" id="' + inputID + 'required' + '"/></td>';
             bodyText += '</tr><tr>';
         }
-        bodyText += '<td id="' + inputID + '-label-confirm" class="key" title="' + PWM_CONFIG.showString('Tooltip_FormOptions_Confirm') + '">Confirm</td><td><input type="checkbox" id="' + inputID + 'confirmationRequired' + '"/></td>';
-        bodyText += '</tr><tr>';
-        if (options['readonly'] == 'show') {
+        if (showConfirmation) {
+            bodyText += '<td id="' + inputID + '-label-confirm" class="key" title="' + PWM_CONFIG.showString('Tooltip_FormOptions_Confirm') + '">Confirm</td><td><input type="checkbox" id="' + inputID + 'confirmationRequired' + '"/></td>';
+            bodyText += '</tr><tr>';
+        }
+        if (showReadOnly) {
             bodyText += '<td id="' + inputID + '-label-readOnly" class="key" title="' + PWM_CONFIG.showString('Tooltip_FormOptions_ReadOnly') + '">Read Only</td><td><input type="checkbox" id="' + inputID + 'readonly' + '"/></td>';
             bodyText += '</tr><tr>';
         }
@@ -807,14 +813,16 @@ FormTableHandler.showOptionsDialog = function(keyName, iteration) {
                 FormTableHandler.showDescriptionDialog(keyName, iteration);
             });
 
-            PWM_MAIN.clearDijitWidget(inputID + "required");
-            new dijit.form.CheckBox({
-                checked: PWM_VAR['clientSettingCache'][keyName][iteration]['required'],
-                onChange: function () {
-                    PWM_VAR['clientSettingCache'][keyName][iteration]['required'] = this.checked;
-                    FormTableHandler.write(keyName)
-                }
-            }, inputID + "required");
+            if (showRequired) {
+                PWM_MAIN.clearDijitWidget(inputID + "required");
+                new dijit.form.CheckBox({
+                    checked: PWM_VAR['clientSettingCache'][keyName][iteration]['required'],
+                    onChange: function () {
+                        PWM_VAR['clientSettingCache'][keyName][iteration]['required'] = this.checked;
+                        FormTableHandler.write(keyName)
+                    }
+                }, inputID + "required");
+            }
 
             PWM_MAIN.clearDijitWidget(inputID + "confirmationRequired");
             new dijit.form.CheckBox({
@@ -847,17 +855,6 @@ FormTableHandler.showOptionsDialog = function(keyName, iteration) {
                 }, inputID + "unique");
             }
 
-            if (PWM_SETTINGS['settings'][keyName]['options']['unique'] == 'show') {
-                PWM_MAIN.clearDijitWidget(inputID + "unique");
-                new dijit.form.CheckBox({
-                    checked: PWM_VAR['clientSettingCache'][keyName][iteration]['unique'],
-                    onChange: function () {
-                        PWM_VAR['clientSettingCache'][keyName][iteration]['unique'] = this.checked;
-                        FormTableHandler.write(keyName)
-                    }
-                }, inputID + "unique");
-            }
-
             PWM_MAIN.clearDijitWidget(inputID + "minimumLength");
             new dijit.form.NumberSpinner({
                 value: PWM_VAR['clientSettingCache'][keyName][iteration]['minimumLength'],
@@ -2993,3 +2990,88 @@ UILibrary.manageNumericInput = function(elementID, readFunction) {
         }
     });
 };
+
+ActionHandler.showHeadersDialog = function(keyName, iteration) {
+    var addValue = function(keyName, iteration) {
+        var body = '<table class="noborder">';
+        body += '<tr><td>Name</td><td><input class="configStringInput" id="newHeaderName" style="width:300px"/></td></tr>';
+        body += '<tr><td>Value</td><td><input class="configStringInput" id="newHeaderValue" style="width:300px"/></td></tr>';
+        body += '</table>';
+
+        var updateFunction = function(){
+            PWM_MAIN.getObject('dialog_ok_button').disabled = true;
+            PWM_VAR['newHeaderName'] = PWM_MAIN.getObject('newHeaderName').value;
+            PWM_VAR['newHeaderValue'] = PWM_MAIN.getObject('newHeaderValue').value;
+            if (PWM_VAR['newHeaderName'].length > 0 && PWM_VAR['newHeaderValue'].length > 0) {
+                PWM_MAIN.getObject('dialog_ok_button').disabled = false;
+            }
+        };
+
+        PWM_MAIN.showConfirmDialog({
+            title:'New Header',
+            text:body,
+            showClose:true,
+            loadFunction:function(){
+                PWM_MAIN.addEventHandler('newHeaderName','input',function(){
+                    updateFunction();
+                });
+                PWM_MAIN.addEventHandler('newHeaderValue','input',function(){
+                    updateFunction();
+                });
+                updateFunction();
+            },okAction:function(){
+                var headers = PWM_VAR['clientSettingCache'][keyName][iteration]['headers'];
+                headers[PWM_VAR['newHeaderName']] = PWM_VAR['newHeaderValue'];
+                ActionHandler.write(keyName);
+                ActionHandler.showHeadersDialog(keyName, iteration);
+            },cancelAction:function(){
+                ActionHandler.showHeadersDialog(keyName, iteration);
+            }
+        });
+
+    };
+
+    var settingValue = PWM_VAR['clientSettingCache'][keyName][iteration];
+    require(["dijit/Dialog","dijit/form/ValidationTextBox","dijit/form/Button","dijit/form/TextBox"],function(Dialog,ValidationTextBox,Button,TextBox){
+        var inputID = 'value_' + keyName + '_' + iteration + "_" + "headers_";
+
+        var bodyText = '';
+        bodyText += '<table class="noborder">';
+        bodyText += '<tr><td><b>Name</b></td><td><b>Value</b></td></tr>';
+        for (var iter in settingValue['headers']) {
+            (function(headerName) {
+                var value = settingValue['headers'][headerName];
+                var optionID = inputID + headerName;
+                bodyText += '<tr><td class="border">' + headerName + '</td><td class="border">' + value + '</td>';
+                bodyText += '<td style="width:15px;"><span class="delete-row-icon action-icon fa fa-times" id="button-' + optionID + '-deleteRow"></span></td>';
+                bodyText += '</tr>';
+            }(iter));
+        }
+        bodyText += '</table>';
+
+        PWM_MAIN.showDialog({
+            title: 'HTTP Headers for webservice ' + settingValue['name'],
+            text: bodyText,
+            buttonHtml:'<button id="button-' + inputID + '-addHeader" class="btn"><span class="btn-icon fa fa-plus-square"></span>Add Header</button>',
+            okAction: function() {
+                ActionHandler.showOptionsDialog(keyName,iteration);
+            },
+            loadFunction: function() {
+                for (var iter in settingValue['headers']) {
+                    (function(headerName) {
+                        var headerID = inputID + headerName;
+                        PWM_MAIN.addEventHandler('button-' + headerID + '-deleteRow', 'click', function () {
+                            delete settingValue['headers'][headerName];
+                            ActionHandler.write(keyName);
+                            ActionHandler.showHeadersDialog(keyName, iteration);
+                        });
+                    }(iter));
+                }
+                PWM_MAIN.addEventHandler('button-' + inputID + '-addHeader','click',function(){
+                    ActionHandler.addHeader(keyName, iteration);
+                });
+            }
+        });
+    });
+};
+

+ 2 - 2
pwm/servlet/web/public/resources/js/helpdesk.js

@@ -186,10 +186,10 @@ PWM_HELPDESK.changePasswordPopup = function() {
     bodyText += '<img style="visibility:hidden;" id="confirmCrossMark" alt="crossMark" height="15" width="15" src="' + PWM_GLOBAL['url-resources'] + '/redX.png">';
     bodyText += '</div></td>';
 
-    bodyText += '</tr></table>';
+    bodyText += '</tr></table><br/>';
     bodyText += '<button name="change" class="btn" id="password_button" disabled="true"><span class="btn-icon fa fa-key"></span>' + PWM_MAIN.showString('Button_ChangePassword') + '</button>';
     if (PWM_VAR['helpdesk_setting_PwUiMode'] == 'both') {
-        bodyText += '<button name="random" class="btn" id="button-autoGeneratePassword"><span class="btn-icon fa fa-retweet"></span>' + PWM_MAIN.showString('Display_AutoGeneratedPassword') + '</button>';
+        bodyText += '<button name="random" class="btn" id="button-autoGeneratePassword"><span class="btn-icon fa fa-retweet"></span>' + PWM_MAIN.showString('Title_RandomPasswords') + '</button>';
     }
 
     try { PWM_MAIN.getObject('message').id = "base-message"; } catch (e) {}

+ 21 - 7
pwm/servlet/web/public/resources/js/main.js

@@ -1868,14 +1868,28 @@ PWM_MAIN.convertSecondsToDisplayTimeDuration = function(amount, fullLength) {
 };
 
 PWM_MAIN.setStyle = function(elementID, property, value) {
-    try {
-        var element = PWM_MAIN.getObject(elementID);
-        if (element) {
-            element.style.setProperty(property, value, null);
+
+    require(["dojo"],function(dojo) {
+        if (dojo.isIE <= 8) { // IE8 and below cant handle the css associated with the locale select menu
+            try {
+                var element = PWM_MAIN.getObject(elementID);
+                if (element) {
+                    element.style[property] = value;
+                }
+            } catch (e) {
+                console.error('error while setting ie8 style, elementID=' + elementID + ", property=" + property + ", value=" + value + ", error: " + e);
+            }
+        } else {
+            try {
+                var element = PWM_MAIN.getObject(elementID);
+                if (element) {
+                    element.style.setProperty(property, value, null);
+                }
+            } catch (e) {
+                console.error('error while setting style, elementID=' + elementID + ", property=" + property + ", value=" + value + ", error: " + e);
+            }
         }
-    } catch (e) {
-        console.error('error while setting style, elementID=' + elementID + ", property=" + property + ", value=" + value + ", error: " + e);
-    }
+    });
 };
 
 PWM_MAIN.addCssClass = function(elementID, className) {

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

@@ -71,6 +71,11 @@ form {
     display: inline;
 }
 
+.link-randomPasswordValue {
+    cursor: pointer;
+    font-family: monospace;
+}
+
 #form {
     border-radius: 3px;
     -moz-border-radius: 3px;
@@ -154,6 +159,10 @@ table td.key {
     padding-left: 4px;
 }
 
+.selectfield {
+    height: auto;
+}
+
 .noticebar {
     width: 100%;
     font-size: smaller;
@@ -721,7 +730,7 @@ dialog .closeIcon { float: right; cursor: pointer; margin-right: 3px; }
 .panel-orgChart-parent .panel-orgChart-person {border-left: 5px solid #697374;}
 .panel-orgChart-parent .panel-orgChart-person:hover {border-left: 5px solid #697374;}
 .panel-orgChart-parent { margin-left: 5px }
-.panel-orgChart-person { position: relative; background: #eaeaea; padding: 6px; width: 220px; height: 70px; margin-right: 10px; margin-bottom: 10px; border:  1px solid transparent; cursor:pointer; }
+.panel-orgChart-person { position: relative; background: #eaeaea; padding: 6px; width: 220px; height: 70px; margin-right: 10px; margin-bottom: 10px; border: 1px solid transparent; cursor:pointer; }
 .panel-orgChart-sibling { float: left; display: block; }
 .panel-orgChart-siblings { margin-left: 40px; }
 .img-orgChart-userPhoto { width: 40px; height: 40px; float: left; border-radius: 2px; margin-right: 6px; margin-bottom: 35px; border: 1px solid #D4D4D4; }