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_JITTER_COUNT                 ("password.randomGenerator.jitter.count"),
     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_MAX_AGE_MS                          ("queue.email.maxAgeMs"),
     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_COUNT                          ("queue.syslog.maxCount"),
     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_VALIDATE_URL                          ("recaptcha.validateUrl"),
     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.jitter.count=50
 peoplesearch.displayName.enableAllMacros=false
-peoplesearch.maxValueCount=100
+peoplesearch.values.verifyUserDN=true
+peoplesearch.values.maxCount=100
 queue.email.retryTimeoutMs=10000
 queue.email.maxAgeMs=86400000
 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.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
+import password.pwm.bean.AboutApplicationBean;
 import password.pwm.bean.SmsItemBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
@@ -39,6 +40,7 @@ import password.pwm.event.AuditEvent;
 import password.pwm.event.AuditManager;
 import password.pwm.event.SystemAuditRecord;
 import password.pwm.health.HealthMonitor;
+import password.pwm.http.servlet.AdminServlet;
 import password.pwm.ldap.LdapConnectionService;
 import password.pwm.token.TokenService;
 import password.pwm.util.*;
@@ -229,7 +231,29 @@ public class PwmApplication {
         LOGGER.info(logEnvironment());
         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) {
+            final Date startTime = new Date();
             final PwmService newServiceInstance;
             try {
                 final Object newInstance = serviceClass.newInstance();
@@ -243,7 +267,7 @@ public class PwmApplication {
             try {
                 LOGGER.debug("initializing service " + serviceClass.getName());
                 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) {
                 LOGGER.warn("error instantiating service class '" + serviceClass.getName() + "', service will remain unavailable, error: " + e.getMessage());
             } catch (Exception e) {
@@ -256,12 +280,10 @@ public class PwmApplication {
             }
             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
         try {
             final String previousHash = readAppAttribute(AppAttribute.CONFIG_HASH);
@@ -306,6 +328,15 @@ public class PwmApplication {
         } catch (PwmException e) {
             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() {
@@ -465,7 +496,7 @@ public class PwmApplication {
 
         envStats.put("memmax",Runtime.getRuntime().maxMemory());
         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);
     }
@@ -719,28 +750,30 @@ public class PwmApplication {
             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,
         }
 
-        public PwmEnvironment setConfig(Configuration config) {
+        public PwmEnvironment(Configuration config, File applicationPath) {
             this.config = config;
-            return this;
+            this.applicationPath = applicationPath;
         }
 
         public PwmEnvironment setApplicationMode(MODE applicationMode) {
@@ -771,11 +804,6 @@ public class PwmApplication {
             return this;
         }
 
-        public PwmEnvironment setApplicationPath(File applicationPath) {
-            this.applicationPath = applicationPath;
-            return this;
-        }
-
         public PwmEnvironment setInitLogging(boolean initLogging) {
             this.initLogging = initLogging;
             return this;

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

@@ -46,5 +46,5 @@ pwmDBLoggerMaxQueueSize=50000
 pwmDBLoggerMaxDirtyBufferMS=500
 enableEulaDisplay=false
 databaseAccessor.keyLength=128
-missingVersionString=[Missing_Version_#]
+missingVersionString=[Version Missing]
 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 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 {
         private long memoryFree;
@@ -74,6 +150,7 @@ public class AboutApplicationBean implements Serializable {
         private String vmName;
         private String osName;
         private String osVersion;
+        private String randomAlgorithm;
 
         public JavaInformation() {
         }
@@ -165,6 +242,14 @@ public class AboutApplicationBean implements Serializable {
         public void setOsVersion(String 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) {
         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),
     PEOPLE_SEARCH_DISPLAY_NAME(
             "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(
             "peopleSearch.photo.ldapAttribute", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_PHOTO_URL_OVERRIDE(
             "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(
             "peopleSearch.maxCacheSeconds", PwmSettingSyntax.DURATION, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_PHOTO_QUERY_FILTER(
@@ -814,8 +814,6 @@ public enum PwmSetting {
             "peopleSearch.orgChart.parentAttribute", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH),
     PEOPLE_SEARCH_ORGCHART_CHILD_ATTRIBUTE(
             "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 key="audit.syslog.servers" level="1" required="false">
         <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>
             <value/>
         </default>
@@ -2994,9 +2994,22 @@
             <value>false</value>
         </default>
     </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">
-        <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>
             <value>@LDAP:givenName@ @LDAP:sn@ - @LDAP:title@</value>
         </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>
         <default/>
     </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">
         <label>PeopleSearch Maximum Cache Seconds</label>
         <description><![CDATA[]]></description>
@@ -3055,18 +3061,6 @@
             <value>directReports</value>
         </default>
     </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">
         <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>

+ 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 boolean locked = false;
-    private boolean setting_writeLabels = false;
+    private boolean setting_writeLabels = true;
     private final ReentrantReadWriteLock domModifyLock = new ReentrantReadWriteLock();
 
 // -------------------------- STATIC METHODS --------------------------

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

@@ -73,10 +73,8 @@ public class UserMatchViewerFunction implements SettingUIFunction {
             throws Exception
     {
         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)
-                .setApplicationPath(pwmApplication.getApplicationPath())
                 .setInitLogging(false)
                 .setConfigurationFile(null)
                 .setWebInfPath(pwmApplication.getWebInfPath())

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

@@ -96,7 +96,7 @@ public class PasswordValue implements StoredValue {
                 } else {
                     try {
                         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;
                     } catch (Exception e) {
                         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);
 
         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;
         }
 
@@ -397,7 +397,7 @@ public class AuditManager implements PwmService {
         }
 
         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;
         }
 

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

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

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

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

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

@@ -1,13 +1,19 @@
 package password.pwm.http;
 
 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) {
         for (final HttpMethod method : HttpMethod.values()) {
             if (method.toString().equalsIgnoreCase(input)) {
@@ -16,4 +22,8 @@ public enum HttpMethod {
         }
         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;
     }
 
+    public boolean hasParameter(final String name)
+            throws PwmUnrecoverableException {
+        return this.getHttpServletRequest().getParameterMap().containsKey(name);
+    }
+
     public String readParameterAsString(final String name)
             throws PwmUnrecoverableException {
         final int maxLength = Integer.parseInt(configuration.readAppProperty(AppProperty.HTTP_PARAM_MAX_READ_LENGTH));
@@ -303,5 +308,10 @@ public abstract class PwmHttpRequestWrapper {
         }
         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()
     {
-        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) {
             LOGGER.trace(this, "noted originally requested url as: " + originalRequestedUrl);
             pwmSession.getSessionStateBean().setOriginalRequestURL(originalRequestedUrl);
@@ -589,4 +583,14 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
     public void invalidateSession() {
         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
         sslContext.init(null, new TrustManager[]{new X509TrustManager() {
             public X509Certificate[] getAcceptedIssuers() {
-                System.out.println("getAcceptedIssuers =============");
                 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());
 

+ 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
-        final String originalRequestedUrl = figureOriginalRequestUrl(pwmRequest);
+        final String originalRequestedUrl = pwmRequest.getURLwithQueryString();
 
         pwmRequest.markPreLoginUrl();
 
@@ -413,7 +413,7 @@ public class AuthenticationFilter extends AbstractPwmFilter {
             return false;
         }
 
-        final String originalURL = figureOriginalRequestUrl(pwmRequest);
+        final String originalURL = pwmRequest.getURLwithQueryString();
 
         final String state = OAuthConsumerServlet.makeStateStringForRequest(pwmRequest, originalURL);
         final String redirectUri = OAuthConsumerServlet.figureOauthSelfEndPointUrl(pwmRequest);
@@ -519,15 +519,5 @@ public class AuthenticationFilter extends AbstractPwmFilter {
 
         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 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()) {
             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.bean.AdminBean;
 import password.pwm.util.Helper;
+import password.pwm.util.PwmRandom;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.report.ReportService;
 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.setLocalDbFreeSpace(Helper.formatDiskSize(Helper.diskSpaceRemaining(pwmApplication.getLocalDB().getFileLocation())));
 
-        { // java version
+        { // java info
             final Runtime runtime = Runtime.getRuntime();
-            final AboutApplicationBean.JavaInformation javaInformation = aboutBean.getJavaInformation();
+            final AboutApplicationBean.JavaInformation javaInformation = new AboutApplicationBean.JavaInformation();
             javaInformation.setMemoryFree(runtime.freeMemory());
             javaInformation.setMemoryAllocated(runtime.totalMemory());
             javaInformation.setMemoryMax(runtime.maxMemory());
@@ -294,6 +295,21 @@ public class AdminServlet extends PwmServlet {
 
             javaInformation.setOsName(System.getProperty("os.name"));
             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;

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

@@ -336,13 +336,11 @@ public class ConfigGuideServlet extends PwmServlet {
     )
             throws IOException, PwmUnrecoverableException {
         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)
-                .setApplicationPath(pwmRequest.getPwmApplication().getApplicationPath())
                 .setInitLogging(false)
-                .setConfigurationFile(null)
-                .setWebInfPath(pwmRequest.getPwmApplication().getWebInfPath()).createPwmApplication();
+                .setWebInfPath(pwmRequest.getPwmApplication().getWebInfPath())
+                .createPwmApplication();
         final LDAPStatusChecker ldapStatusChecker = new LDAPStatusChecker();
         final List<HealthRecord> records = new ArrayList<>();
         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 {
             final PwmRequest pwmRequest = PwmRequest.forRequest(req, resp);
 
+            if (!method.isIdempotent()) {
+                Validator.validatePwmFormID(pwmRequest);
+            }
+
             // check for duplicate form submit.
             try {
                 Validator.validatePwmRequestCounter(pwmRequest);
@@ -104,10 +108,6 @@ public abstract class PwmServlet extends HttpServlet {
                 }
             }
 
-            if (method == HttpMethod.POST) {
-                Validator.validatePwmFormID(pwmRequest);
-            }
-
             this.processAction(pwmRequest);
         } catch (Exception e) {
             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.Validator;
 import password.pwm.bean.ResponseInfoBean;
-import password.pwm.bean.SessionStateBean;
 import password.pwm.bean.UserInfoBean;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.ChallengeProfile;
@@ -139,7 +138,7 @@ public class SetupResponsesServlet extends PwmServlet {
             pwmSession.getSetupResponseBean().setUserLocale(pwmSession.getSessionStateBean().getLocale());
         }
 
-        SetupResponsesBean setupResponsesBean = (SetupResponsesBean)pwmSession.getSessionBean(SetupResponsesBean.class);
+        SetupResponsesBean setupResponsesBean = pwmSession.getSessionBean(SetupResponsesBean.class);
         initializeBean(pwmRequest, setupResponsesBean);
 
         // check to see if the user has any challenges assigned
@@ -173,12 +172,12 @@ public class SetupResponsesServlet extends PwmServlet {
                     break;
 
                 case clearExisting:
-                    handleClearResponses(pwmRequest, setupResponsesBean);
+                    handleClearResponses(pwmRequest);
                     return;
 
                 case changeResponses:
                     pwmSession.clearSessionBean(SetupResponsesBean.class);
-                    setupResponsesBean = (SetupResponsesBean)pwmSession.getSessionBean(SetupResponsesBean.class);
+                    setupResponsesBean = pwmSession.getSessionBean(SetupResponsesBean.class);
                     this.initializeBean(pwmRequest, setupResponsesBean);
                     setupResponsesBean.setUserLocale(pwmSession.getSessionStateBean().getLocale());
 
@@ -190,8 +189,7 @@ public class SetupResponsesServlet extends PwmServlet {
     }
 
     private void handleClearResponses(
-            final PwmRequest pwmRequest,
-            final SetupResponsesBean setupResponsesBean
+            final PwmRequest pwmRequest
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, IOException {
         LOGGER.trace(pwmRequest, "request for response clear received");
@@ -257,8 +255,7 @@ public class SetupResponsesServlet extends PwmServlet {
 
         try { // everything good, so lets save responses.
             final ResponseInfoBean responses = generateResponseInfoBean(
-                    pwmRequest.getPwmApplication(),
-                    pwmRequest.getPwmSession(),
+                    pwmRequest,
                     setupResponsesBean.getResponseData().getChallengeSet(),
                     setupResponsesBean.getResponseData().getResponseMap(),
                     setupResponsesBean.getHelpdeskResponseData().getResponseMap()
@@ -300,7 +297,7 @@ public class SetupResponsesServlet extends PwmServlet {
             final Map<Challenge, String> responseMap = readResponsesFromJsonRequest(pwmRequest, setupData);
             final int minRandomRequiredSetup = setupData.getMinRandomSetup();
             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) {
             success = false;
             userMessage = e.getErrorInformation().toUserStr(pwmSession, pwmApplication);
@@ -317,7 +314,6 @@ public class SetupResponsesServlet extends PwmServlet {
     )
             throws PwmUnrecoverableException, IOException, ServletException, ChaiUnavailableException
     {
-        final SessionStateBean ssBean = pwmRequest.getPwmSession().getSessionStateBean();
         final SetupResponsesBean.SetupData setupData = helpdeskMode ? setupResponsesBean.getHelpdeskResponseData() : setupResponsesBean.getResponseData();
 
         final ChallengeSet challengeSet = setupData.getChallengeSet();
@@ -434,14 +430,14 @@ public class SetupResponsesServlet extends PwmServlet {
 
 
     private static ResponseInfoBean generateResponseInfoBean(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final ChallengeSet challengeSet,
             final Map<Challenge, String> readResponses,
             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 {
             final ResponseInfoBean responseInfoBean = new ResponseInfoBean(
@@ -463,7 +459,7 @@ public class SetupResponsesServlet extends PwmServlet {
 
             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 (responseSet.getChallengeSet().getRandomChallenges().size() < challengeSet.getRandomChallenges().size()) {
                     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
  */
 
-package password.pwm.http.servlet;
+package password.pwm.http.servlet.peoplesearch;
 
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiException;
@@ -37,7 +37,7 @@ import password.pwm.config.UserPermission;
 import password.pwm.error.*;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmRequest;
-import password.pwm.http.PwmSession;
+import password.pwm.http.servlet.PwmServlet;
 import password.pwm.ldap.*;
 import password.pwm.util.JsonUtil;
 import password.pwm.util.SecureHelper;
@@ -51,6 +51,7 @@ import password.pwm.util.stats.StatisticsManager;
 import password.pwm.ws.server.RestResultBean;
 
 import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -62,248 +63,12 @@ public class PeopleSearchServlet extends PwmServlet {
 
     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 {
         search(HttpMethod.POST),
         detail(HttpMethod.POST),
         photo(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);
         }
 
+        final PeopleSearchConfiguration peopleSearchConfiguration = new PeopleSearchConfiguration(pwmRequest.getConfig());
+
         final PeopleSearchActions peopleSearchAction = this.readProcessAction(pwmRequest);
         if (peopleSearchAction != null) {
             switch (peopleSearchAction) {
@@ -363,7 +130,7 @@ public class PeopleSearchServlet extends PwmServlet {
                     return;
 
                 case detail:
-                    restUserDetailRequest(pwmRequest);
+                    restUserDetailRequest(pwmRequest, peopleSearchConfiguration);
                     return;
 
                 case photo:
@@ -371,11 +138,11 @@ public class PeopleSearchServlet extends PwmServlet {
                     return;
 
                 case clientData:
-                    restLoadClientData(pwmRequest);
+                    restLoadClientData(pwmRequest, peopleSearchConfiguration);
                     return;
 
-                case userTreeData:
-                    restUserTreeData(pwmRequest);
+                case orgChartData:
+                    restOrgChartData(pwmRequest, peopleSearchConfiguration);
                     return;
             }
         }
@@ -388,30 +155,28 @@ public class PeopleSearchServlet extends PwmServlet {
     }
 
     private void restLoadClientData(
-            final PwmRequest pwmRequest
+            final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration
+
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
 
         final Map<String, String> searchColumns = new LinkedHashMap<>();
-        final String photoStyle = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_STYLE_ATTR);
         final List<FormConfiguration> searchForm = pwmRequest.getConfig().readSettingAsForm(PwmSetting.PEOPLE_SEARCH_RESULT_FORM);
         for (final FormConfiguration formConfiguration : searchForm) {
             searchColumns.put(formConfiguration.getName(),
                     formConfiguration.getLabel(pwmRequest.getLocale()));
         }
 
-        final boolean orgChartEnabled = orgChartIsEnabled(pwmRequest.getConfig());
-
         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);
         LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
         pwmRequest.outputJsonResult(restResultBean);
-    };
+    }
 
 
     private void restSearchRequest(
@@ -419,34 +184,27 @@ public class PeopleSearchServlet extends PwmServlet {
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
-        final Date startTime = new Date();
         final String bodyString = pwmRequest.readRequestBodyAsString();
         final Map<String, String> valueMap = JsonUtil.deserializeStringMap(bodyString);
 
         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);
             if (cachedOutput != null) {
                 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;
+            } else {
+                StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES);
             }
         }
 
         final SearchResultBean outputData = makeSearchResultsImpl(pwmRequest, username);
         final RestResultBean restResultBean = new RestResultBean(outputData);
         pwmRequest.outputJsonResult(restResultBean);
+        StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_SEARCHES);
         storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, outputData);
     }
 
@@ -459,11 +217,10 @@ public class PeopleSearchServlet extends PwmServlet {
         final Date startTime = new Date();
 
         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.SearchConfiguration searchConfiguration = new UserSearchEngine.SearchConfiguration();
         searchConfiguration.setContexts(
@@ -493,10 +250,6 @@ public class PeopleSearchServlet extends PwmServlet {
             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(
                 startTime).asCompactString() + " not using cache, size=" + results.getResults().size());
 
@@ -507,18 +260,18 @@ public class PeopleSearchServlet extends PwmServlet {
     }
 
     private static UserSearchEngine.UserSearchResults doDetailLookup(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration,
             final UserIdentity userIdentity
     )
             throws PwmUnrecoverableException
     {
-        final Configuration config = pwmApplication.getConfig();
+        final Configuration config = pwmRequest.getConfig();
         final List<FormConfiguration> detailFormConfig = config.readSettingAsForm(PwmSetting.PEOPLE_SEARCH_DETAIL_FORM);
         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);
             if (!attributeHeaderMap.containsKey(orgChartParentAttr)) {
                 attributeHeaderMap.put(orgChartParentAttr, orgChartParentAttr);
@@ -530,23 +283,26 @@ public class PeopleSearchServlet extends PwmServlet {
         }
 
         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());
-            return new UserSearchEngine.UserSearchResults(attributeHeaderMap,
-                    Collections.singletonMap(userIdentity, values), false);
+            return new UserSearchEngine.UserSearchResults(
+                    attributeHeaderMap,
+                    Collections.singletonMap(userIdentity, values),
+                    false
+            );
         } catch (ChaiException e) {
             LOGGER.error("unexpected error during detail lookup of '" + userIdentity + "', error: " + 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);
         }
 
@@ -566,12 +322,13 @@ public class PeopleSearchServlet extends PwmServlet {
             if (asParent) {
                 parentIdentity = userIdentity;
             } else {
-                final UserDetailBean userDetailBean = makeUserDetailRequestImpl(pwmRequest, userKey);
+                final UserDetailBean userDetailBean = makeUserDetailRequestImpl(pwmRequest, peopleSearchConfiguration, userKey);
                 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) {
             LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
             pwmRequest.respondWithError(e.getErrorInformation());
@@ -580,9 +337,7 @@ public class PeopleSearchServlet extends PwmServlet {
         }
     }
 
-
-
-    private UserTreeData makeUserTreeData(
+    private OrgChartData makeOrgChartData(
             final PwmRequest pwmRequest,
             final UserIdentity parentIdentity
 
@@ -590,58 +345,49 @@ public class PeopleSearchServlet extends PwmServlet {
             throws PwmUnrecoverableException
     {
         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);
             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 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(
-            final PwmRequest pwmRequest
+            final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration
     )
             throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
     {
@@ -657,8 +403,9 @@ public class PeopleSearchServlet extends PwmServlet {
         }
 
         try {
-            final UserDetailBean detailData = makeUserDetailRequestImpl(pwmRequest, userKey);
+            final UserDetailBean detailData = makeUserDetailRequestImpl(pwmRequest, peopleSearchConfiguration, userKey);
             pwmRequest.outputJsonResult(new RestResultBean(detailData));
+            pwmRequest.getPwmApplication().getStatisticsManager().incrementValue(Statistic.PEOPLESEARCH_DETAILS);
         } catch (PwmOperationalException e) {
             LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
             pwmRequest.respondWithError(e.getErrorInformation());
@@ -668,74 +415,70 @@ public class PeopleSearchServlet extends PwmServlet {
 
     private UserDetailBean makeUserDetailRequestImpl(
             final PwmRequest pwmRequest,
+            final PeopleSearchConfiguration peopleSearchConfiguration,
             final String userKey
     )
             throws PwmUnrecoverableException, IOException, ServletException, PwmOperationalException, ChaiUnavailableException
     {
         final Date startTime = new Date();
-        final boolean useProxy = useProxy(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession());
         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);
             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 {
-            checkIfUserIdentityViewable(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
+            checkIfUserIdentityViewable(pwmRequest, userIdentity);
         } catch (PwmOperationalException e) {
             LOGGER.error(pwmRequest.getPwmSession(), "error during detail results request while checking if requested userIdentity is within search scope: " + e.getMessage());
             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 UserDetailBean userDetailBean = new UserDetailBean();
         userDetailBean.setUserKey(userKey);
         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);
         final String photoURL = figurePhotoURL(pwmRequest, userIdentity);
         if (photoURL != null) {
             userDetailBean.setPhotoURL(photoURL);
         }
-        final String displayName = figureDisplaynameValue(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
+        final List<String> displayName = figureDisplaynames(pwmRequest, userIdentity);
         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 parentDN = searchResults.get(parentAttr);
             if (parentDN != null && !parentDN.isEmpty()) {
                 userDetailBean.setHasOrgChart(true);
                 final UserIdentity parentIdentity = new UserIdentity(parentDN,userIdentity.getLdapProfileID());
                 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());
         storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, userDetailBean);
-        StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_SEARCHES);
         return userDetailBean;
     }
 
@@ -769,7 +512,7 @@ public class PeopleSearchServlet extends PwmServlet {
         final String overrideURL = pwmApplication.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_URL_OVERRIDE);
         try {
             if (overrideURL != null && !overrideURL.isEmpty()) {
-                final MacroMachine macroMachine = getMacroMachine(pwmApplication, pwmRequest.getPwmSession(), userIdentity);
+                final MacroMachine macroMachine = getMacroMachine(pwmRequest, userIdentity);
                 return macroMachine.expandMacros(overrideURL);
             }
 
@@ -787,20 +530,38 @@ public class PeopleSearchServlet extends PwmServlet {
     }
 
     private static String figureDisplaynameValue(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             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);
     }
 
+    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)
-            throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException {
+            throws ChaiUnavailableException, PwmUnrecoverableException, IOException, ServletException
+    {
         final String userKey = pwmRequest.readParameterAsString("userKey");
         if (userKey.length() < 1) {
             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());
         try {
-            checkIfUserIdentityViewable(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), userIdentity);
+            checkIfUserIdentityViewable(pwmRequest, userIdentity);
         } 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());
             LOGGER.error(pwmRequest, errorInformation);
@@ -820,9 +581,9 @@ public class PeopleSearchServlet extends PwmServlet {
             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 {
             photoData = readPhotoDataFromLdap(pwmRequest, userIdentity);
         } catch (PwmOperationalException e) {
@@ -834,10 +595,11 @@ public class PeopleSearchServlet extends PwmServlet {
 
         OutputStream outputStream = null;
         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.write(photoData.getContents());
@@ -850,49 +612,36 @@ public class PeopleSearchServlet extends PwmServlet {
     }
 
     private static Map<String,AttributeDetailBean> convertResultMapToBeans(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final UserIdentity userIdentity,
             final List<FormConfiguration> detailForm,
             final Map<String, String> searchResults
     )
             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<>();
         for (FormConfiguration formConfiguration : detailForm) {
             if (formConfiguration.isRequired() || searchResults.containsKey(formConfiguration.getName())) {
                 AttributeDetailBean bean = new AttributeDetailBean();
                 bean.setName(formConfiguration.getName());
-                bean.setLabel(formConfiguration.getLabel(pwmSession.getSessionStateBean().getLocale()));
+                bean.setLabel(formConfiguration.getLabel(pwmRequest.getLocale()));
                 bean.setType(formConfiguration.getType());
                 if (searchAttributes.contains(formConfiguration.getName())) {
                     bean.setSearchable(true);
                 }
                 if (formConfiguration.getType() == FormConfiguration.Type.userDN) {
                     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 {
                     bean.setValue(searchResults.containsKey(formConfiguration.getName()) ? searchResults.get(
@@ -904,72 +653,67 @@ public class PeopleSearchServlet extends PwmServlet {
         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(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final UserIdentity userIdentity
     )
             throws PwmUnrecoverableException
     {
-        final boolean useProxy = useProxy(pwmApplication, pwmSession);
+        final boolean useProxy = useProxy(pwmRequest);
         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(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             final UserIdentity userIdentity
     )
             throws PwmUnrecoverableException
     {
-        final ChaiUser chaiUser = getChaiUser(pwmApplication, pwmSession, userIdentity);
+        final ChaiUser chaiUser = getChaiUser(pwmRequest, userIdentity);
         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();
-            final UserStatusReader userStatusReader = new UserStatusReader(pwmApplication, pwmSession.getLabel());
+            final UserStatusReader userStatusReader = new UserStatusReader(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());
             userStatusReader.populateUserInfoBean(userInfoBean, locale, userIdentity, chaiProvider);
         } else {
             userInfoBean = null;
         }
         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(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final PwmRequest pwmRequest,
             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, "*");
         while (filterString.contains("**")) {
             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) {
             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 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"));
         }
 
-        byte[][] photoData;
+        byte[] photoData;
         String mimeType;
         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) {
             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) {
@@ -1022,47 +767,122 @@ public class PeopleSearchServlet extends PwmServlet {
         return Collections.unmodifiableSet(new HashSet<>(searchResultForm));
     }
 
-    private static UserTreeReferenceBean userDetailToTreeReference(
+    private static OrgChartReferenceBean makeOrgChartReferenceForIdentity(
             final PwmRequest pwmRequest,
             final UserIdentity userIdentity,
             final String nextNodeAttribute
     )
             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 {
-            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);
             if (nextNodeValue != null && !nextNodeValue.isEmpty()) {
-                userTreeReferenceBean.setHasMoreNodes(true);
+                orgChartReferenceBean.setHasMoreNodes(true);
             }
         } 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
 
-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_TrialMode=%1% Trial.
+Header_TrialMode=Trial Mode.
 Header_HealthWarningsPresent=System warnings exist, click to view.
 IntruderRecordType_ADDRESS=Address
 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_Label.TokensPassed=Tokens 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_Description.PeopleSearchSearches=Number of directory searches executed using the people search module.
 Statistic_Label.PeopleSearchDetails=PeopleSearch Detail Views
 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_Description.HelpdeskPasswordSet=Number of password modifications initiated using the helpdesk module.
 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.
 Tooltip_ResetButton=Return this setting to its default value.
 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_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.

+ 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_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_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_TokenIncorrect=Incorrect code, 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;
 
 import java.security.SecureRandom;
-import java.util.Random;
 import java.util.UUID;
 
 public class PwmRandom {
 
-    private final Random internalRand = new SecureRandom();
+    private final SecureRandom internalRand = new SecureRandom();
 
     private final static PwmRandom SINGLETON = new PwmRandom();
 
@@ -57,6 +56,10 @@ public class PwmRandom {
         return internalRand.nextBoolean();
     }
 
+    public String getAlgorithm() {
+        return internalRand.getAlgorithm();
+    }
+
     public String alphaNumericString(final int 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 {
         AES("AES"),
+        AES_CHECKSUM("AES/CBC/PKCS5Padding"),
+        CONFIG("AES"),
 
         ;
 
@@ -160,6 +162,18 @@ public class SecureHelper {
     )
             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 {
             if (value == null || value.length() < 1) {
                 return "";
@@ -168,7 +182,7 @@ public class SecureHelper {
             final byte[] decoded = urlSafe
                     ? StringUtil.base64Decode(value, StringUtil.Base64Options.URL_SAFE,StringUtil.Base64Options.GZIP)
                     : StringUtil.base64Decode(value);
-            return decryptBytes(decoded, key);
+            return decryptBytes(decoded, key, blockAlgorithm);
         } catch (Exception e) {
             final String errorMsg = "unexpected error performing simple decrypt operation: " + e.getMessage();
             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
     {
         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)
-                .setApplicationPath(applicationPath)
                 .setApplicationPathType(MAIN_OPTIONS.applicationPathType)
                 .setInitLogging(false)
                 .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_OTP_PASSED                 (Type.INCREMENTOR, "RecoveryOTPPassed", 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_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_USER_LOOKUP                (Type.INCREMENTOR, "HelpdeskUserLookup", 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 List<String> configuredSettings = new ArrayList<>();
             for (final PwmSetting pwmSetting : config.nonDefaultSettings()) {
-                if (!config.isDefaultValue(pwmSetting)) {
+                if (!pwmSetting.getCategory().hasProfiles() && !config.isDefaultValue(pwmSetting)) {
                     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:display key="Button_Refresh" bundle="Admin"/>
                         </button>
-                    </div>
-                    <div style="text-align: center">
                         <form action="<pwm:url url="Administration"/>" method="post">
                             <button type="submit" class="btn" id="button-downloadUserReportCsv">
                                 <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">
                 <%=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>
-                <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>
                 </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>
                 </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>
                 </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>
                 </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>
                 </div>
                 <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 adminUser = false;
-    boolean headerVisibility = true;
     boolean configMode = false;
-    boolean showOpenCloseButtons = true;
     try {
         final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
         final PwmApplication.MODE applicationMode = pwmRequest.getPwmApplication().getApplicationMode();
@@ -40,18 +38,9 @@
             if (!new PwmURL(request).isConfigManagerURL()) {
                 if (configMode || PwmConstants.TRIAL_MODE) {
                     includeHeader = true;
-                    showOpenCloseButtons = false;
                 } else if (pwmRequest.isAuthenticated()) {
                     if (adminUser && !pwmRequest.isForcedPageView()) {
                         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>
 </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:display key="MenuItem_ConfigManager" bundle="Admin"/>
         </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:display key="MenuItem_ConfigEditor" bundle="Admin"/>
         </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:display key="MenuItem_ViewLog" bundle="Config"/>
+            &nbsp;
+            <pwm:if test="showIcons"><span class="btn-icon fa fa-external-link"></span></pwm:if>
         </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">
     <% 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) { %>
-    <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) { %>
-    <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>
-    <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">
-        <span class="fa fa-chevron-circle-up"></span>
+        <span class="fa fa-chevron-circle-right"></span>
     </div>
     <% } %>
+    <br/>
+    <%=PwmConstants.PWM_APP_NAME_VERSION%>
 </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>
 <% } %>
 <% } %>

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

@@ -735,13 +735,13 @@
 <pwm:script>
     <script type="text/javascript">
         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(){
@@ -751,6 +751,7 @@
                 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_PwUiMode'] = '<%=helpdeskProfile.readSettingAsEnum(PwmSetting.HELPDESK_SET_PASSWORD_MODE,HelpdeskUIMode.class) %>';
+                PWM_VAR['helpdesk_setting_maskPasswords'] = false;
             });
         });
     </script>

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

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

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

@@ -259,7 +259,7 @@
     </servlet>
     <servlet>
         <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-name>CaptchaServlet</servlet-name>
@@ -362,8 +362,6 @@
         <servlet-name>PeopleSearchServlet</servlet-name>
         <url-pattern>/private/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-name>CaptchaServlet</servlet-name>

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

@@ -75,22 +75,20 @@
 <div id="floatparent">
 </div>
 <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 vs = dojo.window.getBox();
 
@@ -100,8 +98,9 @@
                 var styleText = "position: absolute; ";
                 styleText += "top: " + posV + "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++;
@@ -114,7 +113,7 @@
                 }
 
                 var div = document.createElement('div');
-                div.innerHTML = passwordValue;
+                //div.innerHTML = '&nbsp;';
                 div.id = divId;
                 div.setAttribute("class",'health-' + PWM_GLOBAL['pwm-health']);
 
@@ -141,102 +140,77 @@
                     change = true;
                 }
                 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');
-            }, 1000);
+                setInterval(function(){
+                    verticalCenter('centerbody');
+                }, 1000);
 
-        });
-    }
+            });
+        }
 
-    PWM_GLOBAL['startupFunctions'].push(function(){
-        startupHealthPage();
-    });
-</script>
+        PWM_GLOBAL['startupFunctions'].push(function(){
+            startupHealthPage();
+        });
+    </script>
 </pwm:script>
 <pwm:script-ref url="/public/resources/js/admin.js"/>
 <%@ 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 {
-    margin-left: 10px;
-    margin-right: 10px;
+    border: 1px white solid;
+    padding: 5px;
     max-height: 250px;
-    margin-bottom: 15px;
+    margin: 2px 2px 15px;
     overflow-x: auto;
-    /* border-bottom: 1px solid #bfbfbf; */
 }
 
 .pane-settingValue {
@@ -426,6 +425,11 @@ table {
     white-space: nowrap;
 }
 
+.noWrapTextBox.border {
+    border: 1px solid #bfbfbf;
+    margin: 0;
+}
+
 .configStringPanel {
     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"],
         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
             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 nextFunction = function() {
         if (syntax == 'PROFILE') {
-            PWM_CFGEDIT.drawNavigationMenu();
+            PWM_MAIN.goto('ConfigEditor');
         }
         if (reload) {
             StringArrayValueHandler.init(settingKey);
@@ -571,9 +571,13 @@ FormTableHandler.redraw = function(keyName) {
     var parentDiv = 'table_setting_' + keyName;
     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)) {
         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;
         parentDivElement.appendChild(headerRow);
     }
@@ -604,16 +608,17 @@ FormTableHandler.drawRow = function(parentDiv, settingKey, iteration, value) {
         newTableRow.setAttribute("style", "border-width: 0");
 
         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 optionList = PWM_GLOBAL['formTypeOptions'];
         if ('types' in options) {
             optionList = JSON.parse(options['types']);
         }
         if (!PWM_MAIN.isEmpty(optionList)) {
+            htmlRow += '<td style="width:15px;">';
             htmlRow += '<select id="' + inputID + 'type">';
             for (var optionItem in optionList) {
                 if (optionList[optionItem] != 'userDN' || userDNtypeAllowed) {
@@ -623,26 +628,26 @@ FormTableHandler.drawRow = function(parentDiv, settingKey, iteration, value) {
                 }
             }
             htmlRow += '</select>';
+            htmlRow += '</td>';
         }
-        htmlRow += '</td>';
 
         var hideOptions = PWM_SETTINGS['settings'][settingKey]['options']['hideOptions'] == 'true';
         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)) {
             htmlRow += '<span id="' + inputID + '-moveDown" class="action-icon fa fa-chevron-down"></span>';
         }
         htmlRow += '</td>';
 
-        htmlRow += '<td>';
+        htmlRow += '<td style="width:10px">';
         if (itemCount > 1 && iteration != 0) {
             htmlRow += '<span id="' + inputID + '-moveUp" class="action-icon fa fa-chevron-up"></span>';
         }
         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;
         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 () {
             FormTableHandler.showLabelDialog(settingKey, iteration);
         });
+        PWM_MAIN.addEventHandler("icon-editLabel-" + inputID, 'click, keypress', function () {
+            FormTableHandler.showLabelDialog(settingKey, iteration);
+        });
         PWM_MAIN.addEventHandler(inputID + "optionsButton", 'click', function () {
             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);
 };
 
-PWM_CFGEDIT.clearDivElements = function(parentDiv, showLoading) {
+PWM_CFGEDIT.clearDivElements = function(parentDiv) {
     var parentDivElement = PWM_MAIN.getObject(parentDiv);
     if (parentDivElement != null) {
         if (parentDivElement.hasChildNodes()) {
@@ -196,19 +196,6 @@ PWM_CFGEDIT.clearDivElements = function(parentDiv, showLoading) {
                 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_VAR['cancelHeartbeatCheck'];
+    PWM_VAR['cancelHeartbeatCheck'] = true;
     PWM_MAIN.preloadAll(function(){
         var confirmText = PWM_CONFIG.showString('MenuDisplay_SaveConfig');
         var confirmFunction = function(){
@@ -300,46 +287,6 @@ PWM_CFGEDIT.setConfigurationPassword = function(password) {
     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) {
     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-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:'indicator-noResults',text:'No search results',position:'above'});
 
@@ -783,7 +725,7 @@ PWM_CFGEDIT.selectTemplate = function(newTemplate) {
 PWM_CFGEDIT.loadMainPageBody = function() {
 
     PWM_CFGEDIT.drawNavigationMenu();
-    var storedPreferences = PWM_CFGEDIT.readLocalStorage();
+    var storedPreferences = PWM_MAIN.readLocalStorage();
     if (storedPreferences['lastSelected']) {
         PWM_CFGEDIT.dispatchNavigationItem(storedPreferences['lastSelected']);
     } else {
@@ -863,18 +805,18 @@ PWM_CFGEDIT.drawHtmlOutlineForSetting = function(settingInfo, options) {
     var htmlBody = '<div id="outline_' + settingKey + '" class="setting_outline" style="display:none">'
         + '<div class="setting_title" id="title_' + settingKey + '">'
         + '<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']) {
-        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 id="titlePane_' + settingKey + '" class="setting_body">';
 
     if (settingInfo['description']) {
-        var prefs = PWM_CFGEDIT.readLocalStorage();
+        var prefs = PWM_MAIN.readLocalStorage();
         var expandHelp = 'helpExpanded' in prefs && settingKey in prefs['helpExpanded'];
         htmlBody += '<div class="pane-help" id="pane-help-' + settingKey + '" style="display:' + (expandHelp ? 'inherit' : 'none') + '">'
         + settingInfo['description'] + '</div>';
@@ -894,18 +836,6 @@ PWM_CFGEDIT.initSettingDisplay = function(setting, options) {
     var settingKey = setting['key'];
     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_CFGEDIT.displaySettingHelp(settingKey);
     });
@@ -1052,9 +982,9 @@ PWM_CFGEDIT.drawNavigationMenu = function() {
                     openOnClick: true,
                     id: 'navigationTree',
                     onClick: function(item){
-                        var storedPreferences = PWM_CFGEDIT.readLocalStorage();
+                        var storedPreferences = PWM_MAIN.readLocalStorage();
                         storedPreferences['lastSelected'] = item;
-                        PWM_CFGEDIT.writeLocalStorage(storedPreferences);
+                        PWM_MAIN.writeLocalStorage(storedPreferences);
                         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) {
     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) {
     console.log('toggle help for ' + settingKey);
-    var prefs = PWM_CFGEDIT.readLocalStorage();
+    var prefs = PWM_MAIN.readLocalStorage();
     prefs['helpExpanded'] = 'helpExpanded' in prefs ? prefs['helpExpanded'] : {};
     var element = PWM_MAIN.getObject('pane-help-' + settingKey);
     if (element) {
@@ -1287,35 +1194,8 @@ PWM_CFGEDIT.displaySettingHelp = function(settingKey) {
             element.style.display = 'none';
             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() {

+ 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) {
     options = options === undefined ? {} : options;
@@ -167,8 +186,13 @@ PWM_CONFIG.showHeaderHealth = function() {
                     }
                 }
                 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>';
+                } else {
+                    PWM_MAIN.removeCssClass('button-openHeader','blink');
+                    PWM_MAIN.setStyle('button-openHeader','color');
                 }
                 setTimeout(function () {
                     PWM_CONFIG.showHeaderHealth()
@@ -393,12 +417,18 @@ PWM_CONFIG.initConfigHeader = function() {
         PWM_MAIN.goto('/private/config/ConfigManager');
     });
     PWM_MAIN.addEventHandler('button-closeHeader','click',function(){
-        PWM_MAIN.closeHeaderWarningPanel();
+        PWM_CONFIG.closeHeaderWarningPanel();
     });
     PWM_MAIN.addEventHandler('button-openHeader','click',function(){
-        PWM_MAIN.openHeaderWarningPanel();
+        PWM_CONFIG.openHeaderWarningPanel();
     });
 
     PWM_CONFIG.showHeaderHealth();
+
+    var prefs = PWM_MAIN.readLocalStorage();
+    if (prefs['headerVisibility'] == 'show') {
+        PWM_CONFIG.openHeaderWarningPanel();
+    }
+
     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() {
     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) {
@@ -85,8 +70,7 @@ PWM_HELPDESK.doPasswordChange = function(password, random) {
     } else {
         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 loadFunction = function(results) {
             var bodyText = "";
@@ -100,11 +84,19 @@ PWM_HELPDESK.doPasswordChange = function(password, random) {
                 bodyText += '<br/>';
                 bodyText += results['successMessage'];
                 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/><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') {
                 bodyText += '<span style="padding-left: 10px">&nbsp;</span>';
                 bodyText += '<button class="btn" id="button-clearResponses">';
@@ -116,11 +108,20 @@ PWM_HELPDESK.doPasswordChange = function(password, random) {
                 showClose: true,
                 allowMove: true,
                 id: 'dialogPopup',
-                title: PWM_MAIN.showString('Title_ChangePassword') + ': ' + PWM_VAR['helpdesk_username'],
+                title: PWM_MAIN.showString('Title_ChangePassword') + ' - ' + PWM_VAR['helpdesk_username'],
                 text: bodyText,
                 loadFunction:function(){
                     PWM_MAIN.addEventHandler('button-continue','click',function(){ PWM_MAIN.getObject('continueForm').submit(); });
                     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() {
-    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_MAIN.stopEvent(evt);
                     var row = PWM_VAR['heldesk_search_grid'].row(evt);
                     PWM_HELPDESK.loadSearchDetails(row.data['userKey']);
                 });
@@ -380,9 +403,9 @@ PWM_HELPDESK.sendVerificationToken = function(userKey,choiceFlag) {
     var dialoagLoadFunction = function(){};
     if (choiceFlag) {
         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() {
             PWM_MAIN.addEventHandler('emailChoiceButton','click',function(){sendTokenAction('email')});
             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');
 };
 
-
-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() {
     require(["dojo/_base/array", "dojo/query", "dojo/on"], function(array, query, on){
         array.forEach(
@@ -697,7 +678,7 @@ PWM_MAIN.showErrorDialog = function(error, options) {
     }
     if (error && error['error']) {
         var code = error['errorCode'];
-        if (code == 5028 || code == 5035) {
+        if (code == 5028 || code == 5034 || code == 5035) {
             forceReload = true;
         }
         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();
 

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

@@ -79,12 +79,33 @@ PWM_PS.processPeopleSearch = function() {
 };
 
 PWM_PS.convertDetailResultToHtml = function(data) {
+    var blankSrc = PWM_MAIN.addPwmFormIDtoURL(PWM_GLOBAL['url-resources'] + '/UserPhoto.png');
     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 += '<table class="peopleSearch-userDetails">';
     for (var iter in data['detail']) {
@@ -127,6 +148,19 @@ PWM_PS.convertDetailResultToHtml = 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']) {
         (function(iterCount){
             var attributeData = data['detail'][iterCount];
@@ -167,7 +201,6 @@ PWM_PS.showUserDetail = function(userKey) {
     };
     PWM_MAIN.showWaitDialog({
         loadFunction:function(){
-            PWM_VAR['detailInProgress'] = false;
             var url = "PeopleSearch?processAction=detail";
             var loadFunction = function(data) {
                 if (data['error'] == true) {
@@ -176,34 +209,15 @@ PWM_PS.showUserDetail = function(userKey) {
                     return;
                 }
                 var htmlBody = PWM_PS.convertDetailResultToHtml(data['data']);
-                PWM_MAIN.closeWaitDialog();
                 PWM_MAIN.showDialog({
-                    title:data['data']['displayName'],
+                    title:'Details',
                     allowMove:true,
+                    showOk:false,
                     text:htmlBody,
                     showClose:true,
                     loadFunction:function(){
-                        var photoURL = data['data']['photoURL'];
-                        if (photoURL) {
-                            PWM_PS.loadPicture(PWM_MAIN.getObject("userPhotoParentDiv"),photoURL);
-                        }
-
                         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 = '';
-        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>';
         }
-        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;
-        for (var iter in parentReference['displayNames']) {
+        for (var iter in userReference['displayNames']) {
             (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++;
             })(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;
     };
 
-
     var htmlOutput = '';
+    htmlOutput += '<div class="panel-orgChart">'; //1
     if ('parent' in data) {
         htmlOutput += makePanel(data['parent'], 'parent', 'up');
     }
     if ('siblings' in data) {
+        htmlOutput += '<div class="panel-orgChart-siblings">'; //2
         for (var iter in data['siblings']) {
             (function(iterCount){
                 var siblingReference = data['siblings'][iterCount];
                 htmlOutput += makePanel(siblingReference, 'sibling', 'down');
             })(iter);
         }
+        htmlOutput += '</div>'; //2
     }
+    htmlOutput += '<div class="panel-orgChart-footer"></div>';
+    htmlOutput += '</div>'; //1
     return htmlOutput;
 };
 
-PWM_PS.applyUserTreeDataToOrgChartEvents = function(data) {
+PWM_PS.applyOrgChartDataToOrgChartEvents = function(data) {
     var applyEventsToReference = function(reference,asParent) {
+        PWM_MAIN.addEventHandler('panel-orgChart-person-' + reference['userKey'],'click',function(){
+            PWM_PS.showUserDetail(reference['userKey']);
+        });
         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_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) {
@@ -292,22 +320,24 @@ PWM_PS.showOrgChartView = function(userKey, asParent) {
     };
     PWM_MAIN.showWaitDialog({
         loadFunction:function(){
-            var url = "PeopleSearch?processAction=userTreeData";
+            var url = "PeopleSearch?processAction=orgChartData";
             var loadFunction = function(data) {
                 if (data['error'] == true) {
                     PWM_MAIN.closeWaitDialog();
                     PWM_MAIN.showErrorDialog(data);
                     return;
                 }
-                var htmlBody = PWM_PS.convertUserTreeDataToOrgChartHtml(data['data']);
+                var htmlBody = PWM_PS.convertOrgChartDataToOrgChartHtml(data['data']);
                 PWM_MAIN.closeWaitDialog();
                 PWM_MAIN.showDialog({
                     title:PWM_MAIN.showString('Title_OrgChart'),
                     allowMove:true,
+                    showOk:false,
                     text:htmlBody,
+                    dialogClass:'orgChart',
                     showClose:true,
                     loadFunction:function(){
-                        PWM_PS.applyUserTreeDataToOrgChartEvents(data['data']);
+                        PWM_PS.applyOrgChartDataToOrgChartEvents(data['data']);
                         setTimeout(function() {
                             try {PWM_MAIN.getObject('dialog_ok_button').focus(); } catch (e) { /*noop */}
                         },1000);
@@ -339,15 +369,11 @@ PWM_PS.makeSearchGrid = function(nextFunction) {
             }
 
             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});
@@ -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){
         var image = new Image();
-        image.setAttribute('id',"userPhotoImage");
-        image.setAttribute('style',PWM_VAR['photo_style_attribute']);
+        image.setAttribute('class',cssClass);
         on(image,"load",function(){
             if (parentDiv) {
                 while (parentDiv.firstChild) {

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

@@ -26,6 +26,7 @@ html, body {
     height: 100%;
     margin: 0;
     width: 100%;
+    min-height: inherit;
 }
 
 /* main content section, all content should be inside a centerbody div */
@@ -99,3 +100,28 @@ progress .wait {
     width: auto;
     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;
     height: 100%;
     margin: 0;
+    min-height: 350px;
 }
 
 a {
@@ -254,27 +255,6 @@ input[type=password]::-ms-reveal{display: none;}
      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 */
 div.progress-container {
@@ -365,17 +345,45 @@ div.progress-container > div {
 }
 
 #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%;
-    background-color: #aaaaaa;
+    margin-bottom: 15px;
+}
+
+.header-warning-button {
     text-align: center;
+    width: 100%;
+    padding-bottom: 10px;
+    display: block;
+    color: black;
+    cursor: pointer;
 }
 
+
 .header-error {
     text-align: center;
     white-space: normal;
     max-width: 600px;
     padding-left: 6px;
     padding-right: 6px;
+    margin-top: 10px;
     margin-left: auto;
     margin-right: auto;
     position: relative;
@@ -457,13 +465,6 @@ div.progress-container > div {
 }
 
 
-#dialogPopup_underlay {
-    background-color: #222222;
-}
-
-#idleDialog_underlay {
-    background-color: #111111;
-}
 
 .agreementText {
     border: 0;
@@ -524,34 +525,11 @@ img.qrcodeimage {
     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 {
     min-height: 400px;
     height: 60vh;
 }
 
-.peoplesearch-input-username {
-    width:400px;
-}
-
 .checkboxWrapper {
     border: 0;
     padding: 5px;
@@ -572,11 +550,11 @@ select {
 
 #button-openHeader {
     position: absolute;
-    top: 3px;
-    right: 4px;
+    top: 10px;
+    right: 10px;
     z-index: 100;
     cursor: pointer;
-    color: red;
+    color: #ffcd59;
 }
 
 #button-closeHeader {
@@ -632,56 +610,19 @@ input[type=search] {
     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]) {
     width: 100%;
     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; } }
 @-moz-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 {
     width:80%;
     margin-left: 10%;
@@ -693,29 +634,83 @@ progress:not([value]) {
     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 */
+