Jason Rivard 4 лет назад
Родитель
Сommit
0e85726d54
100 измененных файлов с 1711 добавлено и 1345 удалено
  1. 2 8
      build/checkstyle-import.xml
  2. 1 1
      client/pom.xml
  3. 2 2
      data-service/pom.xml
  4. 1 1
      docker/pom.xml
  5. 1 1
      onejar/pom.xml
  6. 12 12
      pom.xml
  7. 4 33
      server/pom.xml
  8. 6 0
      server/src/main/java/password/pwm/AppProperty.java
  9. 2 2
      server/src/main/java/password/pwm/PwmAboutProperty.java
  10. 106 75
      server/src/main/java/password/pwm/PwmApplication.java
  11. 1 1
      server/src/main/java/password/pwm/PwmConstants.java
  12. 19 17
      server/src/main/java/password/pwm/PwmDomain.java
  13. 0 1
      server/src/main/java/password/pwm/bean/DomainID.java
  14. 13 8
      server/src/main/java/password/pwm/bean/SessionLabel.java
  15. 9 9
      server/src/main/java/password/pwm/bean/UserIdentity.java
  16. 7 40
      server/src/main/java/password/pwm/config/AppConfig.java
  17. 0 86
      server/src/main/java/password/pwm/config/ConfigurationUtil.java
  18. 56 60
      server/src/main/java/password/pwm/config/DomainConfig.java
  19. 41 29
      server/src/main/java/password/pwm/config/PwmSetting.java
  20. 6 4
      server/src/main/java/password/pwm/config/PwmSettingCategory.java
  21. 5 0
      server/src/main/java/password/pwm/config/PwmSettingFlag.java
  22. 5 0
      server/src/main/java/password/pwm/config/PwmSettingMetaDataReader.java
  23. 53 0
      server/src/main/java/password/pwm/config/StoredSettingReader.java
  24. 18 6
      server/src/main/java/password/pwm/config/function/UserMatchViewerFunction.java
  25. 20 11
      server/src/main/java/password/pwm/config/profile/LdapProfile.java
  26. 13 5
      server/src/main/java/password/pwm/config/profile/NewUserProfile.java
  27. 1 1
      server/src/main/java/password/pwm/config/profile/PwmPasswordPolicy.java
  28. 2 2
      server/src/main/java/password/pwm/config/stored/ConfigurationCleaner.java
  29. 3 2
      server/src/main/java/password/pwm/config/stored/ConfigurationReader.java
  30. 4 2
      server/src/main/java/password/pwm/config/stored/StoredConfigXmlSerializer.java
  31. 11 6
      server/src/main/java/password/pwm/config/stored/StoredConfigurationUtil.java
  32. 17 5
      server/src/main/java/password/pwm/config/value/AbstractValue.java
  33. 2 1
      server/src/main/java/password/pwm/config/value/FileValue.java
  34. 4 20
      server/src/main/java/password/pwm/config/value/ValueTypeConverter.java
  35. 11 10
      server/src/main/java/password/pwm/error/PwmError.java
  36. 15 12
      server/src/main/java/password/pwm/health/ApplianceStatusChecker.java
  37. 3 8
      server/src/main/java/password/pwm/health/CertificateChecker.java
  38. 243 118
      server/src/main/java/password/pwm/health/ConfigurationChecker.java
  39. 4 3
      server/src/main/java/password/pwm/health/DatabaseStatusChecker.java
  40. 5 4
      server/src/main/java/password/pwm/health/HealthRecord.java
  41. 40 44
      server/src/main/java/password/pwm/health/HealthService.java
  42. 10 1
      server/src/main/java/password/pwm/health/HealthSupplier.java
  43. 3 1
      server/src/main/java/password/pwm/health/JavaChecker.java
  44. 69 55
      server/src/main/java/password/pwm/health/LDAPHealthChecker.java
  45. 2 2
      server/src/main/java/password/pwm/health/LocalDBHealthChecker.java
  46. 3 35
      server/src/main/java/password/pwm/http/HttpEventManager.java
  47. 1 0
      server/src/main/java/password/pwm/http/HttpHeader.java
  48. 3 7
      server/src/main/java/password/pwm/http/IdleTimeoutCalculator.java
  49. 14 22
      server/src/main/java/password/pwm/http/PwmCookiePath.java
  50. 3 3
      server/src/main/java/password/pwm/http/PwmHttpRequestWrapper.java
  51. 8 9
      server/src/main/java/password/pwm/http/PwmRequest.java
  52. 4 8
      server/src/main/java/password/pwm/http/PwmSession.java
  53. 2 2
      server/src/main/java/password/pwm/http/auth/BasicFilterAuthenticationProvider.java
  54. 2 0
      server/src/main/java/password/pwm/http/bean/DisplayElement.java
  55. 1 1
      server/src/main/java/password/pwm/http/bean/SetupOtpBean.java
  56. 213 0
      server/src/main/java/password/pwm/http/filter/DomainInitFilter.java
  57. 2 2
      server/src/main/java/password/pwm/http/filter/ObsoleteUrlFilter.java
  58. 24 82
      server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java
  59. 3 5
      server/src/main/java/password/pwm/http/filter/SessionFilter.java
  60. 9 24
      server/src/main/java/password/pwm/http/servlet/AbstractPwmServlet.java
  61. 5 5
      server/src/main/java/password/pwm/http/servlet/ClientApiServlet.java
  62. 1 1
      server/src/main/java/password/pwm/http/servlet/ControlledPwmServlet.java
  63. 6 6
      server/src/main/java/password/pwm/http/servlet/DeleteAccountServlet.java
  64. 11 10
      server/src/main/java/password/pwm/http/servlet/ForgottenUsernameServlet.java
  65. 6 4
      server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java
  66. 9 10
      server/src/main/java/password/pwm/http/servlet/SetupOtpServlet.java
  67. 9 5
      server/src/main/java/password/pwm/http/servlet/SetupResponsesServlet.java
  68. 4 4
      server/src/main/java/password/pwm/http/servlet/ShortcutServlet.java
  69. 11 9
      server/src/main/java/password/pwm/http/servlet/activation/ActivateUserServlet.java
  70. 6 5
      server/src/main/java/password/pwm/http/servlet/activation/ActivateUserUtils.java
  71. 10 10
      server/src/main/java/password/pwm/http/servlet/admin/AdminServlet.java
  72. 67 36
      server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java
  73. 1 1
      server/src/main/java/password/pwm/http/servlet/admin/UserDebugDataReader.java
  74. 39 26
      server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java
  75. 2 1
      server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServletUtil.java
  76. 58 50
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java
  77. 21 45
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServletUtils.java
  78. 1 1
      server/src/main/java/password/pwm/http/servlet/configeditor/DomainStateReader.java
  79. 18 5
      server/src/main/java/password/pwm/http/servlet/configeditor/data/NavTreeDataMaker.java
  80. 30 0
      server/src/main/java/password/pwm/http/servlet/configeditor/data/NavTreeSettings.java
  81. 23 7
      server/src/main/java/password/pwm/http/servlet/configeditor/data/SettingDataMaker.java
  82. 16 10
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java
  83. 3 3
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideUtils.java
  84. 4 4
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java
  85. 28 28
      server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java
  86. 2 1
      server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordStageProcessor.java
  87. 21 19
      server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordStateMachine.java
  88. 10 7
      server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordUtil.java
  89. 61 48
      server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java
  90. 10 9
      server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServletUtil.java
  91. 8 3
      server/src/main/java/password/pwm/http/servlet/newuser/NewUserFormUtils.java
  92. 6 6
      server/src/main/java/password/pwm/http/servlet/newuser/NewUserServlet.java
  93. 9 8
      server/src/main/java/password/pwm/http/servlet/newuser/NewUserUtils.java
  94. 4 1
      server/src/main/java/password/pwm/http/servlet/oauth/OAuthConsumerServlet.java
  95. 11 11
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchDataReader.java
  96. 7 14
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchService.java
  97. 4 4
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchServlet.java
  98. 1 1
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PhotoDataReader.java
  99. 2 2
      server/src/main/java/password/pwm/http/servlet/resource/ResourceFileServlet.java
  100. 22 16
      server/src/main/java/password/pwm/http/servlet/resource/ResourceServletConfiguration.java

+ 2 - 8
build/checkstyle-import.xml

@@ -120,22 +120,16 @@
     </subpackage>
     </subpackage>
 
 
     <!-- nmas -->
     <!-- nmas -->
-    <subpackage name="util.operations.cr">
+    <subpackage name="svc.cr">
         <allow pkg="com.novell.ldap"/>
         <allow pkg="com.novell.ldap"/>
         <allow pkg="com.novell.security.nmas"/>
         <allow pkg="com.novell.security.nmas"/>
     </subpackage>
     </subpackage>
 
 
     <!-- database -->
     <!-- database -->
-    <subpackage name="util.db">
+    <subpackage name="svc.db">
         <allow pkg="java.sql"/>
         <allow pkg="java.sql"/>
     </subpackage>
     </subpackage>
 
 
-    <subpackage name="util.java">
-        <allow class="net.iharder.Base64"/>
-        <allow pkg="org.apache.commons.codec"/>
-        <allow pkg="org.apache.commons.lang3"/>
-    </subpackage>
-
     <subpackage name="util.localdb">
     <subpackage name="util.localdb">
         <allow pkg="java.sql"/>
         <allow pkg="java.sql"/>
     </subpackage>
     </subpackage>

+ 1 - 1
client/pom.xml

@@ -17,7 +17,7 @@
         <npm.version>6.14.8</npm.version>
         <npm.version>6.14.8</npm.version>
     </properties>
     </properties>
 
 
-    <name>PWM Password Self Service: Angular Client JAR</name>
+    <name>PWM Password Self Service: Angular Client WebJAR</name>
 
 
     <profiles>
     <profiles>
         <profile>
         <profile>

+ 2 - 2
data-service/pom.xml

@@ -120,7 +120,7 @@
         <dependency>
         <dependency>
             <groupId>commons-net</groupId>
             <groupId>commons-net</groupId>
             <artifactId>commons-net</artifactId>
             <artifactId>commons-net</artifactId>
-            <version>3.7.2</version>
+            <version>3.8.0</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <groupId>org.apache.commons</groupId>
@@ -130,7 +130,7 @@
         <dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
             <artifactId>commons-lang3</artifactId>
-            <version>3.11</version>
+            <version>3.12.0</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>com.sun.mail</groupId>
             <groupId>com.sun.mail</groupId>

+ 1 - 1
docker/pom.xml

@@ -33,7 +33,7 @@
             <plugin>
             <plugin>
                 <groupId>com.google.cloud.tools</groupId>
                 <groupId>com.google.cloud.tools</groupId>
                 <artifactId>jib-maven-plugin</artifactId>
                 <artifactId>jib-maven-plugin</artifactId>
-                <version>2.7.0</version>
+                <version>2.8.0</version>
                 <executions>
                 <executions>
                     <execution>
                     <execution>
                         <id>make-docker-image</id>
                         <id>make-docker-image</id>

+ 1 - 1
onejar/pom.xml

@@ -16,7 +16,7 @@
     <name>PWM Password Self Service: Executable Server JAR</name>
     <name>PWM Password Self Service: Executable Server JAR</name>
 
 
     <properties>
     <properties>
-        <tomcat.version>9.0.39</tomcat.version>
+        <tomcat.version>9.0.44</tomcat.version>
     </properties>
     </properties>
 
 
     <build>
     <build>

+ 12 - 12
pom.xml

@@ -43,7 +43,7 @@
         <skipTests>false</skipTests>
         <skipTests>false</skipTests>
 
 
         <!-- git.commit.time is populated via git-commit-id-plugin and results in a (hopefully) reproducible maven build -->
         <!-- git.commit.time is populated via git-commit-id-plugin and results in a (hopefully) reproducible maven build -->
-        <project.build.outputTimestamp>${git.commit.time}</project.build.outputTimestamp>
+        <project.build.outreportservreportservputTimestamp>${git.commit.time}</project.build.outreportservreportservputTimestamp>
     </properties>
     </properties>
 
 
     <modules>
     <modules>
@@ -232,12 +232,12 @@
             <plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
-                <version>3.1.1</version>
+                <version>3.1.2</version>
                 <dependencies>
                 <dependencies>
                     <dependency>
                     <dependency>
                         <groupId>com.puppycrawl.tools</groupId>
                         <groupId>com.puppycrawl.tools</groupId>
                         <artifactId>checkstyle</artifactId>
                         <artifactId>checkstyle</artifactId>
-                        <version>8.39</version>
+                        <version>8.41</version>
                     </dependency>
                     </dependency>
                 </dependencies>
                 </dependencies>
                 <executions>
                 <executions>
@@ -321,7 +321,7 @@
                     <dependency>
                     <dependency>
                         <groupId>com.github.spotbugs</groupId>
                         <groupId>com.github.spotbugs</groupId>
                         <artifactId>spotbugs</artifactId>
                         <artifactId>spotbugs</artifactId>
-                        <version>4.2.0</version>
+                        <version>4.2.2</version>
                     </dependency>
                     </dependency>
                 </dependencies>
                 </dependencies>
                 <configuration>
                 <configuration>
@@ -365,7 +365,7 @@
             <plugin>
             <plugin>
                 <groupId>org.owasp</groupId>
                 <groupId>org.owasp</groupId>
                 <artifactId>dependency-check-maven</artifactId>
                 <artifactId>dependency-check-maven</artifactId>
-                <version>6.0.5</version>
+                <version>6.1.2</version>
                 <executions>
                 <executions>
                     <execution>
                     <execution>
                         <goals>
                         <goals>
@@ -382,13 +382,13 @@
         <dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <artifactId>lombok</artifactId>
-            <version>1.18.16</version>
+            <version>1.18.20</version>
             <scope>provided</scope>
             <scope>provided</scope>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>com.github.spotbugs</groupId>
             <groupId>com.github.spotbugs</groupId>
             <artifactId>spotbugs-annotations</artifactId>
             <artifactId>spotbugs-annotations</artifactId>
-            <version>4.2.0</version>
+            <version>4.2.2</version>
             <scope>provided</scope>
             <scope>provided</scope>
         </dependency>
         </dependency>
 
 
@@ -396,19 +396,19 @@
         <dependency>
         <dependency>
             <groupId>junit</groupId>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <artifactId>junit</artifactId>
-            <version>4.13.1</version>
+            <version>4.13.2</version>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>org.mockito</groupId>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
             <artifactId>mockito-core</artifactId>
-            <version>3.7.0</version>
+            <version>3.8.0</version>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>org.assertj</groupId>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <artifactId>assertj-core</artifactId>
-            <version>3.18.1</version>
+            <version>3.19.0</version>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
@@ -426,13 +426,13 @@
         <dependency>
         <dependency>
             <groupId>org.openjdk.jmh</groupId>
             <groupId>org.openjdk.jmh</groupId>
             <artifactId>jmh-core</artifactId>
             <artifactId>jmh-core</artifactId>
-            <version>1.27</version>
+            <version>1.28</version>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>org.openjdk.jmh</groupId>
             <groupId>org.openjdk.jmh</groupId>
             <artifactId>jmh-generator-annprocess</artifactId>
             <artifactId>jmh-generator-annprocess</artifactId>
-            <version>1.27</version>
+            <version>1.28</version>
             <scope>test</scope>
             <scope>test</scope>
         </dependency>
         </dependency>
     </dependencies>
     </dependencies>

+ 4 - 33
server/pom.xml

@@ -38,35 +38,6 @@
     </profiles>
     </profiles>
     <build>
     <build>
         <plugins>
         <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.8.1</version>
-                <configuration>
-                    <source>${maven.compiler.source}</source>
-                    <target>${maven.compiler.target}</target>
-                    <release>${maven.compiler.release}</release>
-                    <showWarnings>true</showWarnings>
-                    <compilerArgs>
-                        <arg>-Xmaxwarns</arg>
-                        <arg>9999</arg>
-                        <arg>-XDcompilePolicy=simple</arg>
-                        <arg>-Xplugin:ErrorProne</arg>
-                    </compilerArgs>
-                    <annotationProcessorPaths>
-                        <path>
-                            <groupId>com.google.errorprone</groupId>
-                            <artifactId>error_prone_core</artifactId>
-                            <version>2.4.0</version>
-                        </path>
-                        <path>
-                            <groupId>org.projectlombok</groupId>
-                            <artifactId>lombok</artifactId>
-                            <version>1.18.16</version>
-                        </path>
-                    </annotationProcessorPaths>
-                </configuration>
-            </plugin>
             <plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <artifactId>maven-surefire-plugin</artifactId>
@@ -232,7 +203,7 @@
         <dependency>
         <dependency>
             <groupId>commons-net</groupId>
             <groupId>commons-net</groupId>
             <artifactId>commons-net</artifactId>
             <artifactId>commons-net</artifactId>
-            <version>3.7.2</version>
+            <version>3.8.0</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <groupId>org.apache.commons</groupId>
@@ -252,7 +223,7 @@
         <dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
             <artifactId>commons-lang3</artifactId>
-            <version>3.11</version>
+            <version>3.12.0</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>commons-validator</groupId>
             <groupId>commons-validator</groupId>
@@ -338,12 +309,12 @@
         <dependency>
         <dependency>
             <groupId>com.github.ben-manes.caffeine</groupId>
             <groupId>com.github.ben-manes.caffeine</groupId>
             <artifactId>caffeine</artifactId>
             <artifactId>caffeine</artifactId>
-            <version>2.8.8</version>
+            <version>3.0.0</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>com.nulab-inc</groupId>
             <groupId>com.nulab-inc</groupId>
             <artifactId>zxcvbn</artifactId>
             <artifactId>zxcvbn</artifactId>
-            <version>1.3.1</version>
+            <version>1.4.0</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>com.github.ziplet</groupId>
             <groupId>com.github.ziplet</groupId>

+ 6 - 0
server/src/main/java/password/pwm/AppProperty.java

@@ -131,6 +131,7 @@ public enum AppProperty
     HTTP_CLIENT_CONNECT_TIMEOUT_MS                  ( "http.client.connectTimeoutMs" ),
     HTTP_CLIENT_CONNECT_TIMEOUT_MS                  ( "http.client.connectTimeoutMs" ),
     HTTP_CLIENT_REQUEST_TIMEOUT_MS                  ( "http.client.requestTimeoutMs" ),
     HTTP_CLIENT_REQUEST_TIMEOUT_MS                  ( "http.client.requestTimeoutMs" ),
     HTTP_CLIENT_RESPONSE_MAX_SIZE                   ( "http.client.response.maxSize" ),
     HTTP_CLIENT_RESPONSE_MAX_SIZE                   ( "http.client.response.maxSize" ),
+    HTTP_CLIENT_IMPLEMENTATION                      ( "http.client.implementation" ),
     HTTP_CLIENT_ENABLE_HOSTNAME_VERIFICATION        ( "http.client.enableHostnameVerification" ),
     HTTP_CLIENT_ENABLE_HOSTNAME_VERIFICATION        ( "http.client.enableHostnameVerification" ),
     HTTP_CLIENT_PROMISCUOUS_WORDLIST_ENABLE         ( "http.client.promiscuous.wordlist.enable" ),
     HTTP_CLIENT_PROMISCUOUS_WORDLIST_ENABLE         ( "http.client.promiscuous.wordlist.enable" ),
     HTTP_ENABLE_GZIP                                ( "http.gzip.enable" ),
     HTTP_ENABLE_GZIP                                ( "http.gzip.enable" ),
@@ -138,6 +139,7 @@ public enum AppProperty
     HTTP_HEADER_SERVER                              ( "http.header.server" ),
     HTTP_HEADER_SERVER                              ( "http.header.server" ),
     HTTP_HEADER_SEND_CONTENT_LANGUAGE               ( "http.header.sendContentLanguage" ),
     HTTP_HEADER_SEND_CONTENT_LANGUAGE               ( "http.header.sendContentLanguage" ),
     HTTP_HEADER_SEND_XAMB                           ( "http.header.sendXAmb" ),
     HTTP_HEADER_SEND_XAMB                           ( "http.header.sendXAmb" ),
+    HTTP_HEADER_SEND_XDOMAIN                        ( "http.header.sendXDomain" ),
     HTTP_HEADER_SEND_XINSTANCE                      ( "http.header.sendXInstance" ),
     HTTP_HEADER_SEND_XINSTANCE                      ( "http.header.sendXInstance" ),
     HTTP_HEADER_SEND_XNOISE                         ( "http.header.sendXNoise" ),
     HTTP_HEADER_SEND_XNOISE                         ( "http.header.sendXNoise" ),
     HTTP_HEADER_SEND_XSESSIONID                     ( "http.header.sendXSessionID" ),
     HTTP_HEADER_SEND_XSESSIONID                     ( "http.header.sendXSessionID" ),
@@ -145,6 +147,7 @@ public enum AppProperty
     HTTP_HEADER_SEND_XCONTENTTYPEOPTIONS            ( "http.header.sendXContentTypeOptions" ),
     HTTP_HEADER_SEND_XCONTENTTYPEOPTIONS            ( "http.header.sendXContentTypeOptions" ),
     HTTP_HEADER_SEND_XXSSPROTECTION                 ( "http.header.sendXXSSProtection" ),
     HTTP_HEADER_SEND_XXSSPROTECTION                 ( "http.header.sendXXSSProtection" ),
     HTTP_HEADER_NOISE_LENGTH                        ( "http.header.noise.length" ),
     HTTP_HEADER_NOISE_LENGTH                        ( "http.header.noise.length" ),
+    HTTP_HEADER_CACHE_CONTROL                       ( "http.header.cacheControl" ),
     HTTP_HEADER_CSP_NONCE_BYTES                     ( "http.header.csp.nonce.bytes" ),
     HTTP_HEADER_CSP_NONCE_BYTES                     ( "http.header.csp.nonce.bytes" ),
     HTTP_PARAM_NAME_FORWARD_URL                     ( "http.parameter.forward" ),
     HTTP_PARAM_NAME_FORWARD_URL                     ( "http.parameter.forward" ),
     HTTP_PARAM_NAME_LOGOUT_URL                      ( "http.parameter.logout" ),
     HTTP_PARAM_NAME_LOGOUT_URL                      ( "http.parameter.logout" ),
@@ -191,6 +194,7 @@ public enum AppProperty
     INTRUDER_MAX_DELAY_PENALTY_MS                   ( "intruder.maximumDelayPenaltyMS" ),
     INTRUDER_MAX_DELAY_PENALTY_MS                   ( "intruder.maximumDelayPenaltyMS" ),
     INTRUDER_DELAY_PER_COUNT_MS                     ( "intruder.delayPerCountMS" ),
     INTRUDER_DELAY_PER_COUNT_MS                     ( "intruder.delayPerCountMS" ),
     INTRUDER_DELAY_MAX_JITTER_MS                    ( "intruder.delayMaxJitterMS" ),
     INTRUDER_DELAY_MAX_JITTER_MS                    ( "intruder.delayMaxJitterMS" ),
+    INTRUDER_STORAGE_HASH_ALGORITHM                 ( "intruder.storageHashAlgorithm" ),
     HEALTHCHECK_ENABLED                             ( "healthCheck.enabled" ),
     HEALTHCHECK_ENABLED                             ( "healthCheck.enabled" ),
     HEALTHCHECK_NOMINAL_CHECK_INTERVAL              ( "healthCheck.nominalCheckIntervalSeconds" ),
     HEALTHCHECK_NOMINAL_CHECK_INTERVAL              ( "healthCheck.nominalCheckIntervalSeconds" ),
     HEALTHCHECK_MIN_CHECK_INTERVAL                  ( "healthCheck.minimumCheckIntervalSeconds" ),
     HEALTHCHECK_MIN_CHECK_INTERVAL                  ( "healthCheck.minimumCheckIntervalSeconds" ),
@@ -373,6 +377,8 @@ public enum AppProperty
     WORDLIST_BUILTIN_PATH                           ( "wordlist.builtin.path" ),
     WORDLIST_BUILTIN_PATH                           ( "wordlist.builtin.path" ),
     WORDLIST_CHAR_LENGTH_MAX                        ( "wordlist.maxCharLength" ),
     WORDLIST_CHAR_LENGTH_MAX                        ( "wordlist.maxCharLength" ),
     WORDLIST_CHAR_LENGTH_MIN                        ( "wordlist.minCharLength" ),
     WORDLIST_CHAR_LENGTH_MIN                        ( "wordlist.minCharLength" ),
+    WORDLIST_BUCKET_CHECK_WARNING_TIMEOUT_MS        ( "wordlist.bucketCheckLogWarningTimeoutMs" ),
+    WORDLIST_WARMUP_COUNT                           ( "wordlist.warmup.count" ),
     WORDLIST_IMPORT_AUTO_IMPORT_RECHECK_SECONDS     ( "wordlist.import.autoImportRecheckSeconds" ),
     WORDLIST_IMPORT_AUTO_IMPORT_RECHECK_SECONDS     ( "wordlist.import.autoImportRecheckSeconds" ),
     WORDLIST_IMPORT_DURATION_GOAL_MS                ( "wordlist.import.durationGoalMS" ),
     WORDLIST_IMPORT_DURATION_GOAL_MS                ( "wordlist.import.durationGoalMS" ),
     WORDLIST_IMPORT_MIN_FREE_SPACE                  ( "wordlist.import.minFreeSpace" ),
     WORDLIST_IMPORT_MIN_FREE_SPACE                  ( "wordlist.import.minFreeSpace" ),

+ 2 - 2
server/src/main/java/password/pwm/PwmAboutProperty.java

@@ -23,7 +23,7 @@ package password.pwm;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.Display;
 import password.pwm.ldap.LdapConnectionService;
 import password.pwm.ldap.LdapConnectionService;
-import password.pwm.util.db.DatabaseService;
+import password.pwm.svc.db.DatabaseService;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
@@ -63,7 +63,7 @@ public enum PwmAboutProperty
     app_emailQueueOldestTime( null, pwmApplication -> format( pwmApplication.getEmailQueue().eldestItem() ) ),
     app_emailQueueOldestTime( null, pwmApplication -> format( pwmApplication.getEmailQueue().eldestItem() ) ),
     app_smsQueueSize( null, pwmApplication -> Integer.toString( pwmApplication.getSmsQueue().queueSize() ) ),
     app_smsQueueSize( null, pwmApplication -> Integer.toString( pwmApplication.getSmsQueue().queueSize() ) ),
     app_smsQueueOldestTime( null, pwmApplication -> format( pwmApplication.getSmsQueue().eldestItem() ) ),
     app_smsQueueOldestTime( null, pwmApplication -> format( pwmApplication.getSmsQueue().eldestItem() ) ),
-    app_syslogQueueSize( null, pwmApplication -> Integer.toString( pwmApplication.getAuditManager().syslogQueueSize() ) ),
+    app_syslogQueueSize( null, pwmApplication -> Integer.toString( pwmApplication.getAuditService().syslogQueueSize() ) ),
     app_localDbLogSize( null, pwmApplication -> Integer.toString( pwmApplication.getLocalDBLogger().getStoredEventCount() ) ),
     app_localDbLogSize( null, pwmApplication -> Integer.toString( pwmApplication.getLocalDBLogger().getStoredEventCount() ) ),
     app_localDbLogOldestTime( null, pwmApplication -> format( pwmApplication.getLocalDBLogger().getTailDate() ) ),
     app_localDbLogOldestTime( null, pwmApplication -> format( pwmApplication.getLocalDBLogger().getTailDate() ) ),
     app_localDbStorageSize( null, pwmApplication -> StringUtil.formatDiskSize( FileSystemUtility.getFileDirectorySize( pwmApplication.getLocalDB().getFileLocation() ) ) ),
     app_localDbStorageSize( null, pwmApplication -> StringUtil.formatDiskSize( FileSystemUtility.getFileDirectorySize( pwmApplication.getLocalDB().getFileLocation() ) ) ),

+ 106 - 75
server/src/main/java/password/pwm/PwmApplication.java

@@ -41,23 +41,25 @@ import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmServiceEnum;
 import password.pwm.svc.PwmServiceEnum;
 import password.pwm.svc.PwmServiceManager;
 import password.pwm.svc.PwmServiceManager;
 import password.pwm.svc.cache.CacheService;
 import password.pwm.svc.cache.CacheService;
+import password.pwm.svc.db.DatabaseAccessor;
+import password.pwm.svc.db.DatabaseService;
 import password.pwm.svc.email.EmailService;
 import password.pwm.svc.email.EmailService;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
-import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditService;
 import password.pwm.svc.event.AuditService;
-import password.pwm.svc.event.SystemAuditRecord;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.httpclient.HttpClientService;
 import password.pwm.svc.httpclient.HttpClientService;
-import password.pwm.svc.intruder.IntruderService;
 import password.pwm.svc.intruder.IntruderRecordType;
 import password.pwm.svc.intruder.IntruderRecordType;
+import password.pwm.svc.intruder.IntruderSystemService;
 import password.pwm.svc.node.NodeService;
 import password.pwm.svc.node.NodeService;
-import password.pwm.svc.pwnotify.PwNotifyService;
 import password.pwm.svc.report.ReportService;
 import password.pwm.svc.report.ReportService;
 import password.pwm.svc.secure.SystemSecureService;
 import password.pwm.svc.secure.SystemSecureService;
 import password.pwm.svc.sessiontrack.SessionTrackService;
 import password.pwm.svc.sessiontrack.SessionTrackService;
 import password.pwm.svc.sessiontrack.UserAgentUtils;
 import password.pwm.svc.sessiontrack.UserAgentUtils;
 import password.pwm.svc.shorturl.UrlShortenerService;
 import password.pwm.svc.shorturl.UrlShortenerService;
+import password.pwm.svc.sms.SmsQueueService;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
+import password.pwm.svc.stats.StatisticsService;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.wordlist.SeedlistService;
 import password.pwm.svc.wordlist.SeedlistService;
 import password.pwm.svc.wordlist.SharedHistoryService;
 import password.pwm.svc.wordlist.SharedHistoryService;
@@ -66,8 +68,6 @@ import password.pwm.util.MBeanUtility;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PwmScheduler;
 import password.pwm.util.PwmScheduler;
 import password.pwm.util.cli.commands.ExportHttpsTomcatConfigCommand;
 import password.pwm.util.cli.commands.ExportHttpsTomcatConfigCommand;
-import password.pwm.util.db.DatabaseAccessor;
-import password.pwm.util.db.DatabaseService;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -81,7 +81,6 @@ import password.pwm.util.logging.PwmLogLevel;
 import password.pwm.util.logging.PwmLogManager;
 import password.pwm.util.logging.PwmLogManager;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.macro.MacroRequest;
-import password.pwm.util.queue.SmsQueueManager;
 import password.pwm.util.secure.HttpsServerCertificateManager;
 import password.pwm.util.secure.HttpsServerCertificateManager;
 import password.pwm.util.secure.PwmRandom;
 import password.pwm.util.secure.PwmRandom;
 
 
@@ -96,11 +95,13 @@ import java.security.KeyStore;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
+import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeMap;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -118,7 +119,9 @@ public class PwmApplication
     private Map<DomainID, PwmDomain> domains;
     private Map<DomainID, PwmDomain> domains;
     private String runtimeNonce = PwmRandom.getInstance().randomUUID().toString();
     private String runtimeNonce = PwmRandom.getInstance().randomUUID().toString();
 
 
-    private final PwmServiceManager pwmServiceManager = new PwmServiceManager( this, DomainID.systemId(), PwmServiceEnum.forScope( PwmSettingScope.SYSTEM ) );
+    private final PwmServiceManager pwmServiceManager = new PwmServiceManager(
+            SessionLabel.SYSTEM_LABEL,
+            this, DomainID.systemId(), PwmServiceEnum.forScope( PwmSettingScope.SYSTEM ) );
 
 
     private final Instant startupTime = Instant.now();
     private final Instant startupTime = Instant.now();
     private Instant installTime = Instant.now();
     private Instant installTime = Instant.now();
@@ -136,9 +139,10 @@ public class PwmApplication
     {
     {
         this.pwmEnvironment = Objects.requireNonNull( pwmEnvironment );
         this.pwmEnvironment = Objects.requireNonNull( pwmEnvironment );
 
 
-
-        pwmEnvironment.verifyIfApplicationPathIsSetProperly();
-
+        if ( !pwmEnvironment.isInternalRuntimeInstance() )
+        {
+            pwmEnvironment.verifyIfApplicationPathIsSetProperly();
+        }
 
 
         try
         try
         {
         {
@@ -249,7 +253,7 @@ public class PwmApplication
         LOGGER.debug( () -> "application environment flags: " + JsonUtil.serializeCollection( pwmEnvironment.getFlags() ) );
         LOGGER.debug( () -> "application environment flags: " + JsonUtil.serializeCollection( pwmEnvironment.getFlags() ) );
         LOGGER.debug( () -> "application environment parameters: " + JsonUtil.serializeMap( pwmEnvironment.getParameters() ) );
         LOGGER.debug( () -> "application environment parameters: " + JsonUtil.serializeMap( pwmEnvironment.getParameters() ) );
 
 
-        pwmScheduler = new PwmScheduler( getInstanceID() );
+        pwmScheduler = new PwmScheduler( this );
 
 
         pwmServiceManager.initAllServices();
         pwmServiceManager.initAllServices();
 
 
@@ -262,7 +266,7 @@ public class PwmApplication
         {
         {
             final TimeDuration totalTime = TimeDuration.fromCurrent( startTime );
             final TimeDuration totalTime = TimeDuration.fromCurrent( startTime );
             LOGGER.info( () -> PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION + " open for bidness! (" + totalTime.asCompactString() + ")" );
             LOGGER.info( () -> PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION + " open for bidness! (" + totalTime.asCompactString() + ")" );
-            StatisticsManager.incrementStat( this, Statistic.PWM_STARTUPS );
+            StatisticsClient.incrementStat( this, Statistic.PWM_STARTUPS );
             LOGGER.debug( () -> "buildTime=" + PwmConstants.BUILD_TIME + ", javaLocale=" + Locale.getDefault() + ", DefaultLocale=" + PwmConstants.DEFAULT_LOCALE );
             LOGGER.debug( () -> "buildTime=" + PwmConstants.BUILD_TIME + ", javaLocale=" + Locale.getDefault() + ", DefaultLocale=" + PwmConstants.DEFAULT_LOCALE );
 
 
             pwmScheduler.immediateExecuteRunnableInNewThread( this::postInitTasks, this.getClass().getSimpleName() + " postInit tasks" );
             pwmScheduler.immediateExecuteRunnableInNewThread( this::postInitTasks, this.getClass().getSimpleName() + " postInit tasks" );
@@ -302,10 +306,6 @@ public class PwmApplication
     {
     {
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();
 
 
-        getPwmScheduler().immediateExecuteRunnableInNewThread( UserAgentUtils::initializeCache, "initialize useragent cache" );
-        getPwmScheduler().immediateExecuteRunnableInNewThread( PwmSettingMetaDataReader::initCache, "initialize PwmSetting cache" );
-
-
         if ( Boolean.parseBoolean( getConfig().readAppProperty( AppProperty.LOGGING_OUTPUT_CONFIGURATION ) ) )
         if ( Boolean.parseBoolean( getConfig().readAppProperty( AppProperty.LOGGING_OUTPUT_CONFIGURATION ) ) )
         {
         {
             outputConfigurationToLog( this );
             outputConfigurationToLog( this );
@@ -313,18 +313,7 @@ public class PwmApplication
         }
         }
 
 
         // send system audit event
         // send system audit event
-        try
-        {
-            final SystemAuditRecord auditRecord = new AuditRecordFactory( this ).createSystemAuditRecord(
-                    AuditEvent.STARTUP,
-                    null
-            );
-            getAuditManager().submit( null, auditRecord );
-        }
-        catch ( final PwmException e )
-        {
-            LOGGER.warn( () -> "unable to submit start alert event " + e.getMessage() );
-        }
+        AuditServiceClient.submitSystemEvent( this, SessionLabel.SYSTEM_LABEL, AuditEvent.STARTUP );
 
 
         try
         try
         {
         {
@@ -338,7 +327,7 @@ public class PwmApplication
 
 
         try
         try
         {
         {
-            this.getIntruderService().clear( IntruderRecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME );
+            this.getAdminDomain().getIntruderService().clear( IntruderRecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
@@ -366,11 +355,15 @@ public class PwmApplication
             }
             }
         }
         }
 
 
+        getPwmScheduler().immediateExecuteRunnableInNewThread( UserAgentUtils::initializeCache, "initialize useragent cache" );
+        getPwmScheduler().immediateExecuteRunnableInNewThread( PwmSettingMetaDataReader::initCache, "initialize PwmSetting cache" );
+
         MBeanUtility.registerMBean( this );
         MBeanUtility.registerMBean( this );
         LOGGER.trace( () -> "completed post init tasks", () -> TimeDuration.fromCurrent( startTime ) );
         LOGGER.trace( () -> "completed post init tasks", () -> TimeDuration.fromCurrent( startTime ) );
     }
     }
 
 
-    public static PwmApplication createPwmApplication( final PwmEnvironment pwmEnvironment ) throws PwmUnrecoverableException
+    public static PwmApplication createPwmApplication( final PwmEnvironment pwmEnvironment )
+            throws PwmUnrecoverableException
     {
     {
         return new PwmApplication( pwmEnvironment );
         return new PwmApplication( pwmEnvironment );
     }
     }
@@ -427,24 +420,17 @@ public class PwmApplication
 
 
     public void shutdown( final boolean keepServicesRunning )
     public void shutdown( final boolean keepServicesRunning )
     {
     {
-        LOGGER.warn( () -> "shutting down" );
+        final Instant startTime = Instant.now();
+
+        if ( keepServicesRunning )
         {
         {
-            // send system audit event
-            try
-            {
-                final SystemAuditRecord auditRecord = new AuditRecordFactory( this ).createSystemAuditRecord(
-                        AuditEvent.SHUTDOWN,
-                        null
-                );
-                if ( getAuditManager() != null )
-                {
-                    getAuditManager().submit( null, auditRecord );
-                }
-            }
-            catch ( final PwmException e )
-            {
-                LOGGER.warn( () -> "unable to submit shutdown alert event " + e.getMessage() );
-            }
+            LOGGER.warn( () -> "preparing for restart" );
+            AuditServiceClient.submitSystemEvent( this, SessionLabel.SYSTEM_LABEL, AuditEvent.RESTART );
+        }
+        else
+        {
+            LOGGER.warn( () -> "shutting down" );
+            AuditServiceClient.submitSystemEvent( this, SessionLabel.SYSTEM_LABEL, AuditEvent.SHUTDOWN );
         }
         }
 
 
         MBeanUtility.unregisterMBean( this );
         MBeanUtility.unregisterMBean( this );
@@ -489,8 +475,14 @@ public class PwmApplication
         {
         {
             try
             try
             {
             {
-                LOGGER.trace( () -> "beginning close of LocalDB" );
+                final Instant startCloseDbTime = Instant.now();
+                LOGGER.debug( () -> "beginning close of LocalDB" );
                 localDB.close();
                 localDB.close();
+                final TimeDuration closeLocalDbDuration = TimeDuration.fromCurrent( startCloseDbTime );
+                if ( closeLocalDbDuration.isLongerThan( TimeDuration.SECONDS_10 ) )
+                {
+                    LOGGER.info( () -> "completed close of LocalDB", () -> closeLocalDbDuration );
+                }
             }
             }
             catch ( final Exception e )
             catch ( final Exception e )
             {
             {
@@ -506,7 +498,8 @@ public class PwmApplication
 
 
         pwmScheduler.shutdown();
         pwmScheduler.shutdown();
 
 
-        LOGGER.info( () -> PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION + " closed for bidness, cya!" );
+        LOGGER.info( () -> PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION
+                + " closed for bidness, cya!", () -> TimeDuration.fromCurrent( startTime ) );
     }
     }
 
 
     private static void outputKeystore( final PwmApplication pwmApplication ) throws Exception
     private static void outputKeystore( final PwmApplication pwmApplication ) throws Exception
@@ -719,8 +712,6 @@ public class PwmApplication
     }
     }
 
 
 
 
-
-
     private Instant fetchInstallDate( final Instant startupTime )
     private Instant fetchInstallDate( final Instant startupTime )
     {
     {
         if ( localDB != null )
         if ( localDB != null )
@@ -756,6 +747,11 @@ public class PwmApplication
             }
             }
         }
         }
 
 
+        if ( pwmApplication.getLocalDB() == null || pwmApplication.getApplicationMode() != PwmApplicationMode.RUNNING )
+        {
+            return DEFAULT_INSTANCE_ID;
+        }
+
         {
         {
             final Optional<String> optionalStoredInstanceID = readAppAttribute( AppAttribute.INSTANCE_ID, String.class );
             final Optional<String> optionalStoredInstanceID = readAppAttribute( AppAttribute.INSTANCE_ID, String.class );
             if ( optionalStoredInstanceID.isPresent() )
             if ( optionalStoredInstanceID.isPresent() )
@@ -786,9 +782,9 @@ public class PwmApplication
         return ( SharedHistoryService ) pwmServiceManager.getService( PwmServiceEnum.SharedHistoryManager );
         return ( SharedHistoryService ) pwmServiceManager.getService( PwmServiceEnum.SharedHistoryManager );
     }
     }
 
 
-    public IntruderService getIntruderService( )
+    public IntruderSystemService getIntruderSystemService( ) throws PwmUnrecoverableException
     {
     {
-        return ( IntruderService ) pwmServiceManager.getService( PwmServiceEnum.IntruderManager );
+        return ( IntruderSystemService ) pwmServiceManager.getService( PwmServiceEnum.IntruderSystemService );
     }
     }
 
 
     public LocalDBLogger getLocalDBLogger( )
     public LocalDBLogger getLocalDBLogger( )
@@ -808,28 +804,39 @@ public class PwmApplication
 
 
     public List<PwmService> getPwmServices( )
     public List<PwmService> getPwmServices( )
     {
     {
-        final List<PwmService> pwmServices = new ArrayList<>();
+        final List<PwmService> pwmServices = new ArrayList<>( this.pwmServiceManager.getRunningServices() );
         pwmServices.add( this.localDBLogger );
         pwmServices.add( this.localDBLogger );
-        pwmServices.addAll( this.pwmServiceManager.getRunningServices() );
         return Collections.unmodifiableList( pwmServices );
         return Collections.unmodifiableList( pwmServices );
     }
     }
 
 
-    public List<PwmService> getAppAndDomainPwmServices( )
+    public Map<DomainID, List<PwmService>> getAppAndDomainPwmServices( )
     {
     {
-        final List<PwmService> pwmServices = new ArrayList<>( getPwmServices() );
-        domains().values().forEach( domain -> pwmServices.addAll( domain.getPwmServices() ) );
-        return Collections.unmodifiableList( pwmServices );
+        final Map<DomainID, List<PwmService>> pwmServices = new LinkedHashMap<>();
+
+        for ( final PwmService pwmService : getPwmServices() )
+        {
+            pwmServices.computeIfAbsent( DomainID.systemId(), k -> new ArrayList<>() ).add( pwmService );
+        }
 
 
+        for ( final PwmDomain pwmDomain : domains().values() )
+        {
+            for ( final PwmService pwmService : pwmDomain.getPwmServices() )
+            {
+                pwmServices.computeIfAbsent( pwmDomain.getDomainID(), k -> new ArrayList<>() ).add( pwmService );
+            }
+        }
+
+        return Collections.unmodifiableMap( pwmServices );
     }
     }
 
 
     public WordlistService getWordlistService( )
     public WordlistService getWordlistService( )
     {
     {
-        return ( WordlistService ) pwmServiceManager.getService( PwmServiceEnum.WordlistManager );
+        return ( WordlistService ) pwmServiceManager.getService( PwmServiceEnum.WordlistService );
     }
     }
 
 
     public SeedlistService getSeedlistManager( )
     public SeedlistService getSeedlistManager( )
     {
     {
-        return ( SeedlistService ) pwmServiceManager.getService( PwmServiceEnum.SeedlistManager );
+        return ( SeedlistService ) pwmServiceManager.getService( PwmServiceEnum.SeedlistService );
     }
     }
 
 
     public ReportService getReportService( )
     public ReportService getReportService( )
@@ -839,22 +846,17 @@ public class PwmApplication
 
 
     public EmailService getEmailQueue( )
     public EmailService getEmailQueue( )
     {
     {
-        return ( EmailService ) pwmServiceManager.getService( PwmServiceEnum.EmailQueueManager );
+        return ( EmailService ) pwmServiceManager.getService( PwmServiceEnum.EmailService );
     }
     }
 
 
-    public AuditService getAuditManager( )
+    public AuditService getAuditService( )
     {
     {
         return ( AuditService ) pwmServiceManager.getService( PwmServiceEnum.AuditService );
         return ( AuditService ) pwmServiceManager.getService( PwmServiceEnum.AuditService );
     }
     }
 
 
-    public SmsQueueManager getSmsQueue( )
-    {
-        return ( SmsQueueManager ) pwmServiceManager.getService( PwmServiceEnum.SmsQueueManager );
-    }
-
-    public PwNotifyService getPwNotifyService( )
+    public SmsQueueService getSmsQueue( )
     {
     {
-        return ( PwNotifyService ) pwmServiceManager.getService( PwmServiceEnum.PwExpiryNotifyService );
+        return ( SmsQueueService ) pwmServiceManager.getService( PwmServiceEnum.SmsQueueManager );
     }
     }
 
 
     public UrlShortenerService getUrlShortener( )
     public UrlShortenerService getUrlShortener( )
@@ -894,9 +896,9 @@ public class PwmApplication
         return ( DatabaseService ) pwmServiceManager.getService( PwmServiceEnum.DatabaseService );
         return ( DatabaseService ) pwmServiceManager.getService( PwmServiceEnum.DatabaseService );
     }
     }
 
 
-    public StatisticsManager getStatisticsManager( )
+    public StatisticsService getStatisticsManager( )
     {
     {
-        return ( StatisticsManager ) pwmServiceManager.getService( PwmServiceEnum.StatisticsManager );
+        return ( StatisticsService ) pwmServiceManager.getService( PwmServiceEnum.StatisticsService );
     }
     }
 
 
     public SessionStateService getSessionStateService( )
     public SessionStateService getSessionStateService( )
@@ -942,7 +944,7 @@ public class PwmApplication
             final MacroRequest macroRequest
             final MacroRequest macroRequest
     )
     )
     {
     {
-        final SmsQueueManager smsQueue = getSmsQueue();
+        final SmsQueueService smsQueue = getSmsQueue();
         if ( smsQueue == null )
         if ( smsQueue == null )
         {
         {
             LOGGER.error( sessionLabel, () -> "SMS queue is unavailable, unable to send SMS to: " + to );
             LOGGER.error( sessionLabel, () -> "SMS queue is unavailable, unable to send SMS to: " + to );
@@ -1119,4 +1121,33 @@ public class PwmApplication
         }
         }
         return false;
         return false;
     }
     }
+
+    public enum Condition
+    {
+        RunningMode( ( pwmApplication ) -> pwmApplication.getApplicationMode() == PwmApplicationMode.RUNNING ),
+        LocalDBOpen( ( pwmApplication ) -> pwmApplication.getLocalDB() != null && LocalDB.Status.OPEN == pwmApplication.getLocalDB().status() ),
+        NotInternalInstance( pwmApplication -> !pwmApplication.getPwmEnvironment().isInternalRuntimeInstance() ),;
+
+        private final Function<PwmApplication, Boolean> function;
+
+        Condition( final Function<PwmApplication, Boolean> function )
+        {
+            this.function = function;
+        }
+
+        private boolean matches( final PwmApplication pwmApplication )
+        {
+            return function.apply( pwmApplication );
+        }
+    }
+
+    public boolean checkConditions( final Set<Condition> conditions )
+    {
+        if ( conditions == null )
+        {
+            return true;
+        }
+
+        return conditions.stream().allMatch( ( c ) -> c.matches( this ) );
+    }
 }
 }

+ 1 - 1
server/src/main/java/password/pwm/PwmConstants.java

@@ -210,7 +210,7 @@ public abstract class PwmConstants
             "this password is an memorial of the richard d. kiel memorial abend" );
             "this password is an memorial of the richard d. kiel memorial abend" );
 
 
 
 
-    private static String readPwmConstantsBundle( final String key )
+    public static String readPwmConstantsBundle( final String key )
     {
     {
         return ResourceBundle.getBundle( PwmConstants.class.getName() ).getString( key );
         return ResourceBundle.getBundle( PwmConstants.class.getName() ).getString( key );
     }
     }

+ 19 - 17
server/src/main/java/password/pwm/PwmDomain.java

@@ -24,6 +24,7 @@ import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.PwmSettingScope;
 import password.pwm.config.PwmSettingScope;
@@ -39,11 +40,11 @@ import password.pwm.svc.PwmServiceManager;
 import password.pwm.svc.cache.CacheService;
 import password.pwm.svc.cache.CacheService;
 import password.pwm.svc.event.AuditService;
 import password.pwm.svc.event.AuditService;
 import password.pwm.svc.httpclient.HttpClientService;
 import password.pwm.svc.httpclient.HttpClientService;
-import password.pwm.svc.intruder.IntruderService;
+import password.pwm.svc.intruder.IntruderDomainService;
 import password.pwm.svc.pwnotify.PwNotifyService;
 import password.pwm.svc.pwnotify.PwNotifyService;
 import password.pwm.svc.secure.DomainSecureService;
 import password.pwm.svc.secure.DomainSecureService;
 import password.pwm.svc.sessiontrack.SessionTrackService;
 import password.pwm.svc.sessiontrack.SessionTrackService;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsService;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.userhistory.UserHistoryService;
 import password.pwm.svc.userhistory.UserHistoryService;
 import password.pwm.svc.wordlist.SharedHistoryService;
 import password.pwm.svc.wordlist.SharedHistoryService;
@@ -51,8 +52,8 @@ import password.pwm.util.DailySummaryJob;
 import password.pwm.util.PwmScheduler;
 import password.pwm.util.PwmScheduler;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.operations.CrService;
-import password.pwm.util.operations.OtpService;
+import password.pwm.svc.cr.CrService;
+import password.pwm.svc.otp.OtpService;
 
 
 import java.time.Instant;
 import java.time.Instant;
 import java.util.List;
 import java.util.List;
@@ -71,6 +72,7 @@ public class PwmDomain
 
 
     private final PwmApplication pwmApplication;
     private final PwmApplication pwmApplication;
     private final DomainID domainID;
     private final DomainID domainID;
+    private final SessionLabel sessionLabel;
 
 
     private final PwmServiceManager pwmServiceManager;
     private final PwmServiceManager pwmServiceManager;
 
 
@@ -78,8 +80,9 @@ public class PwmDomain
     {
     {
         this.pwmApplication = Objects.requireNonNull( pwmApplication );
         this.pwmApplication = Objects.requireNonNull( pwmApplication );
         this.domainID = Objects.requireNonNull( domainID );
         this.domainID = Objects.requireNonNull( domainID );
+        this.sessionLabel = SessionLabel.builder().domain( domainID.stringValue() ).build();
 
 
-        this.pwmServiceManager = new PwmServiceManager( pwmApplication, domainID, PwmServiceEnum.forScope( PwmSettingScope.DOMAIN ) );
+        this.pwmServiceManager = new PwmServiceManager( sessionLabel, pwmApplication, domainID, PwmServiceEnum.forScope( PwmSettingScope.DOMAIN ) );
     }
     }
 
 
     public void initialize()
     public void initialize()
@@ -93,7 +96,6 @@ public class PwmDomain
         {
         {
             final ExecutorService executorService = PwmScheduler.makeSingleThreadExecutorService( getPwmApplication(), DailySummaryJob.class );
             final ExecutorService executorService = PwmScheduler.makeSingleThreadExecutorService( getPwmApplication(), DailySummaryJob.class );
             pwmApplication.getPwmScheduler().scheduleDailyZuluZeroStartJob( new DailySummaryJob( this ), executorService, TimeDuration.ZERO );
             pwmApplication.getPwmScheduler().scheduleDailyZuluZeroStartJob( new DailySummaryJob( this ), executorService, TimeDuration.ZERO );
-            new DailySummaryJob( this ).run();
         }
         }
 
 
         LOGGER.trace( () -> "completed initializing domain " + domainID.stringValue(), () -> TimeDuration.fromCurrent( startTime ) );
         LOGGER.trace( () -> "completed initializing domain " + domainID.stringValue(), () -> TimeDuration.fromCurrent( startTime ) );
@@ -109,7 +111,7 @@ public class PwmDomain
         return pwmApplication.getApplicationMode();
         return pwmApplication.getApplicationMode();
     }
     }
 
 
-    public StatisticsManager getStatisticsManager( )
+    public StatisticsService getStatisticsManager( )
     {
     {
         return pwmApplication.getStatisticsManager();
         return pwmApplication.getStatisticsManager();
     }
     }
@@ -154,9 +156,9 @@ public class PwmDomain
         return ( LdapConnectionService ) pwmServiceManager.getService( PwmServiceEnum.LdapConnectionService );
         return ( LdapConnectionService ) pwmServiceManager.getService( PwmServiceEnum.LdapConnectionService );
     }
     }
 
 
-    public AuditService getAuditManager()
+    public AuditService getAuditService()
     {
     {
-        return pwmApplication.getAuditManager();
+        return pwmApplication.getAuditService();
     }
     }
 
 
     public SessionTrackService getSessionTrackService()
     public SessionTrackService getSessionTrackService()
@@ -164,12 +166,12 @@ public class PwmDomain
         return pwmApplication.getSessionTrackService();
         return pwmApplication.getSessionTrackService();
     }
     }
 
 
-    public ChaiUser getProxiedChaiUser( final UserIdentity userIdentity )
+    public ChaiUser getProxiedChaiUser( final SessionLabel sessionLabel, final UserIdentity userIdentity )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         try
         try
         {
         {
-            final ChaiProvider proxiedProvider = getProxyChaiProvider( userIdentity.getLdapProfileID() );
+            final ChaiProvider proxiedProvider = getProxyChaiProvider( sessionLabel, userIdentity.getLdapProfileID() );
             return proxiedProvider.getEntryFactory().newChaiUser( userIdentity.getUserDN() );
             return proxiedProvider.getEntryFactory().newChaiUser( userIdentity.getUserDN() );
         }
         }
         catch ( final ChaiUnavailableException e )
         catch ( final ChaiUnavailableException e )
@@ -178,16 +180,16 @@ public class PwmDomain
         }
         }
     }
     }
 
 
-    public ChaiProvider getProxyChaiProvider( final String identifier )
+    public ChaiProvider getProxyChaiProvider( final SessionLabel sessionLabel, final String identifier )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         Objects.requireNonNull( identifier );
         Objects.requireNonNull( identifier );
-        return getLdapConnectionService().getProxyChaiProvider( identifier );
+        return getLdapConnectionService().getProxyChaiProvider( sessionLabel, identifier );
     }
     }
 
 
     public List<PwmService> getPwmServices( )
     public List<PwmService> getPwmServices( )
     {
     {
-        return pwmApplication.getPwmServices();
+        return pwmServiceManager.getRunningServices();
     }
     }
 
 
     public UserSearchEngine getUserSearchEngine()
     public UserSearchEngine getUserSearchEngine()
@@ -200,9 +202,9 @@ public class PwmDomain
         return pwmApplication.getHttpClientService();
         return pwmApplication.getHttpClientService();
     }
     }
 
 
-    public IntruderService getIntruderManager()
+    public IntruderDomainService getIntruderService()
     {
     {
-        return pwmApplication.getIntruderService();
+        return ( IntruderDomainService ) pwmServiceManager.getService( PwmServiceEnum.IntruderDomainService );
     }
     }
 
 
     public TokenService getTokenService()
     public TokenService getTokenService()
@@ -222,7 +224,7 @@ public class PwmDomain
 
 
     public PwNotifyService getPwNotifyService()
     public PwNotifyService getPwNotifyService()
     {
     {
-        return pwmApplication.getPwNotifyService();
+        return ( PwNotifyService ) pwmServiceManager.getService( PwmServiceEnum.PwExpiryNotifyService );
     }
     }
 
 
     public ResourceServletService getResourceServletService( )
     public ResourceServletService getResourceServletService( )

+ 0 - 1
server/src/main/java/password/pwm/bean/DomainID.java

@@ -35,7 +35,6 @@ public class DomainID implements Comparable<DomainID>, Serializable
 {
 {
     public static final List<String> DOMAIN_RESERVED_WORDS = List.of( "system", "private", "public", "pwm", "sspr", "domain", "profile", "password" );
     public static final List<String> DOMAIN_RESERVED_WORDS = List.of( "system", "private", "public", "pwm", "sspr", "domain", "profile", "password" );
     public static final DomainID DOMAIN_ID_DEFAULT = create( "default" );
     public static final DomainID DOMAIN_ID_DEFAULT = create( "default" );
-    public static final DomainID DOMAIN_ID_PLACEHOLDER = create( "default" );
 
 
     private static final String SYSTEM_ID = "system";
     private static final String SYSTEM_ID = "system";
     private static final DomainID SYSTEM_DOMAIN_ID = new DomainID( SYSTEM_ID );
     private static final DomainID SYSTEM_DOMAIN_ID = new DomainID( SYSTEM_ID );

+ 13 - 8
server/src/main/java/password/pwm/bean/SessionLabel.java

@@ -22,6 +22,8 @@ package password.pwm.bean;
 
 
 import lombok.Builder;
 import lombok.Builder;
 import lombok.Value;
 import lombok.Value;
+import password.pwm.PwmConstants;
+import password.pwm.svc.PwmService;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
 
 
@@ -29,16 +31,10 @@ import java.io.Serializable;
 @Builder( toBuilder = true )
 @Builder( toBuilder = true )
 public class SessionLabel implements Serializable
 public class SessionLabel implements Serializable
 {
 {
-    public static final SessionLabel SYSTEM_LABEL = null;
     public static final String SESSION_LABEL_SESSION_ID = "#";
     public static final String SESSION_LABEL_SESSION_ID = "#";
-    public static final SessionLabel PW_EXP_NOTICE_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "pwExpireNotice" ).build();
-    public static final SessionLabel TOKEN_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "token" ).build();
+    public static final SessionLabel SYSTEM_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( PwmConstants.PWM_APP_NAME ).build();
+    public static final SessionLabel TEST_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "test" ).build();
     public static final SessionLabel CLI_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "cli" ).build();
     public static final SessionLabel CLI_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "cli" ).build();
-    public static final SessionLabel HEALTH_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "health" ).build();
-    public static final SessionLabel REPORTING_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "reporting" ).build();
-    public static final SessionLabel AUDITING_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "auditing" ).build();
-    public static final SessionLabel TELEMETRY_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "telemetry" ).build();
-    public static final SessionLabel PWNOTIFY_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "pwnotify" ).build();
     public static final SessionLabel CONTEXT_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "context" ).build();
     public static final SessionLabel CONTEXT_SESSION_LABEL = SessionLabel.builder().sessionID( SESSION_LABEL_SESSION_ID ).username( "context" ).build();
 
 
     private final String sessionID;
     private final String sessionID;
@@ -49,4 +45,13 @@ public class SessionLabel implements Serializable
     private final String sourceHostname;
     private final String sourceHostname;
     private final String profile;
     private final String profile;
     private final String domain;
     private final String domain;
+
+    public static SessionLabel forPwmService( final PwmService pwmService, final DomainID domainID )
+    {
+        return SessionLabel.builder()
+                .sessionID( SESSION_LABEL_SESSION_ID )
+                .username( pwmService.getClass().getSimpleName() )
+                .domain( domainID.stringValue() )
+                .build();
+    }
 }
 }

+ 9 - 9
server/src/main/java/password/pwm/bean/UserIdentity.java

@@ -200,7 +200,7 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
         }
         }
     }
     }
 
 
-    public static UserIdentity fromDelimitedKey( final String key )
+    public static UserIdentity fromDelimitedKey( final SessionLabel sessionLabel, final String key )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         JavaHelper.requireNonEmpty( key );
         JavaHelper.requireNonEmpty( key );
@@ -211,7 +211,7 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
-            LOGGER.trace( () -> "unable to deserialize UserIdentity: " + key + " using JSON method: " + e.getMessage() );
+            LOGGER.trace( sessionLabel, () -> "unable to deserialize UserIdentity: " + key + " using JSON method: " + e.getMessage() );
         }
         }
 
 
         // old style
         // old style
@@ -253,7 +253,7 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
      * @deprecated  Should be used by calling {@link #fromDelimitedKey(String)} or {@link #fromObfuscatedKey(String, PwmApplication)}.
      * @deprecated  Should be used by calling {@link #fromDelimitedKey(String)} or {@link #fromObfuscatedKey(String, PwmApplication)}.
      */
      */
     @Deprecated
     @Deprecated
-    public static UserIdentity fromKey( final String key, final PwmApplication pwmApplication )
+    public static UserIdentity fromKey( final SessionLabel sessionLabel, final String key, final PwmApplication pwmApplication )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         JavaHelper.requireNonEmpty( key );
         JavaHelper.requireNonEmpty( key );
@@ -263,10 +263,10 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
             return fromObfuscatedKey( key, pwmApplication );
             return fromObfuscatedKey( key, pwmApplication );
         }
         }
 
 
-        return fromDelimitedKey( key );
+        return fromDelimitedKey( sessionLabel, key );
     }
     }
 
 
-    public boolean canonicalEquals( final UserIdentity otherIdentity, final PwmApplication pwmApplication )
+    public boolean canonicalEquals( final SessionLabel sessionLabel, final UserIdentity otherIdentity, final PwmApplication pwmApplication )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         if ( otherIdentity == null )
         if ( otherIdentity == null )
@@ -274,8 +274,8 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
             return false;
             return false;
         }
         }
 
 
-        final UserIdentity thisCanonicalIdentity = this.canonicalized( pwmApplication );
-        final UserIdentity otherCanonicalIdentity = otherIdentity.canonicalized( pwmApplication );
+        final UserIdentity thisCanonicalIdentity = this.canonicalized( sessionLabel, pwmApplication );
+        final UserIdentity otherCanonicalIdentity = otherIdentity.canonicalized( sessionLabel, pwmApplication );
         return thisCanonicalIdentity.equals( otherCanonicalIdentity );
         return thisCanonicalIdentity.equals( otherCanonicalIdentity );
     }
     }
 
 
@@ -308,7 +308,7 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
         return COMPARATOR.compare( this, otherIdentity );
         return COMPARATOR.compare( this, otherIdentity );
     }
     }
 
 
-    public UserIdentity canonicalized( final PwmApplication pwmApplication )
+    public UserIdentity canonicalized( final SessionLabel sessionLabel, final PwmApplication pwmApplication )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         if ( this.canonical )
         if ( this.canonical )
@@ -316,7 +316,7 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
             return this;
             return this;
         }
         }
 
 
-        final ChaiUser chaiUser = pwmApplication.domains().get( this.getDomainID() ).getProxiedChaiUser( this );
+        final ChaiUser chaiUser = pwmApplication.domains().get( this.getDomainID() ).getProxiedChaiUser( sessionLabel, this );
         final String userDN;
         final String userDN;
         try
         try
         {
         {

+ 7 - 40
server/src/main/java/password/pwm/config/AppConfig.java

@@ -25,13 +25,12 @@ import password.pwm.PwmConstants;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.PrivateKeyCertificate;
 import password.pwm.bean.PrivateKeyCertificate;
 import password.pwm.config.option.CertificateMatchingMode;
 import password.pwm.config.option.CertificateMatchingMode;
+import password.pwm.config.option.DataStorageMethod;
 import password.pwm.config.profile.EmailServerProfile;
 import password.pwm.config.profile.EmailServerProfile;
 import password.pwm.config.profile.ProfileDefinition;
 import password.pwm.config.profile.ProfileDefinition;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.value.FileValue;
 import password.pwm.config.value.FileValue;
 import password.pwm.config.value.data.UserPermission;
 import password.pwm.config.value.data.UserPermission;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
@@ -40,7 +39,6 @@ import password.pwm.util.java.LazySupplier;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogLevel;
 import password.pwm.util.logging.PwmLogLevel;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.secure.PwmRandom;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
@@ -208,6 +206,11 @@ public class AppConfig implements SettingReader
         return settingReader.readLocalizedBundle( className, keyName );
         return settingReader.readLocalizedBundle( className, keyName );
     }
     }
 
 
+    public List<DataStorageMethod> readGenericStorageLocations( final PwmSetting setting )
+    {
+        return settingReader.readGenericStorageLocations( setting );
+    }
+
     private class ConfigurationSuppliers
     private class ConfigurationSuppliers
     {
     {
         private final Supplier<Map<String, String>> appPropertyOverrides = new LazySupplier<>( () ->
         private final Supplier<Map<String, String>> appPropertyOverrides = new LazySupplier<>( () ->
@@ -216,43 +219,7 @@ public class AppConfig implements SettingReader
 
 
         private final LazySupplier.CheckedSupplier<PwmSecurityKey, PwmUnrecoverableException> pwmSecurityKey
         private final LazySupplier.CheckedSupplier<PwmSecurityKey, PwmUnrecoverableException> pwmSecurityKey
                 = LazySupplier.checked( () ->
                 = LazySupplier.checked( () ->
-        {
-            final PasswordData configValue = settingReader.readSettingAsPassword( PwmSetting.PWM_SECURITY_KEY );
-
-            if ( configValue == null || configValue.getStringValue().isEmpty() )
-            {
-                final String errorMsg = "Security Key value is not configured, will generate temp value for use by runtime instance";
-                final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
-                LOGGER.warn( errorInfo::toDebugStr );
-                if ( tempInstanceKey == null )
-                {
-                    tempInstanceKey = new PwmSecurityKey( PwmRandom.getInstance().alphaNumericString( 1024 ) );
-                }
-                return tempInstanceKey;
-            }
-            else
-            {
-                final int minSecurityKeyLength = Integer.parseInt( readAppProperty( AppProperty.SECURITY_CONFIG_MIN_SECURITY_KEY_LENGTH ) );
-                if ( configValue.getStringValue().length() < minSecurityKeyLength )
-                {
-                    final String errorMsg = "Security Key must be greater than 32 characters in length";
-                    final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
-                    throw new PwmUnrecoverableException( errorInfo );
-                }
-
-                try
-                {
-                    return new PwmSecurityKey( configValue.getStringValue() );
-                }
-                catch ( final Exception e )
-                {
-                    final String errorMsg = "unexpected error generating Security Key crypto: " + e.getMessage();
-                    final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
-                    LOGGER.error( errorInfo::toDebugStr, e );
-                    throw new PwmUnrecoverableException( errorInfo );
-                }
-            }
-        } );
+                settingReader.readSecurityKey( PwmSetting.PWM_SECURITY_KEY, AppConfig.this ) );
 
 
         private final Supplier<Map<Locale, String>> localeFlagMap = new LazySupplier<>( () ->
         private final Supplier<Map<Locale, String>> localeFlagMap = new LazySupplier<>( () ->
         {
         {

+ 0 - 86
server/src/main/java/password/pwm/config/ConfigurationUtil.java

@@ -1,86 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2020 The PWM Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package password.pwm.config;
-
-import password.pwm.config.option.DataStorageMethod;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Static helper methods for reading {@link DomainConfig} values.
- */
-public class ConfigurationUtil
-{
-    private ConfigurationUtil()
-    {
-    }
-
-    public static List<DataStorageMethod> getCrReadPreference( final DomainConfig domainConfig )
-    {
-        final List<DataStorageMethod> readPreferences = new ArrayList<>(
-                domainConfig.getResponseStorageLocations( PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE ) );
-
-        if ( readPreferences.size() == 1 && readPreferences.iterator().next() == DataStorageMethod.AUTO )
-        {
-            readPreferences.clear();
-            if ( domainConfig.getAppConfig().hasDbConfigured() )
-            {
-                readPreferences.add( DataStorageMethod.DB );
-            }
-            else
-            {
-                readPreferences.add( DataStorageMethod.LDAP );
-            }
-        }
-
-
-        if ( domainConfig.readSettingAsBoolean( PwmSetting.EDIRECTORY_USE_NMAS_RESPONSES ) )
-        {
-            readPreferences.add( DataStorageMethod.NMAS );
-        }
-
-        return Collections.unmodifiableList( readPreferences );
-    }
-
-    public static List<DataStorageMethod> getCrWritePreference( final DomainConfig domainConfig )
-    {
-        final List<DataStorageMethod> writeMethods = new ArrayList<>( domainConfig.getResponseStorageLocations( PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE ) );
-        if ( writeMethods.size() == 1 && writeMethods.get( 0 ) == DataStorageMethod.AUTO )
-        {
-            writeMethods.clear();
-            if ( domainConfig.getAppConfig().hasDbConfigured() )
-            {
-                writeMethods.add( DataStorageMethod.DB );
-            }
-            else
-            {
-                writeMethods.add( DataStorageMethod.LDAP );
-            }
-        }
-        if ( domainConfig.readSettingAsBoolean( PwmSetting.EDIRECTORY_STORE_NMAS_RESPONSES ) )
-        {
-            writeMethods.add( DataStorageMethod.NMAS );
-        }
-        return writeMethods;
-    }
-}

+ 56 - 60
server/src/main/java/password/pwm/config/DomainConfig.java

@@ -47,19 +47,20 @@ import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.config.value.data.NamedSecretData;
 import password.pwm.config.value.data.NamedSecretData;
 import password.pwm.config.value.data.RemoteWebServiceConfiguration;
 import password.pwm.config.value.data.RemoteWebServiceConfiguration;
 import password.pwm.config.value.data.UserPermission;
 import password.pwm.config.value.data.UserPermission;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
+import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.LazySupplier;
 import password.pwm.util.java.LazySupplier;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.secure.PwmRandom;
+import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
+import password.pwm.util.secure.SecureEngine;
 
 
+import java.io.StringWriter;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
@@ -106,7 +107,6 @@ public class DomainConfig implements SettingReader
         return getDomainID().stringValue().equals( adminDomainStr );
         return getDomainID().stringValue().equals( adminDomainStr );
     }
     }
 
 
-
     public List<FormConfiguration> readSettingAsForm( final PwmSetting setting )
     public List<FormConfiguration> readSettingAsForm( final PwmSetting setting )
     {
     {
         return settingReader.readSettingAsForm( setting );
         return settingReader.readSettingAsForm( setting );
@@ -203,7 +203,6 @@ public class DomainConfig implements SettingReader
         return StoredConfigurationUtil.profilesForSetting( this.getDomainID(), PwmSetting.PASSWORD_PROFILE_LIST, storedConfiguration );
         return StoredConfigurationUtil.profilesForSetting( this.getDomainID(), PwmSetting.PASSWORD_PROFILE_LIST, storedConfiguration );
     }
     }
 
 
-
     public List<String> readSettingAsStringArray( final PwmSetting setting )
     public List<String> readSettingAsStringArray( final PwmSetting setting )
     {
     {
         return settingReader.readSettingAsStringArray( setting );
         return settingReader.readSettingAsStringArray( setting );
@@ -234,31 +233,15 @@ public class DomainConfig implements SettingReader
         return settingReader.readSettingAsPrivateKey( setting );
         return settingReader.readSettingAsPrivateKey( setting );
     }
     }
 
 
-    private PwmSecurityKey tempInstanceKey = null;
-
     public PwmSecurityKey getSecurityKey( ) throws PwmUnrecoverableException
     public PwmSecurityKey getSecurityKey( ) throws PwmUnrecoverableException
     {
     {
+        //return configurationSuppliers.pwmSecurityKey.call();
         return getAppConfig().getSecurityKey();
         return getAppConfig().getSecurityKey();
     }
     }
 
 
-    public List<DataStorageMethod> getResponseStorageLocations( final PwmSetting setting )
-    {
-        return getGenericStorageLocations( setting );
-    }
-
-    public List<DataStorageMethod> getOtpSecretStorageLocations( final PwmSetting setting )
-    {
-        return getGenericStorageLocations( setting );
-    }
-
-    private List<DataStorageMethod> getGenericStorageLocations( final PwmSetting setting )
+    public List<DataStorageMethod> readGenericStorageLocations( final PwmSetting setting )
     {
     {
-        final String input = readSettingAsString( setting );
-
-        return Arrays.stream( input.split( "-" ) )
-                .map( s ->  JavaHelper.readEnumFromString( DataStorageMethod.class, s ) )
-                .flatMap( Optional::stream )
-                .collect( Collectors.toUnmodifiableList() );
+        return settingReader.readGenericStorageLocations( setting );
     }
     }
 
 
     public LdapProfile getDefaultLdapProfile( ) throws PwmUnrecoverableException
     public LdapProfile getDefaultLdapProfile( ) throws PwmUnrecoverableException
@@ -300,44 +283,22 @@ public class DomainConfig implements SettingReader
             );
             );
         } );
         } );
 
 
+
         private final LazySupplier.CheckedSupplier<PwmSecurityKey, PwmUnrecoverableException> pwmSecurityKey
         private final LazySupplier.CheckedSupplier<PwmSecurityKey, PwmUnrecoverableException> pwmSecurityKey
                 = LazySupplier.checked( () ->
                 = LazySupplier.checked( () ->
         {
         {
-            final PasswordData configValue = readSettingAsPassword( PwmSetting.PWM_SECURITY_KEY );
-
-            if ( configValue == null || configValue.getStringValue().isEmpty() )
-            {
-                final String errorMsg = "Security Key value is not configured, will generate temp value for use by runtime instance";
-                final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
-                LOGGER.warn( errorInfo::toDebugStr );
-                if ( tempInstanceKey == null )
-                {
-                    tempInstanceKey = new PwmSecurityKey( PwmRandom.getInstance().alphaNumericString( 1024 ) );
-                }
-                return tempInstanceKey;
-            }
-            else
-            {
-                final int minSecurityKeyLength = Integer.parseInt( readAppProperty( AppProperty.SECURITY_CONFIG_MIN_SECURITY_KEY_LENGTH ) );
-                if ( configValue.getStringValue().length() < minSecurityKeyLength )
-                {
-                    final String errorMsg = "Security Key must be greater than 32 characters in length";
-                    final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
-                    throw new PwmUnrecoverableException( errorInfo );
-                }
-
-                try
-                {
-                    return new PwmSecurityKey( configValue.getStringValue() );
-                }
-                catch ( final Exception e )
-                {
-                    final String errorMsg = "unexpected err0or generating Security Key crypto: " + e.getMessage();
-                    final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
-                    LOGGER.error( errorInfo::toDebugStr, e );
-                    throw new PwmUnrecoverableException( errorInfo );
-                }
-            }
+            final StringWriter keyData = new StringWriter();
+            keyData.append( domainID.stringValue() );
+            CollectionUtil.iteratorToStream( getStoredConfiguration().keys() )
+                    .filter( key -> Objects.equals( key.getDomainID(), getDomainID() ) )
+                    .sorted()
+                    .map( storedConfiguration::readStoredValue )
+                    .flatMap( Optional::stream )
+                    .forEach( value -> keyData.append( value.valueHash() ) );
+
+            final String hashedData = SecureEngine.hash( keyData.toString(), PwmHashAlgorithm.SHA512 );
+            final PwmSecurityKey domainKey = new PwmSecurityKey( hashedData );
+            return getAppConfig().getSecurityKey().add( domainKey );
         } );
         } );
     }
     }
 
 
@@ -412,4 +373,39 @@ public class DomainConfig implements SettingReader
     {
     {
         return getDomainID().toString();
         return getDomainID().toString();
     }
     }
+
+    public List<DataStorageMethod> getCrReadPreference()
+    {
+        return calculateMethods( PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE, PwmSetting.EDIRECTORY_USE_NMAS_RESPONSES );
+    }
+
+    public List<DataStorageMethod> getCrWritePreference()
+    {
+        return calculateMethods( PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE, PwmSetting.EDIRECTORY_STORE_NMAS_RESPONSES );
+    }
+
+    private List<DataStorageMethod> calculateMethods(
+            final PwmSetting setting,
+            final PwmSetting addNmasSetting
+    )
+    {
+        final List<DataStorageMethod> methods = new ArrayList<>( this.readGenericStorageLocations( setting ) );
+        if ( methods.size() == 1 && methods.get( 0 ) == DataStorageMethod.AUTO )
+        {
+            methods.clear();
+            if ( getAppConfig().hasDbConfigured() )
+            {
+                methods.add( DataStorageMethod.DB );
+            }
+            else
+            {
+                methods.add( DataStorageMethod.LDAP );
+            }
+        }
+        if ( this.readSettingAsBoolean( addNmasSetting ) )
+        {
+            methods.add( DataStorageMethod.NMAS );
+        }
+        return Collections.unmodifiableList( methods );
+    }
 }
 }

+ 41 - 29
server/src/main/java/password/pwm/config/PwmSetting.java

@@ -61,31 +61,37 @@ public enum PwmSetting
             "domain.list", PwmSettingSyntax.DOMAIN, PwmSettingCategory.DOMAINS ),
             "domain.list", PwmSettingSyntax.DOMAIN, PwmSettingCategory.DOMAINS ),
     DOMAIN_SYSTEM_ADMIN(
     DOMAIN_SYSTEM_ADMIN(
             "domain.system.adminDomain", PwmSettingSyntax.STRING, PwmSettingCategory.DOMAINS ),
             "domain.system.adminDomain", PwmSettingSyntax.STRING, PwmSettingCategory.DOMAINS ),
+    DOMAIN_DOMAIN_PATHS(
+            "domain.system.domainPaths", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.DOMAINS ),
 
 
     // application settings
     // application settings
     APP_PROPERTY_OVERRIDES(
     APP_PROPERTY_OVERRIDES(
             "pwm.appProperty.overrides", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.APPLICATION ),
             "pwm.appProperty.overrides", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.APPLICATION ),
+    HIDE_CONFIGURATION_HEALTH_WARNINGS(
+            "display.hideConfigHealthWarnings", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.APPLICATION ),
 
 
     // domain settings
     // domain settings
     PWM_SITE_URL(
     PWM_SITE_URL(
             "pwm.selfURL", PwmSettingSyntax.STRING, PwmSettingCategory.APPLICATION ),
             "pwm.selfURL", PwmSettingSyntax.STRING, PwmSettingCategory.APPLICATION ),
-    PUBLISH_STATS_ENABLE(
-            "pwm.publishStats.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.TELEMETRY ),
-    PUBLISH_STATS_SITE_DESCRIPTION(
-            "pwm.publishStats.siteDescription", PwmSettingSyntax.STRING, PwmSettingCategory.TELEMETRY ),
+
+
+    // domain settings
     URL_FORWARD(
     URL_FORWARD(
-            "pwm.forwardURL", PwmSettingSyntax.STRING, PwmSettingCategory.GENERAL ),
+            "pwm.forwardURL", PwmSettingSyntax.STRING, PwmSettingCategory.URL_SETTINGS ),
     URL_LOGOUT(
     URL_LOGOUT(
-            "pwm.logoutURL", PwmSettingSyntax.STRING, PwmSettingCategory.GENERAL ),
+            "pwm.logoutURL", PwmSettingSyntax.STRING, PwmSettingCategory.URL_SETTINGS ),
     URL_HOME(
     URL_HOME(
-            "pwm.homeURL", PwmSettingSyntax.STRING, PwmSettingCategory.GENERAL ),
+            "pwm.homeURL", PwmSettingSyntax.STRING, PwmSettingCategory.URL_SETTINGS ),
     URL_INTRO(
     URL_INTRO(
-            "pwm.introURL", PwmSettingSyntax.SELECT, PwmSettingCategory.GENERAL ),
-    IDLE_TIMEOUT_SECONDS(
-            "idleTimeoutSeconds", PwmSettingSyntax.DURATION, PwmSettingCategory.GENERAL ),
-    HIDE_CONFIGURATION_HEALTH_WARNINGS(
-            "display.hideConfigHealthWarnings", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.GENERAL ),
+            "pwm.introURL", PwmSettingSyntax.SELECT, PwmSettingCategory.URL_SETTINGS ),
+    DOMAIN_HOSTS(
+            "domain.hosts", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.URL_SETTINGS ),
 
 
+    // telemetry
+    PUBLISH_STATS_ENABLE(
+            "pwm.publishStats.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.TELEMETRY ),
+    PUBLISH_STATS_SITE_DESCRIPTION(
+            "pwm.publishStats.siteDescription", PwmSettingSyntax.STRING, PwmSettingCategory.TELEMETRY ),
 
 
     KNOWN_LOCALES(
     KNOWN_LOCALES(
             "knownLocales", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.LOCALIZATION ),
             "knownLocales", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.LOCALIZATION ),
@@ -129,6 +135,8 @@ public enum PwmSetting
             "display.idleTimeout", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.UI_FEATURES ),
             "display.idleTimeout", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.UI_FEATURES ),
     PASSWORD_SHOW_STRENGTH_METER(
     PASSWORD_SHOW_STRENGTH_METER(
             "password.showStrengthMeter", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.UI_FEATURES ),
             "password.showStrengthMeter", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.UI_FEATURES ),
+    IDLE_TIMEOUT_SECONDS(
+            "idleTimeoutSeconds", PwmSettingSyntax.DURATION, PwmSettingCategory.UI_FEATURES ),
     DISPLAY_CSS_CUSTOM_STYLE(
     DISPLAY_CSS_CUSTOM_STYLE(
             "display.css.customStyleLocation", PwmSettingSyntax.STRING, PwmSettingCategory.UI_WEB ),
             "display.css.customStyleLocation", PwmSettingSyntax.STRING, PwmSettingCategory.UI_WEB ),
     DISPLAY_CSS_CUSTOM_MOBILE_STYLE(
     DISPLAY_CSS_CUSTOM_MOBILE_STYLE(
@@ -144,7 +152,7 @@ public enum PwmSetting
 
 
     // change password
     // change password
     CHANGE_PASSWORD_PROFILE_LIST(
     CHANGE_PASSWORD_PROFILE_LIST(
-            "changePassword.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "changePassword.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     CHANGE_PASSWORD_ENABLE(
     CHANGE_PASSWORD_ENABLE(
             "changePassword.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.CHANGE_PASSWORD_SETTINGS ),
             "changePassword.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.CHANGE_PASSWORD_SETTINGS ),
     QUERY_MATCH_CHANGE_PASSWORD(
     QUERY_MATCH_CHANGE_PASSWORD(
@@ -180,7 +188,7 @@ public enum PwmSetting
     ACCOUNT_INFORMATION_ENABLED(
     ACCOUNT_INFORMATION_ENABLED(
             "display.accountInformation", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.ACCOUNT_INFO_SETTINGS ),
             "display.accountInformation", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.ACCOUNT_INFO_SETTINGS ),
     ACCOUNT_INFORMATION_PROFILE_LIST(
     ACCOUNT_INFORMATION_PROFILE_LIST(
-            "accountInfo.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "accountInfo.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     ACCOUNT_INFORMATION_QUERY_MATCH(
     ACCOUNT_INFORMATION_QUERY_MATCH(
             "accountInfo.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.ACCOUNT_INFO_PROFILE ),
             "accountInfo.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.ACCOUNT_INFO_PROFILE ),
     ACCOUNT_INFORMATION_HISTORY(
     ACCOUNT_INFORMATION_HISTORY(
@@ -193,7 +201,7 @@ public enum PwmSetting
 
 
     // delete info
     // delete info
     DELETE_ACCOUNT_PROFILE_LIST(
     DELETE_ACCOUNT_PROFILE_LIST(
-            "deleteAccount.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "deleteAccount.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     DELETE_ACCOUNT_ENABLE(
     DELETE_ACCOUNT_ENABLE(
             "deleteAccount.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.DELETE_ACCOUNT_SETTINGS ),
             "deleteAccount.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.DELETE_ACCOUNT_SETTINGS ),
     DELETE_ACCOUNT_PERMISSION(
     DELETE_ACCOUNT_PERMISSION(
@@ -291,7 +299,7 @@ public enum PwmSetting
 
 
     // ldap global settings
     // ldap global settings
     LDAP_PROFILE_LIST(
     LDAP_PROFILE_LIST(
-            "ldap.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "ldap.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     LDAP_IDLE_TIMEOUT(
     LDAP_IDLE_TIMEOUT(
             "ldap.idleTimeout", PwmSettingSyntax.DURATION, PwmSettingCategory.LDAP_GLOBAL ),
             "ldap.idleTimeout", PwmSettingSyntax.DURATION, PwmSettingCategory.LDAP_GLOBAL ),
     DEFAULT_OBJECT_CLASSES(
     DEFAULT_OBJECT_CLASSES(
@@ -312,7 +320,7 @@ public enum PwmSetting
 
 
     // New multiple email settings
     // New multiple email settings
     EMAIL_SERVERS(
     EMAIL_SERVERS(
-            "email.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.APPLICATION ),
+            "email.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_SYSTEM ),
     EMAIL_SERVER_ADDRESS(
     EMAIL_SERVER_ADDRESS(
             "email.smtp.address", PwmSettingSyntax.STRING, PwmSettingCategory.EMAIL_SERVERS ),
             "email.smtp.address", PwmSettingSyntax.STRING, PwmSettingCategory.EMAIL_SERVERS ),
     EMAIL_SERVER_TYPE(
     EMAIL_SERVER_TYPE(
@@ -451,7 +459,7 @@ public enum PwmSetting
     PASSWORD_POLICY_CASE_SENSITIVITY(
     PASSWORD_POLICY_CASE_SENSITIVITY(
             "password.policy.caseSensitivity", PwmSettingSyntax.SELECT, PwmSettingCategory.PASSWORD_GLOBAL ),
             "password.policy.caseSensitivity", PwmSettingSyntax.SELECT, PwmSettingCategory.PASSWORD_GLOBAL ),
     PASSWORD_PROFILE_LIST(
     PASSWORD_PROFILE_LIST(
-            "password.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "password.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
 
 
 
 
     // wordlist settings
     // wordlist settings
@@ -606,7 +614,7 @@ public enum PwmSetting
     INTRUDER_ENABLE(
     INTRUDER_ENABLE(
             "intruder.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.INTRUDER_SETTINGS ),
             "intruder.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.INTRUDER_SETTINGS ),
     INTRUDER_STORAGE_METHOD(
     INTRUDER_STORAGE_METHOD(
-            "intruder.storageMethod", PwmSettingSyntax.SELECT, PwmSettingCategory.INTRUDER_SETTINGS ),
+            "intruder.storageMethod", PwmSettingSyntax.SELECT, PwmSettingCategory.INTRUDER_SYSTEM_SETTINGS ),
     SECURITY_SIMULATE_LDAP_BAD_PASSWORD(
     SECURITY_SIMULATE_LDAP_BAD_PASSWORD(
             "security.ldap.simulateBadPassword", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.INTRUDER_SETTINGS ),
             "security.ldap.simulateBadPassword", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.INTRUDER_SETTINGS ),
 
 
@@ -653,7 +661,7 @@ public enum PwmSetting
 
 
     // OTP
     // OTP
     OTP_PROFILE_LIST(
     OTP_PROFILE_LIST(
-            "otp.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "otp.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     OTP_SETUP_USER_PERMISSION(
     OTP_SETUP_USER_PERMISSION(
             "otp.secret.allowSetup.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.OTP_PROFILE ),
             "otp.secret.allowSetup.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.OTP_PROFILE ),
     OTP_ALLOW_SETUP(
     OTP_ALLOW_SETUP(
@@ -741,7 +749,7 @@ public enum PwmSetting
 
 
     // challenge policy profile
     // challenge policy profile
     CHALLENGE_PROFILE_LIST(
     CHALLENGE_PROFILE_LIST(
-            "challenge.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "challenge.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     CHALLENGE_POLICY_QUERY_MATCH(
     CHALLENGE_POLICY_QUERY_MATCH(
             "challenge.policy.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.CHALLENGE_POLICY ),
             "challenge.policy.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.CHALLENGE_POLICY ),
     CHALLENGE_RANDOM_CHALLENGES(
     CHALLENGE_RANDOM_CHALLENGES(
@@ -778,7 +786,7 @@ public enum PwmSetting
 
 
     // recovery profile
     // recovery profile
     RECOVERY_PROFILE_LIST(
     RECOVERY_PROFILE_LIST(
-            "recovery.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "recovery.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     RECOVERY_PROFILE_QUERY_MATCH(
     RECOVERY_PROFILE_QUERY_MATCH(
             "recovery.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.RECOVERY_DEF ),
             "recovery.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.RECOVERY_DEF ),
     RECOVERY_VERIFICATION_METHODS(
     RECOVERY_VERIFICATION_METHODS(
@@ -844,7 +852,7 @@ public enum PwmSetting
             "newUser.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.NEWUSER_SETTINGS ),
             "newUser.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.NEWUSER_SETTINGS ),
 
 
     NEWUSER_PROFILE_LIST(
     NEWUSER_PROFILE_LIST(
-            "newUser.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "newUser.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
 
 
     NEWUSER_FORM(
     NEWUSER_FORM(
             "newUser.form", PwmSettingSyntax.FORM, PwmSettingCategory.NEWUSER_PROFILE ),
             "newUser.form", PwmSettingSyntax.FORM, PwmSettingCategory.NEWUSER_PROFILE ),
@@ -917,7 +925,7 @@ public enum PwmSetting
             "activateUser.searchFilter", PwmSettingSyntax.STRING, PwmSettingCategory.ACTIVATION_SETTINGS ),
             "activateUser.searchFilter", PwmSettingSyntax.STRING, PwmSettingCategory.ACTIVATION_SETTINGS ),
 
 
     ACTIVATE_USER_PROFILE_LIST(
     ACTIVATE_USER_PROFILE_LIST(
-            "activateUser.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "activateUser.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
 
 
     ACTIVATE_USER_QUERY_MATCH(
     ACTIVATE_USER_QUERY_MATCH(
             "activateUser.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.ACTIVATION_PROFILE ),
             "activateUser.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.ACTIVATION_PROFILE ),
@@ -936,7 +944,7 @@ public enum PwmSetting
     UPDATE_PROFILE_ENABLE(
     UPDATE_PROFILE_ENABLE(
             "updateAttributes.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.UPDATE_SETTINGS ),
             "updateAttributes.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.UPDATE_SETTINGS ),
     UPDATE_PROFILE__PROFILE_LIST(
     UPDATE_PROFILE__PROFILE_LIST(
-            "updateAttributes.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "updateAttributes.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     UPDATE_PROFILE_QUERY_MATCH(
     UPDATE_PROFILE_QUERY_MATCH(
             "updateAttributes.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.UPDATE_PROFILE ),
             "updateAttributes.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.UPDATE_PROFILE ),
     UPDATE_PROFILE_WRITE_ATTRIBUTES(
     UPDATE_PROFILE_WRITE_ATTRIBUTES(
@@ -980,7 +988,7 @@ public enum PwmSetting
     PEOPLE_SEARCH_PUBLIC_PROFILE(
     PEOPLE_SEARCH_PUBLIC_PROFILE(
             "peopleSearch.public.profile", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH_SETTINGS ),
             "peopleSearch.public.profile", PwmSettingSyntax.STRING, PwmSettingCategory.PEOPLE_SEARCH_SETTINGS ),
     PEOPLESEARCH_PROFILE_LIST(
     PEOPLESEARCH_PROFILE_LIST(
-            "peopleSearch.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "peopleSearch.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     PEOPLE_SEARCH_QUERY_MATCH(
     PEOPLE_SEARCH_QUERY_MATCH(
             "peopleSearch.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.PEOPLE_SEARCH_PROFILE ),
             "peopleSearch.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.PEOPLE_SEARCH_PROFILE ),
     PEOPLE_SEARCH_SEARCH_FORM(
     PEOPLE_SEARCH_SEARCH_FORM(
@@ -1059,7 +1067,7 @@ public enum PwmSetting
     HELPDESK_ENABLE(
     HELPDESK_ENABLE(
             "helpdesk.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.HELPDESK_SETTINGS ),
             "helpdesk.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.HELPDESK_SETTINGS ),
     HELPDESK_PROFILE_LIST(
     HELPDESK_PROFILE_LIST(
-            "helpdesk.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL ),
+            "helpdesk.profile.list", PwmSettingSyntax.PROFILE, PwmSettingCategory.INTERNAL_DOMAIN ),
     HELPDESK_PROFILE_QUERY_MATCH(
     HELPDESK_PROFILE_QUERY_MATCH(
             "helpdesk.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.HELPDESK_BASE ),
             "helpdesk.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.HELPDESK_BASE ),
     HELPDESK_SEARCH_FORM(
     HELPDESK_SEARCH_FORM(
@@ -1451,9 +1459,13 @@ public enum PwmSetting
 
 
         static <T> T referenceForTempleSet(
         static <T> T referenceForTempleSet(
                 final List<TemplateSetReference<T>> templateSetReferences,
                 final List<TemplateSetReference<T>> templateSetReferences,
-                final PwmSettingTemplateSet pwmSettingTemplate
+                final PwmSettingTemplateSet pwmSettingTemplateSet
         )
         )
         {
         {
+            final PwmSettingTemplateSet effectiveTemplateSet = pwmSettingTemplateSet == null
+                    ? PwmSettingTemplateSet.getDefault()
+                    : pwmSettingTemplateSet;
+
             if ( templateSetReferences == null || templateSetReferences.isEmpty() )
             if ( templateSetReferences == null || templateSetReferences.isEmpty() )
             {
             {
                 throw new IllegalStateException( "templateSetReferences can not be null" );
                 throw new IllegalStateException( "templateSetReferences can not be null" );
@@ -1469,7 +1481,7 @@ public enum PwmSetting
                 for ( final TemplateSetReference<T> templateSetReference : templateSetReferences )
                 for ( final TemplateSetReference<T> templateSetReference : templateSetReferences )
                 {
                 {
                     final Set<PwmSettingTemplate> temporarySet = CollectionUtil.copiedEnumSet( templateSetReference.getSettingTemplates(), PwmSettingTemplate.class );
                     final Set<PwmSettingTemplate> temporarySet = CollectionUtil.copiedEnumSet( templateSetReference.getSettingTemplates(), PwmSettingTemplate.class );
-                    temporarySet.retainAll( pwmSettingTemplate.getTemplates() );
+                    temporarySet.retainAll( effectiveTemplateSet.getTemplates() );
                     final int matchCount = temporarySet.size();
                     final int matchCount = temporarySet.size();
                     if ( matchCount == matchCountExamSize )
                     if ( matchCount == matchCountExamSize )
                     {
                     {

+ 6 - 4
server/src/main/java/password/pwm/config/PwmSettingCategory.java

@@ -78,9 +78,9 @@ public enum PwmSettingCategory
     NODES( SYSTEM ),
     NODES( SYSTEM ),
 
 
     DOMAIN( SETTINGS ),
     DOMAIN( SETTINGS ),
-    GENERAL( DOMAIN ),
-    LOCALIZATION( DOMAIN ),
-    TELEMETRY( DOMAIN ),
+    URL_SETTINGS( SETTINGS ),
+    LOCALIZATION( SETTINGS ),
+    TELEMETRY( SETTINGS ),
 
 
     AUDITING( SETTINGS ),
     AUDITING( SETTINGS ),
     AUDIT_CONFIG( AUDITING ),
     AUDIT_CONFIG( AUDITING ),
@@ -91,6 +91,7 @@ public enum PwmSettingCategory
     CAPTCHA( SETTINGS ),
     CAPTCHA( SETTINGS ),
 
 
     INTRUDER( SETTINGS ),
     INTRUDER( SETTINGS ),
+    INTRUDER_SYSTEM_SETTINGS( INTRUDER ),
     INTRUDER_SETTINGS( INTRUDER ),
     INTRUDER_SETTINGS( INTRUDER ),
     INTRUDER_TIMEOUTS( INTRUDER ),
     INTRUDER_TIMEOUTS( INTRUDER ),
 
 
@@ -201,7 +202,8 @@ public enum PwmSettingCategory
     DELETE_ACCOUNT_SETTINGS( DELETE_ACCOUNT ),
     DELETE_ACCOUNT_SETTINGS( DELETE_ACCOUNT ),
     DELETE_ACCOUNT_PROFILE( DELETE_ACCOUNT ),
     DELETE_ACCOUNT_PROFILE( DELETE_ACCOUNT ),
 
 
-    INTERNAL( SETTINGS ),;
+    INTERNAL_DOMAIN( SETTINGS ),
+    INTERNAL_SYSTEM( SETTINGS ),;
 
 
     private static final Comparator<PwmSettingCategory> MENU_LOCATION_COMPARATOR = Comparator.comparing(
     private static final Comparator<PwmSettingCategory> MENU_LOCATION_COMPARATOR = Comparator.comparing(
             ( pwmSettingCategory ) -> pwmSettingCategory.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE ) );
             ( pwmSettingCategory ) -> pwmSettingCategory.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE ) );

+ 5 - 0
server/src/main/java/password/pwm/config/PwmSettingFlag.java

@@ -37,6 +37,11 @@ public enum PwmSettingFlag
     /* No Default - Makes the setting UI act as if there is not a default to reset to */
     /* No Default - Makes the setting UI act as if there is not a default to reset to */
     NoDefault,
     NoDefault,
 
 
+    /* Only shown if multi-domain is enabled */
+    MultiDomain,
+
+    ReloadEditorOnModify,
+
     Select_AllowUserInput,
     Select_AllowUserInput,
 
 
     Permission_HideGroups,
     Permission_HideGroups,

+ 5 - 0
server/src/main/java/password/pwm/config/PwmSettingMetaDataReader.java

@@ -167,6 +167,11 @@ public class PwmSettingMetaDataReader
         }
         }
     }
     }
 
 
+    /**
+     * Not required for normal operation, but executing this gets all the enum values poopulated form XML source.  If run prior to users accessing the settings
+     * module (particularly the config editor) it will increase the initial load performance significantly.  There are no side effects to calling this operation
+     * other than cache population.
+     */
     public static void initCache()
     public static void initCache()
     {
     {
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();

+ 53 - 0
server/src/main/java/password/pwm/config/StoredSettingReader.java

@@ -20,9 +20,11 @@
 
 
 package password.pwm.config;
 package password.pwm.config;
 
 
+import password.pwm.AppProperty;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.PrivateKeyCertificate;
 import password.pwm.bean.PrivateKeyCertificate;
+import password.pwm.config.option.DataStorageMethod;
 import password.pwm.config.profile.Profile;
 import password.pwm.config.profile.Profile;
 import password.pwm.config.profile.ProfileDefinition;
 import password.pwm.config.profile.ProfileDefinition;
 import password.pwm.config.stored.StoredConfigKey;
 import password.pwm.config.stored.StoredConfigKey;
@@ -38,16 +40,21 @@ import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.config.value.data.NamedSecretData;
 import password.pwm.config.value.data.NamedSecretData;
 import password.pwm.config.value.data.RemoteWebServiceConfiguration;
 import password.pwm.config.value.data.RemoteWebServiceConfiguration;
 import password.pwm.config.value.data.UserPermission;
 import password.pwm.config.value.data.UserPermission;
+import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.secure.PwmRandom;
+import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.InvocationTargetException;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
@@ -177,6 +184,16 @@ public class StoredSettingReader implements SettingReader
         return ValueTypeConverter.valueToNamedPassword( readSetting( setting ) );
         return ValueTypeConverter.valueToNamedPassword( readSetting( setting ) );
     }
     }
 
 
+    public List<DataStorageMethod> readGenericStorageLocations( final PwmSetting setting )
+    {
+        final String input = readSettingAsString( setting );
+
+        return Arrays.stream( input.split( "-" ) )
+                .map( s ->  JavaHelper.readEnumFromString( DataStorageMethod.class, s ) )
+                .flatMap( Optional::stream )
+                .collect( Collectors.toUnmodifiableList() );
+    }
+
     public PrivateKeyCertificate readSettingAsPrivateKey( final PwmSetting setting )
     public PrivateKeyCertificate readSettingAsPrivateKey( final PwmSetting setting )
     {
     {
         Objects.requireNonNull( setting );
         Objects.requireNonNull( setting );
@@ -342,4 +359,40 @@ public class StoredSettingReader implements SettingReader
         dataCache.customText.put( key, localizedMap );
         dataCache.customText.put( key, localizedMap );
         return localizedMap;
         return localizedMap;
     }
     }
+
+    public PwmSecurityKey readSecurityKey( final PwmSetting pwmSetting, final AppConfig appConfig )
+            throws PwmUnrecoverableException
+    {
+        final PasswordData configValue = readSettingAsPassword( pwmSetting );
+
+        if ( configValue == null || configValue.getStringValue().isEmpty() )
+        {
+            final String errorMsg = "Security Key value is not configured, will generate temp value for use by runtime instance";
+            final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
+            LOGGER.warn( errorInfo::toDebugStr );
+            return new PwmSecurityKey( PwmRandom.getInstance().alphaNumericString( 1024 ) );
+        }
+        else
+        {
+            final int minSecurityKeyLength = Integer.parseInt( appConfig.readAppProperty( AppProperty.SECURITY_CONFIG_MIN_SECURITY_KEY_LENGTH ) );
+            if ( configValue.getStringValue().length() < minSecurityKeyLength )
+            {
+                final String errorMsg = "Security Key must be greater than 32 characters in length";
+                final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
+                throw new PwmUnrecoverableException( errorInfo );
+            }
+
+            try
+            {
+                return new PwmSecurityKey( configValue.getStringValue() );
+            }
+            catch ( final Exception e )
+            {
+                final String errorMsg = "unexpected error generating Security Key crypto: " + e.getMessage();
+                final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_INVALID_SECURITY_KEY, errorMsg );
+                LOGGER.error( errorInfo::toDebugStr, e );
+                throw new PwmUnrecoverableException( errorInfo );
+            }
+        }
+    }
 }
 }

+ 18 - 6
server/src/main/java/password/pwm/config/function/UserMatchViewerFunction.java

@@ -78,7 +78,12 @@ public class UserMatchViewerFunction implements SettingUIFunction
 
 
         final Instant startSearchTime = Instant.now();
         final Instant startSearchTime = Instant.now();
         final int maxResultSize = Integer.parseInt( pwmDomain.getConfig().readAppProperty( AppProperty.CONFIG_EDITOR_USER_PERMISSION_MATCH_LIMIT ) );
         final int maxResultSize = Integer.parseInt( pwmDomain.getConfig().readAppProperty( AppProperty.CONFIG_EDITOR_USER_PERMISSION_MATCH_LIMIT ) );
-        final Collection<UserIdentity> users = discoverMatchingUsers( pwmDomain, maxResultSize, storedConfiguration.newStoredConfiguration(), key );
+        final Collection<UserIdentity> users = discoverMatchingUsers(
+                pwmRequest.getLabel(),
+                pwmDomain,
+                maxResultSize,
+                storedConfiguration.newStoredConfiguration(),
+                key );
         final TimeDuration searchDuration = TimeDuration.fromCurrent( startSearchTime );
         final TimeDuration searchDuration = TimeDuration.fromCurrent( startSearchTime );
 
 
         final String message = LocaleHelper.getLocalizedMessage(
         final String message = LocaleHelper.getLocalizedMessage(
@@ -96,6 +101,7 @@ public class UserMatchViewerFunction implements SettingUIFunction
     }
     }
 
 
     public List<UserIdentity> discoverMatchingUsers(
     public List<UserIdentity> discoverMatchingUsers(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final int maxResultSize,
             final int maxResultSize,
             final StoredConfiguration storedConfiguration,
             final StoredConfiguration storedConfiguration,
@@ -109,7 +115,7 @@ public class UserMatchViewerFunction implements SettingUIFunction
         final List<UserPermission> permissions = ValueTypeConverter.valueToUserPermissions( storedValue );
         final List<UserPermission> permissions = ValueTypeConverter.valueToUserPermissions( storedValue );
         final PwmDomain tempDomain = tempApplication.domains().get( key.getDomainID() );
         final PwmDomain tempDomain = tempApplication.domains().get( key.getDomainID() );
 
 
-        validateUserPermissionLdapValues( tempDomain, permissions );
+        validateUserPermissionLdapValues( sessionLabel, tempDomain, permissions );
 
 
         final int maxSearchSeconds = Integer.parseInt( pwmDomain.getConfig().readAppProperty( AppProperty.CONFIG_EDITOR_USER_PERMISSION_TIMEOUT_SECONDS ) );
         final int maxSearchSeconds = Integer.parseInt( pwmDomain.getConfig().readAppProperty( AppProperty.CONFIG_EDITOR_USER_PERMISSION_TIMEOUT_SECONDS ) );
         final TimeDuration maxSearchTime = TimeDuration.of( maxSearchSeconds, TimeDuration.Unit.SECONDS );
         final TimeDuration maxSearchTime = TimeDuration.of( maxSearchSeconds, TimeDuration.Unit.SECONDS );
@@ -121,6 +127,7 @@ public class UserMatchViewerFunction implements SettingUIFunction
     }
     }
 
 
     private static void validateUserPermissionLdapValues(
     private static void validateUserPermissionLdapValues(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final List<UserPermission> permissions
             final List<UserPermission> permissions
     )
     )
@@ -132,18 +139,23 @@ public class UserMatchViewerFunction implements SettingUIFunction
             {
             {
                 if ( userPermission.getLdapBase() != null && !userPermission.getLdapBase().isEmpty() )
                 if ( userPermission.getLdapBase() != null && !userPermission.getLdapBase().isEmpty() )
                 {
                 {
-                    testIfLdapDNIsValid( pwmDomain, userPermission.getLdapBase(), userPermission.getLdapProfileID() );
+                    testIfLdapDNIsValid( sessionLabel, pwmDomain, userPermission.getLdapBase(), userPermission.getLdapProfileID() );
                 }
                 }
             }
             }
             else if ( userPermission.getType() == UserPermissionType.ldapGroup )
             else if ( userPermission.getType() == UserPermissionType.ldapGroup )
             {
             {
-                testIfLdapDNIsValid( pwmDomain, userPermission.getLdapBase(), userPermission.getLdapProfileID() );
+                testIfLdapDNIsValid( sessionLabel, pwmDomain, userPermission.getLdapBase(), userPermission.getLdapProfileID() );
             }
             }
         }
         }
     }
     }
 
 
 
 
-    private static void testIfLdapDNIsValid( final PwmDomain pwmDomain, final String baseDN, final String profileID )
+    private static void testIfLdapDNIsValid(
+            final SessionLabel sessionLabel,
+            final PwmDomain pwmDomain,
+            final String baseDN,
+            final String profileID
+    )
             throws PwmOperationalException, PwmUnrecoverableException
             throws PwmOperationalException, PwmUnrecoverableException
     {
     {
         final Set<String> profileIDsToTest = new LinkedHashSet<>();
         final Set<String> profileIDsToTest = new LinkedHashSet<>();
@@ -167,7 +179,7 @@ public class UserMatchViewerFunction implements SettingUIFunction
             ChaiEntry chaiEntry = null;
             ChaiEntry chaiEntry = null;
             try
             try
             {
             {
-                final ChaiProvider proxiedProvider = pwmDomain.getProxyChaiProvider( loopID );
+                final ChaiProvider proxiedProvider = pwmDomain.getProxyChaiProvider( sessionLabel, loopID );
                 chaiEntry = proxiedProvider.getEntryFactory().newChaiEntry( baseDN );
                 chaiEntry = proxiedProvider.getEntryFactory().newChaiEntry( baseDN );
             }
             }
             catch ( final Exception e )
             catch ( final Exception e )

+ 20 - 11
server/src/main/java/password/pwm/config/profile/LdapProfile.java

@@ -27,6 +27,7 @@ import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmDomain;
 import password.pwm.PwmDomain;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.stored.StoredConfiguration;
@@ -60,6 +61,7 @@ public class LdapProfile extends AbstractProfile implements Profile
     }
     }
 
 
     public Map<String, String> getSelectableContexts(
     public Map<String, String> getSelectableContexts(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain
             final PwmDomain pwmDomain
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
@@ -71,13 +73,14 @@ public class LdapProfile extends AbstractProfile implements Profile
         {
         {
             final String dn = entry.getKey();
             final String dn = entry.getKey();
             final String label = entry.getValue();
             final String label = entry.getValue();
-            final String canonicalDN = readCanonicalDN( pwmDomain, dn );
+            final String canonicalDN = readCanonicalDN( sessionLabel, pwmDomain, dn );
             canonicalValues.put( canonicalDN, label );
             canonicalValues.put( canonicalDN, label );
         }
         }
         return Collections.unmodifiableMap( canonicalValues );
         return Collections.unmodifiableMap( canonicalValues );
     }
     }
 
 
     public List<String> getRootContexts(
     public List<String> getRootContexts(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain
             final PwmDomain pwmDomain
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
@@ -86,7 +89,7 @@ public class LdapProfile extends AbstractProfile implements Profile
         final List<String> canonicalValues = new ArrayList<>();
         final List<String> canonicalValues = new ArrayList<>();
         for ( final String dn : rawValues )
         for ( final String dn : rawValues )
         {
         {
-            final String canonicalDN = readCanonicalDN( pwmDomain, dn );
+            final String canonicalDN = readCanonicalDN( sessionLabel, pwmDomain, dn );
             canonicalValues.add( canonicalDN );
             canonicalValues.add( canonicalDN );
         }
         }
         return Collections.unmodifiableList( canonicalValues );
         return Collections.unmodifiableList( canonicalValues );
@@ -112,10 +115,10 @@ public class LdapProfile extends AbstractProfile implements Profile
         return configUsernameAttr != null && configUsernameAttr.length() > 0 ? configUsernameAttr : ldapNamingAttribute;
         return configUsernameAttr != null && configUsernameAttr.length() > 0 ? configUsernameAttr : ldapNamingAttribute;
     }
     }
 
 
-    public ChaiProvider getProxyChaiProvider( final PwmDomain pwmDomain ) throws PwmUnrecoverableException
+    public ChaiProvider getProxyChaiProvider( final SessionLabel sessionLabel, final PwmDomain pwmDomain ) throws PwmUnrecoverableException
     {
     {
         verifyIsEnabled();
         verifyIsEnabled();
-        return pwmDomain.getProxyChaiProvider( this.getIdentifier() );
+        return pwmDomain.getProxyChaiProvider( sessionLabel, this.getIdentifier() );
     }
     }
 
 
     @Override
     @Override
@@ -131,6 +134,7 @@ public class LdapProfile extends AbstractProfile implements Profile
     }
     }
 
 
     public String readCanonicalDN(
     public String readCanonicalDN(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final String dnValue
             final String dnValue
     )
     )
@@ -163,7 +167,7 @@ public class LdapProfile extends AbstractProfile implements Profile
         {
         {
             try
             try
             {
             {
-                final ChaiProvider chaiProvider = this.getProxyChaiProvider( pwmDomain );
+                final ChaiProvider chaiProvider = this.getProxyChaiProvider( sessionLabel, pwmDomain );
                 final ChaiEntry chaiEntry = chaiProvider.getEntryFactory().newChaiEntry( dnValue );
                 final ChaiEntry chaiEntry = chaiProvider.getEntryFactory().newChaiEntry( dnValue );
                 canonicalValue = chaiEntry.readCanonicalDN();
                 canonicalValue = chaiEntry.readCanonicalDN();
 
 
@@ -190,23 +194,28 @@ public class LdapProfile extends AbstractProfile implements Profile
         return canonicalValue;
         return canonicalValue;
     }
     }
 
 
-    public UserIdentity getTestUser( final PwmDomain pwmDomain ) throws PwmUnrecoverableException
+    public UserIdentity getTestUser( final SessionLabel sessionLabel, final PwmDomain pwmDomain ) throws PwmUnrecoverableException
     {
     {
-        return readUserIdentity( pwmDomain, PwmSetting.LDAP_TEST_USER_DN );
+        return readUserIdentity( sessionLabel, pwmDomain, PwmSetting.LDAP_TEST_USER_DN );
     }
     }
 
 
-    public UserIdentity getProxyUser( final PwmDomain pwmDomain ) throws PwmUnrecoverableException
+    public UserIdentity getProxyUser( final SessionLabel sessionLabel, final PwmDomain pwmDomain ) throws PwmUnrecoverableException
     {
     {
-        return readUserIdentity( pwmDomain, PwmSetting.LDAP_PROXY_USER_DN );
+        return readUserIdentity( sessionLabel, pwmDomain, PwmSetting.LDAP_PROXY_USER_DN );
     }
     }
 
 
-    private UserIdentity readUserIdentity( final PwmDomain pwmDomain, final PwmSetting pwmSetting ) throws PwmUnrecoverableException
+    private UserIdentity readUserIdentity(
+            final SessionLabel sessionLabel,
+            final PwmDomain pwmDomain,
+            final PwmSetting pwmSetting
+    )
+            throws PwmUnrecoverableException
     {
     {
         final String testUserDN = this.readSettingAsString( pwmSetting );
         final String testUserDN = this.readSettingAsString( pwmSetting );
 
 
         if ( StringUtil.notEmpty( testUserDN ) )
         if ( StringUtil.notEmpty( testUserDN ) )
         {
         {
-            return UserIdentity.create( testUserDN, this.getIdentifier(), pwmDomain.getDomainID() ).canonicalized( pwmDomain.getPwmApplication() );
+            return UserIdentity.create( testUserDN, this.getIdentifier(), pwmDomain.getDomainID() ).canonicalized( sessionLabel, pwmDomain.getPwmApplication() );
         }
         }
 
 
         return null;
         return null;

+ 13 - 5
server/src/main/java/password/pwm/config/profile/NewUserProfile.java

@@ -27,6 +27,7 @@ import password.pwm.AppProperty;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.PwmDomain;
 import password.pwm.PwmDomain;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
@@ -34,6 +35,7 @@ import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.PwmRequestContext;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.util.password.PasswordUtility;
@@ -70,7 +72,13 @@ public class NewUserProfile extends AbstractProfile implements Profile
         return value != null && !value.isEmpty() ? value : this.getIdentifier();
         return value != null && !value.isEmpty() ? value : this.getIdentifier();
     }
     }
 
 
-    public PwmPasswordPolicy getNewUserPasswordPolicy( final PwmDomain pwmDomain, final Locale userLocale )
+    public PwmPasswordPolicy getNewUserPasswordPolicy( final PwmRequestContext pwmRequestContext )
+            throws PwmUnrecoverableException
+    {
+        return getNewUserPasswordPolicy( pwmRequestContext.getSessionLabel(), pwmRequestContext.getPwmDomain(), pwmRequestContext.getLocale() );
+    }
+
+    public PwmPasswordPolicy getNewUserPasswordPolicy( final SessionLabel sessionLabel, final PwmDomain pwmDomain, final Locale locale )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         final DomainConfig domainConfig = pwmDomain.getConfig();
         final DomainConfig domainConfig = pwmDomain.getConfig();
@@ -81,7 +89,7 @@ public class NewUserProfile extends AbstractProfile implements Profile
             newUserPasswordPolicyCache.clear();
             newUserPasswordPolicyCache.clear();
         }
         }
 
 
-        final PwmPasswordPolicy cachedPolicy = newUserPasswordPolicyCache.get( userLocale );
+        final PwmPasswordPolicy cachedPolicy = newUserPasswordPolicyCache.get( locale );
         if ( cachedPolicy != null )
         if ( cachedPolicy != null )
         {
         {
             return cachedPolicy;
             return cachedPolicy;
@@ -131,7 +139,7 @@ public class NewUserProfile extends AbstractProfile implements Profile
             {
             {
                 try
                 try
                 {
                 {
-                    final ChaiProvider chaiProvider = pwmDomain.getProxyChaiProvider( ldapProfile.getIdentifier() );
+                    final ChaiProvider chaiProvider = pwmDomain.getProxyChaiProvider( sessionLabel, ldapProfile.getIdentifier() );
                     final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser( lookupDN );
                     final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser( lookupDN );
                     final UserIdentity userIdentity = UserIdentity.create( lookupDN, ldapProfile.getIdentifier(), pwmDomain.getDomainID() );
                     final UserIdentity userIdentity = UserIdentity.create( lookupDN, ldapProfile.getIdentifier(), pwmDomain.getDomainID() );
                     thePolicy = PasswordUtility.readPasswordPolicyForUser( pwmDomain, null, userIdentity, chaiUser );
                     thePolicy = PasswordUtility.readPasswordPolicyForUser( pwmDomain, null, userIdentity, chaiUser );
@@ -142,7 +150,7 @@ public class NewUserProfile extends AbstractProfile implements Profile
                 }
                 }
             }
             }
         }
         }
-        newUserPasswordPolicyCache.put( userLocale, thePolicy );
+        newUserPasswordPolicyCache.put( locale, thePolicy );
         return thePolicy;
         return thePolicy;
     }
     }
 
 
@@ -190,7 +198,7 @@ public class NewUserProfile extends AbstractProfile implements Profile
                         {
                         {
                                 "configured ldap profile for new user profile is invalid.  check setting "
                                 "configured ldap profile for new user profile is invalid.  check setting "
                                         + PwmSetting.NEWUSER_LDAP_PROFILE.toMenuLocationDebug( this.getIdentifier(), PwmConstants.DEFAULT_LOCALE ),
                                         + PwmSetting.NEWUSER_LDAP_PROFILE.toMenuLocationDebug( this.getIdentifier(), PwmConstants.DEFAULT_LOCALE ),
-                                }
+                        }
                 ) );
                 ) );
             }
             }
             return ldapProfile;
             return ldapProfile;

+ 1 - 1
server/src/main/java/password/pwm/config/profile/PwmPasswordPolicy.java

@@ -321,7 +321,7 @@ public class PwmPasswordPolicy implements Profile, Serializable
         }
         }
 
 
         final Locale resolvedLocale = LocaleHelper.localeResolver( locale, policyMetaData.getRuleText().keySet() );
         final Locale resolvedLocale = LocaleHelper.localeResolver( locale, policyMetaData.getRuleText().keySet() );
-        return Optional.of( policyMetaData.getRuleText().get( resolvedLocale ) );
+        return Optional.ofNullable( policyMetaData.getRuleText().get( resolvedLocale ) );
     }
     }
 
 
     public PwmPasswordPolicy merge( final PwmPasswordPolicy otherPolicy )
     public PwmPasswordPolicy merge( final PwmPasswordPolicy otherPolicy )

+ 2 - 2
server/src/main/java/password/pwm/config/stored/ConfigurationCleaner.java

@@ -44,7 +44,7 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
 
 
-class ConfigurationCleaner
+public class ConfigurationCleaner
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( ConfigurationCleaner.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( ConfigurationCleaner.class );
 
 
@@ -56,7 +56,7 @@ class ConfigurationCleaner
             new RemoveSuperfluousProfileSettings(),
             new RemoveSuperfluousProfileSettings(),
             new RemoveDefaultSettings() );
             new RemoveDefaultSettings() );
 
 
-    static void postProcessStoredConfig(
+    public static void postProcessStoredConfig(
             final StoredConfigurationModifier storedConfiguration
             final StoredConfigurationModifier storedConfiguration
     )
     )
     {
     {

+ 3 - 2
server/src/main/java/password/pwm/config/stored/ConfigurationReader.java

@@ -35,6 +35,7 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
@@ -243,7 +244,7 @@ public class ConfigurationReader
             }
             }
         }
         }
 
 
-        if ( pwmApplication != null && pwmApplication.getAuditManager() != null )
+        if ( pwmApplication != null && pwmApplication.getAuditService() != null )
         {
         {
             auditModifiedSettings( pwmApplication, storedConfiguration, sessionLabel );
             auditModifiedSettings( pwmApplication, storedConfiguration, sessionLabel );
         }
         }
@@ -285,7 +286,7 @@ public class ConfigurationReader
             final String finalMsg = modifyMessage;
             final String finalMsg = modifyMessage;
             LOGGER.trace( () -> "sending audit notice: " + finalMsg );
             LOGGER.trace( () -> "sending audit notice: " + finalMsg );
 
 
-            pwmApplication.getAuditManager().submit( sessionLabel, new AuditRecordFactory( pwmApplication ).createUserAuditRecord(
+            AuditServiceClient.submit( pwmApplication, sessionLabel, AuditRecordFactory.make( sessionLabel, pwmApplication ).createUserAuditRecord(
                     AuditEvent.MODIFY_CONFIGURATION,
                     AuditEvent.MODIFY_CONFIGURATION,
                     userIdentity,
                     userIdentity,
                     sessionLabel,
                     sessionLabel,

+ 4 - 2
server/src/main/java/password/pwm/config/stored/StoredConfigXmlSerializer.java

@@ -22,6 +22,7 @@ package password.pwm.config.stored;
 
 
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingFlag;
 import password.pwm.config.PwmSettingFlag;
@@ -72,6 +73,7 @@ public class StoredConfigXmlSerializer implements StoredConfigSerializer
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( StoredConfigXmlSerializer.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( StoredConfigXmlSerializer.class );
     private static final String XML_FORMAT_VERSION = "5";
     private static final String XML_FORMAT_VERSION = "5";
+    private static final SessionLabel SESSION_LABEL = SessionLabel.SYSTEM_LABEL;
     private static final boolean ENABLE_PERF_LOGGING = false;
     private static final boolean ENABLE_PERF_LOGGING = false;
 
 
     @Override
     @Override
@@ -375,7 +377,7 @@ public class StoredConfigXmlSerializer implements StoredConfigSerializer
                 {
                 {
                     try
                     try
                     {
                     {
-                        userIdentity = UserIdentity.fromDelimitedKey( modifyUserValue.get() );
+                        userIdentity = UserIdentity.fromDelimitedKey( SESSION_LABEL, modifyUserValue.get() );
                     }
                     }
                     catch ( final Exception e )
                     catch ( final Exception e )
                     {
                     {
@@ -391,7 +393,7 @@ public class StoredConfigXmlSerializer implements StoredConfigSerializer
                 {
                 {
                     try
                     try
                     {
                     {
-                        userIdentity = UserIdentity.fromDelimitedKey( metaElement.get().getText().orElse( "" ) );
+                        userIdentity = UserIdentity.fromDelimitedKey( SESSION_LABEL, metaElement.get().getText().orElse( "" ) );
                     }
                     }
                     catch ( final DateTimeParseException | PwmUnrecoverableException e )
                     catch ( final DateTimeParseException | PwmUnrecoverableException e )
                     {
                     {

+ 11 - 6
server/src/main/java/password/pwm/config/stored/StoredConfigurationUtil.java

@@ -55,13 +55,13 @@ import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
-import java.util.TreeMap;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Predicate;
@@ -254,16 +254,22 @@ public abstract class StoredConfigurationUtil
 
 
     public static Map<String, String> makeDebugMap(
     public static Map<String, String> makeDebugMap(
             final StoredConfiguration storedConfiguration,
             final StoredConfiguration storedConfiguration,
-            final List<StoredConfigKey> interestedItems,
+            final Collection<StoredConfigKey> interestedItems,
             final Locale locale
             final Locale locale
     )
     )
     {
     {
-        return Collections.unmodifiableMap( new TreeMap<>( interestedItems.stream()
+        return Collections.unmodifiableMap( interestedItems.stream()
                 .filter( key -> !key.isRecordType( StoredConfigKey.RecordType.PROPERTY ) )
                 .filter( key -> !key.isRecordType( StoredConfigKey.RecordType.PROPERTY ) )
+                .sorted()
                 .collect( Collectors.toMap(
                 .collect( Collectors.toMap(
                         key -> key.getLabel( locale ),
                         key -> key.getLabel( locale ),
-                        key -> StoredConfigurationUtil.getValueOrDefault( storedConfiguration, key ).toDebugString( locale )
-                ) ) ) );
+                        key -> StoredConfigurationUtil.getValueOrDefault( storedConfiguration, key ).toDebugString( locale ),
+                        ( u, v ) ->
+                        {
+                            throw new IllegalStateException(  String.format( "duplicate key %s", u ) );
+                        },
+                        LinkedHashMap::new
+                ) ) );
     }
     }
 
 
     public static Set<StoredConfigKey> allPossibleSettingKeysForConfiguration(
     public static Set<StoredConfigKey> allPossibleSettingKeysForConfiguration(
@@ -301,7 +307,6 @@ public abstract class StoredConfigurationUtil
 
 
         return PwmSetting.sortedValues().stream()
         return PwmSetting.sortedValues().stream()
                 .filter( ( setting ) -> domainID.inScope( setting.getCategory().getScope() ) )
                 .filter( ( setting ) -> domainID.inScope( setting.getCategory().getScope() ) )
-                .parallel()
                 .flatMap( function )
                 .flatMap( function )
                 .collect( Collectors.toUnmodifiableSet() )
                 .collect( Collectors.toUnmodifiableSet() )
                 .stream();
                 .stream();

+ 17 - 5
server/src/main/java/password/pwm/config/value/AbstractValue.java

@@ -20,7 +20,6 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import password.pwm.PwmConstants;
 import password.pwm.config.stored.StoredConfigXmlConstants;
 import password.pwm.config.stored.StoredConfigXmlConstants;
 import password.pwm.config.stored.XmlOutputProcessData;
 import password.pwm.config.stored.XmlOutputProcessData;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
@@ -41,6 +40,20 @@ import java.util.Locale;
 
 
 public abstract class AbstractValue implements StoredValue
 public abstract class AbstractValue implements StoredValue
 {
 {
+    private static final PwmSecurityKey HASHING_KEY;
+
+    static
+    {
+        try
+        {
+            HASHING_KEY = new PwmSecurityKey( "hash-key" );
+        }
+        catch ( final PwmUnrecoverableException e )
+        {
+            throw new IllegalStateException( "unable to create internal HASHING_KEY: " + e.getMessage() );
+        }
+    }
+
     private final transient LazySupplier<String> valueHashSupplier = new LazySupplier<>( () -> valueHashComputer( AbstractValue.this ) );
     private final transient LazySupplier<String> valueHashSupplier = new LazySupplier<>( () -> valueHashComputer( AbstractValue.this ) );
 
 
     public String toString()
     public String toString()
@@ -76,9 +89,8 @@ public abstract class AbstractValue implements StoredValue
     {
     {
         try
         try
         {
         {
-            final PwmSecurityKey testingKey = new PwmSecurityKey( "test" );
             final XmlOutputProcessData xmlOutputProcessData = XmlOutputProcessData.builder()
             final XmlOutputProcessData xmlOutputProcessData = XmlOutputProcessData.builder()
-                    .pwmSecurityKey( testingKey )
+                    .pwmSecurityKey( HASHING_KEY )
                     .storedValueEncoderMode( StoredValueEncoder.Mode.PLAIN )
                     .storedValueEncoderMode( StoredValueEncoder.Mode.PLAIN )
                     .build();
                     .build();
             final List<XmlElement> xmlValues = storedValue.toXmlValues( StoredConfigXmlConstants.XML_ELEMENT_VALUE, xmlOutputProcessData );
             final List<XmlElement> xmlValues = storedValue.toXmlValues( StoredConfigXmlConstants.XML_ELEMENT_VALUE, xmlOutputProcessData );
@@ -86,8 +98,8 @@ public abstract class AbstractValue implements StoredValue
             document.getRootElement().addContent( xmlValues );
             document.getRootElement().addContent( xmlValues );
             final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             XmlFactory.getFactory().outputDocument( document, byteArrayOutputStream );
             XmlFactory.getFactory().outputDocument( document, byteArrayOutputStream );
-            final String stringToHash = byteArrayOutputStream.toString( PwmConstants.DEFAULT_CHARSET );
-            return SecureEngine.hash( stringToHash, PwmHashAlgorithm.SHA512 );
+            final byte[] bytesToHash = byteArrayOutputStream.toByteArray();
+            return SecureEngine.hash( bytesToHash, PwmHashAlgorithm.SHA512 );
 
 
         }
         }
         catch ( final IOException | PwmUnrecoverableException e )
         catch ( final IOException | PwmUnrecoverableException e )

+ 2 - 1
server/src/main/java/password/pwm/config/value/FileValue.java

@@ -84,7 +84,8 @@ public class FileValue extends AbstractValue implements StoredValue
         public static FileContent fromEncodedString( final String input )
         public static FileContent fromEncodedString( final String input )
                 throws IOException
                 throws IOException
         {
         {
-            return new FileContent( input );
+            final String whitespaceStripped = StringUtil.stripAllWhitespace( input );
+            return new FileContent( whitespaceStripped );
         }
         }
 
 
         public static FileContent fromBytes( final ImmutableByteArray contents )
         public static FileContent fromBytes( final ImmutableByteArray contents )

+ 4 - 20
server/src/main/java/password/pwm/config/value/ValueTypeConverter.java

@@ -40,7 +40,6 @@ import password.pwm.util.logging.PwmLogger;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
@@ -192,20 +191,12 @@ public final class ValueTypeConverter
         }
         }
 
 
         final List<String> results = new ArrayList<>( ( List<String> ) value.toNativeObject() );
         final List<String> results = new ArrayList<>( ( List<String> ) value.toNativeObject() );
-        for ( final Iterator iter = results.iterator(); iter.hasNext(); )
-        {
-            final Object loopString = iter.next();
-            if ( loopString == null || loopString.toString().length() < 1 )
-            {
-                iter.remove();
-            }
-        }
-        return results;
+        results.removeIf( StringUtil::isEmpty );
+        return List.copyOf( results );
     }
     }
 
 
     public static List<UserPermission> valueToUserPermissions( final StoredValue value )
     public static List<UserPermission> valueToUserPermissions( final StoredValue value )
     {
     {
-
         Objects.requireNonNull( value );
         Objects.requireNonNull( value );
 
 
         if ( !( value instanceof UserPermissionValue ) )
         if ( !( value instanceof UserPermissionValue ) )
@@ -214,15 +205,8 @@ public final class ValueTypeConverter
         }
         }
 
 
         final List<UserPermission> results = new ArrayList<>( ( List<UserPermission> ) value.toNativeObject() );
         final List<UserPermission> results = new ArrayList<>( ( List<UserPermission> ) value.toNativeObject() );
-        for ( final Iterator iter = results.iterator(); iter.hasNext(); )
-        {
-            final Object loopString = iter.next();
-            if ( loopString == null || loopString.toString().length() < 1 )
-            {
-                iter.remove();
-            }
-        }
-        return results;
+        results.removeIf( Objects::isNull );
+        return List.copyOf( results );
     }
     }
 
 
     public static Map<String, List<ChallengeItemConfiguration>> valueToChallengeItems( final StoredValue value )
     public static Map<String, List<ChallengeItemConfiguration>> valueToChallengeItems( final StoredValue value )

+ 11 - 10
server/src/main/java/password/pwm/error/PwmError.java

@@ -146,11 +146,11 @@ public enum PwmError
     ERROR_MISSING_PARAMETER(
     ERROR_MISSING_PARAMETER(
             5013, "Error_MissingParameter", Collections.emptySet() ),
             5013, "Error_MissingParameter", Collections.emptySet() ),
     ERROR_INTERNAL(
     ERROR_INTERNAL(
-            5015, "Error_Internal", null, ErrorFlag.ForceLogout ),
+            5015, "Error_Internal", null ),
     ERROR_CANT_MATCH_USER(
     ERROR_CANT_MATCH_USER(
             5016, "Error_CantMatchUser", Collections.emptySet() ),
             5016, "Error_CantMatchUser", Collections.emptySet() ),
     ERROR_DIRECTORY_UNAVAILABLE(
     ERROR_DIRECTORY_UNAVAILABLE(
-            5017, "Error_DirectoryUnavailable", null, ErrorFlag.ForceLogout ),
+            5017, "Error_DirectoryUnavailable", null ),
     ERROR_ACTIVATION_VALIDATIONFAIL(
     ERROR_ACTIVATION_VALIDATIONFAIL(
             5018, "Error_ActivationValidationFailed", Collections.emptySet() ),
             5018, "Error_ActivationValidationFailed", Collections.emptySet() ),
     ERROR_SERVICE_NOT_AVAILABLE(
     ERROR_SERVICE_NOT_AVAILABLE(
@@ -184,7 +184,7 @@ public enum PwmError
     ERROR_INVALID_CONFIG(
     ERROR_INVALID_CONFIG(
             5033, "Error_InvalidConfig", Collections.emptySet() ),
             5033, "Error_InvalidConfig", Collections.emptySet() ),
     ERROR_INVALID_FORMID(
     ERROR_INVALID_FORMID(
-            5034, "Error_InvalidFormID", Collections.emptySet() ),
+            5034, "Error_InvalidFormID", Collections.emptySet(), ErrorFlag.Trivial ),
     ERROR_INCORRECT_REQ_SEQUENCE(
     ERROR_INCORRECT_REQ_SEQUENCE(
             5035, "Error_IncorrectRequestSequence", Collections.emptySet() ),
             5035, "Error_IncorrectRequestSequence", Collections.emptySet() ),
     ERROR_TOKEN_MISSING_CONTACT(
     ERROR_TOKEN_MISSING_CONTACT(
@@ -310,6 +310,8 @@ public enum PwmError
             6000, "Error_RemoteErrorValue", Collections.emptySet(), ErrorFlag.Permanent ),
             6000, "Error_RemoteErrorValue", Collections.emptySet(), ErrorFlag.Permanent ),
     ERROR_TELEMETRY_SEND_ERROR(
     ERROR_TELEMETRY_SEND_ERROR(
             6001, "Error_TelemetrySendError", Collections.emptySet() ),
             6001, "Error_TelemetrySendError", Collections.emptySet() ),
+    ERROR_HTTP_CLIENT(
+            6002, "Error_HttpError", Collections.emptySet() ),
 
 
     ERROR_FIELD_REQUIRED(
     ERROR_FIELD_REQUIRED(
             5100, "Error_FieldRequired", Collections.emptySet() ),
             5100, "Error_FieldRequired", Collections.emptySet() ),
@@ -351,17 +353,17 @@ public enum PwmError
 
 
     /* End of list*/;
     /* End of list*/;
 
 
-    enum ErrorFlag
+    private enum ErrorFlag
     {
     {
         Permanent,
         Permanent,
-        ForceLogout,
+        Trivial,
     }
     }
 
 
     private final int errorCode;
     private final int errorCode;
     private final String resourceKey;
     private final String resourceKey;
     private final Set<ChaiError> chaiErrorCode;
     private final Set<ChaiError> chaiErrorCode;
     private final boolean errorIsPermanent;
     private final boolean errorIsPermanent;
-    private final boolean forceLogout;
+    private final boolean trivial;
 
 
     PwmError(
     PwmError(
             final int errorCode,
             final int errorCode,
@@ -373,9 +375,8 @@ public enum PwmError
         this.resourceKey = resourceKey;
         this.resourceKey = resourceKey;
         this.errorCode = errorCode;
         this.errorCode = errorCode;
         this.errorIsPermanent = JavaHelper.enumArrayContainsValue( errorFlags, ErrorFlag.Permanent );
         this.errorIsPermanent = JavaHelper.enumArrayContainsValue( errorFlags, ErrorFlag.Permanent );
-        this.forceLogout = JavaHelper.enumArrayContainsValue( errorFlags, ErrorFlag.ForceLogout );
+        this.trivial = JavaHelper.enumArrayContainsValue( errorFlags, ErrorFlag.Trivial );
         this.chaiErrorCode = chaiErrorCode == null ? Collections.emptySet() : Set.copyOf( chaiErrorCode );
         this.chaiErrorCode = chaiErrorCode == null ? Collections.emptySet() : Set.copyOf( chaiErrorCode );
-
     }
     }
 
 
     public String getLocalizedMessage( final Locale locale, final SettingReader config, final String... fieldValue )
     public String getLocalizedMessage( final Locale locale, final SettingReader config, final String... fieldValue )
@@ -420,9 +421,9 @@ public enum PwmError
         return null;
         return null;
     }
     }
 
 
-    private boolean isForceLogout( )
+    public boolean isTrivial()
     {
     {
-        return forceLogout;
+        return trivial;
     }
     }
 
 
     public boolean isErrorIsPermanent( )
     public boolean isErrorIsPermanent( )

+ 15 - 12
server/src/main/java/password/pwm/health/ApplianceStatusChecker.java

@@ -24,7 +24,6 @@ import org.apache.commons.io.FileUtils;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.PwmEnvironment;
 import password.pwm.PwmEnvironment;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
-import password.pwm.bean.SessionLabel;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
@@ -59,14 +58,15 @@ public class ApplianceStatusChecker implements HealthSupplier
     }
     }
 
 
     @Override
     @Override
-    public List<Supplier<List<HealthRecord>>> jobs( final PwmApplication pwmApplication )
+    public List<Supplier<List<HealthRecord>>> jobs( final HealthSupplier.HealthSupplierRequest request )
     {
     {
-        final Supplier<List<HealthRecord>> supplier = () -> doHealthCheck( pwmApplication );
+        final Supplier<List<HealthRecord>> supplier = () -> doHealthCheck( request );
         return Collections.singletonList( supplier );
         return Collections.singletonList( supplier );
     }
     }
 
 
-    public List<HealthRecord> doHealthCheck( final PwmApplication pwmApplication )
+    private List<HealthRecord> doHealthCheck( final HealthSupplier.HealthSupplierRequest request )
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
         final boolean isApplianceAvailable = pwmApplication.getPwmEnvironment().getFlags().contains( PwmEnvironment.ApplicationFlag.Appliance );
         final boolean isApplianceAvailable = pwmApplication.getPwmEnvironment().getFlags().contains( PwmEnvironment.ApplicationFlag.Appliance );
 
 
         if ( !isApplianceAvailable )
         if ( !isApplianceAvailable )
@@ -76,21 +76,23 @@ public class ApplianceStatusChecker implements HealthSupplier
 
 
         try
         try
         {
         {
-            return List.copyOf( readApplianceHealthStatus( pwmApplication ) );
+            return List.copyOf( readApplianceHealthStatus( request ) );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
-            LOGGER.error( SessionLabel.HEALTH_SESSION_LABEL, () -> "error communicating with client " + e.getMessage() );
+            LOGGER.error( request.getSessionLabel(), () -> "error communicating with client " + e.getMessage() );
         }
         }
 
 
         return Collections.emptyList();
         return Collections.emptyList();
     }
     }
 
 
-    private List<HealthRecord> readApplianceHealthStatus( final PwmApplication pwmApplication ) throws IOException, PwmUnrecoverableException, PwmOperationalException
+    private List<HealthRecord> readApplianceHealthStatus( final HealthSupplier.HealthSupplierRequest request  )
+            throws PwmUnrecoverableException, PwmOperationalException
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
         final List<HealthRecord> healthRecords = new ArrayList<>();
         final List<HealthRecord> healthRecords = new ArrayList<>();
 
 
-        final String url = figureUrl( pwmApplication );
+        final String url = figureUrl( request );
         final Map<String, String> requestHeaders = Collections.singletonMap( "sspr-authorization-token", getApplianceAccessToken( pwmApplication ) );
         final Map<String, String> requestHeaders = Collections.singletonMap( "sspr-authorization-token", getApplianceAccessToken( pwmApplication ) );
 
 
         final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
         final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
@@ -104,9 +106,9 @@ public class ApplianceStatusChecker implements HealthSupplier
                 .headers( requestHeaders )
                 .headers( requestHeaders )
                 .build();
                 .build();
 
 
-        final PwmHttpClientResponse response = pwmHttpClient.makeRequest( pwmHttpClientRequest, SessionLabel.HEALTH_SESSION_LABEL );
+        final PwmHttpClientResponse response = pwmHttpClient.makeRequest( pwmHttpClientRequest, request.getSessionLabel() );
 
 
-        LOGGER.trace( SessionLabel.HEALTH_SESSION_LABEL, () -> "https response from appliance server request: " + response.getBody() );
+        LOGGER.trace( request.getSessionLabel(), () -> "https response from appliance server request: " + response.getBody() );
 
 
         final String jsonString = response.getBody();
         final String jsonString = response.getBody();
 
 
@@ -150,8 +152,9 @@ public class ApplianceStatusChecker implements HealthSupplier
         return "";
         return "";
     }
     }
 
 
-    private String figureUrl( final PwmApplication pwmApplication ) throws PwmOperationalException
+    private String figureUrl( final HealthSupplier.HealthSupplierRequest request ) throws PwmOperationalException
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
         final String hostnameFile = pwmApplication.getPwmEnvironment().getParameters().get( PwmEnvironment.ApplicationParameter.ApplianceHostnameFile );
         final String hostnameFile = pwmApplication.getPwmEnvironment().getParameters().get( PwmEnvironment.ApplicationParameter.ApplianceHostnameFile );
         if ( StringUtil.isEmpty( hostnameFile ) )
         if ( StringUtil.isEmpty( hostnameFile ) )
         {
         {
@@ -164,7 +167,7 @@ public class ApplianceStatusChecker implements HealthSupplier
         final String port = pwmApplication.getPwmEnvironment().getParameters().get( PwmEnvironment.ApplicationParameter.AppliancePort );
         final String port = pwmApplication.getPwmEnvironment().getParameters().get( PwmEnvironment.ApplicationParameter.AppliancePort );
 
 
         final String url = "https://" + hostname + ":" + port + "/sspr/appliance-update-status";
         final String url = "https://" + hostname + ":" + port + "/sspr/appliance-update-status";
-        LOGGER.trace( SessionLabel.HEALTH_SESSION_LABEL, () -> "calculated appliance host url as: " + url );
+        LOGGER.trace( request.getSessionLabel(), () -> "calculated appliance host url as: " + url );
         return url;
         return url;
     }
     }
 
 

+ 3 - 8
server/src/main/java/password/pwm/health/CertificateChecker.java

@@ -23,7 +23,6 @@ package password.pwm.health;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
-import password.pwm.PwmDomain;
 import password.pwm.config.AppConfig;
 import password.pwm.config.AppConfig;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.stored.StoredConfigKey;
 import password.pwm.config.stored.StoredConfigKey;
@@ -52,14 +51,10 @@ public class CertificateChecker implements HealthSupplier
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( CertificateChecker.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( CertificateChecker.class );
 
 
-    public List<HealthRecord> doHealthCheck( final PwmDomain pwmDomain )
-    {
-        final CertificateCheckJob job = new CertificateCheckJob( pwmDomain.getPwmApplication().getConfig() );
-        return Collections.unmodifiableList( job.get() );
-    }
-
-    public List<Supplier<List<HealthRecord>>> jobs( final PwmApplication pwmApplication )
+    @Override
+    public List<Supplier<List<HealthRecord>>> jobs( final HealthSupplierRequest request )
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
         return Collections.singletonList( new CertificateCheckJob( pwmApplication.getConfig() ) );
         return Collections.singletonList( new CertificateCheckJob( pwmApplication.getConfig() ) );
     }
     }
 
 

+ 243 - 118
server/src/main/java/password/pwm/health/ConfigurationChecker.java

@@ -20,6 +20,7 @@
 
 
 package password.pwm.health;
 package password.pwm.health;
 
 
+import lombok.Value;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmApplicationMode;
@@ -27,6 +28,7 @@ import password.pwm.PwmConstants;
 import password.pwm.PwmDomain;
 import password.pwm.PwmDomain;
 import password.pwm.PwmEnvironment;
 import password.pwm.PwmEnvironment;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.AppConfig;
 import password.pwm.config.AppConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
@@ -61,11 +63,13 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.NoSuchElementException;
 import java.util.NoSuchElementException;
+import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
@@ -74,9 +78,9 @@ public class ConfigurationChecker implements HealthSupplier
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( ConfigurationChecker.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( ConfigurationChecker.class );
 
 
-    private static final List<Class<? extends ConfigHealthCheck>> ALL_CHECKS = List.of(
+    private static final List<Class<? extends ConfigDomainHealthCheck>> DOMAIN_CHECKS = List.of(
             VerifyNewUserPasswordPolicy.class,
             VerifyNewUserPasswordPolicy.class,
-            VerifyBasicConfigs.class,
+            VerifyBasicDomainConfigs.class,
             VerifyPasswordStrengthLevels.class,
             VerifyPasswordStrengthLevels.class,
             VerifyPasswordPolicyConfigs.class,
             VerifyPasswordPolicyConfigs.class,
             VerifyResponseLdapAttribute.class,
             VerifyResponseLdapAttribute.class,
@@ -87,60 +91,70 @@ public class ConfigurationChecker implements HealthSupplier
             VerifyPasswordWaitTimes.class,
             VerifyPasswordWaitTimes.class,
             VerifyUserPermissionSettings.class );
             VerifyUserPermissionSettings.class );
 
 
+    private static final List<Class<? extends ConfigSystemHealthCheck>> SYSTEM_CHECKS = List.of(
+            VerifyBasicSystemConfigs.class,
+            VerifyDbConfiguredIfNeededSystem.class );
+
     @Override
     @Override
-    public List<Supplier<List<HealthRecord>>> jobs( final PwmApplication pwmApplication )
+    public List<Supplier<List<HealthRecord>>> jobs( final HealthSupplier.HealthSupplierRequest request )
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
+        final SessionLabel sessionLabel = request.getSessionLabel();
+
+        if ( pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.HIDE_CONFIGURATION_HEALTH_WARNINGS ) )
+        {
+            return Collections.emptyList();
+        }
+
         final List<Supplier<List<HealthRecord>>> suppliers = new ArrayList<>();
         final List<Supplier<List<HealthRecord>>> suppliers = new ArrayList<>();
         suppliers.add( () -> checkAppMode( pwmApplication ) );
         suppliers.add( () -> checkAppMode( pwmApplication ) );
         for ( final PwmDomain domain : pwmApplication.domains().values() )
         for ( final PwmDomain domain : pwmApplication.domains().values() )
         {
         {
-            final Supplier<List<HealthRecord>> supplier = () -> allChecks( domain.getConfig(), PwmConstants.DEFAULT_LOCALE );
-            suppliers.add( supplier );
+            suppliers.add( () -> allDomainChecks( sessionLabel, domain.getConfig(), PwmConstants.DEFAULT_LOCALE ) );
         }
         }
+        suppliers.add( () -> allSystemChecks( sessionLabel, pwmApplication.getConfig(), PwmConstants.DEFAULT_LOCALE ) );
         return suppliers;
         return suppliers;
     }
     }
 
 
-    public List<HealthRecord> doHealthCheck( final AppConfig appConfig, final Locale locale )
+    public List<HealthRecord> doHealthCheck( final PwmApplication tempApp, final SessionLabel sessionLabel )
     {
     {
-        final List<HealthRecord> healthRecords = new ArrayList<>();
-        for ( final DomainConfig domain : appConfig.getDomainConfigs().values() )
-        {
-           healthRecords.addAll( allChecks( domain, locale ) );
-        }
-        return Collections.unmodifiableList( healthRecords );
+        final HealthSupplier.HealthSupplierRequest request = new HealthSupplierRequest( tempApp, sessionLabel );
+        return jobs( request ).stream().map( Supplier::get ).flatMap( Collection::stream ).collect( Collectors.toList() );
     }
     }
 
 
-    private List<HealthRecord> checkAppMode( final PwmApplication pwmApplication )
+    private List<HealthRecord> allDomainChecks( final SessionLabel sessionLabel, final DomainConfig config, final Locale locale )
     {
     {
         final List<HealthRecord> records = new ArrayList<>();
         final List<HealthRecord> records = new ArrayList<>();
-
-        if ( pwmApplication.getApplicationMode() == PwmApplicationMode.CONFIGURATION )
+        for ( final Class<? extends ConfigDomainHealthCheck> clazz : DOMAIN_CHECKS )
         {
         {
-            records.add( HealthRecord.forMessage(
-                    DomainID.systemId(),
-                    HealthMessage.Config_ConfigMode ) );
+            final ConfigDomainHealthCheck healthCheckClass;
+            try
+            {
+                healthCheckClass = clazz.getDeclaredConstructor().newInstance();
+                records.addAll( healthCheckClass.healthCheck( new DomainHealthCheckRequest( config, locale, sessionLabel ) ) );
+            }
+            catch ( final Exception e )
+            {
+                LOGGER.error( () -> "unexpected error during health check operation for class " + clazz.toString() + ", error:" + e.getMessage(), e );
+            }
         }
         }
         return Collections.unmodifiableList( records );
         return Collections.unmodifiableList( records );
     }
     }
 
 
-    private List<HealthRecord> allChecks(
-            final DomainConfig config,
+    private List<HealthRecord> allSystemChecks(
+            final SessionLabel sessionLabel,
+            final AppConfig config,
             final Locale locale
             final Locale locale
     )
     )
     {
     {
-        if ( config.readSettingAsBoolean( PwmSetting.HIDE_CONFIGURATION_HEALTH_WARNINGS ) )
-        {
-            return Collections.emptyList();
-        }
-
         final List<HealthRecord> records = new ArrayList<>();
         final List<HealthRecord> records = new ArrayList<>();
-        for ( final Class<? extends ConfigHealthCheck> clazz : ALL_CHECKS )
+        for ( final Class<? extends ConfigSystemHealthCheck> clazz : SYSTEM_CHECKS )
         {
         {
-            final ConfigHealthCheck healthCheckClass;
+            final ConfigSystemHealthCheck healthCheckClass;
             try
             try
             {
             {
                 healthCheckClass = clazz.getDeclaredConstructor().newInstance();
                 healthCheckClass = clazz.getDeclaredConstructor().newInstance();
-                records.addAll( healthCheckClass.healthCheck( config, locale ) );
+                records.addAll( healthCheckClass.healthCheck( new SystemHealthCheckRequest( config, locale, sessionLabel ) ) );
             }
             }
             catch ( final Exception e )
             catch ( final Exception e )
             {
             {
@@ -150,32 +164,43 @@ public class ConfigurationChecker implements HealthSupplier
         return Collections.unmodifiableList( records );
         return Collections.unmodifiableList( records );
     }
     }
 
 
-    static class VerifyNewUserPasswordPolicy implements ConfigHealthCheck
+    private List<HealthRecord> checkAppMode( final PwmApplication pwmApplication )
+    {
+        final List<HealthRecord> records = new ArrayList<>();
+
+        if ( pwmApplication.getApplicationMode() == PwmApplicationMode.CONFIGURATION )
+        {
+            records.add( HealthRecord.forMessage(
+                    DomainID.systemId(),
+                    HealthMessage.Config_ConfigMode ) );
+        }
+        return Collections.unmodifiableList( records );
+    }
+
+    static class VerifyNewUserPasswordPolicy implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig domainConfig, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
 
 
-            if ( domainConfig.readSettingAsBoolean( PwmSetting.NEWUSER_ENABLE ) )
+            if ( config.readSettingAsBoolean( PwmSetting.NEWUSER_ENABLE ) )
             {
             {
-
-
-
-                for ( final NewUserProfile newUserProfile : domainConfig.getNewUserProfiles().values() )
+                for ( final NewUserProfile newUserProfile : config.getNewUserProfiles().values() )
                 {
                 {
                     try
                     try
                     {
                     {
                         final PwmApplication tempApplication = PwmApplication.createPwmApplication( PwmEnvironment.builder().build()
                         final PwmApplication tempApplication = PwmApplication.createPwmApplication( PwmEnvironment.builder().build()
-                                .makeRuntimeInstance( domainConfig.getAppConfig() ) );
+                                .makeRuntimeInstance( config.getAppConfig() ) );
                         final PwmDomain tempDomain = tempApplication.domains().get( ConfigGuideForm.DOMAIN_ID );
                         final PwmDomain tempDomain = tempApplication.domains().get( ConfigGuideForm.DOMAIN_ID );
 
 
-                        newUserProfile.getNewUserPasswordPolicy( tempDomain, PwmConstants.DEFAULT_LOCALE );
+                        newUserProfile.getNewUserPasswordPolicy( domainHealthCheckRequest.getSessionLabel(), tempDomain, PwmConstants.DEFAULT_LOCALE );
                     }
                     }
                     catch ( final PwmUnrecoverableException e )
                     catch ( final PwmUnrecoverableException e )
                     {
                     {
                         records.add( HealthRecord.forMessage(
                         records.add( HealthRecord.forMessage(
-                                domainConfig.getDomainID(),
+                                config.getDomainID(),
                                 HealthMessage.NewUser_PwTemplateBad,
                                 HealthMessage.NewUser_PwTemplateBad,
                                 PwmSetting.NEWUSER_PASSWORD_POLICY_USER.toMenuLocationDebug( newUserProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE ),
                                 PwmSetting.NEWUSER_PASSWORD_POLICY_USER.toMenuLocationDebug( newUserProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE ),
                                 e.getMessage() ) );
                                 e.getMessage() ) );
@@ -184,20 +209,50 @@ public class ConfigurationChecker implements HealthSupplier
             }
             }
 
 
             return Collections.unmodifiableList( records );
             return Collections.unmodifiableList( records );
-
         }
         }
     }
     }
 
 
+    static class VerifyBasicSystemConfigs implements ConfigSystemHealthCheck
+    {
+        @Override
+        public List<HealthRecord> healthCheck( final SystemHealthCheckRequest systemHealthCheckRequest )
+        {
+            final AppConfig config = systemHealthCheckRequest.getDomainConfig();
+            final Locale locale = systemHealthCheckRequest.getLocale();
+
+            final String separator = LocaleHelper.getLocalizedMessage( locale, Config.Display_SettingNavigationSeparator, null );
+            final List<HealthRecord> records = new ArrayList<>();
+
+            if ( Boolean.parseBoolean( config.readAppProperty( AppProperty.LDAP_PROMISCUOUS_ENABLE ) ) )
+            {
+                final String appPropertyKey = "AppProperty" + separator + AppProperty.LDAP_PROMISCUOUS_ENABLE.getKey();
+                records.add( HealthRecord.forMessage(
+                        DomainID.systemId(),
+                        HealthMessage.Config_PromiscuousLDAP,
+                        appPropertyKey ) );
+            }
 
 
+            if ( config.readSettingAsBoolean( PwmSetting.DISPLAY_SHOW_DETAILED_ERRORS ) )
+            {
+                records.add( HealthRecord.forMessage(
+                        DomainID.systemId(),
+                        HealthMessage.Config_ShowDetailedErrors,
+                        PwmSetting.DISPLAY_SHOW_DETAILED_ERRORS.toMenuLocationDebug( null, locale ) ) );
+            }
+            return Collections.unmodifiableList( records );
+        }
+    }
 
 
-    static class VerifyBasicConfigs implements ConfigHealthCheck
+    static class VerifyBasicDomainConfigs implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
             final String siteUrl = config.getAppConfig().readSettingAsString( PwmSetting.PWM_SITE_URL );
             final String siteUrl = config.getAppConfig().readSettingAsString( PwmSetting.PWM_SITE_URL );
-            final String separator = LocaleHelper.getLocalizedMessage( locale, Config.Display_SettingNavigationSeparator, null );
 
 
             if ( siteUrl == null || siteUrl.isEmpty() || siteUrl.equals(
             if ( siteUrl == null || siteUrl.isEmpty() || siteUrl.equals(
                     PwmSetting.PWM_SITE_URL.getDefaultValue( config.getTemplate() ).toNativeObject() ) )
                     PwmSetting.PWM_SITE_URL.getDefaultValue( config.getTemplate() ).toNativeObject() ) )
@@ -216,23 +271,6 @@ public class ConfigurationChecker implements HealthSupplier
                         PwmSetting.LDAP_ENABLE_WIRE_TRACE.toMenuLocationDebug( null, locale ) ) );
                         PwmSetting.LDAP_ENABLE_WIRE_TRACE.toMenuLocationDebug( null, locale ) ) );
             }
             }
 
 
-            if ( Boolean.parseBoolean( config.readAppProperty( AppProperty.LDAP_PROMISCUOUS_ENABLE ) ) )
-            {
-                final String appPropertyKey = "AppProperty" + separator + AppProperty.LDAP_PROMISCUOUS_ENABLE.getKey();
-                records.add( HealthRecord.forMessage(
-                        config.getDomainID(),
-                        HealthMessage.Config_PromiscuousLDAP,
-                        appPropertyKey ) );
-            }
-
-            if ( config.getAppConfig().readSettingAsBoolean( PwmSetting.DISPLAY_SHOW_DETAILED_ERRORS ) )
-            {
-                records.add( HealthRecord.forMessage(
-                        config.getDomainID(),
-                        HealthMessage.Config_ShowDetailedErrors,
-                        PwmSetting.DISPLAY_SHOW_DETAILED_ERRORS.toMenuLocationDebug( null, locale ) ) );
-            }
-
             if ( config.getLdapProfiles().isEmpty() )
             if ( config.getLdapProfiles().isEmpty() )
             {
             {
                 records.add( HealthRecord.forMessage(
                 records.add( HealthRecord.forMessage(
@@ -291,11 +329,12 @@ public class ConfigurationChecker implements HealthSupplier
         }
         }
     }
     }
 
 
-    static class VerifyPasswordStrengthLevels implements ConfigHealthCheck
+    static class VerifyPasswordStrengthLevels implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
 
 
             final List<StoredConfigKey> interestedKeys = CollectionUtil.iteratorToStream( config.getStoredConfiguration().keys() )
             final List<StoredConfigKey> interestedKeys = CollectionUtil.iteratorToStream( config.getStoredConfiguration().keys() )
@@ -303,62 +342,71 @@ public class ConfigurationChecker implements HealthSupplier
                     .filter( key -> key.toPwmSetting().getSyntax() == PwmSettingSyntax.PASSWORD )
                     .filter( key -> key.toPwmSetting().getSyntax() == PwmSettingSyntax.PASSWORD )
                     .collect( Collectors.toList() );
                     .collect( Collectors.toList() );
 
 
-            try
+            for ( final StoredConfigKey key : interestedKeys )
             {
             {
-                for ( final StoredConfigKey key : interestedKeys )
+                try
                 {
                 {
-                    final PwmSetting pwmSetting = key.toPwmSetting();
-                    final StoredValue storedValue = config.getStoredConfiguration().readStoredValue( key ).orElseThrow();
-                    final PasswordData passwordValue = ValueTypeConverter.valueToPassword( storedValue );
-                    if ( passwordValue != null )
-                    {
-                        final String stringValue = passwordValue.getStringValue();
-
-                        if ( StringUtil.notEmpty( stringValue ) )
-                        {
-                            final int strength = PasswordUtility.judgePasswordStrength( config, stringValue );
-                            if ( strength < 50 )
-                            {
-                                records.add( HealthRecord.forMessage(
-                                        config.getDomainID(),
-                                        HealthMessage.Config_WeakPassword,
-                                        pwmSetting.toMenuLocationDebug( key.getProfileID(), locale ), String.valueOf( strength ) ) );
-                            }
-                        }
-                    }
+                    checkKey( domainHealthCheckRequest, key ).ifPresent( records::add );
+                }
+                catch ( final PwmUnrecoverableException e )
+                {
+                    LOGGER.error( () -> "unexpected error examining password strength of configuration: " );
                 }
                 }
             }
             }
-            catch ( final PwmUnrecoverableException e )
+
+            return Collections.unmodifiableList( records );
+        }
+
+        private Optional<HealthRecord> checkKey( final DomainHealthCheckRequest domainHealthCheckRequest, final StoredConfigKey key )
+                throws PwmUnrecoverableException
+        {
+            final StoredConfiguration config = domainHealthCheckRequest.getDomainConfig().getStoredConfiguration();
+            final PwmSetting pwmSetting = key.toPwmSetting();
+            final StoredValue storedValue = config.readStoredValue( key ).orElseThrow();
+            final PasswordData passwordValue = ValueTypeConverter.valueToPassword( storedValue );
+
+            if ( passwordValue != null )
             {
             {
-                LOGGER.error( () -> "unexpected error examining password strength of configuration: " );
+                final String stringValue = passwordValue.getStringValue();
+
+                if ( StringUtil.notEmpty( stringValue ) )
+                {
+                    final int strength = PasswordUtility.judgePasswordStrength( domainHealthCheckRequest.getDomainConfig(), stringValue );
+                    if ( strength < 50 )
+                    {
+                        return Optional.of( HealthRecord.forMessage(
+                                domainHealthCheckRequest.getDomainConfig().getDomainID(),
+                                HealthMessage.Config_WeakPassword,
+                                pwmSetting.toMenuLocationDebug( key.getProfileID(), domainHealthCheckRequest.getLocale() ), String.valueOf( strength ) ) );
+                    }
+                }
             }
             }
 
 
-            return Collections.unmodifiableList( records );
+            return Optional.empty();
         }
         }
     }
     }
 
 
-    static class VerifyResponseLdapAttribute implements ConfigHealthCheck
+    static class VerifyResponseLdapAttribute implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck(
-                final DomainConfig config,
-                final Locale locale
-        )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
-            final PwmSetting[] interestedSettings = new PwmSetting[]
-                    {
+            final List<PwmSetting> interestedSettings = List.of(
                             PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE,
                             PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE,
-                            PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE,
-                    };
+                            PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE );
+
             for ( final PwmSetting loopSetting : interestedSettings )
             for ( final PwmSetting loopSetting : interestedSettings )
             {
             {
-                if ( config.getResponseStorageLocations( loopSetting ).contains( DataStorageMethod.LDAP ) )
+                if ( config.readGenericStorageLocations( loopSetting ).contains( DataStorageMethod.LDAP ) )
                 {
                 {
                     for ( final LdapProfile ldapProfile : config.getLdapProfiles().values() )
                     for ( final LdapProfile ldapProfile : config.getLdapProfiles().values() )
                     {
                     {
                         final String responseAttr = ldapProfile.readSettingAsString( PwmSetting.CHALLENGE_USER_ATTRIBUTE );
                         final String responseAttr = ldapProfile.readSettingAsString( PwmSetting.CHALLENGE_USER_ATTRIBUTE );
-                        final boolean hasResponseAttribute = responseAttr != null && !responseAttr.isEmpty();
+                        final boolean hasResponseAttribute = StringUtil.notEmpty( responseAttr );
                         if ( !hasResponseAttribute )
                         if ( !hasResponseAttribute )
                         {
                         {
                             records.add( HealthRecord.forMessage(
                             records.add( HealthRecord.forMessage(
@@ -375,11 +423,14 @@ public class ConfigurationChecker implements HealthSupplier
         }
         }
     }
     }
 
 
-    static class VerifyDbConfiguredIfNeeded implements ConfigHealthCheck
+    static class VerifyDbConfiguredIfNeeded implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
             if ( !config.getAppConfig().hasDbConfigured() )
             if ( !config.getAppConfig().hasDbConfigured() )
             {
             {
@@ -388,12 +439,11 @@ public class ConfigurationChecker implements HealthSupplier
                     final List<PwmSetting> settingsToCheck = List.of(
                     final List<PwmSetting> settingsToCheck = List.of(
                             PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE,
                             PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE,
                             PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE,
                             PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE,
-                            PwmSetting.INTRUDER_STORAGE_METHOD,
                             PwmSetting.EVENTS_USER_STORAGE_METHOD );
                             PwmSetting.EVENTS_USER_STORAGE_METHOD );
 
 
                     for ( final PwmSetting loopSetting : settingsToCheck )
                     for ( final PwmSetting loopSetting : settingsToCheck )
                     {
                     {
-                        if ( config.getResponseStorageLocations( loopSetting ).contains( DataStorageMethod.DB ) )
+                        if ( config.readGenericStorageLocations( loopSetting ).contains( DataStorageMethod.DB ) )
                         {
                         {
                             causalSettings.add( loopSetting );
                             causalSettings.add( loopSetting );
                         }
                         }
@@ -414,7 +464,7 @@ public class ConfigurationChecker implements HealthSupplier
                 }
                 }
             }
             }
 
 
-            if ( config.getResponseStorageLocations( PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE ).contains( DataStorageMethod.LOCALDB ) )
+            if ( config.readGenericStorageLocations( PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE ).contains( DataStorageMethod.LOCALDB ) )
             {
             {
                 records.add( HealthRecord.forMessage(
                 records.add( HealthRecord.forMessage(
                         config.getDomainID(),
                         config.getDomainID(),
@@ -422,7 +472,7 @@ public class ConfigurationChecker implements HealthSupplier
                         PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE.toMenuLocationDebug( null, locale ) ) );
                         PwmSetting.FORGOTTEN_PASSWORD_WRITE_PREFERENCE.toMenuLocationDebug( null, locale ) ) );
             }
             }
 
 
-            if ( config.getOtpSecretStorageLocations( PwmSetting.OTP_SECRET_WRITE_PREFERENCE ).contains( DataStorageMethod.LOCALDB ) )
+            if ( config.readGenericStorageLocations( PwmSetting.OTP_SECRET_WRITE_PREFERENCE ).contains( DataStorageMethod.LOCALDB ) )
             {
             {
                 records.add( HealthRecord.forMessage(
                 records.add( HealthRecord.forMessage(
                         config.getDomainID(),
                         config.getDomainID(),
@@ -434,11 +484,52 @@ public class ConfigurationChecker implements HealthSupplier
         }
         }
     }
     }
 
 
-    static class VerifyPasswordPolicyConfigs implements ConfigHealthCheck
+    static class VerifyDbConfiguredIfNeededSystem implements ConfigSystemHealthCheck
+    {
+        @Override
+        public List<HealthRecord> healthCheck( final SystemHealthCheckRequest systemHealthCheckRequest )
+        {
+            final AppConfig config = systemHealthCheckRequest.getDomainConfig();
+            final Locale locale = systemHealthCheckRequest.getLocale();
+            final List<HealthRecord> records = new ArrayList<>();
+            if ( !config.hasDbConfigured() )
+            {
+                final Set<PwmSetting> causalSettings = new LinkedHashSet<>();
+                {
+                    final List<PwmSetting> settingsToCheck = List.of(
+                            PwmSetting.INTRUDER_STORAGE_METHOD  );
+
+                    for ( final PwmSetting loopSetting : settingsToCheck )
+                    {
+                        if ( config.readGenericStorageLocations( loopSetting ).contains( DataStorageMethod.DB ) )
+                        {
+                            causalSettings.add( loopSetting );
+                        }
+                    }
+                }
+
+
+                for ( final PwmSetting setting : causalSettings )
+                {
+                    records.add( HealthRecord.forMessage(
+                            DomainID.systemId(),
+                            HealthMessage.Config_MissingDB,
+                            setting.toMenuLocationDebug( null, locale ) ) );
+                }
+            }
+
+            return records;
+        }
+    }
+
+    static class VerifyPasswordPolicyConfigs implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
             for ( final String profileID : config.getPasswordProfileIDs() )
             for ( final String profileID : config.getPasswordProfileIDs() )
             {
             {
@@ -456,11 +547,14 @@ public class ConfigurationChecker implements HealthSupplier
         }
         }
     }
     }
 
 
-    static class VerifyNewUserLdapProfile implements ConfigHealthCheck
+    static class VerifyNewUserLdapProfile implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
             for ( final NewUserProfile newUserProfile : config.getNewUserProfiles().values() )
             for ( final NewUserProfile newUserProfile : config.getNewUserProfiles().values() )
             {
             {
@@ -482,11 +576,14 @@ public class ConfigurationChecker implements HealthSupplier
         }
         }
     }
     }
 
 
-    static class VerifyIfDeprecatedJsFormOptionUsed implements ConfigHealthCheck
+    static class VerifyIfDeprecatedJsFormOptionUsed implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
 
 
             final List<StoredConfigKey> interestedKeys = CollectionUtil.iteratorToStream( config.getStoredConfiguration().keys() )
             final List<StoredConfigKey> interestedKeys = CollectionUtil.iteratorToStream( config.getStoredConfiguration().keys() )
@@ -517,11 +614,14 @@ public class ConfigurationChecker implements HealthSupplier
         }
         }
     }
     }
 
 
-    static class VerifyIfDeprecatedSendMethodValuesUsed implements ConfigHealthCheck
+    static class VerifyIfDeprecatedSendMethodValuesUsed implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final Set<MessageSendMethod> deprecatedMethods = Arrays
             final Set<MessageSendMethod> deprecatedMethods = Arrays
                     .stream( MessageSendMethod.values() )
                     .stream( MessageSendMethod.values() )
                     .filter( MessageSendMethod::isDeprecated )
                     .filter( MessageSendMethod::isDeprecated )
@@ -605,11 +705,14 @@ public class ConfigurationChecker implements HealthSupplier
         }
         }
     }
     }
 
 
-    static class VerifyPasswordWaitTimes implements ConfigHealthCheck
+    static class VerifyPasswordWaitTimes implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
 
 
             for ( final ChangePasswordProfile changePasswordProfile : config.getChangePasswordProfile().values() )
             for ( final ChangePasswordProfile changePasswordProfile : config.getChangePasswordProfile().values() )
@@ -637,11 +740,14 @@ public class ConfigurationChecker implements HealthSupplier
 
 
 
 
 
 
-    static class VerifyUserPermissionSettings implements ConfigHealthCheck
+    static class VerifyUserPermissionSettings implements ConfigDomainHealthCheck
     {
     {
         @Override
         @Override
-        public List<HealthRecord> healthCheck( final DomainConfig config, final Locale locale )
+        public List<HealthRecord> healthCheck( final DomainHealthCheckRequest domainHealthCheckRequest )
         {
         {
+            final DomainConfig config = domainHealthCheckRequest.getDomainConfig();
+            final Locale locale = domainHealthCheckRequest.getLocale();
+
             final List<HealthRecord> records = new ArrayList<>();
             final List<HealthRecord> records = new ArrayList<>();
             final StoredConfiguration storedConfiguration = config.getStoredConfiguration();
             final StoredConfiguration storedConfiguration = config.getStoredConfiguration();
             final List<StoredConfigKey> interestedKeys = CollectionUtil.iteratorToStream( config.getStoredConfiguration().keys() )
             final List<StoredConfigKey> interestedKeys = CollectionUtil.iteratorToStream( config.getStoredConfiguration().keys() )
@@ -713,10 +819,29 @@ public class ConfigurationChecker implements HealthSupplier
         }
         }
     }
     }
 
 
-    interface ConfigHealthCheck
+    @Value
+    static class DomainHealthCheckRequest
+    {
+        private final DomainConfig domainConfig;
+        private final Locale locale;
+        private final SessionLabel sessionLabel;
+    }
+
+    interface ConfigDomainHealthCheck
+    {
+        List<HealthRecord> healthCheck( DomainHealthCheckRequest domainHealthCheckRequest );
+    }
+
+    @Value
+    static class SystemHealthCheckRequest
+    {
+        private final AppConfig domainConfig;
+        private final Locale locale;
+        private final SessionLabel sessionLabel;
+    }
+
+    interface ConfigSystemHealthCheck
     {
     {
-        List<HealthRecord> healthCheck(
-                DomainConfig domainConfig,
-                Locale locale );
+        List<HealthRecord> healthCheck( SystemHealthCheckRequest systemHealthCheckRequest );
     }
     }
 }
 }

+ 4 - 3
server/src/main/java/password/pwm/health/DatabaseStatusChecker.java

@@ -25,8 +25,8 @@ import password.pwm.PwmEnvironment;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
 import password.pwm.config.AppConfig;
 import password.pwm.config.AppConfig;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmException;
-import password.pwm.util.db.DatabaseAccessor;
-import password.pwm.util.db.DatabaseTable;
+import password.pwm.svc.db.DatabaseAccessor;
+import password.pwm.svc.db.DatabaseTable;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import java.util.Collections;
 import java.util.Collections;
@@ -38,8 +38,9 @@ public class DatabaseStatusChecker implements HealthSupplier
     private static final PwmLogger LOGGER = PwmLogger.forClass( DatabaseStatusChecker.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( DatabaseStatusChecker.class );
 
 
     @Override
     @Override
-    public List<Supplier<List<HealthRecord>>> jobs( final PwmApplication pwmApplication )
+    public List<Supplier<List<HealthRecord>>> jobs( final HealthSupplierRequest request )
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
         final Supplier<List<HealthRecord>> supplier = () -> doHealthCheck( pwmApplication );
         final Supplier<List<HealthRecord>> supplier = () -> doHealthCheck( pwmApplication );
         return Collections.singletonList( supplier );
         return Collections.singletonList( supplier );
     }
     }

+ 5 - 4
server/src/main/java/password/pwm/health/HealthRecord.java

@@ -25,7 +25,8 @@ import org.jetbrains.annotations.NotNull;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.SettingReader;
 import password.pwm.config.SettingReader;
-import password.pwm.ws.server.rest.bean.HealthData;
+import password.pwm.ws.server.rest.bean.PublicHealthData;
+import password.pwm.ws.server.rest.bean.PublicHealthRecord;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
 import java.time.Instant;
 import java.time.Instant;
@@ -138,15 +139,15 @@ public class HealthRecord implements Serializable, Comparable<HealthRecord>
         return Collections.singletonList( this );
         return Collections.singletonList( this );
     }
     }
 
 
-    public static HealthData asHealthDataBean(
+    public static PublicHealthData asHealthDataBean(
             final DomainConfig domainConfig,
             final DomainConfig domainConfig,
             final Locale locale,
             final Locale locale,
             final List<HealthRecord> profileRecords
             final List<HealthRecord> profileRecords
     )
     )
     {
     {
-        final List<password.pwm.ws.server.rest.bean.HealthRecord> healthRecordBeans = password.pwm.ws.server.rest.bean.HealthRecord.fromHealthRecords(
+        final List<PublicHealthRecord> healthRecordBeans = PublicHealthRecord.fromHealthRecords(
                 profileRecords, locale, domainConfig );
                 profileRecords, locale, domainConfig );
-        return HealthData.builder()
+        return PublicHealthData.builder()
                 .timestamp( Instant.now() )
                 .timestamp( Instant.now() )
                 .overall( HealthUtils.getMostSevereHealthStatus( profileRecords ).toString() )
                 .overall( HealthUtils.getMostSevereHealthStatus( profileRecords ).toString() )
                 .records( healthRecordBeans )
                 .records( healthRecordBeans )

+ 40 - 44
server/src/main/java/password/pwm/health/HealthService.java

@@ -27,9 +27,10 @@ import password.pwm.bean.DomainID;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SessionLabel;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.util.debug.DebugItemGenerator;
+import password.pwm.svc.AbstractPwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.util.PwmScheduler;
 import password.pwm.util.PwmScheduler;
+import password.pwm.util.debug.DebugItemGenerator;
 import password.pwm.util.java.AtomicLoopIntIncrementer;
 import password.pwm.util.java.AtomicLoopIntIncrementer;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -51,7 +52,6 @@ import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentHashMap;
@@ -61,7 +61,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 import java.util.zip.ZipOutputStream;
 import java.util.zip.ZipOutputStream;
 
 
-public class HealthService implements PwmService
+public class HealthService extends AbstractPwmService implements PwmService
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( HealthService.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( HealthService.class );
 
 
@@ -81,8 +81,6 @@ public class HealthService implements PwmService
     private final Map<HealthMonitorFlag, Serializable> healthProperties = new ConcurrentHashMap<>();
     private final Map<HealthMonitorFlag, Serializable> healthProperties = new ConcurrentHashMap<>();
     private final AtomicInteger healthCheckCount = new AtomicInteger( 0 );
     private final AtomicInteger healthCheckCount = new AtomicInteger( 0 );
 
 
-    private STATUS status = STATUS.CLOSED;
-    private PwmApplication pwmApplication;
     private volatile HealthData healthData = emptyHealthData();
     private volatile HealthData healthData = emptyHealthData();
 
 
     enum HealthMonitorFlag
     enum HealthMonitorFlag
@@ -96,18 +94,16 @@ public class HealthService implements PwmService
     }
     }
 
 
     @Override
     @Override
-    public void init( final PwmApplication pwmApplication, final DomainID domainID )
+    public STATUS postAbstractInit( final PwmApplication pwmApplication, final DomainID domainID )
             throws PwmException
             throws PwmException
     {
     {
-        this.pwmApplication = Objects.requireNonNull( pwmApplication );
         this.healthData = emptyHealthData();
         this.healthData = emptyHealthData();
         settings = HealthMonitorSettings.fromConfiguration( pwmApplication.getConfig() );
         settings = HealthMonitorSettings.fromConfiguration( pwmApplication.getConfig() );
 
 
         if ( !settings.isHealthCheckEnabled() )
         if ( !settings.isHealthCheckEnabled() )
         {
         {
             LOGGER.debug( () -> "health monitor will remain inactive due to AppProperty " + AppProperty.HEALTHCHECK_ENABLED.getKey() );
             LOGGER.debug( () -> "health monitor will remain inactive due to AppProperty " + AppProperty.HEALTHCHECK_ENABLED.getKey() );
-            status = STATUS.CLOSED;
-            return;
+            return STATUS.CLOSED;
         }
         }
 
 
         executorService = PwmScheduler.makeBackgroundExecutor( pwmApplication, this.getClass() );
         executorService = PwmScheduler.makeBackgroundExecutor( pwmApplication, this.getClass() );
@@ -119,12 +115,12 @@ public class HealthService implements PwmService
             pwmApplication.getPwmScheduler().scheduleFixedRateJob( new ThreadDumpLogger(), executorService, TimeDuration.SECOND, settings.getThreadDumpInterval() );
             pwmApplication.getPwmScheduler().scheduleFixedRateJob( new ThreadDumpLogger(), executorService, TimeDuration.SECOND, settings.getThreadDumpInterval() );
         }
         }
 
 
-        status = STATUS.OPEN;
+        return STATUS.OPEN;
     }
     }
 
 
     public Instant getLastHealthCheckTime( )
     public Instant getLastHealthCheckTime( )
     {
     {
-        if ( status != STATUS.OPEN )
+        if ( status() != STATUS.OPEN )
         {
         {
             return null;
             return null;
         }
         }
@@ -134,22 +130,17 @@ public class HealthService implements PwmService
 
 
     public HealthStatus getMostSevereHealthStatus( )
     public HealthStatus getMostSevereHealthStatus( )
     {
     {
-        if ( status != STATUS.OPEN )
+        if ( status() != STATUS.OPEN )
         {
         {
             return HealthStatus.GOOD;
             return HealthStatus.GOOD;
         }
         }
         return HealthUtils.getMostSevereHealthStatus( getHealthRecords( ) );
         return HealthUtils.getMostSevereHealthStatus( getHealthRecords( ) );
     }
     }
 
 
-    @Override
-    public STATUS status( )
-    {
-        return status;
-    }
 
 
     public Set<HealthRecord> getHealthRecords( )
     public Set<HealthRecord> getHealthRecords( )
     {
     {
-        if ( status != STATUS.OPEN )
+        if ( status() != STATUS.OPEN )
         {
         {
             return Collections.emptySet();
             return Collections.emptySet();
         }
         }
@@ -158,12 +149,12 @@ public class HealthService implements PwmService
         {
         {
             final Instant startTime = Instant.now();
             final Instant startTime = Instant.now();
             LOGGER.trace( () ->  "begin force immediate check" );
             LOGGER.trace( () ->  "begin force immediate check" );
-            final Future future = pwmApplication.getPwmScheduler().scheduleJob( new ImmediateJob(), executorService, TimeDuration.ZERO );
+            final Future future = getPwmApplication().getPwmScheduler().scheduleJob( new ImmediateJob(), executorService, TimeDuration.ZERO );
             settings.getMaximumForceCheckWait().pause( future::isDone );
             settings.getMaximumForceCheckWait().pause( future::isDone );
             LOGGER.trace( () ->  "exit force immediate check, done=" + future.isDone(), () -> TimeDuration.fromCurrent( startTime ) );
             LOGGER.trace( () ->  "exit force immediate check, done=" + future.isDone(), () -> TimeDuration.fromCurrent( startTime ) );
         }
         }
 
 
-        pwmApplication.getPwmScheduler().scheduleJob( new UpdateJob(), executorService, settings.getNominalCheckInterval() );
+        getPwmApplication().getPwmScheduler().scheduleJob( new UpdateJob(), executorService, settings.getNominalCheckInterval() );
 
 
         {
         {
             final HealthData localHealthData = this.healthData;
             final HealthData localHealthData = this.healthData;
@@ -188,7 +179,7 @@ public class HealthService implements PwmService
             supportZipWriterService.shutdown();
             supportZipWriterService.shutdown();
         }
         }
         healthData = emptyHealthData();
         healthData = emptyHealthData();
-        status = STATUS.CLOSED;
+        setStatus( STATUS.CLOSED );
     }
     }
 
 
     private HealthData emptyHealthData()
     private HealthData emptyHealthData()
@@ -197,7 +188,7 @@ public class HealthService implements PwmService
     }
     }
 
 
     @Override
     @Override
-    public List<HealthRecord> healthCheck( )
+    public List<HealthRecord> serviceHealthCheck( )
     {
     {
         return Collections.emptyList();
         return Collections.emptyList();
     }
     }
@@ -206,7 +197,7 @@ public class HealthService implements PwmService
     private void doHealthChecks( )
     private void doHealthChecks( )
     {
     {
         final int counter = healthCheckCount.getAndIncrement();
         final int counter = healthCheckCount.getAndIncrement();
-        if ( status != STATUS.OPEN )
+        if ( status() != STATUS.OPEN )
         {
         {
             return;
             return;
         }
         }
@@ -215,8 +206,7 @@ public class HealthService implements PwmService
         LOGGER.trace( () -> "beginning health check execution #" + counter  );
         LOGGER.trace( () -> "beginning health check execution #" + counter  );
         final List<HealthRecord> tempResults = new ArrayList<>();
         final List<HealthRecord> tempResults = new ArrayList<>();
 
 
-
-        for ( final Supplier<List<HealthRecord>> loopSupplier : gatherSuppliers( pwmApplication ) )
+        for ( final Supplier<List<HealthRecord>> loopSupplier : gatherSuppliers( getPwmApplication(), getSessionLabel() ) )
         {
         {
             try
             try
             {
             {
@@ -228,7 +218,7 @@ public class HealthService implements PwmService
             }
             }
             catch ( final Exception e )
             catch ( final Exception e )
             {
             {
-                if ( status == STATUS.OPEN )
+                if ( status() == STATUS.OPEN )
                 {
                 {
                     LOGGER.warn( () -> "unexpected error during healthCheck: " + e.getMessage(), e );
                     LOGGER.warn( () -> "unexpected error during healthCheck: " + e.getMessage(), e );
                 }
                 }
@@ -239,30 +229,36 @@ public class HealthService implements PwmService
         LOGGER.trace( () -> "completed health check execution #" + counter, () -> TimeDuration.fromCurrent( startTime ) );
         LOGGER.trace( () -> "completed health check execution #" + counter, () -> TimeDuration.fromCurrent( startTime ) );
     }
     }
 
 
-    private static List<Supplier<List<HealthRecord>>> gatherSuppliers( final PwmApplication pwmApplication )
+    private static List<Supplier<List<HealthRecord>>> gatherSuppliers(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel
+    )
     {
     {
         final List<Supplier<List<HealthRecord>>> suppliers = new ArrayList<>();
         final List<Supplier<List<HealthRecord>>> suppliers = new ArrayList<>();
 
 
-        for ( final PwmService service : pwmApplication.getAppAndDomainPwmServices() )
+        for ( final Map.Entry<DomainID, List<PwmService>> domainIDListEntry : pwmApplication.getAppAndDomainPwmServices().entrySet() )
         {
         {
-            try
+            for ( final PwmService service : domainIDListEntry.getValue() )
             {
             {
-                final List<HealthRecord> loopResults = service.healthCheck();
-                if ( loopResults != null )
+                try
                 {
                 {
-                    final Supplier<List<HealthRecord>> wrappedSupplier = () -> loopResults;
-                    suppliers.add( wrappedSupplier );
+                    final List<HealthRecord> loopResults = service.healthCheck();
+                    if ( loopResults != null )
+                    {
+                        final Supplier<List<HealthRecord>> wrappedSupplier = () -> loopResults;
+                        suppliers.add( wrappedSupplier );
+                    }
+                }
+                catch ( final Exception e )
+                {
+                    LOGGER.warn( () -> "unexpected error during healthCheck: " + e.getMessage(), e );
                 }
                 }
-            }
-            catch ( final Exception e )
-            {
-                LOGGER.warn( () -> "unexpected error during healthCheck: " + e.getMessage(), e );
             }
             }
         }
         }
 
 
         for ( final HealthSupplier supplier : HEALTH_SUPPLIERS )
         for ( final HealthSupplier supplier : HEALTH_SUPPLIERS )
         {
         {
-            suppliers.addAll( supplier.jobs( pwmApplication ) );
+            suppliers.addAll( supplier.jobs( new HealthSupplier.HealthSupplierRequest( pwmApplication, sessionLabel ) ) );
         }
         }
 
 
         return Collections.unmodifiableList( suppliers );
         return Collections.unmodifiableList( suppliers );
@@ -328,11 +324,11 @@ public class HealthService implements PwmService
 
 
     private void scheduleNextZipOutput()
     private void scheduleNextZipOutput()
     {
     {
-        final int intervalSeconds = JavaHelper.silentParseInt( pwmApplication.getConfig().readAppProperty( AppProperty.HEALTH_SUPPORT_BUNDLE_WRITE_INTERVAL_SECONDS ), 0 );
+        final int intervalSeconds = JavaHelper.silentParseInt( getPwmApplication().getConfig().readAppProperty( AppProperty.HEALTH_SUPPORT_BUNDLE_WRITE_INTERVAL_SECONDS ), 0 );
         if ( intervalSeconds > 0 )
         if ( intervalSeconds > 0 )
         {
         {
             final TimeDuration intervalDuration = TimeDuration.of( intervalSeconds, TimeDuration.Unit.SECONDS );
             final TimeDuration intervalDuration = TimeDuration.of( intervalSeconds, TimeDuration.Unit.SECONDS );
-            pwmApplication.getPwmScheduler().scheduleJob( new SupportZipFileWriter( pwmApplication ), supportZipWriterService, intervalDuration );
+            getPwmApplication().getPwmScheduler().scheduleJob( new SupportZipFileWriter( getPwmApplication() ), supportZipWriterService, intervalDuration );
         }
         }
     }
     }
 
 
@@ -354,7 +350,7 @@ public class HealthService implements PwmService
             }
             }
             catch ( final Exception e )
             catch ( final Exception e )
             {
             {
-                LOGGER.debug( SessionLabel.HEALTH_SESSION_LABEL, () -> "error writing support zip to file system: " + e.getMessage() );
+                LOGGER.debug( getSessionLabel(), () -> "error writing support zip to file system: " + e.getMessage() );
             }
             }
 
 
             scheduleNextZipOutput();
             scheduleNextZipOutput();
@@ -363,14 +359,14 @@ public class HealthService implements PwmService
         private void writeSupportZipToAppPath()
         private void writeSupportZipToAppPath()
                 throws IOException, PwmUnrecoverableException
                 throws IOException, PwmUnrecoverableException
         {
         {
-            final File appPath = HealthService.this.pwmApplication.getPwmEnvironment().getApplicationPath();
+            final File appPath = getPwmApplication().getPwmEnvironment().getApplicationPath();
             if ( !appPath.exists() )
             if ( !appPath.exists() )
             {
             {
                 return;
                 return;
             }
             }
 
 
             final int rotationCount = JavaHelper.silentParseInt( pwmApplication.getConfig().readAppProperty( AppProperty.HEALTH_SUPPORT_BUNDLE_FILE_WRITE_COUNT ), 10 );
             final int rotationCount = JavaHelper.silentParseInt( pwmApplication.getConfig().readAppProperty( AppProperty.HEALTH_SUPPORT_BUNDLE_FILE_WRITE_COUNT ), 10 );
-            final DebugItemGenerator debugItemGenerator = new DebugItemGenerator( pwmApplication, SessionLabel.HEALTH_SESSION_LABEL );
+            final DebugItemGenerator debugItemGenerator = new DebugItemGenerator( pwmApplication, getSessionLabel() );
 
 
             final File supportPath = new File( appPath.getPath() + File.separator + "support" );
             final File supportPath = new File( appPath.getPath() + File.separator + "support" );
 
 
@@ -385,7 +381,7 @@ public class HealthService implements PwmService
 
 
             try ( ZipOutputStream zipOutputStream = new ZipOutputStream( new FileOutputStream( newSupportFile ) ) )
             try ( ZipOutputStream zipOutputStream = new ZipOutputStream( new FileOutputStream( newSupportFile ) ) )
             {
             {
-                LOGGER.trace( SessionLabel.HEALTH_SESSION_LABEL, () -> "beginning periodic support bundle filesystem output" );
+                LOGGER.trace( getSessionLabel(), () -> "beginning periodic support bundle filesystem output" );
                 debugItemGenerator.outputZipDebugFile( zipOutputStream );
                 debugItemGenerator.outputZipDebugFile( zipOutputStream );
             }
             }
 
 

+ 10 - 1
server/src/main/java/password/pwm/health/HealthSupplier.java

@@ -20,12 +20,21 @@
 
 
 package password.pwm.health;
 package password.pwm.health;
 
 
+import lombok.Value;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
+import password.pwm.bean.SessionLabel;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 public interface HealthSupplier
 public interface HealthSupplier
 {
 {
-    List<Supplier<List<HealthRecord>>> jobs( PwmApplication pwmApplication );
+    List<Supplier<List<HealthRecord>>> jobs( HealthSupplierRequest request );
+
+    @Value
+    class HealthSupplierRequest
+    {
+        private final PwmApplication pwmApplication;
+        private final SessionLabel sessionLabel;
+    }
 }
 }

+ 3 - 1
server/src/main/java/password/pwm/health/JavaChecker.java

@@ -32,8 +32,10 @@ import java.util.function.Supplier;
 public class JavaChecker implements HealthSupplier
 public class JavaChecker implements HealthSupplier
 {
 {
     @Override
     @Override
-    public List<Supplier<List<HealthRecord>>> jobs( final PwmApplication pwmApplication )
+    public List<Supplier<List<HealthRecord>>> jobs( final HealthSupplier.HealthSupplierRequest request )
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
+
         final Supplier<List<HealthRecord>> supplier = () -> doHealthCheck( pwmApplication );
         final Supplier<List<HealthRecord>> supplier = () -> doHealthCheck( pwmApplication );
         return Collections.singletonList( supplier );
         return Collections.singletonList( supplier );
     }
     }

+ 69 - 55
server/src/main/java/password/pwm/health/LDAPHealthChecker.java

@@ -64,7 +64,7 @@ import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.util.password.RandomPasswordGenerator;
 import password.pwm.util.password.RandomPasswordGenerator;
-import password.pwm.ws.server.rest.bean.HealthData;
+import password.pwm.ws.server.rest.bean.PublicHealthData;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
 import java.net.InetAddress;
 import java.net.InetAddress;
@@ -89,15 +89,15 @@ public class LDAPHealthChecker implements HealthSupplier
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( LDAPHealthChecker.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( LDAPHealthChecker.class );
 
 
-    @Override
-    public List<Supplier<List<HealthRecord>>> jobs( final PwmApplication pwmApplication )
+    public List<Supplier<List<HealthRecord>>> jobs( final HealthSupplier.HealthSupplierRequest request )
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
         return pwmApplication.domains().values().stream()
         return pwmApplication.domains().values().stream()
-                .map( domain -> ( Supplier<List<HealthRecord>> ) () -> doHealthCheck( domain ) )
+                .map( domain -> ( Supplier<List<HealthRecord>> ) () -> doHealthCheck( request.getSessionLabel(), domain ) )
                 .collect( Collectors.toList() );
                 .collect( Collectors.toList() );
     }
     }
 
 
-    public List<HealthRecord> doHealthCheck( final PwmDomain pwmDomain )
+    public List<HealthRecord> doHealthCheck( final SessionLabel sessionLabel, final PwmDomain pwmDomain )
     {
     {
         final DomainConfig config = pwmDomain.getConfig();
         final DomainConfig config = pwmDomain.getConfig();
         final List<HealthRecord> returnRecords = new ArrayList<>();
         final List<HealthRecord> returnRecords = new ArrayList<>();
@@ -107,18 +107,18 @@ public class LDAPHealthChecker implements HealthSupplier
         {
         {
             final String profileID = entry.getKey();
             final String profileID = entry.getKey();
             final List<HealthRecord> profileRecords = new ArrayList<>(
             final List<HealthRecord> profileRecords = new ArrayList<>(
-                    checkBasicLdapConnectivity( pwmDomain, config, entry.getValue(), true )
+                    checkBasicLdapConnectivity( sessionLabel, pwmDomain, config, entry.getValue(), true )
             );
             );
 
 
             if ( profileRecords.isEmpty() )
             if ( profileRecords.isEmpty() )
             {
             {
-                profileRecords.addAll( checkLdapServerUrls( pwmDomain, config, ldapProfiles.get( profileID ) ) );
+                profileRecords.addAll( checkLdapServerUrls( sessionLabel, pwmDomain, config, ldapProfiles.get( profileID ) ) );
             }
             }
 
 
             if ( profileRecords.isEmpty() )
             if ( profileRecords.isEmpty() )
             {
             {
                 profileRecords.add( HealthRecord.forMessage( pwmDomain.getDomainID(), HealthMessage.LDAP_OK ) );
                 profileRecords.add( HealthRecord.forMessage( pwmDomain.getDomainID(), HealthMessage.LDAP_OK ) );
-                profileRecords.addAll( doLdapTestUserCheck( config, ldapProfiles.get( profileID ), pwmDomain ) );
+                profileRecords.addAll( doLdapTestUserCheck( sessionLabel, config, ldapProfiles.get( profileID ), pwmDomain ) );
             }
             }
             returnRecords.addAll( profileRecords );
             returnRecords.addAll( profileRecords );
         }
         }
@@ -154,13 +154,13 @@ public class LDAPHealthChecker implements HealthSupplier
             final List<String> urls = config.getLdapProfiles().values().iterator().next().readSettingAsStringArray( PwmSetting.LDAP_SERVER_URLS );
             final List<String> urls = config.getLdapProfiles().values().iterator().next().readSettingAsStringArray( PwmSetting.LDAP_SERVER_URLS );
             if ( urls != null && !urls.isEmpty() && StringUtil.notEmpty( urls.iterator().next() ) )
             if ( urls != null && !urls.isEmpty() && StringUtil.notEmpty( urls.iterator().next() ) )
             {
             {
-                returnRecords.addAll( checkVendorSameness( pwmDomain ) );
+                returnRecords.addAll( checkVendorSameness( sessionLabel, pwmDomain ) );
 
 
-                returnRecords.addAll( checkUserPermissionValues( pwmDomain ) );
+                returnRecords.addAll( checkUserPermissionValues( sessionLabel, pwmDomain ) );
 
 
-                returnRecords.addAll( checkLdapDNSyntaxValues( pwmDomain ) );
+                returnRecords.addAll( checkLdapDNSyntaxValues( sessionLabel, pwmDomain ) );
 
 
-                returnRecords.addAll( checkNewUserPasswordTemplateSetting( pwmDomain, config ) );
+                returnRecords.addAll( checkNewUserPasswordTemplateSetting( sessionLabel, pwmDomain, config ) );
 
 
      //           returnRecords.addAll( checkUserSearching( pwmApplication ) );
      //           returnRecords.addAll( checkUserSearching( pwmApplication ) );
             }
             }
@@ -171,6 +171,7 @@ public class LDAPHealthChecker implements HealthSupplier
 
 
     @SuppressWarnings( "checkstyle:MethodLength" )
     @SuppressWarnings( "checkstyle:MethodLength" )
     public List<HealthRecord> doLdapTestUserCheck(
     public List<HealthRecord> doLdapTestUserCheck(
+            final SessionLabel sessionLabel,
             final DomainConfig config,
             final DomainConfig config,
             final LdapProfile ldapProfile,
             final LdapProfile ldapProfile,
             final PwmDomain pwmDomain
             final PwmDomain pwmDomain
@@ -189,13 +190,13 @@ public class LDAPHealthChecker implements HealthSupplier
 
 
         try
         try
         {
         {
-            testUserDN = ldapProfile.readCanonicalDN( pwmDomain, testUserDN );
-            proxyUserDN = ldapProfile.readCanonicalDN( pwmDomain, proxyUserDN );
+            testUserDN = ldapProfile.readCanonicalDN( sessionLabel, pwmDomain, testUserDN );
+            proxyUserDN = ldapProfile.readCanonicalDN( sessionLabel, pwmDomain, proxyUserDN );
         }
         }
         catch ( final PwmUnrecoverableException e )
         catch ( final PwmUnrecoverableException e )
         {
         {
             final String msgString = e.getMessage();
             final String msgString = e.getMessage();
-            LOGGER.trace( SessionLabel.HEALTH_SESSION_LABEL, () -> "unexpected error while testing test user (during object creation): message="
+            LOGGER.trace( sessionLabel, () -> "unexpected error while testing test user (during object creation): message="
                     + msgString + " debug info: " + JavaHelper.readHostileExceptionMessage( e ) );
                     + msgString + " debug info: " + JavaHelper.readHostileExceptionMessage( e ) );
             returnRecords.add( HealthRecord.forMessage( pwmDomain.getDomainID(), HealthMessage.LDAP_TestUserUnexpected,
             returnRecords.add( HealthRecord.forMessage( pwmDomain.getDomainID(), HealthMessage.LDAP_TestUserUnexpected,
                     PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug( ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE ),
                     PwmSetting.LDAP_TEST_USER_DN.toMenuLocationDebug( ldapProfile.getIdentifier(), PwmConstants.DEFAULT_LOCALE ),
@@ -225,7 +226,7 @@ public class LDAPHealthChecker implements HealthSupplier
 
 
                 chaiProvider = LdapOperationsHelper.createChaiProvider(
                 chaiProvider = LdapOperationsHelper.createChaiProvider(
                         pwmDomain,
                         pwmDomain,
-                        SessionLabel.HEALTH_SESSION_LABEL,
+                        sessionLabel,
                         ldapProfile,
                         ldapProfile,
                         config,
                         config,
                         proxyUserDN,
                         proxyUserDN,
@@ -249,7 +250,7 @@ public class LDAPHealthChecker implements HealthSupplier
             {
             {
                 final String msgString = e.getMessage();
                 final String msgString = e.getMessage();
                 LOGGER.trace(
                 LOGGER.trace(
-                        SessionLabel.HEALTH_SESSION_LABEL,
+                        sessionLabel,
                         () -> "unexpected error while testing test user (during object creation): message="
                         () -> "unexpected error while testing test user (during object creation): message="
                                 + msgString + " debug info: " + JavaHelper.readHostileExceptionMessage( e )
                                 + msgString + " debug info: " + JavaHelper.readHostileExceptionMessage( e )
                 );
                 );
@@ -278,7 +279,7 @@ public class LDAPHealthChecker implements HealthSupplier
             }
             }
 
 
             LOGGER.trace(
             LOGGER.trace(
-                    SessionLabel.HEALTH_SESSION_LABEL,
+                    sessionLabel,
                     () -> "beginning process to check ldap test user password read/write operations for profile "
                     () -> "beginning process to check ldap test user password read/write operations for profile "
                             + ldapProfile.getIdentifier()
                             + ldapProfile.getIdentifier()
             );
             );
@@ -295,7 +296,7 @@ public class LDAPHealthChecker implements HealthSupplier
                     }
                     }
                     catch ( final Exception e )
                     catch ( final Exception e )
                     {
                     {
-                        LOGGER.debug( SessionLabel.HEALTH_SESSION_LABEL, () -> "error reading user password from directory " + e.getMessage() );
+                        LOGGER.debug( sessionLabel, () -> "error reading user password from directory " + e.getMessage() );
                         returnRecords.add( HealthRecord.forMessage(
                         returnRecords.add( HealthRecord.forMessage(
                                 pwmDomain.getDomainID(),
                                 pwmDomain.getDomainID(),
                                 HealthMessage.LDAP_TestUserReadPwError,
                                 HealthMessage.LDAP_TestUserReadPwError,
@@ -312,7 +313,7 @@ public class LDAPHealthChecker implements HealthSupplier
                     final UserIdentity userIdentity = UserIdentity.create( testUserDN, ldapProfile.getIdentifier(), pwmDomain.getDomainID() );
                     final UserIdentity userIdentity = UserIdentity.create( testUserDN, ldapProfile.getIdentifier(), pwmDomain.getDomainID() );
 
 
                     final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
                     final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
-                            pwmDomain, null, userIdentity, theUser );
+                            pwmDomain, sessionLabel, userIdentity, theUser );
 
 
                     boolean doPasswordChange = true;
                     boolean doPasswordChange = true;
                     final int minLifetimeSeconds = passwordPolicy.getRuleHelper().readIntValue( PwmPasswordRule.MinimumLifetime );
                     final int minLifetimeSeconds = passwordPolicy.getRuleHelper().readIntValue( PwmPasswordRule.MinimumLifetime );
@@ -320,7 +321,7 @@ public class LDAPHealthChecker implements HealthSupplier
                     {
                     {
                         final Instant pwdLastModified = PasswordUtility.determinePwdLastModified(
                         final Instant pwdLastModified = PasswordUtility.determinePwdLastModified(
                                 pwmDomain,
                                 pwmDomain,
-                                SessionLabel.HEALTH_SESSION_LABEL,
+                                sessionLabel,
                                 userIdentity
                                 userIdentity
                         );
                         );
 
 
@@ -329,7 +330,7 @@ public class LDAPHealthChecker implements HealthSupplier
                         {
                         {
                             final UserInfo userInfo = UserInfoFactory.newUserInfo(
                             final UserInfo userInfo = UserInfoFactory.newUserInfo(
                                     pwmDomain.getPwmApplication(),
                                     pwmDomain.getPwmApplication(),
-                                    SessionLabel.HEALTH_SESSION_LABEL,
+                                    sessionLabel,
                                     locale,
                                     locale,
                                     userIdentity,
                                     userIdentity,
                                     chaiProvider
                                     chaiProvider
@@ -340,14 +341,14 @@ public class LDAPHealthChecker implements HealthSupplier
                         {
                         {
                             final boolean withinMinLifetime = PasswordUtility.isPasswordWithinMinimumLifetimeImpl(
                             final boolean withinMinLifetime = PasswordUtility.isPasswordWithinMinimumLifetimeImpl(
                                     theUser,
                                     theUser,
-                                    SessionLabel.HEALTH_SESSION_LABEL,
+                                    sessionLabel,
                                     passwordPolicy,
                                     passwordPolicy,
                                     pwdLastModified,
                                     pwdLastModified,
                                     passwordStatus
                                     passwordStatus
                             );
                             );
                             if ( withinMinLifetime )
                             if ( withinMinLifetime )
                             {
                             {
-                                LOGGER.trace( SessionLabel.HEALTH_SESSION_LABEL, () -> "skipping test user password set due to password being within minimum lifetime" );
+                                LOGGER.trace( sessionLabel, () -> "skipping test user password set due to password being within minimum lifetime" );
                                 doPasswordChange = false;
                                 doPasswordChange = false;
                             }
                             }
                         }
                         }
@@ -358,7 +359,7 @@ public class LDAPHealthChecker implements HealthSupplier
                         try
                         try
                         {
                         {
                             theUser.setPassword( newPassword.getStringValue() );
                             theUser.setPassword( newPassword.getStringValue() );
-                            LOGGER.debug( SessionLabel.HEALTH_SESSION_LABEL, () -> "set random password on test user " + userIdentity.toDisplayString() );
+                            LOGGER.debug( sessionLabel, () -> "set random password on test user " + userIdentity.toDisplayString() );
                         }
                         }
                         catch ( final ChaiException e )
                         catch ( final ChaiException e )
                         {
                         {
@@ -377,7 +378,7 @@ public class LDAPHealthChecker implements HealthSupplier
             catch ( final Exception e )
             catch ( final Exception e )
             {
             {
                 final String msg = "error setting test user password: " + JavaHelper.readHostileExceptionMessage( e );
                 final String msg = "error setting test user password: " + JavaHelper.readHostileExceptionMessage( e );
-                LOGGER.error( SessionLabel.HEALTH_SESSION_LABEL, () -> msg, e );
+                LOGGER.error( sessionLabel, () -> msg, e );
                 returnRecords.add( HealthRecord.forMessage(
                 returnRecords.add( HealthRecord.forMessage(
                         pwmDomain.getDomainID(),
                         pwmDomain.getDomainID(),
                         HealthMessage.LDAP_TestUserUnexpected,
                         HealthMessage.LDAP_TestUserUnexpected,
@@ -392,7 +393,7 @@ public class LDAPHealthChecker implements HealthSupplier
                 final UserIdentity userIdentity = UserIdentity.create( theUser.getEntryDN(), ldapProfile.getIdentifier(), pwmDomain.getDomainID() );
                 final UserIdentity userIdentity = UserIdentity.create( theUser.getEntryDN(), ldapProfile.getIdentifier(), pwmDomain.getDomainID() );
                 final UserInfo userInfo = UserInfoFactory.newUserInfo(
                 final UserInfo userInfo = UserInfoFactory.newUserInfo(
                         pwmDomain.getPwmApplication(),
                         pwmDomain.getPwmApplication(),
-                        SessionLabel.HEALTH_SESSION_LABEL,
+                        sessionLabel,
                         PwmConstants.DEFAULT_LOCALE,
                         PwmConstants.DEFAULT_LOCALE,
                         userIdentity,
                         userIdentity,
                         chaiProvider
                         chaiProvider
@@ -445,6 +446,7 @@ public class LDAPHealthChecker implements HealthSupplier
 
 
 
 
     public List<HealthRecord> checkLdapServerUrls(
     public List<HealthRecord> checkLdapServerUrls(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final DomainConfig config,
             final DomainConfig config,
             final LdapProfile ldapProfile
             final LdapProfile ldapProfile
@@ -460,7 +462,7 @@ public class LDAPHealthChecker implements HealthSupplier
             {
             {
                 chaiProvider = LdapOperationsHelper.createChaiProvider(
                 chaiProvider = LdapOperationsHelper.createChaiProvider(
                         pwmDomain,
                         pwmDomain,
-                        SessionLabel.HEALTH_SESSION_LABEL,
+                        sessionLabel,
                         config,
                         config,
                         ldapProfile,
                         ldapProfile,
                         Collections.singletonList( loopURL ),
                         Collections.singletonList( loopURL ),
@@ -498,6 +500,7 @@ public class LDAPHealthChecker implements HealthSupplier
     }
     }
 
 
     public List<HealthRecord> checkBasicLdapConnectivity(
     public List<HealthRecord> checkBasicLdapConnectivity(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final DomainConfig config,
             final DomainConfig config,
             final LdapProfile ldapProfile,
             final LdapProfile ldapProfile,
@@ -532,7 +535,7 @@ public class LDAPHealthChecker implements HealthSupplier
                             ldapProfile.getIdentifier(),
                             ldapProfile.getIdentifier(),
                             "Missing Proxy User Password: " + menuLocationStr ) );
                             "Missing Proxy User Password: " + menuLocationStr ) );
                 }
                 }
-                chaiProvider = LdapOperationsHelper.createChaiProvider( pwmDomain, SessionLabel.HEALTH_SESSION_LABEL, ldapProfile, config, proxyDN, proxyPW );
+                chaiProvider = LdapOperationsHelper.createChaiProvider( pwmDomain, sessionLabel, ldapProfile, config, proxyDN, proxyPW );
                 final ChaiUser adminEntry = chaiProvider.getEntryFactory().newChaiUser( proxyDN );
                 final ChaiUser adminEntry = chaiProvider.getEntryFactory().newChaiUser( proxyDN );
                 adminEntry.exists();
                 adminEntry.exists();
                 directoryVendor = chaiProvider.getDirectoryVendor();
                 directoryVendor = chaiProvider.getDirectoryVendor();
@@ -598,7 +601,7 @@ public class LDAPHealthChecker implements HealthSupplier
 
 
             if ( directoryVendor != null && directoryVendor == DirectoryVendor.ACTIVE_DIRECTORY )
             if ( directoryVendor != null && directoryVendor == DirectoryVendor.ACTIVE_DIRECTORY )
             {
             {
-                returnRecords.addAll( checkAd( pwmDomain, config, ldapProfile ) );
+                returnRecords.addAll( checkAd( sessionLabel, pwmDomain, ldapProfile ) );
             }
             }
 
 
             if ( testContextlessRoot )
             if ( testContextlessRoot )
@@ -654,7 +657,11 @@ public class LDAPHealthChecker implements HealthSupplier
         return returnRecords;
         return returnRecords;
     }
     }
 
 
-    private static List<HealthRecord> checkAd( final PwmDomain pwmDomain, final DomainConfig config, final LdapProfile ldapProfile )
+    private static List<HealthRecord> checkAd(
+            final SessionLabel sessionLabel,
+            final PwmDomain pwmDomain,
+            final LdapProfile ldapProfile
+    )
     {
     {
         final List<HealthRecord> returnList = new ArrayList<>();
         final List<HealthRecord> returnList = new ArrayList<>();
         final List<String> serverURLs = ldapProfile.readSettingAsStringArray( PwmSetting.LDAP_SERVER_URLS );
         final List<String> serverURLs = ldapProfile.readSettingAsStringArray( PwmSetting.LDAP_SERVER_URLS );
@@ -694,7 +701,7 @@ public class LDAPHealthChecker implements HealthSupplier
             }
             }
         }
         }
 
 
-        returnList.addAll( checkAdPasswordPolicyApi( pwmDomain ) );
+        returnList.addAll( checkAdPasswordPolicyApi( sessionLabel, pwmDomain ) );
 
 
         return returnList;
         return returnList;
     }
     }
@@ -711,7 +718,7 @@ public class LDAPHealthChecker implements HealthSupplier
         return false;
         return false;
     }
     }
 
 
-    private List<HealthRecord> checkVendorSameness( final PwmDomain pwmDomain )
+    private List<HealthRecord> checkVendorSameness( final SessionLabel sessionLabel, final PwmDomain pwmDomain )
     {
     {
         final Map<HealthService.HealthMonitorFlag, Serializable> healthProperties = pwmDomain.getPwmApplication().getHealthMonitor().getHealthProperties();
         final Map<HealthService.HealthMonitorFlag, Serializable> healthProperties = pwmDomain.getPwmApplication().getHealthMonitor().getHealthProperties();
         if ( healthProperties.containsKey( HealthService.HealthMonitorFlag.LdapVendorSameCheck ) )
         if ( healthProperties.containsKey( HealthService.HealthMonitorFlag.LdapVendorSameCheck ) )
@@ -719,7 +726,7 @@ public class LDAPHealthChecker implements HealthSupplier
             return ( List<HealthRecord> ) healthProperties.get( HealthService.HealthMonitorFlag.LdapVendorSameCheck );
             return ( List<HealthRecord> ) healthProperties.get( HealthService.HealthMonitorFlag.LdapVendorSameCheck );
         }
         }
 
 
-        LOGGER.trace( SessionLabel.HEALTH_SESSION_LABEL, () -> "beginning check for replica vendor sameness" );
+        LOGGER.trace( sessionLabel, () -> "beginning check for replica vendor sameness" );
         boolean errorReachingServer = false;
         boolean errorReachingServer = false;
         final Map<String, DirectoryVendor> replicaVendorMap = new HashMap<>();
         final Map<String, DirectoryVendor> replicaVendorMap = new HashMap<>();
 
 
@@ -742,7 +749,7 @@ public class LDAPHealthChecker implements HealthSupplier
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
             errorReachingServer = true;
             errorReachingServer = true;
-            LOGGER.error( SessionLabel.HEALTH_SESSION_LABEL, () -> "error during replica vendor sameness check: " + e.getMessage() );
+            LOGGER.error( sessionLabel, () -> "error during replica vendor sameness check: " + e.getMessage() );
         }
         }
 
 
         final ArrayList<HealthRecord> healthRecords = new ArrayList<>();
         final ArrayList<HealthRecord> healthRecords = new ArrayList<>();
@@ -765,7 +772,7 @@ public class LDAPHealthChecker implements HealthSupplier
             // cache the error
             // cache the error
             healthProperties.put( HealthService.HealthMonitorFlag.LdapVendorSameCheck, healthRecords );
             healthProperties.put( HealthService.HealthMonitorFlag.LdapVendorSameCheck, healthRecords );
 
 
-            LOGGER.warn( SessionLabel.HEALTH_SESSION_LABEL, () -> "multiple ldap vendors found: " + vendorMsg.toString() );
+            LOGGER.warn( sessionLabel, () -> "multiple ldap vendors found: " + vendorMsg.toString() );
         }
         }
         else if ( discoveredVendors.size() == 1 )
         else if ( discoveredVendors.size() == 1 )
         {
         {
@@ -779,7 +786,7 @@ public class LDAPHealthChecker implements HealthSupplier
         return healthRecords;
         return healthRecords;
     }
     }
 
 
-    private static List<HealthRecord> checkAdPasswordPolicyApi( final PwmDomain pwmDomain )
+    private static List<HealthRecord> checkAdPasswordPolicyApi( final SessionLabel sessionLabel, final PwmDomain pwmDomain )
     {
     {
         final boolean passwordPolicyApiEnabled = pwmDomain.getConfig().readSettingAsBoolean( PwmSetting.AD_ENFORCE_PW_HISTORY_ON_SET );
         final boolean passwordPolicyApiEnabled = pwmDomain.getConfig().readSettingAsBoolean( PwmSetting.AD_ENFORCE_PW_HISTORY_ON_SET );
         if ( !passwordPolicyApiEnabled )
         if ( !passwordPolicyApiEnabled )
@@ -797,7 +804,7 @@ public class LDAPHealthChecker implements HealthSupplier
             }
             }
         }
         }
 
 
-        LOGGER.trace( SessionLabel.HEALTH_SESSION_LABEL, () -> "beginning check for ad api password policy (asn "
+        LOGGER.trace( sessionLabel, () -> "beginning check for ad api password policy (asn "
                 + PwmConstants.LDAP_AD_PASSWORD_POLICY_CONTROL_ASN + ") support" );
                 + PwmConstants.LDAP_AD_PASSWORD_POLICY_CONTROL_ASN + ") support" );
         boolean errorReachingServer = false;
         boolean errorReachingServer = false;
         final ArrayList<HealthRecord> healthRecords = new ArrayList<>();
         final ArrayList<HealthRecord> healthRecords = new ArrayList<>();
@@ -839,7 +846,7 @@ public class LDAPHealthChecker implements HealthSupplier
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
             errorReachingServer = true;
             errorReachingServer = true;
-            LOGGER.error( SessionLabel.HEALTH_SESSION_LABEL,
+            LOGGER.error( sessionLabel,
                     () ->  "error during ad api password policy (asn " + PwmConstants.LDAP_AD_PASSWORD_POLICY_CONTROL_ASN + ") check: " + e.getMessage() );
                     () ->  "error during ad api password policy (asn " + PwmConstants.LDAP_AD_PASSWORD_POLICY_CONTROL_ASN + ") check: " + e.getMessage() );
         }
         }
 
 
@@ -852,7 +859,7 @@ public class LDAPHealthChecker implements HealthSupplier
         return healthRecords;
         return healthRecords;
     }
     }
 
 
-    private static List<HealthRecord> checkUserPermissionValues( final PwmDomain pwmDomain )
+    private static List<HealthRecord> checkUserPermissionValues( final SessionLabel sessionLabel, final PwmDomain pwmDomain )
     {
     {
         final List<HealthRecord> returnList = new ArrayList<>();
         final List<HealthRecord> returnList = new ArrayList<>();
         final DomainConfig config = pwmDomain.getConfig();
         final DomainConfig config = pwmDomain.getConfig();
@@ -867,7 +874,7 @@ public class LDAPHealthChecker implements HealthSupplier
                     {
                     {
                         try
                         try
                         {
                         {
-                            returnList.addAll( checkUserPermission( pwmDomain, userPermission, pwmSetting ) );
+                            returnList.addAll( checkUserPermission( sessionLabel, pwmDomain, userPermission, pwmSetting ) );
                         }
                         }
                         catch ( final PwmUnrecoverableException e )
                         catch ( final PwmUnrecoverableException e )
                         {
                         {
@@ -880,7 +887,9 @@ public class LDAPHealthChecker implements HealthSupplier
         return returnList;
         return returnList;
     }
     }
 
 
-    private static List<HealthRecord> checkLdapDNSyntaxValues( final PwmDomain pwmDomain )
+    private static List<HealthRecord> checkLdapDNSyntaxValues(
+            final SessionLabel sessionLabel,
+            final PwmDomain pwmDomain )
     {
     {
         final List<HealthRecord> returnList = new ArrayList<>();
         final List<HealthRecord> returnList = new ArrayList<>();
         final DomainConfig config = pwmDomain.getConfig();
         final DomainConfig config = pwmDomain.getConfig();
@@ -901,7 +910,7 @@ public class LDAPHealthChecker implements HealthSupplier
                             final String value = config.getLdapProfiles().get( profile ).readSettingAsString( pwmSetting );
                             final String value = config.getLdapProfiles().get( profile ).readSettingAsString( pwmSetting );
                             if ( value != null && !value.isEmpty() )
                             if ( value != null && !value.isEmpty() )
                             {
                             {
-                                final Optional<String> errorMsg = validateDN( pwmDomain, value, profile );
+                                final Optional<String> errorMsg = validateDN( sessionLabel, pwmDomain, value, profile );
                                 errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                                 errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                                         pwmDomain.getDomainID(),
                                         pwmDomain.getDomainID(),
                                         HealthMessage.Config_DNValueValidity,
                                         HealthMessage.Config_DNValueValidity,
@@ -916,7 +925,7 @@ public class LDAPHealthChecker implements HealthSupplier
                             {
                             {
                                 for ( final String value : values )
                                 for ( final String value : values )
                                 {
                                 {
-                                    final Optional<String> errorMsg = validateDN( pwmDomain, value, profile );
+                                    final Optional<String> errorMsg = validateDN( sessionLabel, pwmDomain, value, profile );
                                     errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                                     errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                                             pwmDomain.getDomainID(),
                                             pwmDomain.getDomainID(),
                                             HealthMessage.Config_DNValueValidity,
                                             HealthMessage.Config_DNValueValidity,
@@ -938,6 +947,7 @@ public class LDAPHealthChecker implements HealthSupplier
     }
     }
 
 
     private static List<HealthRecord> checkNewUserPasswordTemplateSetting(
     private static List<HealthRecord> checkNewUserPasswordTemplateSetting(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final DomainConfig domainConfig
             final DomainConfig domainConfig
     )
     )
@@ -969,7 +979,7 @@ public class LDAPHealthChecker implements HealthSupplier
                 final LdapProfile ldapProfile = newUserProfile.getLdapProfile( pwmDomain.getConfig() );
                 final LdapProfile ldapProfile = newUserProfile.getLdapProfile( pwmDomain.getConfig() );
                 if ( NewUserProfile.TEST_USER_CONFIG_VALUE.equals( policyUserStr ) )
                 if ( NewUserProfile.TEST_USER_CONFIG_VALUE.equals( policyUserStr ) )
                 {
                 {
-                    final UserIdentity testUser = ldapProfile.getTestUser( pwmDomain );
+                    final UserIdentity testUser = ldapProfile.getTestUser( sessionLabel, pwmDomain );
                     if ( testUser != null )
                     if ( testUser != null )
                     {
                     {
                         return Collections.emptyList();
                         return Collections.emptyList();
@@ -978,7 +988,7 @@ public class LDAPHealthChecker implements HealthSupplier
 
 
                 final UserIdentity newUserTemplateIdentity = UserIdentity.create( policyUserStr, ldapProfile.getIdentifier(), pwmDomain.getDomainID() );
                 final UserIdentity newUserTemplateIdentity = UserIdentity.create( policyUserStr, ldapProfile.getIdentifier(), pwmDomain.getDomainID() );
 
 
-                final ChaiUser chaiUser = pwmDomain.getProxiedChaiUser( newUserTemplateIdentity );
+                final ChaiUser chaiUser = pwmDomain.getProxiedChaiUser( sessionLabel, newUserTemplateIdentity );
 
 
                 try
                 try
                 {
                 {
@@ -1009,6 +1019,7 @@ public class LDAPHealthChecker implements HealthSupplier
     }
     }
 
 
     private static List<HealthRecord> checkUserSearching(
     private static List<HealthRecord> checkUserSearching(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain
             final PwmDomain pwmDomain
     )
     )
     {
     {
@@ -1029,7 +1040,7 @@ public class LDAPHealthChecker implements HealthSupplier
                     .username( healthUsername )
                     .username( healthUsername )
                     .build();
                     .build();
 
 
-            pwmDomain.getUserSearchEngine().performMultiUserSearch( searchConfiguration, 1, Collections.singletonList( "cn" ), SessionLabel.HEALTH_SESSION_LABEL );
+            pwmDomain.getUserSearchEngine().performMultiUserSearch( searchConfiguration, 1, Collections.singletonList( "cn" ), sessionLabel );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
@@ -1057,6 +1068,7 @@ public class LDAPHealthChecker implements HealthSupplier
     }
     }
 
 
     private static List<HealthRecord> checkUserPermission(
     private static List<HealthRecord> checkUserPermission(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final UserPermission userPermission,
             final UserPermission userPermission,
             final PwmSetting pwmSetting
             final PwmSetting pwmSetting
@@ -1104,7 +1116,7 @@ public class LDAPHealthChecker implements HealthSupplier
                     final String userDN = userPermission.getLdapBase();
                     final String userDN = userPermission.getLdapBase();
                     if ( userDN != null && !isExampleDN( userDN ) )
                     if ( userDN != null && !isExampleDN( userDN ) )
                     {
                     {
-                        final Optional<String> errorMsg = validateDN( pwmDomain, userDN, ldapProfileID );
+                        final Optional<String> errorMsg = validateDN( sessionLabel, pwmDomain, userDN, ldapProfileID );
                         errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                         errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                                 pwmDomain.getDomainID(),
                                 pwmDomain.getDomainID(),
                                 HealthMessage.Config_UserPermissionValidity,
                                 HealthMessage.Config_UserPermissionValidity,
@@ -1118,7 +1130,7 @@ public class LDAPHealthChecker implements HealthSupplier
                     final String groupDN = userPermission.getLdapBase();
                     final String groupDN = userPermission.getLdapBase();
                     if ( groupDN != null && !isExampleDN( groupDN ) )
                     if ( groupDN != null && !isExampleDN( groupDN ) )
                     {
                     {
-                        final Optional<String> errorMsg = validateDN( pwmDomain, groupDN, ldapProfileID );
+                        final Optional<String> errorMsg = validateDN( sessionLabel, pwmDomain, groupDN, ldapProfileID );
                         errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                         errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                                 pwmDomain.getDomainID(),
                                 pwmDomain.getDomainID(),
                                 HealthMessage.Config_UserPermissionValidity,
                                 HealthMessage.Config_UserPermissionValidity,
@@ -1133,7 +1145,7 @@ public class LDAPHealthChecker implements HealthSupplier
                     final String baseDN = userPermission.getLdapBase();
                     final String baseDN = userPermission.getLdapBase();
                     if ( baseDN != null && !isExampleDN( baseDN ) )
                     if ( baseDN != null && !isExampleDN( baseDN ) )
                     {
                     {
-                        final Optional<String> errorMsg = validateDN( pwmDomain, baseDN, ldapProfileID );
+                        final Optional<String> errorMsg = validateDN( sessionLabel, pwmDomain, baseDN, ldapProfileID );
                         errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                         errorMsg.ifPresent( s -> returnList.add( HealthRecord.forMessage(
                                 pwmDomain.getDomainID(),
                                 pwmDomain.getDomainID(),
                                 HealthMessage.Config_UserPermissionValidity,
                                 HealthMessage.Config_UserPermissionValidity,
@@ -1151,6 +1163,7 @@ public class LDAPHealthChecker implements HealthSupplier
     }
     }
 
 
     private static Optional<String> validateDN(
     private static Optional<String> validateDN(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final String dnValue,
             final String dnValue,
             final String ldapProfileID
             final String ldapProfileID
@@ -1162,7 +1175,7 @@ public class LDAPHealthChecker implements HealthSupplier
             return Optional.empty();
             return Optional.empty();
         }
         }
 
 
-        final ChaiProvider chaiProvider = pwmDomain.getProxyChaiProvider( ldapProfileID );
+        final ChaiProvider chaiProvider = pwmDomain.getProxyChaiProvider( sessionLabel, ldapProfileID );
         try
         try
         {
         {
             if ( !isExampleDN( dnValue ) )
             if ( !isExampleDN( dnValue ) )
@@ -1216,7 +1229,8 @@ public class LDAPHealthChecker implements HealthSupplier
         return false;
         return false;
     }
     }
 
 
-    public static HealthData healthForNewConfiguration(
+    public static PublicHealthData healthForNewConfiguration(
+            final SessionLabel sessionLabel,
             final PwmDomain pwmDomain,
             final PwmDomain pwmDomain,
             final DomainConfig config,
             final DomainConfig config,
             final Locale locale,
             final Locale locale,
@@ -1235,11 +1249,11 @@ public class LDAPHealthChecker implements HealthSupplier
 
 
         final LdapProfile ldapProfile = config.getLdapProfiles().get( profileID );
         final LdapProfile ldapProfile = config.getLdapProfiles().get( profileID );
         final List<HealthRecord> profileRecords = new ArrayList<>(
         final List<HealthRecord> profileRecords = new ArrayList<>(
-                ldapHealthChecker.checkBasicLdapConnectivity( tempDomain, config, ldapProfile, testContextless ) );
+                ldapHealthChecker.checkBasicLdapConnectivity( sessionLabel, tempDomain, config, ldapProfile, testContextless ) );
 
 
         if ( fullTest )
         if ( fullTest )
         {
         {
-            profileRecords.addAll( ldapHealthChecker.checkLdapServerUrls( tempDomain, config, ldapProfile ) );
+            profileRecords.addAll( ldapHealthChecker.checkLdapServerUrls( sessionLabel, tempDomain, config, ldapProfile ) );
         }
         }
 
 
         if ( profileRecords.isEmpty() )
         if ( profileRecords.isEmpty() )
@@ -1249,7 +1263,7 @@ public class LDAPHealthChecker implements HealthSupplier
 
 
         if ( fullTest )
         if ( fullTest )
         {
         {
-            profileRecords.addAll( ldapHealthChecker.doLdapTestUserCheck( config, ldapProfile, tempDomain ) );
+            profileRecords.addAll( ldapHealthChecker.doLdapTestUserCheck( sessionLabel, config, ldapProfile, tempDomain ) );
         }
         }
 
 
         return HealthRecord.asHealthDataBean( config, locale, profileRecords );
         return HealthRecord.asHealthDataBean( config, locale, profileRecords );

+ 2 - 2
server/src/main/java/password/pwm/health/LocalDBHealthChecker.java

@@ -35,10 +35,10 @@ import java.util.function.Supplier;
 
 
 public class LocalDBHealthChecker implements HealthSupplier
 public class LocalDBHealthChecker implements HealthSupplier
 {
 {
-
     @Override
     @Override
-    public List<Supplier<List<HealthRecord>>> jobs( final PwmApplication pwmApplication )
+    public List<Supplier<List<HealthRecord>>> jobs( final HealthSupplierRequest request )
     {
     {
+        final PwmApplication pwmApplication = request.getPwmApplication();
         final Supplier<List<HealthRecord>> supplier = () -> doHealthCheck( pwmApplication );
         final Supplier<List<HealthRecord>> supplier = () -> doHealthCheck( pwmApplication );
         return Collections.singletonList( supplier );
         return Collections.singletonList( supplier );
     }
     }

+ 3 - 35
server/src/main/java/password/pwm/http/HttpEventManager.java

@@ -27,6 +27,7 @@ import password.pwm.bean.DomainID;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.EpsStatistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
@@ -68,10 +69,7 @@ public class HttpEventManager implements
             final PwmApplication pwmApplication = contextManager.getPwmApplication();
             final PwmApplication pwmApplication = contextManager.getPwmApplication();
             httpSession.setAttribute( PwmConstants.SESSION_ATTR_PWM_APP_NONCE, pwmApplication.getRuntimeNonce() );
             httpSession.setAttribute( PwmConstants.SESSION_ATTR_PWM_APP_NONCE, pwmApplication.getRuntimeNonce() );
 
 
-            if ( pwmApplication.getStatisticsManager() != null )
-            {
-                pwmApplication.getStatisticsManager().updateEps( EpsStatistic.SESSIONS, 1 );
-            }
+            StatisticsClient.updateEps( pwmApplication, EpsStatistic.SESSIONS );
 
 
             LOGGER.trace( () -> "new http session created" );
             LOGGER.trace( () -> "new http session created" );
 
 
@@ -164,41 +162,11 @@ public class HttpEventManager implements
     @Override
     @Override
     public void sessionWillPassivate( final HttpSessionEvent event )
     public void sessionWillPassivate( final HttpSessionEvent event )
     {
     {
-        /*
-        try
-        {
-            final PwmSession pwmSession = PwmSessionFactory.readPwmSession( event.getSession() );
-            LOGGER.trace( pwmSession.getLabel(), () -> "passivating session" );
-        }
-        catch ( final PwmUnrecoverableException e )
-        {
-            LOGGER.error( () -> "unable to passivate session: " + e.getMessage() );
-        }
-
-         */
     }
     }
 
 
     @Override
     @Override
     public void sessionDidActivate( final HttpSessionEvent event )
     public void sessionDidActivate( final HttpSessionEvent event )
     {
     {
-        /*
-        try
-        {
-            final HttpSession httpSession = event.getSession();
-            final PwmSession pwmSession = PwmSessionFactory.readPwmSession( httpSession );
-            LOGGER.trace( pwmSession.getLabel(), () -> "activating (de-passivating) session" );
-            final PwmApplication pwmApplication = ContextManager.getPwmApplication( httpSession.getServletContext() );
-            if ( pwmApplication != null )
-            {
-                pwmApplication.getSessionTrackService().addSessionData( pwmSession );
-            }
-        }
-        catch ( final PwmUnrecoverableException e )
-        {
-            LOGGER.error( () -> "unable to activate (de-passivate) session: " + e.getMessage() );
-        }
-
-         */
     }
     }
 
 
     private static String makeSessionDestroyedDebugMsg( final PwmSession pwmSession )
     private static String makeSessionDestroyedDebugMsg( final PwmSession pwmSession )
@@ -210,7 +178,7 @@ public class HttpEventManager implements
         final Instant lastAccessedTime = sessionStateBean.getSessionLastAccessedTime();
         final Instant lastAccessedTime = sessionStateBean.getSessionLastAccessedTime();
         final TimeDuration timeDuration = TimeDuration.between( startTime, lastAccessedTime );
         final TimeDuration timeDuration = TimeDuration.between( startTime, lastAccessedTime );
         debugItems.put( "firstToLastRequestInterval", timeDuration.asCompactString() );
         debugItems.put( "firstToLastRequestInterval", timeDuration.asCompactString() );
-        final TimeDuration avgReqDuration = TimeDuration.of( sessionStateBean.getAvgRequestDuration().getLastMillis(), TimeDuration.Unit.MILLISECONDS );
+        final TimeDuration avgReqDuration =  sessionStateBean.getAvgRequestDuration().getAverageAsDuration();
         debugItems.put( "avgRequestDuration", avgReqDuration.asCompactString() );
         debugItems.put( "avgRequestDuration", avgReqDuration.asCompactString() );
         return StringHelper.stringMapToString( debugItems, "," );
         return StringHelper.stringMapToString( debugItems, "," );
     }
     }

+ 1 - 0
server/src/main/java/password/pwm/http/HttpHeader.java

@@ -57,6 +57,7 @@ public enum HttpHeader
 
 
 
 
     XAmb( "X-" + PwmConstants.PWM_APP_NAME + "-Amb" ),
     XAmb( "X-" + PwmConstants.PWM_APP_NAME + "-Amb" ),
+    XDomain( "X-" + PwmConstants.PWM_APP_NAME + "-Domain" ),
     XVersion( "X-" + PwmConstants.PWM_APP_NAME + "-Version" ),
     XVersion( "X-" + PwmConstants.PWM_APP_NAME + "-Version" ),
     XInstance( "X-" + PwmConstants.PWM_APP_NAME + "-Instance" ),
     XInstance( "X-" + PwmConstants.PWM_APP_NAME + "-Instance" ),
     XSessionID( "X-" + PwmConstants.PWM_APP_NAME + "-SessionID" ),
     XSessionID( "X-" + PwmConstants.PWM_APP_NAME + "-SessionID" ),

+ 3 - 7
server/src/main/java/password/pwm/http/IdleTimeoutCalculator.java

@@ -20,14 +20,12 @@
 
 
 package password.pwm.http;
 package password.pwm.http;
 
 
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
+import lombok.Value;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.Permission;
 import password.pwm.Permission;
-import password.pwm.PwmDomain;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.HelpdeskProfile;
 import password.pwm.config.profile.HelpdeskProfile;
@@ -157,9 +155,7 @@ public class IdleTimeoutCalculator
         return Collections.unmodifiableSet( results );
         return Collections.unmodifiableSet( results );
     }
     }
 
 
-    @Getter
-    @AllArgsConstructor
-    @EqualsAndHashCode
+    @Value
     static class MaxIdleTimeoutResult implements Comparable<MaxIdleTimeoutResult>
     static class MaxIdleTimeoutResult implements Comparable<MaxIdleTimeoutResult>
     {
     {
         private final String reason;
         private final String reason;

+ 14 - 22
server/src/main/java/password/pwm/http/PwmCookiePath.java

@@ -23,33 +23,25 @@ package password.pwm.http;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 
 
+import java.util.function.Function;
+
 public enum PwmCookiePath
 public enum PwmCookiePath
 {
 {
-    Domain,
-    Private,
-    CurrentURL,
-    PwmServlet,;
+    Domain( ( pwmRequest ) -> "/" ),
+    Private( ( pwmRequest ) -> PwmConstants.URL_PREFIX_PRIVATE ),
+    CurrentURL( ( pwmRequest ) -> pwmRequest.getURL().toString() ),
+    PwmServlet( ( pwmRequest ) -> pwmRequest.getURL().determinePwmServletPath() ),;
+
+    private final transient Function<PwmRequest, String> suffixFunction;
+
+    PwmCookiePath( final Function<PwmRequest, String> suffixFunction )
+    {
+        this.suffixFunction = suffixFunction;
+    }
 
 
     String toStringPath( final PwmRequest pwmRequest )
     String toStringPath( final PwmRequest pwmRequest )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        switch ( this )
-        {
-            case Domain:
-                return pwmRequest.getBasePath() + "/";
-
-            case Private:
-                return pwmRequest.getBasePath() + PwmConstants.URL_PREFIX_PRIVATE;
-
-            case CurrentURL:
-                return pwmRequest.getURL().toString();
-
-            case PwmServlet:
-                return pwmRequest.getURL().determinePwmServletPath();
-
-            default:
-                throw new IllegalStateException( "undefined CookiePath type: " + this );
-        }
-
+        return pwmRequest.getBasePath() + suffixFunction.apply( pwmRequest );
     }
     }
 }
 }

+ 3 - 3
server/src/main/java/password/pwm/http/PwmHttpRequestWrapper.java

@@ -187,7 +187,7 @@ public class PwmHttpRequestWrapper
         return Collections.unmodifiableMap( outputMap );
         return Collections.unmodifiableMap( outputMap );
     }
     }
 
 
-    public PasswordData readParameterAsPassword( final String name )
+    public Optional<PasswordData> readParameterAsPassword( final String name )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         final int maxLength = Integer.parseInt( appConfig.readAppProperty( AppProperty.HTTP_PARAM_MAX_READ_LENGTH ) );
         final int maxLength = Integer.parseInt( appConfig.readAppProperty( AppProperty.HTTP_PARAM_MAX_READ_LENGTH ) );
@@ -201,10 +201,10 @@ public class PwmHttpRequestWrapper
             if ( sanitizedValue != null )
             if ( sanitizedValue != null )
             {
             {
                 final String trimmedVale = trim ? sanitizedValue.trim() : sanitizedValue;
                 final String trimmedVale = trim ? sanitizedValue.trim() : sanitizedValue;
-                return new PasswordData( trimmedVale );
+                return Optional.of( new PasswordData( trimmedVale ) );
             }
             }
         }
         }
-        return null;
+        return Optional.empty();
     }
     }
 
 
     public String readParameterAsString( final String name, final int maxLength, final Flag... flags )
     public String readParameterAsString( final String name, final int maxLength, final Flag... flags )

+ 8 - 9
server/src/main/java/password/pwm/http/PwmRequest.java

@@ -35,6 +35,7 @@ import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
+import password.pwm.config.AppConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.AccountInformationProfile;
 import password.pwm.config.profile.AccountInformationProfile;
@@ -149,12 +150,7 @@ public class PwmRequest extends PwmHttpRequestWrapper
 
 
     public PwmSession getPwmSession( )
     public PwmSession getPwmSession( )
     {
     {
-        return getPwmSession( this.getPwmDomain() );
-    }
-
-    public PwmSession getPwmSession( final PwmDomain pwmDomain )
-    {
-        return PwmSessionFactory.readPwmSession( this.getHttpServletRequest().getSession(), pwmDomain );
+        return PwmSessionFactory.readPwmSession( this.getHttpServletRequest().getSession(), getPwmDomain() );
     }
     }
 
 
     public SessionLabel getLabel( )
     public SessionLabel getLabel( )
@@ -184,7 +180,9 @@ public class PwmRequest extends PwmHttpRequestWrapper
         {
         {
             return PwmConstants.DEFAULT_LOCALE;
             return PwmConstants.DEFAULT_LOCALE;
         }
         }
-        return getPwmSession().getSessionStateBean().getLocale();
+
+        final Locale userLocale = getPwmSession().getSessionStateBean().getLocale();
+        return userLocale != null ? userLocale : PwmConstants.DEFAULT_LOCALE;
     }
     }
 
 
     public void forwardToJsp( final JspUrl jspURL )
     public void forwardToJsp( final JspUrl jspURL )
@@ -612,9 +610,10 @@ public class PwmRequest extends PwmHttpRequestWrapper
     {
     {
         final String rawContextPath = this.getHttpServletRequest().getContextPath();
         final String rawContextPath = this.getHttpServletRequest().getContextPath();
 
 
-        if ( getAppConfig().isMultiDomain() )
+        final AppConfig appConfig = getAppConfig();
+        if ( appConfig.isMultiDomain() && appConfig.readSettingAsBoolean( PwmSetting.DOMAIN_DOMAIN_PATHS ) )
         {
         {
-            return rawContextPath + "/" + this.getDomainID().stringValue();
+            return rawContextPath + "/" + StringUtil.urlPathEncode( this.getDomainID().stringValue() );
         }
         }
 
 
         return rawContextPath;
         return rawContextPath;

+ 4 - 8
server/src/main/java/password/pwm/http/PwmSession.java

@@ -39,6 +39,7 @@ import password.pwm.ldap.UserInfoBean;
 import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
@@ -105,10 +106,7 @@ public class PwmSession implements Serializable
 
 
         this.sessionStateBean.setSessionLastAccessedTime( Instant.now() );
         this.sessionStateBean.setSessionLastAccessedTime( Instant.now() );
 
 
-        if ( pwmDomain.getStatisticsManager() != null )
-        {
-            pwmDomain.getStatisticsManager().incrementValue( Statistic.HTTP_SESSIONS );
-        }
+        StatisticsClient.incrementStat( pwmDomain.getPwmApplication(), Statistic.HTTP_SESSIONS );
 
 
         pwmDomain.getSessionTrackService().addSessionData( this );
         pwmDomain.getSessionTrackService().addSessionData( this );
         this.sessionManager = new SessionManager( pwmDomain, this );
         this.sessionManager = new SessionManager( pwmDomain, this );
@@ -160,7 +158,7 @@ public class PwmSession implements Serializable
                     pwmRequest.getLabel(),
                     pwmRequest.getLabel(),
                     getSessionStateBean().getLocale(),
                     getSessionStateBean().getLocale(),
                     oldUserInfoBean.getUserIdentity(),
                     oldUserInfoBean.getUserIdentity(),
-                    pwmDomain.getProxyChaiProvider( oldUserInfoBean.getUserIdentity().getLdapProfileID() )
+                    pwmDomain.getProxyChaiProvider( pwmRequest.getLabel(), oldUserInfoBean.getUserIdentity().getLdapProfileID() )
             );
             );
         }
         }
         else
         else
@@ -207,7 +205,6 @@ public class PwmSession implements Serializable
 
 
         UserIdentity userIdentity = null;
         UserIdentity userIdentity = null;
         String userID = null;
         String userID = null;
-        String domain = null;
         String profile = null;
         String profile = null;
 
 
         if ( isAuthenticated() )
         if ( isAuthenticated() )
@@ -218,7 +215,6 @@ public class PwmSession implements Serializable
                 userIdentity = userInfo.getUserIdentity();
                 userIdentity = userInfo.getUserIdentity();
                 userID = userInfo.getUsername();
                 userID = userInfo.getUsername();
                 profile = userIdentity.getLdapProfileID();
                 profile = userIdentity.getLdapProfileID();
-                domain = userIdentity.getDomainID().toString();
             }
             }
             catch ( final PwmUnrecoverableException e )
             catch ( final PwmUnrecoverableException e )
             {
             {
@@ -230,7 +226,7 @@ public class PwmSession implements Serializable
                 .sessionID( ssBean.getSessionID() )
                 .sessionID( ssBean.getSessionID() )
                 .userID( userIdentity == null ? null : userIdentity.toDelimitedKey() )
                 .userID( userIdentity == null ? null : userIdentity.toDelimitedKey() )
                 .username( userID )
                 .username( userID )
-                .domain( domain )
+                .domain( domainID.stringValue() )
                 .profile( profile )
                 .profile( profile )
                 .sourceAddress( ssBean.getSrcAddress() )
                 .sourceAddress( ssBean.getSrcAddress() )
                 .sourceHostname( ssBean.getSrcHostname() )
                 .sourceHostname( ssBean.getSrcHostname() )

+ 2 - 2
server/src/main/java/password/pwm/http/auth/BasicFilterAuthenticationProvider.java

@@ -32,7 +32,7 @@ import password.pwm.ldap.auth.PwmAuthenticationSource;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.BasicAuthInfo;
 import password.pwm.util.BasicAuthInfo;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
@@ -84,7 +84,7 @@ public class BasicFilterAuthenticationProvider implements PwmHttpFilterAuthentic
         {
         {
             if ( e.getError() == PwmError.ERROR_DIRECTORY_UNAVAILABLE )
             if ( e.getError() == PwmError.ERROR_DIRECTORY_UNAVAILABLE )
             {
             {
-                StatisticsManager.incrementStat( pwmRequest, Statistic.LDAP_UNAVAILABLE_COUNT );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.LDAP_UNAVAILABLE_COUNT );
             }
             }
             throw new PwmUnrecoverableException( e.getError() );
             throw new PwmUnrecoverableException( e.getError() );
         }
         }

+ 2 - 0
server/src/main/java/password/pwm/http/bean/DisplayElement.java

@@ -20,12 +20,14 @@
 
 
 package password.pwm.http.bean;
 package password.pwm.http.bean;
 
 
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.Getter;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
 import java.util.List;
 import java.util.List;
 
 
 @Getter
 @Getter
+@EqualsAndHashCode
 public class DisplayElement implements Serializable
 public class DisplayElement implements Serializable
 {
 {
     private String key;
     private String key;

+ 1 - 1
server/src/main/java/password/pwm/http/bean/SetupOtpBean.java

@@ -22,7 +22,7 @@ package password.pwm.http.bean;
 
 
 import password.pwm.config.option.SessionBeanMode;
 import password.pwm.config.option.SessionBeanMode;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.operations.otp.OTPUserRecord;
+import password.pwm.svc.otp.OTPUserRecord;
 
 
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.NoSuchProviderException;

+ 213 - 0
server/src/main/java/password/pwm/http/filter/DomainInitFilter.java

@@ -0,0 +1,213 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2020 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.http.filter;
+
+import password.pwm.PwmApplication;
+import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
+import password.pwm.bean.DomainID;
+import password.pwm.config.PwmSetting;
+import password.pwm.error.PwmException;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.ContextManager;
+import password.pwm.http.ProcessStatus;
+import password.pwm.http.PwmURL;
+import password.pwm.util.logging.PwmLogger;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public class DomainInitFilter implements Filter
+{
+    private static final PwmLogger LOGGER = PwmLogger.forClass( DomainInitFilter.class );
+
+    @Override
+    public void init( final FilterConfig filterConfig )
+            throws ServletException
+    {
+    }
+
+    @Override
+    public void destroy()
+    {
+    }
+
+    @Override
+    public void doFilter(
+            final ServletRequest servletRequest,
+            final ServletResponse servletResponse,
+            final FilterChain filterChain
+    )
+            throws IOException, ServletException
+    {
+        final HttpServletRequest req = ( HttpServletRequest ) servletRequest;
+        final HttpServletResponse resp = ( HttpServletResponse ) servletResponse;
+
+        final PwmApplication localPwmApplication;
+        try
+        {
+            localPwmApplication = ContextManager.getPwmApplication( req );
+        }
+        catch ( final PwmException e )
+        {
+            LOGGER.error( () -> "unable to load pwmApplication: " + e.getMessage() );
+            throw new ServletException( e.getMessage() );
+        }
+
+        if ( initializeDomainIdInRequest( localPwmApplication, req, resp ) == ProcessStatus.Halt )
+        {
+            return;
+        }
+
+        filterChain.doFilter( req, resp );
+    }
+
+    ProcessStatus initializeDomainIdInRequest(
+            final PwmApplication pwmApplication,
+            final HttpServletRequest req,
+            final HttpServletResponse resp
+    )
+            throws IOException
+    {
+        if ( pwmApplication.isMultiDomain() )
+        {
+            final Optional<DomainID> requestDomainID = readDomainFromRequest( pwmApplication, req );
+            if ( requestDomainID.isPresent() )
+            {
+                req.setAttribute( PwmConstants.REQUEST_ATTR_DOMAIN, requestDomainID.get().stringValue() );
+            }
+            else
+            {
+                try
+                {
+                    final boolean pathMode = pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.DOMAIN_DOMAIN_PATHS );
+                    if ( pathMode )
+                    {
+                        final DomainID redirectDomain = pwmApplication.getAdminDomain().getDomainID();
+                        final String redirectUrl = req.getContextPath() + "/" + redirectDomain.stringValue() + "/";
+                        resp.sendRedirect( redirectUrl );
+                        LOGGER.debug( () -> "request does not indicate domain, redirecting to admin domain url: " + redirectUrl );
+                        return ProcessStatus.Halt;
+                    }
+                    else
+                    {
+                        throw new IllegalStateException( "domain not specified in request and admin domain is not configured." );
+                    }
+                }
+                catch ( final PwmUnrecoverableException e )
+                {
+                    final String msg = "error redirecting non-domain request to admin domain: " + e.getMessage();
+                    resp.sendError( 500, msg );
+                    LOGGER.error( () -> msg );
+                    return ProcessStatus.Halt;
+                }
+            }
+        }
+        else
+        {
+            final String domainStr = pwmApplication.getConfig().getDomainIDs().iterator().next();
+            req.setAttribute( PwmConstants.REQUEST_ATTR_DOMAIN, domainStr );
+        }
+
+        return ProcessStatus.Continue;
+    }
+
+    private static Optional<DomainID> readDomainFromRequest( final PwmApplication pwmApplication, final HttpServletRequest req )
+    {
+        final boolean pathMode = pwmApplication.getConfig().readSettingAsBoolean( PwmSetting.DOMAIN_DOMAIN_PATHS );
+        if ( pathMode )
+        {
+            final Optional<DomainID> readDomainID = readDomainFromPathRequest( pwmApplication, req );
+            if ( readDomainID.isPresent() )
+            {
+                return readDomainID;
+            }
+        }
+
+        final Optional<DomainID> readDomainID = readDomainFromDomainRequest( pwmApplication, req );
+        if ( readDomainID.isPresent() )
+        {
+            return readDomainID;
+        }
+
+        if ( !pathMode )
+        {
+            try
+            {
+                return Optional.of( pwmApplication.getAdminDomain().getDomainID() );
+            }
+            catch ( final PwmUnrecoverableException e )
+            {
+                throw new IllegalStateException( "domain not specified in request and admin domain is not configured." );
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    private static Optional<DomainID> readDomainFromDomainRequest( final PwmApplication pwmApplication, final HttpServletRequest req )
+    {
+        final URI uri = URI.create( req.getRequestURL().toString() );
+        final String host = uri.getHost();
+
+        for ( final PwmDomain pwmDomain : pwmApplication.domains().values() )
+        {
+            final List<String> hostMatches = pwmDomain.getConfig().readSettingAsStringArray( PwmSetting.DOMAIN_HOSTS );
+            if ( hostMatches.contains( host ) )
+            {
+                return Optional.of( pwmDomain.getDomainID() );
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    private static Optional<DomainID> readDomainFromPathRequest( final PwmApplication pwmApplication, final HttpServletRequest req )
+    {
+        final PwmURL pwmURL = PwmURL.create( req, pwmApplication.getConfig() );
+        final List<String> urlPaths = pwmURL.splitPaths();
+        if ( urlPaths.size() <= 1 )
+        {
+            return Optional.empty();
+        }
+
+        final String domainPath = urlPaths.get( 1 );
+
+        final Set<String> domains = pwmApplication.getConfig().getDomainIDs();
+
+        if ( domains.contains( domainPath ) )
+        {
+            return Optional.of( DomainID.create( domainPath ) );
+        }
+        return Optional.empty();
+    }
+}

+ 2 - 2
server/src/main/java/password/pwm/http/filter/ObsoleteUrlFilter.java

@@ -30,7 +30,7 @@ import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
@@ -103,7 +103,7 @@ public class ObsoleteUrlFilter extends AbstractPwmFilter
                             + requestServletUrl
                             + requestServletUrl
                             + "' detected, redirecting to canonical URL of '"
                             + "' detected, redirecting to canonical URL of '"
                             + pwmServletDefinition.servletUrl() + "'" );
                             + pwmServletDefinition.servletUrl() + "'" );
-                    StatisticsManager.incrementStat( pwmRequest, Statistic.OBSOLETE_URL_REQUESTS );
+                    StatisticsClient.incrementStat( pwmRequest, Statistic.OBSOLETE_URL_REQUESTS );
                     pwmRequest.getPwmResponse().sendRedirect( pwmServletDefinition );
                     pwmRequest.getPwmResponse().sendRedirect( pwmServletDefinition );
                     return ProcessStatus.Halt;
                     return ProcessStatus.Halt;
                 }
                 }

+ 24 - 82
server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java

@@ -25,7 +25,6 @@ import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmApplicationMode;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
-import password.pwm.bean.DomainID;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.config.AppConfig;
 import password.pwm.config.AppConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
@@ -37,7 +36,7 @@ import password.pwm.http.ContextManager;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.IdleTimeoutCalculator;
 import password.pwm.http.IdleTimeoutCalculator;
 import password.pwm.http.JspUrl;
 import password.pwm.http.JspUrl;
-import password.pwm.http.ProcessStatus;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmResponse;
 import password.pwm.http.PwmResponse;
@@ -46,7 +45,7 @@ import password.pwm.http.PwmSessionFactory;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsService;
 import password.pwm.util.IPMatcher;
 import password.pwm.util.IPMatcher;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
@@ -76,11 +75,9 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Optional;
-import java.util.Set;
 
 
 public class RequestInitializationFilter implements Filter
 public class RequestInitializationFilter implements Filter
 {
 {
-
     private static final PwmLogger LOGGER = PwmLogger.forClass( RequestInitializationFilter.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( RequestInitializationFilter.class );
 
 
     @Override
     @Override
@@ -98,7 +95,9 @@ public class RequestInitializationFilter implements Filter
     public void doFilter(
     public void doFilter(
             final ServletRequest servletRequest,
             final ServletRequest servletRequest,
             final ServletResponse servletResponse,
             final ServletResponse servletResponse,
-            final FilterChain filterChain ) throws IOException, ServletException
+            final FilterChain filterChain
+    )
+            throws IOException, ServletException
     {
     {
 
 
         final HttpServletRequest req = ( HttpServletRequest ) servletRequest;
         final HttpServletRequest req = ( HttpServletRequest ) servletRequest;
@@ -116,11 +115,6 @@ public class RequestInitializationFilter implements Filter
             throw new ServletException( e.getMessage() );
             throw new ServletException( e.getMessage() );
         }
         }
 
 
-        if ( initializeDomainIdInRequest( localPwmApplication, req, resp ) == ProcessStatus.Halt )
-        {
-            return;
-        }
-
         final PwmURL pwmURL = PwmURL.create( req, localPwmApplication.getConfig() );
         final PwmURL pwmURL = PwmURL.create( req, localPwmApplication.getConfig() );
 
 
         if ( pwmURL.isResourceURL() )
         if ( pwmURL.isResourceURL() )
@@ -164,8 +158,6 @@ public class RequestInitializationFilter implements Filter
             return;
             return;
         }
         }
 
 
-
-
         try
         try
         {
         {
             localPwmApplication.getActiveServletRequests().incrementAndGet();
             localPwmApplication.getActiveServletRequests().incrementAndGet();
@@ -359,8 +351,7 @@ public class RequestInitializationFilter implements Filter
             resp.setHeader( HttpHeader.ContentLanguage, pwmRequest.getLocale().toLanguageTag() );
             resp.setHeader( HttpHeader.ContentLanguage, pwmRequest.getLocale().toLanguageTag() );
         }
         }
 
 
-        addStaticResponseHeaders( pwmApplication, resp.getHttpServletResponse() );
-
+        addStaticResponseHeaders( pwmApplication, pwmRequest.getHttpServletRequest(), resp.getHttpServletResponse() );
 
 
         if ( pwmSession != null )
         if ( pwmSession != null )
         {
         {
@@ -386,6 +377,7 @@ public class RequestInitializationFilter implements Filter
 
 
     public static void addStaticResponseHeaders(
     public static void addStaticResponseHeaders(
             final PwmApplication pwmApplication,
             final PwmApplication pwmApplication,
+            final HttpServletRequest req,
             final HttpServletResponse resp
             final HttpServletResponse resp
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
@@ -399,6 +391,7 @@ public class RequestInitializationFilter implements Filter
         final boolean includeXXSSProtection = Boolean.parseBoolean( config.readAppProperty( AppProperty.HTTP_HEADER_SEND_XXSSPROTECTION ) );
         final boolean includeXXSSProtection = Boolean.parseBoolean( config.readAppProperty( AppProperty.HTTP_HEADER_SEND_XXSSPROTECTION ) );
         final boolean includeXFrameDeny = config.readSettingAsBoolean( PwmSetting.SECURITY_PREVENT_FRAMING );
         final boolean includeXFrameDeny = config.readSettingAsBoolean( PwmSetting.SECURITY_PREVENT_FRAMING );
         final boolean includeXAmb = Boolean.parseBoolean( config.readAppProperty( AppProperty.HTTP_HEADER_SEND_XAMB ) );
         final boolean includeXAmb = Boolean.parseBoolean( config.readAppProperty( AppProperty.HTTP_HEADER_SEND_XAMB ) );
+        final boolean includeDomain = Boolean.parseBoolean( config.readAppProperty( AppProperty.HTTP_HEADER_SEND_XDOMAIN ) );
 
 
         makeNoiseHeader( pwmApplication, config ).ifPresent( noiseHeader -> resp.setHeader( HttpHeader.XNoise.getHttpName(), noiseHeader ) );
         makeNoiseHeader( pwmApplication, config ).ifPresent( noiseHeader -> resp.setHeader( HttpHeader.XNoise.getHttpName(), noiseHeader ) );
 
 
@@ -440,7 +433,18 @@ public class RequestInitializationFilter implements Filter
             ) );
             ) );
         }
         }
 
 
-        resp.setHeader( HttpHeader.CacheControl.getHttpName(), "no-cache, no-store, must-revalidate, proxy-revalidate" );
+        if ( includeDomain && pwmApplication.isMultiDomain() )
+        {
+            resp.setHeader( HttpHeader.XDomain.getHttpName(), PwmHttpRequestWrapper.readDomainIdFromRequest( req ).stringValue() );
+        }
+
+        {
+            final String cacheControl = pwmApplication.getConfig().readAppProperty( AppProperty.HTTP_HEADER_CACHE_CONTROL );
+            if ( StringUtil.notEmpty( cacheControl ) )
+            {
+                resp.setHeader( HttpHeader.CacheControl.getHttpName(), cacheControl );
+            }
+        }
     }
     }
 
 
 
 
@@ -617,7 +621,7 @@ public class RequestInitializationFilter implements Filter
         checkTrial( pwmRequest );
         checkTrial( pwmRequest );
 
 
         // check intruder
         // check intruder
-        pwmRequest.getPwmDomain().getIntruderManager().convenience().checkAddressAndSession( pwmRequest.getPwmSession() );
+        pwmRequest.getPwmDomain().getIntruderService().client().checkAddressAndSession( pwmRequest.getPwmSession() );
     }
     }
 
 
     private static void checkIfSourceAddressChanged( final PwmRequest pwmRequest )
     private static void checkIfSourceAddressChanged( final PwmRequest pwmRequest )
@@ -813,14 +817,14 @@ public class RequestInitializationFilter implements Filter
     {
     {
         if ( PwmConstants.TRIAL_MODE )
         if ( PwmConstants.TRIAL_MODE )
         {
         {
-            final StatisticsManager statisticsManager = pwmRequest.getPwmDomain().getStatisticsManager();
-            final String currentAuthString = statisticsManager.getStatBundleForKey( StatisticsManager.KEY_CURRENT ).getStatistic( Statistic.AUTHENTICATIONS );
+            final StatisticsService statisticsManager = pwmRequest.getPwmDomain().getStatisticsManager();
+            final String currentAuthString = statisticsManager.getStatBundleForKey( StatisticsService.KEY_CURRENT ).getStatistic( Statistic.AUTHENTICATIONS );
             if ( new BigInteger( currentAuthString ).compareTo( BigInteger.valueOf( PwmConstants.TRIAL_MAX_AUTHENTICATIONS ) ) > 0 )
             if ( new BigInteger( currentAuthString ).compareTo( BigInteger.valueOf( PwmConstants.TRIAL_MAX_AUTHENTICATIONS ) ) > 0 )
             {
             {
                 throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_TRIAL_VIOLATION, "maximum usage per server startup exceeded" ) );
                 throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_TRIAL_VIOLATION, "maximum usage per server startup exceeded" ) );
             }
             }
 
 
-            final String totalAuthString = statisticsManager.getStatBundleForKey( StatisticsManager.KEY_CUMULATIVE ).getStatistic( Statistic.AUTHENTICATIONS );
+            final String totalAuthString = statisticsManager.getStatBundleForKey( StatisticsService.KEY_CUMULATIVE ).getStatistic( Statistic.AUTHENTICATIONS );
             if ( new BigInteger( totalAuthString ).compareTo( BigInteger.valueOf( PwmConstants.TRIAL_MAX_TOTAL_AUTH ) ) > 0 )
             if ( new BigInteger( totalAuthString ).compareTo( BigInteger.valueOf( PwmConstants.TRIAL_MAX_TOTAL_AUTH ) ) > 0 )
             {
             {
                 throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_TRIAL_VIOLATION, "maximum usage for this server has been exceeded" ) );
                 throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_TRIAL_VIOLATION, "maximum usage for this server has been exceeded" ) );
@@ -870,66 +874,4 @@ public class RequestInitializationFilter implements Filter
 
 
         return Optional.empty();
         return Optional.empty();
     }
     }
-
-    ProcessStatus initializeDomainIdInRequest(
-            final PwmApplication pwmApplication,
-            final HttpServletRequest req,
-            final HttpServletResponse resp
-    )
-            throws IOException
-    {
-        if ( pwmApplication.isMultiDomain() )
-        {
-            final Optional<DomainID> requestDomainID = readDomainFromRequest( pwmApplication, req );
-            if ( requestDomainID.isPresent() )
-            {
-                req.setAttribute( PwmConstants.REQUEST_ATTR_DOMAIN, requestDomainID.get().stringValue() );
-            }
-            else
-            {
-                try
-                {
-                    final DomainID redirectDomain = pwmApplication.getAdminDomain().getDomainID();
-                    final String redirectUrl = req.getContextPath() + "/" + redirectDomain.stringValue() + "/";
-                    resp.sendRedirect( redirectUrl );
-                    LOGGER.debug( () -> "request does not indicate domain, redirecting to admin domain url: " + redirectUrl );
-                    return ProcessStatus.Halt;
-                }
-                catch ( final PwmUnrecoverableException e )
-                {
-                    final String msg = "error redirecting non-domain request to admin domain: " + e.getMessage();
-                    resp.sendError( 500, msg );
-                    LOGGER.error( () -> msg );
-                    return ProcessStatus.Halt;
-                }
-            }
-        }
-        else
-        {
-            final String domainStr = pwmApplication.getConfig().getDomainIDs().iterator().next();
-            req.setAttribute( PwmConstants.REQUEST_ATTR_DOMAIN, domainStr );
-        }
-
-        return ProcessStatus.Continue;
-    }
-
-    public static Optional<DomainID> readDomainFromRequest( final PwmApplication pwmApplication, final HttpServletRequest req )
-    {
-        final PwmURL pwmURL = PwmURL.create( req, pwmApplication.getConfig() );
-        final List<String> urlPaths = pwmURL.splitPaths();
-        if ( urlPaths.size() <= 1 )
-        {
-            return Optional.empty();
-        }
-
-        final String domainPath = urlPaths.get( 1 );
-
-        final Set<String> domains = pwmApplication.getConfig().getDomainIDs();
-
-        if ( domains.contains( domainPath ) )
-        {
-            return Optional.of( DomainID.create( domainPath ) );
-        }
-        return Optional.empty();
-    }
 }
 }

+ 3 - 5
server/src/main/java/password/pwm/http/filter/SessionFilter.java

@@ -45,6 +45,7 @@ import password.pwm.http.PwmSession;
 import password.pwm.http.PwmURL;
 import password.pwm.http.PwmURL;
 import password.pwm.svc.stats.AvgStatistic;
 import password.pwm.svc.stats.AvgStatistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -137,7 +138,7 @@ public class SessionFilter extends AbstractPwmFilter
         pwmRequest.debugHttpRequestToLog( "completed", () -> requestExecuteTime );
         pwmRequest.debugHttpRequestToLog( "completed", () -> requestExecuteTime );
         pwmRequest.getPwmDomain().getStatisticsManager().updateAverageValue( AvgStatistic.AVG_REQUEST_PROCESS_TIME, requestExecuteTime.asMillis() );
         pwmRequest.getPwmDomain().getStatisticsManager().updateAverageValue( AvgStatistic.AVG_REQUEST_PROCESS_TIME, requestExecuteTime.asMillis() );
         pwmRequest.getPwmSession().getSessionStateBean().getRequestCount().incrementAndGet();
         pwmRequest.getPwmSession().getSessionStateBean().getRequestCount().incrementAndGet();
-        pwmRequest.getPwmSession().getSessionStateBean().getAvgRequestDuration().update( requestExecuteTime.asMillis() );
+        pwmRequest.getPwmSession().getSessionStateBean().getAvgRequestDuration().update( requestExecuteTime );
     }
     }
 
 
     private ProcessStatus handleStandardRequestOperations(
     private ProcessStatus handleStandardRequestOperations(
@@ -186,10 +187,7 @@ public class SessionFilter extends AbstractPwmFilter
         // update last request time.
         // update last request time.
         ssBean.setSessionLastAccessedTime( Instant.now() );
         ssBean.setSessionLastAccessedTime( Instant.now() );
 
 
-        if ( pwmDomain.getStatisticsManager() != null )
-        {
-            pwmDomain.getStatisticsManager().incrementValue( Statistic.HTTP_REQUESTS );
-        }
+        StatisticsClient.incrementStat( pwmDomain.getPwmApplication(), Statistic.HTTP_REQUESTS );
 
 
         return ProcessStatus.Continue;
         return ProcessStatus.Continue;
     }
     }

+ 9 - 24
server/src/main/java/password/pwm/http/servlet/AbstractPwmServlet.java

@@ -32,8 +32,10 @@ import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.bean.PwmSessionBean;
 import password.pwm.http.bean.PwmSessionBean;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.Validator;
 import password.pwm.util.Validator;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.logging.PwmLogLevel;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.SecureEngine;
 import password.pwm.util.secure.SecureEngine;
@@ -47,10 +49,12 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.StringWriter;
 import java.util.Collection;
 import java.util.Collection;
+import java.util.function.Supplier;
 
 
 public abstract class AbstractPwmServlet extends HttpServlet implements PwmServlet
 public abstract class AbstractPwmServlet extends HttpServlet implements PwmServlet
 {
 {
 
 
+
     private static final PwmLogger LOGGER = PwmLogger.forClass( AbstractPwmServlet.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( AbstractPwmServlet.class );
 
 
     @Override
     @Override
@@ -228,17 +232,9 @@ public abstract class AbstractPwmServlet extends HttpServlet implements PwmServl
         {
         {
             case ERROR_DIRECTORY_UNAVAILABLE:
             case ERROR_DIRECTORY_UNAVAILABLE:
                 LOGGER.fatal( pwmRequest.getLabel(), () -> e.getErrorInformation().toDebugStr() );
                 LOGGER.fatal( pwmRequest.getLabel(), () -> e.getErrorInformation().toDebugStr() );
-                try
-                {
-                    pwmDomain.getStatisticsManager().incrementValue( Statistic.LDAP_UNAVAILABLE_COUNT );
-                }
-                catch ( final Throwable e1 )
-                {
-                    //noop
-                }
+                StatisticsClient.incrementStat( pwmRequest, Statistic.LDAP_UNAVAILABLE_COUNT );
                 break;
                 break;
 
 
-
             case ERROR_PASSWORD_REQUIRED:
             case ERROR_PASSWORD_REQUIRED:
                 LOGGER.warn(
                 LOGGER.warn(
                         () -> "attempt to access functionality requiring password authentication, but password not yet supplied by actor, forwarding to password Login page" );
                         () -> "attempt to access functionality requiring password authentication, but password not yet supplied by actor, forwarding to password Login page" );
@@ -255,23 +251,12 @@ public abstract class AbstractPwmServlet extends HttpServlet implements PwmServl
                 }
                 }
                 break;
                 break;
 
 
-
             case ERROR_INTERNAL:
             case ERROR_INTERNAL:
             default:
             default:
-                LOGGER.fatal( pwmRequest.getLabel(), () -> "unexpected error: " + e.getErrorInformation().toDebugStr() );
-                try
-                {
-                    // try to update stats
-                    if ( pwmDomain != null )
-                    {
-                        pwmDomain.getStatisticsManager().incrementValue( Statistic.PWM_UNKNOWN_ERRORS );
-                    }
-                }
-                catch ( final Throwable e1 )
-                {
-                    //noop
-                }
-                break;
+                final Supplier<CharSequence> msg = () -> "unexpected error: " + e.getErrorInformation().toDebugStr();
+                final PwmLogLevel level = e.getError().isTrivial() ? PwmLogLevel.TRACE : PwmLogLevel.ERROR;
+                LOGGER.log( level, pwmRequest.getLabel(), msg );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.PWM_UNKNOWN_ERRORS );
         }
         }
         return false;
         return false;
     }
     }

+ 5 - 5
server/src/main/java/password/pwm/http/servlet/ClientApiServlet.java

@@ -47,7 +47,7 @@ import password.pwm.i18n.Display;
 import password.pwm.svc.sessiontrack.UserAgentUtils;
 import password.pwm.svc.sessiontrack.UserAgentUtils;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsService;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
@@ -58,7 +58,7 @@ import password.pwm.util.secure.SecureEngine;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.rest.RestHealthServer;
 import password.pwm.ws.server.rest.RestHealthServer;
 import password.pwm.ws.server.rest.RestStatisticsServer;
 import password.pwm.ws.server.rest.RestStatisticsServer;
-import password.pwm.ws.server.rest.bean.HealthData;
+import password.pwm.ws.server.rest.bean.PublicHealthData;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.annotation.WebServlet;
@@ -223,7 +223,7 @@ public class ClientApiServlet extends ControlledPwmServlet
 
 
         try
         try
         {
         {
-            final HealthData jsonOutput = RestHealthServer.processGetHealthCheckData(
+            final PublicHealthData jsonOutput = RestHealthServer.processGetHealthCheckData(
                     pwmRequest.getPwmDomain(),
                     pwmRequest.getPwmDomain(),
                     pwmRequest.getLocale() );
                     pwmRequest.getLocale() );
             final RestResultBean restResultBean = RestResultBean.withData( jsonOutput );
             final RestResultBean restResultBean = RestResultBean.withData( jsonOutput );
@@ -427,7 +427,7 @@ public class ClientApiServlet extends ControlledPwmServlet
             final Map<String, Map<String, String>> ldapProfiles = new LinkedHashMap<>();
             final Map<String, Map<String, String>> ldapProfiles = new LinkedHashMap<>();
             for ( final String ldapProfile : pwmDomain.getConfig().getLdapProfiles().keySet() )
             for ( final String ldapProfile : pwmDomain.getConfig().getLdapProfiles().keySet() )
             {
             {
-                final Map<String, String> contexts = pwmDomain.getConfig().getLdapProfiles().get( ldapProfile ).getSelectableContexts( pwmDomain );
+                final Map<String, String> contexts = pwmDomain.getConfig().getLdapProfiles().get( ldapProfile ).getSelectableContexts( pwmRequest.getLabel(), pwmDomain );
                 ldapProfiles.put( ldapProfile, contexts );
                 ldapProfiles.put( ldapProfile, contexts );
             }
             }
             settingMap.put( "ldapProfiles", ldapProfiles );
             settingMap.put( "ldapProfiles", ldapProfiles );
@@ -477,7 +477,7 @@ public class ClientApiServlet extends ControlledPwmServlet
         final String statName = pwmRequest.readParameterAsString( "statName" );
         final String statName = pwmRequest.readParameterAsString( "statName" );
         final String days = pwmRequest.readParameterAsString( "days" );
         final String days = pwmRequest.readParameterAsString( "days" );
 
 
-        final StatisticsManager statisticsManager = pwmRequest.getPwmDomain().getStatisticsManager();
+        final StatisticsService statisticsManager = pwmRequest.getPwmDomain().getStatisticsManager();
         final RestStatisticsServer.OutputVersion1.JsonOutput jsonOutput = new RestStatisticsServer.OutputVersion1.JsonOutput();
         final RestStatisticsServer.OutputVersion1.JsonOutput jsonOutput = new RestStatisticsServer.OutputVersion1.JsonOutput();
         jsonOutput.EPS = RestStatisticsServer.OutputVersion1.addEpsStats( statisticsManager );
         jsonOutput.EPS = RestStatisticsServer.OutputVersion1.addEpsStats( statisticsManager );
 
 

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/ControlledPwmServlet.java

@@ -213,7 +213,7 @@ public abstract class ControlledPwmServlet extends AbstractPwmServlet implements
     public @interface ActionHandler
     public @interface ActionHandler
     {
     {
         String action( );
         String action( );
-    }
+            }
 
 
     private Map<String, Method> createMethodCache()
     private Map<String, Method> createMethodCache()
     {
     {

+ 6 - 6
server/src/main/java/password/pwm/http/servlet/DeleteAccountServlet.java

@@ -45,6 +45,7 @@ import password.pwm.http.bean.DeleteAccountBean;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.operations.ActionExecutor;
 import password.pwm.util.operations.ActionExecutor;
@@ -178,13 +179,13 @@ public class DeleteAccountServlet extends ControlledPwmServlet
         if ( !deleteAccountBean.isAgreementPassed() )
         if ( !deleteAccountBean.isAgreementPassed() )
         {
         {
             deleteAccountBean.setAgreementPassed( true );
             deleteAccountBean.setAgreementPassed( true );
-            final AuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createUserAuditRecord(
+            final AuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createUserAuditRecord(
                     AuditEvent.AGREEMENT_PASSED,
                     AuditEvent.AGREEMENT_PASSED,
                     pwmRequest.getUserInfoIfLoggedIn(),
                     pwmRequest.getUserInfoIfLoggedIn(),
                     pwmRequest.getLabel(),
                     pwmRequest.getLabel(),
                     ProfileDefinition.DeleteAccount.toString()
                     ProfileDefinition.DeleteAccount.toString()
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+            AuditServiceClient.submit( pwmRequest,  auditRecord );
         }
         }
 
 
         return ProcessStatus.Continue;
         return ProcessStatus.Continue;
@@ -194,7 +195,7 @@ public class DeleteAccountServlet extends ControlledPwmServlet
     private ProcessStatus handleDeleteRequest(
     private ProcessStatus handleDeleteRequest(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
-            throws ServletException, IOException, PwmUnrecoverableException, ChaiUnavailableException
+            throws IOException, PwmUnrecoverableException, ChaiUnavailableException
     {
     {
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         final DeleteAccountProfile deleteAccountProfile = getProfile( pwmRequest );
         final DeleteAccountProfile deleteAccountProfile = getProfile( pwmRequest );
@@ -208,7 +209,6 @@ public class DeleteAccountServlet extends ControlledPwmServlet
             {
             {
                 LOGGER.debug( pwmRequest, () -> "executing configured actions to user " + userIdentity );
                 LOGGER.debug( pwmRequest, () -> "executing configured actions to user " + userIdentity );
 
 
-
                 final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings( pwmDomain, userIdentity )
                 final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings( pwmDomain, userIdentity )
                         .setExpandPwmMacros( true )
                         .setExpandPwmMacros( true )
                         .setMacroMachine( pwmRequest.getPwmSession().getSessionManager().getMacroMachine( ) )
                         .setMacroMachine( pwmRequest.getPwmSession().getSessionManager().getMacroMachine( ) )
@@ -230,7 +230,7 @@ public class DeleteAccountServlet extends ControlledPwmServlet
         sendProfileUpdateEmailNotice( pwmRequest );
         sendProfileUpdateEmailNotice( pwmRequest );
 
 
         // mark the event log
         // mark the event log
-        pwmDomain.getAuditManager().submit( AuditEvent.DELETE_ACCOUNT, pwmRequest.getPwmSession().getUserInfo(), pwmRequest.getPwmSession() );
+        AuditServiceClient.submitUserEvent( pwmRequest, AuditEvent.DELETE_ACCOUNT, pwmRequest.getPwmSession().getUserInfo() );
 
 
         final String nextUrl = deleteAccountProfile.readSettingAsString( PwmSetting.DELETE_ACCOUNT_NEXT_URL );
         final String nextUrl = deleteAccountProfile.readSettingAsString( PwmSetting.DELETE_ACCOUNT_NEXT_URL );
         if ( nextUrl != null && !nextUrl.isEmpty() )
         if ( nextUrl != null && !nextUrl.isEmpty() )
@@ -244,7 +244,7 @@ public class DeleteAccountServlet extends ControlledPwmServlet
         // perform ldap entry delete.
         // perform ldap entry delete.
         if ( deleteAccountProfile.readSettingAsBoolean( PwmSetting.DELETE_ACCOUNT_DELETE_USER_ENTRY ) )
         if ( deleteAccountProfile.readSettingAsBoolean( PwmSetting.DELETE_ACCOUNT_DELETE_USER_ENTRY ) )
         {
         {
-            final ChaiUser chaiUser = pwmDomain.getProxiedChaiUser( pwmRequest.getUserInfoIfLoggedIn() );
+            final ChaiUser chaiUser = pwmDomain.getProxiedChaiUser( pwmRequest.getLabel(), pwmRequest.getUserInfoIfLoggedIn() );
             try
             try
             {
             {
                 chaiUser.getChaiProvider().deleteEntry( chaiUser.getEntryDN() );
                 chaiUser.getChaiProvider().deleteEntry( chaiUser.getEntryDN() );

+ 11 - 10
server/src/main/java/password/pwm/http/servlet/ForgottenUsernameServlet.java

@@ -44,6 +44,7 @@ import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.CaptchaUtility;
 import password.pwm.util.CaptchaUtility;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -162,7 +163,7 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
                     forgottenUsernameForm, ssBean.getLocale() );
                     forgottenUsernameForm, ssBean.getLocale() );
 
 
             // check for intruder search
             // check for intruder search
-            pwmDomain.getIntruderManager().convenience().checkAttributes( formValues );
+            pwmDomain.getIntruderService().client().checkAttributes( formValues );
 
 
             // see if the values meet the configured form requirements.
             // see if the values meet the configured form requirements.
             FormUtility.validateFormValues( pwmRequest.getDomainConfig(), formValues, ssBean.getLocale() );
             FormUtility.validateFormValues( pwmRequest.getDomainConfig(), formValues, ssBean.getLocale() );
@@ -195,15 +196,15 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
 
 
             if ( userIdentity == null )
             if ( userIdentity == null )
             {
             {
-                pwmDomain.getIntruderManager().convenience().markAddressAndSession( pwmRequest );
-                pwmDomain.getStatisticsManager().incrementValue( Statistic.FORGOTTEN_USERNAME_FAILURES );
+                pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.FORGOTTEN_USERNAME_FAILURES );
                 setLastError( pwmRequest, PwmError.ERROR_CANT_MATCH_USER.toInfo() );
                 setLastError( pwmRequest, PwmError.ERROR_CANT_MATCH_USER.toInfo() );
                 forwardToFormJsp( pwmRequest );
                 forwardToFormJsp( pwmRequest );
                 return;
                 return;
             }
             }
 
 
             // make sure the user isn't locked.
             // make sure the user isn't locked.
-            pwmDomain.getIntruderManager().convenience().checkUserIdentity( userIdentity );
+            pwmDomain.getIntruderService().client().checkUserIdentity( userIdentity );
 
 
             final UserInfo forgottenUserInfo = UserInfoFactory.newUserInfoUsingProxy(
             final UserInfo forgottenUserInfo = UserInfoFactory.newUserInfoUsingProxy(
                     pwmRequest.getPwmApplication(),
                     pwmRequest.getPwmApplication(),
@@ -214,10 +215,10 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
             // send username
             // send username
             sendUsername( pwmDomain, pwmRequest, forgottenUserInfo );
             sendUsername( pwmDomain, pwmRequest, forgottenUserInfo );
 
 
-            pwmDomain.getIntruderManager().convenience().clearAddressAndSession( pwmSession );
-            pwmDomain.getIntruderManager().convenience().clearAttributes( formValues );
+            pwmDomain.getIntruderService().client().clearAddressAndSession( pwmSession );
+            pwmDomain.getIntruderService().client().clearAttributes( formValues );
 
 
-            pwmDomain.getStatisticsManager().incrementValue( Statistic.FORGOTTEN_USERNAME_SUCCESSES );
+            StatisticsClient.incrementStat( pwmRequest, Statistic.FORGOTTEN_USERNAME_SUCCESSES );
 
 
             // redirect user to success page.
             // redirect user to success page.
             forwardToCompletePage( pwmRequest, userIdentity );
             forwardToCompletePage( pwmRequest, userIdentity );
@@ -232,11 +233,11 @@ public class ForgottenUsernameServlet extends AbstractPwmServlet
                     e.getErrorInformation().getFieldValues() )
                     e.getErrorInformation().getFieldValues() )
                     : e.getErrorInformation();
                     : e.getErrorInformation();
             setLastError( pwmRequest, errorInfo );
             setLastError( pwmRequest, errorInfo );
-            pwmDomain.getIntruderManager().convenience().markAddressAndSession( pwmRequest );
-            pwmDomain.getIntruderManager().convenience().markAttributes( formValues, pwmRequest.getLabel() );
+            pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
+            pwmDomain.getIntruderService().client().markAttributes( formValues, pwmRequest.getLabel() );
         }
         }
 
 
-        pwmDomain.getStatisticsManager().incrementValue( Statistic.FORGOTTEN_USERNAME_FAILURES );
+        StatisticsClient.incrementStat( pwmRequest, Statistic.FORGOTTEN_USERNAME_FAILURES );
         forwardToFormJsp( pwmRequest );
         forwardToFormJsp( pwmRequest );
     }
     }
 
 

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

@@ -25,8 +25,8 @@ import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.Permission;
 import password.pwm.Permission;
-import password.pwm.PwmDomain;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
@@ -52,6 +52,7 @@ import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.FormMap;
 import password.pwm.util.FormMap;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.form.FormUtility;
@@ -237,6 +238,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
 
 
             // check unique fields against ldap
             // check unique fields against ldap
             FormUtility.validateFormValueUniqueness(
             FormUtility.validateFormValueUniqueness(
+                    pwmRequest.getLabel(),
                     pwmDomain,
                     pwmDomain,
                     formValues,
                     formValues,
                     ssBean.getLocale(),
                     ssBean.getLocale(),
@@ -264,7 +266,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
             );
             );
             this.sendUpdateGuestEmailConfirmation( pwmRequest, guestUserInfoBean );
             this.sendUpdateGuestEmailConfirmation( pwmRequest, guestUserInfoBean );
 
 
-            pwmDomain.getStatisticsManager().incrementValue( Statistic.UPDATED_GUESTS );
+            StatisticsClient.incrementStat( pwmRequest, Statistic.UPDATED_GUESTS );
 
 
             //everything good so forward to confirmation page.
             //everything good so forward to confirmation page.
             pwmRequest.getPwmResponse().forwardToSuccessPage( Message.Success_UpdateGuest );
             pwmRequest.getPwmResponse().forwardToSuccessPage( Message.Success_UpdateGuest );
@@ -508,7 +510,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
             //everything good so forward to success page.
             //everything good so forward to success page.
             this.sendGuestUserEmailConfirmation( pwmRequest, userIdentity );
             this.sendGuestUserEmailConfirmation( pwmRequest, userIdentity );
 
 
-            pwmDomain.getStatisticsManager().incrementValue( Statistic.NEW_USERS );
+            StatisticsClient.incrementStat( pwmRequest, Statistic.NEW_USERS );
 
 
             pwmRequest.getPwmResponse().forwardToSuccessPage( Message.Success_CreateGuest );
             pwmRequest.getPwmResponse().forwardToSuccessPage( Message.Success_CreateGuest );
         }
         }
@@ -530,7 +532,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
     private static Instant readExpirationFromRequest(
     private static Instant readExpirationFromRequest(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
-            throws PwmOperationalException, ChaiUnavailableException, ChaiOperationException, PwmUnrecoverableException
+            throws PwmOperationalException, ChaiOperationException, PwmUnrecoverableException
     {
     {
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         final DomainConfig config = pwmDomain.getConfig();
         final DomainConfig config = pwmDomain.getConfig();

+ 9 - 10
server/src/main/java/password/pwm/http/servlet/SetupOtpServlet.java

@@ -24,8 +24,8 @@ import com.novell.ldapchai.exception.ChaiUnavailableException;
 import net.glxn.qrgen.QRCode;
 import net.glxn.qrgen.QRCode;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.Permission;
 import password.pwm.Permission;
-import password.pwm.PwmDomain;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
@@ -45,17 +45,18 @@ import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.SetupOtpBean;
 import password.pwm.http.bean.SetupOtpBean;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationType;
-import password.pwm.svc.PwmService;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.event.UserAuditRecord;
 import password.pwm.svc.event.UserAuditRecord;
+import password.pwm.svc.otp.OTPUserRecord;
+import password.pwm.svc.otp.OtpService;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.Validator;
 import password.pwm.util.Validator;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.util.operations.OtpService;
-import password.pwm.util.operations.otp.OTPUserRecord;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
@@ -191,18 +192,16 @@ public class SetupOtpServlet extends ControlledPwmServlet
                 pwmSession.reloadUserInfoBean( pwmRequest );
                 pwmSession.reloadUserInfoBean( pwmRequest );
 
 
                 // mark the event log
                 // mark the event log
-                final UserAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createUserAuditRecord(
+                final UserAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createUserAuditRecord(
                         AuditEvent.SET_OTP_SECRET,
                         AuditEvent.SET_OTP_SECRET,
                         pwmSession.getUserInfo(),
                         pwmSession.getUserInfo(),
                         pwmSession
                         pwmSession
                 );
                 );
-                pwmDomain.getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
 
 
+                AuditServiceClient.submit( pwmRequest, auditRecord );
+
+                StatisticsClient.incrementStat( pwmRequest, Statistic.SETUP_OTP_SECRET );
 
 
-                if ( pwmDomain.getStatisticsManager() != null && pwmDomain.getStatisticsManager().status() == PwmService.STATUS.OPEN )
-                {
-                    pwmDomain.getStatisticsManager().incrementValue( Statistic.SETUP_OTP_SECRET );
-                }
             }
             }
             catch ( final Exception e )
             catch ( final Exception e )
             {
             {

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

@@ -32,8 +32,8 @@ import com.novell.ldapchai.exception.ChaiValidationException;
 import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiProvider;
 import lombok.Value;
 import lombok.Value;
 import password.pwm.Permission;
 import password.pwm.Permission;
-import password.pwm.PwmDomain;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.ResponseInfoBean;
 import password.pwm.bean.ResponseInfoBean;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
@@ -55,8 +55,10 @@ import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.event.UserAuditRecord;
 import password.pwm.svc.event.UserAuditRecord;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -218,12 +220,13 @@ public class SetupResponsesServlet extends ControlledPwmServlet
             pwmRequest.getPwmDomain().getSessionStateService().clearBean( pwmRequest, SetupResponsesBean.class );
             pwmRequest.getPwmDomain().getSessionStateService().clearBean( pwmRequest, SetupResponsesBean.class );
 
 
             // mark the event log
             // mark the event log
-            final UserAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createUserAuditRecord(
+            final UserAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createUserAuditRecord(
                     AuditEvent.CLEAR_RESPONSES,
                     AuditEvent.CLEAR_RESPONSES,
                     pwmSession.getUserInfo(),
                     pwmSession.getUserInfo(),
                     pwmSession
                     pwmSession
             );
             );
-            pwmDomain.getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+
+            AuditServiceClient.submit( pwmRequest, auditRecord );
 
 
             pwmRequest.getPwmResponse().sendRedirect( PwmServletDefinition.SetupResponses );
             pwmRequest.getPwmResponse().sendRedirect( PwmServletDefinition.SetupResponses );
         }
         }
@@ -437,8 +440,9 @@ public class SetupResponsesServlet extends ControlledPwmServlet
         final String userGUID = pwmSession.getUserInfo().getUserGuid();
         final String userGUID = pwmSession.getUserInfo().getUserGuid();
         pwmDomain.getCrService().writeResponses( pwmRequest.getLabel(), pwmRequest.getUserInfoIfLoggedIn(), theUser, userGUID, responseInfoBean );
         pwmDomain.getCrService().writeResponses( pwmRequest.getLabel(), pwmRequest.getUserInfoIfLoggedIn(), theUser, userGUID, responseInfoBean );
         pwmSession.reloadUserInfoBean( pwmRequest );
         pwmSession.reloadUserInfoBean( pwmRequest );
-        pwmDomain.getStatisticsManager().incrementValue( Statistic.SETUP_RESPONSES );
-        pwmDomain.getAuditManager().submit( AuditEvent.SET_RESPONSES, pwmSession.getUserInfo(), pwmSession );
+
+        StatisticsClient.incrementStat( pwmRequest, Statistic.SETUP_RESPONSES );
+        AuditServiceClient.submitUserEvent( pwmRequest, AuditEvent.SET_RESPONSES, pwmSession.getUserInfo() );
     }
     }
 
 
     private static Map<Challenge, String> readResponsesFromHttpRequest(
     private static Map<Challenge, String> readResponsesFromHttpRequest(

+ 4 - 4
server/src/main/java/password/pwm/http/servlet/ShortcutServlet.java

@@ -22,8 +22,8 @@ package password.pwm.http.servlet;
 
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.util.StringHelper;
 import com.novell.ldapchai.util.StringHelper;
-import password.pwm.PwmDomain;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.value.data.ShortcutItem;
 import password.pwm.config.value.data.ShortcutItem;
@@ -36,9 +36,10 @@ import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.ShortcutsBean;
 import password.pwm.http.bean.ShortcutsBean;
-import password.pwm.ldap.permission.UserPermissionUtility;
 import password.pwm.ldap.permission.UserPermissionType;
 import password.pwm.ldap.permission.UserPermissionType;
+import password.pwm.ldap.permission.UserPermissionUtility;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
@@ -218,7 +219,6 @@ public class ShortcutServlet extends AbstractPwmServlet
             throws PwmUnrecoverableException,  IOException, ServletException
             throws PwmUnrecoverableException,  IOException, ServletException
     {
     {
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
-        final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
 
 
         final String link = pwmRequest.readParameterAsString( "link" );
         final String link = pwmRequest.readParameterAsString( "link" );
         final Map<String, ShortcutItem> visibleItems = shortcutsBean.getVisibleItems();
         final Map<String, ShortcutItem> visibleItems = shortcutsBean.getVisibleItems();
@@ -227,7 +227,7 @@ public class ShortcutServlet extends AbstractPwmServlet
         {
         {
             final ShortcutItem item = visibleItems.get( link );
             final ShortcutItem item = visibleItems.get( link );
 
 
-            pwmDomain.getStatisticsManager().incrementValue( Statistic.SHORTCUTS_SELECTED );
+            StatisticsClient.incrementStat( pwmRequest, Statistic.SHORTCUTS_SELECTED );
             LOGGER.trace( pwmRequest, () -> "shortcut link selected: " + link + ", setting link for 'forwardURL' to " + item.getShortcutURI() );
             LOGGER.trace( pwmRequest, () -> "shortcut link selected: " + link + ", setting link for 'forwardURL' to " + item.getShortcutURI() );
             pwmSession.getSessionStateBean().setForwardURL( item.getShortcutURI().toString() );
             pwmSession.getSessionStateBean().setForwardURL( item.getShortcutURI().toString() );
 
 

+ 11 - 9
server/src/main/java/password/pwm/http/servlet/activation/ActivateUserServlet.java

@@ -54,6 +54,7 @@ import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenType;
 import password.pwm.svc.token.TokenType;
@@ -242,7 +243,7 @@ public class ActivateUserServlet extends ControlledPwmServlet
                     ssBean.getLocale() );
                     ssBean.getLocale() );
 
 
             // check for intruders
             // check for intruders
-            pwmDomain.getIntruderManager().convenience().checkAttributes( formValues );
+            pwmDomain.getIntruderService().client().checkAttributes( formValues );
 
 
             // read the context attr
             // read the context attr
             final String contextParam = pwmRequest.readParameterAsString( PwmConstants.PARAM_CONTEXT );
             final String contextParam = pwmRequest.readParameterAsString( PwmConstants.PARAM_CONTEXT );
@@ -272,13 +273,13 @@ public class ActivateUserServlet extends ControlledPwmServlet
             ActivateUserUtils.validateParamsAgainstLDAP( pwmRequest, formValues, userIdentity );
             ActivateUserUtils.validateParamsAgainstLDAP( pwmRequest, formValues, userIdentity );
 
 
             ActivateUserUtils.initUserActivationBean( pwmRequest, userIdentity );
             ActivateUserUtils.initUserActivationBean( pwmRequest, userIdentity );
-            pwmDomain.getIntruderManager().convenience().clearAttributes( formValues );
-            pwmDomain.getIntruderManager().convenience().clearAddressAndSession( pwmSession );
+            pwmDomain.getIntruderService().client().clearAttributes( formValues );
+            pwmDomain.getIntruderService().client().clearAddressAndSession( pwmSession );
         }
         }
         catch ( final PwmOperationalException e )
         catch ( final PwmOperationalException e )
         {
         {
-            pwmDomain.getIntruderManager().convenience().markAttributes( formValues, pwmRequest.getLabel() );
-            pwmDomain.getIntruderManager().convenience().markAddressAndSession( pwmRequest );
+            pwmDomain.getIntruderService().client().markAttributes( formValues, pwmRequest.getLabel() );
+            pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
             setLastError( pwmRequest, e.getErrorInformation() );
             setLastError( pwmRequest, e.getErrorInformation() );
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
         }
         }
@@ -383,13 +384,14 @@ public class ActivateUserServlet extends ControlledPwmServlet
         if ( !activateUserBean.isAgreementPassed() )
         if ( !activateUserBean.isAgreementPassed() )
         {
         {
             activateUserBean.setAgreementPassed( true );
             activateUserBean.setAgreementPassed( true );
-            final AuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createUserAuditRecord(
+            final AuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createUserAuditRecord(
                     AuditEvent.AGREEMENT_PASSED,
                     AuditEvent.AGREEMENT_PASSED,
                     pwmRequest.getUserInfoIfLoggedIn(),
                     pwmRequest.getUserInfoIfLoggedIn(),
                     pwmRequest.getLabel(),
                     pwmRequest.getLabel(),
                     "ActivateUser"
                     "ActivateUser"
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+
+            AuditServiceClient.submit( pwmRequest, auditRecord );
         }
         }
 
 
         return ProcessStatus.Continue;
         return ProcessStatus.Continue;
@@ -476,8 +478,8 @@ public class ActivateUserServlet extends ControlledPwmServlet
         catch ( final PwmOperationalException e )
         catch ( final PwmOperationalException e )
         {
         {
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
-            pwmDomain.getIntruderManager().convenience().markUserIdentity( activateUserBean.getUserIdentity(), pwmRequest );
-            pwmDomain.getIntruderManager().convenience().markAddressAndSession( pwmRequest );
+            pwmDomain.getIntruderService().client().markUserIdentity( activateUserBean.getUserIdentity(), pwmRequest );
+            pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
             pwmRequest.respondWithError( e.getErrorInformation() );
             pwmRequest.respondWithError( e.getErrorInformation() );
         }
         }
     }
     }

+ 6 - 5
server/src/main/java/password/pwm/http/servlet/activation/ActivateUserUtils.java

@@ -53,7 +53,9 @@ import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.PwmAuthenticationSource;
 import password.pwm.ldap.auth.PwmAuthenticationSource;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -83,7 +85,7 @@ class ActivateUserUtils
     {
     {
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
-        final ChaiUser theUser = pwmDomain.getProxiedChaiUser( userIdentity );
+        final ChaiUser theUser = pwmDomain.getProxiedChaiUser( pwmRequest.getLabel(), userIdentity );
 
 
         final ActivateUserProfile activateUserProfile = ActivateUserServlet.activateUserProfile( pwmRequest );
         final ActivateUserProfile activateUserProfile = ActivateUserServlet.activateUserProfile( pwmRequest );
 
 
@@ -129,12 +131,11 @@ class ActivateUserUtils
             pwmSession.getLoginInfoBean().getAuthFlags().add( AuthenticationType.AUTH_FROM_PUBLIC_MODULE );
             pwmSession.getLoginInfoBean().getAuthFlags().add( AuthenticationType.AUTH_FROM_PUBLIC_MODULE );
             pwmSession.getLoginInfoBean().getLoginFlags().add( LoginInfoBean.LoginFlag.forcePwChange );
             pwmSession.getLoginInfoBean().getLoginFlags().add( LoginInfoBean.LoginFlag.forcePwChange );
 
 
-
             // mark the event log
             // mark the event log
-            pwmDomain.getAuditManager().submit( AuditEvent.ACTIVATE_USER, pwmSession.getUserInfo(), pwmSession );
+            AuditServiceClient.submitUserEvent( pwmRequest, AuditEvent.ACTIVATE_USER, pwmSession.getUserInfo() );
 
 
             // update the stats bean
             // update the stats bean
-            pwmDomain.getStatisticsManager().incrementValue( Statistic.ACTIVATED_USERS );
+            StatisticsClient.incrementStat( pwmRequest, Statistic.ACTIVATED_USERS );
 
 
             // send email or sms
             // send email or sms
             sendPostActivationNotice( pwmRequest );
             sendPostActivationNotice( pwmRequest );
@@ -155,7 +156,7 @@ class ActivateUserUtils
             throws ChaiUnavailableException, PwmDataValidationException, PwmUnrecoverableException
             throws ChaiUnavailableException, PwmDataValidationException, PwmUnrecoverableException
     {
     {
         final String searchFilter = figureLdapSearchFilter( pwmRequest );
         final String searchFilter = figureLdapSearchFilter( pwmRequest );
-        final ChaiProvider chaiProvider = pwmRequest.getPwmDomain().getProxyChaiProvider( userIdentity.getLdapProfileID() );
+        final ChaiProvider chaiProvider = pwmRequest.getPwmDomain().getProxyChaiProvider( pwmRequest.getLabel(), userIdentity.getLdapProfileID() );
         final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser( userIdentity.getUserDN() );
         final ChaiUser chaiUser = chaiProvider.getEntryFactory().newChaiUser( userIdentity.getUserDN() );
 
 
         for ( final Map.Entry<FormConfiguration, String> entry : formValues.entrySet() )
         for ( final Map.Entry<FormConfiguration, String> entry : formValues.entrySet() )

+ 10 - 10
server/src/main/java/password/pwm/http/servlet/admin/AdminServlet.java

@@ -53,13 +53,13 @@ import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.event.AuditEventType;
 import password.pwm.svc.event.AuditEventType;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.intruder.IntruderRecordType;
 import password.pwm.svc.intruder.IntruderRecordType;
+import password.pwm.svc.intruder.PublicIntruderRecord;
 import password.pwm.svc.pwnotify.PwNotifyService;
 import password.pwm.svc.pwnotify.PwNotifyService;
 import password.pwm.svc.pwnotify.PwNotifyStoredJobState;
 import password.pwm.svc.pwnotify.PwNotifyStoredJobState;
 import password.pwm.svc.report.ReportCsvUtility;
 import password.pwm.svc.report.ReportCsvUtility;
 import password.pwm.svc.report.ReportService;
 import password.pwm.svc.report.ReportService;
 import password.pwm.svc.report.UserReportRecord;
 import password.pwm.svc.report.UserReportRecord;
-import password.pwm.svc.stats.StatisticsManager;
-import password.pwm.util.db.DatabaseException;
+import password.pwm.svc.stats.StatisticsService;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.ClosableIterator;
 import password.pwm.util.java.ClosableIterator;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -108,7 +108,6 @@ import java.util.zip.ZipOutputStream;
 )
 )
 public class AdminServlet extends ControlledPwmServlet
 public class AdminServlet extends ControlledPwmServlet
 {
 {
-
     private static final PwmLogger LOGGER = PwmLogger.forClass( AdminServlet.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( AdminServlet.class );
 
 
     public enum AdminAction implements AbstractPwmServlet.ProcessAction
     public enum AdminAction implements AbstractPwmServlet.ProcessAction
@@ -205,7 +204,7 @@ public class AdminServlet extends ControlledPwmServlet
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         try
         try
         {
         {
-            pwmDomain.getAuditManager().outputVaultToCsv( outputStream, pwmRequest.getLocale(), true );
+            pwmDomain.getAuditService().outputVaultToCsv( outputStream, pwmRequest.getLocale(), true );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
@@ -295,7 +294,7 @@ public class AdminServlet extends ControlledPwmServlet
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
         try
         try
         {
         {
-            final StatisticsManager statsManager = pwmDomain.getStatisticsManager();
+            final StatisticsService statsManager = pwmDomain.getStatisticsManager();
             statsManager.outputStatsToCsv( outputStream, pwmRequest.getLocale(), true );
             statsManager.outputStatsToCsv( outputStream, pwmRequest.getLocale(), true );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
@@ -472,7 +471,7 @@ public class AdminServlet extends ControlledPwmServlet
         final int max = readMaxParameter( pwmRequest, 100, 10 * 1000 );
         final int max = readMaxParameter( pwmRequest, 100, 10 * 1000 );
         final AuditEventType auditDataType = AuditEventType.valueOf( pwmRequest.readParameterAsString( "type", AuditEventType.USER.name() ) );
         final AuditEventType auditDataType = AuditEventType.valueOf( pwmRequest.readParameterAsString( "type", AuditEventType.USER.name() ) );
         final ArrayList<AuditRecord> records = new ArrayList<>();
         final ArrayList<AuditRecord> records = new ArrayList<>();
-        final Iterator<AuditRecord> iterator = pwmRequest.getPwmDomain().getAuditManager().readVault();
+        final Iterator<AuditRecord> iterator = pwmRequest.getPwmDomain().getAuditService().readVault();
 
 
         while (
         while (
                 iterator.hasNext()
                 iterator.hasNext()
@@ -516,16 +515,16 @@ public class AdminServlet extends ControlledPwmServlet
 
 
     @ActionHandler( action = "intruderData" )
     @ActionHandler( action = "intruderData" )
     private ProcessStatus restIntruderDataHandler( final PwmRequest pwmRequest )
     private ProcessStatus restIntruderDataHandler( final PwmRequest pwmRequest )
-            throws ChaiUnavailableException, PwmUnrecoverableException, IOException
+            throws  PwmUnrecoverableException, IOException
     {
     {
         final int max = readMaxParameter( pwmRequest, 1000, 10 * 1000 );
         final int max = readMaxParameter( pwmRequest, 1000, 10 * 1000 );
 
 
-        final TreeMap<String, Object> returnData = new TreeMap<>();
+        final TreeMap<String, List<PublicIntruderRecord>> returnData = new TreeMap<>();
         try
         try
         {
         {
             for ( final IntruderRecordType recordType : IntruderRecordType.values() )
             for ( final IntruderRecordType recordType : IntruderRecordType.values() )
             {
             {
-                returnData.put( recordType.toString(), pwmRequest.getPwmDomain().getIntruderManager().getRecords( recordType, max ) );
+                returnData.put( recordType.toString(), pwmRequest.getPwmApplication().getIntruderSystemService().getRecords( recordType, max ) );
             }
             }
         }
         }
         catch ( final PwmException e )
         catch ( final PwmException e )
@@ -744,7 +743,8 @@ public class AdminServlet extends ControlledPwmServlet
     }
     }
 
 
     @ActionHandler( action = "readPwNotifyLog" )
     @ActionHandler( action = "readPwNotifyLog" )
-    public ProcessStatus restreadPwNotifyLog( final PwmRequest pwmRequest ) throws IOException, DatabaseException, PwmUnrecoverableException
+    public ProcessStatus restreadPwNotifyLog( final PwmRequest pwmRequest )
+            throws IOException
     {
     {
         final PwNotifyService pwNotifyService = pwmRequest.getPwmDomain().getPwNotifyService();
         final PwNotifyService pwNotifyService = pwmRequest.getPwmDomain().getPwNotifyService();
 
 

+ 67 - 36
server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java

@@ -23,8 +23,10 @@ package password.pwm.http.servlet.admin;
 import lombok.Builder;
 import lombok.Builder;
 import lombok.Value;
 import lombok.Value;
 import password.pwm.PwmAboutProperty;
 import password.pwm.PwmAboutProperty;
-import password.pwm.PwmDomain;
+import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
+import password.pwm.bean.DomainID;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.DataStorageMethod;
 import password.pwm.config.option.DataStorageMethod;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
@@ -58,6 +60,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
@@ -73,13 +76,31 @@ public class AppDashboardData implements Serializable
     private static final PwmLogger LOGGER = PwmLogger.forClass( AppDashboardData.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( AppDashboardData.class );
 
 
     @Value
     @Value
-    public static class ServiceData implements Serializable
+    public static class ServiceData implements Serializable, Comparable<ServiceData>
     {
     {
+        private static final Comparator<ServiceData> COMPARATOR = Comparator.comparing(
+                ServiceData::getDomainID,
+                Comparator.nullsLast( Comparator.naturalOrder() ) )
+                .thenComparing(
+                        ServiceData::getName,
+                        Comparator.nullsLast( Comparator.naturalOrder() ) )
+                .thenComparing(
+                        ServiceData::getStatus,
+                        Comparator.nullsLast( Comparator.naturalOrder() ) );
+
+        private String guid;
+        private DomainID domainID;
         private String name;
         private String name;
         private PwmService.STATUS status;
         private PwmService.STATUS status;
         private Collection<DataStorageMethod> storageMethod;
         private Collection<DataStorageMethod> storageMethod;
         private List<HealthRecord> health;
         private List<HealthRecord> health;
         private Map<String, String> debugData;
         private Map<String, String> debugData;
+
+        @Override
+        public int compareTo( final ServiceData otherServiceData )
+        {
+            return COMPARATOR.compare( this, otherServiceData );
+        }
     }
     }
 
 
     @Value
     @Value
@@ -133,7 +154,7 @@ public class AppDashboardData implements Serializable
 
 
         final AppDashboardDataBuilder builder = AppDashboardData.builder();
         final AppDashboardDataBuilder builder = AppDashboardData.builder();
         builder.about( makeAboutData( pwmDomain, contextManager, locale ) );
         builder.about( makeAboutData( pwmDomain, contextManager, locale ) );
-        builder.services( getServiceData( pwmDomain ) );
+        builder.services( makeServiceData( pwmDomain.getPwmApplication() ) );
         builder.localDbInfo( makeLocalDbInfo( pwmDomain, locale ) );
         builder.localDbInfo( makeLocalDbInfo( pwmDomain, locale ) );
         builder.javaAbout( makeAboutJavaData( pwmDomain, locale ) );
         builder.javaAbout( makeAboutJavaData( pwmDomain, locale ) );
 
 
@@ -171,7 +192,7 @@ public class AppDashboardData implements Serializable
         builder.sessionCount( pwmDomain.getSessionTrackService().sessionCount() );
         builder.sessionCount( pwmDomain.getSessionTrackService().sessionCount() );
         builder.requestsInProgress( pwmDomain.getPwmApplication().getActiveServletRequests().get() );
         builder.requestsInProgress( pwmDomain.getPwmApplication().getActiveServletRequests().get() );
 
 
-        LOGGER.trace( () -> "AppDashboardData bean created in ", () -> TimeDuration.fromCurrent( startTime ) );
+        LOGGER.trace( () -> "AppDashboardData bean created", () -> TimeDuration.fromCurrent( startTime ) );
         return builder.build();
         return builder.build();
     }
     }
 
 
@@ -238,34 +259,44 @@ public class AppDashboardData implements Serializable
         ) );
         ) );
     }
     }
 
 
-    private static List<ServiceData> getServiceData( final PwmDomain pwmDomain )
+    public static List<ServiceData> makeServiceData( final PwmApplication pwmApplication )
+            throws PwmUnrecoverableException
     {
     {
-        final Map<String, ServiceData> returnData = new TreeMap<>();
-        for ( final PwmService pwmService : pwmDomain.getPwmServices() )
+        final List<ServiceData> returnData = new ArrayList<>();
+        for ( final Map.Entry<DomainID, List<PwmService>> domainIDListEntry : pwmApplication.getAppAndDomainPwmServices().entrySet() )
         {
         {
-            final PwmService.ServiceInfo serviceInfo = pwmService.serviceInfo();
-            final Collection<DataStorageMethod> storageMethods = serviceInfo == null
-                    ? Collections.emptyList()
-                    : serviceInfo.getStorageMethods() == null
-                    ? Collections.emptyList()
-                    : serviceInfo.getStorageMethods();
-
-            final Map<String, String> debugData = serviceInfo == null
-                    ? Collections.emptyMap()
-                    : serviceInfo.getDebugProperties() == null
-                    ? Collections.emptyMap()
-                    : serviceInfo.getDebugProperties();
-
-            returnData.put( pwmService.getClass().getSimpleName(), new ServiceData(
-                    pwmService.getClass().getSimpleName(),
-                    pwmService.status(),
-                    storageMethods,
-                    pwmService.healthCheck(),
-                    debugData
-            ) );
+            final DomainID domainID = domainIDListEntry.getKey();
+            for ( final PwmService pwmService : domainIDListEntry.getValue() )
+            {
+                final PwmService.ServiceInfo serviceInfo = pwmService.serviceInfo();
+                final Collection<DataStorageMethod> storageMethods = serviceInfo == null
+                        ? Collections.emptyList()
+                        : serviceInfo.getStorageMethods() == null
+                                ? Collections.emptyList()
+                                : serviceInfo.getStorageMethods();
+
+                final Map<String, String> debugData = serviceInfo == null
+                        ? Collections.emptyMap()
+                        : serviceInfo.getDebugProperties() == null
+                                ? Collections.emptyMap()
+                                : serviceInfo.getDebugProperties();
+
+                final String guid = pwmApplication.getSecureService().hash( domainID + pwmService.getClass().getSimpleName() );
+
+                returnData.add( new ServiceData(
+                        guid,
+                        domainID,
+                        pwmService.getClass().getSimpleName(),
+                        pwmService.status(),
+                        storageMethods,
+                        pwmService.healthCheck(),
+                        debugData
+                ) );
+            }
         }
         }
 
 
-        return List.copyOf( returnData.values() );
+        Collections.sort( returnData );
+        return List.copyOf( returnData );
     }
     }
 
 
     private static List<DisplayElement> makeLocalDbInfo( final PwmDomain pwmDomain, final Locale locale )
     private static List<DisplayElement> makeLocalDbInfo( final PwmDomain pwmDomain, final Locale locale )
@@ -323,16 +354,16 @@ public class AppDashboardData implements Serializable
                 "sharedHistorySize",
                 "sharedHistorySize",
                 DisplayElement.Type.number,
                 DisplayElement.Type.number,
                 "Syslog Queue Size",
                 "Syslog Queue Size",
-                String.valueOf( pwmDomain.getAuditManager().syslogQueueSize() )
+                String.valueOf( pwmDomain.getAuditService().syslogQueueSize() )
         ) );
         ) );
         localDbInfo.add( new DisplayElement(
         localDbInfo.add( new DisplayElement(
                 "localAuditRecords",
                 "localAuditRecords",
                 DisplayElement.Type.number,
                 DisplayElement.Type.number,
                 "Audit Records",
                 "Audit Records",
-                pwmDomain.getAuditManager().sizeToDebugString()
+                pwmDomain.getAuditService().sizeToDebugString()
         ) );
         ) );
         {
         {
-            final Instant eldestAuditRecord = pwmDomain.getAuditManager().eldestVaultRecord();
+            final Instant eldestAuditRecord = pwmDomain.getAuditService().eldestVaultRecord();
             final String display = eldestAuditRecord != null
             final String display = eldestAuditRecord != null
                     ? TimeDuration.fromCurrent( eldestAuditRecord ).asLongString()
                     ? TimeDuration.fromCurrent( eldestAuditRecord ).asLongString()
                     : notApplicable;
                     : notApplicable;
@@ -366,9 +397,9 @@ public class AppDashboardData implements Serializable
             final String display = pwmDomain.getPwmApplication().getLocalDB() == null
             final String display = pwmDomain.getPwmApplication().getLocalDB() == null
                     ? notApplicable
                     ? notApplicable
                     : pwmDomain.getPwmApplication().getLocalDB().getFileLocation() == null
                     : pwmDomain.getPwmApplication().getLocalDB().getFileLocation() == null
-                    ? notApplicable
-                    : StringUtil.formatDiskSize( FileSystemUtility.getFileDirectorySize(
-                    pwmDomain.getPwmApplication().getLocalDB().getFileLocation() ) );
+                            ? notApplicable
+                            : StringUtil.formatDiskSize( FileSystemUtility.getFileDirectorySize(
+                                    pwmDomain.getPwmApplication().getLocalDB().getFileLocation() ) );
             localDbInfo.add( new DisplayElement(
             localDbInfo.add( new DisplayElement(
                     "localDbSizeOnDisk",
                     "localDbSizeOnDisk",
                     DisplayElement.Type.string,
                     DisplayElement.Type.string,
@@ -380,8 +411,8 @@ public class AppDashboardData implements Serializable
             final String display = pwmDomain.getPwmApplication().getLocalDB() == null
             final String display = pwmDomain.getPwmApplication().getLocalDB() == null
                     ? notApplicable
                     ? notApplicable
                     : pwmDomain.getPwmApplication().getLocalDB().getFileLocation() == null
                     : pwmDomain.getPwmApplication().getLocalDB().getFileLocation() == null
-                    ? notApplicable
-                    : StringUtil.formatDiskSize( FileSystemUtility.diskSpaceRemaining( pwmDomain.getPwmApplication().getLocalDB().getFileLocation() ) );
+                            ? notApplicable
+                            : StringUtil.formatDiskSize( FileSystemUtility.diskSpaceRemaining( pwmDomain.getPwmApplication().getLocalDB().getFileLocation() ) );
             localDbInfo.add( new DisplayElement(
             localDbInfo.add( new DisplayElement(
                     "localDbFreeSpace",
                     "localDbFreeSpace",
                     DisplayElement.Type.string,
                     DisplayElement.Type.string,

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/admin/UserDebugDataReader.java

@@ -70,7 +70,7 @@ public class UserDebugDataReader
 
 
         final Map<ProfileDefinition, String> profiles = UserDebugDataReader.profileMap( pwmDomain, sessionLabel, userIdentity );
         final Map<ProfileDefinition, String> profiles = UserDebugDataReader.profileMap( pwmDomain, sessionLabel, userIdentity );
 
 
-        final PwmPasswordPolicy ldapPasswordPolicy = PasswordUtility.readLdapPasswordPolicy( pwmDomain, pwmDomain.getProxiedChaiUser( userIdentity ) );
+        final PwmPasswordPolicy ldapPasswordPolicy = PasswordUtility.readLdapPasswordPolicy( pwmDomain, pwmDomain.getProxiedChaiUser( sessionLabel, userIdentity ) );
 
 
         final PwmPasswordPolicy configPasswordPolicy = PasswordUtility.determineConfiguredPolicyProfileForUser(
         final PwmPasswordPolicy configPasswordPolicy = PasswordUtility.determineConfiguredPolicyProfileForUser(
                 pwmDomain,
                 pwmDomain,

+ 39 - 26
server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java

@@ -50,6 +50,7 @@ import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.stats.AvgStatistic;
 import password.pwm.svc.stats.AvgStatistic;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.form.FormUtility;
@@ -75,6 +76,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Optional;
 import java.util.Optional;
 
 
 /**
 /**
@@ -131,6 +133,11 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
         return pwmRequest.getChangePasswordProfile( );
         return pwmRequest.getChangePasswordProfile( );
     }
     }
 
 
+    static ChangePasswordBean getBean( final PwmRequest pwmRequest ) throws PwmUnrecoverableException
+    {
+       return pwmRequest.getPwmDomain().getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
+    }
+
     @ActionHandler( action = "reset" )
     @ActionHandler( action = "reset" )
     ProcessStatus processResetAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException
     ProcessStatus processResetAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException
     {
     {
@@ -150,7 +157,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     @ActionHandler( action = "warnResponse" )
     @ActionHandler( action = "warnResponse" )
     public ProcessStatus processWarnResponse( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException
     public ProcessStatus processWarnResponse( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException
     {
     {
-        final ChangePasswordBean changePasswordBean = pwmRequest.getPwmDomain().getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
+        final ChangePasswordBean changePasswordBean = getBean( pwmRequest );
 
 
         if ( pwmRequest.getPwmSession().getUserInfo().getPasswordStatus().isWarnPeriod() )
         if ( pwmRequest.getPwmSession().getUserInfo().getPasswordStatus().isWarnPeriod() )
         {
         {
@@ -181,7 +188,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     @ActionHandler( action = "change" )
     @ActionHandler( action = "change" )
     ProcessStatus processChangeAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException, ChaiUnavailableException
     ProcessStatus processChangeAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException, ChaiUnavailableException
     {
     {
-        final ChangePasswordBean changePasswordBean = pwmRequest.getPwmDomain().getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
+        final ChangePasswordBean changePasswordBean = getBean( pwmRequest );
 
 
         final UserInfo userInfo = pwmRequest.getPwmSession().getUserInfo();
         final UserInfo userInfo = pwmRequest.getPwmSession().getUserInfo();
 
 
@@ -190,14 +197,17 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
             return ProcessStatus.Continue;
             return ProcessStatus.Continue;
         }
         }
 
 
-        final PasswordData password1 = pwmRequest.readParameterAsPassword( "password1" );
-        final PasswordData password2 = pwmRequest.readParameterAsPassword( "password2" );
+        final PasswordData password1 = pwmRequest.readParameterAsPassword( "password1" )
+                .orElseThrow( () -> new NoSuchElementException( "missing password1 field" ) );
+        final PasswordData password2 = pwmRequest.readParameterAsPassword( "password2" )
+                .orElseThrow( () -> new NoSuchElementException( "missing password2 field" ) );
 
 
         // check the password meets the requirements
         // check the password meets the requirements
         try
         try
         {
         {
             final ChaiUser theUser = pwmRequest.getPwmSession().getSessionManager().getActor( );
             final ChaiUser theUser = pwmRequest.getPwmSession().getSessionManager().getActor( );
-            final PwmPasswordRuleValidator pwmPasswordRuleValidator = new PwmPasswordRuleValidator( pwmRequest.getPwmDomain(), userInfo.getPasswordPolicy() );
+            final PwmPasswordRuleValidator pwmPasswordRuleValidator = PwmPasswordRuleValidator.create(
+                    pwmRequest.getLabel(), pwmRequest.getPwmDomain(), userInfo.getPasswordPolicy() );
             final PasswordData oldPassword = pwmRequest.getPwmSession().getLoginInfoBean().getUserCurrentPassword();
             final PasswordData oldPassword = pwmRequest.getPwmSession().getLoginInfoBean().getUserCurrentPassword();
             pwmPasswordRuleValidator.testPassword( password1, oldPassword, userInfo, theUser );
             pwmPasswordRuleValidator.testPassword( password1, oldPassword, userInfo, theUser );
         }
         }
@@ -235,19 +245,19 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     @ActionHandler( action = "agree" )
     @ActionHandler( action = "agree" )
     ProcessStatus processAgreeAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException, ChaiUnavailableException
     ProcessStatus processAgreeAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException, ChaiUnavailableException
     {
     {
-        final ChangePasswordBean changePasswordBean = pwmRequest.getPwmDomain().getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
+        final ChangePasswordBean changePasswordBean = getBean( pwmRequest );
 
 
         LOGGER.debug( pwmRequest, () -> "user accepted password change agreement" );
         LOGGER.debug( pwmRequest, () -> "user accepted password change agreement" );
         if ( !changePasswordBean.isAgreementPassed() )
         if ( !changePasswordBean.isAgreementPassed() )
         {
         {
             changePasswordBean.setAgreementPassed( true );
             changePasswordBean.setAgreementPassed( true );
-            final AuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createUserAuditRecord(
+            final AuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createUserAuditRecord(
                     AuditEvent.AGREEMENT_PASSED,
                     AuditEvent.AGREEMENT_PASSED,
                     pwmRequest.getUserInfoIfLoggedIn(),
                     pwmRequest.getUserInfoIfLoggedIn(),
                     pwmRequest.getLabel(),
                     pwmRequest.getLabel(),
                     "ChangePassword"
                     "ChangePassword"
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+            AuditServiceClient.submit( pwmRequest, auditRecord );
         }
         }
 
 
         return ProcessStatus.Continue;
         return ProcessStatus.Continue;
@@ -257,15 +267,17 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     ProcessStatus processFormAction( final PwmRequest pwmRequest )
     ProcessStatus processFormAction( final PwmRequest pwmRequest )
             throws ServletException, PwmUnrecoverableException, IOException, ChaiUnavailableException
             throws ServletException, PwmUnrecoverableException, IOException, ChaiUnavailableException
     {
     {
-        final ChangePasswordBean cpb = pwmRequest.getPwmDomain().getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
+        final ChangePasswordBean changePasswordBean = getBean( pwmRequest );
         final LocalSessionStateBean ssBean = pwmRequest.getPwmSession().getSessionStateBean();
         final LocalSessionStateBean ssBean = pwmRequest.getPwmSession().getSessionStateBean();
         final UserInfo userInfo = pwmRequest.getPwmSession().getUserInfo();
         final UserInfo userInfo = pwmRequest.getPwmSession().getUserInfo();
         final LoginInfoBean loginBean = pwmRequest.getPwmSession().getLoginInfoBean();
         final LoginInfoBean loginBean = pwmRequest.getPwmSession().getLoginInfoBean();
 
 
-        final PasswordData currentPassword = pwmRequest.readParameterAsPassword( "currentPassword" );
+        final PasswordData currentPassword = pwmRequest.readParameterAsPassword( "currentPassword" )
+                .orElseThrow( () -> new NoSuchElementException( "missing currentPassword field" ) );
+
 
 
         // check the current password
         // check the current password
-        if ( cpb.isCurrentPasswordRequired() && loginBean.getUserCurrentPassword() != null )
+        if ( changePasswordBean.isCurrentPasswordRequired() && loginBean.getUserCurrentPassword() != null )
         {
         {
             if ( currentPassword == null )
             if ( currentPassword == null )
             {
             {
@@ -284,13 +296,13 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
 
 
             if ( !passed )
             if ( !passed )
             {
             {
-                pwmRequest.getPwmDomain().getIntruderManager().convenience().markUserIdentity(
+                pwmRequest.getPwmDomain().getIntruderService().client().markUserIdentity(
                         userInfo.getUserIdentity(), pwmRequest.getLabel() );
                         userInfo.getUserIdentity(), pwmRequest.getLabel() );
                 LOGGER.debug( pwmRequest, () -> "failed password validation check: currentPassword value is incorrect" );
                 LOGGER.debug( pwmRequest, () -> "failed password validation check: currentPassword value is incorrect" );
                 setLastError( pwmRequest, new ErrorInformation( PwmError.ERROR_BAD_CURRENT_PASSWORD ) );
                 setLastError( pwmRequest, new ErrorInformation( PwmError.ERROR_BAD_CURRENT_PASSWORD ) );
                 return ProcessStatus.Continue;
                 return ProcessStatus.Continue;
             }
             }
-            cpb.setCurrentPasswordPassed( true );
+            changePasswordBean.setCurrentPasswordPassed( true );
         }
         }
 
 
         final List<FormConfiguration> formItem = getProfile( pwmRequest ).readSettingAsForm( PwmSetting.PASSWORD_REQUIRE_FORM );
         final List<FormConfiguration> formItem = getProfile( pwmRequest ).readSettingAsForm( PwmSetting.PASSWORD_REQUIRE_FORM );
@@ -304,12 +316,12 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
             ChangePasswordServletUtil.validateParamsAgainstLDAP( formValues, pwmRequest,
             ChangePasswordServletUtil.validateParamsAgainstLDAP( formValues, pwmRequest,
                     pwmRequest.getPwmSession().getSessionManager().getActor( ) );
                     pwmRequest.getPwmSession().getSessionManager().getActor( ) );
 
 
-            cpb.setFormPassed( true );
+            changePasswordBean.setFormPassed( true );
         }
         }
         catch ( final PwmOperationalException e )
         catch ( final PwmOperationalException e )
         {
         {
-            pwmRequest.getPwmDomain().getIntruderManager().convenience().markAddressAndSession( pwmRequest );
-            pwmRequest.getPwmDomain().getIntruderManager().convenience().markUserIdentity( userInfo.getUserIdentity(), pwmRequest.getLabel() );
+            pwmRequest.getPwmDomain().getIntruderService().client().markAddressAndSession( pwmRequest );
+            pwmRequest.getPwmDomain().getIntruderService().client().markUserIdentity( userInfo.getUserIdentity(), pwmRequest.getLabel() );
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
             LOGGER.debug( pwmRequest, e.getErrorInformation() );
             setLastError( pwmRequest, e.getErrorInformation() );
             setLastError( pwmRequest, e.getErrorInformation() );
             return ProcessStatus.Continue;
             return ProcessStatus.Continue;
@@ -321,7 +333,8 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     @ActionHandler( action = "checkProgress" )
     @ActionHandler( action = "checkProgress" )
     ProcessStatus processCheckProgressAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException
     ProcessStatus processCheckProgressAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException
     {
     {
-        final ChangePasswordBean changePasswordBean = pwmRequest.getPwmDomain().getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
+        final ChangePasswordBean changePasswordBean = getBean( pwmRequest );
+
         final PasswordChangeProgressChecker.ProgressTracker progressTracker = changePasswordBean.getChangeProgressTracker();
         final PasswordChangeProgressChecker.ProgressTracker progressTracker = changePasswordBean.getChangeProgressTracker();
         final PasswordChangeProgressChecker.PasswordChangeProgress passwordChangeProgress;
         final PasswordChangeProgressChecker.PasswordChangeProgress passwordChangeProgress;
         if ( progressTracker == null )
         if ( progressTracker == null )
@@ -349,8 +362,9 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     @ActionHandler( action = "complete" )
     @ActionHandler( action = "complete" )
     public ProcessStatus processCompleteAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException
     public ProcessStatus processCompleteAction( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException
     {
     {
-        final ChangePasswordBean cpb = pwmRequest.getPwmDomain().getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
-        final PasswordChangeProgressChecker.ProgressTracker progressTracker = cpb.getChangeProgressTracker();
+        final ChangePasswordBean changePasswordBean = getBean( pwmRequest );
+
+        final PasswordChangeProgressChecker.ProgressTracker progressTracker = changePasswordBean.getChangeProgressTracker();
         boolean isComplete = true;
         boolean isComplete = true;
         if ( progressTracker != null )
         if ( progressTracker != null )
         {
         {
@@ -380,7 +394,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
                     LOGGER.error( pwmRequest, () -> "unable to update average password sync time statistic: " + e.getMessage() );
                     LOGGER.error( pwmRequest, () -> "unable to update average password sync time statistic: " + e.getMessage() );
                 }
                 }
             }
             }
-            cpb.setChangeProgressTracker( null );
+            changePasswordBean.setChangeProgressTracker( null );
             final Locale locale = pwmRequest.getLocale();
             final Locale locale = pwmRequest.getLocale();
             final String completeMessage = getProfile( pwmRequest ).readSettingAsLocalizedString( PwmSetting.PASSWORD_COMPLETE_MESSAGE, locale );
             final String completeMessage = getProfile( pwmRequest ).readSettingAsLocalizedString( PwmSetting.PASSWORD_COMPLETE_MESSAGE, locale );
 
 
@@ -405,7 +419,8 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     }
     }
 
 
     @ActionHandler( action = "checkPassword" )
     @ActionHandler( action = "checkPassword" )
-    private ProcessStatus processCheckPasswordAction( final PwmRequest pwmRequest ) throws IOException, PwmUnrecoverableException, ChaiUnavailableException
+    private ProcessStatus processCheckPasswordAction( final PwmRequest pwmRequest )
+            throws IOException, PwmUnrecoverableException, ChaiUnavailableException
     {
     {
         final RestCheckPasswordServer.JsonInput jsonInput = JsonUtil.deserialize(
         final RestCheckPasswordServer.JsonInput jsonInput = JsonUtil.deserialize(
                 pwmRequest.readRequestBodyAsString(),
                 pwmRequest.readRequestBodyAsString(),
@@ -414,8 +429,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
 
 
         final UserInfo userInfo = pwmRequest.getPwmSession().getUserInfo();
         final UserInfo userInfo = pwmRequest.getPwmSession().getUserInfo();
         final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
         final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
-                pwmRequest.getPwmDomain(),
-                pwmRequest.getLocale(),
+                pwmRequest.getPwmRequestContext(),
                 pwmRequest.getPwmSession().getSessionManager().getActor(),
                 pwmRequest.getPwmSession().getSessionManager().getActor(),
                 userInfo,
                 userInfo,
                 pwmRequest.getPwmSession().getLoginInfoBean(),
                 pwmRequest.getPwmSession().getLoginInfoBean(),
@@ -453,7 +467,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     )
     )
             throws IOException, PwmUnrecoverableException, ServletException
             throws IOException, PwmUnrecoverableException, ServletException
     {
     {
-        final ChangePasswordBean changePasswordBean = pwmRequest.getPwmDomain().getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
+        final ChangePasswordBean changePasswordBean = getBean( pwmRequest );
 
 
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final ChangePasswordProfile changePasswordProfile = getProfile( pwmRequest );
         final ChangePasswordProfile changePasswordProfile = getProfile( pwmRequest );
@@ -509,8 +523,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet
     private void forwardToWaitPage( final PwmRequest pwmRequest )
     private void forwardToWaitPage( final PwmRequest pwmRequest )
             throws PwmUnrecoverableException, ServletException, IOException
             throws PwmUnrecoverableException, ServletException, IOException
     {
     {
-        final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
-        final ChangePasswordBean changePasswordBean = pwmDomain.getSessionStateService().getBean( pwmRequest, ChangePasswordBean.class );
+        final ChangePasswordBean changePasswordBean = getBean( pwmRequest );
         final Instant maxCompleteTime = changePasswordBean.getChangePasswordMaxCompletion();
         final Instant maxCompleteTime = changePasswordBean.getChangePasswordMaxCompletion();
         pwmRequest.setAttribute(
         pwmRequest.setAttribute(
                 PwmRequestAttribute.ChangePassword_MaxWaitSeconds,
                 PwmRequestAttribute.ChangePassword_MaxWaitSeconds,

+ 2 - 1
server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServletUtil.java

@@ -45,6 +45,7 @@ import password.pwm.ldap.PasswordChangeProgressChecker;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.util.password.PasswordUtility;
@@ -237,7 +238,7 @@ public class ChangePasswordServletUtil
         ChangePasswordServletUtil.sendChangePasswordEmailNotice( pwmRequest );
         ChangePasswordServletUtil.sendChangePasswordEmailNotice( pwmRequest );
 
 
         // send audit event
         // send audit event
-        pwmDomain.getAuditManager().submit( AuditEvent.CHANGE_PASSWORD, pwmSession.getUserInfo(), pwmSession );
+        AuditServiceClient.submitUserEvent( pwmRequest, AuditEvent.CHANGE_PASSWORD, pwmSession.getUserInfo() );
     }
     }
 
 
     static boolean warnPageShouldBeShown(
     static boolean warnPageShouldBeShown(

+ 58 - 50
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java

@@ -36,8 +36,9 @@ import password.pwm.config.PwmSettingTemplate;
 import password.pwm.config.SettingUIFunction;
 import password.pwm.config.SettingUIFunction;
 import password.pwm.config.profile.EmailServerProfile;
 import password.pwm.config.profile.EmailServerProfile;
 import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.config.profile.PwmPasswordPolicy;
-import password.pwm.config.stored.ConfigurationProperty;
 import password.pwm.config.stored.ConfigSearchMachine;
 import password.pwm.config.stored.ConfigSearchMachine;
+import password.pwm.config.stored.ConfigurationCleaner;
+import password.pwm.config.stored.ConfigurationProperty;
 import password.pwm.config.stored.StoredConfigKey;
 import password.pwm.config.stored.StoredConfigKey;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.stored.StoredConfigurationModifier;
 import password.pwm.config.stored.StoredConfigurationModifier;
@@ -76,6 +77,7 @@ import password.pwm.ldap.LdapBrowser;
 import password.pwm.svc.email.EmailServer;
 import password.pwm.svc.email.EmailServer;
 import password.pwm.svc.email.EmailServerUtil;
 import password.pwm.svc.email.EmailServerUtil;
 import password.pwm.svc.email.EmailService;
 import password.pwm.svc.email.EmailService;
+import password.pwm.svc.sms.SmsQueueService;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.SampleDataGenerator;
 import password.pwm.util.SampleDataGenerator;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -85,10 +87,9 @@ import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.password.RandomPasswordGenerator;
 import password.pwm.util.password.RandomPasswordGenerator;
-import password.pwm.util.queue.SmsQueueManager;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.rest.RestRandomPasswordServer;
 import password.pwm.ws.server.rest.RestRandomPasswordServer;
-import password.pwm.ws.server.rest.bean.HealthData;
+import password.pwm.ws.server.rest.bean.PublicHealthData;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.annotation.WebServlet;
@@ -98,7 +99,6 @@ import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
@@ -107,9 +107,6 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
 import java.util.TreeMap;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 
 
 @WebServlet(
 @WebServlet(
         name = "ConfigEditorServlet",
         name = "ConfigEditorServlet",
@@ -140,12 +137,13 @@ public class ConfigEditorServlet extends ControlledPwmServlet
         executeSettingFunction( HttpMethod.POST ),
         executeSettingFunction( HttpMethod.POST ),
         setConfigurationPassword( HttpMethod.POST ),
         setConfigurationPassword( HttpMethod.POST ),
         readChangeLog( HttpMethod.POST ),
         readChangeLog( HttpMethod.POST ),
+        readWarnings( HttpMethod.POST ),
         search( HttpMethod.POST ),
         search( HttpMethod.POST ),
         cancelEditing( HttpMethod.POST ),
         cancelEditing( HttpMethod.POST ),
         uploadFile( HttpMethod.POST ),
         uploadFile( HttpMethod.POST ),
         setOption( HttpMethod.POST ),
         setOption( HttpMethod.POST ),
         menuTreeData( HttpMethod.POST ),
         menuTreeData( HttpMethod.POST ),
-        settingData( HttpMethod.GET ),
+        settingData( HttpMethod.POST ),
         testMacro( HttpMethod.POST ),
         testMacro( HttpMethod.POST ),
         browseLdap( HttpMethod.POST ),
         browseLdap( HttpMethod.POST ),
         copyProfile( HttpMethod.POST ),
         copyProfile( HttpMethod.POST ),
@@ -186,12 +184,6 @@ public class ConfigEditorServlet extends ControlledPwmServlet
             configManagerBean.setStoredConfiguration( loadedConfig );
             configManagerBean.setStoredConfiguration( loadedConfig );
         }
         }
 
 
-        return ProcessStatus.Continue;
-    }
-
-    @Override
-    protected void nextStep( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, IOException, ServletException
-    {
         final DomainStateReader domainStateReader = DomainStateReader.forRequest( pwmRequest );
         final DomainStateReader domainStateReader = DomainStateReader.forRequest( pwmRequest );
         final DomainManageMode mode = domainStateReader.getMode();
         final DomainManageMode mode = domainStateReader.getMode();
 
 
@@ -203,13 +195,21 @@ public class ConfigEditorServlet extends ControlledPwmServlet
             }
             }
             else
             else
             {
             {
-                final DomainID baseDomain = pwmRequest.getDomainID();
                 pwmRequest.getPwmResponse().sendRedirect( PwmServletDefinition.ConfigEditor.servletUrl()
                 pwmRequest.getPwmResponse().sendRedirect( PwmServletDefinition.ConfigEditor.servletUrl()
-                                + "/" + baseDomain.stringValue() );
+                        + "/" + DomainID.systemId().stringValue() );
             }
             }
-            return;
+            return ProcessStatus.Halt;
         }
         }
 
 
+        return ProcessStatus.Continue;
+    }
+
+    @Override
+    protected void nextStep( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, IOException, ServletException
+    {
+        final DomainStateReader domainStateReader = DomainStateReader.forRequest( pwmRequest );
+        final DomainManageMode mode = domainStateReader.getMode();
+
         {
         {
             final String value;
             final String value;
             switch ( mode )
             switch ( mode )
@@ -332,7 +332,6 @@ public class ConfigEditorServlet extends ControlledPwmServlet
 
 
         final ReadSettingResponse readSettingResponse;
         final ReadSettingResponse readSettingResponse;
 
 
-        final StoredConfiguration storedConfiguration;
         if ( settingKey.startsWith( "localeBundle" ) )
         if ( settingKey.startsWith( "localeBundle" ) )
         {
         {
             final StringTokenizer st = new StringTokenizer( settingKey, "-" );
             final StringTokenizer st = new StringTokenizer( settingKey, "-" );
@@ -345,8 +344,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
 
 
             final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForLocaleBundle();
             final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForLocaleBundle();
             modifier.writeLocaleBundleMap( domainID, pwmLocaleBundle, keyName, outputMap );
             modifier.writeLocaleBundleMap( domainID, pwmLocaleBundle, keyName, outputMap );
-            storedConfiguration = modifier.newStoredConfiguration();
-            readSettingResponse = ConfigEditorServletUtils.handleLocaleBundleReadSetting( pwmRequest, storedConfiguration, settingKey );
+            readSettingResponse = ConfigEditorServletUtils.handleLocaleBundleReadSetting( pwmRequest, modifier.newStoredConfiguration(), settingKey );
         }
         }
         else
         else
         {
         {
@@ -359,15 +357,16 @@ public class ConfigEditorServlet extends ControlledPwmServlet
             {
             {
                 final StoredValue storedValue = ValueFactory.fromJson( setting, bodyString );
                 final StoredValue storedValue = ValueFactory.fromJson( setting, bodyString );
                 modifier.writeSetting( key, storedValue, loggedInUser );
                 modifier.writeSetting( key, storedValue, loggedInUser );
-                storedConfiguration = modifier.newStoredConfiguration();
             }
             }
             catch ( final PwmOperationalException e )
             catch ( final PwmOperationalException e )
             {
             {
                 throw new PwmUnrecoverableException( e.getErrorInformation() );
                 throw new PwmUnrecoverableException( e.getErrorInformation() );
             }
             }
-            readSettingResponse = ConfigEditorServletUtils.handleReadSetting( pwmRequest, storedConfiguration, settingKey );
+            readSettingResponse = ConfigEditorServletUtils.handleReadSetting( pwmRequest, modifier.newStoredConfiguration(), settingKey );
         }
         }
-        configManagerBean.setStoredConfiguration( storedConfiguration );
+
+        ConfigurationCleaner.postProcessStoredConfig( modifier );
+        configManagerBean.setStoredConfiguration( modifier.newStoredConfiguration() );
         pwmRequest.outputJsonResult( RestResultBean.withData( readSettingResponse ) );
         pwmRequest.outputJsonResult( RestResultBean.withData( readSettingResponse ) );
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }
@@ -403,6 +402,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
             modifier.resetSetting( key, loggedInUser );
             modifier.resetSetting( key, loggedInUser );
         }
         }
 
 
+        ConfigurationCleaner.postProcessStoredConfig( modifier );
         configManagerBean.setStoredConfiguration( modifier.newStoredConfiguration() );
         configManagerBean.setStoredConfiguration( modifier.newStoredConfiguration() );
         pwmRequest.outputJsonResult( RestResultBean.forSuccessMessage( pwmRequest, Message.Success_Unknown ) );
         pwmRequest.outputJsonResult( RestResultBean.forSuccessMessage( pwmRequest, Message.Success_Unknown ) );
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
@@ -412,7 +412,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
     private ProcessStatus restSetConfigurationPassword(
     private ProcessStatus restSetConfigurationPassword(
             final PwmRequest pwmRequest
             final PwmRequest pwmRequest
     )
     )
-            throws IOException, ServletException, PwmUnrecoverableException
+            throws IOException, PwmUnrecoverableException
     {
     {
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
         final StoredConfigurationModifier modifier = StoredConfigurationModifier.newModifier( configManagerBean.getStoredConfiguration() );
         final StoredConfigurationModifier modifier = StoredConfigurationModifier.newModifier( configManagerBean.getStoredConfiguration() );
@@ -545,19 +545,27 @@ public class ConfigEditorServlet extends ControlledPwmServlet
     {
     {
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
 
 
-        final Map<String, Object> returnObj = new ConcurrentHashMap<>();
-        final ExecutorService executor = Executors.newFixedThreadPool( 3 );
-        executor.execute( () -> ConfigEditorServletUtils.outputChangeLogData( pwmRequest, configManagerBean, returnObj ) );
-        executor.execute( () -> returnObj.put( "health", ConfigEditorServletUtils.configurationHealth( pwmRequest, configManagerBean ) ) );
-        JavaHelper.closeAndWaitExecutor( executor, TimeDuration.MINUTE );
-
-        final RestResultBean restResultBean = RestResultBean.withData( new HashMap<>( returnObj ) );
+        final Map<String, String> returnObj = ConfigEditorServletUtils.outputChangeLogData( pwmRequest, configManagerBean );
+        final RestResultBean restResultBean = RestResultBean.withData( new LinkedHashMap<>( returnObj ) );
         pwmRequest.outputJsonResult( restResultBean );
         pwmRequest.outputJsonResult( restResultBean );
 
 
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }
 
 
+    @ActionHandler( action = "readWarnings" )
+    private ProcessStatus restReadWarnings(
+            final PwmRequest pwmRequest
+    )
+            throws IOException, PwmUnrecoverableException
+    {
+        final ConfigManagerBean configManagerBean = getBean( pwmRequest );
 
 
+        final Map<DomainID, List<String>> healthData = ConfigEditorServletUtils.configurationHealth( pwmRequest, configManagerBean );
+        final RestResultBean restResultBean = RestResultBean.withData( new LinkedHashMap<>( healthData ) );
+        pwmRequest.outputJsonResult( restResultBean );
+
+        return ProcessStatus.Halt;
+    }
 
 
     @ActionHandler( action = "search" )
     @ActionHandler( action = "search" )
     private ProcessStatus restSearchSettings(
     private ProcessStatus restSearchSettings(
@@ -615,7 +623,14 @@ public class ConfigEditorServlet extends ControlledPwmServlet
         final String profileID = pwmRequest.readParameterAsString( "profile" );
         final String profileID = pwmRequest.readParameterAsString( "profile" );
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainID( PwmSetting.LDAP_SERVER_URLS );
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainID( PwmSetting.LDAP_SERVER_URLS );
         final DomainConfig config = new AppConfig( configManagerBean.getStoredConfiguration() ).getDomainConfigs().get( domainID );
         final DomainConfig config = new AppConfig( configManagerBean.getStoredConfiguration() ).getDomainConfigs().get( domainID );
-        final HealthData healthData = LDAPHealthChecker.healthForNewConfiguration( pwmRequest.getPwmDomain(), config, pwmRequest.getLocale(), profileID, true, true );
+        final PublicHealthData healthData = LDAPHealthChecker.healthForNewConfiguration(
+                pwmRequest.getLabel(),
+                pwmRequest.getPwmDomain(),
+                config,
+                pwmRequest.getLocale(),
+                profileID,
+                true,
+                true );
         final RestResultBean restResultBean = RestResultBean.withData( healthData );
         final RestResultBean restResultBean = RestResultBean.withData( healthData );
 
 
         pwmRequest.outputJsonResult( restResultBean );
         pwmRequest.outputJsonResult( restResultBean );
@@ -635,7 +650,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
         final AppConfig config = new AppConfig( configManagerBean.getStoredConfiguration() );
         final AppConfig config = new AppConfig( configManagerBean.getStoredConfiguration() );
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainID( PwmSetting.LDAP_SERVER_URLS );
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainID( PwmSetting.LDAP_SERVER_URLS );
         final List<HealthRecord> healthRecords = DatabaseStatusChecker.checkNewDatabaseStatus( pwmRequest.getPwmApplication(), config );
         final List<HealthRecord> healthRecords = DatabaseStatusChecker.checkNewDatabaseStatus( pwmRequest.getPwmApplication(), config );
-        final HealthData healthData = HealthRecord.asHealthDataBean( config.getDomainConfigs().get( domainID ), pwmRequest.getLocale(), healthRecords );
+        final PublicHealthData healthData = HealthRecord.asHealthDataBean( config.getDomainConfigs().get( domainID ), pwmRequest.getLocale(), healthRecords );
         final RestResultBean restResultBean = RestResultBean.withData( healthData );
         final RestResultBean restResultBean = RestResultBean.withData( healthData );
         pwmRequest.outputJsonResult( restResultBean );
         pwmRequest.outputJsonResult( restResultBean );
         LOGGER.debug( pwmRequest, () -> "completed restDatabaseHealthCheck in " + TimeDuration.fromCurrent( startTime ).asCompactString() );
         LOGGER.debug( pwmRequest, () -> "completed restDatabaseHealthCheck in " + TimeDuration.fromCurrent( startTime ).asCompactString() );
@@ -657,7 +672,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
         final StringBuilder output = new StringBuilder();
         final StringBuilder output = new StringBuilder();
         output.append( "beginning SMS send process:\n" );
         output.append( "beginning SMS send process:\n" );
 
 
-        if ( !SmsQueueManager.smsIsConfigured( config.getAppConfig() ) )
+        if ( !SmsQueueService.smsIsConfigured( config.getAppConfig() ) )
         {
         {
             output.append( "SMS not configured." );
             output.append( "SMS not configured." );
         }
         }
@@ -667,7 +682,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
             final SmsItemBean testSmsItem = new SmsItemBean( testParams.get( "to" ), testParams.get( "message" ), pwmRequest.getLabel() );
             final SmsItemBean testSmsItem = new SmsItemBean( testParams.get( "to" ), testParams.get( "message" ), pwmRequest.getLabel() );
             try
             try
             {
             {
-                final String responseBody = SmsQueueManager.sendDirectMessage(
+                final String responseBody = SmsQueueService.sendDirectMessage(
                         pwmRequest.getPwmDomain(),
                         pwmRequest.getPwmDomain(),
                         config,
                         config,
                         pwmRequest.getLabel(),
                         pwmRequest.getLabel(),
@@ -795,19 +810,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
 
 
-        final Map<String, Object> inputParameters = pwmRequest.readBodyAsJsonMap( PwmHttpRequestWrapper.Flag.BypassValidation );
-        final boolean modifiedSettingsOnly = ( boolean ) inputParameters.get( "modifiedSettingsOnly" );
-        final int level = ( int ) ( ( double ) inputParameters.get( "level" ) );
-        final String filterText = ( String ) inputParameters.get( "text" );
-        final DomainStateReader domainStateReader = DomainStateReader.forRequest( pwmRequest );
-
-        final NavTreeSettings navTreeSettings = NavTreeSettings.builder()
-                .modifiedSettingsOnly( modifiedSettingsOnly )
-                .domainManageMode( domainStateReader.getMode() )
-                .level( level )
-                .filterText( filterText )
-                .locale( pwmRequest.getLocale() )
-                .build();
+        final NavTreeSettings navTreeSettings = NavTreeSettings.readFromRequest( pwmRequest );
 
 
         final StoredConfiguration storedConfiguration = configManagerBean.getStoredConfiguration();
         final StoredConfiguration storedConfiguration = configManagerBean.getStoredConfiguration();
 
 
@@ -827,11 +830,15 @@ public class ConfigEditorServlet extends ControlledPwmServlet
     {
     {
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForLocaleBundle();
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForLocaleBundle();
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
         final ConfigManagerBean configManagerBean = getBean( pwmRequest );
-        final SettingData settingData =  SettingDataMaker.generateSettingData(
+
+        final NavTreeSettings navTreeSettings = NavTreeSettings.readFromRequest( pwmRequest );
+
+        final SettingData settingData = SettingDataMaker.generateSettingData(
                 domainID,
                 domainID,
                 configManagerBean.getStoredConfiguration(),
                 configManagerBean.getStoredConfiguration(),
                 pwmRequest.getLabel(),
                 pwmRequest.getLabel(),
-                pwmRequest.getLocale()
+                pwmRequest.getLocale(),
+                navTreeSettings
         );
         );
 
 
         final RestResultBean restResultBean = RestResultBean.withData( settingData );
         final RestResultBean restResultBean = RestResultBean.withData( settingData );
@@ -876,6 +883,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForDomainSetting(  );
         final DomainID domainID = DomainStateReader.forRequest( pwmRequest ).getDomainIDForDomainSetting(  );
 
 
         final LdapBrowser ldapBrowser = new LdapBrowser(
         final LdapBrowser ldapBrowser = new LdapBrowser(
+                pwmRequest.getLabel(),
                 pwmRequest.getPwmDomain().getLdapConnectionService().getChaiProviderFactory(),
                 pwmRequest.getPwmDomain().getLdapConnectionService().getChaiProviderFactory(),
                 configManagerBean.getStoredConfiguration()
                 configManagerBean.getStoredConfiguration()
         );
         );

+ 21 - 45
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServletUtils.java

@@ -23,7 +23,6 @@ package password.pwm.http.servlet.configeditor;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
-import password.pwm.PwmDomain;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
 import password.pwm.config.AppConfig;
 import password.pwm.config.AppConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
@@ -46,32 +45,30 @@ import password.pwm.health.ConfigurationChecker;
 import password.pwm.health.HealthRecord;
 import password.pwm.health.HealthRecord;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.bean.ConfigManagerBean;
 import password.pwm.http.bean.ConfigManagerBean;
-import password.pwm.http.servlet.configguide.ConfigGuideForm;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
-import password.pwm.util.java.CollectionUtil;
-import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.HttpsServerCertificateManager;
 import password.pwm.util.secure.HttpsServerCertificateManager;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.rest.bean.HealthData;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.time.Instant;
 import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.ResourceBundle;
 import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.StringTokenizer;
-import java.util.stream.Collectors;
+import java.util.TreeMap;
 
 
 public class ConfigEditorServletUtils
 public class ConfigEditorServletUtils
 {
 {
@@ -83,7 +80,6 @@ public class ConfigEditorServletUtils
     )
     )
             throws PwmUnrecoverableException, IOException
             throws PwmUnrecoverableException, IOException
     {
     {
-
         final Map<String, PwmRequest.FileUploadItem> fileUploads;
         final Map<String, PwmRequest.FileUploadItem> fileUploads;
         try
         try
         {
         {
@@ -115,47 +111,26 @@ public class ConfigEditorServletUtils
         return Optional.empty();
         return Optional.empty();
     }
     }
 
 
-    static void outputChangeLogData(
+    static Map<String, String> outputChangeLogData(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
-            final ConfigManagerBean configManagerBean,
-            final Map<String, Object> outputMap
+            final ConfigManagerBean configManagerBean
     )
     )
     {
     {
         final Locale locale = pwmRequest.getLocale();
         final Locale locale = pwmRequest.getLocale();
 
 
-        final Set<StoredConfigKey> changeLog = StoredConfigurationUtil.changedValues(
+        final Set<StoredConfigKey> changedKeys = StoredConfigurationUtil.changedValues(
                 pwmRequest.getPwmDomain().getConfig().getStoredConfiguration(),
                 pwmRequest.getPwmDomain().getConfig().getStoredConfiguration(),
                 configManagerBean.getStoredConfiguration() );
                 configManagerBean.getStoredConfiguration() );
 
 
-        final List<StoredConfigKey> keys = CollectionUtil.iteratorToStream(  configManagerBean.getStoredConfiguration().keys() ).collect( Collectors.toList() );
-
         final Map<String, String> changeLogMap = StoredConfigurationUtil.makeDebugMap(
         final Map<String, String> changeLogMap = StoredConfigurationUtil.makeDebugMap(
                 configManagerBean.getStoredConfiguration(),
                 configManagerBean.getStoredConfiguration(),
-                keys,
+                changedKeys,
                 locale );
                 locale );
 
 
-        final StringBuilder output = new StringBuilder();
-        if ( changeLogMap.isEmpty() )
-        {
-            output.append( "No setting changes." );
-        }
-        else
-        {
-            for ( final Map.Entry<String, String> entry : changeLogMap.entrySet() )
-            {
-                output.append( "<div class=\"changeLogKey\">" );
-                output.append( entry.getKey() );
-                output.append( "</div><div class=\"changeLogValue\">" );
-                output.append( StringUtil.escapeHtml( entry.getValue() ) );
-                output.append( "</div>" );
-            }
-        }
-        outputMap.put( "html", output.toString() );
-        outputMap.put( "modified", !changeLog.isEmpty() );
-
+        return changeLogMap;
     }
     }
 
 
-    static HealthData configurationHealth(
+    static Map<DomainID, List<String>> configurationHealth(
             final PwmRequest pwmRequest,
             final PwmRequest pwmRequest,
             final ConfigManagerBean configManagerBean
             final ConfigManagerBean configManagerBean
     )
     )
@@ -169,26 +144,25 @@ public class ConfigEditorServletUtils
             final PwmApplication tempApplication = PwmApplication.createPwmApplication( pwmRequest.getPwmApplication()
             final PwmApplication tempApplication = PwmApplication.createPwmApplication( pwmRequest.getPwmApplication()
                     .getPwmEnvironment()
                     .getPwmEnvironment()
                     .makeRuntimeInstance( new AppConfig( configManagerBean.getStoredConfiguration() ) ) );
                     .makeRuntimeInstance( new AppConfig( configManagerBean.getStoredConfiguration() ) ) );
-            final PwmDomain tempDomain = tempApplication.domains().get( ConfigGuideForm.DOMAIN_ID );
 
 
-            final List<HealthRecord> healthRecords = configurationChecker.doHealthCheck(
-                    new AppConfig( configManagerBean.getStoredConfiguration() ),
-                    pwmRequest.getLocale()
-            );
+            final List<HealthRecord> healthRecords = configurationChecker.doHealthCheck( tempApplication, pwmRequest.getLabel() );
+            final Map<DomainID, List<String>> returnData = new TreeMap<>();
+
+            healthRecords.forEach( record ->
+                    returnData.computeIfAbsent(
+                            record.getDomainID(), k -> new ArrayList<>() )
+                            .add( record.getDetail( locale, pwmRequest.getAppConfig() ) ) );
 
 
             LOGGER.debug( () -> "config health check done in ", () -> TimeDuration.fromCurrent( startTime ) );
             LOGGER.debug( () -> "config health check done in ", () -> TimeDuration.fromCurrent( startTime ) );
 
 
-            return HealthData.builder()
-                    .overall( "CONFIG" )
-                    .records( password.pwm.ws.server.rest.bean.HealthRecord.fromHealthRecords( healthRecords, locale, tempDomain.getConfig() ) )
-                    .build();
+            return Collections.unmodifiableMap( returnData );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
             LOGGER.error( pwmRequest, () -> "error generating health records: " + e.getMessage() );
             LOGGER.error( pwmRequest, () -> "error generating health records: " + e.getMessage() );
         }
         }
 
 
-        return HealthData.builder().build();
+        return Collections.emptyMap();
     }
     }
 
 
     static ConfigEditorServlet.ReadSettingResponse handleLocaleBundleReadSetting(
     static ConfigEditorServlet.ReadSettingResponse handleLocaleBundleReadSetting(
@@ -318,7 +292,9 @@ public class ConfigEditorServletUtils
     {
     {
         try
         try
         {
         {
-            final PasswordData passwordData = pwmRequest.readParameterAsPassword( "password" );
+            final PasswordData passwordData = pwmRequest.readParameterAsPassword( "password" )
+                    .orElseThrow( () -> new NoSuchElementException( "missing 'password' field" ) );
+
             final String alias = pwmRequest.readParameterAsString( "alias" );
             final String alias = pwmRequest.readParameterAsString( "alias" );
             final HttpsServerCertificateManager.KeyStoreFormat keyStoreFormat;
             final HttpsServerCertificateManager.KeyStoreFormat keyStoreFormat;
             try
             try

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/configeditor/DomainStateReader.java

@@ -34,7 +34,7 @@ import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
 
 
-class DomainStateReader
+public class DomainStateReader
 {
 {
     private final PwmRequest pwmRequest;
     private final PwmRequest pwmRequest;
 
 

+ 18 - 5
server/src/main/java/password/pwm/http/servlet/configeditor/data/NavTreeDataMaker.java

@@ -27,7 +27,9 @@ import password.pwm.bean.DomainID;
 import password.pwm.config.AppConfig;
 import password.pwm.config.AppConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingCategory;
 import password.pwm.config.PwmSettingCategory;
+import password.pwm.config.PwmSettingFlag;
 import password.pwm.config.PwmSettingScope;
 import password.pwm.config.PwmSettingScope;
+import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.stored.ConfigSearchMachine;
 import password.pwm.config.stored.ConfigSearchMachine;
 import password.pwm.config.stored.StoredConfigKey;
 import password.pwm.config.stored.StoredConfigKey;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.stored.StoredConfiguration;
@@ -327,7 +329,7 @@ public class NavTreeDataMaker
 
 
         for ( final PwmSetting setting : category.getSettings() )
         for ( final PwmSetting setting : category.getSettings() )
         {
         {
-            if ( settingMatcher( pwmDomain, storedConfiguration, setting, profile, navTreeSettings ) )
+            if ( settingMatcher( pwmDomain.getDomainID(), storedConfiguration, setting, profile, navTreeSettings ) )
             {
             {
                 return true;
                 return true;
             }
             }
@@ -336,17 +338,22 @@ public class NavTreeDataMaker
         return false;
         return false;
     }
     }
 
 
-    private static boolean settingMatcher(
-            final PwmDomain pwmDomain,
+    static boolean settingMatcher(
+            final DomainID domainID,
             final StoredConfiguration storedConfiguration,
             final StoredConfiguration storedConfiguration,
             final PwmSetting setting,
             final PwmSetting setting,
             final String profileID,
             final String profileID,
             final NavTreeSettings navTreeSettings
             final NavTreeSettings navTreeSettings
     )
     )
     {
     {
-        final StoredConfigKey storedConfigKey = StoredConfigKey.forSetting( setting, profileID, pwmDomain.getDomainID() );
-        final boolean valueIsDefault = StoredConfigurationUtil.isDefaultValue( storedConfiguration, storedConfigKey );
+        final StoredConfigKey storedConfigKey = StoredConfigKey.forSetting( setting, profileID, domainID );
+
+        if ( setting.getSyntax() == PwmSettingSyntax.PROFILE && !setting.isHidden() && setting.getCategory().getParent().isHidden() )
+        {
+            return true;
+        }
 
 
+        final boolean valueIsDefault = StoredConfigurationUtil.isDefaultValue( storedConfiguration, storedConfigKey );
         if ( setting.isHidden() && !valueIsDefault )
         if ( setting.isHidden() && !valueIsDefault )
         {
         {
             return false;
             return false;
@@ -375,6 +382,12 @@ public class NavTreeDataMaker
             return false;
             return false;
         }
         }
 
 
+        if ( setting.getFlags().contains( PwmSettingFlag.MultiDomain )
+                && ( !( new AppConfig( storedConfiguration ).isMultiDomain() ) ) )
+        {
+            return false;
+        }
+
         if ( StringUtil.isEmpty( navTreeSettings.getFilterText() ) )
         if ( StringUtil.isEmpty( navTreeSettings.getFilterText() ) )
         {
         {
             return true;
             return true;

+ 30 - 0
server/src/main/java/password/pwm/http/servlet/configeditor/data/NavTreeSettings.java

@@ -23,10 +23,16 @@ package password.pwm.http.servlet.configeditor.data;
 import lombok.Builder;
 import lombok.Builder;
 import lombok.Value;
 import lombok.Value;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.PwmHttpRequestWrapper;
+import password.pwm.http.PwmRequest;
 import password.pwm.http.servlet.configeditor.DomainManageMode;
 import password.pwm.http.servlet.configeditor.DomainManageMode;
+import password.pwm.http.servlet.configeditor.DomainStateReader;
 
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.util.Locale;
 import java.util.Locale;
+import java.util.Map;
 
 
 @Value
 @Value
 @Builder
 @Builder
@@ -43,4 +49,28 @@ public class NavTreeSettings implements Serializable
     private final Locale locale = PwmConstants.DEFAULT_LOCALE;
     private final Locale locale = PwmConstants.DEFAULT_LOCALE;
 
 
     private final DomainManageMode domainManageMode;
     private final DomainManageMode domainManageMode;
+
+    public static NavTreeSettings forBasic()
+    {
+        return NavTreeSettings.builder()
+                .domainManageMode( DomainManageMode.system )
+                .build();
+    }
+
+    public static NavTreeSettings readFromRequest( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, IOException
+    {
+        final Map<String, Object> inputParameters = pwmRequest.readBodyAsJsonMap( PwmHttpRequestWrapper.Flag.BypassValidation );
+        final boolean modifiedSettingsOnly = ( boolean ) inputParameters.get( "modifiedSettingsOnly" );
+        final int level = ( int ) ( ( double ) inputParameters.get( "level" ) );
+        final String filterText = ( String ) inputParameters.get( "text" );
+        final DomainStateReader domainStateReader = DomainStateReader.forRequest( pwmRequest );
+
+        return NavTreeSettings.builder()
+                .modifiedSettingsOnly( modifiedSettingsOnly )
+                .domainManageMode( domainStateReader.getMode() )
+                .level( level )
+                .filterText( filterText )
+                .locale( pwmRequest.getLocale() )
+                .build();
+    }
 }
 }

+ 23 - 7
server/src/main/java/password/pwm/http/servlet/configeditor/data/SettingDataMaker.java

@@ -25,6 +25,7 @@ import password.pwm.bean.SessionLabel;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingCategory;
 import password.pwm.config.PwmSettingCategory;
 import password.pwm.config.PwmSettingTemplateSet;
 import password.pwm.config.PwmSettingTemplateSet;
+import password.pwm.config.stored.StoredConfigKey;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.stored.StoredConfigurationUtil;
 import password.pwm.config.stored.StoredConfigurationUtil;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
@@ -38,6 +39,7 @@ import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 public class SettingDataMaker
 public class SettingDataMaker
@@ -48,19 +50,33 @@ public class SettingDataMaker
             final DomainID domainID,
             final DomainID domainID,
             final StoredConfiguration storedConfiguration,
             final StoredConfiguration storedConfiguration,
             final SessionLabel sessionLabel,
             final SessionLabel sessionLabel,
-            final Locale locale
+            final Locale locale,
+            final NavTreeSettings navTreeSettings
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         final Instant startGenerateTime = Instant.now();
         final Instant startGenerateTime = Instant.now();
         final PwmSettingTemplateSet templateSet = storedConfiguration.getTemplateSet().get( domainID );
         final PwmSettingTemplateSet templateSet = storedConfiguration.getTemplateSet().get( domainID );
 
 
-        final Map<String, SettingInfo> settingMap = Collections.unmodifiableMap( Arrays.stream( PwmSetting.values() )
-                .collect( Collectors.toMap(
-                        PwmSetting::getKey,
-                        pwmSetting -> SettingInfo.forSetting( pwmSetting, templateSet, locale ),
-                        ( u, v ) -> v,
-                        LinkedHashMap::new ) ) );
+        final Map<String, SettingInfo> settingMap;
+        {
+            final Set<PwmSetting> interestedSets = StoredConfigurationUtil.allPossibleSettingKeysForConfiguration( storedConfiguration ).stream()
+                    .filter( k -> k.isRecordType( StoredConfigKey.RecordType.SETTING ) )
+                    .filter( k -> NavTreeDataMaker.settingMatcher( domainID, storedConfiguration, k.toPwmSetting(), k.getProfileID(), navTreeSettings ) )
+                    .map( StoredConfigKey::toPwmSetting )
+                    .collect( Collectors.toSet() );
+
+            settingMap = interestedSets.stream()
+                    .sorted()
+                    .collect( Collectors.toMap(
+                            PwmSetting::getKey,
+                            pwmSetting -> SettingInfo.forSetting( pwmSetting, templateSet, locale ),
+                            ( u, v ) ->
+                            {
+                                throw new IllegalStateException();
+                            },
+                            LinkedHashMap::new ) );
+        }
 
 
         final Map<String, CategoryInfo> categoryInfoMap = Collections.unmodifiableMap( Arrays.stream( PwmSettingCategory.values() )
         final Map<String, CategoryInfo> categoryInfoMap = Collections.unmodifiableMap( Arrays.stream( PwmSettingCategory.values() )
                 .collect( Collectors.toMap(
                 .collect( Collectors.toMap(

+ 16 - 10
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java

@@ -28,6 +28,7 @@ import password.pwm.PwmApplicationMode;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.PwmDomain;
 import password.pwm.PwmDomain;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.AppConfig;
 import password.pwm.config.AppConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.LdapProfile;
 import password.pwm.config.profile.LdapProfile;
@@ -60,6 +61,7 @@ import password.pwm.http.bean.ConfigGuideBean;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.configeditor.ConfigEditorServletUtils;
 import password.pwm.http.servlet.configeditor.ConfigEditorServletUtils;
+import password.pwm.http.servlet.configeditor.data.NavTreeSettings;
 import password.pwm.http.servlet.configeditor.data.SettingData;
 import password.pwm.http.servlet.configeditor.data.SettingData;
 import password.pwm.http.servlet.configeditor.data.SettingDataMaker;
 import password.pwm.http.servlet.configeditor.data.SettingDataMaker;
 import password.pwm.i18n.Message;
 import password.pwm.i18n.Message;
@@ -71,7 +73,8 @@ import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.X509Utils;
 import password.pwm.util.secure.X509Utils;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.rest.bean.HealthData;
+import password.pwm.ws.server.rest.bean.PublicHealthData;
+import password.pwm.ws.server.rest.bean.PublicHealthRecord;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.annotation.WebServlet;
@@ -240,12 +243,13 @@ public class ConfigGuideServlet extends ControlledPwmServlet
                 .getPwmEnvironment()
                 .getPwmEnvironment()
                 .makeRuntimeInstance( tempAppConfig ) );
                 .makeRuntimeInstance( tempAppConfig ) );
         final PwmDomain tempDomain = tempApplication.domains().get( ConfigGuideForm.DOMAIN_ID );
         final PwmDomain tempDomain = tempApplication.domains().get( ConfigGuideForm.DOMAIN_ID );
-        // final DomainConfig tempDomainConfig = tempDomain.getConfig();
 
 
         final LDAPHealthChecker ldapHealthChecker = new LDAPHealthChecker();
         final LDAPHealthChecker ldapHealthChecker = new LDAPHealthChecker();
         final List<HealthRecord> records = new ArrayList<>();
         final List<HealthRecord> records = new ArrayList<>();
         final LdapProfile ldapProfile = tempDomain.getConfig().getDefaultLdapProfile();
         final LdapProfile ldapProfile = tempDomain.getConfig().getDefaultLdapProfile();
 
 
+        final SessionLabel sessionLabel = pwmRequest.getLabel();
+
         switch ( configGuideBean.getStep() )
         switch ( configGuideBean.getStep() )
         {
         {
             case LDAP_SERVER:
             case LDAP_SERVER:
@@ -271,7 +275,7 @@ public class ConfigGuideServlet extends ControlledPwmServlet
 
 
             case LDAP_PROXY:
             case LDAP_PROXY:
             {
             {
-                records.addAll( ldapHealthChecker.checkBasicLdapConnectivity( tempDomain, tempDomain.getConfig(), ldapProfile, false ) );
+                records.addAll( ldapHealthChecker.checkBasicLdapConnectivity( sessionLabel, tempDomain, tempDomain.getConfig(), ldapProfile, false ) );
                 if ( records.isEmpty() )
                 if ( records.isEmpty() )
                 {
                 {
                     records.add( password.pwm.health.HealthRecord.forMessage( ConfigGuideForm.DOMAIN_ID, HealthMessage.LDAP_OK ) );
                     records.add( password.pwm.health.HealthRecord.forMessage( ConfigGuideForm.DOMAIN_ID, HealthMessage.LDAP_OK ) );
@@ -281,7 +285,7 @@ public class ConfigGuideServlet extends ControlledPwmServlet
 
 
             case LDAP_CONTEXT:
             case LDAP_CONTEXT:
             {
             {
-                records.addAll( ldapHealthChecker.checkBasicLdapConnectivity( tempDomain, tempDomain.getConfig(), ldapProfile, true ) );
+                records.addAll( ldapHealthChecker.checkBasicLdapConnectivity( sessionLabel, tempDomain, tempDomain.getConfig(), ldapProfile, true ) );
                 if ( records.isEmpty() )
                 if ( records.isEmpty() )
                 {
                 {
                     records.add( HealthRecord.forMessage(
                     records.add( HealthRecord.forMessage(
@@ -303,8 +307,8 @@ public class ConfigGuideServlet extends ControlledPwmServlet
                 final String testUserValue = configGuideBean.getFormData().get( ConfigGuideFormField.PARAM_LDAP_TEST_USER );
                 final String testUserValue = configGuideBean.getFormData().get( ConfigGuideFormField.PARAM_LDAP_TEST_USER );
                 if ( testUserValue != null && !testUserValue.isEmpty() )
                 if ( testUserValue != null && !testUserValue.isEmpty() )
                 {
                 {
-                    records.addAll( ldapHealthChecker.checkBasicLdapConnectivity( tempDomain, tempDomain.getConfig(), ldapProfile, false ) );
-                    records.addAll( ldapHealthChecker.doLdapTestUserCheck( tempDomain.getConfig(), ldapProfile, tempDomain ) );
+                    records.addAll( ldapHealthChecker.checkBasicLdapConnectivity( sessionLabel, tempDomain, tempDomain.getConfig(), ldapProfile, false ) );
+                    records.addAll( ldapHealthChecker.doLdapTestUserCheck( sessionLabel, tempDomain.getConfig(), ldapProfile, tempDomain ) );
                 }
                 }
                 else
                 else
                 {
                 {
@@ -327,8 +331,8 @@ public class ConfigGuideServlet extends ControlledPwmServlet
                 JavaHelper.unhandledSwitchStatement( configGuideBean.getStep() );
                 JavaHelper.unhandledSwitchStatement( configGuideBean.getStep() );
         }
         }
 
 
-        final HealthData jsonOutput = HealthData.builder()
-                .records( password.pwm.ws.server.rest.bean.HealthRecord.fromHealthRecords( records, pwmRequest.getLocale(), tempDomain.getConfig() ) )
+        final PublicHealthData jsonOutput = PublicHealthData.builder()
+                .records( PublicHealthRecord.fromHealthRecords( records, pwmRequest.getLocale(), tempDomain.getConfig() ) )
                 .timestamp( Instant.now() )
                 .timestamp( Instant.now() )
                 .overall( HealthUtils.getMostSevereHealthStatus( records ).toString() )
                 .overall( HealthUtils.getMostSevereHealthStatus( records ).toString() )
                 .build();
                 .build();
@@ -361,6 +365,7 @@ public class ConfigGuideServlet extends ControlledPwmServlet
         final String dn = inputMap.getOrDefault( "dn", "" );
         final String dn = inputMap.getOrDefault( "dn", "" );
 
 
         final LdapBrowser ldapBrowser = new LdapBrowser(
         final LdapBrowser ldapBrowser = new LdapBrowser(
+                pwmRequest.getLabel(),
                 pwmRequest.getPwmDomain().getLdapConnectionService().getChaiProviderFactory(),
                 pwmRequest.getPwmDomain().getLdapConnectionService().getChaiProviderFactory(),
                 storedConfiguration
                 storedConfiguration
         );
         );
@@ -612,7 +617,7 @@ public class ConfigGuideServlet extends ControlledPwmServlet
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
-            final String errorMsg = "error writing default value for setting " + pwmSetting.toString() + ", error: " + e.getMessage();
+            final String errorMsg = "error writing default value for setting " + pwmSetting + ", error: " + e.getMessage();
             LOGGER.error( () -> errorMsg, e );
             LOGGER.error( () -> errorMsg, e );
             throw new IllegalStateException( errorMsg, e );
             throw new IllegalStateException( errorMsg, e );
         }
         }
@@ -637,7 +642,8 @@ public class ConfigGuideServlet extends ControlledPwmServlet
                 ConfigGuideForm.DOMAIN_ID,
                 ConfigGuideForm.DOMAIN_ID,
                 storedConfiguration,
                 storedConfiguration,
                 pwmRequest.getLabel(),
                 pwmRequest.getLabel(),
-                pwmRequest.getLocale()
+                pwmRequest.getLocale(),
+                NavTreeSettings.forBasic()
         );
         );
 
 
         final RestResultBean restResultBean = RestResultBean.withData( settingData );
         final RestResultBean restResultBean = RestResultBean.withData( settingData );

+ 3 - 3
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideUtils.java

@@ -77,7 +77,6 @@ import java.util.Optional;
 
 
 public class ConfigGuideUtils
 public class ConfigGuideUtils
 {
 {
-
     private static final PwmLogger LOGGER = PwmLogger.getLogger( ConfigGuideUtils.class.getName() );
     private static final PwmLogger LOGGER = PwmLogger.getLogger( ConfigGuideUtils.class.getName() );
 
 
     static void writeConfig(
     static void writeConfig(
@@ -196,7 +195,7 @@ public class ConfigGuideUtils
     {
     {
         final int ordinal = step.ordinal();
         final int ordinal = step.ordinal();
         final int total = GuideStep.values().length - 2;
         final int total = GuideStep.values().length - 2;
-        return new Percent( ordinal, total );
+        return Percent.of( ordinal, total );
     }
     }
 
 
     static void checkLdapServer( final ConfigGuideBean configGuideBean )
     static void checkLdapServer( final ConfigGuideBean configGuideBean )
@@ -294,6 +293,7 @@ public class ConfigGuideUtils
 
 
             final UserMatchViewerFunction userMatchViewerFunction = new UserMatchViewerFunction();
             final UserMatchViewerFunction userMatchViewerFunction = new UserMatchViewerFunction();
             final Collection<UserIdentity> results = userMatchViewerFunction.discoverMatchingUsers(
             final Collection<UserIdentity> results = userMatchViewerFunction.discoverMatchingUsers(
+                    pwmRequest.getLabel(),
                     pwmDomain,
                     pwmDomain,
                     1,
                     1,
                     storedConfiguration,
                     storedConfiguration,
@@ -302,7 +302,7 @@ public class ConfigGuideUtils
             if ( !results.isEmpty() )
             if ( !results.isEmpty() )
             {
             {
                 final UserIdentity foundIdentity = results.iterator().next();
                 final UserIdentity foundIdentity = results.iterator().next();
-                if ( foundIdentity.canonicalEquals( adminIdentity, tempApplication ) )
+                if ( foundIdentity.canonicalEquals( pwmRequest.getLabel(), adminIdentity, tempApplication ) )
                 {
                 {
                     records.add( HealthRecord.forMessage( ConfigGuideForm.DOMAIN_ID, HealthMessage.LDAP_AdminUserOk ) );
                     records.add( HealthRecord.forMessage( ConfigGuideForm.DOMAIN_ID, HealthMessage.LDAP_AdminUserOk ) );
                 }
                 }

+ 4 - 4
server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java

@@ -150,8 +150,8 @@ public class ConfigManagerLoginServlet extends AbstractPwmServlet
             else
             else
             {
             {
                 LOGGER.trace( pwmRequest, () -> "configuration password is not correct" );
                 LOGGER.trace( pwmRequest, () -> "configuration password is not correct" );
-                pwmDomain.getIntruderManager().convenience().markAddressAndSession( pwmRequest );
-                pwmDomain.getIntruderManager().mark( IntruderRecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME, pwmRequest.getLabel() );
+                pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
+                pwmDomain.getIntruderService().mark( IntruderRecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME, pwmRequest.getLabel() );
                 final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_PASSWORD_ONLY_BAD );
                 final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_PASSWORD_ONLY_BAD );
                 updateLoginHistory( pwmRequest, pwmRequest.getUserInfoIfLoggedIn(), false );
                 updateLoginHistory( pwmRequest, pwmRequest.getUserInfoIfLoggedIn(), false );
                 setLastError( pwmRequest, errorInformation );
                 setLastError( pwmRequest, errorInformation );
@@ -257,8 +257,8 @@ public class ConfigManagerLoginServlet extends AbstractPwmServlet
         final PwmSession pwmSession = pwmRequest.getPwmSession();
         final PwmSession pwmSession = pwmRequest.getPwmSession();
 
 
         configManagerBean.setPasswordVerified( true );
         configManagerBean.setPasswordVerified( true );
-        pwmDomain.getIntruderManager().convenience().clearAddressAndSession( pwmSession );
-        pwmDomain.getIntruderManager().clear( IntruderRecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME );
+        pwmDomain.getIntruderService().client().clearAddressAndSession( pwmSession );
+        pwmDomain.getIntruderService().clear( IntruderRecordType.USERNAME, PwmConstants.CONFIGMANAGER_INTRUDER_USERNAME );
         pwmRequest.getPwmSession().getSessionStateBean().setSessionIdRecycleNeeded( true );
         pwmRequest.getPwmSession().getSessionStateBean().setSessionIdRecycleNeeded( true );
         if ( persistentLoginEnabled && "on".equals( pwmRequest.readParameterAsString( "remember" ) ) )
         if ( persistentLoginEnabled && "on".equals( pwmRequest.readParameterAsString( "remember" ) ) )
         {
         {

+ 28 - 28
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java

@@ -72,8 +72,9 @@ import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenType;
 import password.pwm.svc.token.TokenType;
@@ -86,8 +87,8 @@ import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.macro.MacroRequest;
-import password.pwm.util.operations.cr.NMASCrOperator;
-import password.pwm.util.operations.otp.OTPUserRecord;
+import password.pwm.svc.cr.NMASCrOperator;
+import password.pwm.svc.otp.OTPUserRecord;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
 
 
@@ -197,7 +198,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
 
 
         if ( forgottenPasswordBean.getUserIdentity() != null )
         if ( forgottenPasswordBean.getUserIdentity() != null )
         {
         {
-            pwmDomain.getIntruderManager().convenience().checkUserIdentity( forgottenPasswordBean.getUserIdentity() );
+            pwmDomain.getIntruderService().client().checkUserIdentity( forgottenPasswordBean.getUserIdentity() );
         }
         }
 
 
         checkForLocaleSwitch( pwmRequest, forgottenPasswordBean );
         checkForLocaleSwitch( pwmRequest, forgottenPasswordBean );
@@ -423,7 +424,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
             formValues = FormUtility.readFormValuesFromRequest( pwmRequest, forgottenPasswordForm, userLocale );
             formValues = FormUtility.readFormValuesFromRequest( pwmRequest, forgottenPasswordForm, userLocale );
 
 
             // check for intruder search values
             // check for intruder search values
-            pwmDomain.getIntruderManager().convenience().checkAttributes( formValues );
+            pwmDomain.getIntruderService().client().checkAttributes( formValues );
 
 
             // see if the values meet the configured form requirements.
             // see if the values meet the configured form requirements.
             FormUtility.validateFormValues( pwmRequest.getDomainConfig(), formValues, userLocale );
             FormUtility.validateFormValues( pwmRequest.getDomainConfig(), formValues, userLocale );
@@ -461,13 +462,13 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
                 throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_CANT_MATCH_USER ) );
                 throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_CANT_MATCH_USER ) );
             }
             }
 
 
-            AuthenticationUtility.checkIfUserEligibleToAuthentication( pwmDomain, userIdentity );
+            AuthenticationUtility.checkIfUserEligibleToAuthentication( pwmRequest.getLabel(), pwmDomain, userIdentity );
 
 
             final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean( pwmRequest );
             final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean( pwmRequest );
             ForgottenPasswordUtil.initForgottenPasswordBean( pwmRequest.getPwmRequestContext(), userIdentity, forgottenPasswordBean );
             ForgottenPasswordUtil.initForgottenPasswordBean( pwmRequest.getPwmRequestContext(), userIdentity, forgottenPasswordBean );
 
 
             // clear intruder search values
             // clear intruder search values
-            pwmDomain.getIntruderManager().convenience().clearAttributes( formValues );
+            pwmDomain.getIntruderService().client().clearAttributes( formValues );
 
 
             return ProcessStatus.Continue;
             return ProcessStatus.Continue;
         }
         }
@@ -479,10 +480,11 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
                         PwmError.ERROR_RESPONSES_NORESPONSES,
                         PwmError.ERROR_RESPONSES_NORESPONSES,
                         e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues()
                         e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues()
                 );
                 );
-                pwmDomain.getStatisticsManager().incrementValue( Statistic.RECOVERY_FAILURES );
 
 
-                pwmDomain.getIntruderManager().convenience().markAddressAndSession( pwmRequest );
-                pwmDomain.getIntruderManager().convenience().markAttributes( formValues, pwmRequest.getLabel() );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.RECOVERY_FAILURES );
+
+                pwmDomain.getIntruderService().client().markAddressAndSession( pwmRequest );
+                pwmDomain.getIntruderService().client().markAttributes( formValues, pwmRequest.getLabel() );
 
 
                 LOGGER.debug( pwmRequest, errorInfo );
                 LOGGER.debug( pwmRequest, errorInfo );
                 setLastError( pwmRequest, errorInfo );
                 setLastError( pwmRequest, errorInfo );
@@ -529,7 +531,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
                 );
                 );
             }
             }
             forgottenPasswordBean.getProgress().getSatisfiedMethods().add( IdentityVerificationMethod.TOKEN );
             forgottenPasswordBean.getProgress().getSatisfiedMethods().add( IdentityVerificationMethod.TOKEN );
-            StatisticsManager.incrementStat( pwmRequest.getPwmDomain(), Statistic.RECOVERY_TOKENS_PASSED );
+            StatisticsClient.incrementStat( pwmRequest.getPwmDomain(), Statistic.RECOVERY_TOKENS_PASSED );
 
 
             if ( pwmRequest.getDomainConfig().readSettingAsBoolean( PwmSetting.DISPLAY_TOKEN_SUCCESS_BUTTON ) )
             if ( pwmRequest.getDomainConfig().readSettingAsBoolean( PwmSetting.DISPLAY_TOKEN_SUCCESS_BUTTON ) )
             {
             {
@@ -624,13 +626,13 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
 
 
                 if ( otpPassed )
                 if ( otpPassed )
                 {
                 {
-                    StatisticsManager.incrementStat( pwmRequest, Statistic.RECOVERY_OTP_PASSED );
+                    StatisticsClient.incrementStat( pwmRequest, Statistic.RECOVERY_OTP_PASSED );
                     LOGGER.debug( pwmRequest, () -> "one time password validation has been passed" );
                     LOGGER.debug( pwmRequest, () -> "one time password validation has been passed" );
                     forgottenPasswordBean.getProgress().getSatisfiedMethods().add( IdentityVerificationMethod.OTP );
                     forgottenPasswordBean.getProgress().getSatisfiedMethods().add( IdentityVerificationMethod.OTP );
                 }
                 }
                 else
                 else
                 {
                 {
-                    StatisticsManager.incrementStat( pwmRequest, Statistic.RECOVERY_OTP_FAILED );
+                    StatisticsClient.incrementStat( pwmRequest, Statistic.RECOVERY_OTP_FAILED );
                     handleUserVerificationBadAttempt( pwmRequest, forgottenPasswordBean, new ErrorInformation( PwmError.ERROR_INCORRECT_OTP_TOKEN ) );
                     handleUserVerificationBadAttempt( pwmRequest, forgottenPasswordBean, new ErrorInformation( PwmError.ERROR_INCORRECT_OTP_TOKEN ) );
                 }
                 }
             }
             }
@@ -845,7 +847,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
             {
             {
                 final List<FormConfiguration> formConfigurations = pwmRequest.getDomainConfig().readSettingAsForm( PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM );
                 final List<FormConfiguration> formConfigurations = pwmRequest.getDomainConfig().readSettingAsForm( PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM );
                 final Map<FormConfiguration, String> formMap = FormUtility.asFormConfigurationMap( formConfigurations, forgottenPasswordBean.getUserSearchValues() );
                 final Map<FormConfiguration, String> formMap = FormUtility.asFormConfigurationMap( formConfigurations, forgottenPasswordBean.getUserSearchValues() );
-                pwmRequest.getPwmDomain().getIntruderManager().convenience().markAttributes( formMap, pwmRequest.getLabel() );
+                pwmRequest.getPwmDomain().getIntruderService().client().markAttributes( formMap, pwmRequest.getLabel() );
             }
             }
 
 
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_INCORRECT_RESPONSE,
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_INCORRECT_RESPONSE,
@@ -869,7 +871,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
         try
         try
         {
         {
             // check attributes
             // check attributes
-            final ChaiUser theUser = pwmRequest.getPwmDomain().getProxiedChaiUser( userIdentity );
+            final ChaiUser theUser = pwmRequest.getPwmDomain().getProxiedChaiUser( pwmRequest.getLabel(), userIdentity );
             final Locale userLocale = pwmRequest.getLocale();
             final Locale userLocale = pwmRequest.getLocale();
 
 
             final List<FormConfiguration> requiredAttributesForm = forgottenPasswordBean.getAttributeForm();
             final List<FormConfiguration> requiredAttributesForm = forgottenPasswordBean.getAttributeForm();
@@ -933,13 +935,13 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
         if ( !forgottenPasswordBean.isAgreementPassed() )
         if ( !forgottenPasswordBean.isAgreementPassed() )
         {
         {
             forgottenPasswordBean.setAgreementPassed( true );
             forgottenPasswordBean.setAgreementPassed( true );
-            final AuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createUserAuditRecord(
+            final AuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createUserAuditRecord(
                     AuditEvent.AGREEMENT_PASSED,
                     AuditEvent.AGREEMENT_PASSED,
                     pwmRequest.getUserInfoIfLoggedIn(),
                     pwmRequest.getUserInfoIfLoggedIn(),
                     pwmRequest.getLabel(),
                     pwmRequest.getLabel(),
                     "ForgottenPassword"
                     "ForgottenPassword"
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+            AuditServiceClient.submit( pwmRequest, auditRecord );
         }
         }
 
 
         return ProcessStatus.Continue;
         return ProcessStatus.Continue;
@@ -1095,7 +1097,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
         if ( !forgottenPasswordBean.getProgress().isAllPassed() )
         if ( !forgottenPasswordBean.getProgress().isAllPassed() )
         {
         {
             forgottenPasswordBean.getProgress().setAllPassed( true );
             forgottenPasswordBean.getProgress().setAllPassed( true );
-            StatisticsManager.incrementStat( pwmRequest, Statistic.RECOVERY_SUCCESSES );
+            StatisticsClient.incrementStat( pwmRequest, Statistic.RECOVERY_SUCCESSES );
         }
         }
 
 
         final UserInfo userInfo = ForgottenPasswordUtil.readUserInfo( pwmRequest.getPwmRequestContext(), forgottenPasswordBean );
         final UserInfo userInfo = ForgottenPasswordUtil.readUserInfo( pwmRequest.getPwmRequestContext(), forgottenPasswordBean );
@@ -1163,18 +1165,17 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
             throws IOException, ServletException, ChaiUnavailableException, PwmUnrecoverableException
             throws IOException, ServletException, ChaiUnavailableException, PwmUnrecoverableException
     {
     {
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
-        final PwmSession pwmSession = pwmRequest.getPwmSession();
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean( pwmRequest );
         final ForgottenPasswordBean forgottenPasswordBean = forgottenPasswordBean( pwmRequest );
         final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
         final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
 
 
         try
         try
         {
         {
-            final ChaiUser theUser = pwmDomain.getProxiedChaiUser( userIdentity );
+            final ChaiUser theUser = pwmDomain.getProxiedChaiUser( pwmRequest.getLabel(), userIdentity );
             theUser.unlockPassword();
             theUser.unlockPassword();
 
 
             // mark the event log
             // mark the event log
             final UserInfo userInfoBean = ForgottenPasswordUtil.readUserInfo( pwmRequest.getPwmRequestContext(), forgottenPasswordBean );
             final UserInfo userInfoBean = ForgottenPasswordUtil.readUserInfo( pwmRequest.getPwmRequestContext(), forgottenPasswordBean );
-            pwmDomain.getAuditManager().submit( AuditEvent.UNLOCK_PASSWORD, userInfoBean, pwmSession );
+            AuditServiceClient.submitUserEvent( pwmRequest, AuditEvent.UNLOCK_PASSWORD, userInfoBean );
 
 
             ForgottenPasswordUtil.sendUnlockNoticeEmail( pwmRequest.getPwmRequestContext(), forgottenPasswordBean );
             ForgottenPasswordUtil.sendUnlockNoticeEmail( pwmRequest.getPwmRequestContext(), forgottenPasswordBean );
 
 
@@ -1207,7 +1208,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
         }
         }
 
 
         final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
         final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
-        final ChaiUser theUser = pwmDomain.getProxiedChaiUser( userIdentity );
+        final ChaiUser theUser = pwmDomain.getProxiedChaiUser( pwmRequest.getLabel(), userIdentity );
 
 
         try
         try
         {
         {
@@ -1219,7 +1220,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
         {
         {
             final String errorMsg = "unable to unlock user " + theUser.getEntryDN() + " error: " + e.getMessage();
             final String errorMsg = "unable to unlock user " + theUser.getEntryDN() + " error: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_UNLOCK_FAILURE, errorMsg );
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_UNLOCK_FAILURE, errorMsg );
-            LOGGER.error( pwmRequest, () -> errorInformation.toDebugStr() );
+            LOGGER.error( pwmRequest, errorInformation::toDebugStr );
         }
         }
 
 
         try
         try
@@ -1235,8 +1236,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
             LOGGER.info( pwmRequest, () -> "user successfully supplied password recovery responses, forward to change password page: " + theUser.getEntryDN() );
             LOGGER.info( pwmRequest, () -> "user successfully supplied password recovery responses, forward to change password page: " + theUser.getEntryDN() );
 
 
             // mark the event log
             // mark the event log
-            pwmDomain.getAuditManager().submit( AuditEvent.RECOVER_PASSWORD, pwmSession.getUserInfo(),
-                    pwmSession );
+            AuditServiceClient.submitUserEvent( pwmRequest, AuditEvent.RECOVER_PASSWORD, pwmSession.getUserInfo() );
 
 
             // mark user as requiring a new password.
             // mark user as requiring a new password.
             pwmSession.getLoginInfoBean().getLoginFlags().add( LoginInfoBean.LoginFlag.forcePwChange );
             pwmSession.getLoginInfoBean().getLoginFlags().add( LoginInfoBean.LoginFlag.forcePwChange );
@@ -1278,12 +1278,12 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet
                     PwmAuthenticationSource.FORGOTTEN_PASSWORD
                     PwmAuthenticationSource.FORGOTTEN_PASSWORD
             );
             );
             sessionAuthenticator.simulateBadPassword( userIdentity );
             sessionAuthenticator.simulateBadPassword( userIdentity );
-            pwmRequest.getPwmDomain().getIntruderManager().convenience().markUserIdentity( userIdentity, pwmRequest );
+            pwmRequest.getPwmDomain().getIntruderService().client().markUserIdentity( userIdentity, pwmRequest );
         }
         }
 
 
-        pwmRequest.getPwmDomain().getIntruderManager().convenience().markAddressAndSession( pwmRequest );
+        pwmRequest.getPwmDomain().getIntruderService().client().markAddressAndSession( pwmRequest );
 
 
-        StatisticsManager.incrementStat( pwmRequest, Statistic.RECOVERY_FAILURES );
+        StatisticsClient.incrementStat( pwmRequest, Statistic.RECOVERY_FAILURES );
     }
     }
 
 
     private void checkForLocaleSwitch( final PwmRequest pwmRequest, final ForgottenPasswordBean forgottenPasswordBean )
     private void checkForLocaleSwitch( final PwmRequest pwmRequest, final ForgottenPasswordBean forgottenPasswordBean )

+ 2 - 1
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordStageProcessor.java

@@ -38,6 +38,7 @@ import password.pwm.http.bean.ForgottenPasswordBean;
 import password.pwm.http.bean.ForgottenPasswordStage;
 import password.pwm.http.bean.ForgottenPasswordStage;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfo;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -323,7 +324,7 @@ class ForgottenPasswordStageProcessor
             if ( !forgottenPasswordBean.getProgress().isAllPassed() )
             if ( !forgottenPasswordBean.getProgress().isAllPassed() )
             {
             {
                 forgottenPasswordBean.getProgress().setAllPassed( true );
                 forgottenPasswordBean.getProgress().setAllPassed( true );
-                pwmDomain.getStatisticsManager().incrementValue( Statistic.RECOVERY_SUCCESSES );
+                StatisticsClient.incrementStat( pwmRequestContext.getPwmApplication(), Statistic.RECOVERY_SUCCESSES );
             }
             }
 
 
             final UserInfo userInfo = ForgottenPasswordUtil.readUserInfo( pwmRequestContext, forgottenPasswordBean );
             final UserInfo userInfo = ForgottenPasswordUtil.readUserInfo( pwmRequestContext, forgottenPasswordBean );

+ 21 - 19
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordStateMachine.java

@@ -61,7 +61,7 @@ import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenType;
 import password.pwm.svc.token.TokenType;
@@ -75,7 +75,7 @@ import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.macro.MacroRequest;
-import password.pwm.util.operations.otp.OTPUserRecord;
+import password.pwm.svc.otp.OTPUserRecord;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.ws.server.PresentableForm;
 import password.pwm.ws.server.PresentableForm;
 import password.pwm.ws.server.PresentableFormRow;
 import password.pwm.ws.server.PresentableFormRow;
@@ -234,6 +234,7 @@ public class ForgottenPasswordStateMachine
                 throws PwmUnrecoverableException
                 throws PwmUnrecoverableException
         {
         {
             final PwmRequestContext pwmRequestContext = forgottenPasswordStateMachine.getRequestContext();
             final PwmRequestContext pwmRequestContext = forgottenPasswordStateMachine.getRequestContext();
+            final SessionLabel sessionLabel = pwmRequestContext.getSessionLabel();
             final PasswordData password1 = PasswordData.forStringValue( formValues.get( PARAM_PASSWORD ) );
             final PasswordData password1 = PasswordData.forStringValue( formValues.get( PARAM_PASSWORD ) );
             final PasswordData password2 = PasswordData.forStringValue( formValues.get( PARAM_PASSWORD_CONFIRM ) );
             final PasswordData password2 = PasswordData.forStringValue( formValues.get( PARAM_PASSWORD_CONFIRM ) );
 
 
@@ -253,9 +254,8 @@ public class ForgottenPasswordStateMachine
                 if ( verifyOnly )
                 if ( verifyOnly )
                 {
                 {
                     final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
                     final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
-                            pwmRequestContext.getPwmDomain(),
-                            pwmRequestContext.getLocale(),
-                            pwmRequestContext.getPwmDomain().getProxiedChaiUser( userInfo.getUserIdentity() ),
+                            pwmRequestContext,
+                            pwmRequestContext.getPwmDomain().getProxiedChaiUser( sessionLabel, userInfo.getUserIdentity() ),
                             userInfo,
                             userInfo,
                             null,
                             null,
                             password1,
                             password1,
@@ -273,7 +273,7 @@ public class ForgottenPasswordStateMachine
                     PasswordUtility.setPassword(
                     PasswordUtility.setPassword(
                             forgottenPasswordStateMachine.getRequestContext().getPwmDomain(),
                             forgottenPasswordStateMachine.getRequestContext().getPwmDomain(),
                             forgottenPasswordStateMachine.getRequestContext().getSessionLabel(),
                             forgottenPasswordStateMachine.getRequestContext().getSessionLabel(),
-                            forgottenPasswordStateMachine.getRequestContext().getPwmDomain().getProxyChaiProvider( userInfo.getUserIdentity().getLdapProfileID() ),
+                            forgottenPasswordStateMachine.getRequestContext().getPwmDomain().getProxyChaiProvider( sessionLabel, userInfo.getUserIdentity().getLdapProfileID() ),
                             userInfo,
                             userInfo,
                             null,
                             null,
                             password1 );
                             password1 );
@@ -498,7 +498,7 @@ public class ForgottenPasswordStateMachine
 
 
                 if ( otpPassed )
                 if ( otpPassed )
                 {
                 {
-                    pwmRequestContext.getPwmDomain().getStatisticsManager().incrementValue( Statistic.RECOVERY_OTP_PASSED );
+                    StatisticsClient.incrementStat( pwmRequestContext.getPwmApplication(), Statistic.RECOVERY_OTP_PASSED );
                     forgottenPasswordStateMachine.getForgottenPasswordBean().getProgress().getSatisfiedMethods().add( IdentityVerificationMethod.OTP );
                     forgottenPasswordStateMachine.getForgottenPasswordBean().getProgress().getSatisfiedMethods().add( IdentityVerificationMethod.OTP );
                 }
                 }
                 else
                 else
@@ -506,7 +506,8 @@ public class ForgottenPasswordStateMachine
                     errorInformation = errorInformation == null
                     errorInformation = errorInformation == null
                             ? new ErrorInformation( PwmError.ERROR_INCORRECT_OTP_TOKEN )
                             ? new ErrorInformation( PwmError.ERROR_INCORRECT_OTP_TOKEN )
                             : errorInformation;
                             : errorInformation;
-                    pwmRequestContext.getPwmDomain().getStatisticsManager().incrementValue( Statistic.RECOVERY_OTP_FAILED );
+
+                    StatisticsClient.incrementStat( pwmRequestContext.getPwmApplication(), Statistic.RECOVERY_OTP_FAILED );
                     handleUserVerificationBadAttempt( pwmRequestContext, forgottenPasswordStateMachine.getForgottenPasswordBean(), errorInformation );
                     handleUserVerificationBadAttempt( pwmRequestContext, forgottenPasswordStateMachine.getForgottenPasswordBean(), errorInformation );
                     throw new PwmUnrecoverableException( errorInformation );
                     throw new PwmUnrecoverableException( errorInformation );
                 }
                 }
@@ -587,7 +588,7 @@ public class ForgottenPasswordStateMachine
                     }
                     }
 
 
                     forgottenPasswordStateMachine.getForgottenPasswordBean().getProgress().getSatisfiedMethods().add( IdentityVerificationMethod.TOKEN );
                     forgottenPasswordStateMachine.getForgottenPasswordBean().getProgress().getSatisfiedMethods().add( IdentityVerificationMethod.TOKEN );
-                    StatisticsManager.incrementStat( pwmRequestContext.getPwmDomain(), Statistic.RECOVERY_TOKENS_PASSED );
+                    StatisticsClient.incrementStat( pwmRequestContext.getPwmDomain(), Statistic.RECOVERY_TOKENS_PASSED );
 
 
                     if ( pwmRequestContext.getDomainConfig().readSettingAsBoolean( PwmSetting.DISPLAY_TOKEN_SUCCESS_BUTTON ) )
                     if ( pwmRequestContext.getDomainConfig().readSettingAsBoolean( PwmSetting.DISPLAY_TOKEN_SUCCESS_BUTTON ) )
                     {
                     {
@@ -744,7 +745,7 @@ public class ForgottenPasswordStateMachine
                     {
                     {
                         final List<FormConfiguration> formConfigurations = pwmDomain.getConfig().readSettingAsForm( PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM );
                         final List<FormConfiguration> formConfigurations = pwmDomain.getConfig().readSettingAsForm( PwmSetting.FORGOTTEN_PASSWORD_SEARCH_FORM );
                         final Map<FormConfiguration, String> formMap = FormUtility.asFormConfigurationMap( formConfigurations, forgottenPasswordBean.getUserSearchValues() );
                         final Map<FormConfiguration, String> formMap = FormUtility.asFormConfigurationMap( formConfigurations, forgottenPasswordBean.getUserSearchValues() );
-                        pwmDomain.getIntruderManager().convenience().markAttributes( formMap, forgottenPasswordStateMachine.getRequestContext().getSessionLabel() );
+                        pwmDomain.getIntruderService().client().markAttributes( formMap, forgottenPasswordStateMachine.getRequestContext().getSessionLabel() );
                     }
                     }
 
 
                     final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_INCORRECT_RESPONSE,
                     final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_INCORRECT_RESPONSE,
@@ -766,7 +767,7 @@ public class ForgottenPasswordStateMachine
                 try
                 try
                 {
                 {
                     // check attributes
                     // check attributes
-                    final ChaiUser theUser = pwmDomain.getProxiedChaiUser( userIdentity );
+                    final ChaiUser theUser = pwmDomain.getProxiedChaiUser( sessionLabel, userIdentity );
 
 
                     final List<FormConfiguration> requiredAttributesForm = forgottenPasswordBean.getAttributeForm();
                     final List<FormConfiguration> requiredAttributesForm = forgottenPasswordBean.getAttributeForm();
 
 
@@ -969,7 +970,7 @@ public class ForgottenPasswordStateMachine
                 formValues = FormUtility.readFormValuesFromMap( values, forgottenPasswordForm, pwmRequestContext.getLocale() );
                 formValues = FormUtility.readFormValuesFromMap( values, forgottenPasswordForm, pwmRequestContext.getLocale() );
 
 
                 // check for intruder search values
                 // check for intruder search values
-                pwmRequestContext.getPwmDomain().getIntruderManager().convenience().checkAttributes( formValues );
+                pwmRequestContext.getPwmDomain().getIntruderService().client().checkAttributes( formValues );
 
 
                 // see if the values meet the configured form requirements.
                 // see if the values meet the configured form requirements.
                 FormUtility.validateFormValues( pwmRequestContext.getDomainConfig(), formValues, pwmRequestContext.getLocale() );
                 FormUtility.validateFormValues( pwmRequestContext.getDomainConfig(), formValues, pwmRequestContext.getLocale() );
@@ -1007,12 +1008,12 @@ public class ForgottenPasswordStateMachine
                     throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_CANT_MATCH_USER ) );
                     throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_CANT_MATCH_USER ) );
                 }
                 }
 
 
-                AuthenticationUtility.checkIfUserEligibleToAuthentication( pwmRequestContext.getPwmDomain(), userIdentity );
+                AuthenticationUtility.checkIfUserEligibleToAuthentication( pwmRequestContext.getSessionLabel(), pwmRequestContext.getPwmDomain(), userIdentity );
 
 
                 ForgottenPasswordUtil.initForgottenPasswordBean( pwmRequestContext, userIdentity, forgottenPasswordStateMachine.getForgottenPasswordBean() );
                 ForgottenPasswordUtil.initForgottenPasswordBean( pwmRequestContext, userIdentity, forgottenPasswordStateMachine.getForgottenPasswordBean() );
 
 
                 // clear intruder search values
                 // clear intruder search values
-                pwmRequestContext.getPwmDomain().getIntruderManager().convenience().clearAttributes( formValues );
+                pwmRequestContext.getPwmDomain().getIntruderService().client().clearAttributes( formValues );
 
 
                 return;
                 return;
             }
             }
@@ -1024,9 +1025,10 @@ public class ForgottenPasswordStateMachine
                             PwmError.ERROR_RESPONSES_NORESPONSES,
                             PwmError.ERROR_RESPONSES_NORESPONSES,
                             e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues()
                             e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues()
                     );
                     );
-                    pwmRequestContext.getPwmDomain().getStatisticsManager().incrementValue( Statistic.RECOVERY_FAILURES );
 
 
-                    pwmRequestContext.getPwmDomain().getIntruderManager().convenience().markAttributes( formValues, pwmRequestContext.getSessionLabel() );
+                    StatisticsClient.incrementStat( pwmRequestContext.getPwmApplication(), Statistic.RECOVERY_FAILURES );
+
+                    pwmRequestContext.getPwmDomain().getIntruderService().client().markAttributes( formValues, pwmRequestContext.getSessionLabel() );
 
 
                     LOGGER.debug( pwmRequestContext.getSessionLabel(), errorInfo );
                     LOGGER.debug( pwmRequestContext.getSessionLabel(), errorInfo );
                     forgottenPasswordStateMachine.clear();
                     forgottenPasswordStateMachine.clear();
@@ -1075,7 +1077,7 @@ public class ForgottenPasswordStateMachine
             final LdapProfile selectedProfile = pwmRequestContext.getDomainConfig().getLdapProfiles().getOrDefault(
             final LdapProfile selectedProfile = pwmRequestContext.getDomainConfig().getLdapProfiles().getOrDefault(
                     profile,
                     profile,
                     pwmRequestContext.getDomainConfig().getDefaultLdapProfile() );
                     pwmRequestContext.getDomainConfig().getDefaultLdapProfile() );
-            final Map<String, String> selectableContexts = selectedProfile.getSelectableContexts( pwmRequestContext.getPwmDomain() );
+            final Map<String, String> selectableContexts = selectedProfile.getSelectableContexts( pwmRequestContext.getSessionLabel(), pwmRequestContext.getPwmDomain() );
             if ( selectableContexts != null && selectableContexts.size() > 1 )
             if ( selectableContexts != null && selectableContexts.size() > 1 )
             {
             {
                 final Map<String, String> labelLocaleMap = LocaleHelper.localeMapToStringMap(
                 final Map<String, String> labelLocaleMap = LocaleHelper.localeMapToStringMap(
@@ -1117,10 +1119,10 @@ public class ForgottenPasswordStateMachine
             SessionAuthenticator.simulateBadPassword( pwmRequestContext, userIdentity );
             SessionAuthenticator.simulateBadPassword( pwmRequestContext, userIdentity );
 
 
 
 
-            pwmRequestContext.getPwmDomain().getIntruderManager().convenience().markUserIdentity( userIdentity,
+            pwmRequestContext.getPwmDomain().getIntruderService().client().markUserIdentity( userIdentity,
                     pwmRequestContext.getSessionLabel() );
                     pwmRequestContext.getSessionLabel() );
         }
         }
 
 
-        StatisticsManager.incrementStat( pwmRequestContext.getPwmDomain(), Statistic.RECOVERY_FAILURES );
+        StatisticsClient.incrementStat( pwmRequestContext.getPwmDomain(), Statistic.RECOVERY_FAILURES );
     }
     }
 }
 }

+ 10 - 7
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordUtil.java

@@ -63,7 +63,9 @@ import password.pwm.ldap.UserInfoFactory;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.svc.token.TokenType;
 import password.pwm.svc.token.TokenType;
 import password.pwm.svc.token.TokenUtil;
 import password.pwm.svc.token.TokenUtil;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
@@ -170,7 +172,7 @@ public class ForgottenPasswordUtil
 
 
         try
         try
         {
         {
-            final ChaiUser theUser = pwmDomain.getProxiedChaiUser( userIdentity );
+            final ChaiUser theUser = pwmDomain.getProxiedChaiUser( pwmRequestContext.getSessionLabel(), userIdentity );
             responseSet = pwmDomain.getCrService().readUserResponseSet(
             responseSet = pwmDomain.getCrService().readUserResponseSet(
                     pwmRequestContext.getSessionLabel(),
                     pwmRequestContext.getSessionLabel(),
                     userIdentity,
                     userIdentity,
@@ -414,7 +416,7 @@ public class ForgottenPasswordUtil
                         .build()
                         .build()
         );
         );
 
 
-        pwmRequestContext.getPwmDomain().getStatisticsManager().incrementValue( Statistic.RECOVERY_TOKENS_SENT );
+        StatisticsClient.incrementStat( pwmRequestContext.getPwmApplication(), Statistic.RECOVERY_TOKENS_SENT );
     }
     }
 
 
 
 
@@ -434,7 +436,7 @@ public class ForgottenPasswordUtil
         }
         }
 
 
         final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
         final UserIdentity userIdentity = forgottenPasswordBean.getUserIdentity();
-        final ChaiUser theUser = pwmRequest.getPwmDomain().getProxiedChaiUser( userIdentity );
+        final ChaiUser theUser = pwmRequest.getPwmDomain().getProxiedChaiUser( pwmRequest.getLabel(), userIdentity );
 
 
         try
         try
         {
         {
@@ -493,12 +495,13 @@ public class ForgottenPasswordUtil
 
 
             // mark the event log
             // mark the event log
             {
             {
-                final AuditRecord auditRecord = new AuditRecordFactory( pwmDomain ).createUserAuditRecord(
+                final AuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createUserAuditRecord(
                         AuditEvent.RECOVER_PASSWORD,
                         AuditEvent.RECOVER_PASSWORD,
                         userIdentity,
                         userIdentity,
                         pwmRequest.getLabel()
                         pwmRequest.getLabel()
                 );
                 );
-                pwmDomain.getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+
+                AuditServiceClient.submit( pwmRequest, auditRecord );
             }
             }
 
 
             final MessageSendMethod messageSendMethod = forgottenPasswordProfile.readSettingAsEnum( PwmSetting.RECOVERY_SENDNEWPW_METHOD, MessageSendMethod.class );
             final MessageSendMethod messageSendMethod = forgottenPasswordProfile.readSettingAsEnum( PwmSetting.RECOVERY_SENDNEWPW_METHOD, MessageSendMethod.class );
@@ -698,7 +701,7 @@ public class ForgottenPasswordUtil
             final Optional<ResponseSet> responseSet;
             final Optional<ResponseSet> responseSet;
             try
             try
             {
             {
-                final ChaiUser theUser = pwmDomain.getProxiedChaiUser( userInfo.getUserIdentity() );
+                final ChaiUser theUser = pwmDomain.getProxiedChaiUser( pwmRequestContext.getSessionLabel(), userInfo.getUserIdentity() );
                 responseSet = pwmDomain.getCrService().readUserResponseSet(
                 responseSet = pwmDomain.getCrService().readUserResponseSet(
                         sessionLabel,
                         sessionLabel,
                         userInfo.getUserIdentity(),
                         userInfo.getUserIdentity(),
@@ -727,7 +730,7 @@ public class ForgottenPasswordUtil
         {
         {
             try
             try
             {
             {
-                final ChaiUser chaiUser = pwmDomain.getProxiedChaiUser( userInfo.getUserIdentity() );
+                final ChaiUser chaiUser = pwmDomain.getProxiedChaiUser( pwmRequestContext.getSessionLabel(), userInfo.getUserIdentity() );
                 if ( chaiUser.isPasswordLocked() )
                 if ( chaiUser.isPasswordLocked() )
                 {
                 {
                     throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_INTRUDER_LDAP ) );
                     throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_INTRUDER_LDAP ) );

+ 61 - 48
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java

@@ -28,8 +28,8 @@ import com.novell.ldapchai.exception.ChaiPasswordPolicyException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.provider.ChaiProvider;
 import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
-import password.pwm.PwmDomain;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.TokenDestinationItem;
 import password.pwm.bean.TokenDestinationItem;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
@@ -67,12 +67,17 @@ import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.ldap.search.UserSearchResults;
 import password.pwm.ldap.search.UserSearchResults;
+import password.pwm.svc.cr.CrService;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.event.HelpdeskAuditRecord;
 import password.pwm.svc.event.HelpdeskAuditRecord;
-import password.pwm.svc.intruder.IntruderService;
+import password.pwm.svc.intruder.IntruderDomainService;
+import password.pwm.svc.otp.OTPUserRecord;
+import password.pwm.svc.otp.OtpService;
+import password.pwm.svc.secure.DomainSecureService;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.token.TokenUtil;
 import password.pwm.svc.token.TokenUtil;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
@@ -84,12 +89,8 @@ import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.macro.MacroRequest;
 import password.pwm.util.operations.ActionExecutor;
 import password.pwm.util.operations.ActionExecutor;
-import password.pwm.util.operations.CrService;
-import password.pwm.util.operations.OtpService;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.util.password.PasswordUtility;
-import password.pwm.util.operations.otp.OTPUserRecord;
 import password.pwm.util.password.RandomPasswordGenerator;
 import password.pwm.util.password.RandomPasswordGenerator;
-import password.pwm.svc.secure.DomainSecureService;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.rest.RestCheckPasswordServer;
 import password.pwm.ws.server.rest.RestCheckPasswordServer;
 import password.pwm.ws.server.rest.RestRandomPasswordServer;
 import password.pwm.ws.server.rest.RestRandomPasswordServer;
@@ -249,7 +250,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             pwmRequest.respondWithError( errorInformation, false );
             pwmRequest.respondWithError( errorInformation, false );
             return ProcessStatus.Halt;
             return ProcessStatus.Halt;
         }
         }
-        final UserIdentity targetUserIdentity = UserIdentity.fromKey( userKey, pwmRequest.getPwmApplication() );
+        final UserIdentity targetUserIdentity = UserIdentity.fromKey( pwmRequest.getLabel(), userKey, pwmRequest.getPwmApplication() );
         LOGGER.debug( pwmRequest, () -> "received executeAction request for user " + targetUserIdentity.toString() );
         LOGGER.debug( pwmRequest, () -> "received executeAction request for user " + targetUserIdentity.toString() );
 
 
         final List<ActionConfiguration> actionConfigurations = helpdeskProfile.readSettingAsAction( PwmSetting.HELPDESK_ACTIONS );
         final List<ActionConfiguration> actionConfigurations = helpdeskProfile.readSettingAsAction( PwmSetting.HELPDESK_ACTIONS );
@@ -291,7 +292,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
 
 
             // mark the event log
             // mark the event log
             {
             {
-                final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+                final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                         AuditEvent.HELPDESK_ACTION,
                         AuditEvent.HELPDESK_ACTION,
                         pwmSession.getUserInfo().getUserIdentity(),
                         pwmSession.getUserInfo().getUserIdentity(),
                         action.getName(),
                         action.getName(),
@@ -299,7 +300,8 @@ public class HelpdeskServlet extends ControlledPwmServlet
                         pwmSession.getSessionStateBean().getSrcAddress(),
                         pwmSession.getSessionStateBean().getSrcAddress(),
                         pwmSession.getSessionStateBean().getSrcHostname()
                         pwmSession.getSessionStateBean().getSrcHostname()
                 );
                 );
-                pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+
+                AuditServiceClient.submit( pwmRequest, auditRecord );
             }
             }
             final RestResultBean restResultBean = RestResultBean.forSuccessMessage(
             final RestResultBean restResultBean = RestResultBean.forSuccessMessage(
                     pwmRequest.getLocale(),
                     pwmRequest.getLocale(),
@@ -339,7 +341,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             return ProcessStatus.Halt;
             return ProcessStatus.Halt;
         }
         }
 
 
-        final UserIdentity userIdentity = UserIdentity.fromKey( userKey, pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey( pwmRequest.getLabel(), userKey, pwmRequest.getPwmApplication() );
         LOGGER.info( pwmRequest, () -> "received deleteUser request by " + pwmSession.getUserInfo().getUserIdentity().toString() + " for user " + userIdentity.toString() );
         LOGGER.info( pwmRequest, () -> "received deleteUser request by " + pwmSession.getUserInfo().getUserIdentity().toString() + " for user " + userIdentity.toString() );
 
 
         // check if user should be seen by actor
         // check if user should be seen by actor
@@ -363,7 +365,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
 
 
         // execute user delete operation
         // execute user delete operation
         final ChaiProvider provider = helpdeskProfile.readSettingAsBoolean( PwmSetting.HELPDESK_USE_PROXY )
         final ChaiProvider provider = helpdeskProfile.readSettingAsBoolean( PwmSetting.HELPDESK_USE_PROXY )
-                ? pwmDomain.getProxyChaiProvider( userIdentity.getLdapProfileID() )
+                ? pwmDomain.getProxyChaiProvider( pwmRequest.getLabel(), userIdentity.getLdapProfileID() )
                 : pwmSession.getSessionManager().getChaiProvider();
                 : pwmSession.getSessionManager().getChaiProvider();
 
 
 
 
@@ -389,7 +391,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
                     userIdentity.getUserDN(),
                     userIdentity.getUserDN(),
                     userIdentity.getLdapProfileID()
                     userIdentity.getLdapProfileID()
             );
             );
-            final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+            final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                     AuditEvent.HELPDESK_DELETE_USER,
                     AuditEvent.HELPDESK_DELETE_USER,
                     pwmSession.getUserInfo().getUserIdentity(),
                     pwmSession.getUserInfo().getUserIdentity(),
                     null,
                     null,
@@ -397,7 +399,8 @@ public class HelpdeskServlet extends ControlledPwmServlet
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcHostname()
                     pwmSession.getSessionStateBean().getSrcHostname()
             );
             );
-            pwmDomain.getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+
+            AuditServiceClient.submit( pwmRequest, auditRecord );
         }
         }
 
 
         LOGGER.info( pwmRequest, () -> "user " + userIdentity + " has been deleted" );
         LOGGER.info( pwmRequest, () -> "user " + userIdentity + " has been deleted" );
@@ -595,7 +598,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             pwmRequest.respondWithError( errorInformation, false );
             pwmRequest.respondWithError( errorInformation, false );
             return ProcessStatus.Halt;
             return ProcessStatus.Halt;
         }
         }
-        final UserIdentity userIdentity = UserIdentity.fromKey( userKey, pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey( pwmRequest.getLabel(), userKey, pwmRequest.getPwmApplication() );
 
 
         if ( !helpdeskProfile.readSettingAsBoolean( PwmSetting.HELPDESK_ENABLE_UNLOCK ) )
         if ( !helpdeskProfile.readSettingAsBoolean( PwmSetting.HELPDESK_ENABLE_UNLOCK ) )
         {
         {
@@ -607,8 +610,8 @@ public class HelpdeskServlet extends ControlledPwmServlet
 
 
         //clear pwm intruder setting.
         //clear pwm intruder setting.
         {
         {
-            final IntruderService intruderManager = pwmRequest.getPwmDomain().getIntruderManager();
-            intruderManager.convenience().clearUserIdentity( userIdentity );
+            final IntruderDomainService intruderManager = pwmRequest.getPwmDomain().getIntruderService();
+            intruderManager.client().clearUserIdentity( userIdentity );
         }
         }
 
 
 
 
@@ -622,7 +625,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             chaiUser.unlockPassword();
             chaiUser.unlockPassword();
             {
             {
                 // mark the event log
                 // mark the event log
-                final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+                final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                         AuditEvent.HELPDESK_UNLOCK_PASSWORD,
                         AuditEvent.HELPDESK_UNLOCK_PASSWORD,
                         pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
                         pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
                         null,
                         null,
@@ -630,7 +633,8 @@ public class HelpdeskServlet extends ControlledPwmServlet
                         pwmRequest.getLabel().getSourceAddress(),
                         pwmRequest.getLabel().getSourceAddress(),
                         pwmRequest.getLabel().getSourceHostname()
                         pwmRequest.getLabel().getSourceHostname()
                 );
                 );
-                pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+
+                AuditServiceClient.submit( pwmRequest, auditRecord );
             }
             }
         }
         }
         catch ( final ChaiPasswordPolicyException e )
         catch ( final ChaiPasswordPolicyException e )
@@ -675,7 +679,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             pwmRequest.respondWithError( errorInformation, false );
             pwmRequest.respondWithError( errorInformation, false );
             return ProcessStatus.Halt;
             return ProcessStatus.Halt;
         }
         }
-        final UserIdentity userIdentity = UserIdentity.fromKey( userKey, pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey( pwmRequest.getLabel(), userKey, pwmRequest.getPwmApplication() );
 
 
         if ( !helpdeskProfile.readOptionalVerificationMethods().contains( IdentityVerificationMethod.OTP ) )
         if ( !helpdeskProfile.readOptionalVerificationMethods().contains( IdentityVerificationMethod.OTP ) )
         {
         {
@@ -705,7 +709,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             if ( passed )
             if ( passed )
             {
             {
                 final PwmSession pwmSession = pwmRequest.getPwmSession();
                 final PwmSession pwmSession = pwmRequest.getPwmSession();
-                final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+                final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                         AuditEvent.HELPDESK_VERIFY_OTP,
                         AuditEvent.HELPDESK_VERIFY_OTP,
                         pwmSession.getUserInfo().getUserIdentity(),
                         pwmSession.getUserInfo().getUserIdentity(),
                         null,
                         null,
@@ -713,15 +717,15 @@ public class HelpdeskServlet extends ControlledPwmServlet
                         pwmSession.getSessionStateBean().getSrcAddress(),
                         pwmSession.getSessionStateBean().getSrcAddress(),
                         pwmSession.getSessionStateBean().getSrcHostname()
                         pwmSession.getSessionStateBean().getSrcHostname()
                 );
                 );
-                pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+                AuditServiceClient.submit( pwmRequest, auditRecord );
 
 
-                StatisticsManager.incrementStat( pwmRequest, Statistic.HELPDESK_VERIFY_OTP );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.HELPDESK_VERIFY_OTP );
                 verificationStateBean.addRecord( userIdentity, IdentityVerificationMethod.OTP );
                 verificationStateBean.addRecord( userIdentity, IdentityVerificationMethod.OTP );
             }
             }
             else
             else
             {
             {
                 final PwmSession pwmSession = pwmRequest.getPwmSession();
                 final PwmSession pwmSession = pwmRequest.getPwmSession();
-                final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+                final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                         AuditEvent.HELPDESK_VERIFY_OTP_INCORRECT,
                         AuditEvent.HELPDESK_VERIFY_OTP_INCORRECT,
                         pwmSession.getUserInfo().getUserIdentity(),
                         pwmSession.getUserInfo().getUserIdentity(),
                         null,
                         null,
@@ -729,7 +733,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
                         pwmSession.getSessionStateBean().getSrcAddress(),
                         pwmSession.getSessionStateBean().getSrcAddress(),
                         pwmSession.getSessionStateBean().getSrcHostname()
                         pwmSession.getSessionStateBean().getSrcHostname()
                 );
                 );
-                pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+                AuditServiceClient.submit( pwmRequest, auditRecord );
             }
             }
 
 
             return outputVerificationResponseBean( pwmRequest, passed, verificationStateBean );
             return outputVerificationResponseBean( pwmRequest, passed, verificationStateBean );
@@ -754,7 +758,10 @@ public class HelpdeskServlet extends ControlledPwmServlet
         final DomainConfig config = pwmRequest.getDomainConfig();
         final DomainConfig config = pwmRequest.getDomainConfig();
         final Map<String, String> bodyParams = pwmRequest.readBodyAsJsonStringMap();
         final Map<String, String> bodyParams = pwmRequest.readBodyAsJsonStringMap();
 
 
-        final UserIdentity targetUserIdentity = UserIdentity.fromKey( bodyParams.get( PwmConstants.PARAM_USERKEY ), pwmRequest.getPwmApplication() );
+        final UserIdentity targetUserIdentity = UserIdentity.fromKey(
+                pwmRequest.getLabel(),
+                bodyParams.get( PwmConstants.PARAM_USERKEY ),
+                pwmRequest.getPwmApplication() );
         final UserInfo targetUserInfo = HelpdeskServletUtil.getTargetUserInfo( pwmRequest, helpdeskProfile, targetUserIdentity );
         final UserInfo targetUserInfo = HelpdeskServletUtil.getTargetUserInfo( pwmRequest, helpdeskProfile, targetUserIdentity );
 
 
         final String requestedTokenID = bodyParams.get( "id" );
         final String requestedTokenID = bodyParams.get( "id" );
@@ -819,7 +826,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             return ProcessStatus.Halt;
             return ProcessStatus.Halt;
         }
         }
 
 
-        StatisticsManager.incrementStat( pwmRequest, Statistic.HELPDESK_TOKENS_SENT );
+        StatisticsClient.incrementStat( pwmRequest, Statistic.HELPDESK_TOKENS_SENT );
         final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = new HelpdeskVerificationRequestBean();
         final HelpdeskVerificationRequestBean helpdeskVerificationRequestBean = new HelpdeskVerificationRequestBean();
         helpdeskVerificationRequestBean.setDestination( tokenDestinationItem.getDisplay() );
         helpdeskVerificationRequestBean.setDestination( tokenDestinationItem.getDisplay() );
         helpdeskVerificationRequestBean.setUserKey( bodyParams.get( PwmConstants.PARAM_USERKEY ) );
         helpdeskVerificationRequestBean.setUserKey( bodyParams.get( PwmConstants.PARAM_USERKEY ) );
@@ -862,7 +869,10 @@ public class HelpdeskServlet extends ControlledPwmServlet
                 HelpdeskVerificationRequestBean.TokenData.class
                 HelpdeskVerificationRequestBean.TokenData.class
         );
         );
 
 
-        final UserIdentity userIdentity = UserIdentity.fromKey( helpdeskVerificationRequestBean.getUserKey(), pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey(
+                pwmRequest.getLabel(),
+                helpdeskVerificationRequestBean.getUserKey(),
+                pwmRequest.getPwmApplication() );
 
 
         if ( tokenData == null || tokenData.getIssueDate() == null || tokenData.getToken() == null || tokenData.getToken().isEmpty() )
         if ( tokenData == null || tokenData.getIssueDate() == null || tokenData.getToken() == null || tokenData.getToken().isEmpty() )
         {
         {
@@ -891,7 +901,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
         if ( passed )
         if ( passed )
         {
         {
             final PwmSession pwmSession = pwmRequest.getPwmSession();
             final PwmSession pwmSession = pwmRequest.getPwmSession();
-            final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+            final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                     AuditEvent.HELPDESK_VERIFY_TOKEN,
                     AuditEvent.HELPDESK_VERIFY_TOKEN,
                     pwmSession.getUserInfo().getUserIdentity(),
                     pwmSession.getUserInfo().getUserIdentity(),
                     null,
                     null,
@@ -899,13 +909,13 @@ public class HelpdeskServlet extends ControlledPwmServlet
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcHostname()
                     pwmSession.getSessionStateBean().getSrcHostname()
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+            AuditServiceClient.submit( pwmRequest, auditRecord );
             verificationStateBean.addRecord( userIdentity, IdentityVerificationMethod.TOKEN );
             verificationStateBean.addRecord( userIdentity, IdentityVerificationMethod.TOKEN );
         }
         }
         else
         else
         {
         {
             final PwmSession pwmSession = pwmRequest.getPwmSession();
             final PwmSession pwmSession = pwmRequest.getPwmSession();
-            final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+            final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                     AuditEvent.HELPDESK_VERIFY_TOKEN_INCORRECT,
                     AuditEvent.HELPDESK_VERIFY_TOKEN_INCORRECT,
                     pwmSession.getUserInfo().getUserIdentity(),
                     pwmSession.getUserInfo().getUserIdentity(),
                     null,
                     null,
@@ -913,7 +923,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcHostname()
                     pwmSession.getSessionStateBean().getSrcHostname()
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+            AuditServiceClient.submit( pwmRequest, auditRecord );
         }
         }
 
 
         return outputVerificationResponseBean( pwmRequest, passed, verificationStateBean );
         return outputVerificationResponseBean( pwmRequest, passed, verificationStateBean );
@@ -941,7 +951,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
         }
         }
 
 
         //clear pwm intruder setting.
         //clear pwm intruder setting.
-        pwmRequest.getPwmDomain().getIntruderManager().convenience().clearUserIdentity( userIdentity );
+        pwmRequest.getPwmDomain().getIntruderService().client().clearUserIdentity( userIdentity );
 
 
         try
         try
         {
         {
@@ -951,7 +961,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             service.clearOTPUserConfiguration( pwmRequest, userIdentity, chaiUser );
             service.clearOTPUserConfiguration( pwmRequest, userIdentity, chaiUser );
             {
             {
                 // mark the event log
                 // mark the event log
-                final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+                final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                         AuditEvent.HELPDESK_CLEAR_OTP_SECRET,
                         AuditEvent.HELPDESK_CLEAR_OTP_SECRET,
                         pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
                         pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
                         null,
                         null,
@@ -959,7 +969,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
                         pwmRequest.getLabel().getSourceAddress(),
                         pwmRequest.getLabel().getSourceAddress(),
                         pwmRequest.getLabel().getSourceHostname()
                         pwmRequest.getLabel().getSourceHostname()
                 );
                 );
-                pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+                AuditServiceClient.submit( pwmRequest, auditRecord );
             }
             }
         }
         }
         catch ( final PwmOperationalException e )
         catch ( final PwmOperationalException e )
@@ -1036,7 +1046,10 @@ public class HelpdeskServlet extends ControlledPwmServlet
                 HelpdeskVerificationRequestBean.class
                 HelpdeskVerificationRequestBean.class
         );
         );
 
 
-        final UserIdentity userIdentity = UserIdentity.fromKey( helpdeskVerificationRequestBean.getUserKey(), pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey(
+                pwmRequest.getLabel(),
+                helpdeskVerificationRequestBean.getUserKey(),
+                pwmRequest.getPwmApplication() );
 
 
         boolean passed = false;
         boolean passed = false;
         {
         {
@@ -1084,7 +1097,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
         if ( passed )
         if ( passed )
         {
         {
             final PwmSession pwmSession = pwmRequest.getPwmSession();
             final PwmSession pwmSession = pwmRequest.getPwmSession();
-            final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+            final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                     AuditEvent.HELPDESK_VERIFY_ATTRIBUTES,
                     AuditEvent.HELPDESK_VERIFY_ATTRIBUTES,
                     pwmSession.getUserInfo().getUserIdentity(),
                     pwmSession.getUserInfo().getUserIdentity(),
                     null,
                     null,
@@ -1092,13 +1105,13 @@ public class HelpdeskServlet extends ControlledPwmServlet
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcHostname()
                     pwmSession.getSessionStateBean().getSrcHostname()
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+            AuditServiceClient.submit( pwmRequest, auditRecord );
             verificationStateBean.addRecord( userIdentity, IdentityVerificationMethod.ATTRIBUTES );
             verificationStateBean.addRecord( userIdentity, IdentityVerificationMethod.ATTRIBUTES );
         }
         }
         else
         else
         {
         {
             final PwmSession pwmSession = pwmRequest.getPwmSession();
             final PwmSession pwmSession = pwmRequest.getPwmSession();
-            final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+            final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                     AuditEvent.HELPDESK_VERIFY_ATTRIBUTES_INCORRECT,
                     AuditEvent.HELPDESK_VERIFY_ATTRIBUTES_INCORRECT,
                     pwmSession.getUserInfo().getUserIdentity(),
                     pwmSession.getUserInfo().getUserIdentity(),
                     null,
                     null,
@@ -1106,7 +1119,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcAddress(),
                     pwmSession.getSessionStateBean().getSrcHostname()
                     pwmSession.getSessionStateBean().getSrcHostname()
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+            AuditServiceClient.submit( pwmRequest, auditRecord );
         }
         }
 
 
         return outputVerificationResponseBean( pwmRequest, passed, verificationStateBean );
         return outputVerificationResponseBean( pwmRequest, passed, verificationStateBean );
@@ -1180,7 +1193,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
 
 
         // mark the event log
         // mark the event log
         {
         {
-            final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest.getPwmDomain(), pwmRequest ).createHelpdeskAuditRecord(
+            final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                     AuditEvent.HELPDESK_CLEAR_RESPONSES,
                     AuditEvent.HELPDESK_CLEAR_RESPONSES,
                     pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
                     pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
                     null,
                     null,
@@ -1188,7 +1201,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
                     pwmRequest.getPwmSession().getSessionStateBean().getSrcAddress(),
                     pwmRequest.getPwmSession().getSessionStateBean().getSrcAddress(),
                     pwmRequest.getPwmSession().getSessionStateBean().getSrcHostname()
                     pwmRequest.getPwmSession().getSessionStateBean().getSrcHostname()
             );
             );
-            pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+            AuditServiceClient.submit( pwmRequest, auditRecord );
         }
         }
 
 
         final RestResultBean restResultBean = RestResultBean.forSuccessMessage( pwmRequest, Message.Success_Unknown );
         final RestResultBean restResultBean = RestResultBean.forSuccessMessage( pwmRequest, Message.Success_Unknown );
@@ -1204,7 +1217,8 @@ public class HelpdeskServlet extends ControlledPwmServlet
                 RestCheckPasswordServer.JsonInput.class
                 RestCheckPasswordServer.JsonInput.class
         );
         );
 
 
-        final UserIdentity userIdentity = UserIdentity.fromKey( jsonInput.getUsername(), pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey(
+                pwmRequest.getLabel(), jsonInput.getUsername(), pwmRequest.getPwmApplication() );
         final HelpdeskProfile helpdeskProfile = getHelpdeskProfile( pwmRequest );
         final HelpdeskProfile helpdeskProfile = getHelpdeskProfile( pwmRequest );
 
 
         HelpdeskServletUtil.checkIfUserIdentityViewable( pwmRequest, helpdeskProfile, userIdentity );
         HelpdeskServletUtil.checkIfUserIdentityViewable( pwmRequest, helpdeskProfile, userIdentity );
@@ -1229,8 +1243,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
         }
         }
 
 
         final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
         final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
-                pwmRequest.getPwmDomain(),
-                pwmRequest.getLocale(),
+                pwmRequest.getPwmRequestContext(),
                 chaiUser,
                 chaiUser,
                 userInfo,
                 userInfo,
                 null,
                 null,
@@ -1256,7 +1269,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
                 RestSetPasswordServer.JsonInputData.class
                 RestSetPasswordServer.JsonInputData.class
         );
         );
 
 
-        final UserIdentity userIdentity = UserIdentity.fromKey( jsonInput.getUsername(), pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey( pwmRequest.getLabel(), jsonInput.getUsername(), pwmRequest.getPwmApplication() );
         final ChaiUser chaiUser = HelpdeskServletUtil.getChaiUser( pwmRequest, helpdeskProfile, userIdentity );
         final ChaiUser chaiUser = HelpdeskServletUtil.getChaiUser( pwmRequest, helpdeskProfile, userIdentity );
         final UserInfo userInfo = UserInfoFactory.newUserInfo(
         final UserInfo userInfo = UserInfoFactory.newUserInfo(
                 pwmRequest.getPwmApplication(),
                 pwmRequest.getPwmApplication(),
@@ -1335,7 +1348,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
     private ProcessStatus processRandomPasswordAction( final PwmRequest pwmRequest ) throws IOException, PwmUnrecoverableException, ChaiUnavailableException
     private ProcessStatus processRandomPasswordAction( final PwmRequest pwmRequest ) throws IOException, PwmUnrecoverableException, ChaiUnavailableException
     {
     {
         final RestRandomPasswordServer.JsonInput input = JsonUtil.deserialize( pwmRequest.readRequestBodyAsString(), RestRandomPasswordServer.JsonInput.class );
         final RestRandomPasswordServer.JsonInput input = JsonUtil.deserialize( pwmRequest.readRequestBodyAsString(), RestRandomPasswordServer.JsonInput.class );
-        final UserIdentity userIdentity = UserIdentity.fromKey( input.getUsername(), pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey( pwmRequest.getLabel(), input.getUsername(), pwmRequest.getPwmApplication() );
 
 
         final HelpdeskProfile helpdeskProfile = getHelpdeskProfile( pwmRequest );
         final HelpdeskProfile helpdeskProfile = getHelpdeskProfile( pwmRequest );
 
 
@@ -1391,7 +1404,7 @@ public class HelpdeskServlet extends ControlledPwmServlet
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing" );
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing" );
             throw new PwmUnrecoverableException( errorInformation );
             throw new PwmUnrecoverableException( errorInformation );
         }
         }
-        return UserIdentity.fromKey( userKey, pwmRequest.getPwmApplication() );
+        return UserIdentity.fromKey( pwmRequest.getLabel(), userKey, pwmRequest.getPwmApplication() );
     }
     }
 
 
     static PhotoDataReader photoDataReader( final PwmRequest pwmRequest, final HelpdeskProfile helpdeskProfile, final UserIdentity userIdentity )
     static PhotoDataReader photoDataReader( final PwmRequest pwmRequest, final HelpdeskProfile helpdeskProfile, final UserIdentity userIdentity )

+ 10 - 9
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServletUtil.java

@@ -22,8 +22,8 @@ package password.pwm.http.servlet.helpdesk;
 
 
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
-import password.pwm.PwmDomain;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.PwmDomain;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
@@ -37,15 +37,16 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
-import password.pwm.ldap.permission.UserPermissionUtility;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.UserInfoFactory;
 import password.pwm.ldap.permission.UserPermissionType;
 import password.pwm.ldap.permission.UserPermissionType;
+import password.pwm.ldap.permission.UserPermissionUtility;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
 import password.pwm.svc.event.AuditRecordFactory;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.event.HelpdeskAuditRecord;
 import password.pwm.svc.event.HelpdeskAuditRecord;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -210,9 +211,9 @@ public class HelpdeskServletUtil
     )
     )
             throws ChaiUnavailableException, PwmUnrecoverableException
             throws ChaiUnavailableException, PwmUnrecoverableException
     {
     {
-        final UserIdentity actorUserIdentity = pwmRequest.getUserInfoIfLoggedIn().canonicalized( pwmRequest.getPwmApplication() );
+        final UserIdentity actorUserIdentity = pwmRequest.getUserInfoIfLoggedIn().canonicalized( pwmRequest.getLabel(), pwmRequest.getPwmApplication() );
 
 
-        if ( actorUserIdentity.canonicalEquals( userIdentity, pwmRequest.getPwmApplication() ) )
+        if ( actorUserIdentity.canonicalEquals( pwmRequest.getLabel(), userIdentity, pwmRequest.getPwmApplication() ) )
         {
         {
             final String errorMsg = "cannot select self";
             final String errorMsg = "cannot select self";
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_UNAUTHORIZED, errorMsg );
             final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_UNAUTHORIZED, errorMsg );
@@ -233,7 +234,7 @@ public class HelpdeskServletUtil
         }
         }
 
 
         final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo( pwmRequest, helpdeskProfile, userIdentity );
         final HelpdeskDetailInfoBean helpdeskDetailInfoBean = HelpdeskDetailInfoBean.makeHelpdeskDetailInfo( pwmRequest, helpdeskProfile, userIdentity );
-        final HelpdeskAuditRecord auditRecord = new AuditRecordFactory( pwmRequest ).createHelpdeskAuditRecord(
+        final HelpdeskAuditRecord auditRecord = AuditRecordFactory.make( pwmRequest ).createHelpdeskAuditRecord(
                 AuditEvent.HELPDESK_VIEW_DETAIL,
                 AuditEvent.HELPDESK_VIEW_DETAIL,
                 pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
                 pwmRequest.getPwmSession().getUserInfo().getUserIdentity(),
                 null,
                 null,
@@ -241,9 +242,9 @@ public class HelpdeskServletUtil
                 pwmRequest.getLabel().getSourceAddress(),
                 pwmRequest.getLabel().getSourceAddress(),
                 pwmRequest.getLabel().getSourceHostname()
                 pwmRequest.getLabel().getSourceHostname()
         );
         );
-        pwmRequest.getPwmDomain().getAuditManager().submit( pwmRequest.getLabel(), auditRecord );
+        AuditServiceClient.submit( pwmRequest, auditRecord );
 
 
-        StatisticsManager.incrementStat( pwmRequest, Statistic.HELPDESK_USER_LOOKUP );
+        StatisticsClient.incrementStat( pwmRequest, Statistic.HELPDESK_USER_LOOKUP );
         return helpdeskDetailInfoBean;
         return helpdeskDetailInfoBean;
     }
     }
 
 
@@ -329,7 +330,7 @@ public class HelpdeskServletUtil
     {
     {
         final boolean useProxy = helpdeskProfile.readSettingAsBoolean( PwmSetting.HELPDESK_USE_PROXY );
         final boolean useProxy = helpdeskProfile.readSettingAsBoolean( PwmSetting.HELPDESK_USE_PROXY );
         return useProxy
         return useProxy
-                ? pwmRequest.getPwmDomain().getProxiedChaiUser( userIdentity )
+                ? pwmRequest.getPwmDomain().getProxiedChaiUser( pwmRequest.getLabel(), userIdentity )
                 : pwmRequest.getPwmSession().getSessionManager().getActor( userIdentity );
                 : pwmRequest.getPwmSession().getSessionManager().getActor( userIdentity );
     }
     }
 
 

+ 8 - 3
server/src/main/java/password/pwm/http/servlet/newuser/NewUserFormUtils.java

@@ -30,12 +30,12 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.bean.NewUserBean;
 import password.pwm.http.bean.NewUserBean;
+import password.pwm.svc.secure.DomainSecureService;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.svc.secure.DomainSecureService;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.HashMap;
@@ -43,6 +43,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
+import java.util.NoSuchElementException;
 
 
 class NewUserFormUtils
 class NewUserFormUtils
 {
 {
@@ -62,8 +63,12 @@ class NewUserFormUtils
         final List<FormConfiguration> newUserForm = NewUserServlet.getFormDefinition( pwmRequest );
         final List<FormConfiguration> newUserForm = NewUserServlet.getFormDefinition( pwmRequest );
         final Map<FormConfiguration, String> userFormValues = FormUtility.readFormValuesFromRequest( pwmRequest,
         final Map<FormConfiguration, String> userFormValues = FormUtility.readFormValuesFromRequest( pwmRequest,
                 newUserForm, userLocale );
                 newUserForm, userLocale );
-        final PasswordData passwordData1 = pwmRequest.readParameterAsPassword( NewUserServlet.FIELD_PASSWORD1 );
-        final PasswordData passwordData2 = pwmRequest.readParameterAsPassword( NewUserServlet.FIELD_PASSWORD2 );
+        final PasswordData passwordData1 = pwmRequest.readParameterAsPassword( NewUserServlet.FIELD_PASSWORD1 )
+                .orElseThrow( () -> new NoSuchElementException( "missing " +  NewUserServlet.FIELD_PASSWORD1 + " field" ) );
+
+        final PasswordData passwordData2 = pwmRequest.readParameterAsPassword( NewUserServlet.FIELD_PASSWORD2 )
+                .orElseThrow( () -> new NoSuchElementException( "missing " +  NewUserServlet.FIELD_PASSWORD2 + " field" ) );
+
 
 
         final NewUserProfile newUserProfile = NewUserServlet.getNewUserProfile( pwmRequest );
         final NewUserProfile newUserProfile = NewUserServlet.getNewUserProfile( pwmRequest );
         return injectRemoteValuesIntoForm( userFormValues, newUserBean.getRemoteInputData(), newUserProfile, passwordData1, passwordData2 );
         return injectRemoteValuesIntoForm( userFormValues, newUserBean.getRemoteInputData(), newUserProfile, passwordData1, passwordData2 );

+ 6 - 6
server/src/main/java/password/pwm/http/servlet/newuser/NewUserServlet.java

@@ -238,7 +238,7 @@ public class NewUserServlet extends ControlledPwmServlet
         // try to read the new user policy to make sure it's readable, that way an exception is thrown here instead of by the jsp
         // try to read the new user policy to make sure it's readable, that way an exception is thrown here instead of by the jsp
         {
         {
             final Instant startTime = Instant.now();
             final Instant startTime = Instant.now();
-            newUserProfile.getNewUserPasswordPolicy( pwmDomain, pwmSession.getSessionStateBean().getLocale() );
+            newUserProfile.getNewUserPasswordPolicy( pwmRequest.getPwmRequestContext() );
             LOGGER.trace( () -> "read new user password policy in ", () -> TimeDuration.fromCurrent( startTime ) );
             LOGGER.trace( () -> "read new user password policy in ", () -> TimeDuration.fromCurrent( startTime ) );
         }
         }
 
 
@@ -340,7 +340,7 @@ public class NewUserServlet extends ControlledPwmServlet
     }
     }
 
 
     private boolean readProfileFromUrl( final PwmRequest pwmRequest, final NewUserBean newUserBean )
     private boolean readProfileFromUrl( final PwmRequest pwmRequest, final NewUserBean newUserBean )
-            throws PwmUnrecoverableException, ServletException, IOException
+            throws PwmUnrecoverableException, IOException
     {
     {
         final String profileUrlSegment = "profile";
         final String profileUrlSegment = "profile";
         final String urlRemainder = servletUriRemainder( pwmRequest, profileUrlSegment );
         final String urlRemainder = servletUriRemainder( pwmRequest, profileUrlSegment );
@@ -434,6 +434,7 @@ public class NewUserServlet extends ControlledPwmServlet
             validationFlags.add( FormUtility.ValidationFlag.allowResultCaching );
             validationFlags.add( FormUtility.ValidationFlag.allowResultCaching );
         }
         }
         FormUtility.validateFormValueUniqueness(
         FormUtility.validateFormValueUniqueness(
+                pwmRequest.getLabel(),
                 pwmDomain,
                 pwmDomain,
                 formValueData,
                 formValueData,
                 locale,
                 locale,
@@ -445,7 +446,7 @@ public class NewUserServlet extends ControlledPwmServlet
 
 
         final UserInfo uiBean = UserInfoBean.builder()
         final UserInfo uiBean = UserInfoBean.builder()
                 .cachedPasswordRuleAttributes( FormUtility.asStringMap( formValueData ) )
                 .cachedPasswordRuleAttributes( FormUtility.asStringMap( formValueData ) )
-                .passwordPolicy( newUserProfile.getNewUserPasswordPolicy( pwmDomain, locale ) )
+                .passwordPolicy( newUserProfile.getNewUserPasswordPolicy( pwmRequest.getPwmRequestContext() ) )
                 .build();
                 .build();
 
 
         final boolean promptForPassword = newUserProfile.readSettingAsBoolean( PwmSetting.NEWUSER_PROMPT_FOR_PASSWORD );
         final boolean promptForPassword = newUserProfile.readSettingAsBoolean( PwmSetting.NEWUSER_PROMPT_FOR_PASSWORD );
@@ -455,8 +456,7 @@ public class NewUserServlet extends ControlledPwmServlet
         if ( promptForPassword )
         if ( promptForPassword )
         {
         {
             passwordCheckInfo =  PasswordUtility.checkEnteredPassword(
             passwordCheckInfo =  PasswordUtility.checkEnteredPassword(
-                    pwmDomain,
-                    locale,
+                    pwmRequest.getPwmRequestContext(),
                     null,
                     null,
                     uiBean,
                     uiBean,
                     null,
                     null,
@@ -698,7 +698,7 @@ public class NewUserServlet extends ControlledPwmServlet
         {
         {
             final TimeDuration elapsedTime = TimeDuration.fromCurrent( startTime );
             final TimeDuration elapsedTime = TimeDuration.fromCurrent( startTime );
             complete = false;
             complete = false;
-            percentComplete = new Percent( elapsedTime.asMillis(), minWaitTime ).asBigDecimal();
+            percentComplete = Percent.of( elapsedTime.asMillis(), minWaitTime ).asBigDecimal();
         }
         }
 
 
         final LinkedHashMap<String, Object> outputMap = new LinkedHashMap<>();
         final LinkedHashMap<String, Object> outputMap = new LinkedHashMap<>();

+ 9 - 8
server/src/main/java/password/pwm/http/servlet/newuser/NewUserUtils.java

@@ -62,7 +62,9 @@ import password.pwm.ldap.auth.SessionAuthenticator;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditEvent;
+import password.pwm.svc.event.AuditServiceClient;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.svc.token.TokenType;
 import password.pwm.svc.token.TokenType;
 import password.pwm.svc.token.TokenUtil;
 import password.pwm.svc.token.TokenUtil;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
@@ -131,7 +133,6 @@ class NewUserUtils
             throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
             throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
     {
     {
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
         final PwmDomain pwmDomain = pwmRequest.getPwmDomain();
-        final PwmSession pwmSession = pwmRequest.getPwmSession();
 
 
         final long startTime = System.currentTimeMillis();
         final long startTime = System.currentTimeMillis();
 
 
@@ -157,7 +158,7 @@ class NewUserUtils
         }
         }
         else
         else
         {
         {
-            final PwmPasswordPolicy pwmPasswordPolicy = newUserProfile.getNewUserPasswordPolicy( pwmRequest.getPwmDomain(), pwmRequest.getLocale() );
+            final PwmPasswordPolicy pwmPasswordPolicy = newUserProfile.getNewUserPasswordPolicy( pwmRequest.getPwmRequestContext() );
             userPassword = RandomPasswordGenerator.createRandomPassword( pwmRequest.getLabel(), pwmPasswordPolicy, pwmRequest.getPwmDomain() );
             userPassword = RandomPasswordGenerator.createRandomPassword( pwmRequest.getLabel(), pwmPasswordPolicy, pwmRequest.getPwmDomain() );
         }
         }
 
 
@@ -174,7 +175,7 @@ class NewUserUtils
             createObjectClasses.addAll( defaultLDAPProfile.readSettingAsStringArray( PwmSetting.AUTO_ADD_OBJECT_CLASSES ) );
             createObjectClasses.addAll( defaultLDAPProfile.readSettingAsStringArray( PwmSetting.AUTO_ADD_OBJECT_CLASSES ) );
         }
         }
 
 
-        final ChaiProvider chaiProvider = newUserProfile.getLdapProfile( pwmDomain.getConfig() ).getProxyChaiProvider( pwmDomain );
+        final ChaiProvider chaiProvider = newUserProfile.getLdapProfile( pwmDomain.getConfig() ).getProxyChaiProvider( pwmRequest.getLabel(), pwmDomain );
         try
         try
         {
         {
             // create the ldap entry
             // create the ldap entry
@@ -211,7 +212,7 @@ class NewUserUtils
             final PasswordData temporaryPassword;
             final PasswordData temporaryPassword;
             {
             {
                 final RandomPasswordGenerator.RandomGeneratorConfig randomGeneratorConfig = RandomPasswordGenerator.RandomGeneratorConfig.builder()
                 final RandomPasswordGenerator.RandomGeneratorConfig randomGeneratorConfig = RandomPasswordGenerator.RandomGeneratorConfig.builder()
-                        .passwordPolicy( newUserProfile.getNewUserPasswordPolicy( pwmDomain, pwmRequest.getLocale() ) )
+                        .passwordPolicy( newUserProfile.getNewUserPasswordPolicy( pwmRequest.getPwmRequestContext() ) )
                         .build();
                         .build();
                 temporaryPassword = RandomPasswordGenerator.createRandomPassword( pwmRequest.getLabel(), randomGeneratorConfig, pwmDomain );
                 temporaryPassword = RandomPasswordGenerator.createRandomPassword( pwmRequest.getLabel(), randomGeneratorConfig, pwmDomain );
             }
             }
@@ -324,7 +325,7 @@ class NewUserUtils
 
 
                 final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings( pwmDomain, userIdentity )
                 final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings( pwmDomain, userIdentity )
                         .setExpandPwmMacros( true )
                         .setExpandPwmMacros( true )
-                        .setMacroMachine( pwmSession.getSessionManager().getMacroMachine( ) )
+                        .setMacroMachine( pwmRequest.getPwmSession().getSessionManager().getMacroMachine( ) )
                         .createActionExecutor();
                         .createActionExecutor();
 
 
                 actionExecutor.executeActions( actions, pwmRequest.getLabel() );
                 actionExecutor.executeActions( actions, pwmRequest.getLabel() );
@@ -336,10 +337,10 @@ class NewUserUtils
 
 
 
 
         // add audit record
         // add audit record
-        pwmDomain.getAuditManager().submit( AuditEvent.CREATE_USER, pwmSession.getUserInfo(), pwmSession );
+        AuditServiceClient.submitUserEvent( pwmRequest, AuditEvent.CREATE_USER, pwmRequest.getPwmSession().getUserInfo() );
 
 
         // increment the new user creation statistics
         // increment the new user creation statistics
-        pwmDomain.getStatisticsManager().incrementValue( Statistic.NEW_USERS );
+        StatisticsClient.incrementStat( pwmRequest, Statistic.NEW_USERS );
 
 
         NewUserUtils.LOGGER.debug( pwmRequest, () -> "completed createUser process for " + newUserDN + " (" + TimeDuration.fromCurrent(
         NewUserUtils.LOGGER.debug( pwmRequest, () -> "completed createUser process for " + newUserDN + " (" + TimeDuration.fromCurrent(
                 startTime ).asCompactString() + ")" );
                 startTime ).asCompactString() + ")" );
@@ -355,7 +356,7 @@ class NewUserUtils
         {
         {
             final NewUserProfile newUserProfile = NewUserServlet.getNewUserProfile( pwmRequest );
             final NewUserProfile newUserProfile = NewUserServlet.getNewUserProfile( pwmRequest );
             NewUserUtils.LOGGER.warn( pwmRequest, () -> "deleting ldap user account " + userDN );
             NewUserUtils.LOGGER.warn( pwmRequest, () -> "deleting ldap user account " + userDN );
-            newUserProfile.getLdapProfile( pwmRequest.getPwmDomain().getConfig() ).getProxyChaiProvider( pwmRequest.getPwmDomain() ).deleteEntry( userDN );
+            newUserProfile.getLdapProfile( pwmRequest.getPwmDomain().getConfig() ).getProxyChaiProvider( pwmRequest.getLabel(), pwmRequest.getPwmDomain() ).deleteEntry( userDN );
             NewUserUtils.LOGGER.warn( pwmRequest, () -> "ldap user account " + userDN + " has been deleted" );
             NewUserUtils.LOGGER.warn( pwmRequest, () -> "ldap user account " + userDN + " has been deleted" );
         }
         }
         catch ( final ChaiUnavailableException | ChaiOperationException e )
         catch ( final ChaiUnavailableException | ChaiOperationException e )

+ 4 - 1
server/src/main/java/password/pwm/http/servlet/oauth/OAuthConsumerServlet.java

@@ -255,7 +255,10 @@ public class OAuthConsumerServlet extends AbstractPwmServlet
                         null,
                         null,
                         pwmRequest.getLabel()
                         pwmRequest.getLabel()
                 );
                 );
-                if ( resolvedIdentity != null && resolvedIdentity.canonicalEquals( pwmSession.getUserInfo().getUserIdentity(), pwmDomain.getPwmApplication() ) )
+                if ( resolvedIdentity != null && resolvedIdentity.canonicalEquals(
+                        pwmRequest.getLabel(),
+                        pwmSession.getUserInfo().getUserIdentity(),
+                        pwmDomain.getPwmApplication() ) )
                 {
                 {
                     LOGGER.debug( pwmRequest, () -> "verified incoming oauth code for already authenticated session does resolve to same as logged in user" );
                     LOGGER.debug( pwmRequest, () -> "verified incoming oauth code for already authenticated session does resolve to same as logged in user" );
                 }
                 }

+ 11 - 11
server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchDataReader.java

@@ -61,7 +61,7 @@ import password.pwm.svc.cache.CacheKey;
 import password.pwm.svc.cache.CacheLoader;
 import password.pwm.svc.cache.CacheLoader;
 import password.pwm.svc.cache.CachePolicy;
 import password.pwm.svc.cache.CachePolicy;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
@@ -123,12 +123,12 @@ class PeopleSearchDataReader
             if ( cachedResult != null )
             if ( cachedResult != null )
             {
             {
                 final SearchResultBean copyWithCacheSet = cachedResult.toBuilder().fromCache( true ).build();
                 final SearchResultBean copyWithCacheSet = cachedResult.toBuilder().fromCache( true ).build();
-                StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
                 return copyWithCacheSet;
                 return copyWithCacheSet;
             }
             }
             else
             else
             {
             {
-                StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES );
             }
             }
         }
         }
 
 
@@ -136,7 +136,7 @@ class PeopleSearchDataReader
         final SearchResultBean searchResultBean = makeSearchResultsImpl( searchRequestBean )
         final SearchResultBean searchResultBean = makeSearchResultsImpl( searchRequestBean )
                 .toBuilder().fromCache( false ).build();
                 .toBuilder().fromCache( false ).build();
 
 
-        StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_SEARCHES );
+        StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_SEARCHES );
         storeDataInCache( cacheKey, searchResultBean );
         storeDataInCache( cacheKey, searchResultBean );
         LOGGER.trace( pwmRequest, () -> "returning " + searchResultBean.getSearchResults().size()
         LOGGER.trace( pwmRequest, () -> "returning " + searchResultBean.getSearchResults().size()
                 + " results for search request "
                 + " results for search request "
@@ -163,13 +163,13 @@ class PeopleSearchDataReader
             final OrgChartDataBean cachedOutput = pwmRequest.getPwmDomain().getCacheService().get( cacheKey, OrgChartDataBean.class );
             final OrgChartDataBean cachedOutput = pwmRequest.getPwmDomain().getCacheService().get( cacheKey, OrgChartDataBean.class );
             if ( cachedOutput != null )
             if ( cachedOutput != null )
             {
             {
-                StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
                 LOGGER.trace( pwmRequest, () -> "completed makeOrgChartData of " + userIdentity.toDisplayString() + " from cache" );
                 LOGGER.trace( pwmRequest, () -> "completed makeOrgChartData of " + userIdentity.toDisplayString() + " from cache" );
                 return cachedOutput;
                 return cachedOutput;
             }
             }
             else
             else
             {
             {
-                StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES );
             }
             }
         }
         }
 
 
@@ -250,12 +250,12 @@ class PeopleSearchDataReader
             final UserDetailBean cachedOutput = pwmRequest.getPwmDomain().getCacheService().get( cacheKey, UserDetailBean.class );
             final UserDetailBean cachedOutput = pwmRequest.getPwmDomain().getCacheService().get( cacheKey, UserDetailBean.class );
             if ( cachedOutput != null )
             if ( cachedOutput != null )
             {
             {
-                StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
                 return cachedOutput;
                 return cachedOutput;
             }
             }
             else
             else
             {
             {
-                StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES );
+                StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES );
             }
             }
         }
         }
 
 
@@ -590,7 +590,7 @@ class PeopleSearchDataReader
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         final Locale locale = pwmRequest.getLocale();
         final Locale locale = pwmRequest.getLocale();
-        final ChaiProvider chaiProvider = pwmRequest.getPwmDomain().getProxiedChaiUser( userIdentity ).getChaiProvider();
+        final ChaiProvider chaiProvider = pwmRequest.getPwmDomain().getProxiedChaiUser( pwmRequest.getLabel(), userIdentity ).getChaiProvider();
         final UserInfo userInfo = UserInfoFactory.newUserInfo(
         final UserInfo userInfo = UserInfoFactory.newUserInfo(
                 pwmRequest.getPwmApplication(),
                 pwmRequest.getPwmApplication(),
                 pwmRequest.getLabel(),
                 pwmRequest.getLabel(),
@@ -699,7 +699,7 @@ class PeopleSearchDataReader
     {
     {
         final boolean useProxy = useProxy();
         final boolean useProxy = useProxy();
         return useProxy
         return useProxy
-                ? pwmRequest.getPwmDomain().getProxiedChaiUser( userIdentity )
+                ? pwmRequest.getPwmDomain().getProxiedChaiUser( pwmRequest.getLabel(), userIdentity )
                 : pwmRequest.getPwmSession().getSessionManager().getActor( userIdentity );
                 : pwmRequest.getPwmSession().getSessionManager().getActor( userIdentity );
     }
     }
 
 
@@ -842,7 +842,7 @@ class PeopleSearchDataReader
                 final String userKey = ( String ) map.get( "userKey" );
                 final String userKey = ( String ) map.get( "userKey" );
                 if ( userKey != null )
                 if ( userKey != null )
                 {
                 {
-                    final UserIdentity userIdentity = UserIdentity.fromKey( userKey, pwmRequest.getPwmApplication() );
+                    final UserIdentity userIdentity = UserIdentity.fromKey( pwmRequest.getLabel(), userKey, pwmRequest.getPwmApplication() );
                     final String displayValue = figureDisplaynameValue( pwmRequest, userIdentity );
                     final String displayValue = figureDisplaynameValue( pwmRequest, userIdentity );
                     map.put( "_displayName", displayValue );
                     map.put( "_displayName", displayValue );
                 }
                 }

+ 7 - 14
server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchService.java

@@ -21,10 +21,10 @@
 package password.pwm.http.servlet.peoplesearch;
 package password.pwm.http.servlet.peoplesearch;
 
 
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplication;
-import password.pwm.PwmDomain;
 import password.pwm.bean.DomainID;
 import password.pwm.bean.DomainID;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmException;
 import password.pwm.health.HealthRecord;
 import password.pwm.health.HealthRecord;
+import password.pwm.svc.AbstractPwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.util.PwmScheduler;
 import password.pwm.util.PwmScheduler;
 
 
@@ -35,26 +35,17 @@ import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 
 
-public class PeopleSearchService implements PwmService
+public class PeopleSearchService extends AbstractPwmService implements PwmService
 {
 {
-    private PwmDomain pwmDomain;
     private ThreadPoolExecutor threadPoolExecutor;
     private ThreadPoolExecutor threadPoolExecutor;
 
 
     @Override
     @Override
-    public STATUS status()
-    {
-        return STATUS.OPEN;
-    }
-
-    @Override
-    public void init( final PwmApplication pwmApplication, final DomainID domainID )
+    public STATUS postAbstractInit( final PwmApplication pwmApplication, final DomainID domainID )
             throws PwmException
             throws PwmException
     {
     {
-        this.pwmDomain = pwmApplication.domains().get( domainID );
-
         final int maxThreadCount = 5;
         final int maxThreadCount = 5;
 
 
-        final ThreadFactory threadFactory = PwmScheduler.makePwmThreadFactory( PwmScheduler.makeThreadName( pwmDomain.getPwmApplication(), PeopleSearchService.class ), true );
+        final ThreadFactory threadFactory = PwmScheduler.makePwmThreadFactory( PwmScheduler.makeThreadName( pwmApplication, PeopleSearchService.class ), true );
         threadPoolExecutor = new ThreadPoolExecutor(
         threadPoolExecutor = new ThreadPoolExecutor(
                 maxThreadCount,
                 maxThreadCount,
                 maxThreadCount,
                 maxThreadCount,
@@ -63,6 +54,8 @@ public class PeopleSearchService implements PwmService
                 new ArrayBlockingQueue<>( 5000 ),
                 new ArrayBlockingQueue<>( 5000 ),
                 threadFactory
                 threadFactory
         );
         );
+
+        return STATUS.OPEN;
     }
     }
 
 
     @Override
     @Override
@@ -72,7 +65,7 @@ public class PeopleSearchService implements PwmService
     }
     }
 
 
     @Override
     @Override
-    public List<HealthRecord> healthCheck()
+    public List<HealthRecord> serviceHealthCheck()
     {
     {
         return Collections.emptyList();
         return Collections.emptyList();
     }
     }

+ 4 - 4
server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchServlet.java

@@ -43,7 +43,7 @@ import password.pwm.http.servlet.peoplesearch.bean.SearchResultBean;
 import password.pwm.http.servlet.peoplesearch.bean.UserDetailBean;
 import password.pwm.http.servlet.peoplesearch.bean.UserDetailBean;
 import password.pwm.ldap.PhotoDataBean;
 import password.pwm.ldap.PhotoDataBean;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
@@ -194,7 +194,7 @@ public abstract class PeopleSearchServlet extends ControlledPwmServlet
 
 
             addExpiresHeadersToResponse( pwmRequest );
             addExpiresHeadersToResponse( pwmRequest );
             pwmRequest.outputJsonResult( RestResultBean.withData( orgChartData ) );
             pwmRequest.outputJsonResult( RestResultBean.withData( orgChartData ) );
-            StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_ORGCHART );
+            StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_ORGCHART );
         }
         }
         catch ( final PwmException e )
         catch ( final PwmException e )
         {
         {
@@ -220,7 +220,7 @@ public abstract class PeopleSearchServlet extends ControlledPwmServlet
 
 
         addExpiresHeadersToResponse( pwmRequest );
         addExpiresHeadersToResponse( pwmRequest );
         pwmRequest.outputJsonResult( RestResultBean.withData( detailData ) );
         pwmRequest.outputJsonResult( RestResultBean.withData( detailData ) );
-        pwmRequest.getPwmDomain().getStatisticsManager().incrementValue( Statistic.PEOPLESEARCH_DETAILS );
+        StatisticsClient.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_DETAILS );
 
 
         return ProcessStatus.Halt;
         return ProcessStatus.Halt;
     }
     }
@@ -347,7 +347,7 @@ public abstract class PeopleSearchServlet extends ControlledPwmServlet
 
 
         final PeopleSearchProfile peopleSearchProfile = peopleSearchProfile( pwmRequest );
         final PeopleSearchProfile peopleSearchProfile = peopleSearchProfile( pwmRequest );
         final PeopleSearchDataReader peopleSearchDataReader = new PeopleSearchDataReader( pwmRequest, peopleSearchProfile );
         final PeopleSearchDataReader peopleSearchDataReader = new PeopleSearchDataReader( pwmRequest, peopleSearchProfile );
-        final UserIdentity userIdentity = UserIdentity.fromKey( userKey, pwmRequest.getPwmApplication() );
+        final UserIdentity userIdentity = UserIdentity.fromKey( pwmRequest.getLabel(), userKey, pwmRequest.getPwmApplication() );
         peopleSearchDataReader.checkIfUserIdentityViewable( userIdentity );
         peopleSearchDataReader.checkIfUserIdentityViewable( userIdentity );
         return userIdentity;
         return userIdentity;
     }
     }

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/peoplesearch/PhotoDataReader.java

@@ -217,7 +217,7 @@ public class PhotoDataReader
     {
     {
         return LdapOperationsHelper.readPhotoDataFromLdap(
         return LdapOperationsHelper.readPhotoDataFromLdap(
                 pwmRequest.getDomainConfig(),
                 pwmRequest.getDomainConfig(),
-                pwmRequest.getPwmDomain().getProxiedChaiUser( userIdentity ).getChaiProvider(),
+                pwmRequest.getPwmDomain().getProxiedChaiUser( pwmRequest.getLabel(), userIdentity ).getChaiProvider(),
                 userIdentity
                 userIdentity
         );
         );
     }
     }

+ 2 - 2
server/src/main/java/password/pwm/http/servlet/resource/ResourceFileServlet.java

@@ -32,7 +32,7 @@ import password.pwm.http.PwmRequest;
 import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.http.servlet.PwmServlet;
 import password.pwm.http.servlet.PwmServlet;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.svc.stats.StatisticsClient;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.MovingAverage;
 import password.pwm.util.java.MovingAverage;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
@@ -214,7 +214,7 @@ public class ResourceFileServlet extends HttpServlet implements PwmServlet
             pwmRequest.debugHttpRequestToLog( debugText, () -> TimeDuration.fromCurrent( pwmRequest.getRequestStartTime() ) );
             pwmRequest.debugHttpRequestToLog( debugText, () -> TimeDuration.fromCurrent( pwmRequest.getRequestStartTime() ) );
 
 
             final MovingAverage cacheHitRatio = resourceService.getCacheHitRatio();
             final MovingAverage cacheHitRatio = resourceService.getCacheHitRatio();
-            StatisticsManager.incrementStat( pwmDomain, Statistic.HTTP_RESOURCE_REQUESTS );
+            StatisticsClient.incrementStat( pwmDomain, Statistic.HTTP_RESOURCE_REQUESTS );
             cacheHitRatio.update( fromCache ? 1 : 0 );
             cacheHitRatio.update( fromCache ? 1 : 0 );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )

+ 22 - 16
server/src/main/java/password/pwm/http/servlet/resource/ResourceServletConfiguration.java

@@ -26,6 +26,7 @@ import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.IOUtils;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmDomain;
 import password.pwm.PwmDomain;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.DomainConfig;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.value.FileValue;
 import password.pwm.config.value.FileValue;
@@ -81,9 +82,9 @@ class ResourceServletConfiguration
         nonceValue = null;
         nonceValue = null;
     }
     }
 
 
-    private ResourceServletConfiguration( final PwmDomain pwmDomain )
+    private ResourceServletConfiguration( final SessionLabel sessionLabel, final PwmDomain pwmDomain )
     {
     {
-        LOGGER.trace( () -> "initializing" );
+        LOGGER.trace( sessionLabel, () -> "initializing" );
         final DomainConfig domainConfig = pwmDomain.getConfig();
         final DomainConfig domainConfig = pwmDomain.getConfig();
         maxCacheItems = Integer.parseInt( domainConfig.readAppProperty( AppProperty.HTTP_RESOURCES_MAX_CACHE_ITEMS ) );
         maxCacheItems = Integer.parseInt( domainConfig.readAppProperty( AppProperty.HTTP_RESOURCES_MAX_CACHE_ITEMS ) );
         cacheExpireSeconds = Long.parseLong( domainConfig.readAppProperty( AppProperty.HTTP_RESOURCES_EXPIRATION_SECONDS ) );
         cacheExpireSeconds = Long.parseLong( domainConfig.readAppProperty( AppProperty.HTTP_RESOURCES_EXPIRATION_SECONDS ) );
@@ -95,12 +96,14 @@ class ResourceServletConfiguration
         noncePattern = Pattern.compile( noncePrefix + "[^/]*?/" );
         noncePattern = Pattern.compile( noncePrefix + "[^/]*?/" );
         nonceValue = pwmDomain.getPwmApplication().getRuntimeNonce();
         nonceValue = pwmDomain.getPwmApplication().getRuntimeNonce();
 
 
-        zipResources = makeZipResourcesFromConfig( pwmDomain, domainConfig );
+        zipResources = makeZipResourcesFromConfig( sessionLabel, pwmDomain, domainConfig );
 
 
-        customFileBundle = makeCustomFileBundle( domainConfig );
+        customFileBundle = makeCustomFileBundle( sessionLabel, domainConfig );
     }
     }
 
 
-    private Map<String, FileResource> makeCustomFileBundle( final DomainConfig domainConfig )
+    private Map<String, FileResource> makeCustomFileBundle(
+            final SessionLabel sessionLabel,
+            final DomainConfig domainConfig )
     {
     {
         final Map<String, FileResource> customFileBundle = new HashMap<>();
         final Map<String, FileResource> customFileBundle = new HashMap<>();
         final Map<FileValue.FileInformation, FileValue.FileContent> files = domainConfig.readSettingAsFile( PwmSetting.DISPLAY_CUSTOM_RESOURCE_BUNDLE );
         final Map<FileValue.FileInformation, FileValue.FileContent> files = domainConfig.readSettingAsFile( PwmSetting.DISPLAY_CUSTOM_RESOURCE_BUNDLE );
@@ -109,7 +112,7 @@ class ResourceServletConfiguration
             final Map.Entry<FileValue.FileInformation, FileValue.FileContent> entry = files.entrySet().iterator().next();
             final Map.Entry<FileValue.FileInformation, FileValue.FileContent> entry = files.entrySet().iterator().next();
             final FileValue.FileInformation fileInformation = entry.getKey();
             final FileValue.FileInformation fileInformation = entry.getKey();
             final FileValue.FileContent fileContent = entry.getValue();
             final FileValue.FileContent fileContent = entry.getValue();
-            LOGGER.debug( () -> "examining configured zip file resource for items name=" + fileInformation.getFilename() + ", size=" + fileContent.size() );
+            LOGGER.debug( sessionLabel, () -> "examining configured zip file resource for items name=" + fileInformation.getFilename() + ", size=" + fileContent.size() );
 
 
             try
             try
             {
             {
@@ -117,18 +120,21 @@ class ResourceServletConfiguration
                 final String path = "/tmp/" + domainConfig.getDomainID().stringValue();
                 final String path = "/tmp/" + domainConfig.getDomainID().stringValue();
                 FileUtils.writeByteArrayToFile( new File( path ), bytes );
                 FileUtils.writeByteArrayToFile( new File( path ), bytes );
 
 
-                final Map<String, FileResource> customFiles = makeMemoryFileMapFromZipInput( fileContent.getContents() );
+                final Map<String, FileResource> customFiles = makeMemoryFileMapFromZipInput( sessionLabel, fileContent.getContents() );
                 customFileBundle.putAll( customFiles );
                 customFileBundle.putAll( customFiles );
             }
             }
             catch ( final IOException e )
             catch ( final IOException e )
             {
             {
-                LOGGER.error( () -> "error assembling memory file map zip bundle: " + e.getMessage() );
+                LOGGER.error( sessionLabel, () -> "error assembling memory file map zip bundle: " + e.getMessage() );
             }
             }
         }
         }
         return Collections.unmodifiableMap( customFileBundle );
         return Collections.unmodifiableMap( customFileBundle );
     }
     }
 
 
-    private Map<String, ZipFile> makeZipResourcesFromConfig( final PwmDomain pwmDomain, final DomainConfig domainConfig )
+    private Map<String, ZipFile> makeZipResourcesFromConfig(
+            final SessionLabel sessionLabel,
+            final PwmDomain pwmDomain,
+            final DomainConfig domainConfig )
     {
     {
         final Map<String, ZipFile> zipResources = new HashMap<>();
         final Map<String, ZipFile> zipResources = new HashMap<>();
         final String zipFileResourceParam = domainConfig.getAppConfig().readAppProperty( AppProperty.HTTP_RESOURCES_ZIP_FILES );
         final String zipFileResourceParam = domainConfig.getAppConfig().readAppProperty( AppProperty.HTTP_RESOURCES_ZIP_FILES );
@@ -151,25 +157,25 @@ class ResourceServletConfiguration
                         );
                         );
                         final ZipFile zipFile = new ZipFile( zipFileFile );
                         final ZipFile zipFile = new ZipFile( zipFileFile );
                         zipResources.put( ResourceFileServlet.RESOURCE_PATH + configuredZipFileResource.getUrl(), zipFile );
                         zipResources.put( ResourceFileServlet.RESOURCE_PATH + configuredZipFileResource.getUrl(), zipFile );
-                        LOGGER.debug( () -> "registered resource-zip file " + configuredZipFileResource.getZipFile() + " at path " + zipFileFile.getAbsolutePath() );
+                        LOGGER.debug( sessionLabel, () -> "registered resource-zip file " + configuredZipFileResource.getZipFile() + " at path " + zipFileFile.getAbsolutePath() );
                     }
                     }
                     catch ( final IOException e )
                     catch ( final IOException e )
                     {
                     {
-                        LOGGER.warn( () -> "unable to resource-zip file " + configuredZipFileResource + ", error: " + e.getMessage() );
+                        LOGGER.warn( sessionLabel, () -> "unable to resource-zip file " + configuredZipFileResource + ", error: " + e.getMessage() );
                     }
                     }
                 }
                 }
                 else
                 else
                 {
                 {
-                    LOGGER.error( () -> "can't register resource-zip file " + configuredZipFileResource.getZipFile() + " because WEB-INF path is unknown" );
+                    LOGGER.error( sessionLabel, () -> "can't register resource-zip file " + configuredZipFileResource.getZipFile() + " because WEB-INF path is unknown" );
                 }
                 }
             }
             }
         }
         }
         return Collections.unmodifiableMap( zipResources );
         return Collections.unmodifiableMap( zipResources );
     }
     }
 
 
-    static ResourceServletConfiguration fromConfig( final PwmDomain pwmDomain )
+    static ResourceServletConfiguration fromConfig( final SessionLabel sessionLabel, final PwmDomain pwmDomain )
     {
     {
-        return new ResourceServletConfiguration( pwmDomain );
+        return new ResourceServletConfiguration( sessionLabel, pwmDomain );
     }
     }
 
 
     static ResourceServletConfiguration defaultConfiguration( )
     static ResourceServletConfiguration defaultConfiguration( )
@@ -177,7 +183,7 @@ class ResourceServletConfiguration
         return new ResourceServletConfiguration();
         return new ResourceServletConfiguration();
     }
     }
 
 
-    private static Map<String, FileResource> makeMemoryFileMapFromZipInput( final ImmutableByteArray content )
+    private static Map<String, FileResource> makeMemoryFileMapFromZipInput( final SessionLabel sessionLabel, final ImmutableByteArray content )
             throws IOException
             throws IOException
     {
     {
         final ZipInputStream stream = new ZipInputStream( content.newByteArrayInputStream() );
         final ZipInputStream stream = new ZipInputStream( content.newByteArrayInputStream() );
@@ -196,7 +202,7 @@ class ResourceServletConfiguration
                 memoryMap.put( name, new MemoryFileResource( name, contents, lastModified ) );
                 memoryMap.put( name, new MemoryFileResource( name, contents, lastModified ) );
                 {
                 {
                     final String finalEntry = entry.getName();
                     final String finalEntry = entry.getName();
-                    LOGGER.trace( () -> "discovered file in configured resource bundle: " + finalEntry );
+                    LOGGER.trace( sessionLabel, () -> "discovered file in configured resource bundle: " + finalEntry );
                 }
                 }
             }
             }
         }
         }

Некоторые файлы не были показаны из-за большого количества измененных файлов