Browse Source

- peoplesearch detail updates,
- configeditor js refactoring
- admin sidemenu

jrivard 10 years ago
parent
commit
fa465161d2
58 changed files with 1395 additions and 1181 deletions
  1. 3 2
      pwm/servlet/src/password/pwm/AppProperty.java
  2. 2 1
      pwm/servlet/src/password/pwm/AppProperty.properties
  3. 63 35
      pwm/servlet/src/password/pwm/PwmApplication.java
  4. 1 1
      pwm/servlet/src/password/pwm/PwmConstants.properties
  5. 94 1
      pwm/servlet/src/password/pwm/bean/AboutApplicationBean.java
  6. 2 4
      pwm/servlet/src/password/pwm/config/PwmSetting.java
  7. 16 22
      pwm/servlet/src/password/pwm/config/PwmSetting.xml
  8. 1 1
      pwm/servlet/src/password/pwm/config/StoredConfiguration.java
  9. 1 3
      pwm/servlet/src/password/pwm/config/function/UserMatchViewerFunction.java
  10. 1 1
      pwm/servlet/src/password/pwm/config/value/PasswordValue.java
  11. 2 2
      pwm/servlet/src/password/pwm/event/AuditManager.java
  12. 1 4
      pwm/servlet/src/password/pwm/health/LDAPStatusChecker.java
  13. 1 3
      pwm/servlet/src/password/pwm/http/ContextManager.java
  14. 14 4
      pwm/servlet/src/password/pwm/http/HttpMethod.java
  15. 10 0
      pwm/servlet/src/password/pwm/http/PwmHttpRequestWrapper.java
  16. 11 7
      pwm/servlet/src/password/pwm/http/PwmRequest.java
  17. 2 7
      pwm/servlet/src/password/pwm/http/client/PwmHttpClient.java
  18. 2 12
      pwm/servlet/src/password/pwm/http/filter/AuthenticationFilter.java
  19. 8 0
      pwm/servlet/src/password/pwm/http/filter/SessionFilter.java
  20. 18 2
      pwm/servlet/src/password/pwm/http/servlet/AdminServlet.java
  21. 3 5
      pwm/servlet/src/password/pwm/http/servlet/ConfigGuideServlet.java
  22. 4 4
      pwm/servlet/src/password/pwm/http/servlet/PwmServlet.java
  23. 11 15
      pwm/servlet/src/password/pwm/http/servlet/SetupResponsesServlet.java
  24. 65 0
      pwm/servlet/src/password/pwm/http/servlet/peoplesearch/AttributeDetailBean.java
  25. 25 0
      pwm/servlet/src/password/pwm/http/servlet/peoplesearch/OrgChartData.java
  26. 43 0
      pwm/servlet/src/password/pwm/http/servlet/peoplesearch/OrgChartReferenceBean.java
  27. 270 450
      pwm/servlet/src/password/pwm/http/servlet/peoplesearch/PeopleSearchServlet.java
  28. 19 0
      pwm/servlet/src/password/pwm/http/servlet/peoplesearch/PhotoDataBean.java
  29. 26 0
      pwm/servlet/src/password/pwm/http/servlet/peoplesearch/SearchResultBean.java
  30. 62 0
      pwm/servlet/src/password/pwm/http/servlet/peoplesearch/UserDetailBean.java
  31. 24 0
      pwm/servlet/src/password/pwm/http/servlet/peoplesearch/UserReferenceBean.java
  32. 8 2
      pwm/servlet/src/password/pwm/i18n/Admin.properties
  33. 6 0
      pwm/servlet/src/password/pwm/i18n/Config.properties
  34. 1 1
      pwm/servlet/src/password/pwm/i18n/Error.properties
  35. 5 2
      pwm/servlet/src/password/pwm/util/PwmRandom.java
  36. 15 1
      pwm/servlet/src/password/pwm/util/SecureHelper.java
  37. 1 3
      pwm/servlet/src/password/pwm/util/cli/MainClass.java
  38. 3 0
      pwm/servlet/src/password/pwm/util/stats/Statistic.java
  39. 1 1
      pwm/servlet/src/password/pwm/util/stats/StatisticsManager.java
  40. 0 2
      pwm/servlet/web/WEB-INF/jsp/admin-analysis.jsp
  41. 5 5
      pwm/servlet/web/WEB-INF/jsp/configeditor.jsp
  42. 28 38
      pwm/servlet/web/WEB-INF/jsp/fragment/header-warnings.jsp
  43. 8 7
      pwm/servlet/web/WEB-INF/jsp/helpdesk-detail.jsp
  44. 1 1
      pwm/servlet/web/WEB-INF/jsp/peoplesearch.jsp
  45. 1 3
      pwm/servlet/web/WEB-INF/web.xml
  46. 73 99
      pwm/servlet/web/public/health.jsp
  47. BIN
      pwm/servlet/web/public/resources/UserPhoto.png
  48. 8 4
      pwm/servlet/web/public/resources/configStyle.css
  49. 0 2
      pwm/servlet/web/public/resources/js/admin.js
  50. 18 10
      pwm/servlet/web/public/resources/js/configeditor-settings.js
  51. 11 131
      pwm/servlet/web/public/resources/js/configeditor.js
  52. 33 3
      pwm/servlet/web/public/resources/js/configmanager.js
  53. 103 80
      pwm/servlet/web/public/resources/js/helpdesk.js
  54. 35 20
      pwm/servlet/web/public/resources/js/main.js
  55. 89 64
      pwm/servlet/web/public/resources/js/peoplesearch.js
  56. 26 0
      pwm/servlet/web/public/resources/mobileStyle.css
  57. BIN
      pwm/servlet/web/public/resources/orgChart.png
  58. 111 116
      pwm/servlet/web/public/resources/style.css

+ 3 - 2
pwm/servlet/src/password/pwm/AppProperty.java

@@ -155,7 +155,8 @@ public enum AppProperty {
     PASSWORD_RANDOMGEN_MAX_LENGTH                   ("password.randomGenerator.maxLength"),
     PASSWORD_RANDOMGEN_MAX_LENGTH                   ("password.randomGenerator.maxLength"),
     PASSWORD_RANDOMGEN_JITTER_COUNT                 ("password.randomGenerator.jitter.count"),
     PASSWORD_RANDOMGEN_JITTER_COUNT                 ("password.randomGenerator.jitter.count"),
     PEOPLESEARCH_DISPLAYNAME_USEALLMACROS           ("peoplesearch.displayName.enableAllMacros"),
     PEOPLESEARCH_DISPLAYNAME_USEALLMACROS           ("peoplesearch.displayName.enableAllMacros"),
-    PEOPLESEARCH_MAX_VALUE_COUNT                    ("peoplesearch.maxValueCount"),
+    PEOPLESEARCH_MAX_VALUE_VERIFYUSERDN             ("peoplesearch.values.verifyUserDN"),
+    PEOPLESEARCH_VALUE_MAXCOUNT                     ("peoplesearch.values.maxCount"),
     QUEUE_EMAIL_RETRY_TIMEOUT_MS                    ("queue.email.retryTimeoutMs"),
     QUEUE_EMAIL_RETRY_TIMEOUT_MS                    ("queue.email.retryTimeoutMs"),
     QUEUE_EMAIL_MAX_AGE_MS                          ("queue.email.maxAgeMs"),
     QUEUE_EMAIL_MAX_AGE_MS                          ("queue.email.maxAgeMs"),
     QUEUE_EMAIL_MAX_COUNT                           ("queue.email.maxCount"),
     QUEUE_EMAIL_MAX_COUNT                           ("queue.email.maxCount"),
@@ -166,7 +167,7 @@ public enum AppProperty {
     QUEUE_SYSLOG_MAX_AGE_MS                         ("queue.syslog.maxAgeMs"),
     QUEUE_SYSLOG_MAX_AGE_MS                         ("queue.syslog.maxAgeMs"),
     QUEUE_SYSLOG_MAX_COUNT                          ("queue.syslog.maxCount"),
     QUEUE_SYSLOG_MAX_COUNT                          ("queue.syslog.maxCount"),
     QUEUE_MAX_CLOSE_TIMEOUT_MS                      ("queue.maxCloseTimeoutMs"),
     QUEUE_MAX_CLOSE_TIMEOUT_MS                      ("queue.maxCloseTimeoutMs"),
-    RECAPTCHA_CLIENT_JS_URL("recaptcha.clientJsUrl"),
+    RECAPTCHA_CLIENT_JS_URL                         ("recaptcha.clientJsUrl"),
     RECAPTCHA_CLIENT_IFRAME_URL                     ("recaptcha.clientIframeUrl"),
     RECAPTCHA_CLIENT_IFRAME_URL                     ("recaptcha.clientIframeUrl"),
     RECAPTCHA_VALIDATE_URL                          ("recaptcha.validateUrl"),
     RECAPTCHA_VALIDATE_URL                          ("recaptcha.validateUrl"),
     REPORTING_LDAP_SEARCH_TIMEOUT                   ("reporting.ldap.searchTimeoutMs"),
     REPORTING_LDAP_SEARCH_TIMEOUT                   ("reporting.ldap.searchTimeoutMs"),

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

@@ -140,7 +140,8 @@ password.randomGenerator.maxAttempts=2000
 password.randomGenerator.maxLength=1024
 password.randomGenerator.maxLength=1024
 password.randomGenerator.jitter.count=50
 password.randomGenerator.jitter.count=50
 peoplesearch.displayName.enableAllMacros=false
 peoplesearch.displayName.enableAllMacros=false
-peoplesearch.maxValueCount=100
+peoplesearch.values.verifyUserDN=true
+peoplesearch.values.maxCount=100
 queue.email.retryTimeoutMs=10000
 queue.email.retryTimeoutMs=10000
 queue.email.maxAgeMs=86400000
 queue.email.maxAgeMs=86400000
 queue.email.maxCount=100000
 queue.email.maxCount=100000

+ 63 - 35
pwm/servlet/src/password/pwm/PwmApplication.java

@@ -27,6 +27,7 @@ import com.novell.ldapchai.ChaiFactory;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiProvider;
+import password.pwm.bean.AboutApplicationBean;
 import password.pwm.bean.SmsItemBean;
 import password.pwm.bean.SmsItemBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
 import password.pwm.config.Configuration;
@@ -39,6 +40,7 @@ import password.pwm.event.AuditEvent;
 import password.pwm.event.AuditManager;
 import password.pwm.event.AuditManager;
 import password.pwm.event.SystemAuditRecord;
 import password.pwm.event.SystemAuditRecord;
 import password.pwm.health.HealthMonitor;
 import password.pwm.health.HealthMonitor;
+import password.pwm.http.servlet.AdminServlet;
 import password.pwm.ldap.LdapConnectionService;
 import password.pwm.ldap.LdapConnectionService;
 import password.pwm.token.TokenService;
 import password.pwm.token.TokenService;
 import password.pwm.util.*;
 import password.pwm.util.*;
@@ -229,7 +231,29 @@ public class PwmApplication {
         LOGGER.info(logEnvironment());
         LOGGER.info(logEnvironment());
         LOGGER.info(logDebugInfo());
         LOGGER.info(logDebugInfo());
 
 
+        initServices();
+
+        final TimeDuration totalTime = TimeDuration.fromCurrent(startTime);
+        LOGGER.info(PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION + " open for bidness! (" + totalTime.asCompactString() + ")");
+        StatisticsManager.incrementStat(this,Statistic.PWM_STARTUPS);
+        LOGGER.debug("buildTime=" + PwmConstants.BUILD_TIME + ", javaLocale=" + Locale.getDefault() + ", DefaultLocale=" + PwmConstants.DEFAULT_LOCALE);
+
+        final Thread postInitThread = new Thread(){
+            @Override
+            public void run() {
+                postInitTasks();
+            }
+        };
+        postInitThread.setDaemon(true);
+        postInitThread.setName(Helper.makeThreadName(this, PwmApplication.class));
+        postInitThread.start();
+    }
+
+    private void initServices()
+            throws PwmUnrecoverableException
+    {
         for (final Class<? extends PwmService> serviceClass : PWM_SERVICE_CLASSES) {
         for (final Class<? extends PwmService> serviceClass : PWM_SERVICE_CLASSES) {
+            final Date startTime = new Date();
             final PwmService newServiceInstance;
             final PwmService newServiceInstance;
             try {
             try {
                 final Object newInstance = serviceClass.newInstance();
                 final Object newInstance = serviceClass.newInstance();
@@ -243,7 +267,7 @@ public class PwmApplication {
             try {
             try {
                 LOGGER.debug("initializing service " + serviceClass.getName());
                 LOGGER.debug("initializing service " + serviceClass.getName());
                 newServiceInstance.init(this);
                 newServiceInstance.init(this);
-                LOGGER.debug("initialization of service " + serviceClass.getName() + " has completed successfully");
+                LOGGER.debug("completed initialization of service " + serviceClass.getName() + " in " + TimeDuration.fromCurrent(startTime).asCompactString() + ", status=" + newServiceInstance.status());
             } catch (PwmException e) {
             } catch (PwmException e) {
                 LOGGER.warn("error instantiating service class '" + serviceClass.getName() + "', service will remain unavailable, error: " + e.getMessage());
                 LOGGER.warn("error instantiating service class '" + serviceClass.getName() + "', service will remain unavailable, error: " + e.getMessage());
             } catch (Exception e) {
             } catch (Exception e) {
@@ -256,12 +280,10 @@ public class PwmApplication {
             }
             }
             pwmServices.put(serviceClass,newServiceInstance);
             pwmServices.put(serviceClass,newServiceInstance);
         }
         }
+    }
 
 
-        final TimeDuration totalTime = TimeDuration.fromCurrent(startTime);
-        LOGGER.info(PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION + " open for bidness! (" + totalTime.asCompactString() + ")");
-        getStatisticsManager().incrementValue(Statistic.PWM_STARTUPS);
-        LOGGER.debug("buildTime=" + PwmConstants.BUILD_TIME + ", javaLocale=" + Locale.getDefault() + ", DefaultLocale=" + PwmConstants.DEFAULT_LOCALE );
-
+    private void postInitTasks() {
+        final Date startTime = new Date();
         // detect if config has been modified since previous startup
         // detect if config has been modified since previous startup
         try {
         try {
             final String previousHash = readAppAttribute(AppAttribute.CONFIG_HASH);
             final String previousHash = readAppAttribute(AppAttribute.CONFIG_HASH);
@@ -306,6 +328,15 @@ public class PwmApplication {
         } catch (PwmException e) {
         } catch (PwmException e) {
             LOGGER.warn("unable to submit alert event " + JsonUtil.serialize(auditRecord));
             LOGGER.warn("unable to submit alert event " + JsonUtil.serialize(auditRecord));
         }
         }
+
+        try {
+            AboutApplicationBean aboutApplicationBean = AdminServlet.makeInfoBean(this);
+            LOGGER.trace("application info: " + JsonUtil.serialize(aboutApplicationBean));
+        } catch (Exception e) {
+            LOGGER.error("error generating about application bean: " + e.getMessage());
+        }
+
+        LOGGER.trace("completed post init tasks in " + TimeDuration.fromCurrent(startTime).asCompactString());
     }
     }
 
 
     public String getInstanceID() {
     public String getInstanceID() {
@@ -465,7 +496,7 @@ public class PwmApplication {
 
 
         envStats.put("memmax",Runtime.getRuntime().maxMemory());
         envStats.put("memmax",Runtime.getRuntime().maxMemory());
         envStats.put("threads",Thread.activeCount());
         envStats.put("threads",Thread.activeCount());
-        envStats.put("chaiApi",ChaiConstant.CHAI_API_VERSION + ", b" + ChaiConstant.CHAI_API_BUILD_INFO);
+        envStats.put("chaiApi", ChaiConstant.CHAI_API_VERSION + ", b" + ChaiConstant.CHAI_API_BUILD_INFO);
 
 
         return "environment info: " + JsonUtil.serializeMap(envStats);
         return "environment info: " + JsonUtil.serializeMap(envStats);
     }
     }
@@ -719,28 +750,30 @@ public class PwmApplication {
             LOGGER.trace("applicationPath appears to be servlet /WEB-INF directory");
             LOGGER.trace("applicationPath appears to be servlet /WEB-INF directory");
         }
         }
 
 
-        final File infoFile = new File(webInfPath.getAbsolutePath() + File.separator + PwmConstants.APPLICATION_PATH_INFO_FILE);
-        if (applicationPathIsWebInfPath) {
-            if (pwmEnvironment.applicationPathType == PwmEnvironment.ApplicationPathType.derived) {
-                LOGGER.trace("checking " + infoFile.getAbsolutePath() + " status, (applicationPathType=" + PwmEnvironment.ApplicationPathType.derived + ")");
-                if (infoFile.exists()) {
-                    final String errorMsg = "The file " + infoFile.getAbsolutePath() + " exists, and an applicationPath was not explicitly specified."
-                            + "  This happens when an applicationPath was previously configured, but is not now being specified."
-                            + "  An explicit applicationPath parameter must be specified, or the file can be removed if the applicationPath should be changed to the default /WEB-INF directory.";
-                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_STARTUP_ERROR, errorMsg));
-                } else {
-                    LOGGER.trace("marker file " + infoFile.getAbsolutePath() + " does not exist");
+        if (webInfPath != null) {
+            final File infoFile = new File(webInfPath.getAbsolutePath() + File.separator + PwmConstants.APPLICATION_PATH_INFO_FILE);
+            if (applicationPathIsWebInfPath) {
+                if (pwmEnvironment.applicationPathType == PwmEnvironment.ApplicationPathType.derived) {
+                    LOGGER.trace("checking " + infoFile.getAbsolutePath() + " status, (applicationPathType=" + PwmEnvironment.ApplicationPathType.derived + ")");
+                    if (infoFile.exists()) {
+                        final String errorMsg = "The file " + infoFile.getAbsolutePath() + " exists, and an applicationPath was not explicitly specified."
+                                + "  This happens when an applicationPath was previously configured, but is not now being specified."
+                                + "  An explicit applicationPath parameter must be specified, or the file can be removed if the applicationPath should be changed to the default /WEB-INF directory.";
+                        throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_STARTUP_ERROR, errorMsg));
+                    } else {
+                        LOGGER.trace("marker file " + infoFile.getAbsolutePath() + " does not exist");
+                    }
                 }
                 }
-            }
-        } else {
-            if (pwmEnvironment.applicationPathType == PwmEnvironment.ApplicationPathType.specified) {
-                try {
-                    final FileOutputStream fos = new FileOutputStream(infoFile);
-                    final Properties outputProperties = new Properties();
-                    outputProperties.setProperty("lastApplicationPath", applicationPath.getAbsolutePath());
-                    outputProperties.store(fos, "Marker file to record a previously specified applicationPath");
-                } catch (IOException e) {
-                    LOGGER.warn("unable to write applicationPath marker properties file " + infoFile.getAbsolutePath() + "");
+            } else {
+                if (pwmEnvironment.applicationPathType == PwmEnvironment.ApplicationPathType.specified) {
+                    try {
+                        final FileOutputStream fos = new FileOutputStream(infoFile);
+                        final Properties outputProperties = new Properties();
+                        outputProperties.setProperty("lastApplicationPath", applicationPath.getAbsolutePath());
+                        outputProperties.store(fos, "Marker file to record a previously specified applicationPath");
+                    } catch (IOException e) {
+                        LOGGER.warn("unable to write applicationPath marker properties file " + infoFile.getAbsolutePath() + "");
+                    }
                 }
                 }
             }
             }
         }
         }
@@ -761,9 +794,9 @@ public class PwmApplication {
             specified,
             specified,
         }
         }
 
 
-        public PwmEnvironment setConfig(Configuration config) {
+        public PwmEnvironment(Configuration config, File applicationPath) {
             this.config = config;
             this.config = config;
-            return this;
+            this.applicationPath = applicationPath;
         }
         }
 
 
         public PwmEnvironment setApplicationMode(MODE applicationMode) {
         public PwmEnvironment setApplicationMode(MODE applicationMode) {
@@ -771,11 +804,6 @@ public class PwmApplication {
             return this;
             return this;
         }
         }
 
 
-        public PwmEnvironment setApplicationPath(File applicationPath) {
-            this.applicationPath = applicationPath;
-            return this;
-        }
-
         public PwmEnvironment setInitLogging(boolean initLogging) {
         public PwmEnvironment setInitLogging(boolean initLogging) {
             this.initLogging = initLogging;
             this.initLogging = initLogging;
             return this;
             return this;

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

@@ -46,5 +46,5 @@ pwmDBLoggerMaxQueueSize=50000
 pwmDBLoggerMaxDirtyBufferMS=500
 pwmDBLoggerMaxDirtyBufferMS=500
 enableEulaDisplay=false
 enableEulaDisplay=false
 databaseAccessor.keyLength=128
 databaseAccessor.keyLength=128
-missingVersionString=[Missing_Version_#]
+missingVersionString=[Version Missing]
 applicationPathInfoFile=applicationPath.properties
 applicationPathInfoFile=applicationPath.properties

+ 94 - 1
pwm/servlet/src/password/pwm/bean/AboutApplicationBean.java

@@ -60,7 +60,83 @@ public class AboutApplicationBean implements Serializable {
     
     
     private int configurationRestartCounter;
     private int configurationRestartCounter;
 
 
-    private JavaInformation javaInformation = new JavaInformation();
+    private JavaInformation javaInformation;
+    private BuildInformation buildInformation;
+
+    public static class BuildInformation implements Serializable {
+        private String buildTime;
+        private String buildNumber;
+        private String buildType;
+        private String buildUser;
+        private String buildRevision;
+        private String buildJavaVendor;
+        private String buildJavaVersion;
+        private String buildVersion;
+
+        public String getBuildTime() {
+            return buildTime;
+        }
+
+        public void setBuildTime(String buildTime) {
+            this.buildTime = buildTime;
+        }
+
+        public String getBuildNumber() {
+            return buildNumber;
+        }
+
+        public void setBuildNumber(String buildNumber) {
+            this.buildNumber = buildNumber;
+        }
+
+        public String getBuildType() {
+            return buildType;
+        }
+
+        public void setBuildType(String buildType) {
+            this.buildType = buildType;
+        }
+
+        public String getBuildUser() {
+            return buildUser;
+        }
+
+        public void setBuildUser(String buildUser) {
+            this.buildUser = buildUser;
+        }
+
+        public String getBuildRevision() {
+            return buildRevision;
+        }
+
+        public void setBuildRevision(String buildRevision) {
+            this.buildRevision = buildRevision;
+        }
+
+        public String getBuildJavaVendor() {
+            return buildJavaVendor;
+        }
+
+        public void setBuildJavaVendor(String buildJavaVendor) {
+            this.buildJavaVendor = buildJavaVendor;
+        }
+
+        public String getBuildJavaVersion() {
+            return buildJavaVersion;
+        }
+
+        public void setBuildJavaVersion(String buildJavaVersion) {
+            this.buildJavaVersion = buildJavaVersion;
+        }
+
+        public String getBuildVersion() {
+            return buildVersion;
+        }
+
+        public void setBuildVersion(String buildVersion) {
+            this.buildVersion = buildVersion;
+        }
+    }
 
 
     public static class JavaInformation implements Serializable {
     public static class JavaInformation implements Serializable {
         private long memoryFree;
         private long memoryFree;
@@ -74,6 +150,7 @@ public class AboutApplicationBean implements Serializable {
         private String vmName;
         private String vmName;
         private String osName;
         private String osName;
         private String osVersion;
         private String osVersion;
+        private String randomAlgorithm;
 
 
         public JavaInformation() {
         public JavaInformation() {
         }
         }
@@ -165,6 +242,14 @@ public class AboutApplicationBean implements Serializable {
         public void setOsVersion(String osVersion) {
         public void setOsVersion(String osVersion) {
             this.osVersion = osVersion;
             this.osVersion = osVersion;
         }
         }
+
+        public String getRandomAlgorithm() {
+            return randomAlgorithm;
+        }
+
+        public void setRandomAlgorithm(String randomAlgorithm) {
+            this.randomAlgorithm = randomAlgorithm;
+        }
     }
     }
     
     
     ///////////////////////////////
     ///////////////////////////////
@@ -368,4 +453,12 @@ public class AboutApplicationBean implements Serializable {
     public void setJavaInformation(JavaInformation javaInformation) {
     public void setJavaInformation(JavaInformation javaInformation) {
         this.javaInformation = javaInformation;
         this.javaInformation = javaInformation;
     }
     }
+
+    public BuildInformation getBuildInformation() {
+        return buildInformation;
+    }
+
+    public void setBuildInformation(BuildInformation buildInformation) {
+        this.buildInformation = buildInformation;
+    }
 }
 }

+ 2 - 4
pwm/servlet/src/password/pwm/config/PwmSetting.java

@@ -792,12 +792,12 @@ public enum PwmSetting {
             "peopleSearch.useProxy", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.PEOPLE_SEARCH),
             "peopleSearch.useProxy", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_DISPLAY_NAME(
     PEOPLE_SEARCH_DISPLAY_NAME(
             "peopleSearch.displayName.user", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
             "peopleSearch.displayName.user", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
+    PEOPLE_SEARCH_DISPLAY_NAMES_CARD_LABELS(
+            "peopleSearch.displayName.cardLabels", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_PHOTO_ATTRIBUTE(
     PEOPLE_SEARCH_PHOTO_ATTRIBUTE(
             "peopleSearch.photo.ldapAttribute", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
             "peopleSearch.photo.ldapAttribute", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_PHOTO_URL_OVERRIDE(
     PEOPLE_SEARCH_PHOTO_URL_OVERRIDE(
             "peopleSearch.photo.urlOverride", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
             "peopleSearch.photo.urlOverride", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
-    PEOPLE_SEARCH_PHOTO_STYLE_ATTR(
-            "peopleSearch.photo.style", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_MAX_CACHE_SECONDS(
     PEOPLE_SEARCH_MAX_CACHE_SECONDS(
             "peopleSearch.maxCacheSeconds", PwmSettingSyntax.DURATION, PwmSettingCategory.PEOPLE_SEARCH),
             "peopleSearch.maxCacheSeconds", PwmSettingSyntax.DURATION, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_PHOTO_QUERY_FILTER(
     PEOPLE_SEARCH_PHOTO_QUERY_FILTER(
@@ -814,8 +814,6 @@ public enum PwmSetting {
             "peopleSearch.orgChart.parentAttribute", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
             "peopleSearch.orgChart.parentAttribute", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_ORGCHART_CHILD_ATTRIBUTE(
     PEOPLE_SEARCH_ORGCHART_CHILD_ATTRIBUTE(
             "peopleSearch.orgChart.childAttribute", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
             "peopleSearch.orgChart.childAttribute", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
-    PEOPLE_SEARCH_ORGCHART_DISPLAY_VALUES(
-            "peopleSearch.orgChart.displayValues", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.PEOPLE_SEARCH),
 
 
 
 
 
 

+ 16 - 22
pwm/servlet/src/password/pwm/config/PwmSetting.xml

@@ -1839,7 +1839,7 @@
     </setting>
     </setting>
     <setting key="audit.syslog.servers" level="1" required="false">
     <setting key="audit.syslog.servers" level="1" required="false">
         <label>Syslog Audit Server</label>
         <label>Syslog Audit Server</label>
-        <description><![CDATA[Syslog audit server.  When configured, all audit events will be forwarded to the specified syslog server.  The format is <b>&lt;protocol&gt;</b>,<b>&lt;address&gt;</b>,<b>&lt;port&gt;</b>.  The value for <b>&lt;protocol&gt;</b> can be either <i>udp</i> or <i>tcp</i>.<br/><br/>Examples:<table><tr><td>Protocol</td><td>Address</td><td>Port</td><td>Setting</td><tr><tr><td>UDP</td><td>127.0.0.1</td><td>514</td><td>udp,127.0.0.1,514</td><tr><tr><td>TCP</td><td>central-syslog.example.com</td><td>514</td><td>tcp,central-syslog.example.com,514</td><tr></table>]]></description>
+        <description><![CDATA[Syslog audit server.  When configured, all audit events will be forwarded to the specified syslog server.  The format is <b>&lt;protocol&gt;</b>,<b>&lt;address&gt;</b>,<b>&lt;port&gt;</b>.  The value for <b>&lt;protocol&gt;</b> can be either <i>udp</i> or <i>tcp</i>.<br/><br/>Examples:<table><tr><td>Protocol</td><td>Address</td><td>Port</td><td>Setting</td><tr><tr><td>UDP</td><td>127.0.0.1</td><td>514</td><td>udp,127.0.0.1,514</td><tr><tr><td>TCP</td><td>central-syslog.example.com</td><td>514</td><td>tcp,central-syslog.example.com,514</td><tr><tr><td>TLS</td><td>secure-syslog.example.com</td><td>6514</td><td>tls,central-syslog.example.com,6514</td><tr></table>]]></description>
         <default>
         <default>
             <value/>
             <value/>
         </default>
         </default>
@@ -2994,9 +2994,22 @@
             <value>false</value>
             <value>false</value>
         </default>
         </default>
     </setting>
     </setting>
+    <setting key="peopleSearch.displayName.cardLabels" level="1">
+        <label>Display Labels</label>
+        <description><![CDATA[Display labels for user panel in PeopleSearch detail and on org chart.  LDAP Attribute value <b>@LDAP:x@</b> macros can be used.  A maximum of four values can be set.]]></description>
+        <default>
+            <value>@LDAP:givenName@ @LDAP:sn@</value>
+            <value>@LDAP:title@</value>
+            <value>@LDAP:mail@</value>
+            <value>@LDAP:telephoneNumber@</value>
+        </default>
+        <options>
+            <option value="max">4</option>
+        </options>
+    </setting>
     <setting key="peopleSearch.displayName.user" level="1" required="true">
     <setting key="peopleSearch.displayName.user" level="1" required="true">
-        <label>User Name Display</label>
-        <description><![CDATA[Display name for user in PeopleSearch detail.  May use <b>@LDAP:x@</b> macros, but other macros will not work.]]></description>
+        <label>UserDN Name Display</label>
+        <description><![CDATA[Display name for user in PeopleSearch detail.  LDAP Attribute value <b>@LDAP:x@</b> macros can be used.]]></description>
         <default>
         <default>
             <value>@LDAP:givenName@ @LDAP:sn@ - @LDAP:title@</value>
             <value>@LDAP:givenName@ @LDAP:sn@ - @LDAP:title@</value>
         </default>
         </default>
@@ -3013,13 +3026,6 @@
         <description><![CDATA[If the user's photo is not stored in ldap, this URL can be specified to load the photo for the user detail.  May use <b>@LDAP:x@</b> macros, but other macros will not work.  If this setting is specified, the photo will not be loaded from the LDAP directory.]]></description>
         <description><![CDATA[If the user's photo is not stored in ldap, this URL can be specified to load the photo for the user detail.  May use <b>@LDAP:x@</b> macros, but other macros will not work.  If this setting is specified, the photo will not be loaded from the LDAP directory.]]></description>
         <default/>
         <default/>
     </setting>
     </setting>
-    <setting key="peopleSearch.photo.style" level="2">
-        <label>LDAP Photo HTML Style Attribute</label>
-        <description><![CDATA[When displaying the photo, the value for this attribute will be used to specify the HTML <i>&lt;img&gt;</i> tag's <i>style</i> attribute.  Any valid CSS style attribute can be used.  Typically this is used to specify the height and width of the photo such as "<i>height: 100px; width: 100px</i>".]]></description>
-        <default>
-            <value>height: 100px; width: 100px</value>
-        </default>
-    </setting>
     <setting key="peopleSearch.maxCacheSeconds" level="2">
     <setting key="peopleSearch.maxCacheSeconds" level="2">
         <label>PeopleSearch Maximum Cache Seconds</label>
         <label>PeopleSearch Maximum Cache Seconds</label>
         <description><![CDATA[]]></description>
         <description><![CDATA[]]></description>
@@ -3055,18 +3061,6 @@
             <value>directReports</value>
             <value>directReports</value>
         </default>
         </default>
     </setting>
     </setting>
-    <setting key="peopleSearch.orgChart.displayValues" level="1">
-        <label>Org Chart Display Labels</label>
-        <description/>
-        <default>
-            <value>@LDAP:givenName@ @LDAP:sn@</value>
-            <value>@LDAP:title@</value>
-            <value>@LDAP:mail@</value>
-        </default>
-        <options>
-            <option value="max">3</option>
-        </options>
-    </setting>
     <setting key="ldap.edirectory.enableNmas" level="1" required="true">
     <setting key="ldap.edirectory.enableNmas" level="1" required="true">
         <label>Enable NMAS Extensions</label>
         <label>Enable NMAS Extensions</label>
         <description><![CDATA[When connecting to a NetIQ eDirectory LDAP directory, this parameter will control if NMAS extensions will be used when connecting to the ldap directory.  Enabling nmas results in:<ul><li>better error messages when using universal password policies</li><li>better error handling during certain change password scenarios</li></ul>Unless you are using an older version of eDirectory (pre 8.8 or before), it is generally best to set this to true.<br/><br/>All NMAS operations require an SSL connection to the directory.]]></description>
         <description><![CDATA[When connecting to a NetIQ eDirectory LDAP directory, this parameter will control if NMAS extensions will be used when connecting to the ldap directory.  Enabling nmas results in:<ul><li>better error messages when using universal password policies</li><li>better error handling during certain change password scenarios</li></ul>Unless you are using an older version of eDirectory (pre 8.8 or before), it is generally best to set this to true.<br/><br/>All NMAS operations require an SSL connection to the directory.]]></description>

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

@@ -116,7 +116,7 @@ public class StoredConfiguration implements Serializable {
     private ChangeLog changeLog = new ChangeLog();
     private ChangeLog changeLog = new ChangeLog();
 
 
     private boolean locked = false;
     private boolean locked = false;
-    private boolean setting_writeLabels = false;
+    private boolean setting_writeLabels = true;
     private final ReentrantReadWriteLock domModifyLock = new ReentrantReadWriteLock();
     private final ReentrantReadWriteLock domModifyLock = new ReentrantReadWriteLock();
 
 
 // -------------------------- STATIC METHODS --------------------------
 // -------------------------- STATIC METHODS --------------------------

+ 1 - 3
pwm/servlet/src/password/pwm/config/function/UserMatchViewerFunction.java

@@ -73,10 +73,8 @@ public class UserMatchViewerFunction implements SettingUIFunction {
             throws Exception
             throws Exception
     {
     {
         final Configuration config = new Configuration(storedConfiguration);
         final Configuration config = new Configuration(storedConfiguration);
-        final PwmApplication tempApplication = new PwmApplication.PwmEnvironment()
-                .setConfig(config)
+        final PwmApplication tempApplication = new PwmApplication.PwmEnvironment(config,pwmApplication.getApplicationPath())
                 .setApplicationMode(PwmApplication.MODE.CONFIGURATION)
                 .setApplicationMode(PwmApplication.MODE.CONFIGURATION)
-                .setApplicationPath(pwmApplication.getApplicationPath())
                 .setInitLogging(false)
                 .setInitLogging(false)
                 .setConfigurationFile(null)
                 .setConfigurationFile(null)
                 .setWebInfPath(pwmApplication.getWebInfPath())
                 .setWebInfPath(pwmApplication.getWebInfPath())

+ 1 - 1
pwm/servlet/src/password/pwm/config/value/PasswordValue.java

@@ -96,7 +96,7 @@ public class PasswordValue implements StoredValue {
                 } else {
                 } else {
                     try {
                     try {
                         final SecretKey secretKey = SecureHelper.makeKey(key);
                         final SecretKey secretKey = SecureHelper.makeKey(key);
-                        newPasswordValue.value = new PasswordData(SecureHelper.decryptStringValue(rawValue, secretKey));
+                        newPasswordValue.value = new PasswordData(SecureHelper.decryptStringValue(rawValue, secretKey, false, SecureHelper.BlockAlgorithm.CONFIG));
                         return newPasswordValue;
                         return newPasswordValue;
                     } catch (Exception e) {
                     } catch (Exception e) {
                         final String errorMsg = "unable to decode encrypted password value for setting: " + e.getMessage();
                         final String errorMsg = "unable to decode encrypted password value for setting: " + e.getMessage();

+ 2 - 2
pwm/servlet/src/password/pwm/event/AuditManager.java

@@ -387,7 +387,7 @@ public class AuditManager implements PwmService {
         final String jsonRecord = JsonUtil.serialize(auditRecord);
         final String jsonRecord = JsonUtil.serialize(auditRecord);
 
 
         if (status != STATUS.OPEN) {
         if (status != STATUS.OPEN) {
-            LOGGER.warn("discarding audit event (AuditManager is not open); event=" + jsonRecord);
+            LOGGER.debug("discarding audit event (AuditManager is not open); event=" + jsonRecord);
             return;
             return;
         }
         }
 
 
@@ -397,7 +397,7 @@ public class AuditManager implements PwmService {
         }
         }
 
 
         if (!settings.permittedEvents.contains(auditRecord.getEventCode())) {
         if (!settings.permittedEvents.contains(auditRecord.getEventCode())) {
-            LOGGER.warn("discarding audit event, '" + auditRecord.getEventCode() + "', are being ignored; event=" + jsonRecord);
+            LOGGER.debug("discarding event, " + auditRecord.getEventCode() + " are being ignored; event=" + jsonRecord);
             return;
             return;
         }
         }
 
 

+ 1 - 4
pwm/servlet/src/password/pwm/health/LDAPStatusChecker.java

@@ -546,12 +546,9 @@ public class LDAPStatusChecker implements HealthChecker {
     )
     )
                 throws PwmUnrecoverableException
                 throws PwmUnrecoverableException
     {
     {
-        final PwmApplication tempApplication = new PwmApplication.PwmEnvironment()
-                .setConfig(config)
+        final PwmApplication tempApplication = new PwmApplication.PwmEnvironment(config, pwmApplication.getApplicationPath())
                 .setApplicationMode(PwmApplication.MODE.NEW)
                 .setApplicationMode(PwmApplication.MODE.NEW)
-                .setApplicationPath(pwmApplication.getApplicationPath())
                 .setInitLogging(false)
                 .setInitLogging(false)
-                .setConfigurationFile(null)
                 .setWebInfPath(pwmApplication.getWebInfPath())
                 .setWebInfPath(pwmApplication.getWebInfPath())
                 .createPwmApplication();
                 .createPwmApplication();
 
 

+ 1 - 3
pwm/servlet/src/password/pwm/http/ContextManager.java

@@ -186,10 +186,8 @@ public class ContextManager implements Serializable {
 
 
 
 
         try {
         try {
-            pwmApplication = new PwmApplication.PwmEnvironment()
-                    .setConfig(configuration)
+            pwmApplication = new PwmApplication.PwmEnvironment(configuration, applicationPath)
                     .setApplicationMode(mode)
                     .setApplicationMode(mode)
-                    .setApplicationPath(applicationPath)
                     .setInitLogging(true)
                     .setInitLogging(true)
                     .setConfigurationFile(configurationFile)
                     .setConfigurationFile(configurationFile)
                     .setWebInfPath(webInfPath)
                     .setWebInfPath(webInfPath)

+ 14 - 4
pwm/servlet/src/password/pwm/http/HttpMethod.java

@@ -1,13 +1,19 @@
 package password.pwm.http;
 package password.pwm.http;
 
 
 public enum HttpMethod {
 public enum HttpMethod {
-    POST,
-    GET,
-    DELETE,
-    PUT,
+    POST(false),
+    GET(true),
+    DELETE(false),
+    PUT(false),
 
 
     ;
     ;
 
 
+    private final boolean idempotent;
+
+    HttpMethod(boolean idempotent) {
+        this.idempotent = idempotent;
+    }
+
     public static HttpMethod fromString(final String input) {
     public static HttpMethod fromString(final String input) {
         for (final HttpMethod method : HttpMethod.values()) {
         for (final HttpMethod method : HttpMethod.values()) {
             if (method.toString().equalsIgnoreCase(input)) {
             if (method.toString().equalsIgnoreCase(input)) {
@@ -16,4 +22,8 @@ public enum HttpMethod {
         }
         }
         return null;
         return null;
     }
     }
+
+    public boolean isIdempotent() {
+        return idempotent;
+    }
 }
 }

+ 10 - 0
pwm/servlet/src/password/pwm/http/PwmHttpRequestWrapper.java

@@ -166,6 +166,11 @@ public abstract class PwmHttpRequestWrapper {
         return returnValue == null || returnValue.isEmpty() ? valueIfNotPresent : returnValue;
         return returnValue == null || returnValue.isEmpty() ? valueIfNotPresent : returnValue;
     }
     }
 
 
+    public boolean hasParameter(final String name)
+            throws PwmUnrecoverableException {
+        return this.getHttpServletRequest().getParameterMap().containsKey(name);
+    }
+
     public String readParameterAsString(final String name)
     public String readParameterAsString(final String name)
             throws PwmUnrecoverableException {
             throws PwmUnrecoverableException {
         final int maxLength = Integer.parseInt(configuration.readAppProperty(AppProperty.HTTP_PARAM_MAX_READ_LENGTH));
         final int maxLength = Integer.parseInt(configuration.readAppProperty(AppProperty.HTTP_PARAM_MAX_READ_LENGTH));
@@ -303,5 +308,10 @@ public abstract class PwmHttpRequestWrapper {
         }
         }
         return decodedValue;
         return decodedValue;
     }
     }
+
+    public HttpMethod getMethod() {
+        return HttpMethod.fromString(this.getHttpServletRequest().getMethod());
+    }
+
 }
 }
 
 

+ 11 - 7
pwm/servlet/src/password/pwm/http/PwmRequest.java

@@ -375,13 +375,7 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
 
 
     public void markPreLoginUrl()
     public void markPreLoginUrl()
     {
     {
-        String originalRequestedUrl = this.getHttpServletRequest().getRequestURL().toString();
-        {
-            final String queryString = this.getHttpServletRequest().getQueryString();
-            if (queryString != null && queryString.length() > 0) {
-                originalRequestedUrl += "?" + queryString;
-            }
-        }
+        final String originalRequestedUrl = this.getURLwithQueryString();
         if (pwmSession.getSessionStateBean().getOriginalRequestURL() == null) {
         if (pwmSession.getSessionStateBean().getOriginalRequestURL() == null) {
             LOGGER.trace(this, "noted originally requested url as: " + originalRequestedUrl);
             LOGGER.trace(this, "noted originally requested url as: " + originalRequestedUrl);
             pwmSession.getSessionStateBean().setOriginalRequestURL(originalRequestedUrl);
             pwmSession.getSessionStateBean().setOriginalRequestURL(originalRequestedUrl);
@@ -589,4 +583,14 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
     public void invalidateSession() {
     public void invalidateSession() {
         this.getHttpServletRequest().getSession().invalidate();
         this.getHttpServletRequest().getSession().invalidate();
     }
     }
+
+    public String getURLwithQueryString() {
+        final HttpServletRequest req = this.getHttpServletRequest();
+        final String queryString = req.getQueryString();
+        if (queryString != null && !queryString.isEmpty()) {
+            return req.getRequestURI() + '?' + queryString;
+        } else {
+            return req.getRequestURI();
+        }
+    }
 }
 }

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

@@ -184,18 +184,13 @@ public class PwmHttpClient {
         // set up a TrustManager that trusts everything
         // set up a TrustManager that trusts everything
         sslContext.init(null, new TrustManager[]{new X509TrustManager() {
         sslContext.init(null, new TrustManager[]{new X509TrustManager() {
             public X509Certificate[] getAcceptedIssuers() {
             public X509Certificate[] getAcceptedIssuers() {
-                System.out.println("getAcceptedIssuers =============");
                 return new X509Certificate[0];
                 return new X509Certificate[0];
             }
             }
 
 
-            public void checkClientTrusted(X509Certificate[] certs,
-                                           String authType) {
-                System.out.println("checkClientTrusted =============");
+            public void checkClientTrusted(X509Certificate[] certs, String authType) {
             }
             }
 
 
-            public void checkServerTrusted(X509Certificate[] certs,
-                                           String authType) {
-                System.out.println("checkServerTrusted =============");
+            public void checkServerTrusted(X509Certificate[] certs, String authType) {
             }
             }
         }}, new SecureRandom());
         }}, new SecureRandom());
 
 

+ 2 - 12
pwm/servlet/src/password/pwm/http/filter/AuthenticationFilter.java

@@ -264,7 +264,7 @@ public class AuthenticationFilter extends AbstractPwmFilter {
         }
         }
 
 
         //store the original requested url
         //store the original requested url
-        final String originalRequestedUrl = figureOriginalRequestUrl(pwmRequest);
+        final String originalRequestedUrl = pwmRequest.getURLwithQueryString();
 
 
         pwmRequest.markPreLoginUrl();
         pwmRequest.markPreLoginUrl();
 
 
@@ -413,7 +413,7 @@ public class AuthenticationFilter extends AbstractPwmFilter {
             return false;
             return false;
         }
         }
 
 
-        final String originalURL = figureOriginalRequestUrl(pwmRequest);
+        final String originalURL = pwmRequest.getURLwithQueryString();
 
 
         final String state = OAuthConsumerServlet.makeStateStringForRequest(pwmRequest, originalURL);
         final String state = OAuthConsumerServlet.makeStateStringForRequest(pwmRequest, originalURL);
         final String redirectUri = OAuthConsumerServlet.figureOauthSelfEndPointUrl(pwmRequest);
         final String redirectUri = OAuthConsumerServlet.figureOauthSelfEndPointUrl(pwmRequest);
@@ -519,15 +519,5 @@ public class AuthenticationFilter extends AbstractPwmFilter {
 
 
         return false;
         return false;
     }
     }
-
-    private static String figureOriginalRequestUrl(final PwmRequest pwmRequest) {
-        final HttpServletRequest req = pwmRequest.getHttpServletRequest();
-        final String queryString = req.getQueryString();
-        if (queryString != null && !queryString.isEmpty()) {
-            return req.getRequestURI() + '?' + queryString;
-        } else {
-            return req.getRequestURI();
-        }
-    }
 }
 }
 
 

+ 8 - 0
pwm/servlet/src/password/pwm/http/filter/SessionFilter.java

@@ -245,6 +245,14 @@ public class SessionFilter extends AbstractPwmFilter {
         final HttpServletRequest req = pwmRequest.getHttpServletRequest();
         final HttpServletRequest req = pwmRequest.getHttpServletRequest();
         final PwmResponse pwmResponse = pwmRequest.getPwmResponse();
         final PwmResponse pwmResponse = pwmRequest.getPwmResponse();
 
 
+        if (!pwmRequest.getMethod().isIdempotent() && pwmRequest.hasParameter(PwmConstants.PARAM_FORM_ID)) {
+            final String errorMsg = "session is unvalidated and cannot be validated during a " + pwmRequest.getMethod().toString() + " request";
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_INVALID_FORMID,errorMsg);
+            pwmRequest.respondWithError(errorInformation);
+            LOGGER.debug(pwmRequest,errorInformation);
+            return true;
+        }
+
         if (pwmRequest.getURL().isCommandServletURL()) {
         if (pwmRequest.getURL().isCommandServletURL()) {
             return false;
             return false;
         }
         }

+ 18 - 2
pwm/servlet/src/password/pwm/http/servlet/AdminServlet.java

@@ -35,6 +35,7 @@ import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.bean.AdminBean;
 import password.pwm.http.bean.AdminBean;
 import password.pwm.util.Helper;
 import password.pwm.util.Helper;
+import password.pwm.util.PwmRandom;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.report.ReportService;
 import password.pwm.util.report.ReportService;
 import password.pwm.util.stats.StatisticsManager;
 import password.pwm.util.stats.StatisticsManager;
@@ -278,9 +279,9 @@ public class AdminServlet extends PwmServlet {
         aboutBean.setLocalDbStorageSize(Helper.formatDiskSize(Helper.getFileDirectorySize(pwmApplication.getLocalDB().getFileLocation())));
         aboutBean.setLocalDbStorageSize(Helper.formatDiskSize(Helper.getFileDirectorySize(pwmApplication.getLocalDB().getFileLocation())));
         aboutBean.setLocalDbFreeSpace(Helper.formatDiskSize(Helper.diskSpaceRemaining(pwmApplication.getLocalDB().getFileLocation())));
         aboutBean.setLocalDbFreeSpace(Helper.formatDiskSize(Helper.diskSpaceRemaining(pwmApplication.getLocalDB().getFileLocation())));
 
 
-        { // java version
+        { // java info
             final Runtime runtime = Runtime.getRuntime();
             final Runtime runtime = Runtime.getRuntime();
-            final AboutApplicationBean.JavaInformation javaInformation = aboutBean.getJavaInformation();
+            final AboutApplicationBean.JavaInformation javaInformation = new AboutApplicationBean.JavaInformation();
             javaInformation.setMemoryFree(runtime.freeMemory());
             javaInformation.setMemoryFree(runtime.freeMemory());
             javaInformation.setMemoryAllocated(runtime.totalMemory());
             javaInformation.setMemoryAllocated(runtime.totalMemory());
             javaInformation.setMemoryMax(runtime.maxMemory());
             javaInformation.setMemoryMax(runtime.maxMemory());
@@ -294,6 +295,21 @@ public class AdminServlet extends PwmServlet {
 
 
             javaInformation.setOsName(System.getProperty("os.name"));
             javaInformation.setOsName(System.getProperty("os.name"));
             javaInformation.setOsVersion(System.getProperty("os.version"));
             javaInformation.setOsVersion(System.getProperty("os.version"));
+            javaInformation.setRandomAlgorithm(PwmRandom.getInstance().getAlgorithm());
+            aboutBean.setJavaInformation(javaInformation);
+        }
+
+        { // build info
+            final AboutApplicationBean.BuildInformation buildInformation = new AboutApplicationBean.BuildInformation();
+            buildInformation.setBuildTime(PwmConstants.BUILD_TIME);
+            buildInformation.setBuildNumber(PwmConstants.BUILD_NUMBER);
+            buildInformation.setBuildType(PwmConstants.BUILD_TYPE);
+            buildInformation.setBuildUser(PwmConstants.BUILD_USER);
+            buildInformation.setBuildRevision(PwmConstants.BUILD_REVISION);
+            buildInformation.setBuildJavaVendor(PwmConstants.BUILD_JAVA_VENDOR);
+            buildInformation.setBuildJavaVersion(PwmConstants.BUILD_JAVA_VERSION);
+            buildInformation.setBuildVersion(PwmConstants.BUILD_VERSION);
+            aboutBean.setBuildInformation(buildInformation);
         }
         }
 
 
         return aboutBean;
         return aboutBean;

+ 3 - 5
pwm/servlet/src/password/pwm/http/servlet/ConfigGuideServlet.java

@@ -336,13 +336,11 @@ public class ConfigGuideServlet extends PwmServlet {
     )
     )
             throws IOException, PwmUnrecoverableException {
             throws IOException, PwmUnrecoverableException {
         final Configuration tempConfiguration = new Configuration(configGuideBean.getStoredConfiguration());
         final Configuration tempConfiguration = new Configuration(configGuideBean.getStoredConfiguration());
-        final PwmApplication tempApplication = new PwmApplication.PwmEnvironment()
-                .setConfig(tempConfiguration)
+        final PwmApplication tempApplication = new PwmApplication.PwmEnvironment(tempConfiguration, pwmRequest.getPwmApplication().getApplicationPath())
                 .setApplicationMode(PwmApplication.MODE.NEW)
                 .setApplicationMode(PwmApplication.MODE.NEW)
-                .setApplicationPath(pwmRequest.getPwmApplication().getApplicationPath())
                 .setInitLogging(false)
                 .setInitLogging(false)
-                .setConfigurationFile(null)
-                .setWebInfPath(pwmRequest.getPwmApplication().getWebInfPath()).createPwmApplication();
+                .setWebInfPath(pwmRequest.getPwmApplication().getWebInfPath())
+                .createPwmApplication();
         final LDAPStatusChecker ldapStatusChecker = new LDAPStatusChecker();
         final LDAPStatusChecker ldapStatusChecker = new LDAPStatusChecker();
         final List<HealthRecord> records = new ArrayList<>();
         final List<HealthRecord> records = new ArrayList<>();
         final LdapProfile ldapProfile = tempConfiguration.getDefaultLdapProfile();
         final LdapProfile ldapProfile = tempConfiguration.getDefaultLdapProfile();

+ 4 - 4
pwm/servlet/src/password/pwm/http/servlet/PwmServlet.java

@@ -77,6 +77,10 @@ public abstract class PwmServlet extends HttpServlet {
         try {
         try {
             final PwmRequest pwmRequest = PwmRequest.forRequest(req, resp);
             final PwmRequest pwmRequest = PwmRequest.forRequest(req, resp);
 
 
+            if (!method.isIdempotent()) {
+                Validator.validatePwmFormID(pwmRequest);
+            }
+
             // check for duplicate form submit.
             // check for duplicate form submit.
             try {
             try {
                 Validator.validatePwmRequestCounter(pwmRequest);
                 Validator.validatePwmRequestCounter(pwmRequest);
@@ -104,10 +108,6 @@ public abstract class PwmServlet extends HttpServlet {
                 }
                 }
             }
             }
 
 
-            if (method == HttpMethod.POST) {
-                Validator.validatePwmFormID(pwmRequest);
-            }
-
             this.processAction(pwmRequest);
             this.processAction(pwmRequest);
         } catch (Exception e) {
         } catch (Exception e) {
             final PwmRequest pwmRequest;
             final PwmRequest pwmRequest;

+ 11 - 15
pwm/servlet/src/password/pwm/http/servlet/SetupResponsesServlet.java

@@ -36,7 +36,6 @@ import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.Validator;
 import password.pwm.Validator;
 import password.pwm.bean.ResponseInfoBean;
 import password.pwm.bean.ResponseInfoBean;
-import password.pwm.bean.SessionStateBean;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.ChallengeProfile;
 import password.pwm.config.profile.ChallengeProfile;
@@ -139,7 +138,7 @@ public class SetupResponsesServlet extends PwmServlet {
             pwmSession.getSetupResponseBean().setUserLocale(pwmSession.getSessionStateBean().getLocale());
             pwmSession.getSetupResponseBean().setUserLocale(pwmSession.getSessionStateBean().getLocale());
         }
         }
 
 
-        SetupResponsesBean setupResponsesBean = (SetupResponsesBean)pwmSession.getSessionBean(SetupResponsesBean.class);
+        SetupResponsesBean setupResponsesBean = pwmSession.getSessionBean(SetupResponsesBean.class);
         initializeBean(pwmRequest, setupResponsesBean);
         initializeBean(pwmRequest, setupResponsesBean);
 
 
         // check to see if the user has any challenges assigned
         // check to see if the user has any challenges assigned
@@ -173,12 +172,12 @@ public class SetupResponsesServlet extends PwmServlet {
                     break;
                     break;
 
 
                 case clearExisting:
                 case clearExisting:
-                    handleClearResponses(pwmRequest, setupResponsesBean);
+                    handleClearResponses(pwmRequest);
                     return;
                     return;
 
 
                 case changeResponses:
                 case changeResponses:
                     pwmSession.clearSessionBean(SetupResponsesBean.class);
                     pwmSession.clearSessionBean(SetupResponsesBean.class);
-                    setupResponsesBean = (SetupResponsesBean)pwmSession.getSessionBean(SetupResponsesBean.class);
+                    setupResponsesBean = pwmSession.getSessionBean(SetupResponsesBean.class);
                     this.initializeBean(pwmRequest, setupResponsesBean);
                     this.initializeBean(pwmRequest, setupResponsesBean);
                     setupResponsesBean.setUserLocale(pwmSession.getSessionStateBean().getLocale());
                     setupResponsesBean.setUserLocale(pwmSession.getSessionStateBean().getLocale());
 
 
@@ -190,8 +189,7 @@ public class SetupResponsesServlet extends PwmServlet {
     }
     }
 
 
     private void handleClearResponses(
     private void handleClearResponses(
-            final PwmRequest pwmRequest,
-            final SetupResponsesBean setupResponsesBean
+            final PwmRequest pwmRequest
     )
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException {
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException {
         LOGGER.trace(pwmRequest, "request for response clear received");
         LOGGER.trace(pwmRequest, "request for response clear received");
@@ -257,8 +255,7 @@ public class SetupResponsesServlet extends PwmServlet {
 
 
         try { // everything good, so lets save responses.
         try { // everything good, so lets save responses.
             final ResponseInfoBean responses = generateResponseInfoBean(
             final ResponseInfoBean responses = generateResponseInfoBean(
-                    pwmRequest.getPwmApplication(),
-                    pwmRequest.getPwmSession(),
+                    pwmRequest,
                     setupResponsesBean.getResponseData().getChallengeSet(),
                     setupResponsesBean.getResponseData().getChallengeSet(),
                     setupResponsesBean.getResponseData().getResponseMap(),
                     setupResponsesBean.getResponseData().getResponseMap(),
                     setupResponsesBean.getHelpdeskResponseData().getResponseMap()
                     setupResponsesBean.getHelpdeskResponseData().getResponseMap()
@@ -300,7 +297,7 @@ public class SetupResponsesServlet extends PwmServlet {
             final Map<Challenge, String> responseMap = readResponsesFromJsonRequest(pwmRequest, setupData);
             final Map<Challenge, String> responseMap = readResponsesFromJsonRequest(pwmRequest, setupData);
             final int minRandomRequiredSetup = setupData.getMinRandomSetup();
             final int minRandomRequiredSetup = setupData.getMinRandomSetup();
             pwmApplication.getCrService().validateResponses(setupData.getChallengeSet(), responseMap, minRandomRequiredSetup);
             pwmApplication.getCrService().validateResponses(setupData.getChallengeSet(), responseMap, minRandomRequiredSetup);
-            generateResponseInfoBean(pwmApplication, pwmSession, setupData.getChallengeSet(), responseMap, Collections.<Challenge,String>emptyMap());
+            generateResponseInfoBean(pwmRequest, setupData.getChallengeSet(), responseMap, Collections.<Challenge,String>emptyMap());
         } catch (PwmDataValidationException e) {
         } catch (PwmDataValidationException e) {
             success = false;
             success = false;
             userMessage = e.getErrorInformation().toUserStr(pwmSession, pwmApplication);
             userMessage = e.getErrorInformation().toUserStr(pwmSession, pwmApplication);
@@ -317,7 +314,6 @@ public class SetupResponsesServlet extends PwmServlet {
     )
     )
             throws PwmUnrecoverableException, IOException, ServletException, ChaiUnavailableException
             throws PwmUnrecoverableException, IOException, ServletException, ChaiUnavailableException
     {
     {
-        final SessionStateBean ssBean = pwmRequest.getPwmSession().getSessionStateBean();
         final SetupResponsesBean.SetupData setupData = helpdeskMode ? setupResponsesBean.getHelpdeskResponseData() : setupResponsesBean.getResponseData();
         final SetupResponsesBean.SetupData setupData = helpdeskMode ? setupResponsesBean.getHelpdeskResponseData() : setupResponsesBean.getResponseData();
 
 
         final ChallengeSet challengeSet = setupData.getChallengeSet();
         final ChallengeSet challengeSet = setupData.getChallengeSet();
@@ -434,14 +430,14 @@ public class SetupResponsesServlet extends PwmServlet {
 
 
 
 
     private static ResponseInfoBean generateResponseInfoBean(
     private static ResponseInfoBean generateResponseInfoBean(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final ChallengeSet challengeSet,
             final ChallengeSet challengeSet,
             final Map<Challenge, String> readResponses,
             final Map<Challenge, String> readResponses,
             final Map<Challenge, String> helpdeskResponses
             final Map<Challenge, String> helpdeskResponses
     )
     )
-            throws ChaiUnavailableException, PwmDataValidationException, PwmUnrecoverableException {
-        final ChaiProvider provider = pwmSession.getSessionManager().getChaiProvider();
+            throws ChaiUnavailableException, PwmDataValidationException, PwmUnrecoverableException
+    {
+        final ChaiProvider provider = pwmRequest.getPwmSession().getSessionManager().getChaiProvider();
 
 
         try {
         try {
             final ResponseInfoBean responseInfoBean = new ResponseInfoBean(
             final ResponseInfoBean responseInfoBean = new ResponseInfoBean(
@@ -463,7 +459,7 @@ public class SetupResponsesServlet extends PwmServlet {
 
 
             responseSet.meetsChallengeSetRequirements(challengeSet);
             responseSet.meetsChallengeSetRequirements(challengeSet);
 
 
-            final int minRandomRequiredSetup = pwmSession.getSetupResponseBean().getResponseData().getMinRandomSetup();
+            final int minRandomRequiredSetup = pwmRequest.getPwmSession().getSetupResponseBean().getResponseData().getMinRandomSetup();
             if (minRandomRequiredSetup == 0) { // if using recover style, then all readResponseSet must be supplied at this point.
             if (minRandomRequiredSetup == 0) { // if using recover style, then all readResponseSet must be supplied at this point.
                 if (responseSet.getChallengeSet().getRandomChallenges().size() < challengeSet.getRandomChallenges().size()) {
                 if (responseSet.getChallengeSet().getRandomChallenges().size() < challengeSet.getRandomChallenges().size()) {
                     throw new ChaiValidationException("too few random responses", ChaiError.CR_TOO_FEW_RANDOM_RESPONSES);
                     throw new ChaiValidationException("too few random responses", ChaiError.CR_TOO_FEW_RANDOM_RESPONSES);

+ 65 - 0
pwm/servlet/src/password/pwm/http/servlet/peoplesearch/AttributeDetailBean.java

@@ -0,0 +1,65 @@
+package password.pwm.http.servlet.peoplesearch;
+
+import password.pwm.config.FormConfiguration;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+class AttributeDetailBean implements Serializable {
+    private String name;
+    private String label;
+    private FormConfiguration.Type type;
+    private String value;
+    private Collection<UserReferenceBean> userReferences;
+    private boolean searchable;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public FormConfiguration.Type getType() {
+        return type;
+    }
+
+    public void setType(FormConfiguration.Type type) {
+        this.type = type;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public Collection<UserReferenceBean> getUserReferences() {
+        return userReferences;
+    }
+
+    public void setUserReferences(Collection<UserReferenceBean> userReferences) {
+        this.userReferences = userReferences;
+    }
+
+    public boolean isSearchable() {
+        return searchable;
+    }
+
+    public void setSearchable(boolean searchable) {
+        this.searchable = searchable;
+    }
+
+
+}

+ 25 - 0
pwm/servlet/src/password/pwm/http/servlet/peoplesearch/OrgChartData.java

@@ -0,0 +1,25 @@
+package password.pwm.http.servlet.peoplesearch;
+
+import java.io.Serializable;
+import java.util.List;
+
+class OrgChartData implements Serializable {
+    private OrgChartReferenceBean parent;
+    private List<OrgChartReferenceBean> siblings;
+
+    public OrgChartReferenceBean getParent() {
+        return parent;
+    }
+
+    public void setParent(OrgChartReferenceBean parent) {
+        this.parent = parent;
+    }
+
+    public List<OrgChartReferenceBean> getSiblings() {
+        return siblings;
+    }
+
+    public void setSiblings(List<OrgChartReferenceBean> siblings) {
+        this.siblings = siblings;
+    }
+}

+ 43 - 0
pwm/servlet/src/password/pwm/http/servlet/peoplesearch/OrgChartReferenceBean.java

@@ -0,0 +1,43 @@
+package password.pwm.http.servlet.peoplesearch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class OrgChartReferenceBean {
+    public String userKey;
+    public List<String> displayNames = new ArrayList<>();
+    public String photoURL;
+    public boolean hasMoreNodes;
+
+    public String getPhotoURL() {
+        return photoURL;
+    }
+
+    public void setPhotoURL(String photoURL) {
+        this.photoURL = photoURL;
+    }
+
+    public boolean isHasMoreNodes() {
+        return hasMoreNodes;
+    }
+
+    public void setHasMoreNodes(boolean hasMoreNodes) {
+        this.hasMoreNodes = hasMoreNodes;
+    }
+
+    public String getUserKey() {
+        return userKey;
+    }
+
+    public void setUserKey(String userKey) {
+        this.userKey = userKey;
+    }
+
+    public List<String> getDisplayNames() {
+        return displayNames;
+    }
+
+    public void setDisplayNames(List<String> displayNames) {
+        this.displayNames = displayNames;
+    }
+}

+ 270 - 450
pwm/servlet/src/password/pwm/http/servlet/PeopleSearchServlet.java → pwm/servlet/src/password/pwm/http/servlet/peoplesearch/PeopleSearchServlet.java

@@ -20,7 +20,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
 
 
-package password.pwm.http.servlet;
+package password.pwm.http.servlet.peoplesearch;
 
 
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiException;
 import com.novell.ldapchai.exception.ChaiException;
@@ -37,7 +37,7 @@ import password.pwm.config.UserPermission;
 import password.pwm.error.*;
 import password.pwm.error.*;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
-import password.pwm.http.PwmSession;
+import password.pwm.http.servlet.PwmServlet;
 import password.pwm.ldap.*;
 import password.pwm.ldap.*;
 import password.pwm.util.JsonUtil;
 import password.pwm.util.JsonUtil;
 import password.pwm.util.SecureHelper;
 import password.pwm.util.SecureHelper;
@@ -51,6 +51,7 @@ import password.pwm.util.stats.StatisticsManager;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStream;
@@ -62,248 +63,12 @@ public class PeopleSearchServlet extends PwmServlet {
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(PeopleSearchServlet.class);
     private static final PwmLogger LOGGER = PwmLogger.forClass(PeopleSearchServlet.class);
 
 
-    public static class SearchResultBean implements Serializable {
-        private List searchResults = new ArrayList<>();
-        private boolean sizeExceeded;
-
-        public List getSearchResults() {
-            return searchResults;
-        }
-
-        public void setSearchResults(List searchResults) {
-            this.searchResults = searchResults;
-        }
-
-        public boolean isSizeExceeded() {
-            return sizeExceeded;
-        }
-
-        public void setSizeExceeded(boolean sizeExceeded) {
-            this.sizeExceeded = sizeExceeded;
-        }
-    }
-
-    public static class UserDetailBean implements Serializable {
-        private String displayName;
-        private String userKey;
-        private Map<String,AttributeDetailBean> detail;
-        private String photoURL;
-        private boolean hasOrgChart;
-        private String orgChartParentKey;
-
-        public String getDisplayName() {
-            return displayName;
-        }
-
-        public void setDisplayName(String displayName) {
-            this.displayName = displayName;
-        }
-
-        public String getUserKey() {
-            return userKey;
-        }
-
-        public void setUserKey(String userKey) {
-            this.userKey = userKey;
-        }
-
-        public Map<String, AttributeDetailBean> getDetail() {
-            return detail;
-        }
-
-        public void setDetail(Map<String, AttributeDetailBean> detail) {
-            this.detail = detail;
-        }
-
-        public String getPhotoURL() {
-            return photoURL;
-        }
-
-        public void setPhotoURL(String photoURL) {
-            this.photoURL = photoURL;
-        }
-
-        public boolean isHasOrgChart() {
-            return hasOrgChart;
-        }
-
-        public void setHasOrgChart(boolean hasOrgChart) {
-            this.hasOrgChart = hasOrgChart;
-        }
-
-        public String getOrgChartParentKey() {
-            return orgChartParentKey;
-        }
-
-        public void setOrgChartParentKey(String orgChartParentKey) {
-            this.orgChartParentKey = orgChartParentKey;
-        }
-    }
-
-    public static class AttributeDetailBean implements Serializable {
-        private String name;
-        private String label;
-        private FormConfiguration.Type type;
-        private String value;
-        private Collection<UserReferenceBean> userReferences;
-        private boolean searchable;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public String getLabel() {
-            return label;
-        }
-
-        public void setLabel(String label) {
-            this.label = label;
-        }
-
-        public FormConfiguration.Type getType() {
-            return type;
-        }
-
-        public void setType(FormConfiguration.Type type) {
-            this.type = type;
-        }
-
-        public String getValue() {
-            return value;
-        }
-
-        public void setValue(String value) {
-            this.value = value;
-        }
-
-        public Collection<UserReferenceBean> getUserReferences() {
-            return userReferences;
-        }
-
-        public void setUserReferences(Collection<UserReferenceBean> userReferences) {
-            this.userReferences = userReferences;
-        }
-
-        public boolean isSearchable() {
-            return searchable;
-        }
-
-        public void setSearchable(boolean searchable) {
-            this.searchable = searchable;
-        }
-
-
-    }
-
-    public static class UserTreeData implements Serializable {
-        private UserTreeReferenceBean parent;
-        private List<UserTreeReferenceBean> siblings;
-
-        public UserTreeReferenceBean getParent() {
-            return parent;
-        }
-
-        public void setParent(UserTreeReferenceBean parent) {
-            this.parent = parent;
-        }
-
-        public List<UserTreeReferenceBean> getSiblings() {
-            return siblings;
-        }
-
-        public void setSiblings(List<UserTreeReferenceBean> siblings) {
-            this.siblings = siblings;
-        }
-    }
-
-    public static class UserTreeReferenceBean {
-        public String userKey;
-        public List<String> displayNames = new ArrayList<>();
-        public String photoURL;
-        public boolean hasMoreNodes;
-
-        public String getPhotoURL() {
-            return photoURL;
-        }
-
-        public void setPhotoURL(String photoURL) {
-            this.photoURL = photoURL;
-        }
-
-        public boolean isHasMoreNodes() {
-            return hasMoreNodes;
-        }
-
-        public void setHasMoreNodes(boolean hasMoreNodes) {
-            this.hasMoreNodes = hasMoreNodes;
-        }
-
-        public String getUserKey() {
-            return userKey;
-        }
-
-        public void setUserKey(String userKey) {
-            this.userKey = userKey;
-        }
-
-        public List<String> getDisplayNames() {
-            return displayNames;
-        }
-
-        public void setDisplayNames(List<String> displayNames) {
-            this.displayNames = displayNames;
-        }
-    }
-
-    public static class UserReferenceBean implements Serializable {
-        private String userKey;
-        private String displayName;
-
-        public String getUserKey() {
-            return userKey;
-        }
-
-        public void setUserKey(String userKey) {
-            this.userKey = userKey;
-        }
-
-        public String getDisplayName() {
-            return displayName;
-        }
-
-        public void setDisplayName(String displayName) {
-            this.displayName = displayName;
-        }
-    }
-
-    private static class PhotoData {
-        private final String mimeType;
-        private byte[] contents;
-
-        private PhotoData(String mimeType, byte[] contents) {
-            this.mimeType = mimeType;
-            this.contents = contents;
-        }
-
-        public String getMimeType() {
-            return mimeType;
-        }
-
-        public byte[] getContents() {
-            return contents;
-        }
-    }
-
     public enum PeopleSearchActions implements ProcessAction {
     public enum PeopleSearchActions implements ProcessAction {
         search(HttpMethod.POST),
         search(HttpMethod.POST),
         detail(HttpMethod.POST),
         detail(HttpMethod.POST),
         photo(HttpMethod.GET),
         photo(HttpMethod.GET),
         clientData(HttpMethod.GET),
         clientData(HttpMethod.GET),
-        userTreeData(HttpMethod.POST),
+        orgChartData(HttpMethod.POST),
 
 
         ;
         ;
 
 
@@ -355,6 +120,8 @@ public class PeopleSearchServlet extends PwmServlet {
             pwmRequest.getPwmSession().setSessionTimeout(pwmRequest.getHttpServletRequest().getSession(), peopleSearchIdleTimeout);
             pwmRequest.getPwmSession().setSessionTimeout(pwmRequest.getHttpServletRequest().getSession(), peopleSearchIdleTimeout);
         }
         }
 
 
+        final PeopleSearchConfiguration peopleSearchConfiguration = new PeopleSearchConfiguration(pwmRequest.getConfig());
+
         final PeopleSearchActions peopleSearchAction = this.readProcessAction(pwmRequest);
         final PeopleSearchActions peopleSearchAction = this.readProcessAction(pwmRequest);
         if (peopleSearchAction != null) {
         if (peopleSearchAction != null) {
             switch (peopleSearchAction) {
             switch (peopleSearchAction) {
@@ -363,7 +130,7 @@ public class PeopleSearchServlet extends PwmServlet {
                     return;
                     return;
 
 
                 case detail:
                 case detail:
-                    restUserDetailRequest(pwmRequest);
+                    restUserDetailRequest(pwmRequest, peopleSearchConfiguration);
                     return;
                     return;
 
 
                 case photo:
                 case photo:
@@ -371,11 +138,11 @@ public class PeopleSearchServlet extends PwmServlet {
                     return;
                     return;
 
 
                 case clientData:
                 case clientData:
-                    restLoadClientData(pwmRequest);
+                    restLoadClientData(pwmRequest, peopleSearchConfiguration);
                     return;
                     return;
 
 
-                case userTreeData:
-                    restUserTreeData(pwmRequest);
+                case orgChartData:
+                    restOrgChartData(pwmRequest, peopleSearchConfiguration);
                     return;
                     return;
             }
             }
         }
         }
@@ -388,30 +155,28 @@ public class PeopleSearchServlet extends PwmServlet {
     }
     }
 
 
     private void restLoadClientData(
     private void restLoadClientData(
-            final PwmRequest pwmRequest
+            final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration
+
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
     {
 
 
         final Map<String, String> searchColumns = new LinkedHashMap<>();
         final Map<String, String> searchColumns = new LinkedHashMap<>();
-        final String photoStyle = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_STYLE_ATTR);
         final List<FormConfiguration> searchForm = pwmRequest.getConfig().readSettingAsForm(PwmSetting.PEOPLE_SEARCH_RESULT_FORM);
         final List<FormConfiguration> searchForm = pwmRequest.getConfig().readSettingAsForm(PwmSetting.PEOPLE_SEARCH_RESULT_FORM);
         for (final FormConfiguration formConfiguration : searchForm) {
         for (final FormConfiguration formConfiguration : searchForm) {
             searchColumns.put(formConfiguration.getName(),
             searchColumns.put(formConfiguration.getName(),
                     formConfiguration.getLabel(pwmRequest.getLocale()));
                     formConfiguration.getLabel(pwmRequest.getLocale()));
         }
         }
 
 
-        final boolean orgChartEnabled = orgChartIsEnabled(pwmRequest.getConfig());
-
         final HashMap<String,Object> returnValues = new HashMap<>();
         final HashMap<String,Object> returnValues = new HashMap<>();
-        returnValues.put("peoplesearch_search_columns",searchColumns);
-        returnValues.put("photo_style_attribute",photoStyle);
-        returnValues.put("peoplesearch_orgChart_enabled",orgChartEnabled);
+        returnValues.put("peoplesearch_search_columns", searchColumns);
+        returnValues.put("peoplesearch_enablePhoto", peopleSearchConfiguration.isPhotosEnabled());
 
 
         final RestResultBean restResultBean = new RestResultBean(returnValues);
         final RestResultBean restResultBean = new RestResultBean(returnValues);
         LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
         LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
-    };
+    }
 
 
 
 
     private void restSearchRequest(
     private void restSearchRequest(
@@ -419,34 +184,27 @@ public class PeopleSearchServlet extends PwmServlet {
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
     {
-        final Date startTime = new Date();
         final String bodyString = pwmRequest.readRequestBodyAsString();
         final String bodyString = pwmRequest.readRequestBodyAsString();
         final Map<String, String> valueMap = JsonUtil.deserializeStringMap(bodyString);
         final Map<String, String> valueMap = JsonUtil.deserializeStringMap(bodyString);
 
 
         final String username = Validator.sanitizeInputValue(pwmRequest.getConfig(), valueMap.get("username"), 1024);
         final String username = Validator.sanitizeInputValue(pwmRequest.getConfig(), valueMap.get("username"), 1024);
-        final boolean useProxy = useProxy(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession());
-        final CacheKey cacheKey = CacheKey.makeCacheKey(
-                this.getClass(),
-                useProxy ? null : pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity(),
-                "search-" + SecureHelper.hash(username, SecureHelper.HashAlgorithm.SHA1));
-
+        final CacheKey cacheKey = makeCacheKey(pwmRequest, "search", username);
         {
         {
             final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey);
             final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey);
             if (cachedOutput != null) {
             if (cachedOutput != null) {
                 final SearchResultBean resultOutput = JsonUtil.deserialize(cachedOutput, SearchResultBean.class);
                 final SearchResultBean resultOutput = JsonUtil.deserialize(cachedOutput, SearchResultBean.class);
-                final RestResultBean restResultBean = new RestResultBean();
-                pwmRequest.outputJsonResult(new RestResultBean(restResultBean));
-                LOGGER.trace(pwmRequest, "finished rest peoplesearch search using CACHE in "
-                        + TimeDuration.fromCurrent(startTime).asCompactString()
-                        + ", size=" + resultOutput.getSearchResults().size());
-
+                pwmRequest.outputJsonResult(new RestResultBean(resultOutput));
+                StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS);
                 return;
                 return;
+            } else {
+                StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES);
             }
             }
         }
         }
 
 
         final SearchResultBean outputData = makeSearchResultsImpl(pwmRequest, username);
         final SearchResultBean outputData = makeSearchResultsImpl(pwmRequest, username);
         final RestResultBean restResultBean = new RestResultBean(outputData);
         final RestResultBean restResultBean = new RestResultBean(outputData);
         pwmRequest.outputJsonResult(restResultBean);
         pwmRequest.outputJsonResult(restResultBean);
+        StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_SEARCHES);
         storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, outputData);
         storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, outputData);
     }
     }
 
 
@@ -459,11 +217,10 @@ public class PeopleSearchServlet extends PwmServlet {
         final Date startTime = new Date();
         final Date startTime = new Date();
 
 
         if (username == null || username.length() < 1) {
         if (username == null || username.length() < 1) {
-            final SearchResultBean searchResultBean = new SearchResultBean();
-            return searchResultBean;
+            return new SearchResultBean();
         }
         }
 
 
-        final boolean useProxy = useProxy(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession());
+        final boolean useProxy = useProxy(pwmRequest);
         final UserSearchEngine userSearchEngine = new UserSearchEngine(pwmRequest);
         final UserSearchEngine userSearchEngine = new UserSearchEngine(pwmRequest);
         final UserSearchEngine.SearchConfiguration searchConfiguration = new UserSearchEngine.SearchConfiguration();
         final UserSearchEngine.SearchConfiguration searchConfiguration = new UserSearchEngine.SearchConfiguration();
         searchConfiguration.setContexts(
         searchConfiguration.setContexts(
@@ -493,10 +250,6 @@ public class PeopleSearchServlet extends PwmServlet {
             throw new PwmUnrecoverableException(errorInformation);
             throw new PwmUnrecoverableException(errorInformation);
         }
         }
 
 
-        if (pwmRequest.getPwmApplication().getStatisticsManager() != null) {
-            pwmRequest.getPwmApplication().getStatisticsManager().incrementValue(Statistic.PEOPLESEARCH_SEARCHES);
-        }
-
         LOGGER.trace(pwmRequest.getPwmSession(), "finished rest peoplesearch search in " + TimeDuration.fromCurrent(
         LOGGER.trace(pwmRequest.getPwmSession(), "finished rest peoplesearch search in " + TimeDuration.fromCurrent(
                 startTime).asCompactString() + " not using cache, size=" + results.getResults().size());
                 startTime).asCompactString() + " not using cache, size=" + results.getResults().size());
 
 
@@ -507,18 +260,18 @@ public class PeopleSearchServlet extends PwmServlet {
     }
     }
 
 
     private static UserSearchEngine.UserSearchResults doDetailLookup(
     private static UserSearchEngine.UserSearchResults doDetailLookup(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration,
             final UserIdentity userIdentity
             final UserIdentity userIdentity
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        final Configuration config = pwmApplication.getConfig();
+        final Configuration config = pwmRequest.getConfig();
         final List<FormConfiguration> detailFormConfig = config.readSettingAsForm(PwmSetting.PEOPLE_SEARCH_DETAIL_FORM);
         final List<FormConfiguration> detailFormConfig = config.readSettingAsForm(PwmSetting.PEOPLE_SEARCH_DETAIL_FORM);
         final Map<String, String> attributeHeaderMap = UserSearchEngine.UserSearchResults.fromFormConfiguration(
         final Map<String, String> attributeHeaderMap = UserSearchEngine.UserSearchResults.fromFormConfiguration(
-                detailFormConfig, pwmSession.getSessionStateBean().getLocale());
+                detailFormConfig, pwmRequest.getLocale());
 
 
-        if (orgChartIsEnabled(pwmApplication.getConfig())) {
+        if (peopleSearchConfiguration.orgChartIsEnabled()) {
             final String orgChartParentAttr = config.readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_PARENT_ATTRIBUTE);
             final String orgChartParentAttr = config.readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_PARENT_ATTRIBUTE);
             if (!attributeHeaderMap.containsKey(orgChartParentAttr)) {
             if (!attributeHeaderMap.containsKey(orgChartParentAttr)) {
                 attributeHeaderMap.put(orgChartParentAttr, orgChartParentAttr);
                 attributeHeaderMap.put(orgChartParentAttr, orgChartParentAttr);
@@ -530,23 +283,26 @@ public class PeopleSearchServlet extends PwmServlet {
         }
         }
 
 
         try {
         try {
-            final ChaiUser theUser = useProxy(pwmApplication,pwmSession)
-                    ? pwmApplication.getProxiedChaiUser(userIdentity)
-                    : pwmSession.getSessionManager().getActor(pwmApplication, userIdentity);
+            final ChaiUser theUser = getChaiUser(pwmRequest, userIdentity);
             final Map<String, String> values = theUser.readStringAttributes(attributeHeaderMap.keySet());
             final Map<String, String> values = theUser.readStringAttributes(attributeHeaderMap.keySet());
-            return new UserSearchEngine.UserSearchResults(attributeHeaderMap,
-                    Collections.singletonMap(userIdentity, values), false);
+            return new UserSearchEngine.UserSearchResults(
+                    attributeHeaderMap,
+                    Collections.singletonMap(userIdentity, values),
+                    false
+            );
         } catch (ChaiException e) {
         } catch (ChaiException e) {
             LOGGER.error("unexpected error during detail lookup of '" + userIdentity + "', error: " + e.getMessage());
             LOGGER.error("unexpected error during detail lookup of '" + userIdentity + "', error: " + e.getMessage());
             throw new PwmUnrecoverableException(new ErrorInformation(PwmError.forChaiError(e.getErrorCode()),e.getMessage()));
             throw new PwmUnrecoverableException(new ErrorInformation(PwmError.forChaiError(e.getErrorCode()),e.getMessage()));
         }
         }
     }
     }
 
 
-    private void restUserTreeData(
-            final PwmRequest pwmRequest
+    private void restOrgChartData(
+            final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration
     )
     )
-            throws IOException, PwmUnrecoverableException, ServletException {
-        if (!orgChartIsEnabled(pwmRequest.getConfig())) {
+            throws IOException, PwmUnrecoverableException, ServletException
+    {
+        if (!peopleSearchConfiguration.orgChartIsEnabled()) {
             throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE);
             throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE);
         }
         }
 
 
@@ -566,12 +322,13 @@ public class PeopleSearchServlet extends PwmServlet {
             if (asParent) {
             if (asParent) {
                 parentIdentity = userIdentity;
                 parentIdentity = userIdentity;
             } else {
             } else {
-                final UserDetailBean userDetailBean = makeUserDetailRequestImpl(pwmRequest, userKey);
+                final UserDetailBean userDetailBean = makeUserDetailRequestImpl(pwmRequest, peopleSearchConfiguration, userKey);
                 parentIdentity = UserIdentity.fromObfuscatedKey(userDetailBean.getOrgChartParentKey(), pwmRequest.getConfig());
                 parentIdentity = UserIdentity.fromObfuscatedKey(userDetailBean.getOrgChartParentKey(), pwmRequest.getConfig());
             }
             }
 
 
-            final UserTreeData userTreeData = makeUserTreeData(pwmRequest, parentIdentity);
-            pwmRequest.outputJsonResult(new RestResultBean(userTreeData));
+            final OrgChartData orgChartData = makeOrgChartData(pwmRequest, parentIdentity);
+            pwmRequest.outputJsonResult(new RestResultBean(orgChartData));
+            StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_ORGCHART);
         } catch (PwmException e) {
         } catch (PwmException e) {
             LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
             LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
             pwmRequest.respondWithError(e.getErrorInformation());
             pwmRequest.respondWithError(e.getErrorInformation());
@@ -580,9 +337,7 @@ public class PeopleSearchServlet extends PwmServlet {
         }
         }
     }
     }
 
 
-
-
-    private UserTreeData makeUserTreeData(
+    private OrgChartData makeOrgChartData(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final UserIdentity parentIdentity
             final UserIdentity parentIdentity
 
 
@@ -590,58 +345,49 @@ public class PeopleSearchServlet extends PwmServlet {
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         final Date startTime = new Date();
         final Date startTime = new Date();
-        final boolean useProxy = useProxy(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession());
 
 
-        final CacheKey cacheKey = CacheKey.makeCacheKey(
-                this.getClass(),
-                useProxy ? null : pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity(),
-                "treeData-" + parentIdentity.toObfuscatedKey(pwmRequest.getConfig()));
-        {
+        final CacheKey cacheKey = makeCacheKey(pwmRequest, "orgChartData", parentIdentity.toDelimitedKey());
+        { // if value is cached then return;
             final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey);
             final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey);
             if (cachedOutput != null) {
             if (cachedOutput != null) {
-                return JsonUtil.deserialize(cachedOutput, UserTreeData.class);
+                StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS);
+                return JsonUtil.deserialize(cachedOutput, OrgChartData.class);
+            } else {
+                StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES);
             }
             }
         }
         }
 
 
         final String parentAttribute = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_PARENT_ATTRIBUTE);
         final String parentAttribute = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_PARENT_ATTRIBUTE);
         final String childAttribute = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_CHILD_ATTRIBUTE);
         final String childAttribute = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_CHILD_ATTRIBUTE);
 
 
-        try {
-            final UserTreeData userTreeData = new UserTreeData();
-            final UserTreeReferenceBean parentReference = userDetailToTreeReference(pwmRequest, parentIdentity, parentAttribute);
-            userTreeData.setParent(parentReference);
-
-            final ChaiUser parentUser = getChaiUser(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), parentIdentity);
-            final Set<String> childDNs = parentUser.readMultiStringAttribute(childAttribute);
-            int counter = 0;
-            if (childDNs != null) {
-                final Map<String,UserTreeReferenceBean> sortedSiblings = new TreeMap<>();
-                for (final String childDN : childDNs) {
-                    final UserIdentity childIdentity = new UserIdentity(childDN, parentIdentity.getLdapProfileID());
-                    final UserTreeReferenceBean childReference = userDetailToTreeReference(pwmRequest, childIdentity, childAttribute);
-                    if (childReference != null) {
-                        if (childReference.getDisplayNames() != null && !childReference.getDisplayNames().isEmpty()) {
-                            final String firstDisplayName = childReference.getDisplayNames().iterator().next();
-                            sortedSiblings.put(firstDisplayName, childReference);
-                        } else {
-                            sortedSiblings.put(String.valueOf(counter), childReference);
-                        }
-                        counter++;
-                    }
+        final OrgChartData orgChartData = new OrgChartData();
+        final OrgChartReferenceBean parentReference = makeOrgChartReferenceForIdentity(pwmRequest, parentIdentity, parentAttribute);
+        orgChartData.setParent(parentReference);
+
+        final Map<String,OrgChartReferenceBean> sortedSiblings = new TreeMap<>();
+        final List<UserIdentity> childIdentities = readUserDNAttributeValues(pwmRequest, parentIdentity, childAttribute);
+        int counter = 0;
+        for (final UserIdentity  childIdentity : childIdentities) {
+            final OrgChartReferenceBean childReference = makeOrgChartReferenceForIdentity(pwmRequest, childIdentity, childAttribute);
+            if (childReference != null) {
+                if (childReference.getDisplayNames() != null && !childReference.getDisplayNames().isEmpty()) {
+                    final String firstDisplayName = childReference.getDisplayNames().iterator().next();
+                    sortedSiblings.put(firstDisplayName, childReference);
+                } else {
+                    sortedSiblings.put(String.valueOf(counter), childReference);
                 }
                 }
-                userTreeData.setSiblings(new ArrayList<>(sortedSiblings.values()));
+                counter++;
             }
             }
-            storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, userTreeData);
-            LOGGER.trace(pwmRequest, "completed building userTreeData in " + TimeDuration.fromCurrent(startTime).asCompactString());
-            return userTreeData;
-        } catch (ChaiException e) {
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.forChaiError(e.getErrorCode()),e.getMessage()));
+            orgChartData.setSiblings(new ArrayList<>(sortedSiblings.values()));
         }
         }
+        storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, orgChartData);
+        LOGGER.trace(pwmRequest, "completed building orgChartData in " + TimeDuration.fromCurrent(startTime).asCompactString());
+        return orgChartData;
     }
     }
 
 
-
     private void restUserDetailRequest(
     private void restUserDetailRequest(
-            final PwmRequest pwmRequest
+            final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
     {
@@ -657,8 +403,9 @@ public class PeopleSearchServlet extends PwmServlet {
         }
         }
 
 
         try {
         try {
-            final UserDetailBean detailData = makeUserDetailRequestImpl(pwmRequest, userKey);
+            final UserDetailBean detailData = makeUserDetailRequestImpl(pwmRequest, peopleSearchConfiguration, userKey);
             pwmRequest.outputJsonResult(new RestResultBean(detailData));
             pwmRequest.outputJsonResult(new RestResultBean(detailData));
+            pwmRequest.getPwmApplication().getStatisticsManager().incrementValue(Statistic.PEOPLESEARCH_DETAILS);
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
             LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
             LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
             pwmRequest.respondWithError(e.getErrorInformation());
             pwmRequest.respondWithError(e.getErrorInformation());
@@ -668,74 +415,70 @@ public class PeopleSearchServlet extends PwmServlet {
 
 
     private UserDetailBean makeUserDetailRequestImpl(
     private UserDetailBean makeUserDetailRequestImpl(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration,
             final String userKey
             final String userKey
     )
     )
             throws PwmUnrecoverableException, IOException, ServletException, PwmOperationalException, ChaiUnavailableException
             throws PwmUnrecoverableException, IOException, ServletException, PwmOperationalException, ChaiUnavailableException
     {
     {
         final Date startTime = new Date();
         final Date startTime = new Date();
-        final boolean useProxy = useProxy(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession());
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getConfig());
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getConfig());
 
 
-        final CacheKey cacheKey = CacheKey.makeCacheKey(
-                this.getClass(),
-                useProxy ? null : pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity(),
-                "detail-" + userIdentity.toObfuscatedKey(pwmRequest.getConfig())
-        );
+        final CacheKey cacheKey = makeCacheKey(pwmRequest, "detail", userIdentity.toDelimitedKey());
         {
         {
             final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey);
             final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey);
             if (cachedOutput != null) {
             if (cachedOutput != null) {
-                final UserDetailBean resultOutput = JsonUtil.deserialize(cachedOutput, UserDetailBean.class);
-                final RestResultBean restResultBean = new RestResultBean(resultOutput);
-                LOGGER.debug(pwmRequest.getPwmSession(), "finished rest detail request in " + TimeDuration.fromCurrent(
-                        startTime).asCompactString() + " using cached details, results=" + JsonUtil.serialize(restResultBean));
-
-                if (pwmRequest.getPwmApplication().getStatisticsManager() != null) {
-                    pwmRequest.getPwmApplication().getStatisticsManager().incrementValue(Statistic.PEOPLESEARCH_DETAILS);
-                }
-
-                return resultOutput;
+                StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS);
+                return JsonUtil.deserialize(cachedOutput, UserDetailBean.class);
+            } else {
+                StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES);
             }
             }
         }
         }
 
 
         try {
         try {
-            checkIfUserIdentityViewable(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
+            checkIfUserIdentityViewable(pwmRequest, userIdentity);
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
             LOGGER.error(pwmRequest.getPwmSession(), "error during detail results request while checking if requested userIdentity is within search scope: " + e.getMessage());
             LOGGER.error(pwmRequest.getPwmSession(), "error during detail results request while checking if requested userIdentity is within search scope: " + e.getMessage());
             throw e;
             throw e;
         }
         }
 
 
-        final UserSearchEngine.UserSearchResults detailResults = doDetailLookup(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
+        final UserSearchEngine.UserSearchResults detailResults = doDetailLookup(pwmRequest, peopleSearchConfiguration, userIdentity);
         final Map<String, String> searchResults = detailResults.getResults().get(userIdentity);
         final Map<String, String> searchResults = detailResults.getResults().get(userIdentity);
 
 
         final UserDetailBean userDetailBean = new UserDetailBean();
         final UserDetailBean userDetailBean = new UserDetailBean();
         userDetailBean.setUserKey(userKey);
         userDetailBean.setUserKey(userKey);
         final List<FormConfiguration> detailFormConfig = pwmRequest.getConfig().readSettingAsForm( PwmSetting.PEOPLE_SEARCH_DETAIL_FORM);
         final List<FormConfiguration> detailFormConfig = pwmRequest.getConfig().readSettingAsForm( PwmSetting.PEOPLE_SEARCH_DETAIL_FORM);
-        final Map<String,AttributeDetailBean> attributeBeans = convertResultMapToBeans(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity,
-                detailFormConfig, searchResults);
+        final Map<String,AttributeDetailBean> attributeBeans = convertResultMapToBeans(pwmRequest, userIdentity, detailFormConfig, searchResults);
 
 
         userDetailBean.setDetail(attributeBeans);
         userDetailBean.setDetail(attributeBeans);
         final String photoURL = figurePhotoURL(pwmRequest, userIdentity);
         final String photoURL = figurePhotoURL(pwmRequest, userIdentity);
         if (photoURL != null) {
         if (photoURL != null) {
             userDetailBean.setPhotoURL(photoURL);
             userDetailBean.setPhotoURL(photoURL);
         }
         }
-        final String displayName = figureDisplaynameValue(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
+        final List<String> displayName = figureDisplaynames(pwmRequest, userIdentity);
         if (displayName != null) {
         if (displayName != null) {
-            userDetailBean.setDisplayName(displayName);
+            userDetailBean.setDisplayNames(displayName);
         }
         }
 
 
-        if (orgChartIsEnabled(pwmRequest.getConfig())) {
+        if (peopleSearchConfiguration.orgChartIsEnabled()) {
             final String parentAttr = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_PARENT_ATTRIBUTE);
             final String parentAttr = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_PARENT_ATTRIBUTE);
             final String parentDN = searchResults.get(parentAttr);
             final String parentDN = searchResults.get(parentAttr);
             if (parentDN != null && !parentDN.isEmpty()) {
             if (parentDN != null && !parentDN.isEmpty()) {
                 userDetailBean.setHasOrgChart(true);
                 userDetailBean.setHasOrgChart(true);
                 final UserIdentity parentIdentity = new UserIdentity(parentDN,userIdentity.getLdapProfileID());
                 final UserIdentity parentIdentity = new UserIdentity(parentDN,userIdentity.getLdapProfileID());
                 userDetailBean.setOrgChartParentKey(parentIdentity.toObfuscatedKey(pwmRequest.getConfig()));
                 userDetailBean.setOrgChartParentKey(parentIdentity.toObfuscatedKey(pwmRequest.getConfig()));
+            } else {
+                final String childAttr = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_CHILD_ATTRIBUTE);
+                final String childDN = searchResults.get(childAttr);
+                if (childDN != null && !childDN.isEmpty()) {
+                    userDetailBean.setHasOrgChart(true);
+                    // no parent so use self as parent.
+                    userDetailBean.setOrgChartParentKey(userIdentity.toObfuscatedKey(pwmRequest.getConfig()));
+                }
             }
             }
         }
         }
 
 
         LOGGER.trace(pwmRequest.getPwmSession(), "finished building userDetail result in " + TimeDuration.fromCurrent(startTime).asCompactString());
         LOGGER.trace(pwmRequest.getPwmSession(), "finished building userDetail result in " + TimeDuration.fromCurrent(startTime).asCompactString());
         storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, userDetailBean);
         storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, userDetailBean);
-        StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_SEARCHES);
         return userDetailBean;
         return userDetailBean;
     }
     }
 
 
@@ -769,7 +512,7 @@ public class PeopleSearchServlet extends PwmServlet {
         final String overrideURL = pwmApplication.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_URL_OVERRIDE);
         final String overrideURL = pwmApplication.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_URL_OVERRIDE);
         try {
         try {
             if (overrideURL != null && !overrideURL.isEmpty()) {
             if (overrideURL != null && !overrideURL.isEmpty()) {
-                final MacroMachine macroMachine = getMacroMachine(pwmApplication, pwmRequest.getPwmSession(), userIdentity);
+                final MacroMachine macroMachine = getMacroMachine(pwmRequest, userIdentity);
                 return macroMachine.expandMacros(overrideURL);
                 return macroMachine.expandMacros(overrideURL);
             }
             }
 
 
@@ -787,20 +530,38 @@ public class PeopleSearchServlet extends PwmServlet {
     }
     }
 
 
     private static String figureDisplaynameValue(
     private static String figureDisplaynameValue(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final UserIdentity userIdentity
             final UserIdentity userIdentity
     )
     )
-            throws PwmUnrecoverableException, ChaiUnavailableException
+            throws PwmUnrecoverableException
     {
     {
-        final MacroMachine macroMachine = getMacroMachine(pwmApplication, pwmSession, userIdentity);
-        final String settingValue = pwmApplication.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_DISPLAY_NAME);
+        final MacroMachine macroMachine = getMacroMachine(pwmRequest, userIdentity);
+        final String settingValue = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_DISPLAY_NAME);
         return macroMachine.expandMacros(settingValue);
         return macroMachine.expandMacros(settingValue);
     }
     }
 
 
+    private static List<String> figureDisplaynames(
+            final PwmRequest pwmRequest,
+            final UserIdentity userIdentity
+    )
+            throws PwmUnrecoverableException
+    {
+        final List<String> displayLabels = new ArrayList<>();
+        final List<String> displayStringSettings = pwmRequest.getConfig().readSettingAsStringArray(PwmSetting.PEOPLE_SEARCH_DISPLAY_NAMES_CARD_LABELS);
+        if (displayStringSettings != null) {
+            final MacroMachine macroMachine = getMacroMachine(pwmRequest, userIdentity);
+            for (final String displayStringSetting : displayStringSettings) {
+                final String displayLabel = macroMachine.expandMacros(displayStringSetting);
+                displayLabels.add(displayLabel);
+            }
+        }
+        return displayLabels;
+    }
+
 
 
     private void processUserPhotoImageRequest(final PwmRequest pwmRequest)
     private void processUserPhotoImageRequest(final PwmRequest pwmRequest)
-            throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException {
+            throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
+    {
         final String userKey = pwmRequest.readParameterAsString("userKey");
         final String userKey = pwmRequest.readParameterAsString("userKey");
         if (userKey.length() < 1) {
         if (userKey.length() < 1) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing");
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing");
@@ -812,7 +573,7 @@ public class PeopleSearchServlet extends PwmServlet {
 
 
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getConfig());
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getConfig());
         try {
         try {
-            checkIfUserIdentityViewable(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
+            checkIfUserIdentityViewable(pwmRequest, userIdentity);
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "error during photo request while checking if requested userIdentity is within search scope: " + e.getMessage());
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "error during photo request while checking if requested userIdentity is within search scope: " + e.getMessage());
             LOGGER.error(pwmRequest, errorInformation);
             LOGGER.error(pwmRequest, errorInformation);
@@ -820,9 +581,9 @@ public class PeopleSearchServlet extends PwmServlet {
             return;
             return;
         }
         }
 
 
-        LOGGER.info(pwmRequest, "received user photo request to view user " + userIdentity.toString());
+        LOGGER.debug(pwmRequest, "received user photo request to view user " + userIdentity.toString());
 
 
-        final PhotoData photoData;
+        final PhotoDataBean photoData;
         try {
         try {
             photoData = readPhotoDataFromLdap(pwmRequest, userIdentity);
             photoData = readPhotoDataFromLdap(pwmRequest, userIdentity);
         } catch (PwmOperationalException e) {
         } catch (PwmOperationalException e) {
@@ -834,10 +595,11 @@ public class PeopleSearchServlet extends PwmServlet {
 
 
         OutputStream outputStream = null;
         OutputStream outputStream = null;
         try {
         try {
-            final int expireSeconds = 10 * 60;
-            pwmRequest.getPwmResponse().getHttpServletResponse().setContentType(photoData.getMimeType());
-            pwmRequest.getPwmResponse().getHttpServletResponse().setDateHeader("Expires", System.currentTimeMillis() + (expireSeconds * 1000l));
-            pwmRequest.getPwmResponse().getHttpServletResponse().setHeader("Cache-Control", "public, max-age=" + expireSeconds);
+            final long maxCacheSeconds = pwmRequest.getConfig().readSettingAsLong(PwmSetting.PEOPLE_SEARCH_MAX_CACHE_SECONDS);
+            final HttpServletResponse resp = pwmRequest.getPwmResponse().getHttpServletResponse();
+            resp.setContentType(photoData.getMimeType());
+            resp.setDateHeader("Expires", System.currentTimeMillis() + (maxCacheSeconds * 1000l));
+            resp.setHeader("Cache-Control", "public, max-age=" + maxCacheSeconds);
 
 
             outputStream = pwmRequest.getPwmResponse().getOutputStream();
             outputStream = pwmRequest.getPwmResponse().getOutputStream();
             outputStream.write(photoData.getContents());
             outputStream.write(photoData.getContents());
@@ -850,49 +612,36 @@ public class PeopleSearchServlet extends PwmServlet {
     }
     }
 
 
     private static Map<String,AttributeDetailBean> convertResultMapToBeans(
     private static Map<String,AttributeDetailBean> convertResultMapToBeans(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final UserIdentity userIdentity,
             final UserIdentity userIdentity,
             final List<FormConfiguration> detailForm,
             final List<FormConfiguration> detailForm,
             final Map<String, String> searchResults
             final Map<String, String> searchResults
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException
             throws ChaiUnavailableException, PwmUnrecoverableException
     {
     {
-        final int MAX_VALUES = Integer.parseInt(pwmApplication.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_MAX_VALUE_COUNT));
-        final Set<String> searchAttributes = getSearchAttributes(pwmApplication.getConfig());
+        final Set<String> searchAttributes = getSearchAttributes(pwmRequest.getConfig());
         final Map<String,AttributeDetailBean> returnObj = new LinkedHashMap<>();
         final Map<String,AttributeDetailBean> returnObj = new LinkedHashMap<>();
         for (FormConfiguration formConfiguration : detailForm) {
         for (FormConfiguration formConfiguration : detailForm) {
             if (formConfiguration.isRequired() || searchResults.containsKey(formConfiguration.getName())) {
             if (formConfiguration.isRequired() || searchResults.containsKey(formConfiguration.getName())) {
                 AttributeDetailBean bean = new AttributeDetailBean();
                 AttributeDetailBean bean = new AttributeDetailBean();
                 bean.setName(formConfiguration.getName());
                 bean.setName(formConfiguration.getName());
-                bean.setLabel(formConfiguration.getLabel(pwmSession.getSessionStateBean().getLocale()));
+                bean.setLabel(formConfiguration.getLabel(pwmRequest.getLocale()));
                 bean.setType(formConfiguration.getType());
                 bean.setType(formConfiguration.getType());
                 if (searchAttributes.contains(formConfiguration.getName())) {
                 if (searchAttributes.contains(formConfiguration.getName())) {
                     bean.setSearchable(true);
                     bean.setSearchable(true);
                 }
                 }
                 if (formConfiguration.getType() == FormConfiguration.Type.userDN) {
                 if (formConfiguration.getType() == FormConfiguration.Type.userDN) {
                     if (searchResults.containsKey(formConfiguration.getName())) {
                     if (searchResults.containsKey(formConfiguration.getName())) {
-                        final ChaiUser chaiUser = getChaiUser(pwmApplication, pwmSession, userIdentity);
-                        final Set<String> values;
-                        try {
-                            values = chaiUser.readMultiStringAttribute(formConfiguration.getName());
-                            final TreeMap<String, UserReferenceBean> userReferences = new TreeMap<>();
-                            for (final String value : values) {
-                                if (userReferences.size() < MAX_VALUES) {
-                                    final UserIdentity loopIdentity = new UserIdentity(value,
-                                            userIdentity.getLdapProfileID());
-                                    final String displayValue = figureDisplaynameValue(pwmApplication, pwmSession,
-                                            loopIdentity);
-                                    final UserReferenceBean userReference = new UserReferenceBean();
-                                    userReference.setUserKey(loopIdentity.toObfuscatedKey(pwmApplication.getConfig()));
-                                    userReference.setDisplayName(displayValue);
-                                    userReferences.put(displayValue, userReference);
-                                }
-                            }
-                            bean.setUserReferences(userReferences.values());
-                        } catch (ChaiOperationException e) {
-                            LOGGER.error(pwmSession, "error during user detail lookup: " + e.getMessage());
+                        final List<UserIdentity> identityValues = readUserDNAttributeValues(pwmRequest, userIdentity, formConfiguration.getName());
+                        final TreeMap<String, UserReferenceBean> userReferences = new TreeMap<>();
+                        for (final UserIdentity loopIdentity : identityValues) {
+                            final String displayValue = figureDisplaynameValue(pwmRequest, loopIdentity);
+                            final UserReferenceBean userReference = new UserReferenceBean();
+                            userReference.setUserKey(loopIdentity.toObfuscatedKey(pwmRequest.getConfig()));
+                            userReference.setDisplayName(displayValue);
+                            userReferences.put(displayValue, userReference);
                         }
                         }
+                        bean.setUserReferences(userReferences.values());
                     }
                     }
                 } else {
                 } else {
                     bean.setValue(searchResults.containsKey(formConfiguration.getName()) ? searchResults.get(
                     bean.setValue(searchResults.containsKey(formConfiguration.getName()) ? searchResults.get(
@@ -904,72 +653,67 @@ public class PeopleSearchServlet extends PwmServlet {
         return returnObj;
         return returnObj;
     }
     }
 
 
-    private static boolean useProxy(final PwmApplication pwmApplication, final PwmSession pwmSession) {
+    private static boolean useProxy(final PwmRequest pwmRequest) {
 
 
-        final boolean useProxy = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_USE_PROXY);
-        final boolean publicAccessEnabled = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_ENABLE_PUBLIC);
+        final boolean useProxy = pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_USE_PROXY);
+        final boolean publicAccessEnabled = pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_ENABLE_PUBLIC);
 
 
-        if (useProxy) {
-            return true;
-        }
+        return useProxy || !pwmRequest.isAuthenticated() && publicAccessEnabled;
 
 
-        return !pwmSession.getSessionStateBean().isAuthenticated() && publicAccessEnabled;
     }
     }
 
 
     private static ChaiUser getChaiUser(
     private static ChaiUser getChaiUser(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final UserIdentity userIdentity
             final UserIdentity userIdentity
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        final boolean useProxy = useProxy(pwmApplication, pwmSession);
+        final boolean useProxy = useProxy(pwmRequest);
         return useProxy
         return useProxy
-                ? pwmApplication.getProxiedChaiUser(userIdentity)
-                : pwmSession.getSessionManager().getActor(pwmApplication, userIdentity);
+                ? pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity)
+                : pwmRequest.getPwmSession().getSessionManager().getActor(pwmRequest.getPwmApplication(), userIdentity);
     }
     }
 
 
     private static MacroMachine getMacroMachine(
     private static MacroMachine getMacroMachine(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final UserIdentity userIdentity
             final UserIdentity userIdentity
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        final ChaiUser chaiUser = getChaiUser(pwmApplication, pwmSession, userIdentity);
+        final ChaiUser chaiUser = getChaiUser(pwmRequest, userIdentity);
         final UserInfoBean userInfoBean;
         final UserInfoBean userInfoBean;
-        if (Boolean.parseBoolean(pwmApplication.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_DISPLAYNAME_USEALLMACROS))) {
-            final Locale locale = pwmSession.getSessionStateBean().getLocale();
-            final ChaiProvider chaiProvider = pwmApplication.getProxiedChaiUser(userIdentity).getChaiProvider();
+        if (Boolean.parseBoolean(pwmRequest.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_DISPLAYNAME_USEALLMACROS))) {
+            final Locale locale = pwmRequest.getLocale();
+            final ChaiProvider chaiProvider = pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity).getChaiProvider();
             userInfoBean = new UserInfoBean();
             userInfoBean = new UserInfoBean();
-            final UserStatusReader userStatusReader = new UserStatusReader(pwmApplication, pwmSession.getLabel());
+            final UserStatusReader userStatusReader = new UserStatusReader(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
             userStatusReader.populateUserInfoBean(userInfoBean, locale, userIdentity, chaiProvider);
             userStatusReader.populateUserInfoBean(userInfoBean, locale, userIdentity, chaiProvider);
         } else {
         } else {
             userInfoBean = null;
             userInfoBean = null;
         }
         }
         UserDataReader userDataReader = new LdapUserDataReader(userIdentity, chaiUser);
         UserDataReader userDataReader = new LdapUserDataReader(userIdentity, chaiUser);
-        return new MacroMachine(pwmApplication, pwmSession.getLabel(), userInfoBean, null, userDataReader);
+        return new MacroMachine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), userInfoBean, null, userDataReader);
     }
     }
 
 
     private static void checkIfUserIdentityViewable(
     private static void checkIfUserIdentityViewable(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final UserIdentity userIdentity
             final UserIdentity userIdentity
     )
     )
-            throws  PwmUnrecoverableException, PwmOperationalException {
-        final String filterSetting = getSearchFilter(pwmApplication.getConfig());
+            throws  PwmUnrecoverableException, PwmOperationalException
+    {
+        final String filterSetting = getSearchFilter(pwmRequest.getConfig());
         String filterString = filterSetting.replace(PwmConstants.VALUE_REPLACEMENT_USERNAME, "*");
         String filterString = filterSetting.replace(PwmConstants.VALUE_REPLACEMENT_USERNAME, "*");
         while (filterString.contains("**")) {
         while (filterString.contains("**")) {
             filterString = filterString.replace("**", "*");
             filterString = filterString.replace("**", "*");
         }
         }
 
 
-        final boolean match = LdapPermissionTester.testQueryMatch(pwmApplication, pwmSession.getLabel(), userIdentity, filterString);
+        final boolean match = LdapPermissionTester.testQueryMatch(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), userIdentity, filterString);
         if (!match) {
         if (!match) {
             throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "requested userDN is not available within configured search filter"));
             throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "requested userDN is not available within configured search filter"));
         }
         }
     }
     }
 
 
-    private static PhotoData readPhotoDataFromLdap(
+    private static PhotoDataBean readPhotoDataFromLdap(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final UserIdentity userIdentity
             final UserIdentity userIdentity
     )
     )
@@ -980,19 +724,20 @@ public class PeopleSearchServlet extends PwmServlet {
             throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "ldap photo attribute is not configured"));
             throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "ldap photo attribute is not configured"));
         }
         }
 
 
-        byte[][] photoData;
+        byte[] photoData;
         String mimeType;
         String mimeType;
         try {
         try {
-            final ChaiUser chaiUser = getChaiUser(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
-            photoData = chaiUser.readMultiByteAttribute(attribute);
-            if (photoData == null || photoData.length == 0 || photoData[0].length == 0) {
-                throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "user has no photo data stored in LDAP attribute"));
+            final ChaiUser chaiUser = getChaiUser(pwmRequest, userIdentity);
+            final byte[][] photoAttributeData = chaiUser.readMultiByteAttribute(attribute);
+            if (photoAttributeData == null || photoAttributeData.length == 0 || photoAttributeData[0].length == 0) {
+                throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "user has no photo data stored in LDAP attribute"));
             }
             }
-            mimeType = URLConnection.guessContentTypeFromStream(new ByteArrayInputStream(photoData[0]));
+            photoData = photoAttributeData[0];
+            mimeType = URLConnection.guessContentTypeFromStream(new ByteArrayInputStream(photoData));
         } catch (IOException | ChaiOperationException e) {
         } catch (IOException | ChaiOperationException e) {
             throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_UNKNOWN, "error reading user photo ldap attribute: " + e.getMessage()));
             throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_UNKNOWN, "error reading user photo ldap attribute: " + e.getMessage()));
         }
         }
-        return new PhotoData(mimeType, photoData[0]);
+        return new PhotoDataBean(mimeType, photoData);
     }
     }
 
 
     private static String getSearchFilter(final Configuration configuration) {
     private static String getSearchFilter(final Configuration configuration) {
@@ -1022,47 +767,122 @@ public class PeopleSearchServlet extends PwmServlet {
         return Collections.unmodifiableSet(new HashSet<>(searchResultForm));
         return Collections.unmodifiableSet(new HashSet<>(searchResultForm));
     }
     }
 
 
-    private static UserTreeReferenceBean userDetailToTreeReference(
+    private static OrgChartReferenceBean makeOrgChartReferenceForIdentity(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final UserIdentity userIdentity,
             final UserIdentity userIdentity,
             final String nextNodeAttribute
             final String nextNodeAttribute
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        final UserTreeReferenceBean userTreeReferenceBean = new UserTreeReferenceBean();
-        userTreeReferenceBean.setUserKey(userIdentity.toObfuscatedKey(pwmRequest.getConfig()));
-        userTreeReferenceBean.setPhotoURL(figurePhotoURL(pwmRequest, userIdentity));
+        final OrgChartReferenceBean orgChartReferenceBean = new OrgChartReferenceBean();
+        orgChartReferenceBean.setUserKey(userIdentity.toObfuscatedKey(pwmRequest.getConfig()));
+        orgChartReferenceBean.setPhotoURL(figurePhotoURL(pwmRequest, userIdentity));
 
 
-        {
-            final List<String> displayLabels = new ArrayList<>();
-            final List<String> displayStringSettings = pwmRequest.getConfig().readSettingAsStringArray(PwmSetting.PEOPLE_SEARCH_ORGCHART_DISPLAY_VALUES);
-            if (displayStringSettings != null) {
-                final MacroMachine macroMachine = getMacroMachine(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
-                for (final String displayStringSetting : displayStringSettings) {
-                    final String displayLabel = macroMachine.expandMacros(displayStringSetting);
-                    displayLabels.add(displayLabel);
-                }
-            }
-            userTreeReferenceBean.setDisplayNames(displayLabels);
-        }
+            final List<String> displayLabels = figureDisplaynames(pwmRequest, userIdentity);
+            orgChartReferenceBean.setDisplayNames(displayLabels);
 
 
-        userTreeReferenceBean.setHasMoreNodes(false);
+        orgChartReferenceBean.setHasMoreNodes(false);
         try {
         try {
-            final UserDataReader userDataReader = new LdapUserDataReader(userIdentity, getChaiUser(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity));
+            final UserDataReader userDataReader = new LdapUserDataReader(userIdentity, getChaiUser(pwmRequest, userIdentity));
             final String nextNodeValue = userDataReader.readStringAttribute(nextNodeAttribute);
             final String nextNodeValue = userDataReader.readStringAttribute(nextNodeAttribute);
             if (nextNodeValue != null && !nextNodeValue.isEmpty()) {
             if (nextNodeValue != null && !nextNodeValue.isEmpty()) {
-                userTreeReferenceBean.setHasMoreNodes(true);
+                orgChartReferenceBean.setHasMoreNodes(true);
             }
             }
         } catch (ChaiException e) {
         } catch (ChaiException e) {
-            LOGGER.debug(pwmRequest, "error reading nextNodeAttribute during userTreeReference construction: " + e.getMessage());
+            LOGGER.debug(pwmRequest, "error reading nextNodeAttribute during orgChratReference construction: " + e.getMessage());
         }
         }
 
 
-        return userTreeReferenceBean;
+        return orgChartReferenceBean;
     }
     }
 
 
-    private static boolean orgChartIsEnabled(final Configuration config) {
-        final String orgChartParentAttr = config.readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_PARENT_ATTRIBUTE);
-        final String orgChartChildAttr = config.readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_CHILD_ATTRIBUTE);
-        return orgChartParentAttr != null && !orgChartParentAttr.isEmpty() && orgChartChildAttr != null && !orgChartChildAttr.isEmpty();
+    private static List<UserIdentity> readUserDNAttributeValues(
+            final PwmRequest pwmRequest,
+            final UserIdentity userIdentity,
+            final String attributeName
+    )
+            throws PwmUnrecoverableException
+    {
+
+        final List<UserIdentity> returnObj = new ArrayList<>();
+
+        final int MAX_VALUES = Integer.parseInt(pwmRequest.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_VALUE_MAXCOUNT));
+        final ChaiUser chaiUser = getChaiUser(pwmRequest, userIdentity);
+        final Set<String> ldapValues;
+        try {
+            ldapValues = chaiUser.readMultiStringAttribute(attributeName);
+        } catch (ChaiOperationException e) {
+            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, "error reading attribute value '" + attributeName + "', error:" +  e.getMessage()));
+        } catch (ChaiUnavailableException e) {
+            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, e.getMessage()));
+        }
+
+
+        final boolean checkUserDNValues = Boolean.parseBoolean(pwmRequest.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_MAX_VALUE_VERIFYUSERDN));
+        for (final String userDN : ldapValues) {
+            final UserIdentity loopIdentity = new UserIdentity(userDN, userIdentity.getLdapProfileID());
+            if (returnObj.size() < MAX_VALUES) {
+                try {
+                    if (checkUserDNValues) {
+                        checkIfUserIdentityViewable(pwmRequest, loopIdentity);
+                    }
+                    returnObj.add(loopIdentity);
+                } catch (PwmOperationalException e) {
+                    LOGGER.debug(pwmRequest, "discarding userDN " + userDN + " from attribute " + attributeName + " because it does not match search filter");
+                }
+            } else {
+                LOGGER.trace(pwmRequest, "discarding userDN " + userDN + " from attribute " + attributeName + " because maximum value count has been reached");
+            }
+
+        }
+        return returnObj;
+    }
+
+    private CacheKey makeCacheKey(
+            final PwmRequest pwmRequest,
+            final String operationIdentifer,
+            final String dataIdentifer
+    )
+            throws PwmUnrecoverableException
+    {
+        final UserIdentity userIdentity;
+        if (pwmRequest.isAuthenticated() && !useProxy(pwmRequest)) {
+            userIdentity = pwmRequest.getUserInfoIfLoggedIn();
+        } else {
+            userIdentity = null;
+        }
+        return CacheKey.makeCacheKey(
+                this.getClass(),
+                userIdentity,
+                operationIdentifer + "|" + SecureHelper.hash(dataIdentifer, SecureHelper.HashAlgorithm.SHA1));
+    }
+
+    private static class PeopleSearchConfiguration {
+        private final Configuration configuration;
+
+        public PeopleSearchConfiguration(Configuration configuration) {
+            this.configuration = configuration;
+        }
+
+        public String getPhotoAttribute() {
+            return configuration.readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_ATTRIBUTE);
+        }
+
+        public String getPhotoUrlOverride() {
+            return configuration.readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_URL_OVERRIDE);
+        }
+
+        public boolean isPhotosEnabled() {
+            return (getPhotoAttribute() != null
+                    && !getPhotoAttribute().isEmpty())
+                    ||
+                    (getPhotoUrlOverride() != null
+                    && !getPhotoUrlOverride().isEmpty());
+        }
+
+        public boolean orgChartIsEnabled() {
+            final String orgChartParentAttr = configuration.readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_PARENT_ATTRIBUTE);
+            final String orgChartChildAttr = configuration.readSettingAsString(PwmSetting.PEOPLE_SEARCH_ORGCHART_CHILD_ATTRIBUTE);
+            return orgChartParentAttr != null && !orgChartParentAttr.isEmpty() && orgChartChildAttr != null && !orgChartChildAttr.isEmpty();
+        }
     }
     }
 }
 }

+ 19 - 0
pwm/servlet/src/password/pwm/http/servlet/peoplesearch/PhotoDataBean.java

@@ -0,0 +1,19 @@
+package password.pwm.http.servlet.peoplesearch;
+
+class PhotoDataBean {
+    private final String mimeType;
+    private byte[] contents;
+
+    PhotoDataBean(String mimeType, byte[] contents) {
+        this.mimeType = mimeType;
+        this.contents = contents;
+    }
+
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    public byte[] getContents() {
+        return contents;
+    }
+}

+ 26 - 0
pwm/servlet/src/password/pwm/http/servlet/peoplesearch/SearchResultBean.java

@@ -0,0 +1,26 @@
+package password.pwm.http.servlet.peoplesearch;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+class SearchResultBean implements Serializable {
+    private List searchResults = new ArrayList<>();
+    private boolean sizeExceeded;
+
+    public List getSearchResults() {
+        return searchResults;
+    }
+
+    public void setSearchResults(List searchResults) {
+        this.searchResults = searchResults;
+    }
+
+    public boolean isSizeExceeded() {
+        return sizeExceeded;
+    }
+
+    public void setSizeExceeded(boolean sizeExceeded) {
+        this.sizeExceeded = sizeExceeded;
+    }
+}

+ 62 - 0
pwm/servlet/src/password/pwm/http/servlet/peoplesearch/UserDetailBean.java

@@ -0,0 +1,62 @@
+package password.pwm.http.servlet.peoplesearch;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+class UserDetailBean implements Serializable {
+    private List<String> displayNames;
+    private String userKey;
+    private Map<String, AttributeDetailBean> detail;
+    private String photoURL;
+    private boolean hasOrgChart;
+    private String orgChartParentKey;
+
+    public List<String> getDisplayNames() {
+        return displayNames;
+    }
+
+    public void setDisplayNames(List<String> displayNames) {
+        this.displayNames = displayNames;
+    }
+
+    public String getUserKey() {
+        return userKey;
+    }
+
+    public void setUserKey(String userKey) {
+        this.userKey = userKey;
+    }
+
+    public Map<String, AttributeDetailBean> getDetail() {
+        return detail;
+    }
+
+    public void setDetail(Map<String, AttributeDetailBean> detail) {
+        this.detail = detail;
+    }
+
+    public String getPhotoURL() {
+        return photoURL;
+    }
+
+    public void setPhotoURL(String photoURL) {
+        this.photoURL = photoURL;
+    }
+
+    public boolean isHasOrgChart() {
+        return hasOrgChart;
+    }
+
+    public void setHasOrgChart(boolean hasOrgChart) {
+        this.hasOrgChart = hasOrgChart;
+    }
+
+    public String getOrgChartParentKey() {
+        return orgChartParentKey;
+    }
+
+    public void setOrgChartParentKey(String orgChartParentKey) {
+        this.orgChartParentKey = orgChartParentKey;
+    }
+}

+ 24 - 0
pwm/servlet/src/password/pwm/http/servlet/peoplesearch/UserReferenceBean.java

@@ -0,0 +1,24 @@
+package password.pwm.http.servlet.peoplesearch;
+
+import java.io.Serializable;
+
+class UserReferenceBean implements Serializable {
+    private String userKey;
+    private String displayName;
+
+    public String getUserKey() {
+        return userKey;
+    }
+
+    public void setUserKey(String userKey) {
+        this.userKey = userKey;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+}

+ 8 - 2
pwm/servlet/src/password/pwm/i18n/Admin.properties

@@ -22,9 +22,9 @@
 
 
 # Strings found in "admin" viewable sections of the application
 # Strings found in "admin" viewable sections of the application
 
 
-Header_AdminUser=%1%  You are logged in as administrator.
+Header_AdminUser=You are logged in as administrator.
 Header_ConfigModeActive=%1% is in open configuration mode and is not secure.
 Header_ConfigModeActive=%1% is in open configuration mode and is not secure.
-Header_TrialMode=%1% Trial.
+Header_TrialMode=Trial Mode.
 Header_HealthWarningsPresent=System warnings exist, click to view.
 Header_HealthWarningsPresent=System warnings exist, click to view.
 IntruderRecordType_ADDRESS=Address
 IntruderRecordType_ADDRESS=Address
 IntruderRecordType_USERNAME=Username
 IntruderRecordType_USERNAME=Username
@@ -218,10 +218,16 @@ Statistic_Label.TokensSent=Tokens Issued
 Statistic_Description.TokensSent=Number of tokens used for any purpose issued and sent via email or SMS.
 Statistic_Description.TokensSent=Number of tokens used for any purpose issued and sent via email or SMS.
 Statistic_Label.TokensPassed=Tokens Claimed
 Statistic_Label.TokensPassed=Tokens Claimed
 Statistic_Description.TokensPassed=Number of tokens used for any purpose verified and claimed.
 Statistic_Description.TokensPassed=Number of tokens used for any purpose verified and claimed.
+Statistic_Label.PeopleSearchCacheHits=PeopleSearch Cache Hits
+Statistic_Description.PeopleSearchCacheHits=Number of cache hits when reading people search data.
+Statistic_Label.PeopleSearchCacheMisses=PeopleSearch Cache Misses
+Statistic_Description.PeopleSearchCacheMisses=Number of cache misses when reading people search data.
 Statistic_Label.PeopleSearchSearches=PeopleSearch Searches
 Statistic_Label.PeopleSearchSearches=PeopleSearch Searches
 Statistic_Description.PeopleSearchSearches=Number of directory searches executed using the people search module.
 Statistic_Description.PeopleSearchSearches=Number of directory searches executed using the people search module.
 Statistic_Label.PeopleSearchDetails=PeopleSearch Detail Views
 Statistic_Label.PeopleSearchDetails=PeopleSearch Detail Views
 Statistic_Description.PeopleSearchDetails=Number of detailed user views executed using the people search module.
 Statistic_Description.PeopleSearchDetails=Number of detailed user views executed using the people search module.
+Statistic_Label.PeopleSearchOrgChart=PeopleSearch Org Chart Views
+Statistic_Description.PeopleSearchOrgChart=Number of organisational chart views executed using the people search module.
 Statistic_Label.HelpdeskPasswordSet=Helpdesk Password Resets
 Statistic_Label.HelpdeskPasswordSet=Helpdesk Password Resets
 Statistic_Description.HelpdeskPasswordSet=Number of password modifications initiated using the helpdesk module.
 Statistic_Description.HelpdeskPasswordSet=Number of password modifications initiated using the helpdesk module.
 Statistic_Label.HelpdeskUserLookup=Helpdesk User Lookups
 Statistic_Label.HelpdeskUserLookup=Helpdesk User Lookups

+ 6 - 0
pwm/servlet/src/password/pwm/i18n/Config.properties

@@ -119,6 +119,12 @@ Warning_ValueIncorrectFormat=The value does not have the correct format.
 Warning_SmsTestData=The test that will be performed will include resolving configured macros (if any).  The macros will be resolved using data of the logged in user, and thus may include sensitive data.
 Warning_SmsTestData=The test that will be performed will include resolving configured macros (if any).  The macros will be resolved using data of the logged in user, and thus may include sensitive data.
 Tooltip_ResetButton=Return this setting to its default value.
 Tooltip_ResetButton=Return this setting to its default value.
 Tooltip_HelpButton=Show description for this setting.
 Tooltip_HelpButton=Show description for this setting.
+Tooltip_ModifiedNotice=This setting has been modified from the default value
+Tooltip_CancelEditorButton=Cancel changes and return to Configuration Manager
+Tooltip_SaveEditorButton=Save changes
+Tooltip_SetConfigPasswordButton=Set configuration password
+Tooltip_OpenReferenceDocButton=Open reference documentation
+Tooltip_OpenMacroHelpButton=Open macro help and reference
 Tooltip_Setting_Permission_Profile=Specify which of the defined LDAP profiles to use for the associated filter.  If <i>all</i>, all profiles will be checked for the associated filter.  If <i>default</i>, than only the default LDAP Profile will be checked for the associated search filter.
 Tooltip_Setting_Permission_Profile=Specify which of the defined LDAP profiles to use for the associated filter.  If <i>all</i>, all profiles will be checked for the associated filter.  If <i>default</i>, than only the default LDAP Profile will be checked for the associated search filter.
 Tooltip_Setting_Permission_Filter=A valid LDAP search filter.
 Tooltip_Setting_Permission_Filter=A valid LDAP search filter.
 Tooltip_Setting_Permission_Base=An optional LDAP Base DN for the search.  If supplied, only users under this LDAP Base DN will be considered a match.
 Tooltip_Setting_Permission_Base=An optional LDAP Base DN for the search.  If supplied, only users under this LDAP Base DN will be considered a match.

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

@@ -96,7 +96,7 @@ Error_MissingRandomResponse=Please add an additional random response.
 Error_BadCaptchaResponse=Incorrect verification code, please try again.
 Error_BadCaptchaResponse=Incorrect verification code, please try again.
 Error_CaptchaAPIError=An error occurred while validating captcha response.  Please close your browser and try again.  If this error occurs repeatedly contact your helpdesk.
 Error_CaptchaAPIError=An error occurred while validating captcha response.  Please close your browser and try again.  If this error occurs repeatedly contact your helpdesk.
 Error_InvalidConfig=The configuration is invalid or corrupt.  Please correct the error, or remove the configuration file.
 Error_InvalidConfig=The configuration is invalid or corrupt.  Please correct the error, or remove the configuration file.
-Error_InvalidFormID=Your browser session is invalid or has expired.  Please close your browser and then try again.
+Error_InvalidFormID=The browser session is invalid or has expired.  Please try again.
 Error_TokenMissingContact=There is no contact information available for your account.  Please contact your administrator.
 Error_TokenMissingContact=There is no contact information available for your account.  Please contact your administrator.
 Error_TokenIncorrect=Incorrect code, please try again.
 Error_TokenIncorrect=Incorrect code, please try again.
 Error_BadCurrentPassword=Current password is incorrect, please try again.
 Error_BadCurrentPassword=Current password is incorrect, please try again.

+ 5 - 2
pwm/servlet/src/password/pwm/util/PwmRandom.java

@@ -23,12 +23,11 @@
 package password.pwm.util;
 package password.pwm.util;
 
 
 import java.security.SecureRandom;
 import java.security.SecureRandom;
-import java.util.Random;
 import java.util.UUID;
 import java.util.UUID;
 
 
 public class PwmRandom {
 public class PwmRandom {
 
 
-    private final Random internalRand = new SecureRandom();
+    private final SecureRandom internalRand = new SecureRandom();
 
 
     private final static PwmRandom SINGLETON = new PwmRandom();
     private final static PwmRandom SINGLETON = new PwmRandom();
 
 
@@ -57,6 +56,10 @@ public class PwmRandom {
         return internalRand.nextBoolean();
         return internalRand.nextBoolean();
     }
     }
 
 
+    public String getAlgorithm() {
+        return internalRand.getAlgorithm();
+    }
+
     public String alphaNumericString(final int length) {
     public String alphaNumericString(final int length) {
         return alphaNumericString(ALPHANUMERIC_STRING,length);
         return alphaNumericString(ALPHANUMERIC_STRING,length);
     }
     }

+ 15 - 1
pwm/servlet/src/password/pwm/util/SecureHelper.java

@@ -67,6 +67,8 @@ public class SecureHelper {
 
 
     public enum BlockAlgorithm {
     public enum BlockAlgorithm {
         AES("AES"),
         AES("AES"),
+        AES_CHECKSUM("AES/CBC/PKCS5Padding"),
+        CONFIG("AES"),
 
 
         ;
         ;
 
 
@@ -160,6 +162,18 @@ public class SecureHelper {
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
+
+        return decryptStringValue(value, key, urlSafe, DEFAULT_BLOCK_ALGORITHM);
+    }
+
+    public static String decryptStringValue(
+            final String value,
+            final SecretKey key,
+            final boolean urlSafe,
+            final BlockAlgorithm blockAlgorithm
+    )
+            throws PwmUnrecoverableException
+    {
         try {
         try {
             if (value == null || value.length() < 1) {
             if (value == null || value.length() < 1) {
                 return "";
                 return "";
@@ -168,7 +182,7 @@ public class SecureHelper {
             final byte[] decoded = urlSafe
             final byte[] decoded = urlSafe
                     ? StringUtil.base64Decode(value, StringUtil.Base64Options.URL_SAFE,StringUtil.Base64Options.GZIP)
                     ? StringUtil.base64Decode(value, StringUtil.Base64Options.URL_SAFE,StringUtil.Base64Options.GZIP)
                     : StringUtil.base64Decode(value);
                     : StringUtil.base64Decode(value);
-            return decryptBytes(decoded, key);
+            return decryptBytes(decoded, key, blockAlgorithm);
         } catch (Exception e) {
         } catch (Exception e) {
             final String errorMsg = "unexpected error performing simple decrypt operation: " + e.getMessage();
             final String errorMsg = "unexpected error performing simple decrypt operation: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_CRYPT_ERROR, errorMsg);
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_CRYPT_ERROR, errorMsg);

+ 1 - 3
pwm/servlet/src/password/pwm/util/cli/MainClass.java

@@ -350,10 +350,8 @@ public class MainClass {
             throws LocalDBException, PwmUnrecoverableException
             throws LocalDBException, PwmUnrecoverableException
     {
     {
         final PwmApplication.MODE mode = readonly ? PwmApplication.MODE.READ_ONLY : PwmApplication.MODE.RUNNING;
         final PwmApplication.MODE mode = readonly ? PwmApplication.MODE.READ_ONLY : PwmApplication.MODE.RUNNING;
-        final PwmApplication pwmApplication = new PwmApplication.PwmEnvironment()
-                .setConfig(config)
+        final PwmApplication pwmApplication = new PwmApplication.PwmEnvironment(config, applicationPath)
                 .setApplicationMode(mode)
                 .setApplicationMode(mode)
-                .setApplicationPath(applicationPath)
                 .setApplicationPathType(MAIN_OPTIONS.applicationPathType)
                 .setApplicationPathType(MAIN_OPTIONS.applicationPathType)
                 .setInitLogging(false)
                 .setInitLogging(false)
                 .setConfigurationFile(configurationFile)
                 .setConfigurationFile(configurationFile)

+ 3 - 0
pwm/servlet/src/password/pwm/util/stats/Statistic.java

@@ -80,8 +80,11 @@ public enum Statistic {
     RECOVERY_TOKENS_FAILED              (Type.INCREMENTOR, "RecoveryTokensFailed", null),
     RECOVERY_TOKENS_FAILED              (Type.INCREMENTOR, "RecoveryTokensFailed", null),
     RECOVERY_OTP_PASSED                 (Type.INCREMENTOR, "RecoveryOTPPassed", new ConfigSettingDetail(PwmSetting.OTP_ENABLED)),
     RECOVERY_OTP_PASSED                 (Type.INCREMENTOR, "RecoveryOTPPassed", new ConfigSettingDetail(PwmSetting.OTP_ENABLED)),
     RECOVERY_OTP_FAILED                 (Type.INCREMENTOR, "RecoveryOTPFailed", new ConfigSettingDetail(PwmSetting.OTP_ENABLED)),
     RECOVERY_OTP_FAILED                 (Type.INCREMENTOR, "RecoveryOTPFailed", new ConfigSettingDetail(PwmSetting.OTP_ENABLED)),
+    PEOPLESEARCH_CACHE_HITS             (Type.INCREMENTOR, "PeopleSearchCacheHits", new ConfigSettingDetail(PwmSetting.PEOPLE_SEARCH_ENABLE)),
+    PEOPLESEARCH_CACHE_MISSES           (Type.INCREMENTOR, "PeopleSearchCacheMisses", new ConfigSettingDetail(PwmSetting.PEOPLE_SEARCH_ENABLE)),
     PEOPLESEARCH_SEARCHES               (Type.INCREMENTOR, "PeopleSearchSearches", new ConfigSettingDetail(PwmSetting.PEOPLE_SEARCH_ENABLE)),
     PEOPLESEARCH_SEARCHES               (Type.INCREMENTOR, "PeopleSearchSearches", new ConfigSettingDetail(PwmSetting.PEOPLE_SEARCH_ENABLE)),
     PEOPLESEARCH_DETAILS                (Type.INCREMENTOR, "PeopleSearchDetails", new ConfigSettingDetail(PwmSetting.PEOPLE_SEARCH_ENABLE)),
     PEOPLESEARCH_DETAILS                (Type.INCREMENTOR, "PeopleSearchDetails", new ConfigSettingDetail(PwmSetting.PEOPLE_SEARCH_ENABLE)),
+    PEOPLESEARCH_ORGCHART               (Type.INCREMENTOR, "PeopleSearchOrgChart", new ConfigSettingDetail(PwmSetting.PEOPLE_SEARCH_ENABLE)),
     HELPDESK_PASSWORD_SET               (Type.INCREMENTOR, "HelpdeskPasswordSet", null),
     HELPDESK_PASSWORD_SET               (Type.INCREMENTOR, "HelpdeskPasswordSet", null),
     HELPDESK_USER_LOOKUP                (Type.INCREMENTOR, "HelpdeskUserLookup", null),
     HELPDESK_USER_LOOKUP                (Type.INCREMENTOR, "HelpdeskUserLookup", null),
     HELPDESK_TOKENS_SENT                (Type.INCREMENTOR, "HelpdeskTokenSent", null),
     HELPDESK_TOKENS_SENT                (Type.INCREMENTOR, "HelpdeskTokenSent", null),

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

@@ -469,7 +469,7 @@ public class StatisticsManager implements PwmService {
             final Configuration config = pwmApplication.getConfig();
             final Configuration config = pwmApplication.getConfig();
             final List<String> configuredSettings = new ArrayList<>();
             final List<String> configuredSettings = new ArrayList<>();
             for (final PwmSetting pwmSetting : config.nonDefaultSettings()) {
             for (final PwmSetting pwmSetting : config.nonDefaultSettings()) {
-                if (!config.isDefaultValue(pwmSetting)) {
+                if (!pwmSetting.getCategory().hasProfiles() && !config.isDefaultValue(pwmSetting)) {
                     configuredSettings.add(pwmSetting.getKey());
                     configuredSettings.add(pwmSetting.getKey());
                 }
                 }
             }
             }

+ 0 - 2
pwm/servlet/web/WEB-INF/jsp/admin-analysis.jsp

@@ -93,8 +93,6 @@
                             <pwm:if test="showIcons"><span class="btn-icon fa fa-refresh">&nbsp;</span></pwm:if>
                             <pwm:if test="showIcons"><span class="btn-icon fa fa-refresh">&nbsp;</span></pwm:if>
                             <pwm:display key="Button_Refresh" bundle="Admin"/>
                             <pwm:display key="Button_Refresh" bundle="Admin"/>
                         </button>
                         </button>
-                    </div>
-                    <div style="text-align: center">
                         <form action="<pwm:url url="Administration"/>" method="post">
                         <form action="<pwm:url url="Administration"/>" method="post">
                             <button type="submit" class="btn" id="button-downloadUserReportCsv">
                             <button type="submit" class="btn" id="button-downloadUserReportCsv">
                                 <pwm:if test="showIcons"><span class="btn-icon fa fa-download">&nbsp;</span></pwm:if>
                                 <pwm:if test="showIcons"><span class="btn-icon fa fa-download">&nbsp;</span></pwm:if>

+ 5 - 5
pwm/servlet/web/WEB-INF/jsp/configeditor.jsp

@@ -47,19 +47,19 @@
             <div id="header-title">
             <div id="header-title">
                 <%=PwmConstants.PWM_APP_NAME%> Configuration Editor <span id="currentPageDisplay"></span>
                 <%=PwmConstants.PWM_APP_NAME%> Configuration Editor <span id="currentPageDisplay"></span>
                 <span style="visibility: hidden" id="working_icon" class="headerIcon fa fa-cog fa-spin"></span>
                 <span style="visibility: hidden" id="working_icon" class="headerIcon fa fa-cog fa-spin"></span>
-                <div class="headerIcon" id="cancelButton_icon">
+                <div class="headerIcon" id="cancelButton_icon" title="<pwm:display key="Tooltip_CancelEditorButton" bundle="Config"/>">
                     <span class="fa fa-times"></span>
                     <span class="fa fa-times"></span>
                 </div>
                 </div>
-                <div class="headerIcon" id="saveButton_icon">
+                <div class="headerIcon" id="saveButton_icon" title="<pwm:display key="Tooltip_SaveEditorButton" bundle="Config"/>">
                     <span class="fa fa-save"></span>
                     <span class="fa fa-save"></span>
                 </div>
                 </div>
-                <div class="headerIcon" id="setPassword_icon">
+                <div class="headerIcon" id="setPassword_icon" title="<pwm:display key="Tooltip_SetConfigPasswordButton" bundle="Config"/>">
                     <span class="fa fa-key"></span>
                     <span class="fa fa-key"></span>
                 </div>
                 </div>
-                <div class="headerIcon" id="referenceDoc_icon">
+                <div class="headerIcon" id="referenceDoc_icon" title="<pwm:display key="Tooltip_OpenReferenceDocButton" bundle="Config"/>">
                     <span class="fa fa-book"></span>
                     <span class="fa fa-book"></span>
                 </div>
                 </div>
-                <div class="headerIcon" id="macroDoc_icon">
+                <div class="headerIcon" id="macroDoc_icon" title="<pwm:display key="Tooltip_OpenMacroHelpButton" bundle="Config"/>">
                     <span class="fa fa-magic"></span>
                     <span class="fa fa-magic"></span>
                 </div>
                 </div>
                 <span id="idle_status" class="editorIdleStatus">
                 <span id="idle_status" class="editorIdleStatus">

+ 28 - 38
pwm/servlet/web/WEB-INF/jsp/fragment/header-warnings.jsp

@@ -28,9 +28,7 @@
 <%
 <%
     boolean includeHeader = false;
     boolean includeHeader = false;
     boolean adminUser = false;
     boolean adminUser = false;
-    boolean headerVisibility = true;
     boolean configMode = false;
     boolean configMode = false;
-    boolean showOpenCloseButtons = true;
     try {
     try {
         final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
         final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
         final PwmApplication.MODE applicationMode = pwmRequest.getPwmApplication().getApplicationMode();
         final PwmApplication.MODE applicationMode = pwmRequest.getPwmApplication().getApplicationMode();
@@ -40,18 +38,9 @@
             if (!new PwmURL(request).isConfigManagerURL()) {
             if (!new PwmURL(request).isConfigManagerURL()) {
                 if (configMode || PwmConstants.TRIAL_MODE) {
                 if (configMode || PwmConstants.TRIAL_MODE) {
                     includeHeader = true;
                     includeHeader = true;
-                    showOpenCloseButtons = false;
                 } else if (pwmRequest.isAuthenticated()) {
                 } else if (pwmRequest.isAuthenticated()) {
                     if (adminUser && !pwmRequest.isForcedPageView()) {
                     if (adminUser && !pwmRequest.isForcedPageView()) {
                         includeHeader = true;
                         includeHeader = true;
-                        final String headerVisibilityCookie = pwmRequest.readCookie("headerVisibility");
-                        if (headerVisibilityCookie != null) {
-                            if (headerVisibilityCookie.equals("hide")) {
-                                headerVisibility = false;
-                            } else if (headerVisibilityCookie.equals("show")) {
-                                headerVisibility = true;
-                            }
-                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -70,52 +59,53 @@
         });
         });
     </script>
     </script>
 </pwm:script>
 </pwm:script>
-<div id="header-warning" style="<%=headerVisibility?"":"display: none"%>">
-    <span style="cursor:pointer; white-space: nowrap">
-        <a class="btn" id="header_configManagerButton">
+<div id="header-warning" style="display: none">
+    <div class="header-warning-buttons">
+        <a class="header-warning-button" id="header_configManagerButton">
             <pwm:if test="showIcons"><span class="btn-icon fa fa-gears"></span></pwm:if>
             <pwm:if test="showIcons"><span class="btn-icon fa fa-gears"></span></pwm:if>
             <pwm:display key="MenuItem_ConfigManager" bundle="Admin"/>
             <pwm:display key="MenuItem_ConfigManager" bundle="Admin"/>
         </a>
         </a>
-    </span>
-    &nbsp;&nbsp;
-    <span style="cursor:pointer; white-space: nowrap">
-        <a class="btn" id="header_configEditorButton">
+        <a class="header-warning-button" id="header_configEditorButton">
             <pwm:if test="showIcons"><span class="btn-icon fa fa-edit"></span></pwm:if>
             <pwm:if test="showIcons"><span class="btn-icon fa fa-edit"></span></pwm:if>
             <pwm:display key="MenuItem_ConfigEditor" bundle="Admin"/>
             <pwm:display key="MenuItem_ConfigEditor" bundle="Admin"/>
         </a>
         </a>
-    </span>
-    <% if (adminUser) { %>
-    &nbsp;&nbsp;
-    <span style="cursor:pointer; white-space: nowrap">
-        <a class="btn" id="header_openLogViewerButton">
+        <% if (adminUser) { %>
+        <a class="header-warning-button" id="header_openLogViewerButton">
             <pwm:if test="showIcons"><span class="btn-icon fa fa-list-alt"></span></pwm:if>
             <pwm:if test="showIcons"><span class="btn-icon fa fa-list-alt"></span></pwm:if>
             <pwm:display key="MenuItem_ViewLog" bundle="Config"/>
             <pwm:display key="MenuItem_ViewLog" bundle="Config"/>
+            &nbsp;
+            <pwm:if test="showIcons"><span class="btn-icon fa fa-external-link"></span></pwm:if>
         </a>
         </a>
-    </span>
-    <% } %>
-    <br/>
-    <br/>
+        <a class="header-admin-button" href="<pwm:url url="/private/admin"/>">
+            <pwm:if test="showIcons"><span class="btn-icon fa fa-list-alt"></span></pwm:if>
+            <pwm:display key="Title_Admin"/>
+        </a>
+        <% } %>
+    </div>
     <span id="header-warning-message" style="padding-right: 15px; font-weight: bold">
     <span id="header-warning-message" style="padding-right: 15px; font-weight: bold">
     <% if (PwmConstants.TRIAL_MODE) { %>
     <% if (PwmConstants.TRIAL_MODE) { %>
-    <pwm:display key="Header_TrialMode" bundle="Admin" value1="<%=PwmConstants.PWM_APP_NAME_VERSION%>"/>
+    <pwm:display key="Header_TrialMode" bundle="Admin" value1="<%=PwmConstants.PWM_APP_NAME%>"/>
     <% } else if (configMode) { %>
     <% } else if (configMode) { %>
-    <pwm:display key="Header_ConfigModeActive" bundle="Admin" value1="<%=PwmConstants.PWM_APP_NAME_VERSION%>"/>
-    &nbsp;&nbsp;<pwm:if test="showIcons"><span id="icon-configModeHelp" class="btn-icon fa fa-question-circle"></span></pwm:if>
+    <pwm:display key="Header_ConfigModeActive" bundle="Admin" value1="<%=PwmConstants.PWM_APP_NAME%>"/>
+
+    <pwm:if test="showIcons"><span id="icon-configModeHelp" class="btn-icon fa fa-question-circle"></span></pwm:if>
+        <br/><br/>
     <% } else if (adminUser) { %>
     <% } else if (adminUser) { %>
-    <pwm:display key="Header_AdminUser" bundle="Admin" value1="<%=PwmConstants.PWM_APP_NAME_VERSION%>"/>
+    <pwm:display key="Header_AdminUser" bundle="Admin" value1="<%=PwmConstants.PWM_APP_NAME%>"/>
     <% } %>
     <% } %>
     </span>
     </span>
-    <div id="panel-header-healthData" style="cursor: pointer">
-    </div>
-    <% if (showOpenCloseButtons) { %>
+    <div id="panel-header-healthData" style="cursor: pointer"></div>
+    <% if (includeHeader) { %>
     <div id="button-closeHeader">
     <div id="button-closeHeader">
-        <span class="fa fa-chevron-circle-up"></span>
+        <span class="fa fa-chevron-circle-right"></span>
     </div>
     </div>
     <% } %>
     <% } %>
+    <br/>
+    <%=PwmConstants.PWM_APP_NAME_VERSION%>
 </div>
 </div>
-<% if (showOpenCloseButtons) { %>
-<div id="button-openHeader" style="<%=headerVisibility?"display: none":""%>">
-    <span class="fa fa-chevron-circle-down"></span>
+<% if (includeHeader) { %>
+<div id="button-openHeader">
+    <span class="fa fa-chevron-circle-left"></span>
 </div>
 </div>
 <% } %>
 <% } %>
 <% } %>
 <% } %>

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

@@ -735,13 +735,13 @@
 <pwm:script>
 <pwm:script>
     <script type="text/javascript">
     <script type="text/javascript">
         function initiateChangePasswordDialog() {
         function initiateChangePasswordDialog() {
-            <% if (SETTING_PW_UI_MODE == HelpdeskUIMode.autogen) { %>
-            PWM_HELPDESK.generatePasswordPopup();
-            <% } else if (SETTING_PW_UI_MODE == HelpdeskUIMode.random) { %>
-            PWM_HELPDESK.setRandomPasswordPopup();
-            <% } else { %>
-            PWM_HELPDESK.changePasswordPopup();
-            <% } %>
+            if (PWM_VAR['helpdesk_setting_PwUiMode'] == 'autogen') {
+                PWM_HELPDESK.generatePasswordPopup();
+            } else if (PWM_VAR['helpdesk_setting_PwUiMode'] == 'random') {
+                PWM_HELPDESK.setRandomPasswordPopup();
+            } else {
+                PWM_HELPDESK.changePasswordPopup();
+            }
         }
         }
 
 
         PWM_GLOBAL['startupFunctions'].push(function(){
         PWM_GLOBAL['startupFunctions'].push(function(){
@@ -751,6 +751,7 @@
                 PWM_VAR['helpdesk_username'] = '<%=StringUtil.escapeJS(searchedUserInfo.getUsername())%>';
                 PWM_VAR['helpdesk_username'] = '<%=StringUtil.escapeJS(searchedUserInfo.getUsername())%>';
                 PWM_VAR['helpdesk_setting_clearResponses'] = '<%=helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_CLEAR_RESPONSES,HelpdeskClearResponseMode.class)%>';
                 PWM_VAR['helpdesk_setting_clearResponses'] = '<%=helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_CLEAR_RESPONSES,HelpdeskClearResponseMode.class)%>';
                 PWM_VAR['helpdesk_setting_PwUiMode'] = '<%=helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_SET_PASSWORD_MODE,HelpdeskUIMode.class) %>';
                 PWM_VAR['helpdesk_setting_PwUiMode'] = '<%=helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_SET_PASSWORD_MODE,HelpdeskUIMode.class) %>';
+                PWM_VAR['helpdesk_setting_maskPasswords'] = false;
             });
             });
         });
         });
     </script>
     </script>

+ 1 - 1
pwm/servlet/web/WEB-INF/jsp/peoplesearch.jsp

@@ -26,7 +26,7 @@
 <html dir="<pwm:LocaleOrientation/>">
 <html dir="<pwm:LocaleOrientation/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <body class="nihilo">
 <body class="nihilo">
-<div id="wrapper">
+<div id="wrapper" class="peoplesearch-wrapper">
     <jsp:include page="/WEB-INF/jsp/fragment/header-body.jsp">
     <jsp:include page="/WEB-INF/jsp/fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="Title_PeopleSearch"/>
         <jsp:param name="pwm.PageName" value="Title_PeopleSearch"/>
     </jsp:include>
     </jsp:include>

+ 1 - 3
pwm/servlet/web/WEB-INF/web.xml

@@ -259,7 +259,7 @@
     </servlet>
     </servlet>
     <servlet>
     <servlet>
         <servlet-name>PeopleSearchServlet</servlet-name>
         <servlet-name>PeopleSearchServlet</servlet-name>
-        <servlet-class>password.pwm.http.servlet.PeopleSearchServlet</servlet-class>
+        <servlet-class>password.pwm.http.servlet.peoplesearch.PeopleSearchServlet</servlet-class>
     </servlet>
     </servlet>
     <servlet>
     <servlet>
         <servlet-name>CaptchaServlet</servlet-name>
         <servlet-name>CaptchaServlet</servlet-name>
@@ -362,8 +362,6 @@
         <servlet-name>PeopleSearchServlet</servlet-name>
         <servlet-name>PeopleSearchServlet</servlet-name>
         <url-pattern>/private/PeopleSearch</url-pattern>
         <url-pattern>/private/PeopleSearch</url-pattern>
         <url-pattern>/public/PeopleSearch</url-pattern>
         <url-pattern>/public/PeopleSearch</url-pattern>
-        <url-pattern>/private/peoplesearch</url-pattern>
-        <url-pattern>/public/peoplesearch</url-pattern>
     </servlet-mapping>
     </servlet-mapping>
     <servlet-mapping>
     <servlet-mapping>
         <servlet-name>CaptchaServlet</servlet-name>
         <servlet-name>CaptchaServlet</servlet-name>

+ 73 - 99
pwm/servlet/web/public/health.jsp

@@ -75,22 +75,20 @@
 <div id="floatparent">
 <div id="floatparent">
 </div>
 </div>
 <pwm:script>
 <pwm:script>
-<script type="text/javascript">
-    var H_RANGE = 20;
-    var V_RANGE = 20;
-    var MAX_NODES = 150;
-
-    var splatCount = 0;
-    var errorColor = '#d20734';
-    var posV = 0;
-    var posH = 0;
-    var deltaV = Math.floor((Math.random() * V_RANGE * 2)) - V_RANGE;
-    var deltaH = Math.floor((Math.random() * H_RANGE * 2)) - H_RANGE;
-    var passwordValue = null;
-
-    function drawNextSprite() {
-        require(["dojo","dojo/window"],function(dojo){
-            if (passwordValue) {
+    <script type="text/javascript">
+        var H_RANGE = 20;
+        var V_RANGE = 20;
+        var MAX_NODES = 50;
+
+        var splatCount = 0;
+        var errorColor = '#d20734';
+        var posV = 0;
+        var posH = 0;
+        var deltaV = Math.floor((Math.random() * V_RANGE * 2)) - V_RANGE;
+        var deltaH = Math.floor((Math.random() * H_RANGE * 2)) - H_RANGE;
+
+        function drawNextSprite() {
+            require(["dojo","dojo/window"],function(dojo){
                 var floatParent = PWM_MAIN.getObject("floatparent");
                 var floatParent = PWM_MAIN.getObject("floatparent");
                 var vs = dojo.window.getBox();
                 var vs = dojo.window.getBox();
 
 
@@ -100,8 +98,9 @@
                 var styleText = "position: absolute; ";
                 var styleText = "position: absolute; ";
                 styleText += "top: " + posV + "px; ";
                 styleText += "top: " + posV + "px; ";
                 styleText += "left: " + posH +"px; ";
                 styleText += "left: " + posH +"px; ";
-                styleText += "padding: 4px; z-index:2; border-radius: 5px; ";
-                styleText += "filter:alpha(opacity=30); opacity: 0.3; ";
+                styleText += "padding: 4px; z-index:2;";
+                styleText += "filter:alpha(opacity=30); opacity: 0.8; ";
+                styleText += "width:15px; height:15px";
 
 
 
 
                 splatCount++;
                 splatCount++;
@@ -114,7 +113,7 @@
                 }
                 }
 
 
                 var div = document.createElement('div');
                 var div = document.createElement('div');
-                div.innerHTML = passwordValue;
+                //div.innerHTML = '&nbsp;';
                 div.id = divId;
                 div.id = divId;
                 div.setAttribute("class",'health-' + PWM_GLOBAL['pwm-health']);
                 div.setAttribute("class",'health-' + PWM_GLOBAL['pwm-health']);
 
 
@@ -141,102 +140,77 @@
                     change = true;
                     change = true;
                 }
                 }
                 if (change) {
                 if (change) {
-                    splatCount--;
-                    deltaV = deltaV == 0 ? 1 : deltaV;
-                    deltaH = deltaH == 0 ? 1 : deltaH;
-                    drawNextSprite();
-                    return;
-                }
-            }
-            var timeOutTime = 1000 - (PWM_GLOBAL['epsActivityCount'] != null ? Math.floor(PWM_GLOBAL['epsActivityCount']) : 0);
-            timeOutTime = timeOutTime < 100 ? 100 : timeOutTime;
-            setTimeout(function(){
-                drawNextSprite();
-            },timeOutTime);
-        });
-    }
-
-    function fetchRandomPassword() {
-        require(["dojo"],function(dojo){
-            dojo.xhrPost({
-                url: PWM_GLOBAL['url-restservice'] + "/randompassword",
-                headers: {"Accept":"application/json","X-RestClientKey":PWM_GLOBAL['restClientKey']},
-                dataType: "json",
-                timeout: 15000,
-                sync: false,
-                preventCache: true,
-                handleAs: "json",
-                load:  function(resultInfo) {
-                    passwordValue = resultInfo['data']["password"];
-                },
-                error: function(errorObj){
-                    passwordValue = "server unreachable";
+                    div.setAttribute('style','display:none');
+                    //splatCount--;
+                    //deltaV = deltaV == 0 ? 1 : deltaV;
+                    //deltaH = deltaH == 0 ? 1 : deltaH;
+                    //drawNextSprite();
+                    //return;
                 }
                 }
+                var timeOutTime = 1000 - (PWM_GLOBAL['epsActivityCount'] != null ? Math.floor(PWM_GLOBAL['epsActivityCount']) : 0);
+                timeOutTime = timeOutTime < 100 ? 100 : timeOutTime;
+                setTimeout(function(){
+                    drawNextSprite();
+                },timeOutTime);
             });
             });
-        });
-    }
+        }
 
 
-    function handleWarnFlash() {
-        if (PWM_GLOBAL['pwm-health'] == "WARN") {
-            PWM_MAIN.flashDomElement(errorColor,'body',3000);
+        function handleWarnFlash() {
+            if (PWM_GLOBAL['pwm-health'] == "WARN") {
+                PWM_MAIN.flashDomElement(errorColor,'body',3000);
+            }
         }
         }
-    }
 
 
-    function verticalCenter(divName) {
-        require(["dojo","dojo/window"],function(dojo){
-            var vs = dojo.window.getBox();
-            if (document.getElementById) {
-                var windowHeight = vs.h;
-                if (windowHeight > 0) {
-                    var contentElement = document.getElementById(divName);
-                    var contentHeight = contentElement.offsetHeight;
-                    if (windowHeight - contentHeight > 0) {
-                        contentElement.style.position = 'relative';
-                        contentElement.style.top = ((windowHeight / 2) - (contentHeight / 2)) + 'px';
-                    }
-                    else {
-                        contentElement.style.position = 'static';
+        function verticalCenter(divName) {
+            require(["dojo","dojo/window"],function(dojo){
+                var vs = dojo.window.getBox();
+                if (document.getElementById) {
+                    var windowHeight = vs.h;
+                    if (windowHeight > 0) {
+                        var contentElement = document.getElementById(divName);
+                        var contentHeight = contentElement.offsetHeight;
+                        if (windowHeight - contentHeight > 0) {
+                            contentElement.style.position = 'relative';
+                            contentElement.style.top = ((windowHeight / 2) - (contentHeight / 2)) + 'px';
+                        }
+                        else {
+                            contentElement.style.position = 'static';
+                        }
                     }
                     }
                 }
                 }
-            }
-        });
-    }
-
-    function startupHealthPage() {
-        PWM_GLOBAL['pwm-health'] = 'GOOD';
-        require(["dojo","dojo/domReady!","dojo/window"],function(dojo){
-            PWM_MAIN.flashDomElement('white','body',9000);
+            });
+        }
 
 
-            var vs = dojo.window.getBox();
-            posH = Math.floor((Math.random() * (vs.w - 30)));
-            posV = Math.floor((Math.random() * (vs.h - 100)));
+        function startupHealthPage() {
+            PWM_GLOBAL['pwm-health'] = 'GOOD';
+            require(["dojo","dojo/domReady!","dojo/window"],function(dojo){
+                PWM_MAIN.flashDomElement('white','body',9000);
 
 
-            fetchRandomPassword();
-            setInterval(function(){
-                fetchRandomPassword();
-            },30 * 1000);
+                var vs = dojo.window.getBox();
+                posH = Math.floor((Math.random() * (vs.w - 30)));
+                posV = Math.floor((Math.random() * (vs.h - 100)));
 
 
-            setInterval(function(){
-                handleWarnFlash();
-            },30 * 1000);
+                setInterval(function(){
+                    handleWarnFlash();
+                },30 * 1000);
 
 
-            drawNextSprite();
+                drawNextSprite();
 
 
-            PWM_ADMIN.showAppHealth('healthBody', {showTimestamp:true});
-            PWM_ADMIN.showStatChart('<%=Statistic.PASSWORD_CHANGES%>',1,'statsChart',{refreshTime:31*1000});
+                PWM_ADMIN.showAppHealth('healthBody', {showTimestamp:true});
+                PWM_ADMIN.showStatChart('<%=Statistic.PASSWORD_CHANGES%>',1,'statsChart',{refreshTime:31*1000});
 
 
-            verticalCenter('centerbody');
-            setInterval(function(){
                 verticalCenter('centerbody');
                 verticalCenter('centerbody');
-            }, 1000);
+                setInterval(function(){
+                    verticalCenter('centerbody');
+                }, 1000);
 
 
-        });
-    }
+            });
+        }
 
 
-    PWM_GLOBAL['startupFunctions'].push(function(){
-        startupHealthPage();
-    });
-</script>
+        PWM_GLOBAL['startupFunctions'].push(function(){
+            startupHealthPage();
+        });
+    </script>
 </pwm:script>
 </pwm:script>
 <pwm:script-ref url="/public/resources/js/admin.js"/>
 <pwm:script-ref url="/public/resources/js/admin.js"/>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>

BIN
pwm/servlet/web/public/resources/UserPhoto.png


+ 8 - 4
pwm/servlet/web/public/resources/configStyle.css

@@ -329,12 +329,11 @@ table {
 }
 }
 
 
 .pane-help {
 .pane-help {
-    margin-left: 10px;
-    margin-right: 10px;
+    border: 1px white solid;
+    padding: 5px;
     max-height: 250px;
     max-height: 250px;
-    margin-bottom: 15px;
+    margin: 2px 2px 15px;
     overflow-x: auto;
     overflow-x: auto;
-    /* border-bottom: 1px solid #bfbfbf; */
 }
 }
 
 
 .pane-settingValue {
 .pane-settingValue {
@@ -426,6 +425,11 @@ table {
     white-space: nowrap;
     white-space: nowrap;
 }
 }
 
 
+.noWrapTextBox.border {
+    border: 1px solid #bfbfbf;
+    margin: 0;
+}
+
 .configStringPanel {
 .configStringPanel {
     width:480px;
     width:480px;
     max-width:480px;
     max-width:480px;

+ 0 - 2
pwm/servlet/web/public/resources/js/admin.js

@@ -341,8 +341,6 @@ PWM_ADMIN.initIntrudersGrid=function() {
 
 
     require(["dojo","dojo/_base/declare", "dgrid/Grid", "dgrid/Keyboard", "dgrid/Selection", "dgrid/extensions/ColumnResizer", "dgrid/extensions/ColumnReorder", "dgrid/extensions/ColumnHider", "dgrid/extensions/DijitRegistry"],
     require(["dojo","dojo/_base/declare", "dgrid/Grid", "dgrid/Keyboard", "dgrid/Selection", "dgrid/extensions/ColumnResizer", "dgrid/extensions/ColumnReorder", "dgrid/extensions/ColumnHider", "dgrid/extensions/DijitRegistry"],
         function(dojo, declare, Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry){
         function(dojo, declare, Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry){
-            // Create a new constructor by mixing in the components
-
             // Create a new constructor by mixing in the components
             // Create a new constructor by mixing in the components
             var CustomGrid = declare([ Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry ]);
             var CustomGrid = declare([ Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry ]);
 
 

+ 18 - 10
pwm/servlet/web/public/resources/js/configeditor-settings.js

@@ -373,7 +373,7 @@ StringArrayValueHandler.writeSetting = function(settingKey, reload) {
     var syntax = PWM_SETTINGS['settings'][settingKey]['syntax'];
     var syntax = PWM_SETTINGS['settings'][settingKey]['syntax'];
     var nextFunction = function() {
     var nextFunction = function() {
         if (syntax == 'PROFILE') {
         if (syntax == 'PROFILE') {
-            PWM_CFGEDIT.drawNavigationMenu();
+            PWM_MAIN.goto('ConfigEditor');
         }
         }
         if (reload) {
         if (reload) {
             StringArrayValueHandler.init(settingKey);
             StringArrayValueHandler.init(settingKey);
@@ -571,9 +571,13 @@ FormTableHandler.redraw = function(keyName) {
     var parentDiv = 'table_setting_' + keyName;
     var parentDiv = 'table_setting_' + keyName;
     var parentDivElement = PWM_MAIN.getObject(parentDiv);
     var parentDivElement = PWM_MAIN.getObject(parentDiv);
 
 
+    parentDivElement.innerHTML = '<table class="noborder" id="table-top-' + keyName + '"></table>';
+    parentDiv = 'table-top-' + keyName;
+    parentDivElement = PWM_MAIN.getObject(parentDiv);
+
     if (!PWM_MAIN.isEmpty(resultValue)) {
     if (!PWM_MAIN.isEmpty(resultValue)) {
         var headerRow = document.createElement("tr");
         var headerRow = document.createElement("tr");
-        var rowHtml = '<td>Name</td><td>Label</td>';
+        var rowHtml = '<td>Name</td><td></td><td>Label</td>';
         headerRow.innerHTML = rowHtml;
         headerRow.innerHTML = rowHtml;
         parentDivElement.appendChild(headerRow);
         parentDivElement.appendChild(headerRow);
     }
     }
@@ -604,16 +608,17 @@ FormTableHandler.drawRow = function(parentDiv, settingKey, iteration, value) {
         newTableRow.setAttribute("style", "border-width: 0");
         newTableRow.setAttribute("style", "border-width: 0");
 
 
         var htmlRow = '';
         var htmlRow = '';
-        htmlRow += '<td style="width:180px" id="panel-name-' + inputID + '" class="noWrapTextBox"></td>';
-        htmlRow += '<td style="width:170px"><div class="noWrapTextBox" id="' + inputID + 'label"><span class="btn-icon fa fa-edit"></span><span>' + value['labels'][''] + '...</span></div></td>';
+        htmlRow += '<td style="width:180px"><div class="noWrapTextBox border" id="panel-name-' + inputID + '" ></div></td>';
+        htmlRow += '<td style="width:1px" id="icon-editLabel-' + inputID + '"><span class="btn-icon fa fa-edit"></span></td>';
+        htmlRow += '<td style="width:170px"><div style="" class="noWrapTextBox border" id="' + inputID + 'label"><span>' + value['labels'][''] + '...</span></div></td>';
 
 
-        htmlRow += '<td>';
         var userDNtypeAllowed = options['type-userDN'] == 'show';
         var userDNtypeAllowed = options['type-userDN'] == 'show';
         var optionList = PWM_GLOBAL['formTypeOptions'];
         var optionList = PWM_GLOBAL['formTypeOptions'];
         if ('types' in options) {
         if ('types' in options) {
             optionList = JSON.parse(options['types']);
             optionList = JSON.parse(options['types']);
         }
         }
         if (!PWM_MAIN.isEmpty(optionList)) {
         if (!PWM_MAIN.isEmpty(optionList)) {
+            htmlRow += '<td style="width:15px;">';
             htmlRow += '<select id="' + inputID + 'type">';
             htmlRow += '<select id="' + inputID + 'type">';
             for (var optionItem in optionList) {
             for (var optionItem in optionList) {
                 if (optionList[optionItem] != 'userDN' || userDNtypeAllowed) {
                 if (optionList[optionItem] != 'userDN' || userDNtypeAllowed) {
@@ -623,26 +628,26 @@ FormTableHandler.drawRow = function(parentDiv, settingKey, iteration, value) {
                 }
                 }
             }
             }
             htmlRow += '</select>';
             htmlRow += '</select>';
+            htmlRow += '</td>';
         }
         }
-        htmlRow += '</td>';
 
 
         var hideOptions = PWM_SETTINGS['settings'][settingKey]['options']['hideOptions'] == 'true';
         var hideOptions = PWM_SETTINGS['settings'][settingKey]['options']['hideOptions'] == 'true';
         if (!hideOptions) {
         if (!hideOptions) {
-            htmlRow += '<td><button id="' + inputID + 'optionsButton"><span class="btn-icon fa fa-sliders"/> Options</button></td>';
+            htmlRow += '<td style="min-width:90px; border:0"><button id="' + inputID + 'optionsButton"><span class="btn-icon fa fa-sliders"/> Options</button></td>';
         }
         }
 
 
-        htmlRow += '<td>';
+        htmlRow += '<td style="width:10px">';
         if (itemCount > 1 && iteration != (itemCount -1)) {
         if (itemCount > 1 && iteration != (itemCount -1)) {
             htmlRow += '<span id="' + inputID + '-moveDown" class="action-icon fa fa-chevron-down"></span>';
             htmlRow += '<span id="' + inputID + '-moveDown" class="action-icon fa fa-chevron-down"></span>';
         }
         }
         htmlRow += '</td>';
         htmlRow += '</td>';
 
 
-        htmlRow += '<td>';
+        htmlRow += '<td style="width:10px">';
         if (itemCount > 1 && iteration != 0) {
         if (itemCount > 1 && iteration != 0) {
             htmlRow += '<span id="' + inputID + '-moveUp" class="action-icon fa fa-chevron-up"></span>';
             htmlRow += '<span id="' + inputID + '-moveUp" class="action-icon fa fa-chevron-up"></span>';
         }
         }
         htmlRow += '</td>';
         htmlRow += '</td>';
-        htmlRow += '<td><span class="delete-row-icon action-icon fa fa-times" id="' + inputID + '-deleteRowButton"></span></td>';
+        htmlRow += '<td style="width:10px"><span class="delete-row-icon action-icon fa fa-times" id="' + inputID + '-deleteRowButton"></span></td>';
 
 
         newTableRow.innerHTML = htmlRow;
         newTableRow.innerHTML = htmlRow;
         var parentDivElement = PWM_MAIN.getObject(parentDiv);
         var parentDivElement = PWM_MAIN.getObject(parentDiv);
@@ -662,6 +667,9 @@ FormTableHandler.drawRow = function(parentDiv, settingKey, iteration, value) {
         PWM_MAIN.addEventHandler(inputID + "label", 'click, keypress', function () {
         PWM_MAIN.addEventHandler(inputID + "label", 'click, keypress', function () {
             FormTableHandler.showLabelDialog(settingKey, iteration);
             FormTableHandler.showLabelDialog(settingKey, iteration);
         });
         });
+        PWM_MAIN.addEventHandler("icon-editLabel-" + inputID, 'click, keypress', function () {
+            FormTableHandler.showLabelDialog(settingKey, iteration);
+        });
         PWM_MAIN.addEventHandler(inputID + "optionsButton", 'click', function () {
         PWM_MAIN.addEventHandler(inputID + "optionsButton", 'click', function () {
             FormTableHandler.showOptionsDialog(settingKey, iteration);
             FormTableHandler.showOptionsDialog(settingKey, iteration);
         });
         });

+ 11 - 131
pwm/servlet/web/public/resources/js/configeditor.js

@@ -187,7 +187,7 @@ PWM_CFGEDIT.getSettingValueElement = function(settingKey) {
     return PWM_MAIN.getObject(parentDiv);
     return PWM_MAIN.getObject(parentDiv);
 };
 };
 
 
-PWM_CFGEDIT.clearDivElements = function(parentDiv, showLoading) {
+PWM_CFGEDIT.clearDivElements = function(parentDiv) {
     var parentDivElement = PWM_MAIN.getObject(parentDiv);
     var parentDivElement = PWM_MAIN.getObject(parentDiv);
     if (parentDivElement != null) {
     if (parentDivElement != null) {
         if (parentDivElement.hasChildNodes()) {
         if (parentDivElement.hasChildNodes()) {
@@ -196,19 +196,6 @@ PWM_CFGEDIT.clearDivElements = function(parentDiv, showLoading) {
                 parentDivElement.removeChild(firstChild);
                 parentDivElement.removeChild(firstChild);
             }
             }
         }
         }
-        /*
-         if (showLoading) {
-         var newTableRow = document.createElement("tr");
-         newTableRow.setAttribute("style", "border-width: 0");
-         parentDivElement.appendChild(newTableRow);
-
-
-         var newTableData = document.createElement("td");
-         newTableData.setAttribute("style", "border-width: 0");
-         newTableData.innerHTML = PWM_MAIN.showString('Display_PleaseWait');
-         newTableRow.appendChild(newTableData);
-         }
-         */
     }
     }
 };
 };
 
 
@@ -248,7 +235,7 @@ PWM_CFGEDIT.readInitialTextBasedValue = function(key) {
 };
 };
 
 
 PWM_CFGEDIT.saveConfiguration = function() {
 PWM_CFGEDIT.saveConfiguration = function() {
-    PWM_VAR['cancelHeartbeatCheck'];
+    PWM_VAR['cancelHeartbeatCheck'] = true;
     PWM_MAIN.preloadAll(function(){
     PWM_MAIN.preloadAll(function(){
         var confirmText = PWM_CONFIG.showString('MenuDisplay_SaveConfig');
         var confirmText = PWM_CONFIG.showString('MenuDisplay_SaveConfig');
         var confirmFunction = function(){
         var confirmFunction = function(){
@@ -300,46 +287,6 @@ PWM_CFGEDIT.setConfigurationPassword = function(password) {
     ChangePasswordHandler.popup('configPw','Configuration Password',writeFunction);
     ChangePasswordHandler.popup('configPw','Configuration Password',writeFunction);
 };
 };
 
 
-PWM_CFGEDIT.toggleHelpDisplay=function(key, options) {
-    console.log('begin toggle help display for key ' + key);
-    PWM_VAR['toggleHelpDisplay'] = PWM_VAR['toggleHelpDisplay'] || {};
-    var helpDiv = PWM_MAIN.getObject('helpDiv_' + key);
-    var titleId = 'title_' + key;
-    var show;
-    if (options && options['force']) {
-        if (options['force'] == 'show') {
-            show = true;
-        } else if (options['force'] == 'hide') {
-            show = false;
-        }
-    } else {
-        show = PWM_VAR['toggleHelpDisplay'][key] == 'hidden';
-    }
-    if (helpDiv) {
-        if (show) {
-            PWM_VAR['toggleHelpDisplay'][key] = 'visible';
-            require(["dijit/registry","dojo/fx"],function(registry,fx){
-                var node = registry.byId('title_' + key);
-                if (node) {
-                    node.destroy();
-                }
-                fx.wipeIn({node:helpDiv}).play();
-            });
-        } else {
-            PWM_VAR['toggleHelpDisplay'][key] = 'hidden';
-            var helpText = PWM_SETTINGS['settings'][key]['description'];
-            PWM_MAIN.showTooltip({
-                id: [titleId],
-                position: ['above','below'],
-                text: helpText,
-                width: 520
-            });
-            require(["dojo/fx"],function(fx){
-                fx.wipeOut({node:helpDiv}).play();
-            });
-        }
-    }
-};
 
 
 function handleResetClick(settingKey) {
 function handleResetClick(settingKey) {
     var label = PWM_SETTINGS['settings'][settingKey] ? PWM_SETTINGS['settings'][settingKey]['label'] : ' ';
     var label = PWM_SETTINGS['settings'][settingKey] ? PWM_SETTINGS['settings'][settingKey]['label'] : ' ';
@@ -358,11 +305,6 @@ PWM_CFGEDIT.initConfigEditor = function(nextFunction) {
     PWM_MAIN.addEventHandler('button-navigationExpandAll','click',function(){PWM_VAR['navigationTree'].expandAll()});
     PWM_MAIN.addEventHandler('button-navigationExpandAll','click',function(){PWM_VAR['navigationTree'].expandAll()});
     PWM_MAIN.addEventHandler('button-navigationCollapseAll','click',function(){PWM_VAR['navigationTree'].collapseAll()});
     PWM_MAIN.addEventHandler('button-navigationCollapseAll','click',function(){PWM_VAR['navigationTree'].collapseAll()});
 
 
-    PWM_MAIN.showTooltip({id:'cancelButton_icon',text:'Cancel Changes and return to Configuration Manager',position:'below'});
-    PWM_MAIN.showTooltip({id:'saveButton_icon',text:'Save',position:'below'});
-    PWM_MAIN.showTooltip({id:'setPassword_icon',text:'Set Configuration Password',position:'below'});
-    PWM_MAIN.showTooltip({id:'referenceDoc_icon',text:'Open Reference Documentation',position:'below'});
-    PWM_MAIN.showTooltip({id:'macroDoc_icon',text:'Macro Help',position:'below'});
     PWM_MAIN.showTooltip({id:'settingSearchIcon',text:'Search settings, help text and setting values',position:'above'});
     PWM_MAIN.showTooltip({id:'settingSearchIcon',text:'Search settings, help text and setting values',position:'above'});
     PWM_MAIN.showTooltip({id:'indicator-noResults',text:'No search results',position:'above'});
     PWM_MAIN.showTooltip({id:'indicator-noResults',text:'No search results',position:'above'});
 
 
@@ -783,7 +725,7 @@ PWM_CFGEDIT.selectTemplate = function(newTemplate) {
 PWM_CFGEDIT.loadMainPageBody = function() {
 PWM_CFGEDIT.loadMainPageBody = function() {
 
 
     PWM_CFGEDIT.drawNavigationMenu();
     PWM_CFGEDIT.drawNavigationMenu();
-    var storedPreferences = PWM_CFGEDIT.readLocalStorage();
+    var storedPreferences = PWM_MAIN.readLocalStorage();
     if (storedPreferences['lastSelected']) {
     if (storedPreferences['lastSelected']) {
         PWM_CFGEDIT.dispatchNavigationItem(storedPreferences['lastSelected']);
         PWM_CFGEDIT.dispatchNavigationItem(storedPreferences['lastSelected']);
     } else {
     } else {
@@ -863,18 +805,18 @@ PWM_CFGEDIT.drawHtmlOutlineForSetting = function(settingInfo, options) {
     var htmlBody = '<div id="outline_' + settingKey + '" class="setting_outline" style="display:none">'
     var htmlBody = '<div id="outline_' + settingKey + '" class="setting_outline" style="display:none">'
         + '<div class="setting_title" id="title_' + settingKey + '">'
         + '<div class="setting_title" id="title_' + settingKey + '">'
         + '<a id="setting-' + settingKey + '" class="text">' + settingLabel + '</a>'
         + '<a id="setting-' + settingKey + '" class="text">' + settingLabel + '</a>'
-        + '<div class="fa fa-pencil-square modifiedNoticeIcon" title="Setting has been modified" id="modifiedNoticeIcon-' + settingKey + '" style="display: none" ></div>';
+        + '<div class="fa fa-pencil-square modifiedNoticeIcon" title="' + PWM_CONFIG.showString('Tooltip_ModifiedNotice') + '" id="modifiedNoticeIcon-' + settingKey + '" style="display: none" ></div>';
 
 
     if (settingInfo['description']) {
     if (settingInfo['description']) {
-        htmlBody += '<div class="fa fa-question-circle icon_button" title="Help" id="helpButton-' + settingKey + '"></div>';
+        htmlBody += '<div class="fa fa-question-circle icon_button" title="' + PWM_CONFIG.showString('Tooltip_HelpButton') + '" id="helpButton-' + settingKey + '"></div>';
     }
     }
 
 
-    htmlBody += '<div style="visibility: hidden" class="fa fa-undo icon_button" title="Reset" id="resetButton-' + settingKey + '"></div>'
+    htmlBody += '<div style="visibility: hidden" class="fa fa-undo icon_button" title="' + PWM_CONFIG.showString('Tooltip_ResetButton') + '" id="resetButton-' + settingKey + '"></div>'
     + '</div>' // close title
     + '</div>' // close title
     + '<div id="titlePane_' + settingKey + '" class="setting_body">';
     + '<div id="titlePane_' + settingKey + '" class="setting_body">';
 
 
     if (settingInfo['description']) {
     if (settingInfo['description']) {
-        var prefs = PWM_CFGEDIT.readLocalStorage();
+        var prefs = PWM_MAIN.readLocalStorage();
         var expandHelp = 'helpExpanded' in prefs && settingKey in prefs['helpExpanded'];
         var expandHelp = 'helpExpanded' in prefs && settingKey in prefs['helpExpanded'];
         htmlBody += '<div class="pane-help" id="pane-help-' + settingKey + '" style="display:' + (expandHelp ? 'inherit' : 'none') + '">'
         htmlBody += '<div class="pane-help" id="pane-help-' + settingKey + '" style="display:' + (expandHelp ? 'inherit' : 'none') + '">'
         + settingInfo['description'] + '</div>';
         + settingInfo['description'] + '</div>';
@@ -894,18 +836,6 @@ PWM_CFGEDIT.initSettingDisplay = function(setting, options) {
     var settingKey = setting['key'];
     var settingKey = setting['key'];
     options = options === undefined ? {} : options;
     options = options === undefined ? {} : options;
 
 
-    PWM_MAIN.showTooltip({
-        id: "modifiedNoticeIcon-" + settingKey,
-        text: 'Setting has been modified from the default value'
-    });
-    PWM_MAIN.showTooltip({
-        id: "resetButton-" + settingKey,
-        text: PWM_CONFIG.showString('Tooltip_ResetButton')
-    });
-    PWM_MAIN.showTooltip({
-        id: "helpButton-" + settingKey,
-        text: PWM_CONFIG.showString('Tooltip_HelpButton')
-    });
     PWM_MAIN.addEventHandler('helpButton-' + settingKey, 'click', function () {
     PWM_MAIN.addEventHandler('helpButton-' + settingKey, 'click', function () {
         PWM_CFGEDIT.displaySettingHelp(settingKey);
         PWM_CFGEDIT.displaySettingHelp(settingKey);
     });
     });
@@ -1052,9 +982,9 @@ PWM_CFGEDIT.drawNavigationMenu = function() {
                     openOnClick: true,
                     openOnClick: true,
                     id: 'navigationTree',
                     id: 'navigationTree',
                     onClick: function(item){
                     onClick: function(item){
-                        var storedPreferences = PWM_CFGEDIT.readLocalStorage();
+                        var storedPreferences = PWM_MAIN.readLocalStorage();
                         storedPreferences['lastSelected'] = item;
                         storedPreferences['lastSelected'] = item;
-                        PWM_CFGEDIT.writeLocalStorage(storedPreferences);
+                        PWM_MAIN.writeLocalStorage(storedPreferences);
                         PWM_CFGEDIT.dispatchNavigationItem(item);
                         PWM_CFGEDIT.dispatchNavigationItem(item);
                     }
                     }
                 });
                 });
@@ -1228,29 +1158,6 @@ PWM_CFGEDIT.drawHomePage = function() {
 
 
 };
 };
 
 
-PWM_CFGEDIT.readLocalStorage = function() {
-    if(typeof(Storage) !== "undefined") {
-        var storedStr = localStorage.getItem("ConfigEditor_Storage");
-        if (storedStr) {
-            try {
-                return JSON.parse(storedStr);
-            } catch (e) {
-                console.error('Error decoding existing local storage value: ' + e);
-            }
-        }
-        return {};
-    } else {
-        console.log("browser doesn't support local storage");
-    }
-};
-
-PWM_CFGEDIT.writeLocalStorage = function(dataUpdate) {
-    if(typeof(Storage) !== "undefined") {
-        if (dataUpdate) {
-            localStorage.setItem("ConfigEditor_Storage",JSON.stringify(dataUpdate));
-        }
-    }
-};
 
 
 PWM_CFGEDIT.initConfigSettingsDefinition=function(nextFunction) {
 PWM_CFGEDIT.initConfigSettingsDefinition=function(nextFunction) {
     var clientConfigUrl = PWM_GLOBAL['url-context'] + "/private/config/ConfigEditor?processAction=settingData&pwmFormID=" + PWM_GLOBAL['pwmFormID'];
     var clientConfigUrl = PWM_GLOBAL['url-context'] + "/private/config/ConfigEditor?processAction=settingData&pwmFormID=" + PWM_GLOBAL['pwmFormID'];
@@ -1276,7 +1183,7 @@ PWM_CFGEDIT.initConfigSettingsDefinition=function(nextFunction) {
 
 
 PWM_CFGEDIT.displaySettingHelp = function(settingKey) {
 PWM_CFGEDIT.displaySettingHelp = function(settingKey) {
     console.log('toggle help for ' + settingKey);
     console.log('toggle help for ' + settingKey);
-    var prefs = PWM_CFGEDIT.readLocalStorage();
+    var prefs = PWM_MAIN.readLocalStorage();
     prefs['helpExpanded'] = 'helpExpanded' in prefs ? prefs['helpExpanded'] : {};
     prefs['helpExpanded'] = 'helpExpanded' in prefs ? prefs['helpExpanded'] : {};
     var element = PWM_MAIN.getObject('pane-help-' + settingKey);
     var element = PWM_MAIN.getObject('pane-help-' + settingKey);
     if (element) {
     if (element) {
@@ -1287,35 +1194,8 @@ PWM_CFGEDIT.displaySettingHelp = function(settingKey) {
             element.style.display = 'none';
             element.style.display = 'none';
             delete prefs['helpExpanded'][settingKey];
             delete prefs['helpExpanded'][settingKey];
         }
         }
-        PWM_CFGEDIT.writeLocalStorage(prefs);
+        PWM_MAIN.writeLocalStorage(prefs);
     }
     }
-    /*
-     var setting = PWM_SETTINGS['settings'][settingKey];
-
-     var body = '<div>' + setting['description'] + '</div><br/><br/>';
-     body += '<div style="cursor:pointer; text-align:center; width: 100%;">';
-     body += '<span id="button-' + settingKey + '-extendHelp" style="color:grey" class="btn-icon fa fa-arrow-circle-down"></span>'
-     body += '</div>';
-     body += '<div style="display:none" id="panel-' + settingKey + '-extendHelp"><table>';
-     body += '<tr><td>Key</td><td>' + setting['key'] + '</td></tr>';
-     body += '<tr><td>Syntax</td><td>' + setting['syntax'] + '</td></tr>';
-     body += '<tr><td>Required</td><td>' + setting['required'] + '</td></tr>';
-     body += '</table></div>';
-
-
-     PWM_MAIN.showDialog({
-     text: body,
-     title: setting['label'],
-     allowMove: true,
-     showClose: true,
-     loadFunction: function(){
-     PWM_MAIN.addEventHandler('button-' + settingKey + '-extendHelp','click',function(){
-     PWM_MAIN.getObject('button-' + settingKey + '-extendHelp').style.visibility = 'hidden';
-     PWM_MAIN.getObject('panel-' + settingKey + '-extendHelp').style.display = 'inline';
-     });
-     }
-     });
-     */
 };
 };
 
 
 PWM_CFGEDIT.showSettingFilter = function() {
 PWM_CFGEDIT.showSettingFilter = function() {

+ 33 - 3
pwm/servlet/web/public/resources/js/configmanager.js

@@ -134,6 +134,25 @@ PWM_CONFIG.uploadLocalDB=function() {
     });
     });
 };
 };
 
 
+PWM_CONFIG.closeHeaderWarningPanel = function() {
+    console.log('action closeHeader');
+    PWM_MAIN.setStyle('header-warning','display','none');
+    PWM_MAIN.setStyle('button-openHeader','display','inherit');
+    var prefs = PWM_MAIN.readLocalStorage();
+    prefs['headerVisibility'] = 'hide';
+    PWM_MAIN.writeLocalStorage(prefs);
+};
+
+PWM_CONFIG.openHeaderWarningPanel = function() {
+    console.log('action openHeader');
+    PWM_MAIN.setStyle('header-warning','display','inherit');
+    PWM_MAIN.setStyle('button-openHeader','display','none');
+    var prefs = PWM_MAIN.readLocalStorage();
+    prefs['headerVisibility'] = 'show';
+    PWM_MAIN.writeLocalStorage(prefs);
+};
+
+
 
 
 PWM_CONFIG.showString=function (key, options) {
 PWM_CONFIG.showString=function (key, options) {
     options = options === undefined ? {} : options;
     options = options === undefined ? {} : options;
@@ -167,8 +186,13 @@ PWM_CONFIG.showHeaderHealth = function() {
                     }
                     }
                 }
                 }
                 if (hasWarnTopics) {
                 if (hasWarnTopics) {
-                    PWM_MAIN.openHeaderWarningPanel();
+                    PWM_MAIN.addCssClass('button-openHeader','blink');
+                    PWM_MAIN.setStyle('button-openHeader','color','red');
+
                     parentDiv.innerHTML = '<div id="panel-healthHeaderErrors" class="header-error"><span class="fa fa-warning"></span> ' + PWM_ADMIN.showString('Header_HealthWarningsPresent') + '</div>';
                     parentDiv.innerHTML = '<div id="panel-healthHeaderErrors" class="header-error"><span class="fa fa-warning"></span> ' + PWM_ADMIN.showString('Header_HealthWarningsPresent') + '</div>';
+                } else {
+                    PWM_MAIN.removeCssClass('button-openHeader','blink');
+                    PWM_MAIN.setStyle('button-openHeader','color');
                 }
                 }
                 setTimeout(function () {
                 setTimeout(function () {
                     PWM_CONFIG.showHeaderHealth()
                     PWM_CONFIG.showHeaderHealth()
@@ -393,12 +417,18 @@ PWM_CONFIG.initConfigHeader = function() {
         PWM_MAIN.goto('/private/config/ConfigManager');
         PWM_MAIN.goto('/private/config/ConfigManager');
     });
     });
     PWM_MAIN.addEventHandler('button-closeHeader','click',function(){
     PWM_MAIN.addEventHandler('button-closeHeader','click',function(){
-        PWM_MAIN.closeHeaderWarningPanel();
+        PWM_CONFIG.closeHeaderWarningPanel();
     });
     });
     PWM_MAIN.addEventHandler('button-openHeader','click',function(){
     PWM_MAIN.addEventHandler('button-openHeader','click',function(){
-        PWM_MAIN.openHeaderWarningPanel();
+        PWM_CONFIG.openHeaderWarningPanel();
     });
     });
 
 
     PWM_CONFIG.showHeaderHealth();
     PWM_CONFIG.showHeaderHealth();
+
+    var prefs = PWM_MAIN.readLocalStorage();
+    if (prefs['headerVisibility'] == 'show') {
+        PWM_CONFIG.openHeaderWarningPanel();
+    }
+
     console.log('initConfigHeader completed');
     console.log('initConfigHeader completed');
 };
 };

+ 103 - 80
pwm/servlet/web/public/resources/js/helpdesk.js

@@ -45,36 +45,21 @@ PWM_HELPDESK.executeAction = function(actionName) {
 
 
 PWM_HELPDESK.doResponseClear = function() {
 PWM_HELPDESK.doResponseClear = function() {
     var username = PWM_VAR['helpdesk_obfuscatedDN'];
     var username = PWM_VAR['helpdesk_obfuscatedDN'];
-    require(["dojo","dijit/Dialog"],function(dojo){
-        PWM_MAIN.closeWaitDialog();
-        PWM_MAIN.showWaitDialog({loadFunction:function() {
-            var inputValues = { 'username': username };
-            dojo.xhrDelete({
-                url: PWM_GLOBAL['url-restservice'] + "/challenges",
-                headers: {"Accept": "application/json", "X-RestClientKey": PWM_GLOBAL['restClientKey']},
-                content: inputValues,
-                preventCache: true,
-                timeout: PWM_MAIN.ajaxTimeout,
-                sync: false,
-                handleAs: "json",
-                load: function (results) {
-                    var bodyText = "";
-                    if (results['error'] != true) {
-                        PWM_MAIN.showDialog({
-                            title: PWM_MAIN.showString('Button_ClearResponses'),
-                            text: results['successMessage']
-                        });
-                    } else {
-                        PWM_MAIN.showErrorDialog(results);
-                    }
-                },
-                error: function (errorObj) {
-                    PWM_MAIN.closeWaitDialog();
-                    PWM_MAIN.showError("unexpected clear responses error: " + errorObj);
-                }
-            });
-        }});
-    });
+    PWM_MAIN.showWaitDialog({loadFunction:function() {
+        var inputValues = { 'username': username };
+        var url = PWM_GLOBAL['url-restservice'] + "/challenges";
+        var loadFunction = function(results) {
+            if (results['error'] != true) {
+                PWM_MAIN.showDialog({
+                    title: PWM_MAIN.showString('Button_ClearResponses'),
+                    text: results['successMessage']
+                });
+            } else {
+                PWM_MAIN.showErrorDialog(results);
+            }
+        };
+        PWM_MAIN.ajaxRequest(url,loadFunction,{content:inputValues,method:'delete'});
+    }});
 };
 };
 
 
 PWM_HELPDESK.doPasswordChange = function(password, random) {
 PWM_HELPDESK.doPasswordChange = function(password, random) {
@@ -85,8 +70,7 @@ PWM_HELPDESK.doPasswordChange = function(password, random) {
     } else {
     } else {
         inputValues['password'] = password;
         inputValues['password'] = password;
     }
     }
-    var htmlBody = PWM_MAIN.showString('Field_NewPassword') + ': <b>' + password + '</b><br/><br/><br/><div class="WaitDialogBlank"/>';
-    PWM_MAIN.showWaitDialog({text:htmlBody,loadFunction:function() {
+    PWM_MAIN.showWaitDialog({loadFunction:function() {
         var url = PWM_GLOBAL['url-restservice'] + "/setpassword";
         var url = PWM_GLOBAL['url-restservice'] + "/setpassword";
         var loadFunction = function(results) {
         var loadFunction = function(results) {
             var bodyText = "";
             var bodyText = "";
@@ -100,11 +84,19 @@ PWM_HELPDESK.doPasswordChange = function(password, random) {
                 bodyText += '<br/>';
                 bodyText += '<br/>';
                 bodyText += results['successMessage'];
                 bodyText += results['successMessage'];
                 bodyText += '</br></br>';
                 bodyText += '</br></br>';
-                bodyText += PWM_MAIN.showString('Field_NewPassword') + ': <input class="inputfield" value="' + password + '" readonly/>';
+                bodyText += PWM_MAIN.showString('Field_NewPassword');
+
+                if (PWM_VAR['helpdesk_setting_maskPasswords']) {
+                    bodyText += '<button id="button-password-display" class="btn"><span class="btn-icon fa fa-eye"></span>' + PWM_MAIN.showString('Button_Show') + '</button>';
+                    bodyText += ' <input id="panel-password-display" style="display:none" class="inputfield" value="' + password + '" readonly/>';
+                } else {
+                    bodyText += ' <input class="inputfield" value="' + password + '" readonly/>';
+                }
+
                 bodyText += '<br/>';
                 bodyText += '<br/>';
             }
             }
             bodyText += '<br/><br/><button class="btn" id="button-continue">'
             bodyText += '<br/><br/><button class="btn" id="button-continue">'
-            + '<span class="btn-icon fa fa-forward"></span>' + PWM_MAIN.showString('Button_OK') + '</button>';
+                + '<span class="btn-icon fa fa-forward"></span>' + PWM_MAIN.showString('Button_OK') + '</button>';
             if (PWM_VAR['helpdesk_setting_clearResponses'] == 'ask') {
             if (PWM_VAR['helpdesk_setting_clearResponses'] == 'ask') {
                 bodyText += '<span style="padding-left: 10px">&nbsp;</span>';
                 bodyText += '<span style="padding-left: 10px">&nbsp;</span>';
                 bodyText += '<button class="btn" id="button-clearResponses">';
                 bodyText += '<button class="btn" id="button-clearResponses">';
@@ -116,11 +108,20 @@ PWM_HELPDESK.doPasswordChange = function(password, random) {
                 showClose: true,
                 showClose: true,
                 allowMove: true,
                 allowMove: true,
                 id: 'dialogPopup',
                 id: 'dialogPopup',
-                title: PWM_MAIN.showString('Title_ChangePassword') + ': ' + PWM_VAR['helpdesk_username'],
+                title: PWM_MAIN.showString('Title_ChangePassword') + ' - ' + PWM_VAR['helpdesk_username'],
                 text: bodyText,
                 text: bodyText,
                 loadFunction:function(){
                 loadFunction:function(){
                     PWM_MAIN.addEventHandler('button-continue','click',function(){ PWM_MAIN.getObject('continueForm').submit(); });
                     PWM_MAIN.addEventHandler('button-continue','click',function(){ PWM_MAIN.getObject('continueForm').submit(); });
                     PWM_MAIN.addEventHandler('button-clearResponses','click',function(){ PWM_HELPDESK.doResponseClear(); });
                     PWM_MAIN.addEventHandler('button-clearResponses','click',function(){ PWM_HELPDESK.doResponseClear(); });
+
+                    if (PWM_VAR['helpdesk_setting_maskPasswords']) {
+                        PWM_MAIN.addEventHandler('button-password-display','click',function(){
+                            var buttonElement = PWM_MAIN.getObject('button-password-display');
+                            buttonElement.parentNode.removeChild(buttonElement);
+                            PWM_MAIN.getObject('panel-password-display').style.display = 'inline';
+                        });
+                    }
+
                 }
                 }
             });
             });
         };
         };
@@ -143,50 +144,71 @@ PWM_HELPDESK.generatePasswordPopup = function() {
 };
 };
 
 
 PWM_HELPDESK.changePasswordPopup = function() {
 PWM_HELPDESK.changePasswordPopup = function() {
-    require(["dijit/Dialog"],function(){
-        var bodyText = '';
-        bodyText += '<span id="message" class="message message-info" style="width: 400px">' + PWM_MAIN.showString('Display_PasswordPrompt') + '</span>';
-        bodyText += '<table style="border: 0">';
-        bodyText += '<tr style="border: 0"><td style="border: 0"><input type="text" name="password1" id="password1" class="inputfield" style="width: 260px" autocomplete="off" onkeyup="PWM_CHANGEPW.validatePasswords(\'' + PWM_VAR['helpdesk_obfuscatedDN'] + '\');"/></td>';
-        if (PWM_GLOBAL['setting-showStrengthMeter']) {
-            bodyText += '<td style="border:0"><div id="strengthBox" style="visibility:hidden;">';
-            bodyText += '<div id="strengthLabel">' + PWM_MAIN.showString('Display_StrengthMeter') + '</div>';
-            bodyText += '<div class="progress-container" style="margin-bottom:10px">';
-            bodyText += '<div id="strengthBar" style="width:0">&nbsp;</div></div></div></td>';
-        }
-        bodyText += '</tr><tr style="border: 0">';
-        bodyText += '<td style="border: 0"><input type="text" name="password2" id="password2" class="inputfield" style="width: 260px" autocomplete="off" onkeyup="PWM_CHANGEPW.validatePasswords(\'' + PWM_VAR['helpdesk_obfuscatedDN'] + '\');""/></td>';
-
-        bodyText += '<td style="border: 0"><div style="margin:0;">';
-        bodyText += '<img style="visibility:hidden;" id="confirmCheckMark" alt="checkMark" height="15" width="15" src="' + PWM_GLOBAL['url-resources'] + '/greenCheck.png">';
-        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 += '<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>';
-        }
+    var bodyText = '';
+    bodyText += '<span id="message" class="message message-info" style="width: 400px">' + PWM_MAIN.showString('Display_PasswordPrompt') + '</span>';
+    bodyText += '<table style="border: 0"><tr style="border: 0"><td style="border: 0">';
+    if (PWM_VAR['helpdesk_setting_maskPasswords']) {
+        bodyText += '<input type="password" name="password1" id="password1" class="passwordfield" style="width: 260px" autocomplete="off"/>';
+    } else {
+        bodyText += '<input type="text" name="password1" id="password1" class="inputfield" style="width: 260px" autocomplete="off"/>';
+    }
+    bodyText += '</td>';
+    if (PWM_GLOBAL['setting-showStrengthMeter']) {
+        bodyText += '<td style="border:0"><div id="strengthBox" style="visibility:hidden;">';
+        bodyText += '<div id="strengthLabel">' + PWM_MAIN.showString('Display_StrengthMeter') + '</div>';
+        bodyText += '<div class="progress-container" style="margin-bottom:10px">';
+        bodyText += '<div id="strengthBar" style="width:0">&nbsp;</div></div></div></td>';
+    }
+    bodyText += '</tr><tr style="border: 0">';
 
 
-        try { PWM_MAIN.getObject('message').id = "base-message"; } catch (e) {}
-
-        PWM_MAIN.showDialog({
-            title: PWM_MAIN.showString('Title_ChangePassword') + ': ' + PWM_VAR['helpdesk_username'],
-            text: bodyText,
-            showOk: false,
-            showClose: true,
-            allowMove: true,
-            loadFunction: function(){
-                setTimeout(function(){ PWM_MAIN.getObject('password1').focus();},500);
-                PWM_MAIN.addEventHandler('password_button','click',function(){
-                    var pw=PWM_MAIN.getObject('password1').value;
-                    PWM_HELPDESK.doPasswordChange(pw);
-                });
-                PWM_MAIN.addEventHandler('button-autoGeneratePassword','click',function(){
-                    PWM_HELPDESK.generatePasswordPopup();
-                })
+    bodyText += '<td style="border: 0">';
+    if (PWM_VAR['helpdesk_setting_maskPasswords']) {
+        bodyText += '<input type="password" name="password2" id="password2" class="passwordfield" style="width: 260px" autocomplete="off"/>';
+    } else {
+        bodyText += '<input type="text" name="password2" id="password2" class="inputfield" style="width: 260px" autocomplete="off"/>';
+    }
+    bodyText += '</td>';
+
+    bodyText += '<td style="border: 0"><div style="margin:0;">';
+    bodyText += '<img style="visibility:hidden;" id="confirmCheckMark" alt="checkMark" height="15" width="15" src="' + PWM_GLOBAL['url-resources'] + '/greenCheck.png">';
+    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 += '<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>';
+    }
+
+    try { PWM_MAIN.getObject('message').id = "base-message"; } catch (e) {}
+
+    PWM_MAIN.showDialog({
+        title: PWM_MAIN.showString('Title_ChangePassword') + ' - ' + PWM_VAR['helpdesk_username'],
+        text: bodyText,
+        showOk: false,
+        showClose: true,
+        allowMove: true,
+        loadFunction: function(){
+            setTimeout(function(){ PWM_MAIN.getObject('password1').focus();},500);
+            PWM_MAIN.addEventHandler('password1','input',function(){
+                PWM_CHANGEPW.validatePasswords(PWM_VAR['helpdesk_obfuscatedDN']);
+            });
+            PWM_MAIN.addEventHandler('password2','input',function(){
+                PWM_CHANGEPW.validatePasswords(PWM_VAR['helpdesk_obfuscatedDN']);
+            });
+            if (PWM_VAR['helpdesk_setting_maskPasswords']) {
+                ShowHidePasswordHandler.init('password1');
+                ShowHidePasswordHandler.init('password2');
             }
             }
-        });
+
+            PWM_MAIN.addEventHandler('password_button','click',function(){
+                var pw=PWM_MAIN.getObject('password1').value;
+                PWM_HELPDESK.doPasswordChange(pw);
+            });
+            PWM_MAIN.addEventHandler('button-autoGeneratePassword','click',function(){
+                PWM_HELPDESK.generatePasswordPopup();
+            })
+        }
     });
     });
 };
 };
 
 
@@ -269,6 +291,7 @@ PWM_HELPDESK.makeSearchGrid = function(nextAction) {
                 }
                 }
 
 
                 PWM_VAR['heldesk_search_grid'].on(".dgrid-row:click", function(evt){
                 PWM_VAR['heldesk_search_grid'].on(".dgrid-row:click", function(evt){
+                    PWM_MAIN.stopEvent(evt);
                     var row = PWM_VAR['heldesk_search_grid'].row(evt);
                     var row = PWM_VAR['heldesk_search_grid'].row(evt);
                     PWM_HELPDESK.loadSearchDetails(row.data['userKey']);
                     PWM_HELPDESK.loadSearchDetails(row.data['userKey']);
                 });
                 });
@@ -380,9 +403,9 @@ PWM_HELPDESK.sendVerificationToken = function(userKey,choiceFlag) {
     var dialoagLoadFunction = function(){};
     var dialoagLoadFunction = function(){};
     if (choiceFlag) {
     if (choiceFlag) {
         confirmText += '<br/><br/><button class="btn" type="button" name="emailChoiceButton" id="emailChoiceButton">'
         confirmText += '<br/><br/><button class="btn" type="button" name="emailChoiceButton" id="emailChoiceButton">'
-        + '<span class="btn-icon fa fa-file-text"></span>' + PWM_MAIN.showString('Button_Email') + '</button>'
-        + '<button class="btn" type="button" name="smsChoiceButton" id="smsChoiceButton">'
-        + '<span class="btn-icon fa fa-phone"></span>' + PWM_MAIN.showString('Button_SMS') + '</button>';
+            + '<span class="btn-icon fa fa-file-text"></span>' + PWM_MAIN.showString('Button_Email') + '</button>'
+            + '<button class="btn" type="button" name="smsChoiceButton" id="smsChoiceButton">'
+            + '<span class="btn-icon fa fa-phone"></span>' + PWM_MAIN.showString('Button_SMS') + '</button>';
         dialoagLoadFunction = function() {
         dialoagLoadFunction = function() {
             PWM_MAIN.addEventHandler('emailChoiceButton','click',function(){sendTokenAction('email')});
             PWM_MAIN.addEventHandler('emailChoiceButton','click',function(){sendTokenAction('email')});
             PWM_MAIN.addEventHandler('smsChoiceButton','click',function(){sendTokenAction('sms')});
             PWM_MAIN.addEventHandler('smsChoiceButton','click',function(){sendTokenAction('sms')});

+ 35 - 20
pwm/servlet/web/public/resources/js/main.js

@@ -237,25 +237,6 @@ PWM_MAIN.initPage = function() {
     console.log('initPage completed');
     console.log('initPage completed');
 };
 };
 
 
-
-PWM_MAIN.closeHeaderWarningPanel = function() {
-    console.log('action closeHeader');
-    PWM_MAIN.setStyle('header-warning','display','none');
-    PWM_MAIN.setStyle('button-openHeader','display','inherit');
-    require(["dojo/cookie"], function(cookie){
-        cookie('headerVisibility', 'hide', {});
-    });
-};
-
-PWM_MAIN.openHeaderWarningPanel = function() {
-    console.log('action openHeader');
-    PWM_MAIN.setStyle('header-warning','display','inherit');
-    PWM_MAIN.setStyle('button-openHeader','display','none');
-    require(["dojo/cookie"], function(cookie){
-        cookie('headerVisibility', 'show', {});
-    });
-};
-
 PWM_MAIN.applyFormAttributes = function() {
 PWM_MAIN.applyFormAttributes = function() {
     require(["dojo/_base/array", "dojo/query", "dojo/on"], function(array, query, on){
     require(["dojo/_base/array", "dojo/query", "dojo/on"], function(array, query, on){
         array.forEach(
         array.forEach(
@@ -697,7 +678,7 @@ PWM_MAIN.showErrorDialog = function(error, options) {
     }
     }
     if (error && error['error']) {
     if (error && error['error']) {
         var code = error['errorCode'];
         var code = error['errorCode'];
-        if (code == 5028 || code == 5035) {
+        if (code == 5028 || code == 5034 || code == 5035) {
             forceReload = true;
             forceReload = true;
         }
         }
         titleMsg += ' ' + error['errorCode'];
         titleMsg += ' ' + error['errorCode'];
@@ -1966,5 +1947,39 @@ PWM_MAIN.doIfQueryHasResults = function(queryString, trueFunction) {
     });
     });
 };
 };
 
 
+PWM_MAIN.stopEvent = function(e) {
+    if (!e) var e = window.event;
+    e.cancelBubble = true;
+    if (e.stopPropagation) e.stopPropagation();
+};
+
+PWM_MAIN.clearFocus = function() {
+    document.activeElement.blur();
+};
+
+PWM_MAIN.readLocalStorage = function() {
+    if(typeof(Storage) !== "undefined") {
+        var storedStr = localStorage.getItem("ConfigEditor_Storage");
+        if (storedStr) {
+            try {
+                return JSON.parse(storedStr);
+            } catch (e) {
+                console.error('Error decoding existing local storage value: ' + e);
+            }
+        }
+        return {};
+    } else {
+        console.log("browser doesn't support local storage");
+    }
+};
+
+PWM_MAIN.writeLocalStorage = function(dataUpdate) {
+    if(typeof(Storage) !== "undefined") {
+        if (dataUpdate) {
+            localStorage.setItem("ConfigEditor_Storage",JSON.stringify(dataUpdate));
+        }
+    }
+};
+
 PWM_MAIN.pageLoadHandler();
 PWM_MAIN.pageLoadHandler();
 
 

+ 89 - 64
pwm/servlet/web/public/resources/js/peoplesearch.js

@@ -79,12 +79,33 @@ PWM_PS.processPeopleSearch = function() {
 };
 };
 
 
 PWM_PS.convertDetailResultToHtml = function(data) {
 PWM_PS.convertDetailResultToHtml = function(data) {
+    var blankSrc = PWM_MAIN.addPwmFormIDtoURL(PWM_GLOBAL['url-resources'] + '/UserPhoto.png');
     var htmlBody = '';
     var htmlBody = '';
-    if (data['photoURL']) {
-        var blankSrc = PWM_MAIN.addPwmFormIDtoURL(PWM_GLOBAL['url-resources'] + '/dojo/dojo/resources/blank.gif');
-        var styleAttr = PWM_VAR['photo_style_attribute'];
-        htmlBody += '<div id="userPhotoParentDiv"><img style="' + styleAttr + '" src="' + blankSrc + '"></div>';
+
+    htmlBody += '<div class="panel-peoplesearch-person" id="panel-orgChart-person-' + data['userKey'] + '">';
+
+    if (PWM_VAR['peoplesearch_enablePhoto']) {
+        htmlBody += '<div class="panel-peoplesearch-userDetailPhoto" id="panel-userPhoto-' + data['userKey'] + '">';
+        htmlBody += '<img class="img-peoplesearch-userDetailPhoto" src="' + blankSrc + '">';
+        htmlBody += '</div>';
+    }
+
+    htmlBody += '<div class="panel-peoplesearch-displayNames">';
+    var loopID = 1;
+    for (var iter in data['displayNames']) {
+        (function(displayName){
+            htmlBody += '<div class="panel-orgChart-displayName-' + loopID + '">';
+            htmlBody += data['displayNames'][displayName];
+            htmlBody += '</div>'; //4
+            loopID++;
+        })(iter);
+    }
+    htmlBody += '</div>'; //3
+    if (data['hasOrgChart']) {
+        htmlBody += '<div class="icon-peoplesearch-orgChart" id="icon-peoplesearch-orgChart"></div>';
     }
     }
+    htmlBody += '</div>';
+
     htmlBody += '<div id="peopleSearch-userDetailWrapper">';
     htmlBody += '<div id="peopleSearch-userDetailWrapper">';
     htmlBody += '<table class="peopleSearch-userDetails">';
     htmlBody += '<table class="peopleSearch-userDetails">';
     for (var iter in data['detail']) {
     for (var iter in data['detail']) {
@@ -127,6 +148,19 @@ PWM_PS.convertDetailResultToHtml = function(data) {
 };
 };
 
 
 PWM_PS.applyEventHandlersToDetailView = function(data) {
 PWM_PS.applyEventHandlersToDetailView = function(data) {
+    var photoURL = data['photoURL'];
+    if (photoURL) {
+        var photoDiv = PWM_MAIN.getObject('panel-userPhoto-' + data['userKey']);
+        PWM_PS.loadPicture(photoDiv,photoURL,'img-peoplesearch-userDetailPhoto');
+    }
+
+    if (data['hasOrgChart']) {
+        PWM_MAIN.addEventHandler('icon-peoplesearch-orgChart', 'click', function () {
+            PWM_PS.showOrgChartView(data['userKey']);
+        });
+    }
+
+
     for (var iter in data['detail']) {
     for (var iter in data['detail']) {
         (function(iterCount){
         (function(iterCount){
             var attributeData = data['detail'][iterCount];
             var attributeData = data['detail'][iterCount];
@@ -167,7 +201,6 @@ PWM_PS.showUserDetail = function(userKey) {
     };
     };
     PWM_MAIN.showWaitDialog({
     PWM_MAIN.showWaitDialog({
         loadFunction:function(){
         loadFunction:function(){
-            PWM_VAR['detailInProgress'] = false;
             var url = "PeopleSearch?processAction=detail";
             var url = "PeopleSearch?processAction=detail";
             var loadFunction = function(data) {
             var loadFunction = function(data) {
                 if (data['error'] == true) {
                 if (data['error'] == true) {
@@ -176,34 +209,15 @@ PWM_PS.showUserDetail = function(userKey) {
                     return;
                     return;
                 }
                 }
                 var htmlBody = PWM_PS.convertDetailResultToHtml(data['data']);
                 var htmlBody = PWM_PS.convertDetailResultToHtml(data['data']);
-                PWM_MAIN.closeWaitDialog();
                 PWM_MAIN.showDialog({
                 PWM_MAIN.showDialog({
-                    title:data['data']['displayName'],
+                    title:'Details',
                     allowMove:true,
                     allowMove:true,
+                    showOk:false,
                     text:htmlBody,
                     text:htmlBody,
                     showClose:true,
                     showClose:true,
                     loadFunction:function(){
                     loadFunction:function(){
-                        var photoURL = data['data']['photoURL'];
-                        if (photoURL) {
-                            PWM_PS.loadPicture(PWM_MAIN.getObject("userPhotoParentDiv"),photoURL);
-                        }
-
                         PWM_PS.applyEventHandlersToDetailView(data['data']);
                         PWM_PS.applyEventHandlersToDetailView(data['data']);
-
-                        if (PWM_VAR['peoplesearch_orgChart_enabled'] && data['data']['hasOrgChart']) {
-                            var buttonObj = document.createElement('button');
-                            buttonObj.setAttribute('class', 'btn');
-                            buttonObj.id = 'button-orgChart';
-                            buttonObj.innerHTML = '<span class="btn-icon fa fa-sitemap"></span> ' + PWM_MAIN.showString('Button_OrgChart');
-                            PWM_MAIN.getObject('dialog_ok_button').parentElement.appendChild(buttonObj);
-                        }
-
-                        setTimeout(function() {
-                            try {PWM_MAIN.getObject('dialog_ok_button').focus(); } catch (e) { /*noop */}
-                        },1000);
-                        PWM_MAIN.addEventHandler('button-orgChart','click',function(){
-                            PWM_PS.showOrgChartView(userKey);
-                        });
+                        setTimeout(function(){PWM_MAIN.clearFocus()},10);
                     }
                     }
                 });
                 });
             };
             };
@@ -212,62 +226,76 @@ PWM_PS.showUserDetail = function(userKey) {
     });
     });
 };
 };
 
 
-PWM_PS.convertUserTreeDataToOrgChartHtml = function(data) {
 
 
-    var makePanel = function(parentReference, type, direction) {
-        var userKey = parentReference['userKey'];
+
+PWM_PS.convertOrgChartDataToOrgChartHtml = function(data) {
+    var makePanel = function(userReference, type, direction) {
+        var userKey = userReference['userKey'];
 
 
         var output = '';
         var output = '';
-        output += '<div class="panel-orgChart-' + type + '">';
-        output += '<div class="panel-orgChart-person">';
-        if (parentReference['hasMoreNodes']) {
+        output += '<div class="panel-orgChart-' + type + '">';  //1
+        output += '<div class="panel-orgChart-person" id="panel-orgChart-person-' + userKey + '">'; //2
+        if (userReference['hasMoreNodes']) {
             output += '<a id="link-' + userKey + '"><span class="icon-orgChart-' + direction + ' fa fa-arrow-' + direction + '"/> </a>';
             output += '<a id="link-' + userKey + '"><span class="icon-orgChart-' + direction + ' fa fa-arrow-' + direction + '"/> </a>';
         }
         }
-        if ('photoURL' in parentReference) {
-            output += '<img class="img-orgChart" id="" src="' + parentReference['photoURL'] + '">';
+        if (PWM_VAR['peoplesearch_enablePhoto']) {
+            var blankSrc = PWM_MAIN.addPwmFormIDtoURL(PWM_GLOBAL['url-resources'] + '/UserPhoto.png');
+            output += '<div id="panel-userPhoto-' + userKey + '">';
+            output += '<img class="img-orgChart-userPhoto" src="' + blankSrc + '">';
+            output += '</div>';
         }
         }
-        output += '<div class="panel-orgChart-displayNames">';
+        output += '<div class="panel-orgChart-displayNames">'; //3
         var loopID = 1;
         var loopID = 1;
-        for (var iter in parentReference['displayNames']) {
+        for (var iter in userReference['displayNames']) {
             (function(displayName){
             (function(displayName){
-                output += '<div class="panel-orgChart-displayName-' + loopID + '">';
-                output += parentReference['displayNames'][displayName];
-                output += '</div>';
+                output += '<div class="panel-orgChart-displayName-' + loopID + '">';  //4
+                output += userReference['displayNames'][displayName];
+                output += '</div>'; //4
                 loopID++;
                 loopID++;
             })(iter);
             })(iter);
         }
         }
-        output += '</div>';
-        output += ' <span id="button-userDetail-' + userKey + '" class="btn-icon fa fa-info-circle">';
-        output += '</div></div>';
+        output += '</div>'; //3
+        output += '</div>'; //2
+        output += '</div>'; //1
         return output;
         return output;
     };
     };
 
 
-
     var htmlOutput = '';
     var htmlOutput = '';
+    htmlOutput += '<div class="panel-orgChart">'; //1
     if ('parent' in data) {
     if ('parent' in data) {
         htmlOutput += makePanel(data['parent'], 'parent', 'up');
         htmlOutput += makePanel(data['parent'], 'parent', 'up');
     }
     }
     if ('siblings' in data) {
     if ('siblings' in data) {
+        htmlOutput += '<div class="panel-orgChart-siblings">'; //2
         for (var iter in data['siblings']) {
         for (var iter in data['siblings']) {
             (function(iterCount){
             (function(iterCount){
                 var siblingReference = data['siblings'][iterCount];
                 var siblingReference = data['siblings'][iterCount];
                 htmlOutput += makePanel(siblingReference, 'sibling', 'down');
                 htmlOutput += makePanel(siblingReference, 'sibling', 'down');
             })(iter);
             })(iter);
         }
         }
+        htmlOutput += '</div>'; //2
     }
     }
+    htmlOutput += '<div class="panel-orgChart-footer"></div>';
+    htmlOutput += '</div>'; //1
     return htmlOutput;
     return htmlOutput;
 };
 };
 
 
-PWM_PS.applyUserTreeDataToOrgChartEvents = function(data) {
+PWM_PS.applyOrgChartDataToOrgChartEvents = function(data) {
     var applyEventsToReference = function(reference,asParent) {
     var applyEventsToReference = function(reference,asParent) {
+        PWM_MAIN.addEventHandler('panel-orgChart-person-' + reference['userKey'],'click',function(){
+            PWM_PS.showUserDetail(reference['userKey']);
+        });
         if (reference['hasMoreNodes']) {
         if (reference['hasMoreNodes']) {
-            PWM_MAIN.addEventHandler('link-' + reference['userKey'], 'click', function () {
+            PWM_MAIN.addEventHandler('link-' + reference['userKey'], 'click', function (e) {
+                PWM_MAIN.stopEvent(e);
                 PWM_PS.showOrgChartView(reference['userKey'],asParent)
                 PWM_PS.showOrgChartView(reference['userKey'],asParent)
             });
             });
         }
         }
-        PWM_MAIN.addEventHandler('button-userDetail-' + reference['userKey'],'click',function(){
-            PWM_PS.showUserDetail(reference['userKey']);
-        });
+        if ('photoURL' in reference) {
+            var photoURL = reference['photoURL'];
+            var parentDiv = PWM_MAIN.getObject('panel-userPhoto-' + reference['userKey']);
+            PWM_PS.loadPicture(parentDiv, photoURL,'img-orgChart-userPhoto');
+        }
 
 
     };
     };
     if ('parent' in data) {
     if ('parent' in data) {
@@ -292,22 +320,24 @@ PWM_PS.showOrgChartView = function(userKey, asParent) {
     };
     };
     PWM_MAIN.showWaitDialog({
     PWM_MAIN.showWaitDialog({
         loadFunction:function(){
         loadFunction:function(){
-            var url = "PeopleSearch?processAction=userTreeData";
+            var url = "PeopleSearch?processAction=orgChartData";
             var loadFunction = function(data) {
             var loadFunction = function(data) {
                 if (data['error'] == true) {
                 if (data['error'] == true) {
                     PWM_MAIN.closeWaitDialog();
                     PWM_MAIN.closeWaitDialog();
                     PWM_MAIN.showErrorDialog(data);
                     PWM_MAIN.showErrorDialog(data);
                     return;
                     return;
                 }
                 }
-                var htmlBody = PWM_PS.convertUserTreeDataToOrgChartHtml(data['data']);
+                var htmlBody = PWM_PS.convertOrgChartDataToOrgChartHtml(data['data']);
                 PWM_MAIN.closeWaitDialog();
                 PWM_MAIN.closeWaitDialog();
                 PWM_MAIN.showDialog({
                 PWM_MAIN.showDialog({
                     title:PWM_MAIN.showString('Title_OrgChart'),
                     title:PWM_MAIN.showString('Title_OrgChart'),
                     allowMove:true,
                     allowMove:true,
+                    showOk:false,
                     text:htmlBody,
                     text:htmlBody,
+                    dialogClass:'orgChart',
                     showClose:true,
                     showClose:true,
                     loadFunction:function(){
                     loadFunction:function(){
-                        PWM_PS.applyUserTreeDataToOrgChartEvents(data['data']);
+                        PWM_PS.applyOrgChartDataToOrgChartEvents(data['data']);
                         setTimeout(function() {
                         setTimeout(function() {
                             try {PWM_MAIN.getObject('dialog_ok_button').focus(); } catch (e) { /*noop */}
                             try {PWM_MAIN.getObject('dialog_ok_button').focus(); } catch (e) { /*noop */}
                         },1000);
                         },1000);
@@ -339,15 +369,11 @@ PWM_PS.makeSearchGrid = function(nextFunction) {
             }
             }
 
 
             PWM_VAR['peoplesearch_search_grid'].on(".dgrid-row:click", function(evt){
             PWM_VAR['peoplesearch_search_grid'].on(".dgrid-row:click", function(evt){
-                if (PWM_VAR['detailInProgress'] != true) {
-                    PWM_VAR['detailInProgress'] = true;
-                    evt.preventDefault();
-                    var row = PWM_VAR['peoplesearch_search_grid'].row(evt);
-                    var userKey = row.data['userKey'];
-                    PWM_PS.showUserDetail(userKey);
-                } else {
-                    console.log('ignoring dupe detail request event');
-                }
+                PWM_MAIN.stopEvent(evt);
+                evt.preventDefault();
+                var row = PWM_VAR['peoplesearch_search_grid'].row(evt);
+                var userKey = row.data['userKey'];
+                PWM_PS.showUserDetail(userKey);
             });
             });
 
 
             PWM_VAR['peoplesearch_search_grid'].set("sort", { attribute : 'sn', descending: true});
             PWM_VAR['peoplesearch_search_grid'].set("sort", { attribute : 'sn', descending: true});
@@ -356,11 +382,10 @@ PWM_PS.makeSearchGrid = function(nextFunction) {
     );
     );
 };
 };
 
 
-PWM_PS.loadPicture = function(parentDiv,url) {
+PWM_PS.loadPicture = function(parentDiv,url,cssClass) {
     require(["dojo/on"], function(on){
     require(["dojo/on"], function(on){
         var image = new Image();
         var image = new Image();
-        image.setAttribute('id',"userPhotoImage");
-        image.setAttribute('style',PWM_VAR['photo_style_attribute']);
+        image.setAttribute('class',cssClass);
         on(image,"load",function(){
         on(image,"load",function(){
             if (parentDiv) {
             if (parentDiv) {
                 while (parentDiv.firstChild) {
                 while (parentDiv.firstChild) {

+ 26 - 0
pwm/servlet/web/public/resources/mobileStyle.css

@@ -26,6 +26,7 @@ html, body {
     height: 100%;
     height: 100%;
     margin: 0;
     margin: 0;
     width: 100%;
     width: 100%;
+    min-height: inherit;
 }
 }
 
 
 /* main content section, all content should be inside a centerbody div */
 /* main content section, all content should be inside a centerbody div */
@@ -99,3 +100,28 @@ progress .wait {
     width: auto;
     width: auto;
     table-layout: fixed;
     table-layout: fixed;
 }
 }
+
+.dialogBody.orgChart { width: 100%; max-width: 100%; }
+
+.peoplesearch-wrapper #centerbody.wide {
+    bottom: 80px;
+    height: auto !important;
+    left: 0;
+    margin: auto;
+    min-width: auto;
+    position: absolute;
+    right: 40px;
+    top: 80px;
+    width: auto;
+}
+
+
+#peoplesearch-searchResultsGrid {
+    bottom: 0px;
+    height: auto;
+    left: 0px;
+    position: absolute;
+    right: 0px;
+    top: 45px;
+    cursor: pointer;
+}

BIN
pwm/servlet/web/public/resources/orgChart.png


+ 111 - 116
pwm/servlet/web/public/resources/style.css

@@ -26,6 +26,7 @@ html, body {
     font-size: 12px;
     font-size: 12px;
     height: 100%;
     height: 100%;
     margin: 0;
     margin: 0;
+    min-height: 350px;
 }
 }
 
 
 a {
 a {
@@ -254,27 +255,6 @@ input[type=password]::-ms-reveal{display: none;}
      color: #9e9e9e;
      color: #9e9e9e;
 }
 }
 
 
-.dialogBody {
-    width: 500px;
-    max-width: 500px;
-}
-.dialogBody.narrow {
-    width: 350px;
-    max-width: 350px;
-}
-.dialogBody.wide {
-    width: 95%;
-    max-width: 100%;
-    min-width: 800px;
-}
-
-.WaitDialogBlank {
-    height: 46px;
-    width: 46px;
-    margin-left: auto;
-    margin-right: auto;
-    background-image: url('wait.gif');
-}
 
 
 /* used for password complexity meter */
 /* used for password complexity meter */
 div.progress-container {
 div.progress-container {
@@ -365,17 +345,45 @@ div.progress-container > div {
 }
 }
 
 
 #header-warning {
 #header-warning {
+    text-align: center;
+    position: absolute;
+    right:6px;
+    top:6px;
+    width: 190px;
+    border: 2px solid #aaaaaa;
+    background-color: #DDDDDD;
+    z-index: 10;
+    padding: 10px;
+    animation: fadein 0.3s;
+    -moz-animation: fadein 0.3s; /* Firefox */
+    -webkit-animation: fadein 0.3s; /* Safari and Chrome */
+}
+
+.header-warning-buttons {
+    padding-left: 1px;
+    padding-right: 1px;
+    text-align: center;
     width: 100%;
     width: 100%;
-    background-color: #aaaaaa;
+    margin-bottom: 15px;
+}
+
+.header-warning-button {
     text-align: center;
     text-align: center;
+    width: 100%;
+    padding-bottom: 10px;
+    display: block;
+    color: black;
+    cursor: pointer;
 }
 }
 
 
+
 .header-error {
 .header-error {
     text-align: center;
     text-align: center;
     white-space: normal;
     white-space: normal;
     max-width: 600px;
     max-width: 600px;
     padding-left: 6px;
     padding-left: 6px;
     padding-right: 6px;
     padding-right: 6px;
+    margin-top: 10px;
     margin-left: auto;
     margin-left: auto;
     margin-right: auto;
     margin-right: auto;
     position: relative;
     position: relative;
@@ -457,13 +465,6 @@ div.progress-container > div {
 }
 }
 
 
 
 
-#dialogPopup_underlay {
-    background-color: #222222;
-}
-
-#idleDialog_underlay {
-    background-color: #111111;
-}
 
 
 .agreementText {
 .agreementText {
     border: 0;
     border: 0;
@@ -524,34 +525,11 @@ img.qrcodeimage {
     background-color: black;
     background-color: black;
 }
 }
 
 
-#userPhotoParentDiv {
-    width: 100%;
-    text-align: center;
-}
-
-#userPhotoImage {
-    border: 2px solid #D4D4D4;
-}
-
-#peopleSearch-userDetailWrapper {
-    max-height: 450px;
-    overflow-y: auto;
-}
-
-#peoplesearch-searchResultsGrid {
-    min-height: 400px;
-    height: 60vh;
-}
-
 #helpdesk-searchResultsGrid {
 #helpdesk-searchResultsGrid {
     min-height: 400px;
     min-height: 400px;
     height: 60vh;
     height: 60vh;
 }
 }
 
 
-.peoplesearch-input-username {
-    width:400px;
-}
-
 .checkboxWrapper {
 .checkboxWrapper {
     border: 0;
     border: 0;
     padding: 5px;
     padding: 5px;
@@ -572,11 +550,11 @@ select {
 
 
 #button-openHeader {
 #button-openHeader {
     position: absolute;
     position: absolute;
-    top: 3px;
-    right: 4px;
+    top: 10px;
+    right: 10px;
     z-index: 100;
     z-index: 100;
     cursor: pointer;
     cursor: pointer;
-    color: red;
+    color: #ffcd59;
 }
 }
 
 
 #button-closeHeader {
 #button-closeHeader {
@@ -632,56 +610,19 @@ input[type=search] {
     padding-bottom: 10px;
     padding-bottom: 10px;
 }
 }
 
 
-
-dialog::backdrop { background: rgba(0,0,0,0.6); }
-
-dialog {
-    border: 2px solid #DDDDDD;
-    border-radius: 3px;
-    padding:0;
-    animation: fadein 0.3s;
-    -moz-animation: fadein 0.3s; /* Firefox */
-    -webkit-animation: fadein 0.3s; /* Safari and Chrome */
-    max-width: 100%;
-}
-
-dialog .titleBar {
-    text-align: center;
-    font-weight: bold;
-    padding-top: 3px;
-    padding-bottom: 3px;
-    background-color: #eaeaea;
-    margin-bottom: 5px;
-
-}
-
-dialog .body {
-    padding:5px;
-    max-height: 98vh;
-    overflow-x: auto;
-}
-
-dialog .closeIcon {
-    float: right;
-    cursor: pointer;
-    margin-right: 3px;
-}
-
 progress:not([value]) {
 progress:not([value]) {
     width: 100%;
     width: 100%;
     height: 20px;
     height: 20px;
 }
 }
 
 
+.blink { animation: blink 1s steps(5, start) infinite; -webkit-animation: blink 1s steps(5, start) infinite; }
+@keyframes blink { to { visibility: hidden; } }
+@-webkit-keyframes blink { to { visibility: hidden; } }
+
 @keyframes fadein { from { opacity:0; } to { opacity:1; } }
 @keyframes fadein { from { opacity:0; } to { opacity:1; } }
 @-moz-keyframes fadein { from { opacity:0 } to { opacity:1 } }
 @-moz-keyframes fadein { from { opacity:0 } to { opacity:1 } }
 @-webkit-keyframes fadein { from { opacity:0 } to { opacity:1 } }
 @-webkit-keyframes fadein { from { opacity:0 } to { opacity:1 } }
 
 
-
-.peopleSearch-userDetails {
-    max-height: 450px;
-    overflow-y: auto;
-}
-
 .meteredProgressBar {
 .meteredProgressBar {
     width:80%;
     width:80%;
     margin-left: 10%;
     margin-left: 10%;
@@ -693,29 +634,83 @@ progress:not([value]) {
     background: #fdfdfd;
     background: #fdfdfd;
 }
 }
 
 
-.panel-orgChart-person {
-    margin-left:auto;
-    margin-right:auto;
-    display: inline-block;
-    background-color: #D4D4D4;
-    padding: 3px;
-    border-radius: 3px;
-    background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #D4D4D4), color-stop(1, #EEEEEE) );
-    background:linear-gradient(to bottom, #D4D4D4 5%, #EEEEEE 100% );
-    margin-bottom: 5px;
-    width:auto;
+
+/* begin dialog section */
+.dialogBody { width: 500px; max-width: 500px; }
+.dialogBody.narrow { width: 350px; max-width: 350px; }
+.dialogBody.wide { width: 95%; max-width: 100%; min-width: 800px; }
+.WaitDialogBlank { height: 46px; width: 46px; margin-left: auto; margin-right: auto; background-image: url('wait.gif');}
+#dialogPopup_underlay { background-color: #222222; }
+#idleDialog_underlay { background-color: #111111; }
+dialog::backdrop { background: rgba(0,0,0,0.6); }
+dialog { border: 2px solid #DDDDDD; border-radius: 3px; padding:0; max-width: 100%;}
+dialog .titleBar { text-align: center; font-weight: bold; padding-top: 3px; padding-bottom: 3px; background-color: #eaeaea; margin-bottom: 5px; }
+dialog .body { padding:5px; max-height: 98vh; overflow-x: auto; }
+dialog .closeIcon { float: right; cursor: pointer; margin-right: 3px; }
+/* end dialog section */
+
+
+/* begin peoplesearch section */
+
+.icon-peoplesearch-orgChart { background-image: url("orgChart.png"); position: absolute; top:5px; right:5px; height:30px; width:30px; cursor: pointer;
 }
 }
+.panel-peoplesearch-userDetailPhoto { display: inline  }
+.img-peoplesearch-userDetailPhoto { width: 80px; height: 80px; border-radius: 2px; border: 1px solid #d8e2e2; }
+.panel-peoplesearch-person { margin-left:5px; background: #eaeaea; position: relative; padding: 6px; width: auto; height: 83px; margin-right: 5px; margin-bottom: 10px; border:  1px solid transparent; }
+.panel-peoplesearch-displayNames { margin-left: 10px; vertical-align: top; display: inline-block; position: relative }
 
 
-.panel-orgChart-sibling {
-    display: inline-block;
-    margin-left: 25px;
+
+#peopleSearch-userDetailWrapper { max-height: 450px; overflow-y: auto; }
+.peoplesearch-input-username { width:400px; }
+#peoplesearch-searchResultsGrid {
+    bottom: 10px;
+    height: auto;
+    left: 10px;
+    position: absolute;
+    right: 10px;
+    top: 45px;
+    cursor: pointer;
 }
 }
 
 
-.panel-orgChart-displayName-1 {
-    font-weight: bold;
+.peoplesearch-wrapper #centerbody.wide {
+    bottom: 80px;
+    height: auto !important;
+    left: 40px;
+    margin: auto;
+    min-width: auto;
+    position: absolute;
+    right: 40px;
+    top: 100px;
+    width: auto;
 }
 }
 
 
-.img-orgChart {
-    height:25px;
-    width:25px
-}
+.peopleSearch-userDetails { max-height: 450px; overflow-y: auto; border: 1px solid #d8e2e2; }
+.peopleSearch-userDetails td.key { font-weight: normal; color: #949494; width: auto; padding: 3px 15px 3px 10px; border-bottom: 1px solid #d8e2e2; }
+
+
+
+/* end peoplesearch section */
+
+
+/* begin peoplesearch org chart section */
+.dialogBody.orgChart { width: 550px; max-width: 550px; }
+.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-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; }
+.icon-orgChart-up { position: absolute; left: 12px; top: 54px; padding: 4px 8px; border: 1px solid #b6b6b6; }
+.icon-orgChart-down {position: absolute; left: 12px; top: 54px; padding: 4px 8px; border: 1px solid #b6b6b6;}
+.panel-orgChart-displayNames { }
+.panel-orgChart-displayName-1 { font-weight: bold; }
+.panel-orgChart-displayName-2 { color: #697374; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.panel-orgChart-displayName-3 { color: #697374; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 10px; }
+.panel-orgChart-displayName-4 { color: #697374; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 10px; }
+.panel-orgChart-displayName-5 { color: #697374; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 10px; }
+.panel-orgChart-person:hover { border:  1px solid #d8e2e2; }
+.panel-orgChart { max-height: 600px; overflow-y: auto }
+.panel-orgChart-footer {clear: both; margin-bottom: 10px; }
+/* end peoplesearch org chart section */
+