فهرست منبع

rest refactoring; removal of jersey dependency

Jason Rivard 7 سال پیش
والد
کامیت
9a4c725fa2
100فایلهای تغییر یافته به همراه2068 افزوده شده و 2943 حذف شده
  1. 21 5
      client/pom.xml
  2. 26 4
      pom.xml
  3. 25 21
      server/pom.xml
  4. 1 1
      server/src/main/java/password/pwm/AppProperty.java
  5. 0 7
      server/src/main/java/password/pwm/PwmAboutProperty.java
  6. 0 5
      server/src/main/java/password/pwm/PwmApplication.java
  7. 0 31
      server/src/main/java/password/pwm/PwmConstants.java
  8. 3 161
      server/src/main/java/password/pwm/bean/LocalSessionStateBean.java
  9. 3 96
      server/src/main/java/password/pwm/bean/pub/SessionStateInfoBean.java
  10. 2 4
      server/src/main/java/password/pwm/config/PwmSetting.java
  11. 9 0
      server/src/main/java/password/pwm/config/option/WebServiceUsage.java
  12. 57 0
      server/src/main/java/password/pwm/http/HttpContentType.java
  13. 3 0
      server/src/main/java/password/pwm/http/PwmHttpRequestWrapper.java
  14. 1 1
      server/src/main/java/password/pwm/http/PwmHttpResponseWrapper.java
  15. 2 7
      server/src/main/java/password/pwm/http/PwmRequest.java
  16. 2 3
      server/src/main/java/password/pwm/http/PwmResponse.java
  17. 0 21
      server/src/main/java/password/pwm/http/PwmSession.java
  18. 2 11
      server/src/main/java/password/pwm/http/PwmURL.java
  19. 5 6
      server/src/main/java/password/pwm/http/filter/ApplicationModeFilter.java
  20. 4 6
      server/src/main/java/password/pwm/http/filter/AuthenticationFilter.java
  21. 1 1
      server/src/main/java/password/pwm/http/filter/AuthorizationFilter.java
  22. 1 1
      server/src/main/java/password/pwm/http/filter/GZIPFilter.java
  23. 1 1
      server/src/main/java/password/pwm/http/filter/ObsoleteUrlFilter.java
  24. 11 13
      server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java
  25. 2 2
      server/src/main/java/password/pwm/http/filter/SessionFilter.java
  26. 2 2
      server/src/main/java/password/pwm/http/servlet/ActivateUserServlet.java
  27. 4 7
      server/src/main/java/password/pwm/http/servlet/ClientApiServlet.java
  28. 1 1
      server/src/main/java/password/pwm/http/servlet/DeleteAccountServlet.java
  29. 2 2
      server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java
  30. 1 2
      server/src/main/java/password/pwm/http/servlet/LoginServlet.java
  31. 3 4
      server/src/main/java/password/pwm/http/servlet/SetupOtpServlet.java
  32. 1 1
      server/src/main/java/password/pwm/http/servlet/SetupResponsesServlet.java
  33. 68 58
      server/src/main/java/password/pwm/http/servlet/UpdateProfileServlet.java
  34. 58 26
      server/src/main/java/password/pwm/http/servlet/admin/AdminServlet.java
  35. 14 8
      server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java
  36. 3 2
      server/src/main/java/password/pwm/http/servlet/command/CommandServlet.java
  37. 24 36
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java
  38. 12 15
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java
  39. 1 1
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerCertificatesServlet.java
  40. 2 1
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLocalDBServlet.java
  41. 5 4
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerServlet.java
  42. 1 1
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerWordlistServlet.java
  43. 2 4
      server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java
  44. 2 1
      server/src/main/java/password/pwm/http/servlet/forgottenpw/RemoteVerificationMethod.java
  45. 19 25
      server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java
  46. 2 4
      server/src/main/java/password/pwm/http/servlet/newuser/NewUserServlet.java
  47. 1 1
      server/src/main/java/password/pwm/http/servlet/newuser/NewUserUtils.java
  48. 2 2
      server/src/main/java/password/pwm/http/servlet/oauth/OAuthMachine.java
  49. 4 4
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchServlet.java
  50. 0 8
      server/src/main/java/password/pwm/http/tag/value/PwmValue.java
  51. 1 3
      server/src/main/java/password/pwm/ldap/LdapOperationsHelper.java
  52. 42 0
      server/src/main/java/password/pwm/ldap/auth/SimpleLdapAuthenticator.java
  53. 5 1
      server/src/main/java/password/pwm/ldap/search/UserSearchEngine.java
  54. 0 1
      server/src/main/java/password/pwm/svc/PwmServiceEnum.java
  55. 2 1
      server/src/main/java/password/pwm/svc/telemetry/HttpTelemetrySender.java
  56. 0 291
      server/src/main/java/password/pwm/svc/telemetry/VersionChecker.java
  57. 7 4
      server/src/main/java/password/pwm/svc/wordlist/SharedHistoryManager.java
  58. 2 1
      server/src/main/java/password/pwm/util/CaptchaUtility.java
  59. 5 1
      server/src/main/java/password/pwm/util/db/DBConfiguration.java
  60. 0 1
      server/src/main/java/password/pwm/util/db/DatabaseService.java
  61. 1 1
      server/src/main/java/password/pwm/util/db/DatabaseUtil.java
  62. 18 18
      server/src/main/java/password/pwm/util/operations/ActionExecutor.java
  63. 2 2
      server/src/main/java/password/pwm/util/operations/OtpService.java
  64. 73 62
      server/src/main/java/password/pwm/util/operations/PasswordUtility.java
  65. 5 4
      server/src/main/java/password/pwm/util/queue/EmailQueueManager.java
  66. 2 1
      server/src/main/java/password/pwm/ws/client/rest/RestClientHelper.java
  67. 2 1
      server/src/main/java/password/pwm/ws/client/rest/form/RestFormDataClient.java
  68. 0 64
      server/src/main/java/password/pwm/ws/server/PwmRestServlet.java
  69. 19 0
      server/src/main/java/password/pwm/ws/server/RestAuthentication.java
  70. 155 0
      server/src/main/java/password/pwm/ws/server/RestAuthenticationProcessor.java
  71. 7 0
      server/src/main/java/password/pwm/ws/server/RestAuthenticationType.java
  72. 14 0
      server/src/main/java/password/pwm/ws/server/RestMethodHandler.java
  73. 109 0
      server/src/main/java/password/pwm/ws/server/RestRequest.java
  74. 0 1
      server/src/main/java/password/pwm/ws/server/RestRequestBean.java
  75. 73 65
      server/src/main/java/password/pwm/ws/server/RestResultBean.java
  76. 0 242
      server/src/main/java/password/pwm/ws/server/RestServerHelper.java
  77. 361 0
      server/src/main/java/password/pwm/ws/server/RestServlet.java
  78. 12 0
      server/src/main/java/password/pwm/ws/server/RestWebServer.java
  79. 0 68
      server/src/main/java/password/pwm/ws/server/ServicePermissions.java
  80. 0 96
      server/src/main/java/password/pwm/ws/server/StandaloneRestHelper.java
  81. 0 39
      server/src/main/java/password/pwm/ws/server/StandaloneRestRequestBean.java
  82. 0 41
      server/src/main/java/password/pwm/ws/server/rest/AbstractRestServer.java
  83. 0 34
      server/src/main/java/password/pwm/ws/server/rest/PwmResourceConfig.java
  84. 106 216
      server/src/main/java/password/pwm/ws/server/rest/RestChallengesServer.java
  85. 80 152
      server/src/main/java/password/pwm/ws/server/rest/RestCheckPasswordServer.java
  86. 25 17
      server/src/main/java/password/pwm/ws/server/rest/RestFormSigningServer.java
  87. 37 94
      server/src/main/java/password/pwm/ws/server/rest/RestHealthServer.java
  88. 134 88
      server/src/main/java/password/pwm/ws/server/rest/RestProfileServer.java
  89. 76 144
      server/src/main/java/password/pwm/ws/server/rest/RestRandomPasswordServer.java
  90. 80 131
      server/src/main/java/password/pwm/ws/server/rest/RestSetPasswordServer.java
  91. 41 70
      server/src/main/java/password/pwm/ws/server/rest/RestStatisticsServer.java
  92. 52 62
      server/src/main/java/password/pwm/ws/server/rest/RestStatusServer.java
  93. 0 109
      server/src/main/java/password/pwm/ws/server/rest/RestUserReportServer.java
  94. 37 68
      server/src/main/java/password/pwm/ws/server/rest/RestVerifyOtpServer.java
  95. 49 80
      server/src/main/java/password/pwm/ws/server/rest/RestVerifyResponsesServer.java
  96. 0 20
      server/src/main/java/password/pwm/ws/server/rest/bean/AppDashboardData.java
  97. 1 1
      server/src/main/resources/password/pwm/AppProperty.properties
  98. 0 5
      server/src/main/resources/password/pwm/PwmConstants.properties
  99. 11 7
      server/src/main/resources/password/pwm/config/PwmSetting.xml
  100. 6 8
      server/src/main/resources/password/pwm/i18n/PwmSetting.properties

+ 21 - 5
client/pom.xml

@@ -9,10 +9,6 @@
 
     <modelVersion>4.0.0</modelVersion>
 
-    <prerequisites>
-        <maven>3.2</maven>
-    </prerequisites>
-
     <artifactId>pwm-client</artifactId>
     <packaging>jar</packaging>
 
@@ -20,6 +16,26 @@
 
     <build>
         <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>3.0.0-M1</version>
+                <executions>
+                    <execution>
+                        <id>enforce-maven</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireMavenVersion>
+                                    <version>3.2</version>
+                                </requireMavenVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <artifactId>maven-resources-plugin</artifactId>
                 <version>3.0.2</version>
@@ -106,7 +122,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>2.6</version>
+                <version>3.0.2</version>
                 <configuration>
                     <archive>
                         <manifestEntries>

+ 26 - 4
pom.xml

@@ -2,10 +2,6 @@
 
     <modelVersion>4.0.0</modelVersion>
 
-    <prerequisites>
-        <maven>3.2</maven>
-    </prerequisites>
-
     <groupId>org.pwm-project</groupId>
     <artifactId>pwm-parent</artifactId>
     <version>1.8.0-SNAPSHOT</version>
@@ -29,10 +25,36 @@
     <properties>
         <build.number>0</build.number>  <!-- default in case not set on command line -->
         <build.revision>0</build.revision>  <!-- default in case not set on command line -->
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
 
     <modules>
         <module>client</module>
         <module>server</module>
     </modules>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>3.0.0-M1</version>
+                <executions>
+                    <execution>
+                        <id>enforce-maven</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireMavenVersion>
+                                    <version>3.2</version>
+                                </requireMavenVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>

+ 25 - 21
server/pom.xml

@@ -20,7 +20,6 @@
         <skipTests>false</skipTests>
         <timestamp.iso>${maven.build.timestamp}</timestamp.iso>
         <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
 
     <profiles>
@@ -89,6 +88,26 @@
 
     <build>
         <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>3.0.0-M1</version>
+                <executions>
+                    <execution>
+                        <id>enforce-maven</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireMavenVersion>
+                                    <version>3.2</version>
+                                </requireMavenVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-assembly-plugin</artifactId>
@@ -201,7 +220,7 @@
                     <dependency>
                         <groupId>com.puppycrawl.tools</groupId>
                         <artifactId>checkstyle</artifactId>
-                        <version>7.2</version>
+                        <version>8.2</version>
                     </dependency>
                 </dependencies>
                 <executions>
@@ -652,21 +671,6 @@
             <artifactId>axis</artifactId>
             <version>1.4</version>
         </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet</artifactId>
-            <version>2.26-b07</version>
-        </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.media</groupId>
-            <artifactId>jersey-media-json-jackson</artifactId>
-            <version>2.26-b07</version>
-        </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.inject</groupId>
-            <artifactId>jersey-hk2</artifactId>
-            <version>2.26-b07</version>
-        </dependency>
         <dependency>
             <groupId>org.jasig.cas.client</groupId>
             <artifactId>cas-client-core</artifactId>
@@ -715,7 +719,7 @@
         <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
-            <version>2.8.1</version>
+            <version>2.8.2</version>
         </dependency>
         <dependency>
             <groupId>eu.bitwalker</groupId>
@@ -740,7 +744,7 @@
         <dependency>
             <groupId>com.github.ben-manes.caffeine</groupId>
             <artifactId>caffeine</artifactId>
-            <version>2.5.5</version>
+            <version>2.5.6</version>
         </dependency>
 
 
@@ -770,12 +774,12 @@
         <dependency>
             <groupId>org.webjars.bower</groupId>
             <artifactId>dgrid</artifactId>
-            <version>1.2.0</version>
+            <version>1.2.1</version>
         </dependency>
         <dependency>
             <groupId>org.webjars.bower</groupId>
             <artifactId>dstore</artifactId>
-            <version>1.1.1</version>
+            <version>1.1.2</version>
         </dependency>
         <dependency>
             <groupId>org.webjars.bower</groupId>

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

@@ -77,6 +77,7 @@ public enum AppProperty {
     DB_CONNECTIONS_MAX                              ("db.connections.max"),
     DB_CONNECTIONS_TIMEOUT_MS                       ("db.connections.timeoutMs"),
     DB_CONNECTIONS_WATCHDOG_FREQUENCY_SECONDS       ("db.connections.watchdogFrequencySeconds"),
+    DB_SCHEMA_KEY_LENGTH                            ("db.schema.keyLength"),
     DOWNLOAD_FILENAME_STATISTICS_CSV                ("download.filename.statistics.csv"),
     DOWNLOAD_FILENAME_USER_REPORT_SUMMARY_CSV       ("download.filename.reportSummary.csv"),
     DOWNLOAD_FILENAME_USER_REPORT_RECORDS_CSV       ("download.filename.reportRecords.csv"),
@@ -271,7 +272,6 @@ public enum AppProperty {
     SECURITY_INPUT_TRIM                             ("security.input.trim"),
     SECURITY_INPUT_PASSWORD_TRIM                    ("security.input.password.trim"),
     SECURITY_INPUT_THEME_MATCH_REGEX                ("security.input.themeMatchRegex"),
-    SECURITY_WS_REST_CLIENT_KEY_LENGTH              ("security.ws.rest.clientKeyLength"),
     SECURITY_WS_REST_SERVER_SECRET_HEADER           ("security.ws.rest.server.secretKeyHeader"),
     SECURITY_SHAREDHISTORY_HASH_ITERATIONS          ("security.sharedHistory.hashIterations"),
     SECURITY_SHAREDHISTORY_HASH_NAME                ("security.sharedHistory.hashName"),

+ 0 - 7
server/src/main/java/password/pwm/PwmAboutProperty.java

@@ -130,13 +130,6 @@ public enum PwmAboutProperty {
         }
         aboutMap.put(app_chaiApiVersion,           PwmConstants.CHAI_API_VERSION);
 
-        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.VERSION_CHECK_ENABLE)) {
-            if (pwmApplication.getVersionChecker() != null) {
-                aboutMap.put(app_currentPublishedVersion, pwmApplication.getVersionChecker().currentVersion());
-                aboutMap.put(app_currentPublishedVersionCheckTime, dateFormatForInfoBean(pwmApplication.getVersionChecker().lastReadTimestamp()));
-            }
-        }
-
         aboutMap.put(app_secureBlockAlgorithm,     pwmApplication.getSecureService().getDefaultBlockAlgorithm().getLabel());
         aboutMap.put(app_secureHashAlgorithm,      pwmApplication.getSecureService().getDefaultHashAlgorithm().toString());
 

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

@@ -54,7 +54,6 @@ import password.pwm.svc.sessiontrack.SessionTrackService;
 import password.pwm.svc.shorturl.UrlShortenerService;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
-import password.pwm.svc.telemetry.VersionChecker;
 import password.pwm.svc.token.TokenService;
 import password.pwm.svc.wordlist.SeedlistManager;
 import password.pwm.svc.wordlist.SharedHistoryManager;
@@ -502,10 +501,6 @@ public class PwmApplication {
         return (UserSearchEngine)pwmServiceManager.getService(UserSearchEngine.class);
     }
 
-    public VersionChecker getVersionChecker() {
-        return (VersionChecker)pwmServiceManager.getService(VersionChecker.class);
-    }
-
     public ClusterService getClusterService() {
         return (ClusterService) pwmServiceManager.getService(ClusterService.class);
     }

+ 0 - 31
server/src/main/java/password/pwm/PwmConstants.java

@@ -80,10 +80,6 @@ public abstract class PwmConstants {
 
     public static final String PWM_APP_NAME_VERSION = PWM_APP_NAME + " " + SERVLET_VERSION;
 
-    public static final long VERSION_CHECK_FREQUENCEY_MS = Long.parseLong(readPwmConstantsBundle("versionCheckFrequencyMs"));
-    public static final long VERSION_CHECK_FAIL_RETRY_MS = Long.parseLong(readPwmConstantsBundle("versionCheckFailRetryMs"));
-    public static final long STATISTICS_PUBLISH_FREQUENCY_MS = Long.parseLong(readPwmConstantsBundle("statisticsPublishFrequencyMs"));
-
     public static final String CONFIGMANAGER_INTRUDER_USERNAME = "ConfigurationManagerLogin";
 
     public static final Locale DEFAULT_LOCALE = new Locale(readPwmConstantsBundle("locale.defaultLocale"));
@@ -102,8 +98,6 @@ public abstract class PwmConstants {
     public static final int TRIAL_MAX_TOTAL_AUTH = 10000;
 
 
-    public static final int DATABASE_ACCESSOR_KEY_LENGTH = Integer.parseInt(readPwmConstantsBundle("databaseAccessor.keyLength"));
-
     public static final String LDAP_AD_PASSWORD_POLICY_CONTROL_ASN = "1.2.840.113556.1.4.2066";
     public static final String PROFILE_ID_ALL = "all";
 
@@ -111,7 +105,6 @@ public abstract class PwmConstants {
     public static final float JAVA_MINIMUM_VERSION = (float)1.6;
 
     public static final String HTTP_BASIC_AUTH_PREFIX = readPwmConstantsBundle("httpHeaderAuthorizationBasic");
-    public static final String HTTP_HEADER_REST_CLIENT_KEY = readPwmConstantsBundle("httpRestClientKey");
 
     public static final String DEFAULT_BAD_PASSWORD_ATTEMPT = readPwmConstantsBundle("defaultBadPasswordAttempt");
 
@@ -244,30 +237,6 @@ public abstract class PwmConstants {
 // -------------------------- ENUMERATIONS --------------------------
 
 
-    public enum ContentTypeValue {
-        json("application/json; charset=" + PwmConstants.DEFAULT_CHARSET),
-        zip("application/zip"),
-        xml("text/xml; charset=" + PwmConstants.DEFAULT_CHARSET),
-        csv("text/csv; charset=" + PwmConstants.DEFAULT_CHARSET),
-        javascript("text/javascript; charset=" + PwmConstants.DEFAULT_CHARSET),
-        plain("text/plain; charset=" + PwmConstants.DEFAULT_CHARSET),
-        html("text/html; charset=" + PwmConstants.DEFAULT_CHARSET),
-        form("application/x-www-form-urlencoded; charset=" + PwmConstants.DEFAULT_CHARSET),
-        png("image/png"),
-        octetstream("application/octet-stream"),
-        ;
-
-        private final String headerValue;
-
-        ContentTypeValue(final String headerValue) {
-            this.headerValue = headerValue;
-        }
-
-        public String getHeaderValue() {
-            return headerValue;
-        }
-    }
-
     public enum AcceptValue {
         json("application/json"),
         html("text/html"),

+ 3 - 161
server/src/main/java/password/pwm/bean/LocalSessionStateBean.java

@@ -22,6 +22,7 @@
 
 package password.pwm.bean;
 
+import lombok.Data;
 import password.pwm.ldap.UserInfoBean;
 import password.pwm.util.secure.PwmRandom;
 
@@ -38,8 +39,9 @@ import java.util.Locale;
  *
  * @author Jason D. Rivard
  */
+
+@Data
 public class LocalSessionStateBean implements Serializable {
-// ------------------------------ FIELDS ------------------------------
 
     private String srcAddress;
     private String srcHostname;
@@ -51,7 +53,6 @@ public class LocalSessionStateBean implements Serializable {
     private String lastRequestURL;
 
     private String sessionVerificationKey = "key";
-    private String restClientKey;
 
     private boolean debugInitialized;
     private boolean sessionVerified;
@@ -66,146 +67,13 @@ public class LocalSessionStateBean implements Serializable {
     private int intruderAttempts;
     private boolean oauthInProgress;
 
-    // settings
     private int sessionVerificationKeyLength;
     private boolean sessionIdRecycleNeeded;
 
-
-
-// --------------------- GETTER / SETTER METHODS ---------------------
-
     public LocalSessionStateBean(final int sessionVerificationKeyLength) {
         this.sessionVerificationKeyLength = sessionVerificationKeyLength;
     }
 
-    public boolean isPasswordModified() {
-        return passwordModified;
-    }
-
-    public void setPasswordModified(final boolean passwordModified) {
-        this.passwordModified = passwordModified;
-    }
-
-    public boolean isPrivateUrlAccessed() {
-        return this.privateUrlAccessed;
-    }
-
-    public void setPrivateUrlAccessed(final boolean privateUrlAccessed) {
-        this.privateUrlAccessed = privateUrlAccessed;
-    }
-
-    public String getForwardURL() {
-        return forwardURL;
-    }
-
-    public void setForwardURL(final String forwardURL) {
-        this.forwardURL = forwardURL;
-    }
-
-    public Locale getLocale() {
-        return locale;
-    }
-
-    public void setLocale(final Locale locale) {
-        this.locale = locale;
-    }
-
-    public String getLogoutURL() {
-        return logoutURL;
-    }
-
-    public void setLogoutURL(final String logoutURL) {
-        this.logoutURL = logoutURL;
-    }
-
-    public String getSessionID() {
-        return sessionID;
-    }
-
-    public void setSessionID(final String sessionID) {
-        this.sessionID = sessionID;
-    }
-
-    public String getSrcAddress() {
-        return srcAddress;
-    }
-
-    public void setSrcAddress(final String srcAddress) {
-        this.srcAddress = srcAddress;
-    }
-
-    public String getSrcHostname() {
-        return srcHostname;
-    }
-
-    public void setSrcHostname(final String srcHostname) {
-        this.srcHostname = srcHostname;
-    }
-
-    public String getSessionVerificationKey() {
-        return sessionVerificationKey;
-    }
-
-    public boolean isSessionVerified() {
-        return sessionVerified;
-    }
-
-    public void setSessionVerified(final boolean sessionVerified) {
-        this.sessionVerified = sessionVerified;
-    }
-
-    public boolean isDebugInitialized() {
-        return debugInitialized;
-    }
-
-    public void setDebugInitialized(final boolean debugInitialized) {
-        this.debugInitialized = debugInitialized;
-    }
-
-    public String getTheme() {
-        return theme;
-    }
-
-    public void setTheme(final String theme) {
-        this.theme = theme;
-    }
-
-    public Instant getPageLeaveNoticeTime() {
-        return pageLeaveNoticeTime;
-    }
-
-    public void setPageLeaveNoticeTime(final Instant pageLeaveNoticeTime) {
-        this.pageLeaveNoticeTime = pageLeaveNoticeTime;
-    }
-
-    public Instant getSessionCreationTime() {
-        return sessionCreationTime;
-    }
-
-    public void setSessionCreationTime(final Instant sessionCreationTime) {
-        this.sessionCreationTime = sessionCreationTime;
-    }
-
-    public Instant getSessionLastAccessedTime() {
-        return sessionLastAccessedTime;
-    }
-
-    public void setSessionLastAccessedTime(final Instant sessionLastAccessedTime) {
-        this.sessionLastAccessedTime = sessionLastAccessedTime;
-    }
-
-    public String getLastRequestURL() {
-        return lastRequestURL;
-    }
-
-    public void setLastRequestURL(final String lastRequestURL) {
-        this.lastRequestURL = lastRequestURL;
-    }
-
-    public int getIntruderAttempts() {
-        return intruderAttempts;
-    }
-
     public void incrementIntruderAttempts() {
         intruderAttempts++;
     }
@@ -214,34 +82,8 @@ public class LocalSessionStateBean implements Serializable {
         intruderAttempts = 0;
     }
 
-    public boolean isOauthInProgress()
-    {
-        return oauthInProgress;
-    }
-
-    public void setOauthInProgress(final boolean oauthInProgress)
-    {
-        this.oauthInProgress = oauthInProgress;
-    }
-
-    public String getRestClientKey() {
-        return restClientKey;
-    }
-
-    public void setRestClientKey(final String restClientKey) {
-        this.restClientKey = restClientKey;
-    }
-
     public void regenerateSessionVerificationKey() {
         sessionVerificationKey = PwmRandom.getInstance().alphaNumericString(sessionVerificationKeyLength) + Long.toHexString(System.currentTimeMillis());
     }
-
-    public boolean isSessionIdRecycleNeeded() {
-        return sessionIdRecycleNeeded;
-    }
-
-    public void setSessionIdRecycleNeeded(final boolean sessionIdRecycleNeeded) {
-        this.sessionIdRecycleNeeded = sessionIdRecycleNeeded;
-    }
 }
 

+ 3 - 96
server/src/main/java/password/pwm/bean/pub/SessionStateInfoBean.java

@@ -22,9 +22,12 @@
 
 package password.pwm.bean.pub;
 
+import lombok.Data;
+
 import java.time.Instant;
 import java.util.Locale;
 
+@Data
 public class SessionStateInfoBean implements PublishedBean {
     private String label;
     private Instant createTime;
@@ -38,100 +41,4 @@ public class SessionStateInfoBean implements PublishedBean {
     private String srcHost;
     private String lastUrl;
     private int intruderAttempts;
-
-    public String getLabel() {
-        return label;
-    }
-
-    public void setLabel(final String label) {
-        this.label = label;
-    }
-
-    public Instant getCreateTime() {
-        return createTime;
-    }
-
-    public void setCreateTime(final Instant createTime) {
-        this.createTime = createTime;
-    }
-
-    public Instant getLastTime() {
-        return lastTime;
-    }
-
-    public void setLastTime(final Instant lastTime) {
-        this.lastTime = lastTime;
-    }
-
-    public String getIdle() {
-        return idle;
-    }
-
-    public void setIdle(final String idle) {
-        this.idle = idle;
-    }
-
-    public Locale getLocale() {
-        return locale;
-    }
-
-    public void setLocale(final Locale locale) {
-        this.locale = locale;
-    }
-
-    public String getLdapProfile() {
-        return ldapProfile;
-    }
-
-    public void setLdapProfile(final String ldapProfile) {
-        this.ldapProfile = ldapProfile;
-    }
-
-    public String getUserDN() {
-        return userDN;
-    }
-
-    public void setUserDN(final String userDN) {
-        this.userDN = userDN;
-    }
-
-    public String getUserID() {
-        return userID;
-    }
-
-    public void setUserID(final String userID) {
-        this.userID = userID;
-    }
-
-    public String getSrcAddress() {
-        return srcAddress;
-    }
-
-    public void setSrcAddress(final String srcAddress) {
-        this.srcAddress = srcAddress;
-    }
-
-    public String getSrcHost() {
-        return srcHost;
-    }
-
-    public void setSrcHost(final String srcHost) {
-        this.srcHost = srcHost;
-    }
-
-    public String getLastUrl() {
-        return lastUrl;
-    }
-
-    public void setLastUrl(final String lastUrl) {
-        this.lastUrl = lastUrl;
-    }
-
-    public int getIntruderAttempts() {
-        return intruderAttempts;
-    }
-
-    public void setIntruderAttempts(final int intruderAttempts) {
-        this.intruderAttempts = intruderAttempts;
-    }
 }

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

@@ -69,8 +69,6 @@ public enum PwmSetting {
     // application settings
     PWM_SITE_URL(
             "pwm.selfURL", PwmSettingSyntax.STRING, PwmSettingCategory.GENERAL),
-    VERSION_CHECK_ENABLE(
-            "pwm.versionCheck.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.TELEMETRY),
     PUBLISH_STATS_ENABLE(
             "pwm.publishStats.enable", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.TELEMETRY),
     PUBLISH_STATS_SITE_DESCRIPTION(
@@ -1114,12 +1112,12 @@ public enum PwmSetting {
             "webservices.enableReadAnswers", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.REST_SERVER),
     PUBLIC_HEALTH_STATS_WEBSERVICES(
             "webservices.healthStats.makePublic", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.REST_SERVER),
+    WEBSERVICES_EXTERNAL_SECRET(
+            "webservices.external.secrets", PwmSettingSyntax.NAMED_SECRET, PwmSettingCategory.REST_SERVER),
     WEBSERVICES_QUERY_MATCH(
             "webservices.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.REST_SERVER),
     WEBSERVICES_THIRDPARTY_QUERY_MATCH(
             "webservices.thirdParty.queryMatch", PwmSettingSyntax.USER_PERMISSION, PwmSettingCategory.REST_SERVER),
-    WEBSERVICES_EXTERNAL_SECRET(
-            "webservices.external.secrets", PwmSettingSyntax.NAMED_SECRET, PwmSettingCategory.REST_SERVER),
 
 
     EXTERNAL_MACROS_DEST_TOKEN_URLS(

+ 9 - 0
server/src/main/java/password/pwm/config/option/WebServiceUsage.java

@@ -23,6 +23,15 @@
 package password.pwm.config.option;
 
 public enum WebServiceUsage {
+    Challenges,
+    CheckPassword,
     Health,
+    Profile,
+    RandomPassword,
+    SetPassword,
     SigningForm,
+    Statistics,
+    Status,
+    VerifyOtp,
+    VerifyResponses,
 }

+ 57 - 0
server/src/main/java/password/pwm/http/HttpContentType.java

@@ -0,0 +1,57 @@
+package password.pwm.http;
+
+import password.pwm.PwmConstants;
+import password.pwm.util.java.StringUtil;
+
+import java.nio.charset.Charset;
+
+public enum HttpContentType {
+    json("application/json", PwmConstants.DEFAULT_CHARSET),
+    zip("application/zip"),
+    xml("text/xml", PwmConstants.DEFAULT_CHARSET),
+    csv("text/csv", PwmConstants.DEFAULT_CHARSET),
+    javascript("text/javascript", PwmConstants.DEFAULT_CHARSET),
+    plain("text/plain", PwmConstants.DEFAULT_CHARSET),
+    html("text/html", PwmConstants.DEFAULT_CHARSET),
+    form("application/x-www-form-urlencoded", PwmConstants.DEFAULT_CHARSET),
+    png("image/png"),
+    octetstream("application/octet-stream"),
+    ;
+
+    private final String mimeType;
+    private final Charset charset;
+
+    HttpContentType(final String mimeType, final Charset charset) {
+        this.mimeType = mimeType;
+        this.charset = charset;
+    }
+
+    HttpContentType(final String mimeType) {
+        this(mimeType, null);
+    }
+
+    public String getHeaderValue() {
+        if (charset == null) {
+            return mimeType;
+        }
+        return mimeType + "; charset=" + charset;
+    }
+
+    public String getMimeType() {
+        return this.mimeType;
+    }
+
+    public static HttpContentType fromContentTypeHeader(final String value) {
+        if (StringUtil.isEmpty(value)) {
+            return null;
+        }
+
+        for (final HttpContentType httpContentType : HttpContentType.values()) {
+            if (value.equalsIgnoreCase(httpContentType.getMimeType())) {
+                return httpContentType;
+            }
+        }
+
+        return null;
+    }
+}

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

@@ -379,6 +379,9 @@ public abstract class PwmHttpRequestWrapper {
         return HttpMethod.fromString(this.getHttpServletRequest().getMethod());
     }
 
+    public Configuration getConfig() {
+        return configuration;
+    }
 
 }
 

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

@@ -115,7 +115,7 @@ public class PwmHttpResponseWrapper {
         httpServletResponse.setStatus(status);
     }
 
-    public void setContentType(final PwmConstants.ContentTypeValue contentType) {
+    public void setContentType(final HttpContentType contentType) {
         this.getHttpServletResponse().setContentType(contentType.getHeaderValue());
     }
 

+ 2 - 7
server/src/main/java/password/pwm/http/PwmRequest.java

@@ -34,15 +34,14 @@ import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
-import password.pwm.ldap.UserInfo;
-import password.pwm.config.Configuration;
-import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.config.PwmSetting;
+import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.http.servlet.command.CommandServlet;
+import password.pwm.ldap.UserInfo;
 import password.pwm.util.Validator;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
@@ -150,10 +149,6 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
         return pwmSession.getSessionStateBean().getLocale();
     }
 
-    public Configuration getConfig() {
-        return pwmApplication.getConfig();
-    }
-
     public void forwardToJsp(final JspUrl jspURL)
             throws ServletException, IOException, PwmUnrecoverableException
     {

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

@@ -24,7 +24,6 @@ package password.pwm.http;
 
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import password.pwm.PwmApplication;
-import password.pwm.PwmConstants;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.error.ErrorInformation;
@@ -195,7 +194,7 @@ public class PwmResponse extends PwmHttpResponseWrapper {
         preCommitActions();
         final HttpServletResponse resp = this.getHttpServletResponse();
         final String outputString = restResultBean.toJson();
-        resp.setContentType(PwmConstants.ContentTypeValue.json.getHeaderValue());
+        resp.setContentType(HttpContentType.json.getHeaderValue());
         resp.getWriter().print(outputString);
         resp.getWriter().close();
     }
@@ -215,7 +214,7 @@ public class PwmResponse extends PwmHttpResponseWrapper {
         writeCookie(cookieName, encryptedValue, seconds, path, PwmHttpResponseWrapper.Flag.BypassSanitation);
     }
 
-    public void markAsDownload(final PwmConstants.ContentTypeValue contentType, final String filename) {
+    public void markAsDownload(final HttpContentType contentType, final String filename) {
         this.setHeader(HttpHeader.ContentDisposition,"attachment; fileName=" + filename);
         this.setContentType(contentType);
     }

+ 0 - 21
server/src/main/java/password/pwm/http/PwmSession.java

@@ -70,7 +70,6 @@ public class PwmSession implements Serializable {
     private UserInfo userInfoBean;
     private UserSessionDataCacheBean userSessionDataCacheBean;
 
-    private Settings settings = new Settings();
     private static final Object CREATION_LOCK = new Object();
 
     private transient SessionManager sessionManager;
@@ -113,7 +112,6 @@ public class PwmSession implements Serializable {
 
         pwmApplication.getSessionTrackService().addSessionData(this);
 
-        settings.restKeyLength = Integer.parseInt(pwmApplication.getConfig().readAppProperty(AppProperty.SECURITY_WS_REST_CLIENT_KEY_LENGTH));
         LOGGER.trace(this,"created new session");
     }
 
@@ -303,29 +301,10 @@ public class PwmSession implements Serializable {
         }
     }
 
-    public String getRestClientKey() {
-        if (!this.isAuthenticated()) {
-            return "";
-        }
-
-        final String restClientKey = this.getSessionStateBean().getRestClientKey();
-        if (restClientKey != null && restClientKey.length() > 0) {
-            return restClientKey;
-        }
-
-        final String newKey = Long.toString(System.currentTimeMillis(),36) + PwmRandom.getInstance().alphaNumericString(settings.restKeyLength);
-        this.getSessionStateBean().setRestClientKey(newKey);
-        return newKey;
-    }
-
     public boolean isAuthenticated() {
         return getLoginInfoBean().isAuthenticated();
     }
 
-    private static class Settings implements Serializable {
-        private int restKeyLength = 36; // default
-    }
-
     public int size() {
         try {
             final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

+ 2 - 11
server/src/main/java/password/pwm/http/PwmURL.java

@@ -154,18 +154,9 @@ public class PwmURL {
 
     }
 
-    public boolean isJerseyWebService() {
-        return checkIfStartsWithURL(PwmConstants.URL_PREFIX_PUBLIC + "/rest/")
-                && !isStandaloneWebService();
-    }
+    public boolean isRestService() {
+        return checkIfStartsWithURL(PwmConstants.URL_PREFIX_PUBLIC + "/rest/");
 
-    public boolean isStandaloneWebService() {
-        return checkIfStartsWithURL(PwmConstants.URL_PREFIX_PUBLIC + "/rest/")
-                && (
-                checkIfStartsWithURL(PwmConstants.URL_PREFIX_PUBLIC + "/rest/signing")
-                        ||
-                        checkIfStartsWithURL(PwmConstants.URL_PREFIX_PUBLIC + "/rest/ping")
-        );
     }
 
     public boolean isConfigManagerURL() {

+ 5 - 6
server/src/main/java/password/pwm/http/filter/ApplicationModeFilter.java

@@ -52,7 +52,7 @@ public class ApplicationModeFilter extends AbstractPwmFilter {
     {
         // ignore if resource request
         final PwmURL pwmURL = pwmRequest.getURL();
-        if (!pwmURL.isResourceURL() && !pwmURL.isStandaloneWebService() && !pwmURL.isReferenceURL() && !pwmURL.isClientApiServlet()) {
+        if (!pwmURL.isResourceURL() && !pwmURL.isRestService() && !pwmURL.isReferenceURL() && !pwmURL.isClientApiServlet()) {
             // check for valid config
             try {
                 if (checkConfigModes(pwmRequest) == ProcessStatus.Halt) {
@@ -72,9 +72,8 @@ public class ApplicationModeFilter extends AbstractPwmFilter {
 
     @Override
     boolean isInterested(final PwmApplicationMode mode, final PwmURL pwmURL) {
-        return !pwmURL.isJerseyWebService()
-                && !pwmURL.isStandaloneWebService()
-                && !pwmURL.isJerseyWebService();
+        return !pwmURL.isRestService()
+                && !pwmURL.isRestService();
     }
 
     private static ProcessStatus checkConfigModes(
@@ -89,7 +88,7 @@ public class ApplicationModeFilter extends AbstractPwmFilter {
 
         if (mode == PwmApplicationMode.NEW) {
             // check if current request is actually for the config url, if it is, just do nothing.
-            if (pwmURL.isCommandServletURL() || pwmURL.isJerseyWebService()) {
+            if (pwmURL.isCommandServletURL() || pwmURL.isRestService()) {
                 return ProcessStatus.Continue;
             }
 
@@ -129,7 +128,7 @@ public class ApplicationModeFilter extends AbstractPwmFilter {
                         || pwmURL.isLogoutURL()
                         || pwmURL.isOauthConsumer()
                         || pwmURL.isAdminUrl()
-                        || pwmURL.isJerseyWebService();
+                        || pwmURL.isRestService();
 
                 if (!permittedURl) {
                     final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_APPLICATION_NOT_RUNNING);

+ 4 - 6
server/src/main/java/password/pwm/http/filter/AuthenticationFilter.java

@@ -22,7 +22,6 @@
 
 package password.pwm.http.filter;
 
-import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
@@ -127,7 +126,7 @@ public class AuthenticationFilter extends AbstractPwmFilter {
 
     @Override
     boolean isInterested(final PwmApplicationMode mode, final PwmURL pwmURL) {
-        return !pwmURL.isResourceURL() && !pwmURL.isStandaloneWebService();
+        return !pwmURL.isResourceURL() && !pwmURL.isRestService();
     }
 
     private void processAuthenticatedSession(
@@ -492,11 +491,10 @@ public class AuthenticationFilter extends AbstractPwmFilter {
                 sessionAuthenticator.authenticateUser(userIdentity, basicAuthInfo.getPassword());
                 pwmSession.getLoginInfoBean().setBasicAuth(basicAuthInfo);
 
-            } catch (ChaiUnavailableException e) {
-                StatisticsManager.incrementStat(pwmRequest, Statistic.LDAP_UNAVAILABLE_COUNT);
-                final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, e.getMessage());
-                throw new PwmUnrecoverableException(errorInformation);
             } catch (PwmException e) {
+                if (e.getError() == PwmError.ERROR_DIRECTORY_UNAVAILABLE) {
+                    StatisticsManager.incrementStat(pwmRequest, Statistic.LDAP_UNAVAILABLE_COUNT);
+                }
                 throw new PwmUnrecoverableException(e.getError());
             }
         }

+ 1 - 1
server/src/main/java/password/pwm/http/filter/AuthorizationFilter.java

@@ -57,7 +57,7 @@ public class AuthorizationFilter extends AbstractPwmFilter {
 
     @Override
     boolean isInterested(final PwmApplicationMode mode, final PwmURL pwmURL) {
-        return !pwmURL.isStandaloneWebService();
+        return !pwmURL.isRestService();
     }
 
     public void processFilter(

+ 1 - 1
server/src/main/java/password/pwm/http/filter/GZIPFilter.java

@@ -82,7 +82,7 @@ public class GZIPFilter implements Filter {
 
         try {
             final PwmURL pwmURL = new PwmURL((HttpServletRequest) servletRequest);
-            if (pwmURL.isResourceURL() || pwmURL.isJerseyWebService()) {
+            if (pwmURL.isResourceURL() || pwmURL.isRestService()) {
                 return false;
             }
         } catch (Exception e) {

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

@@ -101,6 +101,6 @@ public class ObsoleteUrlFilter extends AbstractPwmFilter {
 
     @Override
     boolean isInterested(final PwmApplicationMode mode, final PwmURL pwmURL) {
-        return !pwmURL.isStandaloneWebService();
+        return !pwmURL.isRestService();
     }
 }

+ 11 - 13
server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java

@@ -112,7 +112,7 @@ public class RequestInitializationFilter implements Filter {
 
         if (testPwmApplicationLoad == null && pwmURL.isResourceURL()) {
             filterChain.doFilter(req, resp);
-        } else if (pwmURL.isStandaloneWebService() || pwmURL.isJerseyWebService()) {
+        } else if (pwmURL.isRestService()) {
             filterChain.doFilter(req, resp);
         } else {
             if (mode == PwmApplicationMode.ERROR) {
@@ -390,17 +390,16 @@ public class RequestInitializationFilter implements Filter {
     }
 
 
-    public static String readUserHostname(final PwmRequest pwmRequest) throws PwmUnrecoverableException {
-        final Configuration config = pwmRequest.getConfig();
+    public static String readUserHostname(final HttpServletRequest request, final Configuration config) throws PwmUnrecoverableException {
         if (config != null && !config.readSettingAsBoolean(PwmSetting.REVERSE_DNS_ENABLE)) {
             return "";
         }
 
-        final String userIPAddress = readUserIPAddress(pwmRequest);
+        final String userIPAddress = readUserIPAddress(request, config);
         try {
             return InetAddress.getByName(userIPAddress).getCanonicalHostName();
         } catch (UnknownHostException e) {
-            LOGGER.trace(pwmRequest, "unknown host while trying to compute hostname for src request: " + e.getMessage());
+            LOGGER.trace("unknown host while trying to compute hostname for src request: " + e.getMessage());
         }
         return "";
     }
@@ -411,14 +410,13 @@ public class RequestInitializationFilter implements Filter {
      *
      * @return String containing the textual representation of the source IP address, or null if the request is invalid.
      */
-    public static String readUserIPAddress(final PwmRequest pwmRequest) throws PwmUnrecoverableException {
-        final Configuration config = pwmRequest.getConfig();
+    public static String readUserIPAddress(final HttpServletRequest request, final Configuration config) throws PwmUnrecoverableException {
         final boolean useXForwardedFor = config != null && config.readSettingAsBoolean(PwmSetting.USE_X_FORWARDED_FOR_HEADER);
 
         String userIP = "";
 
         if (useXForwardedFor) {
-            userIP = pwmRequest.readHeaderValueAsString(HttpHeader.XForwardedFor);
+            userIP = request.getHeader(HttpHeader.XForwardedFor.getHttpName());
             if (!StringUtil.isEmpty(userIP)) {
                 final int commaIndex = userIP.indexOf(',');
                 if (commaIndex > -1) {
@@ -436,7 +434,7 @@ public class RequestInitializationFilter implements Filter {
         }
 
         if (StringUtil.isEmpty(userIP)) {
-            userIP = pwmRequest.getHttpServletRequest().getRemoteAddr();
+            userIP = request.getRemoteAddr();
         }
 
         return userIP == null ? "" : userIP;
@@ -459,12 +457,12 @@ public class RequestInitializationFilter implements Filter {
 
         // mark session ip address
         if (ssBean.getSrcAddress() == null) {
-            ssBean.setSrcAddress(readUserIPAddress(pwmRequest));
+            ssBean.setSrcAddress(readUserIPAddress(pwmRequest.getHttpServletRequest(), pwmRequest.getConfig()));
         }
 
         // mark the user's hostname in the session bean
         if (ssBean.getSrcHostname() == null) {
-            ssBean.setSrcHostname(readUserHostname(pwmRequest));
+            ssBean.setSrcHostname(readUserHostname(pwmRequest.getHttpServletRequest(), pwmRequest.getConfig()));
         }
 
         // update the privateUrlAccessed flag
@@ -517,7 +515,7 @@ public class RequestInitializationFilter implements Filter {
 
         // check the user's IP address
         if (!pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.MULTI_IP_SESSION_ALLOWED)) {
-            final String remoteAddress = readUserIPAddress(pwmRequest);
+            final String remoteAddress = readUserIPAddress(pwmRequest.getHttpServletRequest(), pwmRequest.getConfig());
             if (!ssBean.getSrcAddress().equals(remoteAddress)) {
                 final String errorMsg = "current network address '" + remoteAddress + "' has changed from original network address '" + ssBean.getSrcAddress() + "'";
                 final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_SECURITY_VIOLATION,errorMsg);
@@ -599,7 +597,7 @@ public class RequestInitializationFilter implements Filter {
         if (
                 performCsrfHeaderChecks
                         && !pwmRequest.getMethod().isIdempotent()
-                        && !pwmRequest.getURL().isJerseyWebService()
+                        && !pwmRequest.getURL().isRestService()
                 )
         {
             final String originValue = pwmRequest.readHeaderValueAsString(HttpHeader.Origin);

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

@@ -79,7 +79,7 @@ public class SessionFilter extends AbstractPwmFilter {
 
     @Override
     boolean isInterested(final PwmApplicationMode mode, final PwmURL pwmURL) {
-        return !pwmURL.isStandaloneWebService();
+        return !pwmURL.isRestService();
     }
 
     private static final AtomicInteger REQUEST_COUNTER = new AtomicInteger(0);
@@ -98,7 +98,7 @@ public class SessionFilter extends AbstractPwmFilter {
         pwmRequest.debugHttpRequestToLog("requestID=" + requestID);
 
         final PwmURL pwmURL = pwmRequest.getURL();
-        if (!pwmURL.isStandaloneWebService() && !pwmURL.isResourceURL()) {
+        if (!pwmURL.isRestService() && !pwmURL.isResourceURL()) {
             if (handleStandardRequestOperations(pwmRequest) == ProcessStatus.Halt) {
                 return;
             }

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

@@ -398,7 +398,7 @@ public class ActivateUserServlet extends AbstractPwmServlet {
                             .setMacroMachine(macroMachine)
                             .createActionExecutor();
 
-                    actionExecutor.executeActions(configValues, pwmSession);
+                    actionExecutor.executeActions(configValues, pwmRequest.getSessionLabel());
                 }
             }
 
@@ -441,7 +441,7 @@ public class ActivateUserServlet extends AbstractPwmServlet {
                                     .setExpandPwmMacros(true)
                                     .setMacroMachine(macroMachine)
                                     .createActionExecutor();
-                            actionExecutor.executeActions(configValues, pwmSession);
+                            actionExecutor.executeActions(configValues, pwmRequest.getSessionLabel());
                         }
                     } catch (PwmOperationalException e) {
                         final ErrorInformation info = new ErrorInformation(PwmError.ERROR_ACTIVATION_FAILURE, e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues());

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

@@ -138,8 +138,7 @@ public class ClientApiServlet extends ControlledPwmServlet {
         pwmRequest.getPwmResponse().setHeader(HttpHeader.Cache_Control, "public, max-age=" + maxCacheAgeSeconds);
 
         final AppData appData = makeAppData(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), pwmRequest.getHttpServletRequest(), pwmRequest.getPwmResponse().getHttpServletResponse(), pageUrl);
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(appData);
+        final RestResultBean restResultBean = RestResultBean.withData(appData);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -160,8 +159,7 @@ public class ClientApiServlet extends ControlledPwmServlet {
         try {
             final LinkedHashMap<String,String> displayData = new LinkedHashMap<>(makeDisplayData(pwmRequest.getPwmApplication(),
                     pwmRequest.getPwmSession(), bundleName));
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(displayData);
+            final RestResultBean restResultBean = RestResultBean.withData(displayData);
             pwmRequest.outputJsonResult(restResultBean);
         } catch (Exception e) {
             final String errorMSg = "error during rest /strings call for bundle " + bundleName + ", error: " + e.getMessage();
@@ -198,8 +196,7 @@ public class ClientApiServlet extends ControlledPwmServlet {
                     pwmRequest.getPwmApplication(),
                     pwmRequest.getLocale(),
                     false);
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(jsonOutput);
+            final RestResultBean restResultBean = RestResultBean.withData(jsonOutput);
             pwmRequest.outputJsonResult(restResultBean);
         } catch (PwmException e) {
             final ErrorInformation errorInformation = e.getErrorInformation();
@@ -221,7 +218,7 @@ public class ClientApiServlet extends ControlledPwmServlet {
         final PingResponse pingResponse = new PingResponse();
         pingResponse.setTime(Instant.now());
         pingResponse.setRuntimeNonce(pwmRequest.getPwmApplication().getRuntimeNonce());
-        pwmRequest.outputJsonResult(new RestResultBean(pingResponse));
+        pwmRequest.outputJsonResult(RestResultBean.withData(pingResponse));
         return ProcessStatus.Halt;
     }
 

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

@@ -206,7 +206,7 @@ public class DeleteAccountServlet extends ControlledPwmServlet {
                         .createActionExecutor();
 
                 try {
-                    actionExecutor.executeActions(actions, pwmRequest.getPwmSession());
+                    actionExecutor.executeActions(actions, pwmRequest.getSessionLabel());
                 } catch (PwmOperationalException e) {
                     LOGGER.error("error during user delete action execution: " + e.getMessage());
                     throw new PwmUnrecoverableException(e.getErrorInformation(),e.getCause());

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

@@ -233,7 +233,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
             final Date expirationDate = readExpirationFromRequest(pwmRequest);
 
             // Update user attributes
-            LdapOperationsHelper.writeFormValuesToLdap(pwmApplication, pwmSession, theGuest, formValues, false);
+            LdapOperationsHelper.writeFormValuesToLdap(pwmApplication, pwmSession.getSessionManager().getMacroMachine(pwmApplication), theGuest, formValues, false);
 
             // Write expirationDate
             if (expirationDate != null) {
@@ -465,7 +465,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
                             .setMacroMachine(macroMachine)
                             .createActionExecutor();
 
-                    actionExecutor.executeActions(actions, pwmSession);
+                    actionExecutor.executeActions(actions, pwmRequest.getSessionLabel());
                 }
             }
 

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

@@ -158,9 +158,8 @@ public class LoginServlet extends ControlledPwmServlet {
 
         // login has succeeded
         final String nextLoginUrl = determinePostLoginUrl(pwmRequest);
-        final RestResultBean restResultBean = new RestResultBean();
         final HashMap<String,String> resultMap = new HashMap<>(Collections.singletonMap("nextURL", nextLoginUrl));
-        restResultBean.setData(resultMap);
+        final RestResultBean restResultBean = RestResultBean.withData(resultMap);
         LOGGER.debug(pwmRequest, "rest login succeeded");
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;

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

@@ -316,14 +316,13 @@ public class SetupOtpServlet extends AbstractPwmServlet {
 
         try {
             final boolean passed = otpService.validateToken(
-                    pwmSession,
+                    pwmRequest.getSessionLabel(),
                     pwmSession.getUserInfo().getUserIdentity(),
                     otpUserRecord,
                     code,
                     false
             );
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(passed);
+            final RestResultBean restResultBean = RestResultBean.withData(passed);
 
             LOGGER.trace(pwmSession,"returning result for restValidateCode: " + JsonUtil.serialize(restResultBean));
             pwmRequest.outputJsonResult(restResultBean);
@@ -375,7 +374,7 @@ public class SetupOtpServlet extends AbstractPwmServlet {
                 }
 
                 if (otpService.validateToken(
-                        pwmSession,
+                        pwmRequest.getSessionLabel(),
                         pwmSession.getUserInfo().getUserIdentity(),
                         otpBean.getOtpUserRecord(),
                         otpToken,

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

@@ -243,7 +243,7 @@ public class SetupResponsesServlet extends ControlledPwmServlet {
         }
 
         final ValidationResponseBean validationResponseBean = new ValidationResponseBean(userMessage,success);
-        final RestResultBean restResultBean = new RestResultBean(validationResponseBean);
+        final RestResultBean restResultBean = RestResultBean.withData(validationResponseBean);
         LOGGER.trace(pwmRequest,"completed rest validate response in "
                 + TimeDuration.fromCurrent(startTime).asCompactString()
                 + ", result=" + JsonUtil.serialize(restResultBean));

+ 68 - 58
server/src/main/java/password/pwm/http/servlet/UpdateProfileServlet.java

@@ -25,9 +25,11 @@ package password.pwm.http.servlet;
 import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
+import lombok.Data;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.TokenVerificationProgress;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
@@ -70,6 +72,7 @@ import password.pwm.ws.server.RestResultBean;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -96,6 +99,12 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(UpdateProfileServlet.class);
 
+    @Data
+    public static class ValidateResponse implements Serializable {
+        private String message;
+        private boolean success;
+    }
+
     public enum UpdateProfileAction implements AbstractPwmServlet.ProcessAction {
         updateProfile(HttpMethod.POST),
         agree(HttpMethod.POST),
@@ -206,7 +215,7 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
             final Map<FormConfiguration,String> formValues = readFromJsonRequest(pwmRequest, updateAttributesProfile, updateProfileBean);
 
             // verify form meets the form requirements
-            verifyFormAttributes(pwmRequest, formValues, true);
+            verifyFormAttributes(pwmRequest.getPwmApplication(), pwmRequest.getUserInfoIfLoggedIn(), pwmRequest.getLocale(), formValues, true);
 
             updateProfileBean.getFormData().putAll(FormUtility.asStringMap(formValues));
         } catch (PwmOperationalException e) {
@@ -214,13 +223,10 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
             userMessage = e.getErrorInformation().toUserStr(pwmRequest.getPwmSession(), pwmRequest.getPwmApplication());
         }
 
-        final LinkedHashMap<String, String> outputMap = new LinkedHashMap<>();
-        outputMap.put("version", "1");
-        outputMap.put("message", userMessage);
-        outputMap.put("success", String.valueOf(success));
-
-        pwmRequest.outputJsonResult(new RestResultBean(outputMap));
-
+        final ValidateResponse response = new ValidateResponse();
+        response.setMessage(userMessage);
+        response.setSuccess(success);
+        pwmRequest.outputJsonResult(RestResultBean.withData(response));
         return ProcessStatus.Halt;
     }
 
@@ -291,8 +297,7 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
         return ProcessStatus.Continue;
     }
 
-    protected void nextStep(
-            final PwmRequest pwmRequest)
+    protected void nextStep(final PwmRequest pwmRequest)
             throws IOException, ServletException, PwmUnrecoverableException, ChaiUnavailableException
     {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
@@ -335,7 +340,7 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
             // verify form meets the form requirements
             final List<FormConfiguration> formFields = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
             final Map<FormConfiguration, String> formValues = FormUtility.readFormValuesFromMap(updateProfileBean.getFormData(), formFields, pwmRequest.getLocale());
-            verifyFormAttributes(pwmRequest, formValues, true);
+            verifyFormAttributes(pwmRequest.getPwmApplication(), pwmRequest.getUserInfoIfLoggedIn(), pwmRequest.getLocale(), formValues, true);
         } catch (PwmException e) {
             LOGGER.error(pwmSession, e.getMessage());
             setLastError(pwmRequest, e.getErrorInformation());
@@ -369,7 +374,29 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
         try {
             // write the form values
             final ChaiUser theUser = pwmSession.getSessionManager().getActor(pwmApplication);
-            doProfileUpdate(pwmRequest, updateProfileBean.getFormData(), theUser);
+            doProfileUpdate(
+                    pwmRequest.getPwmApplication(),
+                    pwmRequest.getSessionLabel(),
+                    pwmRequest.getLocale(),
+                    pwmSession.getUserInfo(),
+                    pwmSession.getSessionManager().getMacroMachine(pwmApplication),
+                    updateAttributesProfile,
+                    updateProfileBean.getFormData(),
+                    theUser
+            );
+
+            // re-populate the uiBean because we have changed some values.
+            pwmSession.reloadUserInfoBean(pwmApplication);
+
+            // clear cached read attributes.
+            pwmRequest.getPwmSession().reloadUserInfoBean(pwmApplication);
+
+            // mark the event log
+            pwmApplication.getAuditManager().submit(AuditEvent.UPDATE_PROFILE, pwmSession.getUserInfo(), pwmSession);
+
+            // clear the bean
+            pwmApplication.getSessionStateService().clearBean(pwmRequest, UpdateProfileBean.class);
+
             pwmRequest.getPwmResponse().forwardToSuccessPage(Message.Success_UpdateProfile);
             return;
         } catch (PwmException e) {
@@ -456,79 +483,61 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
 
 
     public static void doProfileUpdate(
-            final PwmRequest pwmRequest,
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final Locale locale,
+            final UserInfo userInfo,
+            final MacroMachine macroMachine,
+            final UpdateAttributesProfile updateAttributesProfile,
             final Map<String,String> formValues,
             final ChaiUser theUser
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
     {
-        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
-        final PwmSession pwmSession = pwmRequest.getPwmSession();
-        final UserInfo userInfo = pwmRequest.getPwmSession().getUserInfo();
-        final UpdateAttributesProfile updateAttributesProfile = pwmRequest.getPwmSession().getSessionManager().getUpdateAttributeProfile(pwmApplication);
-
         final List<FormConfiguration> formFields = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
-        final Map<FormConfiguration, String> formMap = FormUtility.readFormValuesFromMap(formValues, formFields, pwmRequest.getLocale());
+        final Map<FormConfiguration, String> formMap = FormUtility.readFormValuesFromMap(formValues, formFields, locale);
 
         // verify form meets the form requirements (may be redundant, but shouldn't hurt)
-        verifyFormAttributes(pwmRequest, formMap, false);
+        verifyFormAttributes(pwmApplication, userInfo.getUserIdentity(), locale, formMap, false);
 
         // write values.
-        LOGGER.info("updating profile for " + pwmRequest.getPwmSession().getUserInfo().getUserIdentity());
-
-        pwmRequest.getPwmSession().getSessionManager().getChaiProvider();
+        LOGGER.info("updating profile for " + userInfo.getUserIdentity());
 
-        LdapOperationsHelper.writeFormValuesToLdap(pwmRequest.getPwmApplication(), pwmRequest.getPwmSession(), theUser, formMap, false);
+        LdapOperationsHelper.writeFormValuesToLdap(pwmApplication, macroMachine, theUser, formMap, false);
 
         final UserIdentity userIdentity = userInfo.getUserIdentity();
 
-        // re-populate the uiBean because we have changed some values.
-        pwmSession.reloadUserInfoBean(pwmApplication);
-
-        // clear cached read attributes.
-        pwmRequest.getPwmSession().reloadUserInfoBean(pwmApplication);
-
         {  // execute configured actions
             final List<ActionConfiguration> actions = updateAttributesProfile.readSettingAsAction(PwmSetting.UPDATE_PROFILE_WRITE_ATTRIBUTES);
             if (actions != null && !actions.isEmpty()) {
-                LOGGER.debug(pwmRequest, "executing configured actions to user " + userIdentity);
+                LOGGER.debug(sessionLabel, "executing configured actions to user " + userIdentity);
 
 
                 final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings(pwmApplication, userIdentity)
                         .setExpandPwmMacros(true)
-                        .setMacroMachine(pwmSession.getSessionManager().getMacroMachine(pwmApplication))
+                        .setMacroMachine(macroMachine)
                         .createActionExecutor();
 
-                actionExecutor.executeActions(actions, pwmSession);
+                actionExecutor.executeActions(actions, sessionLabel);
             }
         }
-        final boolean verification = updateAttributesProfile.readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_EMAIL_VERIFICATION);
-        sendProfileUpdateEmailNotice(pwmSession, pwmApplication, verification);
-
-        // mark the event log
-        pwmApplication.getAuditManager().submit(AuditEvent.UPDATE_PROFILE, pwmSession.getUserInfo(), pwmSession);
-
-        // mark the uiBean so we user isn't recycled to the update profile page by the CommandServlet
-        pwmSession.reloadUserInfoBean(pwmApplication);
-
-        // clear out the updateProfileBean
-        pwmApplication.getSessionStateService().clearBean(pwmRequest, UpdateProfileBean.class);
+        sendProfileUpdateEmailNotice(pwmApplication, macroMachine, userInfo, locale, sessionLabel);
 
         // success, so forward to success page
         pwmApplication.getStatisticsManager().incrementValue(Statistic.UPDATE_ATTRIBUTES);
     }
 
     private static void verifyFormAttributes(
-            final PwmRequest pwmRequest,
+            final PwmApplication pwmApplication,
+            final UserIdentity userIdentity,
+            final Locale userLocale,
             final Map<FormConfiguration, String> formValues,
             final boolean allowResultCaching
     )
             throws PwmOperationalException, PwmUnrecoverableException
     {
-        final Locale userLocale = pwmRequest.getLocale();
-
         // see if the values meet form requirements.
-        FormUtility.validateFormValues(pwmRequest.getConfig(), formValues, userLocale);
+        FormUtility.validateFormValues(pwmApplication.getConfig(), formValues, userLocale);
 
         final List<FormUtility.ValidationFlag> validationFlags = new ArrayList<>();
         if (allowResultCaching) {
@@ -537,33 +546,34 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
 
         // check unique fields against ldap
         FormUtility.validateFormValueUniqueness(
-                pwmRequest.getPwmApplication(),
+                pwmApplication,
                 formValues,
                 userLocale,
-                Collections.singletonList(pwmRequest.getPwmSession().getUserInfo().getUserIdentity()),
+                Collections.singletonList(userIdentity),
                 validationFlags.toArray(new FormUtility.ValidationFlag[validationFlags.size()])
         );
     }
 
     private static void sendProfileUpdateEmailNotice(
-            final PwmSession pwmSession,
             final PwmApplication pwmApplication,
-            final boolean verification
+            final MacroMachine macroMachine,
+            final UserInfo userInfo,
+            final Locale locale,
+            final SessionLabel sessionLabel
     )
-            throws PwmUnrecoverableException, ChaiUnavailableException {
+            throws PwmUnrecoverableException, ChaiUnavailableException
+    {
         final Configuration config = pwmApplication.getConfig();
-        final Locale locale = pwmSession.getSessionStateBean().getLocale();
 
         final EmailItemBean configuredEmailSetting = config.readSettingAsEmail(PwmSetting.EMAIL_UPDATEPROFILE, locale);
         pwmApplication.getEmailQueue().submitEmail(
                 configuredEmailSetting,
-                pwmSession.getUserInfo(),
-                pwmSession.getSessionManager().getMacroMachine(pwmApplication)
+                userInfo,
+                macroMachine
         );
 
         if (configuredEmailSetting == null) {
-            LOGGER.debug(pwmSession, "skipping send profile update email for '" + pwmSession.getUserInfo().getUserIdentity() + "' no email configured");
-            return;
+            LOGGER.debug(sessionLabel, "skipping send profile update email for '" + userInfo.getUserIdentity().toDisplayString() + "' no email configured");
         }
     }
 

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

@@ -34,9 +34,11 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.ProcessStatus;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmURL;
@@ -44,6 +46,7 @@ import password.pwm.http.bean.AdminBean;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.ControlledPwmServlet;
 import password.pwm.http.servlet.PwmServletDefinition;
+import password.pwm.i18n.Message;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.event.AuditRecord;
 import password.pwm.svc.event.HelpdeskAuditRecord;
@@ -53,7 +56,9 @@ import password.pwm.svc.intruder.RecordType;
 import password.pwm.svc.report.ReportColumnFilter;
 import password.pwm.svc.report.ReportCsvUtility;
 import password.pwm.svc.report.ReportService;
+import password.pwm.svc.report.UserCacheRecord;
 import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.util.java.ClosableIterator;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
@@ -102,6 +107,7 @@ public class AdminServlet extends ControlledPwmServlet {
         reportCommand(HttpMethod.POST),
         reportStatus(HttpMethod.GET),
         reportSummary(HttpMethod.GET),
+        reportData(HttpMethod.GET),
         downloadUserDebug(HttpMethod.GET),
         auditData(HttpMethod.GET),
         sessionData(HttpMethod.GET),
@@ -171,7 +177,7 @@ public class AdminServlet extends ControlledPwmServlet {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
         pwmRequest.getPwmResponse().markAsDownload(
-                PwmConstants.ContentTypeValue.csv,
+                HttpContentType.csv,
                 pwmApplication.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_AUDIT_RECORDS_CSV)
         );
 
@@ -196,7 +202,7 @@ public class AdminServlet extends ControlledPwmServlet {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
         pwmRequest.getPwmResponse().markAsDownload(
-                PwmConstants.ContentTypeValue.csv,
+                HttpContentType.csv,
                 pwmApplication.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_USER_REPORT_RECORDS_CSV)
         );
 
@@ -225,7 +231,7 @@ public class AdminServlet extends ControlledPwmServlet {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
         pwmRequest.getPwmResponse().markAsDownload(
-                PwmConstants.ContentTypeValue.csv,
+                HttpContentType.csv,
                 pwmApplication.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_USER_REPORT_SUMMARY_CSV)
         );
 
@@ -249,7 +255,7 @@ public class AdminServlet extends ControlledPwmServlet {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
 
         pwmRequest.getPwmResponse().markAsDownload(
-                PwmConstants.ContentTypeValue.csv,
+                HttpContentType.csv,
                 pwmRequest.getPwmApplication().getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_STATISTICS_CSV)
         );
 
@@ -279,7 +285,7 @@ public class AdminServlet extends ControlledPwmServlet {
 
         //pwmApplication.getIntruderManager().clear();
 
-        final RestResultBean restResultBean = new RestResultBean();
+        final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -295,7 +301,7 @@ public class AdminServlet extends ControlledPwmServlet {
         LOGGER.trace(pwmRequest, "issuing command '" + reportCommand + "' to report engine");
         pwmRequest.getPwmApplication().getReportService().executeCommand(reportCommand);
 
-        final RestResultBean restResultBean = new RestResultBean();
+        final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -308,8 +314,7 @@ public class AdminServlet extends ControlledPwmServlet {
                     pwmRequest.getPwmApplication().getReportService(),
                     pwmRequest.getPwmSession().getSessionStateBean().getLocale()
             );
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(returnMap);
+            final RestResultBean restResultBean = RestResultBean.withData(returnMap);
             pwmRequest.outputJsonResult(restResultBean);
         } catch (LocalDBException e) {
             throw new PwmUnrecoverableException(e.getErrorInformation());
@@ -319,8 +324,8 @@ public class AdminServlet extends ControlledPwmServlet {
 
     @ActionHandler(action = "reportSummary")
     private ProcessStatus processReportSummary(final PwmRequest pwmRequest)
-
-            throws ChaiUnavailableException, PwmUnrecoverableException, IOException {
+            throws ChaiUnavailableException, PwmUnrecoverableException, IOException
+    {
         final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
         final LinkedHashMap<String,Object> returnMap = new LinkedHashMap<>();
         returnMap.put("raw",pwmApplication.getReportService().getSummaryData());
@@ -329,8 +334,38 @@ public class AdminServlet extends ControlledPwmServlet {
                 pwmRequest.getPwmSession().getSessionStateBean().getLocale()
         ));
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(returnMap);
+        final RestResultBean restResultBean = RestResultBean.withData(returnMap);
+        pwmRequest.outputJsonResult(restResultBean);
+        return ProcessStatus.Halt;
+    }
+
+    @ActionHandler(action = "reportData")
+    public ProcessStatus processReportData(final PwmRequest pwmRequest)
+            throws PwmUnrecoverableException, IOException
+    {
+        final int maximum = Math.min(pwmRequest.readParameterAsInt("maximum", 1000), 10 * 1000);
+
+        final ReportService reportService = pwmRequest.getPwmApplication().getReportService();
+        final ArrayList<UserCacheRecord> reportData = new ArrayList<>();
+        ClosableIterator<UserCacheRecord> cacheBeanIterator = null;
+        try {
+            cacheBeanIterator = reportService.iterator();
+            while (cacheBeanIterator.hasNext() && reportData.size() < maximum) {
+                final UserCacheRecord userCacheRecord = cacheBeanIterator.next();
+                if (userCacheRecord != null) {
+                    reportData.add(userCacheRecord);
+                }
+            }
+        } finally {
+            if (cacheBeanIterator != null) {
+                cacheBeanIterator.close();
+            }
+        }
+
+        final HashMap<String,Object> returnData = new HashMap<>();
+        returnData.put("users",reportData);
+
+        final RestResultBean restResultBean = RestResultBean.withData(returnData);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -345,7 +380,7 @@ public class AdminServlet extends ControlledPwmServlet {
         final UserIdentity userIdentity = adminBean.getLastUserDebug();
         if (userIdentity != null) {
             pwmRequest.getPwmResponse().markAsDownload(
-                    PwmConstants.ContentTypeValue.json,
+                    HttpContentType.json,
                     pwmApplication.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_USER_DEBUG_JSON)
             );
             final UserDebugDataBean userDebugData = UserDebugDataReader.readUserDebugData(pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getSessionLabel(), userIdentity);
@@ -384,8 +419,7 @@ public class AdminServlet extends ControlledPwmServlet {
         outputMap.put("helpdesk",helpdeskRecords);
         outputMap.put("system",systemRecords);
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(outputMap);
+        final RestResultBean restResultBean = RestResultBean.withData(outputMap);
         LOGGER.debug(pwmRequest.getPwmSession(),"output " + counter + " audit records.");
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
@@ -403,8 +437,7 @@ public class AdminServlet extends ControlledPwmServlet {
             gridData.add(infos.next());
             counter++;
         }
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(gridData);
+        final RestResultBean restResultBean = RestResultBean.withData(gridData);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -425,8 +458,7 @@ public class AdminServlet extends ControlledPwmServlet {
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInfo));
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(returnData);
+        final RestResultBean restResultBean = RestResultBean.withData(returnData);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -449,7 +481,7 @@ public class AdminServlet extends ControlledPwmServlet {
             jsonOutput.keyData = RestStatisticsServer.doKeyStat(statisticsManager, statKey);
         }
 
-        final RestResultBean restResultBean = new RestResultBean(jsonOutput);
+        final RestResultBean restResultBean = RestResultBean.withData(jsonOutput);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -457,7 +489,7 @@ public class AdminServlet extends ControlledPwmServlet {
     private void processDebugUserSearch(final PwmRequest pwmRequest)
             throws PwmUnrecoverableException
     {
-        final String username = pwmRequest.readParameterAsString("username");
+        final String username = pwmRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation);
         if (StringUtil.isEmpty(username)) {
             return;
         }
@@ -468,8 +500,8 @@ public class AdminServlet extends ControlledPwmServlet {
             userIdentity = userSearchEngine.resolveUsername(username,null,null, pwmRequest.getSessionLabel());
             final AdminBean adminBean = pwmRequest.getPwmApplication().getSessionStateService().getBean(pwmRequest, AdminBean.class);
             adminBean.setLastUserDebug(userIdentity);
-        } catch (ChaiUnavailableException e) {
-            setLastError(pwmRequest, PwmUnrecoverableException.fromChaiException(e).getErrorInformation());
+        } catch (PwmUnrecoverableException e) {
+            setLastError(pwmRequest, e.getErrorInformation());
             return;
         } catch (PwmOperationalException e) {
             setLastError(pwmRequest, e.getErrorInformation());
@@ -481,7 +513,7 @@ public class AdminServlet extends ControlledPwmServlet {
     }
 
     private void processThreadPageView(final PwmRequest pwmRequest) throws IOException {
-        pwmRequest.getPwmResponse().setContentType(PwmConstants.ContentTypeValue.plain);
+        pwmRequest.getPwmResponse().setContentType(HttpContentType.plain);
         final Writer writer = pwmRequest.getPwmResponse().getWriter();
         final ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true,true);
         for (final ThreadInfo threadInfo : threads) {
@@ -510,8 +542,8 @@ public class AdminServlet extends ControlledPwmServlet {
     private static int readMaxParameter(final PwmRequest pwmRequest, final int defaultValue, final int maxValue)
             throws PwmUnrecoverableException
     {
-            final String stringMax = pwmRequest.readParameterAsString("maximum",String.valueOf(defaultValue));
-            return Math.max(Integer.parseInt(stringMax), maxValue);
+        final String stringMax = pwmRequest.readParameterAsString("maximum",String.valueOf(defaultValue));
+        return Math.max(Integer.parseInt(stringMax), maxValue);
     }
 
     public enum Page {

+ 14 - 8
server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java

@@ -306,9 +306,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
             );
             passwordChangeProgress = checker.figureProgress(progressTracker);
         }
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(passwordChangeProgress);
-
+        final RestResultBean restResultBean = RestResultBean.withData(passwordChangeProgress);
         LOGGER.trace(pwmRequest, "returning result for restCheckProgress: " + JsonUtil.serialize(restResultBean));
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
@@ -376,12 +374,20 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
                 userInfo
         );
 
-        final RestCheckPasswordServer.JsonOutput checkResult = RestCheckPasswordServer.doPasswordRuleCheck(
+        final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
                 pwmRequest.getPwmApplication(),
-                pwmRequest.getPwmSession(),
-                passwordCheckRequest);
+                pwmRequest.getLocale(),
+                pwmRequest.getPwmSession().getSessionManager().getActor(pwmRequest.getPwmApplication()),
+                userInfo,
+                pwmRequest.getPwmSession().getLoginInfoBean(),
+                PasswordData.forStringValue(jsonInput.getPassword1()),
+                PasswordData.forStringValue(jsonInput.getPassword1())
+        );
+
+
+        final RestCheckPasswordServer.JsonOutput checkResult = RestCheckPasswordServer.JsonOutput.fromPasswordCheckInfo(passwordCheckInfo);
 
-        final RestResultBean restResultBean = new RestResultBean(checkResult);
+        final RestResultBean restResultBean = RestResultBean.withData(checkResult);
         pwmRequest.outputJsonResult(restResultBean);
 
         return ProcessStatus.Halt;
@@ -393,7 +399,7 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         final PasswordData passwordData = RandomPasswordGenerator.createRandomPassword(pwmRequest.getPwmSession(), pwmRequest.getPwmApplication());
         final RestRandomPasswordServer.JsonOutput jsonOutput = new RestRandomPasswordServer.JsonOutput();
         jsonOutput.setPassword(passwordData.getStringValue());
-        final RestResultBean restResultBean = new RestResultBean(jsonOutput);
+        final RestResultBean restResultBean = RestResultBean.withData(jsonOutput);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/command/CommandServlet.java

@@ -31,6 +31,7 @@ import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.ProcessStatus;
@@ -113,7 +114,7 @@ public abstract class CommandServlet extends ControlledPwmServlet {
         pwmRequest.validatePwmFormID();
         if (!pwmRequest.getPwmResponse().isCommitted()) {
             pwmRequest.getPwmResponse().setHeader(HttpHeader.Cache_Control, "no-cache, no-store, must-revalidate");
-            pwmRequest.getPwmResponse().setContentType(PwmConstants.ContentTypeValue.plain);
+            pwmRequest.getPwmResponse().setContentType(HttpContentType.plain);
         }
         return ProcessStatus.Halt;
     }
@@ -157,7 +158,7 @@ public abstract class CommandServlet extends ControlledPwmServlet {
         LOGGER.debug("pageLeaveNotice indicated at " + pageLeaveNoticeTime.toString() + ", referer=" + referrer);
         if (!pwmRequest.getPwmResponse().isCommitted()) {
             pwmRequest.getPwmResponse().setHeader(HttpHeader.Cache_Control, "no-cache, no-store, must-revalidate");
-            pwmRequest.getPwmResponse().setContentType(PwmConstants.ContentTypeValue.plain);
+            pwmRequest.getPwmResponse().setContentType(HttpContentType.plain);
         }
         return ProcessStatus.Halt;
     }

+ 24 - 36
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java

@@ -280,19 +280,15 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
             final Class implementingClass = Class.forName(functionName);
             final SettingUIFunction function = (SettingUIFunction) implementingClass.newInstance();
             final Serializable result = function.provideFunction(pwmRequest, configManagerBean.getStoredConfiguration(), pwmSetting, profileID, extraData);
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setSuccessMessage(Message.Success_Unknown.getLocalizedMessage(pwmRequest.getLocale(),pwmRequest.getConfig()));
-            restResultBean.setData(result);
+            final RestResultBean restResultBean = RestResultBean.forSuccessMessage(result, pwmRequest, Message.Success_Unknown);
             pwmRequest.outputJsonResult(restResultBean);
         } catch (Exception e) {
             final RestResultBean restResultBean;
             if (e instanceof PwmException) {
                 restResultBean = RestResultBean.fromError(((PwmException) e).getErrorInformation(), pwmRequest, true);
             } else {
-                restResultBean = new RestResultBean();
-                restResultBean.setError(true);
-                restResultBean.setErrorDetail(e.getMessage());
-                restResultBean.setErrorMessage("error performing user search: " + e.getMessage());
+                final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, "error performing user search: " + e.getMessage());
+                restResultBean = RestResultBean.fromError(errorInformation, pwmRequest);
             }
             pwmRequest.outputJsonResult(restResultBean);
         }
@@ -396,7 +392,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
             returnMap.put("syntax", theSetting.getSyntax().toString());
         }
         returnMap.put("value", returnValue);
-        pwmRequest.outputJsonResult(new RestResultBean(returnMap));
+        pwmRequest.outputJsonResult(RestResultBean.withData(returnMap));
     }
 
     private void restWriteSetting(
@@ -443,7 +439,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
             returnMap.put("syntax", setting.getSyntax().toString());
             returnMap.put("isDefault", storedConfig.isDefaultValue(setting, profileID));
         }
-        pwmRequest.outputJsonResult(new RestResultBean(returnMap));
+        pwmRequest.outputJsonResult(RestResultBean.withData(returnMap));
     }
 
     private void restResetSetting(
@@ -495,31 +491,29 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
             throws IOException, ServletException, PwmUnrecoverableException {
         final PwmSession pwmSession = pwmRequest.getPwmSession();
 
-        RestResultBean restResultBean = new RestResultBean();
-        final HashMap<String, String> resultData = new HashMap<>();
-        restResultBean.setData(resultData);
 
         final List<String> validationErrors = configManagerBean.getStoredConfiguration().validateValues();
         if (!validationErrors.isEmpty()) {
             final String errorString = validationErrors.get(0);
             final ErrorInformation errorInfo = new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR, errorString, new String[]{errorString});
-            restResultBean = RestResultBean.fromError(errorInfo, pwmRequest);
-            LOGGER.error(pwmSession, "save configuration aborted, error: " + errorString);
+            pwmRequest.outputJsonResult(RestResultBean.fromError(errorInfo, pwmRequest));
+            LOGGER.error(pwmSession, errorInfo);
+            return;
         } else {
             try {
                 ConfigManagerServlet.saveConfiguration(pwmRequest, configManagerBean.getStoredConfiguration());
                 configManagerBean.setConfiguration(null);
-                restResultBean.setError(false);
                 configManagerBean.setConfiguration(null);
                 LOGGER.debug(pwmSession, "save configuration operation completed");
+                pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
             } catch (PwmUnrecoverableException e) {
                 final ErrorInformation errorInfo = e.getErrorInformation();
-                restResultBean = RestResultBean.fromError(errorInfo, pwmRequest);
-                LOGGER.warn(pwmSession, "unable to save configuration: " + e.getMessage());
+                pwmRequest.outputJsonResult(RestResultBean.fromError(errorInfo, pwmRequest));
+                LOGGER.error(pwmSession, errorInfo);
+                return;
             }
         }
 
-        pwmRequest.outputJsonResult(restResultBean);
     }
 
     private void restCancelEditing(
@@ -593,8 +587,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
             LOGGER.error(pwmRequest, "error generating health records: " + e.getMessage());
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(returnObj);
+        final RestResultBean restResultBean = RestResultBean.withData(returnObj);
         pwmRequest.outputJsonResult(restResultBean);
     }
 
@@ -608,7 +601,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         final String bodyData = pwmRequest.readRequestBodyAsString();
         final Map<String, String> valueMap = JsonUtil.deserializeStringMap(bodyData);
         final Locale locale = pwmRequest.getLocale();
-        final RestResultBean restResultBean = new RestResultBean();
+        final RestResultBean restResultBean;
         final String searchTerm = valueMap.get("search");
         final StoredConfigurationImpl storedConfiguration = configManagerBean.getStoredConfiguration();
 
@@ -643,10 +636,10 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
                 outputMap.put(key, new TreeMap<>(returnData.get(key)));
             }
 
-            restResultBean.setData(outputMap);
+            restResultBean = RestResultBean.withData(outputMap);
             LOGGER.trace(pwmRequest, "finished search operation with " + returnData.size() + " results in " + TimeDuration.fromCurrent(startTime).asCompactString());
         } else {
-            restResultBean.setData(new ArrayList<StoredConfigurationImpl.ConfigRecordID>());
+            restResultBean = RestResultBean.withData(new ArrayList<StoredConfigurationImpl.ConfigRecordID>());
         }
 
         pwmRequest.outputJsonResult(restResultBean);
@@ -663,8 +656,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         final String profileID = pwmRequest.readParameterAsString("profile");
         final Configuration config = new Configuration(configManagerBean.getStoredConfiguration());
         final HealthData healthData = LDAPStatusChecker.healthForNewConfiguration(pwmRequest.getPwmApplication(), config, pwmRequest.getLocale(), profileID, true, true);
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(healthData);
+        final RestResultBean restResultBean = RestResultBean.withData(healthData);
 
         pwmRequest.outputJsonResult(restResultBean);
         LOGGER.debug(pwmRequest, "completed restLdapHealthCheck in " + TimeDuration.fromCurrent(startTime).asCompactString());
@@ -680,8 +672,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         final Configuration config = new Configuration(configManagerBean.getStoredConfiguration());
         final List<HealthRecord> healthRecords = DatabaseStatusChecker.checkNewDatabaseStatus(pwmRequest.getPwmApplication(), config);
         final HealthData healthData = HealthRecord.asHealthDataBean(config, pwmRequest.getLocale(), healthRecords);
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(healthData);
+        final RestResultBean restResultBean = RestResultBean.withData(healthData);
         pwmRequest.outputJsonResult(restResultBean);
         LOGGER.debug(pwmRequest, "completed restDatabaseHealthCheck in " + TimeDuration.fromCurrent(startTime).asCompactString());
     }
@@ -711,9 +702,8 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
             }
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
         final HealthData healthData = HealthRecord.asHealthDataBean(config, pwmRequest.getLocale(), returnRecords);
-        restResultBean.setData(healthData);
+        final RestResultBean restResultBean = RestResultBean.withData(healthData);
         pwmRequest.outputJsonResult(restResultBean);
         LOGGER.debug(pwmRequest, "completed restSmsHealthCheck in " + TimeDuration.fromCurrent(startTime).asCompactString());
     }
@@ -876,7 +866,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         NavTreeHelper.moveNavItemToTopOfList(PwmSettingCategory.TEMPLATES.toString(), navigationData);
 
         LOGGER.trace(pwmRequest,"completed navigation tree data request in " + TimeDuration.fromCurrent(startTime).asCompactString());
-        pwmRequest.outputJsonResult(new RestResultBean(navigationData));
+        pwmRequest.outputJsonResult(RestResultBean.withData(navigationData));
     }
 
     private void restConfigSettingData(final PwmRequest pwmRequest, final ConfigManagerBean configManagerBean)
@@ -924,9 +914,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
             returnMap.put("var", varMap);
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
-
-        restResultBean.setData(returnMap);
+        final RestResultBean restResultBean = RestResultBean.withData(returnMap);
         pwmRequest.outputJsonResult(restResultBean);
     }
 
@@ -934,7 +922,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
         try {
             final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
             if (inputMap == null || !inputMap.containsKey("input")) {
-                pwmRequest.outputJsonResult(new RestResultBean("missing input"));
+                pwmRequest.outputJsonResult(RestResultBean.withData("missing input"));
                 return;
             }
 
@@ -946,7 +934,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
             }
             final String input = inputMap.get("input");
             final String output = macroMachine.expandMacros(input);
-            pwmRequest.outputJsonResult(new RestResultBean(output));
+            pwmRequest.outputJsonResult(RestResultBean.withData(output));
         } catch (PwmUnrecoverableException e) {
             LOGGER.error(pwmRequest, e.getErrorInformation());
             pwmRequest.respondWithError(e.getErrorInformation());
@@ -977,7 +965,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
                 + TimeDuration.fromCurrent(startTime).asCompactString()
                 + ", result=" + JsonUtil.serialize(result));
 
-        pwmRequest.outputJsonResult(new RestResultBean(result));
+        pwmRequest.outputJsonResult(RestResultBean.withData(result));
     }
 
     private void restCopyProfile(final PwmRequest pwmRequest, final ConfigManagerBean configManagerBean)

+ 12 - 15
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java

@@ -64,6 +64,7 @@ import password.pwm.http.PwmURL;
 import password.pwm.http.bean.ConfigGuideBean;
 import password.pwm.http.servlet.AbstractPwmServlet;
 import password.pwm.http.servlet.configeditor.ConfigEditorServlet;
+import password.pwm.i18n.Message;
 import password.pwm.ldap.LdapBrowser;
 import password.pwm.ldap.schema.SchemaManager;
 import password.pwm.ldap.schema.SchemaOperationResult;
@@ -273,8 +274,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
                     }
                     writeConfig(ContextManager.getContextManager(req.getSession()),storedConfig);
                     LOGGER.trace(pwmSession, "read config from file: " + storedConfig.toString());
-                    final RestResultBean restResultBean = new RestResultBean();
-                    restResultBean.setSuccessMessage("read message");
+                    final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
                     pwmRequest.getPwmResponse().outputJsonResult(restResultBean);
                     req.getSession().invalidate();
                 } catch (PwmException e) {
@@ -300,7 +300,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
     {
         final boolean value = Boolean.parseBoolean(pwmRequest.readParameterAsString("value"));
         configGuideBean.setUseConfiguredCerts(value);
-        pwmRequest.outputJsonResult(new RestResultBean());
+        pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
     }
 
 
@@ -398,8 +398,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
                 pwmRequest.getLocale(), tempConfiguration);
         jsonOutput.timestamp = Instant.now();
         jsonOutput.overall = HealthMonitor.getMostSevereHealthStatus(records).toString();
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(jsonOutput);
+        final RestResultBean restResultBean = RestResultBean.withData(jsonOutput);
         pwmRequest.outputJsonResult(restResultBean);
     }
 
@@ -420,7 +419,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
             final UserMatchViewerFunction userMatchViewerFunction = new UserMatchViewerFunction();
             final StoredConfigurationImpl storedConfiguration = ConfigGuideForm.generateStoredConfig(configGuideBean);
             final Serializable output = userMatchViewerFunction.provideFunction(pwmRequest, storedConfiguration, PwmSetting.QUERY_MATCH_PWM_ADMIN, null, null);
-            pwmRequest.outputJsonResult(new RestResultBean(output));
+            pwmRequest.outputJsonResult(RestResultBean.withData(output));
         } catch (PwmException e) {
             LOGGER.error(pwmRequest,e.getErrorInformation());
             pwmRequest.respondWithError(e.getErrorInformation());
@@ -456,7 +455,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
                 + TimeDuration.fromCurrent(startTime).asCompactString()
                 + ", result=" + JsonUtil.serialize(result));
 
-        pwmRequest.outputJsonResult(new RestResultBean(result));
+        pwmRequest.outputJsonResult(RestResultBean.withData(result));
     }
 
     private void restUpdateLdapForm(
@@ -473,8 +472,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
             configGuideBean.getFormData().putAll(incomingFormData);
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
-        pwmRequest.outputJsonResult(restResultBean);
+        pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
     }
 
     private void restGotoStep(final PwmRequest pwmRequest, final ConfigGuideBean configGuideBean)
@@ -530,11 +528,11 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
             }
             final HashMap<String,String> resultData = new HashMap<>();
             resultData.put("serverRestart","true");
-            pwmRequest.outputJsonResult(new RestResultBean(resultData));
+            pwmRequest.outputJsonResult(RestResultBean.withData(resultData));
             pwmRequest.invalidateSession();
         } else {
             configGuideBean.setStep(step);
-            pwmRequest.outputJsonResult(new RestResultBean());
+            pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
             LOGGER.trace("setting current step to: " + step);
         }
     }
@@ -623,7 +621,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
     {
         try {
             final SchemaOperationResult schemaOperationResult = extendSchema(configGuideBean, true);
-            pwmRequest.outputJsonResult(new RestResultBean(schemaOperationResult.getOperationLog()));
+            pwmRequest.outputJsonResult(RestResultBean.withData(schemaOperationResult.getOperationLog()));
         } catch (Exception e) {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN,e.getMessage());
             pwmRequest.outputJsonResult(RestResultBean.fromError(errorInformation, pwmRequest));
@@ -659,8 +657,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
             final int maxFileSize = Integer.parseInt(pwmRequest.getConfig().readAppProperty(AppProperty.CONFIG_MAX_JDBC_JAR_SIZE));
             final FileValue fileValue = ConfigEditorServlet.readFileUploadToSettingValue(pwmRequest, maxFileSize);
             configGuideBean.setDatabaseDriver(fileValue);
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setSuccessMessage("upload completed");
+            final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
             pwmRequest.getPwmResponse().outputJsonResult(restResultBean);
         } catch (PwmException e) {
             final RestResultBean restResultBean = RestResultBean.fromError(e.getErrorInformation(), pwmRequest);
@@ -678,7 +675,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
             storedConfiguration.writeConfigProperty(ConfigurationProperty.CONFIG_IS_EDITABLE, "true");
             storedConfiguration.setPassword(password);
             writeConfig(contextManager, storedConfiguration);
-            pwmRequest.outputJsonResult(new RestResultBean());
+            pwmRequest.outputJsonResult(RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown));
             pwmRequest.invalidateSession();
         } catch (PwmOperationalException e) {
             LOGGER.error("error during skip config guide: " + e.getMessage(),e);

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

@@ -95,7 +95,7 @@ public class ConfigManagerCertificatesServlet extends AbstractPwmServlet {
         final ArrayList<CertificateDebugDataItem> certificateDebugDataItems = new ArrayList<>(makeCertificateDebugData(pwmRequest.getConfig()));
 
         if (action != null && action == ConfigManagerCertificateAction.certificateData) {
-            final RestResultBean restResultBean = new RestResultBean(certificateDebugDataItems);
+            final RestResultBean restResultBean = RestResultBean.withData(certificateDebugDataItems);
             pwmRequest.outputJsonResult(restResultBean);
             return;
         }

+ 2 - 1
server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLocalDBServlet.java

@@ -34,6 +34,7 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.ContextManager;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
@@ -132,7 +133,7 @@ public class ConfigManagerLocalDBServlet extends AbstractPwmServlet {
         final PwmResponse resp = pwmRequest.getPwmResponse();
         final Instant startTime = Instant.now();
         resp.setHeader(HttpHeader.ContentDisposition, "attachment;filename=" + PwmConstants.PWM_APP_NAME + "-LocalDB.bak");
-        resp.setContentType(PwmConstants.ContentTypeValue.octetstream);
+        resp.setContentType(HttpContentType.octetstream);
         resp.setHeader(HttpHeader.ContentTransferEncoding, "binary");
         final LocalDBUtility localDBUtility = new LocalDBUtility(pwmRequest.getPwmApplication().getLocalDB());
         try {

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

@@ -37,6 +37,7 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.ContextManager;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
@@ -251,7 +252,7 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
         }
         final HashMap<String,String> resultData = new HashMap<>();
         LOGGER.info(pwmSession, "Configuration Locked");
-        pwmRequest.outputJsonResult(new RestResultBean(resultData));
+        pwmRequest.outputJsonResult(RestResultBean.withData(resultData));
     }
 
     public static void saveConfiguration(
@@ -313,7 +314,7 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
             final StoredConfigurationImpl storedConfiguration = readCurrentConfiguration(pwmRequest);
             final OutputStream responseWriter = resp.getOutputStream();
             resp.setHeader(HttpHeader.ContentDisposition, "attachment;filename=" + PwmConstants.DEFAULT_CONFIG_FILE_FILENAME);
-            resp.setContentType(PwmConstants.ContentTypeValue.xml);
+            resp.setContentType(HttpContentType.xml);
             storedConfiguration.toXml(responseWriter);
             responseWriter.close();
         } catch (Exception e) {
@@ -326,7 +327,7 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
     {
         final PwmResponse resp = pwmRequest.getPwmResponse();
         resp.setHeader(HttpHeader.ContentDisposition, "attachment;filename=" + PwmConstants.PWM_APP_NAME + "-Support.zip");
-        resp.setContentType(PwmConstants.ContentTypeValue.zip);
+        resp.setContentType(HttpContentType.zip);
 
         final String pathPrefix = PwmConstants.PWM_APP_NAME + "-Support" + "/";
 
@@ -384,7 +385,7 @@ public class ConfigManagerServlet extends AbstractPwmServlet {
             throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
     {
         pwmRequest.getPwmResponse().markAsDownload(
-                PwmConstants.ContentTypeValue.csv,
+                HttpContentType.csv,
                 pwmRequest.getConfig().readAppProperty(AppProperty.DOWNLOAD_FILENAME_LDAP_PERMISSION_CSV)
         );
 

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

@@ -227,7 +227,7 @@ public class ConfigManagerWordlistServlet extends AbstractPwmServlet {
             }
             outputData.put(wordlistType,wordlistDataBean);
         }
-        pwmRequest.outputJsonResult(new RestResultBean(outputData));
+        pwmRequest.outputJsonResult(RestResultBean.withData(outputData));
     }
 
     public static class WordlistDataBean implements Serializable {

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

@@ -84,7 +84,6 @@ import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.svc.token.TokenPayload;
 import password.pwm.svc.token.TokenType;
 import password.pwm.util.CaptchaUtility;
-import password.pwm.util.LocaleHelper;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PostChangePasswordAction;
 import password.pwm.util.RandomPasswordGenerator;
@@ -717,8 +716,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             ForgottenPasswordUtil.initializeAndSendToken(pwmRequest, userInfo, tokenSendMethod);
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setSuccessMessage(LocaleHelper.getLocalizedMessage(Message.Success_TokenResend, pwmRequest));
+        final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_TokenResend);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -1156,7 +1154,7 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
                                 .setExpandPwmMacros(true)
                                 .createActionExecutor();
 
-                        actionExecutor.executeActions(configValues, pwmSession);
+                        actionExecutor.executeActions(configValues, pwmSession.getLabel());
                     }
                 } catch (PwmOperationalException e) {
                     final ErrorInformation info = new ErrorInformation(PwmError.ERROR_UNKNOWN, e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues());

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

@@ -34,6 +34,7 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.client.PwmHttpClient;
@@ -130,7 +131,7 @@ public class RemoteVerificationMethod implements VerificationMethodSystem {
         lastResponse = null;
 
         final Map<String, String> headers = new LinkedHashMap<>();
-        headers.put(HttpHeader.Content_Type.getHttpName(), PwmConstants.ContentTypeValue.json.getHeaderValue());
+        headers.put(HttpHeader.Content_Type.getHttpName(), HttpContentType.json.getHeaderValue());
         headers.put(HttpHeader.Accept_Language.getHttpName(), locale.toLanguageTag());
 
         final RemoteVerificationRequestBean remoteVerificationRequestBean = new RemoteVerificationRequestBean();

+ 19 - 25
server/src/main/java/password/pwm/http/servlet/helpdesk/HelpdeskServlet.java

@@ -247,7 +247,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             returnValues.setVerificationForm(formInformations);
         }
 
-        final RestResultBean restResultBean = new RestResultBean(returnValues);
+        final RestResultBean restResultBean = RestResultBean.withData(returnValues);
         LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
@@ -303,7 +303,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
                     .setMacroMachine(macroMachine)
                     .createActionExecutor();
 
-            actionExecutor.executeAction(action, pwmRequest.getPwmSession());
+            actionExecutor.executeAction(action, pwmRequest.getSessionLabel());
 
             // mark the event log
             {
@@ -397,8 +397,8 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         }
 
         LOGGER.info(pwmSession, "user " + userIdentity + " has been deleted");
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmSession.getSessionStateBean().getLocale(), Message.Success_Unknown, pwmApplication.getConfig()));
+
+        final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -438,7 +438,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication()).canonicalized(pwmRequest.getPwmApplication());
         final HelpdeskDetailInfoBean helpdeskDetailInfoBean =  HelpdeskServletUtil.processDetailRequestImpl(pwmRequest, helpdeskProfile, userIdentity);
 
-        final RestResultBean restResultBean = new RestResultBean(helpdeskDetailInfoBean);
+        final RestResultBean restResultBean = RestResultBean.withData(helpdeskDetailInfoBean);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -460,8 +460,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             final HelpdeskSearchResultsBean emptyResults = new HelpdeskSearchResultsBean();
             emptyResults.setSearchResults(new ArrayList<>());
             emptyResults.setSizeExceeded(false);
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(emptyResults);
+            final RestResultBean restResultBean = RestResultBean.withData(emptyResults);
             pwmRequest.outputJsonResult(restResultBean);
             return ProcessStatus.Halt;
         }
@@ -499,16 +498,14 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             final ErrorInformation errorInformation = e.getErrorInformation();
             LOGGER.error(pwmRequest, errorInformation);
             final RestResultBean restResultBean = RestResultBean.fromError(errorInformation, pwmRequest);
-            restResultBean.setData(new ArrayList<Map<String, String>>());
             pwmRequest.outputJsonResult(restResultBean);
             return ProcessStatus.Halt;
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
         final HelpdeskSearchResultsBean outputData = new HelpdeskSearchResultsBean();
         outputData.setSearchResults(results.resultsAsJsonOutput(pwmRequest.getPwmApplication(), pwmRequest.getUserInfoIfLoggedIn()));
         outputData.setSizeExceeded(sizeExceeded);
-        restResultBean.setData(outputData);
+        final RestResultBean restResultBean = RestResultBean.withData(outputData);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -575,8 +572,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             return ProcessStatus.Halt;
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(), Message.Success_Unknown, pwmRequest.getConfig()));
+        final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -613,7 +609,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         final OTPUserRecord otpUserRecord = pwmRequest.getPwmApplication().getOtpService().readOTPUserConfiguration(pwmRequest.getSessionLabel(), userIdentity);
         try {
             final boolean passed = pwmRequest.getPwmApplication().getOtpService().validateToken(
-                    pwmRequest.getPwmSession(),
+                    pwmRequest.getSessionLabel(),
                     userIdentity,
                     otpUserRecord,
                     code,
@@ -656,7 +652,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             }
 
             final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
-            final RestResultBean restResultBean = new RestResultBean(responseBean);
+            final RestResultBean restResultBean = RestResultBean.withData(responseBean);
             pwmRequest.outputJsonResult(restResultBean);
         } catch (PwmOperationalException e) {
             pwmRequest.outputJsonResult(RestResultBean.fromError(e.getErrorInformation(), pwmRequest));
@@ -765,7 +761,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         final SecureService secureService = pwmRequest.getPwmApplication().getSecureService();
         helpdeskVerificationRequestBean.setTokenData(secureService.encryptObjectToString(tokenData));
 
-        final RestResultBean restResultBean = new RestResultBean(helpdeskVerificationRequestBean);
+        final RestResultBean restResultBean = RestResultBean.withData(helpdeskVerificationRequestBean);
         pwmRequest.outputJsonResult(restResultBean);
         LOGGER.debug(pwmRequest, "helpdesk operator "
                 + pwmRequest.getUserInfoIfLoggedIn().toDisplayString()
@@ -845,7 +841,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         }
 
         final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
-        final RestResultBean restResultBean = new RestResultBean(responseBean);
+        final RestResultBean restResultBean = RestResultBean.withData(responseBean);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -895,8 +891,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             return ProcessStatus.Halt;
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(), Message.Success_Unknown, pwmRequest.getConfig()));
+        final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -930,7 +925,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         final boolean passed = HelpdeskServletUtil.checkIfRequiredVerificationPassed(userIdentity, state, helpdeskProfile);
         final HashMap<String, Object> results = new HashMap<>();
         results.put("passed", passed);
-        final RestResultBean restResultBean = new RestResultBean(results);
+        final RestResultBean restResultBean = RestResultBean.withData(results);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -948,7 +943,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         } catch (ChaiOperationException e) {
             throw PwmUnrecoverableException.fromChaiException(e);
         }
-        final RestResultBean restResultBean = new RestResultBean(results);
+        final RestResultBean restResultBean = RestResultBean.withData(results);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -1033,7 +1028,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         }
 
         final HelpdeskVerificationResponseBean responseBean = new HelpdeskVerificationResponseBean(passed, verificationStateBean.toClientString(pwmRequest.getPwmApplication()));
-        final RestResultBean restResultBean = new RestResultBean(responseBean);
+        final RestResultBean restResultBean = RestResultBean.withData(responseBean);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -1093,8 +1088,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
             pwmRequest.getPwmApplication().getAuditManager().submit(auditRecord);
         }
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setSuccessMessage(Message.getLocalizedMessage(pwmRequest.getLocale(), Message.Success_Unknown, pwmRequest.getConfig()));
+        final RestResultBean restResultBean = RestResultBean.forSuccessMessage(pwmRequest, Message.Success_Unknown);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }
@@ -1141,7 +1135,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         );
 
         final RestCheckPasswordServer.JsonOutput jsonResponse = RestCheckPasswordServer.JsonOutput.fromPasswordCheckInfo(passwordCheckInfo);
-        final RestResultBean restResultBean = new RestResultBean(jsonResponse);
+        final RestResultBean restResultBean = RestResultBean.withData(jsonResponse);
         pwmRequest.outputJsonResult(restResultBean);
 
         return ProcessStatus.Halt;
@@ -1225,7 +1219,7 @@ public class HelpdeskServlet extends ControlledPwmServlet {
         final RestRandomPasswordServer.JsonOutput jsonOutput = new RestRandomPasswordServer.JsonOutput();
         jsonOutput.setPassword(randomPassword.getStringValue());
 
-        final RestResultBean restResultBean = new RestResultBean(jsonOutput);
+        final RestResultBean restResultBean = RestResultBean.withData(jsonOutput);
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
     }

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

@@ -352,8 +352,7 @@ public class NewUserServlet extends ControlledPwmServlet {
             final RestCheckPasswordServer.JsonOutput jsonData = RestCheckPasswordServer.JsonOutput.fromPasswordCheckInfo(
                     passwordCheckInfo);
 
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(jsonData);
+            final RestResultBean restResultBean = RestResultBean.withData(jsonData);
             pwmRequest.outputJsonResult(restResultBean);
         } catch (PwmOperationalException e) {
             final RestResultBean restResultBean = RestResultBean.fromError(e.getErrorInformation(), pwmRequest);
@@ -575,8 +574,7 @@ public class NewUserServlet extends ControlledPwmServlet {
         outputMap.put("percentComplete", percentComplete);
         outputMap.put("complete", complete);
 
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(outputMap);
+        final RestResultBean restResultBean = RestResultBean.withData(outputMap);
 
         LOGGER.trace(pwmRequest, "returning result for restCheckProgress: " + JsonUtil.serialize(restResultBean));
         pwmRequest.outputJsonResult(restResultBean);

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

@@ -276,7 +276,7 @@ class NewUserUtils {
                         .setMacroMachine(pwmSession.getSessionManager().getMacroMachine(pwmApplication))
                         .createActionExecutor();
 
-                actionExecutor.executeActions(actions, pwmSession);
+                actionExecutor.executeActions(actions, pwmSession.getLabel());
             }
         }
 

+ 2 - 2
server/src/main/java/password/pwm/http/servlet/oauth/OAuthMachine.java

@@ -29,7 +29,6 @@ import org.apache.http.client.methods.HttpPost;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.util.EntityUtils;
 import password.pwm.AppProperty;
-import password.pwm.PwmConstants;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.Configuration;
@@ -38,6 +37,7 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmURL;
@@ -240,7 +240,7 @@ public class OAuthMachine {
         httpPost.setHeader(HttpHeader.Authorization.getHttpName(),
                 new BasicAuthInfo(settings.getClientID(), settings.getSecret()).toAuthHeader());
         final StringEntity bodyEntity = new StringEntity(requestBody);
-        bodyEntity.setContentType(PwmConstants.ContentTypeValue.form.getHeaderValue());
+        bodyEntity.setContentType(HttpContentType.form.getHeaderValue());
         httpPost.setEntity(bodyEntity);
 
         final X509Certificate[] certs = settings.getCertificates();

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

@@ -115,7 +115,7 @@ public abstract class PeopleSearchServlet extends ControlledPwmServlet {
                 pwmRequest.getLocale()
         );
 
-        final RestResultBean restResultBean = new RestResultBean(peopleSearchClientConfigBean);
+        final RestResultBean restResultBean = RestResultBean.withData(peopleSearchClientConfigBean);
         LOGGER.trace(pwmRequest, "returning clientData: " + JsonUtil.serialize(restResultBean));
         pwmRequest.outputJsonResult(restResultBean);
         return ProcessStatus.Halt;
@@ -137,7 +137,7 @@ public abstract class PeopleSearchServlet extends ControlledPwmServlet {
         final PeopleSearchDataReader peopleSearchDataReader = new PeopleSearchDataReader(pwmRequest);
 
         final SearchResultBean searchResultBean = peopleSearchDataReader.makeSearchResultBean(username, includeDisplayName);
-        final RestResultBean restResultBean = new RestResultBean(searchResultBean);
+        final RestResultBean restResultBean = RestResultBean.withData(searchResultBean);
 
         addExpiresHeadersToResponse(pwmRequest);
         pwmRequest.outputJsonResult(restResultBean);
@@ -178,7 +178,7 @@ public abstract class PeopleSearchServlet extends ControlledPwmServlet {
             final OrgChartDataBean orgChartData = peopleSearchDataReader.makeOrgChartData(userIdentity, noChildren);
 
             addExpiresHeadersToResponse(pwmRequest);
-            pwmRequest.outputJsonResult(new RestResultBean(orgChartData));
+            pwmRequest.outputJsonResult(RestResultBean.withData(orgChartData));
             StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_ORGCHART);
         } catch (PwmException e) {
             LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());
@@ -204,7 +204,7 @@ public abstract class PeopleSearchServlet extends ControlledPwmServlet {
             final UserDetailBean detailData = peopleSearchDataReader.makeUserDetailRequest(userKey);
 
             addExpiresHeadersToResponse(pwmRequest);
-            pwmRequest.outputJsonResult(new RestResultBean(detailData));
+            pwmRequest.outputJsonResult(RestResultBean.withData(detailData));
             pwmRequest.getPwmApplication().getStatisticsManager().incrementValue(Statistic.PEOPLESEARCH_DETAILS);
         } catch (PwmOperationalException e) {
             LOGGER.error(pwmRequest, "error generating user detail object: " + e.getMessage());

+ 0 - 8
server/src/main/java/password/pwm/http/tag/value/PwmValue.java

@@ -57,7 +57,6 @@ public enum PwmValue {
     instanceID(new InstanceIDOutput()),
     headerMenuNotice(new HeaderMenuNoticeOutput()),
     clientETag(new ClientETag()),
-    restClientKey(new RestClientKey()),
     localeCode(new LocaleCodeOutput()),
     localeDir(new LocaleDirOutput()),
     localeFlagFile(new LocaleFlagFileOutput()),
@@ -203,13 +202,6 @@ public enum PwmValue {
         }
     }
 
-    static class RestClientKey implements ValueOutput {
-        @Override
-        public String valueOutput(final PwmRequest pwmRequest, final PageContext pageContext) throws ChaiUnavailableException, PwmUnrecoverableException {
-            return pwmRequest.getPwmSession().getRestClientKey();
-        }
-    }
-
     static class LocaleCodeOutput implements ValueOutput {
         @Override
         public String valueOutput(final PwmRequest pwmRequest, final PageContext pageContext) throws ChaiUnavailableException, PwmUnrecoverableException {

+ 1 - 3
server/src/main/java/password/pwm/ldap/LdapOperationsHelper.java

@@ -46,7 +46,6 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.PwmSession;
 import password.pwm.ldap.search.SearchConfiguration;
 import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.cache.CacheKey;
@@ -227,7 +226,7 @@ public class LdapOperationsHelper {
      */
     public static void writeFormValuesToLdap(
             final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
+            final MacroMachine macroMachine,
             final ChaiUser theUser,
             final Map<FormConfiguration,String> formValues,
             final boolean expandMacros
@@ -242,7 +241,6 @@ public class LdapOperationsHelper {
             }
         }
 
-        final MacroMachine macroMachine = pwmSession.getSessionManager().getMacroMachine(pwmApplication);
         writeMapToLdap(theUser, tempMap, macroMachine, expandMacros);
     }
 

+ 42 - 0
server/src/main/java/password/pwm/ldap/auth/SimpleLdapAuthenticator.java

@@ -0,0 +1,42 @@
+package password.pwm.ldap.auth;
+
+import com.novell.ldapchai.exception.ChaiUnavailableException;
+import password.pwm.PwmApplication;
+import password.pwm.bean.SessionLabel;
+import password.pwm.bean.UserIdentity;
+import password.pwm.error.PwmOperationalException;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.PasswordData;
+
+public class SimpleLdapAuthenticator {
+    public static AuthenticationResult authenticateUser(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final UserIdentity userIdentity,
+            final PasswordData password
+    ) throws PwmUnrecoverableException
+    {
+        final AuthenticationRequest authEngine = LDAPAuthenticationRequest.createLDAPAuthenticationRequest(
+                pwmApplication,
+                sessionLabel,
+                userIdentity,
+                AuthenticationType.AUTHENTICATED,
+                PwmAuthenticationSource.BASIC_AUTH
+        );
+
+        final AuthenticationResult authResult;
+        try {
+            authResult = authEngine.authenticateUser(password);
+        } catch (ChaiUnavailableException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
+        } catch (PwmOperationalException e) {
+            throw new PwmUnrecoverableException(e.getErrorInformation());
+        }
+
+        if (authResult.getAuthenticationType() == AuthenticationType.AUTHENTICATED) {
+            return authResult;
+        }
+
+        return null;
+    }
+}

+ 5 - 1
server/src/main/java/password/pwm/ldap/search/UserSearchEngine.java

@@ -137,7 +137,7 @@ public class UserSearchEngine implements PwmService {
             final String profile,
             final SessionLabel sessionLabel
     )
-            throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException
+            throws PwmUnrecoverableException, PwmOperationalException
     {
         //check if username is a key
         {
@@ -156,6 +156,8 @@ public class UserSearchEngine implements PwmService {
                     }
                 } catch (ChaiOperationException e) {
                     throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_CANT_MATCH_USER, e.getMessage()));
+                } catch (ChaiUnavailableException e) {
+                    throw PwmUnrecoverableException.fromChaiException(e);
                 }
             }
         }
@@ -178,6 +180,8 @@ public class UserSearchEngine implements PwmService {
             }
         } catch (PwmOperationalException e) {
             throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_CANT_MATCH_USER,e.getErrorInformation().getDetailedErrorMsg(),e.getErrorInformation().getFieldValues()));
+        } catch (ChaiUnavailableException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
         }
     }
 

+ 0 - 1
server/src/main/java/password/pwm/svc/PwmServiceEnum.java

@@ -41,7 +41,6 @@ public enum PwmServiceEnum {
     SmsQueueManager(        password.pwm.util.queue.SmsQueueManager.class),
     UrlShortenerService(    password.pwm.svc.shorturl.UrlShortenerService.class),
     TokenService(           password.pwm.svc.token.TokenService.class),
-    VersionChecker(         password.pwm.svc.telemetry.VersionChecker.class),
     IntruderManager(        password.pwm.svc.intruder.IntruderManager.class),
     CrService(              password.pwm.util.operations.CrService.class,           Flag.StartDuringRuntimeInstance),
     OtpService(             password.pwm.util.operations.OtpService.class),

+ 2 - 1
server/src/main/java/password/pwm/svc/telemetry/HttpTelemetrySender.java

@@ -29,6 +29,7 @@ import password.pwm.PwmConstants;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.TelemetryPublishBean;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.client.PwmHttpClient;
@@ -65,7 +66,7 @@ public class HttpTelemetrySender implements TelemetrySender {
         final PwmHttpClient pwmHttpClient = new PwmHttpClient(pwmApplication, SessionLabel.TELEMETRY_SESSION_LABEL, pwmHttpClientConfiguration);
         final String body = JsonUtil.serialize(statsPublishBean);
         final Map<String,String> headers = new HashMap<>();
-        headers.put(HttpHeader.Content_Type.getHttpName(), PwmConstants.ContentTypeValue.json.getHeaderValue());
+        headers.put(HttpHeader.Content_Type.getHttpName(), HttpContentType.json.getHeaderValue());
         headers.put(HttpHeader.Accept.getHttpName(), PwmConstants.AcceptValue.json.getHeaderValue());
         final PwmHttpClientRequest pwmHttpClientRequest = new PwmHttpClientRequest(
                 HttpMethod.POST,

+ 0 - 291
server/src/main/java/password/pwm/svc/telemetry/VersionChecker.java

@@ -1,291 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.svc.telemetry;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.util.EntityUtils;
-import password.pwm.PwmApplication;
-import password.pwm.PwmApplicationMode;
-import password.pwm.PwmConstants;
-import password.pwm.config.PwmSetting;
-import password.pwm.config.option.DataStorageMethod;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
-import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.health.HealthRecord;
-import password.pwm.health.HealthStatus;
-import password.pwm.health.HealthTopic;
-import password.pwm.http.client.PwmHttpClient;
-import password.pwm.i18n.Display;
-import password.pwm.svc.PwmService;
-import password.pwm.util.java.JsonUtil;
-import password.pwm.util.java.TimeDuration;
-import password.pwm.util.localdb.LocalDB;
-import password.pwm.util.localdb.LocalDBException;
-import password.pwm.util.logging.PwmLogger;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-public class VersionChecker implements PwmService {
-    private static final PwmLogger LOGGER = PwmLogger.forClass(VersionChecker.class);
-
-    private static final String KEY_VERSION = "version";
-    private static final String KEY_BUILD = "build";
-    private static final String LOCALDB_KEY_VERSION_CHECK_INFO_CACHE = "versionCheckInfoCache";
-    
-    private static final String VERSION_CHECK_URL = PwmConstants.PWM_URL_CLOUD + "/rest/pwm/current-version";
-
-    private PwmApplication pwmApplication;
-    private VersionCheckInfoCache versionCheckInfoCache;
-    private STATUS status = STATUS.CLOSED;
-
-    public VersionChecker() {
-    }
-
-    public void init(final PwmApplication pwmApplication) {
-        this.pwmApplication = pwmApplication;
-        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.VERSION_CHECK_ENABLE)) {
-            status = STATUS.CLOSED;
-            return;
-        }
-
-        if (pwmApplication.getLocalDB() != null && pwmApplication.getLocalDB().status() == LocalDB.Status.OPEN) {
-            try {
-                final String versionChkInfoJson = pwmApplication.getLocalDB().get(LocalDB.DB.PWM_META,
-                        LOCALDB_KEY_VERSION_CHECK_INFO_CACHE);
-                if (versionChkInfoJson != null && versionChkInfoJson.length() > 0) {
-                    versionCheckInfoCache = JsonUtil.deserialize(versionChkInfoJson, VersionCheckInfoCache.class);
-                }
-            } catch (LocalDBException e) {
-                LOGGER.error("error reading version check info out of LocalDB: " + e.getMessage());
-            }
-        }
-
-        if (pwmApplication.getApplicationMode() != PwmApplicationMode.RUNNING && pwmApplication.getApplicationMode() != PwmApplicationMode.CONFIGURATION ) {
-            LOGGER.trace("skipping init due to application mode");
-            return;
-        }
-
-        if (versionCheckInfoCache != null && versionCheckInfoCache.getLastError() != null) {
-            versionCheckInfoCache = null;
-        }
-
-        if (!isVersionCurrent()) {
-            LOGGER.warn("this version of PWM is outdated, please check the project website for the current version");
-        }
-
-        status = STATUS.OPEN;
-    }
-
-    public String currentVersion() {
-        if (status() != STATUS.OPEN) {
-            return Display.getLocalizedMessage(PwmConstants.DEFAULT_LOCALE, Display.Value_NotApplicable, pwmApplication.getConfig());
-        }
-
-        try {
-            final VersionCheckInfoCache versionCheckInfo = getVersionCheckInfo();
-            if (versionCheckInfo != null) {
-                return versionCheckInfo.getCurrentVersion();
-            }
-        } catch (Exception e) {
-            LOGGER.error("unable to retrieve current version data from cloud: " + e.toString());
-        }
-        return Display.getLocalizedMessage(PwmConstants.DEFAULT_LOCALE, Display.Value_NotApplicable, pwmApplication.getConfig());
-    }
-
-    public Date lastReadTimestamp() {
-        if (status() != STATUS.OPEN) {
-            return null;
-        }
-
-        try {
-            final VersionCheckInfoCache versionCheckInfo = getVersionCheckInfo();
-            if (versionCheckInfo != null) {
-                return versionCheckInfo.getLastCheckTimestamp();
-            }
-        } catch (Exception e) {
-            LOGGER.error("unable to determine last read timestamp: " + e.toString());
-        }
-        return null;
-    }
-
-    public boolean isVersionCurrent() {
-        if (status() != STATUS.OPEN) {
-            return true;
-        }
-
-        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.VERSION_CHECK_ENABLE)) {
-            return true;
-        }
-
-        if (PwmConstants.BUILD_NUMBER == null || PwmConstants.BUILD_NUMBER.length() < 1) {
-            return true;
-        }
-        /*
-        try {
-            final VersionCheckInfoCache versionCheckInfo = getVersionCheckInfo();
-            final String currentBuild = versionCheckInfo.getCurrentBuild();
-            final int currentBuildNumber = Integer.parseInt(currentBuild);
-            int localBuildNumber;
-            try {
-                localBuildNumber = Integer.parseInt(PwmConstants.BUILD_NUMBER);
-            } catch (NumberFormatException e) {
-                localBuildNumber = 0;
-            }
-            if (localBuildNumber < currentBuildNumber) {
-                LOGGER.trace("current build " + currentBuildNumber + " is newer than local build (" + localBuildNumber + ")");
-                return false;
-            }
-        } catch (Exception e) {
-            LOGGER.error("unable to retrieve current version data from cloud: " + e.toString());
-        }
-        */
-        return true;
-    }
-
-    private synchronized VersionCheckInfoCache getVersionCheckInfo() {
-        if (versionCheckInfoCache != null) {
-            if (versionCheckInfoCache.getLastError() != null) {
-                if (TimeDuration.fromCurrent(versionCheckInfoCache.getLastCheckTimestamp()).isLongerThan(PwmConstants.VERSION_CHECK_FAIL_RETRY_MS)) {
-                    versionCheckInfoCache = null;
-                }                
-            } else if (TimeDuration.fromCurrent(versionCheckInfoCache.getLastCheckTimestamp()).isLongerThan(PwmConstants.VERSION_CHECK_FREQUENCEY_MS)) {
-                versionCheckInfoCache = null;
-            }
-        }
-
-        if (versionCheckInfoCache != null) {
-            return versionCheckInfoCache;
-        }
-
-        try {
-            final Map<String,String> responseMap = doCurrentVersionFetch();
-            versionCheckInfoCache = new VersionCheckInfoCache(null, responseMap.get(KEY_VERSION), responseMap.get(KEY_BUILD));
-            LOGGER.info("version check to " + VERSION_CHECK_URL +" completed, current-build="
-                    + versionCheckInfoCache.getCurrentBuild()
-                    + ", current-version=" + versionCheckInfoCache.getCurrentVersion());
-        } catch (Exception e) {
-            final String errorMsg = "unable to reach version check service '" + VERSION_CHECK_URL + "', error: " + e.getMessage();
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNREACHABLE_CLOUD_SERVICE, errorMsg);
-            LOGGER.error(errorInformation.toDebugStr());
-            versionCheckInfoCache = new VersionCheckInfoCache(errorInformation,"","");
-        }
-
-        if (pwmApplication.getLocalDB() != null && pwmApplication.getLocalDB().status() == LocalDB.Status.OPEN) {
-            try {
-                final String jsonVersionInfo = JsonUtil.serialize(versionCheckInfoCache);
-                pwmApplication.getLocalDB().put(LocalDB.DB.PWM_META, LOCALDB_KEY_VERSION_CHECK_INFO_CACHE,jsonVersionInfo);
-            } catch (LocalDBException e) {
-                LOGGER.error("error writing version check info out of LocalDB: " + e.getMessage());
-            }
-        }
-
-        return versionCheckInfoCache;
-    }
-
-    private Map<String,String> doCurrentVersionFetch() throws IOException, URISyntaxException, PwmUnrecoverableException {
-        final URI requestURI = new URI(VERSION_CHECK_URL);
-        final HttpGet httpGet = new HttpGet(requestURI.toString());
-        httpGet.setHeader("Accept", PwmConstants.ContentTypeValue.json.getHeaderValue());
-        LOGGER.trace("sending cloud version request to: " + VERSION_CHECK_URL);
-
-        final HttpResponse httpResponse = PwmHttpClient.getHttpClient(pwmApplication.getConfig()).execute(httpGet);
-        if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
-            throw new IOException("http response error code: " + httpResponse.getStatusLine().getStatusCode());
-        }
-        final String responseBody = EntityUtils.toString(httpResponse.getEntity());
-        return JsonUtil.deserializeStringMap(responseBody);
-    }
-
-    public STATUS status() {
-        return status;
-    }
-
-    public void close() {
-    }
-
-    public List<HealthRecord> healthCheck() {
-        final ArrayList<HealthRecord> returnRecords = new ArrayList<>();
-
-        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.VERSION_CHECK_ENABLE)) { // version checking
-            final VersionCheckInfoCache checkInfoCache = getVersionCheckInfo();
-            if (checkInfoCache.getLastError() == null) {
-                if (!isVersionCurrent()) {
-                    returnRecords.add(new HealthRecord(HealthStatus.CAUTION, HealthTopic.Application,
-                            "This version of " + PwmConstants.PWM_APP_NAME + " is out of date." + "  The current version is "
-                                    + versionCheckInfoCache.getCurrentVersion() + " (b" + versionCheckInfoCache.getCurrentBuild() + ")."
-                                    + "  Check the project page for more information."));
-                }
-            } else {
-                returnRecords.add(new HealthRecord(HealthStatus.WARN,HealthTopic.Application,"Unable to check current version: " + versionCheckInfoCache.getLastError().toDebugStr()));
-            }
-        }
-
-        return returnRecords;
-    }
-
-    private static class VersionCheckInfoCache implements Serializable {
-        private Date lastCheckTimestamp;
-        private ErrorInformation lastError;
-        private String currentVersion;
-        private String currentBuild;
-
-        private VersionCheckInfoCache(final ErrorInformation lastError, final String currentVersion, final String currentBuild) {
-            this.lastCheckTimestamp = new Date();
-            this.lastError = lastError;
-            this.currentVersion = currentVersion;
-            this.currentBuild = currentBuild;
-        }
-
-        public Date getLastCheckTimestamp() {
-            return lastCheckTimestamp;
-        }
-
-        public ErrorInformation getLastError() {
-            return lastError;
-        }
-
-        public String getCurrentVersion() {
-            return currentVersion;
-        }
-
-        public String getCurrentBuild() {
-            return currentBuild;
-        }
-    }
-
-    public ServiceInfoBean serviceInfo()
-    {
-        return new ServiceInfoBean(Collections.<DataStorageMethod>emptyList());
-    }
-}

+ 7 - 4
server/src/main/java/password/pwm/svc/wordlist/SharedHistoryManager.java

@@ -25,15 +25,15 @@ package password.pwm.svc.wordlist;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmApplicationMode;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.DataStorageMethod;
 import password.pwm.error.PwmException;
 import password.pwm.health.HealthRecord;
-import password.pwm.http.PwmSession;
 import password.pwm.svc.PwmService;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.Sleeper;
 import password.pwm.util.java.TimeDuration;
-import password.pwm.util.java.JavaHelper;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.logging.PwmLogger;
@@ -236,7 +236,10 @@ public class SharedHistoryManager implements PwmService {
         return word.length() > 0 ? word : null;
     }
 
-    public synchronized void addWord(final PwmSession pwmSession, final String word) {
+    public synchronized void addWord(
+            final SessionLabel sessionLabel,
+            final String word
+    ) {
         if (status != STATUS.OPEN) {
             return;
         }
@@ -263,7 +266,7 @@ public class SharedHistoryManager implements PwmService {
                 LOGGER.trace(logOutput.toString());
             }
         } catch (Exception e) {
-            LOGGER.warn(pwmSession, "error adding word to global history list: " + e.getMessage());
+            LOGGER.warn(sessionLabel, "error adding word to global history list: " + e.getMessage());
         }
     }
 

+ 2 - 1
server/src/main/java/password/pwm/util/CaptchaUtility.java

@@ -34,6 +34,7 @@ import password.pwm.config.option.ApplicationPage;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
@@ -101,7 +102,7 @@ public class CaptchaUtility {
                     HttpMethod.POST,
                     pwmApplication.getConfig().readAppProperty(AppProperty.RECAPTCHA_VALIDATE_URL),
                     bodyText.toString(),
-                    Collections.singletonMap("Content-Type",PwmConstants.ContentTypeValue.form.getHeaderValue())
+                    Collections.singletonMap("Content-Type", HttpContentType.form.getHeaderValue())
             );
             LOGGER.debug(pwmRequest, "sending reCaptcha verification request" );
             final PwmHttpClient client = new PwmHttpClient(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel());

+ 5 - 1
server/src/main/java/password/pwm/util/db/DBConfiguration.java

@@ -51,6 +51,7 @@ public class DBConfiguration implements Serializable {
     private final List<JDBCDriverLoader.ClassLoaderStrategy> classLoaderStrategies;
     private final int maxConnections;
     private final int connectionTimeout;
+    private final int keyColumnLength;
 
     public boolean isEnabled() {
         return
@@ -81,6 +82,8 @@ public class DBConfiguration implements Serializable {
          final int maxConnections = Integer.parseInt(config.readAppProperty(AppProperty.DB_CONNECTIONS_MAX));
          final int connectionTimeout = Integer.parseInt(config.readAppProperty(AppProperty.DB_CONNECTIONS_TIMEOUT_MS));
 
+         final int keyColumnLength = Integer.parseInt(config.readAppProperty(AppProperty.DB_SCHEMA_KEY_LENGTH));
+
          return new DBConfiguration(
                  config.readSettingAsString(PwmSetting.DATABASE_CLASS),
                  config.readSettingAsString(PwmSetting.DATABASE_URL),
@@ -91,7 +94,8 @@ public class DBConfiguration implements Serializable {
                  jdbcDriverBytes,
                  strategies,
                  maxConnections,
-                 connectionTimeout
+                 connectionTimeout,
+                 keyColumnLength
          );
      }
 }

+ 0 - 1
server/src/main/java/password/pwm/util/db/DatabaseService.java

@@ -71,7 +71,6 @@ public class DatabaseService implements PwmService
     static final String VALUE_COLUMN = "value";
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(DatabaseService.class);
-    static final int KEY_COLUMN_LENGTH = PwmConstants.DATABASE_ACCESSOR_KEY_LENGTH;
 
     private DBConfiguration dbConfiguration;
 

+ 1 - 1
server/src/main/java/password/pwm/util/db/DatabaseUtil.java

@@ -123,7 +123,7 @@ class DatabaseUtil {
             final StringBuilder sqlString = new StringBuilder();
             sqlString.append("CREATE table ").append(table.toString()).append(" (").append("\n");
             sqlString.append("  " + DatabaseService.KEY_COLUMN + " ").append(dbConfiguration.getColumnTypeKey()).append("(").append(
-                    DatabaseService.KEY_COLUMN_LENGTH).append(") NOT NULL PRIMARY KEY,").append("\n");
+                    dbConfiguration.getKeyColumnLength()).append(") NOT NULL PRIMARY KEY,").append("\n");
             sqlString.append("  " + DatabaseService.VALUE_COLUMN + " ").append(dbConfiguration.getColumnTypeValue()).append(" ");
             sqlString.append("\n");
             sqlString.append(")").append("\n");

+ 18 - 18
server/src/main/java/password/pwm/util/operations/ActionExecutor.java

@@ -26,6 +26,7 @@ import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.PwmApplication;
+import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.error.ErrorInformation;
@@ -33,7 +34,6 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.HttpMethod;
-import password.pwm.http.PwmSession;
 import password.pwm.http.client.PwmHttpClient;
 import password.pwm.http.client.PwmHttpClientConfiguration;
 import password.pwm.http.client.PwmHttpClientRequest;
@@ -60,41 +60,41 @@ public class ActionExecutor {
 
     public void executeActions(
             final List<ActionConfiguration> configValues,
-            final PwmSession pwmSession
+            final SessionLabel sessionLabel
     )
             throws ChaiUnavailableException, PwmOperationalException, PwmUnrecoverableException
     {
         for (final ActionConfiguration loopAction : configValues) {
-            this.executeAction(loopAction, pwmSession);
+            this.executeAction(loopAction, sessionLabel);
         }
     }
 
     public void executeAction(
             final ActionConfiguration actionConfiguration,
-            final PwmSession pwmSession
+            final SessionLabel sessionLabel
     )
             throws ChaiUnavailableException, PwmOperationalException, PwmUnrecoverableException
     {
-        LOGGER.trace(pwmSession, "preparing to execute " + actionConfiguration.getType() + " action " + actionConfiguration.getName());
+        LOGGER.trace(sessionLabel, "preparing to execute " + actionConfiguration.getType() + " action " + actionConfiguration.getName());
 
         switch (actionConfiguration.getType()) {
             case ldap:
-                executeLdapAction(pwmSession, actionConfiguration);
+                executeLdapAction(sessionLabel, actionConfiguration);
                 break;
 
             case webservice:
-                executeWebserviceAction(pwmSession, actionConfiguration);
+                executeWebserviceAction(sessionLabel, actionConfiguration);
                 break;
 
             default:
                 JavaHelper.unhandledSwitchStatement(actionConfiguration.getType());
         }
 
-        LOGGER.info(pwmSession, "action " + actionConfiguration.getName() + " completed successfully");
+        LOGGER.info(sessionLabel, "action " + actionConfiguration.getName() + " completed successfully");
     }
 
     private void executeLdapAction(
-            final PwmSession pwmSession,
+            final SessionLabel sessionLabel,
             final ActionConfiguration actionConfiguration
     )
             throws ChaiUnavailableException, PwmOperationalException, PwmUnrecoverableException
@@ -125,7 +125,7 @@ public class ActionExecutor {
         }
 
         writeLdapAttribute(
-                pwmSession,
+                sessionLabel,
                 theUser,
                 attributeName,
                 attributeValue,
@@ -135,7 +135,7 @@ public class ActionExecutor {
     }
 
     private void executeWebserviceAction(
-            final PwmSession pwmSession,
+            final SessionLabel sessionLabel,
             final ActionConfiguration actionConfiguration
     )
             throws PwmOperationalException, PwmUnrecoverableException
@@ -173,9 +173,9 @@ public class ActionExecutor {
             {
                 if (actionConfiguration.getCertificates() != null) {
                     final PwmHttpClientConfiguration clientConfiguration = new PwmHttpClientConfiguration.Builder().setCertificate(actionConfiguration.getCertificates()).create();
-                    client = new PwmHttpClient(pwmApplication, pwmSession.getLabel(), clientConfiguration);
+                    client = new PwmHttpClient(pwmApplication, sessionLabel, clientConfiguration);
                 } else {
-                    client = new PwmHttpClient(pwmApplication, pwmSession.getLabel());
+                    client = new PwmHttpClient(pwmApplication, sessionLabel);
                 }
             }
             final PwmHttpClientResponse clientResponse = client.makeRequest(clientRequest);
@@ -200,7 +200,7 @@ public class ActionExecutor {
     }
 
     private static void writeLdapAttribute(
-            final PwmSession pwmSession,
+            final SessionLabel sessionLabel,
             final ChaiUser theUser,
             final String attrName,
             final String attrValue,
@@ -218,13 +218,13 @@ public class ActionExecutor {
                 : attrValue;
 
 
-        LOGGER.trace(pwmSession,"beginning ldap " + effectiveLdapMethod.toString() + " operation on " + theUser.getEntryDN() + ", attribute " + attrName);
+        LOGGER.trace(sessionLabel,"beginning ldap " + effectiveLdapMethod.toString() + " operation on " + theUser.getEntryDN() + ", attribute " + attrName);
         switch (effectiveLdapMethod) {
             case replace:
             {
                 try {
                     theUser.writeStringAttribute(attrName, effectiveAttrValue);
-                    LOGGER.info(pwmSession,"replaced attribute on user " + theUser.getEntryDN() + " (" + attrName + "=" + effectiveAttrValue + ")");
+                    LOGGER.info(sessionLabel,"replaced attribute on user " + theUser.getEntryDN() + " (" + attrName + "=" + effectiveAttrValue + ")");
                 } catch (ChaiOperationException e) {
                     final String errorMsg = "error setting '" + attrName + "' attribute on user " + theUser.getEntryDN() + ", error: " + e.getMessage();
                     final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
@@ -239,7 +239,7 @@ public class ActionExecutor {
             {
                 try {
                     theUser.addAttribute(attrName, effectiveAttrValue);
-                    LOGGER.info(pwmSession,"added attribute on user " + theUser.getEntryDN() + " (" + attrName + "=" + effectiveAttrValue + ")");
+                    LOGGER.info(sessionLabel,"added attribute on user " + theUser.getEntryDN() + " (" + attrName + "=" + effectiveAttrValue + ")");
                 } catch (ChaiOperationException e) {
                     final String errorMsg = "error adding '" + attrName + "' attribute value from user " + theUser.getEntryDN() + ", error: " + e.getMessage();
                     final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
@@ -255,7 +255,7 @@ public class ActionExecutor {
             {
                 try {
                     theUser.deleteAttribute(attrName, effectiveAttrValue);
-                    LOGGER.info(pwmSession,"deleted attribute value on user " + theUser.getEntryDN() + " (" + attrName + ")");
+                    LOGGER.info(sessionLabel,"deleted attribute value on user " + theUser.getEntryDN() + " (" + attrName + ")");
                 } catch (ChaiOperationException e) {
                     final String errorMsg = "error deletig '" + attrName + "' attribute value on user " + theUser.getEntryDN() + ", error: " + e.getMessage();
                     final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);

+ 2 - 2
server/src/main/java/password/pwm/util/operations/OtpService.java

@@ -93,7 +93,7 @@ public class OtpService implements PwmService {
     }
 
     public boolean validateToken(
-            final PwmSession pwmSession,
+            final SessionLabel sessionLabel,
             final UserIdentity userIdentity,
             final OTPUserRecord otpUserRecord,
             final String userInput,
@@ -119,7 +119,7 @@ public class OtpService implements PwmService {
                     throw new UnsupportedOperationException("OTP type not supported: " + otpUserRecord.getType());
             }
         } catch (Exception e) {
-            LOGGER.error(pwmSession.getLabel(),"error checking otp secret: " + e.getMessage());
+            LOGGER.error(sessionLabel,"error checking otp secret: " + e.getMessage());
         }
 
         if (!otpCorrect && allowRecoveryCodes && otpUserRecord.getRecoveryCodes() != null && otpUserRecord.getRecoveryInfo() != null) {

+ 73 - 62
server/src/main/java/password/pwm/util/operations/PasswordUtility.java

@@ -313,36 +313,9 @@ public class PasswordUtility {
             }
         }
 
-        try {
-            final ChaiProvider provider = pwmSession.getSessionManager().getChaiProvider();
-            final ChaiUser theUser = ChaiFactory.createChaiUser(pwmSession.getUserInfo().getUserIdentity().getUserDN(), provider);
-            final boolean boundAsSelf = theUser.getEntryDN().equals(provider.getChaiConfiguration().getSetting(ChaiSetting.BIND_DN));
-            LOGGER.trace(pwmSession, "preparing to setActorPassword for '" + theUser.getEntryDN() + "', bindAsSelf=" + boundAsSelf + ", authType=" + pwmSession.getLoginInfoBean().getType());
+        final ChaiProvider provider = pwmSession.getSessionManager().getChaiProvider();
 
-            final boolean setting_enableChange = Boolean.parseBoolean(pwmApplication.getConfig().readAppProperty(AppProperty.LDAP_PASSWORD_CHANGE_SELF_ENABLE));
-            if (setting_enableChange) {
-                if (setPasswordWithoutOld) {
-                    theUser.setPassword(newPassword.getStringValue(), true);
-                } else {
-                    theUser.changePassword(oldPassword.getStringValue(), newPassword.getStringValue());
-                }
-            } else {
-                LOGGER.debug(pwmSession, "skipping actual ldap password change operation due to app property " + AppProperty.LDAP_PASSWORD_CHANGE_SELF_ENABLE.getKey() + "=false");
-            }
-        } catch (ChaiPasswordPolicyException e) {
-            final String errorMsg = "error setting password for user '" + userInfo.getUserIdentity() + "'' " + e.toString();
-            final PwmError pwmError = PwmError.forChaiError(e.getErrorCode());
-            final ErrorInformation error = new ErrorInformation(pwmError == null ? PwmError.PASSWORD_UNKNOWN_VALIDATION : pwmError, errorMsg);
-            throw new PwmOperationalException(error);
-        } catch (ChaiOperationException e) {
-            final String errorMsg = "error setting password for user '" + userInfo.getUserIdentity() + "'' " + e.getMessage();
-            final PwmError pwmError = PwmError.forChaiError(e.getErrorCode()) == null ? PwmError.ERROR_UNKNOWN : PwmError.forChaiError(e.getErrorCode());
-            final ErrorInformation error = new ErrorInformation(pwmError, errorMsg);
-            throw new PwmOperationalException(error);
-        }
-
-        // at this point the password has been changed, so log it.
-        LOGGER.info(pwmSession, "user '" + userInfo.getUserIdentity() + "' successfully changed password");
+        setPassword(pwmApplication, pwmSession.getLabel(), provider, userInfo.getUserIdentity(), setPasswordWithoutOld ? null : oldPassword, newPassword);
 
         // update the session state bean's password modified flag
         pwmSession.getSessionStateBean().setPasswordModified(true);
@@ -368,14 +341,6 @@ public class PasswordUtility {
         // update statistics
         {
             pwmApplication.getStatisticsManager().incrementValue(Statistic.PASSWORD_CHANGES);
-            pwmApplication.getStatisticsManager().updateEps(EpsStatistic.PASSWORD_CHANGES,1);
-            final int passwordStrength = PasswordUtility.judgePasswordStrength(newPassword.getStringValue());
-            pwmApplication.getStatisticsManager().updateAverageValue(Statistic.AVG_PASSWORD_STRENGTH,passwordStrength);
-        }
-
-        // add the old password to the global history list (if the old password is known)
-        if (oldPassword != null && pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.PASSWORD_SHAREDHISTORY_ENABLE)) {
-            pwmApplication.getSharedHistoryManager().addWord(pwmSession, oldPassword.getStringValue());
         }
 
         // invoke post password change actions
@@ -399,7 +364,7 @@ public class PasswordUtility {
                         .setMacroMachine(macroMachine)
                         .setExpandPwmMacros(true)
                         .createActionExecutor();
-                actionExecutor.executeActions(configValues, pwmSession);
+                actionExecutor.executeActions(configValues, pwmSession.getLabel());
             }
         }
 
@@ -407,6 +372,71 @@ public class PasswordUtility {
         LdapOperationsHelper.updateLastPasswordUpdateAttribute(pwmApplication, pwmSession.getLabel(), userInfo.getUserIdentity());
     }
 
+    public static void setPassword(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final ChaiProvider chaiProvider,
+            final UserIdentity userIdentity,
+            final PasswordData oldPassword,
+            final PasswordData newPassword
+    )
+            throws PwmUnrecoverableException, PwmOperationalException
+    {
+        final Instant startTime = Instant.now();
+        final boolean bindIsSelf;
+        final String bindDN;
+
+        try {
+            final ChaiUser theUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), chaiProvider);
+            bindDN = chaiProvider.getChaiConfiguration().getSetting(ChaiSetting.BIND_DN);
+            bindIsSelf = userIdentity.canonicalEquals(new UserIdentity(bindDN, userIdentity.getLdapProfileID()), pwmApplication);
+
+            LOGGER.trace(sessionLabel, "preparing to setActorPassword for '" + theUser.getEntryDN() + "', using bind DN: " + bindDN);
+
+            final boolean setting_enableChange = Boolean.parseBoolean(pwmApplication.getConfig().readAppProperty(AppProperty.LDAP_PASSWORD_CHANGE_SELF_ENABLE));
+            if (setting_enableChange) {
+                if (oldPassword == null) {
+                    theUser.setPassword(newPassword.getStringValue(), true);
+                } else {
+                    theUser.changePassword(oldPassword.getStringValue(), newPassword.getStringValue());
+                }
+            } else {
+                LOGGER.debug(sessionLabel, "skipping actual ldap password change operation due to app property " + AppProperty.LDAP_PASSWORD_CHANGE_SELF_ENABLE.getKey() + "=false");
+            }
+        } catch (ChaiPasswordPolicyException e) {
+            final String errorMsg = "error setting password for user '" + userIdentity.toDisplayString() + "'' " + e.toString();
+            final PwmError pwmError = PwmError.forChaiError(e.getErrorCode());
+            final ErrorInformation error = new ErrorInformation(pwmError == null ? PwmError.PASSWORD_UNKNOWN_VALIDATION : pwmError, errorMsg);
+            throw new PwmOperationalException(error);
+        } catch (ChaiOperationException e) {
+            final String errorMsg = "error setting password for user '" + userIdentity.toDisplayString() + "'' " + e.getMessage();
+            final PwmError pwmError = PwmError.forChaiError(e.getErrorCode()) == null ? PwmError.ERROR_UNKNOWN : PwmError.forChaiError(e.getErrorCode());
+            final ErrorInformation error = new ErrorInformation(pwmError, errorMsg);
+            throw new PwmOperationalException(error);
+        } catch (ChaiUnavailableException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
+        }
+
+        // add the old password to the global history list (if the old password is known)
+        if (oldPassword != null && pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.PASSWORD_SHAREDHISTORY_ENABLE)) {
+            pwmApplication.getSharedHistoryManager().addWord(sessionLabel, oldPassword.getStringValue());
+        }
+
+        // update stats
+        pwmApplication.getStatisticsManager().updateEps(EpsStatistic.PASSWORD_CHANGES,1);
+
+        final int passwordStrength = PasswordUtility.judgePasswordStrength(newPassword.getStringValue());
+        pwmApplication.getStatisticsManager().updateAverageValue(Statistic.AVG_PASSWORD_STRENGTH,passwordStrength);
+
+        // at this point the password has been changed, so log it.
+        final String msg = (bindIsSelf
+                ? "user " + userIdentity.toDisplayString() + " has changed own password"
+                : "password for user '" + userIdentity.toDisplayString() + "' has been changed by " + bindDN)
+                + " (" + TimeDuration.fromCurrent(startTime).asCompactString() + ")";
+
+        LOGGER.info(sessionLabel, msg);
+    }
+
     public static void helpdeskSetUserPassword(
             final PwmSession pwmSession,
             final ChaiUser chaiUser,
@@ -431,27 +461,7 @@ public class PasswordUtility {
             throw new PwmOperationalException(errorInformation);
         }
 
-        final boolean setting_enableChange = Boolean.parseBoolean(pwmApplication.getConfig().readAppProperty(AppProperty.LDAP_PASSWORD_CHANGE_HELPDESK_ENABLE));
-        try {
-            if (setting_enableChange) {
-                chaiUser.setPassword(newPassword.getStringValue());
-            } else {
-                LOGGER.debug(pwmSession, "skipping actual ldap password change operation due to app property " + AppProperty.LDAP_PASSWORD_CHANGE_HELPDESK_ENABLE.getKey() + "=false");
-            }
-        } catch (ChaiPasswordPolicyException e) {
-            final String errorMsg = "error setting password for user '" + chaiUser.getEntryDN() + "'' " + e.toString();
-            final PwmError pwmError = PwmError.forChaiError(e.getErrorCode());
-            final ErrorInformation error = new ErrorInformation(pwmError == null ? PwmError.PASSWORD_UNKNOWN_VALIDATION : pwmError, errorMsg);
-            throw new PwmOperationalException(error);
-        } catch (ChaiOperationException e) {
-            final String errorMsg = "error setting password for user '" + chaiUser.getEntryDN() + "'' " + e.getMessage();
-            final PwmError pwmError = PwmError.forChaiError(e.getErrorCode()) == null ? PwmError.ERROR_UNKNOWN : PwmError.forChaiError(e.getErrorCode());
-            final ErrorInformation error = new ErrorInformation(pwmError, errorMsg);
-            throw new PwmOperationalException(error);
-        }
-
-        // at this point the password has been changed, so log it.
-        LOGGER.info(sessionLabel, "user '" + pwmSession.getUserInfo().getUserIdentity() + "' successfully changed password for " + chaiUser.getEntryDN());
+        setPassword(pwmApplication, pwmSession.getLabel(), chaiUser.getChaiProvider(), userIdentity, null, newPassword);
 
         // create a proxy user object for pwm to update/read the user.
         final ChaiUser proxiedUser = pwmApplication.getProxiedChaiUser(userIdentity);
@@ -470,7 +480,6 @@ public class PasswordUtility {
         }
 
         // update statistics
-        pwmApplication.getStatisticsManager().updateEps(EpsStatistic.PASSWORD_CHANGES,1);
         pwmApplication.getStatisticsManager().incrementValue(Statistic.HELPDESK_PASSWORD_SET);
 
         // create a uib for end user
@@ -504,12 +513,14 @@ public class PasswordUtility {
                         .setExpandPwmMacros(true)
                         .createActionExecutor();
 
-                actionExecutor.executeActions(actions,pwmSession);
+                actionExecutor.executeActions(actions, pwmSession.getLabel());
             }
         }
 
         final HelpdeskClearResponseMode settingClearResponses = HelpdeskClearResponseMode.valueOf(
-                helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_CLEAR_RESPONSES));
+                helpdeskProfile.readSettingAsString(PwmSetting.HELPDESK_CLEAR_RESPONSES)
+        );
+
         if (settingClearResponses == HelpdeskClearResponseMode.yes) {
             final String userGUID = LdapOperationsHelper.readLdapGuidValue(pwmApplication, sessionLabel, userIdentity, false);
             pwmApplication.getCrService().clearResponses(pwmSession.getLabel(), userIdentity, proxiedUser, userGUID);

+ 5 - 4
server/src/main/java/password/pwm/util/queue/EmailQueueManager.java

@@ -37,6 +37,7 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthMessage;
 import password.pwm.health.HealthRecord;
+import password.pwm.http.HttpContentType;
 import password.pwm.ldap.UserInfo;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.stats.Statistic;
@@ -408,15 +409,15 @@ public class EmailQueueManager implements PwmService {
                     final MimeMultipart content = new MimeMultipart("alternative");
                     final MimeBodyPart text = new MimeBodyPart();
                     final MimeBodyPart html = new MimeBodyPart();
-                    text.setContent(emailItemBean.getBodyPlain(), PwmConstants.ContentTypeValue.plain.getHeaderValue());
-                    html.setContent(emailItemBean.getBodyHtml(), PwmConstants.ContentTypeValue.html.getHeaderValue());
+                    text.setContent(emailItemBean.getBodyPlain(), HttpContentType.plain.getHeaderValue());
+                    html.setContent(emailItemBean.getBodyHtml(), HttpContentType.html.getHeaderValue());
                     content.addBodyPart(text);
                     content.addBodyPart(html);
                     message.setContent(content);
                 } else if (hasPlainText) {
-                    message.setContent(emailItemBean.getBodyPlain(), PwmConstants.ContentTypeValue.plain.getHeaderValue());
+                    message.setContent(emailItemBean.getBodyPlain(), HttpContentType.plain.getHeaderValue());
                 } else if (hasHtml) {
-                    message.setContent(emailItemBean.getBodyHtml(), PwmConstants.ContentTypeValue.html.getHeaderValue());
+                    message.setContent(emailItemBean.getBodyHtml(), HttpContentType.html.getHeaderValue());
                 }
 
                 messages.add(message);

+ 2 - 1
server/src/main/java/password/pwm/ws/client/rest/RestClientHelper.java

@@ -32,6 +32,7 @@ import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.client.PwmHttpClient;
 import password.pwm.util.logging.PwmLogger;
 
@@ -53,7 +54,7 @@ public class RestClientHelper {
         if (locale != null) {
             httpPost.setHeader("Accept-Locale", locale.toString());
         }
-        httpPost.setHeader("Content-Type", PwmConstants.ContentTypeValue.json.getHeaderValue());
+        httpPost.setHeader("Content-Type", HttpContentType.json.getHeaderValue());
         final HttpResponse httpResponse;
         try {
             final StringEntity stringEntity = new StringEntity(jsonRequestBody);

+ 2 - 1
server/src/main/java/password/pwm/ws/client/rest/form/RestFormDataClient.java

@@ -31,6 +31,7 @@ import password.pwm.config.value.data.RemoteWebServiceConfiguration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.client.PwmHttpClient;
@@ -77,7 +78,7 @@ public class RestFormDataClient {
     {
         final Map<String,String> httpHeaders = new LinkedHashMap<>();
         httpHeaders.put(HttpHeader.Accept.getHttpName(), PwmConstants.AcceptValue.json.getHeaderValue());
-        httpHeaders.put(HttpHeader.Content_Type.getHttpName(), PwmConstants.ContentTypeValue.json.getHeaderValue());
+        httpHeaders.put(HttpHeader.Content_Type.getHttpName(), HttpContentType.json.getHeaderValue());
         if (locale != null) {
             httpHeaders.put(HttpHeader.Accept_Language.getHttpName(), locale.toString());
         }

+ 0 - 64
server/src/main/java/password/pwm/ws/server/PwmRestServlet.java

@@ -1,64 +0,0 @@
-package password.pwm.ws.server;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import password.pwm.PwmConstants;
-import password.pwm.error.PwmError;
-import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.HttpHeader;
-import password.pwm.http.HttpMethod;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Set;
-
-public abstract class PwmRestServlet extends HttpServlet{
-
-    protected void service(final HttpServletRequest req, final HttpServletResponse resp)
-            throws ServletException, IOException
-    {
-
-        try {
-            resp.setHeader(HttpHeader.Content_Type.getHttpName(), PwmConstants.ContentTypeValue.json.toString());
-            resp.setHeader(HttpHeader.Server.getHttpName(), PwmConstants.PWM_APP_NAME);
-            checkMethods(req);
-            final StandaloneRestRequestBean restRequestBean = StandaloneRestHelper.initialize(req);
-            final RestResultBean restResultBean = invokeWebService(restRequestBean);
-            try (PrintWriter pw = resp.getWriter()) {
-                pw.write(restResultBean.toJson());
-            }
-        } catch (PwmUnrecoverableException e) {
-            final RestResultBean restResultBean = RestResultBean.fromError(e.getErrorInformation());
-            try (PrintWriter pw = resp.getWriter()) {
-                pw.write(restResultBean.toJson());
-            }
-            resp.sendError(500, e.getMessage());
-        } catch (Throwable e) {
-            resp.sendError(500, e.getMessage());
-        }
-    }
-
-    private void checkMethods(final HttpServletRequest req)
-            throws PwmUnrecoverableException
-    {
-        final Set<HttpMethod> methods = getServiceInfo().getMethods();
-        final HttpMethod methodUsed = HttpMethod.fromString(req.getMethod());
-        if (!methods.contains(methodUsed)) {
-            throw new PwmUnrecoverableException(PwmError.ERROR_REST_INVOCATION_ERROR, "method not supported");
-        }
-    }
-
-    public abstract RestResultBean invokeWebService(StandaloneRestRequestBean standaloneRestRequestBean);
-
-    public abstract ServiceInfo getServiceInfo();
-
-    @Getter
-    @AllArgsConstructor
-    public static class ServiceInfo {
-        private Set<HttpMethod> methods;
-    }
-}

+ 19 - 0
server/src/main/java/password/pwm/ws/server/RestAuthentication.java

@@ -0,0 +1,19 @@
+package password.pwm.ws.server;
+
+import com.novell.ldapchai.provider.ChaiProvider;
+import lombok.Value;
+import password.pwm.bean.UserIdentity;
+import password.pwm.config.option.WebServiceUsage;
+
+import java.io.Serializable;
+import java.util.Set;
+
+@Value
+public class RestAuthentication implements Serializable {
+    private RestAuthenticationType type;
+    private String namedSecretName;
+    private UserIdentity ldapIdentity;
+    private Set<WebServiceUsage> usages;
+    private boolean thirdPartyEnabled;
+    private transient ChaiProvider chaiProvider;
+}

+ 155 - 0
server/src/main/java/password/pwm/ws/server/RestAuthenticationProcessor.java

@@ -0,0 +1,155 @@
+package password.pwm.ws.server;
+
+import com.novell.ldapchai.provider.ChaiProvider;
+import password.pwm.PwmApplication;
+import password.pwm.bean.SessionLabel;
+import password.pwm.bean.UserIdentity;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.option.WebServiceUsage;
+import password.pwm.config.value.data.NamedSecretData;
+import password.pwm.config.value.data.UserPermission;
+import password.pwm.error.ErrorInformation;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmOperationalException;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.ldap.LdapPermissionTester;
+import password.pwm.ldap.auth.AuthenticationResult;
+import password.pwm.ldap.auth.SimpleLdapAuthenticator;
+import password.pwm.ldap.search.UserSearchEngine;
+import password.pwm.util.BasicAuthInfo;
+import password.pwm.util.java.JavaHelper;
+import password.pwm.util.logging.PwmLogger;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class RestAuthenticationProcessor {
+    private static final PwmLogger LOGGER = PwmLogger.forClass(RestAuthentication.class);
+
+    private final PwmApplication pwmApplication;
+    private final HttpServletRequest httpServletRequest;
+    private final SessionLabel sessionLabel;
+
+    public RestAuthenticationProcessor(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final HttpServletRequest httpServletRequest
+    ) {
+        this.pwmApplication = pwmApplication;
+        this.sessionLabel = sessionLabel;
+        this.httpServletRequest = httpServletRequest;
+    }
+
+    public RestAuthentication readRestAuthentication() throws PwmUnrecoverableException {
+        { // named secret auth
+            final String namedSecretName = readNamedSecretName();
+            if (namedSecretName != null) {
+                LOGGER.trace(sessionLabel, "authenticating with named secret '" + namedSecretName + "'");
+                final Set<WebServiceUsage> usages = new HashSet<>(JavaHelper.readEnumListFromStringCollection(
+                        WebServiceUsage.class,
+                        pwmApplication.getConfig().readSettingAsNamedPasswords(PwmSetting.WEBSERVICES_EXTERNAL_SECRET).get(namedSecretName).getUsage()
+                ));
+                return new RestAuthentication(
+                        RestAuthenticationType.NAMED_SECRET,
+                        namedSecretName,
+                        null,
+                        Collections.unmodifiableSet(usages),
+                        true,
+                        null
+                );
+            }
+        }
+
+        { // ldap auth
+            final UserIdentity userIdentity = readLdapUserIdentity();
+            if (userIdentity != null) {
+                {
+                    final List<UserPermission> userPermission = pwmApplication.getConfig().readSettingAsUserPermission(PwmSetting.WEBSERVICES_QUERY_MATCH);
+                    final boolean result = LdapPermissionTester.testUserPermissions(pwmApplication, sessionLabel, userIdentity, userPermission);
+                    if (!result) {
+                        final String errorMsg = "user does not have webservice permission due to setting "
+                                + PwmSetting.WEBSERVICES_QUERY_MATCH.toMenuLocationDebug(null, httpServletRequest.getLocale());
+                        throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,errorMsg));
+                    }
+                }
+
+                final boolean thirdParty;
+                {
+                    final List<UserPermission> userPermission = pwmApplication.getConfig().readSettingAsUserPermission(PwmSetting.WEBSERVICES_THIRDPARTY_QUERY_MATCH);
+                    thirdParty = LdapPermissionTester.testUserPermissions(pwmApplication, sessionLabel, userIdentity, userPermission);
+                }
+
+                final ChaiProvider chaiProvider = authenticateUser(userIdentity);
+
+                return new RestAuthentication(
+                        RestAuthenticationType.LDAP,
+                        null,
+                        userIdentity,
+                        Collections.unmodifiableSet(new HashSet<>(Arrays.asList(WebServiceUsage.values()))),
+                        thirdParty,
+                        chaiProvider
+                );
+            }
+        }
+
+        final Set<WebServiceUsage> publicUsages;
+        if (pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.PUBLIC_HEALTH_STATS_WEBSERVICES)) {
+            final WebServiceUsage[] usages = {WebServiceUsage.Health, WebServiceUsage.Statistics};
+            publicUsages = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(usages)));
+        } else {
+            publicUsages = Collections.emptySet();
+        }
+
+        return new RestAuthentication(
+                RestAuthenticationType.PUBLIC,
+                null,
+                null,
+                publicUsages,
+                false,
+                null
+        );
+    }
+
+    private String readNamedSecretName() throws PwmUnrecoverableException {
+        final BasicAuthInfo basicAuthInfo = BasicAuthInfo.parseAuthHeader(pwmApplication, httpServletRequest);
+        if (basicAuthInfo != null) {
+            final String basicAuthUsername = basicAuthInfo.getUsername();
+            final Map<String, NamedSecretData> secrets = pwmApplication.getConfig().readSettingAsNamedPasswords(PwmSetting.WEBSERVICES_EXTERNAL_SECRET);
+            final NamedSecretData namedSecretData = secrets.get(basicAuthUsername);
+            if (namedSecretData != null) {
+                if (namedSecretData.getPassword().equals(basicAuthInfo.getPassword())) {
+                    return basicAuthUsername;
+                }
+                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_WRONGPASSWORD,"incorrect password value for named secret"));
+            }
+        }
+        return null;
+    }
+
+    private UserIdentity readLdapUserIdentity() throws PwmUnrecoverableException {
+        final BasicAuthInfo basicAuthInfo = BasicAuthInfo.parseAuthHeader(pwmApplication, httpServletRequest);
+        if (basicAuthInfo == null) {
+            return null;
+        }
+
+        final UserSearchEngine userSearchEngine = pwmApplication.getUserSearchEngine();
+        try {
+            return userSearchEngine.resolveUsername(basicAuthInfo.getUsername(), null, null, sessionLabel);
+        } catch (PwmOperationalException e) {
+            throw new PwmUnrecoverableException(e.getErrorInformation());
+        }
+    }
+
+    private ChaiProvider authenticateUser(final UserIdentity userIdentity) throws PwmUnrecoverableException {
+        final BasicAuthInfo basicAuthInfo = BasicAuthInfo.parseAuthHeader(pwmApplication, httpServletRequest);
+
+        final AuthenticationResult authenticationResult = SimpleLdapAuthenticator.authenticateUser(pwmApplication, sessionLabel, userIdentity, basicAuthInfo.getPassword());
+        return authenticationResult.getUserProvider();
+
+    }
+}

+ 7 - 0
server/src/main/java/password/pwm/ws/server/RestAuthenticationType.java

@@ -0,0 +1,7 @@
+package password.pwm.ws.server;
+
+public enum RestAuthenticationType {
+    PUBLIC,
+    LDAP,
+    NAMED_SECRET,
+}

+ 14 - 0
server/src/main/java/password/pwm/ws/server/RestMethodHandler.java

@@ -0,0 +1,14 @@
+package password.pwm.ws.server;
+
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RestMethodHandler {
+    HttpContentType[] produces() default {};
+    HttpContentType[] consumes() default {};
+    HttpMethod[] method() default {};
+}

+ 109 - 0
server/src/main/java/password/pwm/ws/server/RestRequest.java

@@ -0,0 +1,109 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2017 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.ws.server;
+
+import com.novell.ldapchai.provider.ChaiProvider;
+import password.pwm.PwmApplication;
+import password.pwm.bean.SessionLabel;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpHeader;
+import password.pwm.http.PwmHttpRequestWrapper;
+import password.pwm.util.LocaleHelper;
+import password.pwm.util.logging.PwmLogger;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Locale;
+
+public class RestRequest extends PwmHttpRequestWrapper {
+    private static final PwmLogger LOGGER = PwmLogger.forClass(RestRequest.class);
+
+    private final PwmApplication pwmApplication;
+    private final RestAuthentication restAuthentication;
+    private final SessionLabel sessionLabel;
+
+    public static RestRequest forRequest(
+            final PwmApplication pwmApplication,
+            final RestAuthentication restAuthentication,
+            final SessionLabel sessionLabel,
+            final HttpServletRequest httpServletRequest
+    )
+            throws PwmUnrecoverableException
+    {
+        return new RestRequest(pwmApplication, restAuthentication, sessionLabel, httpServletRequest);
+    }
+
+    private RestRequest(
+            final PwmApplication pwmApplication,
+            final RestAuthentication restAuthentication,
+            final SessionLabel sessionLabel,
+            final HttpServletRequest httpServletRequest
+            )
+    {
+        super(httpServletRequest, pwmApplication.getConfig());
+        this.pwmApplication = pwmApplication;
+        this.restAuthentication = restAuthentication;
+        this.sessionLabel = sessionLabel;
+    }
+
+    public RestAuthentication getRestAuthentication() {
+        return restAuthentication;
+    }
+
+    public PwmApplication getPwmApplication() {
+        return pwmApplication;
+    }
+
+    public HttpContentType readContentType() {
+        return HttpContentType.fromContentTypeHeader(readHeaderValueAsString(HttpHeader.Content_Type));
+    }
+
+    public HttpContentType readAcceptType() {
+        return HttpContentType.fromContentTypeHeader(readHeaderValueAsString(HttpHeader.Accept));
+    }
+
+    public Locale getLocale() {
+        final List<Locale> knownLocales = getConfig().getKnownLocales();
+        return LocaleHelper.localeResolver(getHttpServletRequest().getLocale(), knownLocales);
+    }
+
+    public SessionLabel getSessionLabel() {
+        return sessionLabel;
+    }
+
+    public ChaiProvider getChaiProvider(final String ldapProfileID)
+            throws PwmUnrecoverableException
+    {
+        if (getRestAuthentication().getType() == RestAuthenticationType.LDAP) {
+            if (!getRestAuthentication().getLdapIdentity().getLdapProfileID().equals(ldapProfileID)) {
+                final String errorMsg = "target user ldap profileID does not match authenticated user ldap profileID";
+                throw PwmUnrecoverableException.newException(PwmError.ERROR_REST_INVOCATION_ERROR, errorMsg);
+            }
+            return getRestAuthentication().getChaiProvider();
+        }
+        return getPwmApplication().getProxyChaiProvider(ldapProfileID);
+    }
+}
+

+ 0 - 1
server/src/main/java/password/pwm/ws/server/RestRequestBean.java

@@ -37,7 +37,6 @@ import java.util.Set;
 @Setter
 public class RestRequestBean implements Serializable {
     private boolean authenticated;
-    private boolean external;
     private UserIdentity userIdentity;
     private PwmSession pwmSession;
     private PwmApplication pwmApplication;

+ 73 - 65
server/src/main/java/password/pwm/ws/server/RestResultBean.java

@@ -22,6 +22,10 @@
 
 package password.pwm.ws.server;
 
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
 import password.pwm.PwmApplication;
 import password.pwm.config.Configuration;
 import password.pwm.error.ErrorInformation;
@@ -30,10 +34,12 @@ import password.pwm.i18n.Config;
 import password.pwm.i18n.Message;
 import password.pwm.util.java.JsonUtil;
 
-import javax.ws.rs.core.Response;
 import java.io.Serializable;
 import java.util.Locale;
 
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@Getter
+@Setter(AccessLevel.PRIVATE)
 public class RestResultBean implements Serializable {
     private boolean error;
     private int errorCode;
@@ -42,59 +48,10 @@ public class RestResultBean implements Serializable {
     private String successMessage;
     private Serializable data;
 
-    public RestResultBean() {
-    }
-
-    public RestResultBean(final Serializable data) {
-        this.data = data;
-    }
-
-    public boolean isError() {
-        return error;
-    }
-
-    public void setError(final boolean error) {
-        this.error = error;
-    }
-
-    public int getErrorCode() {
-        return errorCode;
-    }
-
-    public void setErrorCode(final int errorCode) {
-        this.errorCode = errorCode;
-    }
-
-    public String getErrorMessage() {
-        return errorMessage;
-    }
-
-    public void setErrorMessage(final String errorMessage) {
-        this.errorMessage = errorMessage;
-    }
-
-    public String getSuccessMessage() {
-        return successMessage;
-    }
-
-    public void setSuccessMessage(final String successMessage) {
-        this.successMessage = successMessage;
-    }
-
-    public Serializable getData() {
-        return data;
-    }
-
-    public void setData(final Serializable data) {
-        this.data = data;
-    }
-
-    public String getErrorDetail() {
-        return errorDetail;
-    }
-
-    public void setErrorDetail(final String errorDetail) {
-        this.errorDetail = errorDetail;
+    public static RestResultBean withData(final Serializable data) {
+        final RestResultBean restResultBean = new RestResultBean();
+        restResultBean.setData(data);
+        return restResultBean;
     }
 
     public static RestResultBean fromError(
@@ -124,12 +81,30 @@ public class RestResultBean implements Serializable {
         return fromError(errorInformation, pwmApplication, locale, config, false);
     }
 
+    public static RestResultBean fromError(
+            final RestRequest restRequestBean,
+            final ErrorInformation errorInformation
+    ) {
+        final PwmApplication pwmApplication = restRequestBean.getPwmApplication();
+        final Configuration config = restRequestBean.getPwmApplication().getConfig();
+        final Locale locale = restRequestBean.getLocale();
+        return fromError(errorInformation, pwmApplication, locale, config, false);
+    }
+
     public static RestResultBean fromError(
             final ErrorInformation errorInformation
     ) {
         return fromError(errorInformation, null, null, null, false);
     }
 
+    public static RestResultBean fromError(
+            final ErrorInformation errorInformation,
+            final boolean showDetails
+    )
+    {
+        return fromError(errorInformation, null, null, null, showDetails);
+    }
+
     public static RestResultBean fromError(
             final ErrorInformation errorInformation,
             final PwmRequest pwmRequest,
@@ -145,6 +120,22 @@ public class RestResultBean implements Serializable {
         return fromError(errorInformation, pwmRequest.getPwmApplication(), pwmRequest.getLocale(), pwmRequest.getConfig(), false);
     }
 
+
+    public static RestResultBean forSuccessMessage(
+            final Serializable data,
+            final Locale locale,
+            final Configuration config,
+            final Message message,
+            final String... fieldValues
+
+    ) {
+        final RestResultBean restResultBean = new RestResultBean();
+        final String msgText = Message.getLocalizedMessage(locale, message, config, fieldValues);
+        restResultBean.setSuccessMessage(msgText);
+        restResultBean.setData(data);
+        return restResultBean;
+    }
+
     public static RestResultBean forSuccessMessage(
             final Locale locale,
             final Configuration config,
@@ -158,6 +149,32 @@ public class RestResultBean implements Serializable {
         return restResultBean;
     }
 
+    public static RestResultBean forSuccessMessage(
+            final Serializable data,
+            final PwmRequest pwmRequest,
+            final Message message,
+            final String... fieldValues
+    ) {
+        return forSuccessMessage(data, pwmRequest.getLocale(), pwmRequest.getConfig(), message, fieldValues);
+    }
+
+    public static RestResultBean forSuccessMessage(
+            final Serializable data,
+            final RestRequest restRequest,
+            final Message message,
+            final String... fieldValues
+    ) {
+        return forSuccessMessage(data, restRequest.getLocale(), restRequest.getConfig(), message, fieldValues);
+    }
+
+    public static RestResultBean forSuccessMessage(
+            final RestRequest restRequest,
+            final Message message,
+            final String... fieldValues
+    ) {
+        return forSuccessMessage(restRequest.getLocale(), restRequest.getConfig(), message, fieldValues);
+    }
+
     public static RestResultBean forSuccessMessage(
             final PwmRequest pwmRequest,
             final Message message,
@@ -169,9 +186,7 @@ public class RestResultBean implements Serializable {
     public static RestResultBean forConfirmMessage(
             final Locale locale,
             final Configuration config,
-            final Config message,
-            final String... fieldValues
-
+            final Config message
     ) {
         final RestResultBean restResultBean = new RestResultBean();
         final String msgText = Config.getLocalizedMessage(locale, message, config);
@@ -188,13 +203,6 @@ public class RestResultBean implements Serializable {
 
 
     public String toJson() {
-        return JsonUtil.serialize(this);
-    }
-
-    public Response asJsonResponse() {
-        final Response.ResponseBuilder responseBuilder = Response.ok();
-        final String body = this.toJson();
-        responseBuilder.entity(body);
-        return responseBuilder.build();
+        return JsonUtil.serialize(this, JsonUtil.Flag.PrettyPrint) + "\n";
     }
 }

+ 0 - 242
server/src/main/java/password/pwm/ws/server/RestServerHelper.java

@@ -1,242 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.ws.server;
-
-import com.novell.ldapchai.exception.ChaiUnavailableException;
-import password.pwm.Permission;
-import password.pwm.PwmApplication;
-import password.pwm.PwmApplicationMode;
-import password.pwm.PwmConstants;
-import password.pwm.bean.UserIdentity;
-import password.pwm.config.PwmSetting;
-import password.pwm.config.profile.HelpdeskProfile;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
-import password.pwm.error.PwmOperationalException;
-import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.HttpHeader;
-import password.pwm.http.PwmRequest;
-import password.pwm.http.PwmSession;
-import password.pwm.http.filter.AuthenticationFilter;
-import password.pwm.ldap.search.UserSearchEngine;
-import password.pwm.svc.intruder.RecordType;
-import password.pwm.util.LocaleHelper;
-import password.pwm.util.logging.PwmLogger;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Response;
-import java.net.URISyntaxException;
-import java.util.List;
-import java.util.Locale;
-
-public abstract class RestServerHelper {
-    private static final PwmLogger LOGGER = PwmLogger.forClass(RestServerHelper.class);
-
-    public static javax.ws.rs.core.Response handleHtmlRequest() throws URISyntaxException {
-        final String msg = "This REST service requires an appropriate "
-                + HttpHeader.Accept.getHttpName() + " header to be set.  This request used an html "
-                + HttpHeader.Accept.getHttpName() + " header, which is not supported.  See documentation at \"/public/reference/\".";
-        final String content = "<html><body>" + msg + "</body></html>";
-        return javax.ws.rs.core.Response.status(Response.Status.NOT_ACCEPTABLE).entity(content).build();
-    }
-
-    public static RestRequestBean initializeRestRequest(
-            final HttpServletRequest request,
-            final HttpServletResponse response,
-            final ServicePermissions servicePermissions,
-            final String requestedUsername
-    )
-            throws PwmUnrecoverableException
-    {
-        final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
-        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
-        final PwmSession pwmSession = pwmRequest.getPwmSession();
-
-        if (pwmSession.getSessionStateBean().getLocale() == null) {
-            final List<Locale> knownLocales = pwmApplication.getConfig().getKnownLocales();
-            final Locale userLocale = LocaleHelper.localeResolver(request.getLocale(), knownLocales);
-            pwmSession.getSessionStateBean().setLocale(userLocale == null ? PwmConstants.DEFAULT_LOCALE : userLocale);
-        }
-
-        pwmRequest.debugHttpRequestToLog("[REST WebService Request]");
-
-        try {
-            handleAuthentication(request,response,pwmApplication,pwmSession);
-        } catch (ChaiUnavailableException e) {
-            throw new PwmUnrecoverableException(PwmError.ERROR_DIRECTORY_UNAVAILABLE);
-        }
-
-        final RestRequestBean restRequestBean = new RestRequestBean();
-        restRequestBean.setAuthenticated(pwmSession.isAuthenticated());
-        restRequestBean.setExternal(determineIfRestClientIsExternal(pwmSession,request));
-        restRequestBean.setUserIdentity(
-                lookupUsername(pwmApplication, pwmSession, restRequestBean.isExternal(), servicePermissions, requestedUsername));
-        restRequestBean.setPwmApplication(pwmApplication);
-        restRequestBean.setPwmSession(pwmSession);
-
-
-        if (servicePermissions.isPublicDuringConfig()) {
-            if (pwmApplication.getApplicationMode() == PwmApplicationMode.NEW || pwmApplication.getApplicationMode() == PwmApplicationMode.CONFIGURATION) {
-                return restRequestBean;
-            }
-        }
-
-        // check permissions
-        final boolean authenticated = pwmSession.isAuthenticated();
-        if (servicePermissions.isAuthRequired()) {
-            if (!authenticated) {
-                throw new PwmUnrecoverableException(PwmError.ERROR_AUTHENTICATION_REQUIRED);
-            }
-        }
-
-        if (restRequestBean.isExternal()) {
-            if (servicePermissions.isBlockExternal()) {
-                final boolean allowExternal = pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.ENABLE_EXTERNAL_WEBSERVICES);
-                if (!allowExternal) {
-                    final String errorMsg = "external web services are not enabled";
-                    LOGGER.warn(pwmSession, errorMsg);
-                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, errorMsg));
-                }
-            }
-
-            if (restRequestBean.isAuthenticated()) {
-                if (!pwmSession.getSessionManager().checkPermission(pwmApplication, Permission.WEBSERVICE)) {
-                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, "authenticated user does not have external webservices permission");
-                    throw new PwmUnrecoverableException(errorInformation);
-                }
-            }
-        }
-
-        final boolean adminPermission= restRequestBean.getPwmSession().getSessionManager().checkPermission(pwmApplication, Permission.PWMADMIN);
-        if (servicePermissions.isAdminOnly()) {
-            if (!adminPermission) {
-                final ErrorInformation errorInfo = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"admin authorization is required");
-                throw new PwmUnrecoverableException(errorInfo);
-            }
-        }
-
-        return restRequestBean;
-    }
-
-    private static UserIdentity lookupUsername(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
-            final boolean isExternal,
-            final ServicePermissions servicePermissions,
-            final String username
-    )
-            throws PwmUnrecoverableException
-    {
-        if (username == null || username.length() < 1) {
-            return null;
-        }
-
-        if (!pwmSession.isAuthenticated()) {
-            throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_AUTHENTICATION_REQUIRED));
-        }
-
-        pwmApplication.getIntruderManager().check(RecordType.USERNAME,username);
-
-        if (isExternal) {
-            if (!pwmSession.getSessionManager().checkPermission(pwmApplication, Permission.WEBSERVICE_THIRDPARTY)) {
-                final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"authenticated user does not match thirdparty webservices query filter");
-                throw new PwmUnrecoverableException(errorInformation);
-            }
-        }
-
-
-        if (!isExternal) {
-            if (servicePermissions.isHelpdeskPermitted()) {
-                final HelpdeskProfile helpdeskProfile = pwmSession.getSessionManager().getHelpdeskProfile(pwmApplication);
-                if (helpdeskProfile == null) {
-                    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"authenticated non-external request for third-party does not match third-party webservices query filter");
-                    throw new PwmUnrecoverableException(errorInformation);
-                }
-            } else {
-                final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"web service is not permitted for internal third-party username");
-                throw new PwmUnrecoverableException(errorInformation);
-            }
-        }
-
-        final String ldapProfileID;
-        final String effectiveUsername;
-        if (username.contains("|")) {
-            final int pipeIndex = username.indexOf("|");
-            ldapProfileID = username.substring(0, pipeIndex);
-            effectiveUsername = username.substring(pipeIndex + 1, username.length());
-        } else {
-            ldapProfileID = null;
-            effectiveUsername = username;
-        }
-
-        try {
-            final UserSearchEngine userSearchEngine = pwmApplication.getUserSearchEngine();
-            return userSearchEngine.resolveUsername(effectiveUsername, null, ldapProfileID, pwmSession.getLabel());
-        } catch (PwmOperationalException e) {
-            throw new PwmUnrecoverableException(e.getErrorInformation());
-        } catch (ChaiUnavailableException e) {
-            throw new PwmUnrecoverableException(PwmError.ERROR_DIRECTORY_UNAVAILABLE);
-        }
-    }
-
-
-    private static void handleAuthentication(
-            final HttpServletRequest request,
-            final HttpServletResponse response,
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession
-    )
-            throws PwmUnrecoverableException, ChaiUnavailableException
-    {
-        if (pwmApplication.getApplicationMode() != PwmApplicationMode.RUNNING) {
-            return;
-        }
-
-        if (pwmSession.isAuthenticated()) {
-            return;
-        }
-
-        final PwmRequest pwmRequest = PwmRequest.forRequest(request,response);
-        new AuthenticationFilter.BasicFilterAuthenticationProvider().attemptAuthentication(pwmRequest);
-    }
-
-    public static boolean determineIfRestClientIsExternal(final PwmSession pwmSession, final HttpServletRequest request)
-            throws PwmUnrecoverableException
-    {
-        final String requestClientKey = request.getHeader(PwmConstants.HTTP_HEADER_REST_CLIENT_KEY);
-        if (requestClientKey == null || requestClientKey.length() < 1) {
-            return true;
-        }
-
-        final String sessionClientKey = pwmSession.getRestClientKey();
-        return !requestClientKey.equals(sessionClientKey);
-    }
-
-    public static void handleNonJsonErrorResult(final ErrorInformation errorInformation) {
-        final Response.ResponseBuilder responseBuilder = Response.serverError();
-        responseBuilder.entity(errorInformation.toDebugStr() + "\n");
-        throw new WebApplicationException(responseBuilder.build());
-    }
-}

+ 361 - 0
server/src/main/java/password/pwm/ws/server/RestServlet.java

@@ -0,0 +1,361 @@
+package password.pwm.ws.server;
+
+import com.google.gson.stream.MalformedJsonException;
+import com.novell.ldapchai.ChaiFactory;
+import com.novell.ldapchai.ChaiUser;
+import com.novell.ldapchai.exception.ChaiUnavailableException;
+import com.novell.ldapchai.provider.ChaiProvider;
+import lombok.Data;
+import lombok.Value;
+import password.pwm.PwmApplication;
+import password.pwm.PwmApplicationMode;
+import password.pwm.PwmConstants;
+import password.pwm.bean.SessionLabel;
+import password.pwm.bean.UserIdentity;
+import password.pwm.config.PwmSetting;
+import password.pwm.error.ErrorInformation;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmOperationalException;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.ContextManager;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpHeader;
+import password.pwm.http.HttpMethod;
+import password.pwm.http.filter.RequestInitializationFilter;
+import password.pwm.ldap.search.UserSearchEngine;
+import password.pwm.util.LocaleHelper;
+import password.pwm.util.java.AtomicLoopIntIncrementer;
+import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.TimeDuration;
+import password.pwm.util.logging.PwmLogger;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+public abstract class RestServlet extends HttpServlet{
+    private static final AtomicLoopIntIncrementer REQUEST_COUNTER = new AtomicLoopIntIncrementer(Integer.MAX_VALUE);
+
+    private static final PwmLogger LOGGER = PwmLogger.forClass(RestServlet.class);
+
+    protected void service(final HttpServletRequest req, final HttpServletResponse resp)
+            throws ServletException, IOException
+    {
+        final Instant startTime = Instant.now();
+
+        RestResultBean restResultBean = RestResultBean.fromError(new ErrorInformation(PwmError.ERROR_APP_UNAVAILABLE), false);
+
+        final PwmApplication pwmApplication;
+        try {
+            pwmApplication = ContextManager.getContextManager(req.getServletContext()).getPwmApplication();
+        } catch (PwmUnrecoverableException e) {
+            outputRestResultBean(restResultBean, req, resp);
+            return;
+        }
+
+        final Locale locale;
+        {
+            final List<Locale> knownLocales = pwmApplication.getConfig().getKnownLocales();
+            locale = LocaleHelper.localeResolver(req.getLocale(), knownLocales);
+        }
+
+        final SessionLabel sessionLabel;
+        try {
+            sessionLabel = new SessionLabel(
+                    "rest-" + REQUEST_COUNTER.next(),
+                    null,
+                    null,
+                    RequestInitializationFilter.readUserIPAddress(req, pwmApplication.getConfig()),
+                    RequestInitializationFilter.readUserHostname(req, pwmApplication.getConfig())
+            );
+        } catch (PwmUnrecoverableException e) {
+            restResultBean = RestResultBean.fromError(e.getErrorInformation(), pwmApplication, locale, pwmApplication.getConfig(), pwmApplication.determineIfDetailErrorMsgShown());
+            outputRestResultBean(restResultBean, req, resp);
+            return;
+        }
+        LOGGER.trace(sessionLabel, "beginning rest service invocation");
+
+        if (pwmApplication.getApplicationMode() != PwmApplicationMode.RUNNING) {
+            outputRestResultBean(restResultBean, req, resp);
+            return;
+        }
+
+        if (!pwmApplication.getConfig().readSettingAsBoolean(PwmSetting.ENABLE_EXTERNAL_WEBSERVICES)) {
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "webservices are not enabled");
+            restResultBean = RestResultBean.fromError(errorInformation, pwmApplication, locale, pwmApplication.getConfig(), pwmApplication.determineIfDetailErrorMsgShown());
+            outputRestResultBean(restResultBean, req, resp);
+            return;
+        }
+
+        try {
+            final RestAuthentication restAuthentication = new RestAuthenticationProcessor(pwmApplication, sessionLabel, req).readRestAuthentication();
+            LOGGER.debug(sessionLabel, "rest request authentication status: " + JsonUtil.serialize(restAuthentication));
+
+            final RestRequest restRequest = RestRequest.forRequest(pwmApplication, restAuthentication, sessionLabel, req);
+
+            checkRestServiceClassPermissions(restRequest);
+
+            preCheckRequest(restRequest);
+
+            restResultBean = invokeWebService(restRequest);
+        } catch (PwmUnrecoverableException e) {
+            restResultBean = RestResultBean.fromError(e.getErrorInformation(), pwmApplication, locale, pwmApplication.getConfig(), pwmApplication.determineIfDetailErrorMsgShown());
+        } catch (Throwable e) {
+            final String errorMsg = "internal error during rest service invocation: " + e.getMessage();
+            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
+            restResultBean = RestResultBean.fromError(errorInformation, pwmApplication, locale, pwmApplication.getConfig(), pwmApplication.determineIfDetailErrorMsgShown());
+            LOGGER.error(sessionLabel, errorInformation);
+        }
+
+        outputRestResultBean(restResultBean, req, resp);
+        LOGGER.trace(sessionLabel, "completed rest invocation in " + TimeDuration.compactFromCurrent(startTime) + " success=" + !restResultBean.isError());
+    }
+
+    private RestResultBean invokeWebService(final RestRequest restRequest) throws IOException, PwmUnrecoverableException {
+        final Method interestedMethod = discoverMethodForAction(this.getClass(), restRequest);
+
+        if (interestedMethod != null) {
+            interestedMethod.setAccessible(true);
+            try {
+                return (RestResultBean) interestedMethod.invoke(this, restRequest);
+            } catch (InvocationTargetException e) {
+                final Throwable rootException = e.getTargetException();
+                if (rootException instanceof PwmUnrecoverableException) {
+                    throw (PwmUnrecoverableException)rootException;
+                }
+                throw PwmUnrecoverableException.newException(PwmError.ERROR_UNKNOWN, e.getMessage());
+            } catch (IllegalAccessException e) {
+                throw PwmUnrecoverableException.newException(PwmError.ERROR_UNKNOWN, e.getMessage());
+            }
+        }
+        return null;
+    }
+
+    @Data
+    private static class MethodMatcher {
+        boolean methodMatch;
+        boolean contentMatch;
+        boolean acceptMatch;
+    }
+
+    private Method discoverMethodForAction(final Class clazz, final RestRequest restRequest)
+            throws PwmUnrecoverableException
+    {
+        final HttpMethod reqMethod = restRequest.getMethod();
+        final HttpContentType reqContent = restRequest.readContentType();
+        final HttpContentType reqAccept = restRequest.readAcceptType();
+
+        final MethodMatcher anyMatch = new MethodMatcher();
+
+        final Collection<Method> methods = JavaHelper.getAllMethodsForClass(clazz);
+        for (Method method : methods) {
+            final RestMethodHandler annotation = method.getAnnotation(RestMethodHandler.class);
+            final MethodMatcher loopMatch = new MethodMatcher();
+
+            if (annotation != null) {
+                if (annotation.method().length == 0 || Arrays.asList(annotation.method()).contains(reqMethod)) {
+                    loopMatch.setMethodMatch(true);
+                    anyMatch.setMethodMatch(true);
+                }
+
+                if (annotation.consumes().length == 0 || Arrays.asList(annotation.consumes()).contains(reqContent)) {
+                    loopMatch.setContentMatch(true);
+                    anyMatch.setContentMatch(true);
+                }
+
+                if (annotation.produces().length == 0 || Arrays.asList(annotation.produces()).contains(reqAccept)) {
+                    loopMatch.setAcceptMatch(true);
+                    anyMatch.setAcceptMatch(true);
+                }
+
+                if (loopMatch.isMethodMatch() && loopMatch.isContentMatch() && loopMatch.isAcceptMatch()) {
+                    return method;
+                }
+            }
+        }
+
+        final String errorMsg;
+        if (!anyMatch.isMethodMatch()) {
+            errorMsg = "HTTP method invalid";
+        } else if (reqAccept == null && !anyMatch.isAcceptMatch()) {
+            errorMsg = HttpHeader.Accept.getHttpName() + " header is required";
+        } else if (!anyMatch.isAcceptMatch()) {
+            errorMsg = HttpHeader.Accept.getHttpName() + " header value does not match an available processor";
+        } else if (reqContent == null && !anyMatch.isContentMatch()) {
+            errorMsg = HttpHeader.Content_Type.getHttpName() + " header is required";
+        } else if (!anyMatch.isContentMatch()) {
+            errorMsg = HttpHeader.Content_Type.getHttpName() + " header value does not match an available processor";
+        } else {
+            errorMsg = "incorrect method, Content-Type header, or Accept header.";
+        }
+
+        throw PwmUnrecoverableException.newException(PwmError.ERROR_REST_INVOCATION_ERROR, errorMsg);
+    }
+
+    private void checkRestServiceClassPermissions(final RestRequest restRequest)
+            throws PwmUnrecoverableException
+    {
+        final RestWebServer classAnnotation = this.getClass().getDeclaredAnnotation(RestWebServer.class);
+        if (classAnnotation == null) {
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_UNKNOWN, "class is missing " + RestWebServer.class.getSimpleName() + " annotation");
+        }
+
+        if (!restRequest.getRestAuthentication().getUsages().contains(classAnnotation.webService())) {
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_UNAUTHORIZED, "access to " + classAnnotation.webService() + " service is not permitted for this login");
+        }
+
+        if (restRequest.getRestAuthentication().getType() == RestAuthenticationType.PUBLIC) {
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_UNAUTHORIZED, "this service requires authentication");
+        }
+    }
+
+    public abstract void preCheckRequest(RestRequest request) throws PwmUnrecoverableException;
+
+    private void outputRestResultBean(
+            final RestResultBean restResultBean,
+            final HttpServletRequest request,
+            final HttpServletResponse resp
+    )
+            throws IOException
+    {
+        final HttpContentType acceptType = HttpContentType.fromContentTypeHeader(request.getHeader(HttpHeader.Accept.getHttpName()));
+        resp.setHeader(HttpHeader.Server.getHttpName(), PwmConstants.PWM_APP_NAME);
+
+        if (acceptType != null) {
+            switch (acceptType) {
+                case json: {
+                    resp.setHeader(HttpHeader.Content_Type.getHttpName(), HttpContentType.json.getHeaderValue());
+                    try (PrintWriter pw = resp.getWriter()) {
+                        pw.write(restResultBean.toJson());
+                    }
+                }
+                break;
+
+                case plain: {
+                    resp.setHeader(HttpHeader.Content_Type.getHttpName(), HttpContentType.plain.getHeaderValue());
+                    if (restResultBean.isError()) {
+                        resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, restResultBean.getErrorMessage());
+                    } else {
+                        if (restResultBean.getData() != null) {
+                            try (PrintWriter pw = resp.getWriter()) {
+                                pw.write(restResultBean.getData().toString());
+                            }
+                        }
+                    }
+                }
+                break;
+
+                default: {
+                    final String msg = "unhandled " + HttpHeader.Content_Type.getHttpName() + " header value in request";
+                    outputLastHopeError(msg, resp);
+                }
+            }
+        } else {
+            final String msg = "unknown " + HttpHeader.Content_Type.getHttpName() + " header value in request";
+            outputLastHopeError(msg, resp);
+        }
+    }
+
+    private static void outputLastHopeError(final String msg, final HttpServletResponse response) throws IOException {
+        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+        response.setHeader(HttpHeader.Content_Type.getHttpName(), HttpContentType.json.getHeaderValue());
+        try (PrintWriter pw = response.getWriter()) {
+            pw.write("Error: ");
+            pw.write(msg);
+            pw.write("\n");
+        }
+    }
+
+    @Value
+    public static class TargetUserIdentity {
+        private RestRequest restRequest;
+        private UserIdentity userIdentity;
+        private boolean self;
+
+        public ChaiProvider getChaiProvider() throws PwmUnrecoverableException {
+            return restRequest.getChaiProvider(userIdentity.getLdapProfileID());
+        }
+
+        public ChaiUser getChaiUser() throws PwmUnrecoverableException {
+            try {
+                return ChaiFactory.createChaiUser(userIdentity.getUserDN(), getChaiProvider());
+            } catch (ChaiUnavailableException e) {
+                throw PwmUnrecoverableException.fromChaiException(e);
+            }
+        }
+    }
+
+    public static <T> T deserializeJsonBody(final RestRequest restRequest, final Class<T> classOfT)
+            throws IOException, PwmUnrecoverableException
+    {
+        try {
+            final T jsonData = JsonUtil.deserialize(restRequest.readRequestBodyAsString(), classOfT);
+            if (jsonData == null) {
+                throw PwmUnrecoverableException.newException(PwmError.ERROR_REST_INVOCATION_ERROR, "missing json body");
+            }
+            return jsonData;
+        } catch (Exception e) {
+            if (e.getCause() instanceof MalformedJsonException) {
+                throw PwmUnrecoverableException.newException(PwmError.ERROR_REST_INVOCATION_ERROR, "json parse error: " + e.getCause().getMessage());
+            }
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_REST_INVOCATION_ERROR, "json parse error: " + e.getMessage());
+        }
+    }
+
+    public static TargetUserIdentity resolveRequestedUsername(
+            final RestRequest restRequest,
+            final String username
+    )
+            throws PwmUnrecoverableException
+    {
+        final PwmApplication pwmApplication = restRequest.getPwmApplication();
+
+        if (StringUtil.isEmpty(username)) {
+            if (restRequest.getRestAuthentication().getType() == RestAuthenticationType.NAMED_SECRET) {
+                throw PwmUnrecoverableException.newException(PwmError.ERROR_REST_INVOCATION_ERROR, "username field required");
+            }
+        } else {
+            if (!restRequest.getRestAuthentication().isThirdPartyEnabled()) {
+                throw PwmUnrecoverableException.newException(PwmError.ERROR_UNAUTHORIZED, "username specified in request, however third party permission is not granted to the authenticated login.");
+            }
+        }
+
+        if (StringUtil.isEmpty(username)) {
+            if (restRequest.getRestAuthentication().getType() == RestAuthenticationType.LDAP) {
+                return new TargetUserIdentity(restRequest, restRequest.getRestAuthentication().getLdapIdentity(), true);
+            }
+        }
+
+        final String ldapProfileID;
+        final String effectiveUsername;
+        if (username.contains("|")) {
+            final int pipeIndex = username.indexOf("|");
+            ldapProfileID = username.substring(0, pipeIndex);
+            effectiveUsername = username.substring(pipeIndex + 1, username.length());
+        } else {
+            ldapProfileID = null;
+            effectiveUsername = username;
+        }
+
+        try {
+            final UserSearchEngine userSearchEngine = pwmApplication.getUserSearchEngine();
+            final UserIdentity userIdentity = userSearchEngine.resolveUsername(effectiveUsername, null, ldapProfileID, restRequest.getSessionLabel());
+            return new TargetUserIdentity(restRequest, userIdentity, false);
+        } catch (PwmOperationalException e) {
+            throw new PwmUnrecoverableException(e.getErrorInformation());
+        }
+    }
+
+}

+ 12 - 0
server/src/main/java/password/pwm/ws/server/RestWebServer.java

@@ -0,0 +1,12 @@
+package password.pwm.ws.server;
+
+import password.pwm.config.option.WebServiceUsage;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RestWebServer {
+    WebServiceUsage webService();
+    boolean requireAuthentication();
+}

+ 0 - 68
server/src/main/java/password/pwm/ws/server/ServicePermissions.java

@@ -1,68 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.ws.server;
-
-import lombok.Builder;
-import lombok.Getter;
-
-import java.io.Serializable;
-
-@Getter
-@Builder(toBuilder = true)
-public class ServicePermissions implements Serializable {
-
-    public static final ServicePermissions PUBLIC = ServicePermissions.builder()
-            .adminOnly(false)
-            .authRequired(false)
-            .blockExternal(false)
-            .helpdeskPermitted(true)
-            .build();
-
-    public static final ServicePermissions ADMIN_OR_CONFIGMODE = ServicePermissions.builder()
-            .adminOnly(false)
-            .authRequired(false)
-            .blockExternal(true)
-            .publicDuringConfig(true)
-            .build();
-
-    public static final ServicePermissions ADMIN_LOCAL_OR_EXTERNAL = ServicePermissions.builder()
-            .adminOnly(true)
-            .authRequired(true)
-            .blockExternal(false)
-            .build();
-
-    @Builder.Default
-    private boolean authRequired = true;
-
-    @Builder.Default
-    private boolean adminOnly = true;
-
-    @Builder.Default
-    private boolean blockExternal = true;
-
-    @Builder.Default
-    private boolean publicDuringConfig = false;
-
-    @Builder.Default
-    private boolean helpdeskPermitted = false;
-}

+ 0 - 96
server/src/main/java/password/pwm/ws/server/StandaloneRestHelper.java

@@ -1,96 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.ws.server;
-
-import com.novell.ldapchai.util.StringHelper;
-import password.pwm.AppProperty;
-import password.pwm.PwmApplication;
-import password.pwm.config.PwmSetting;
-import password.pwm.config.option.WebServiceUsage;
-import password.pwm.config.value.data.NamedSecretData;
-import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.ContextManager;
-import password.pwm.http.PwmHttpRequestWrapper;
-import password.pwm.util.BasicAuthInfo;
-import password.pwm.util.java.JavaHelper;
-import password.pwm.util.logging.PwmLogger;
-
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class StandaloneRestHelper {
-    private static final PwmLogger LOGGER = PwmLogger.forClass(StandaloneRestHelper.class);
-
-    public static StandaloneRestRequestBean initialize(final HttpServletRequest httpServletRequest)
-            throws PwmUnrecoverableException, IOException
-    {
-        final PwmApplication pwmApplication = ContextManager.getPwmApplication(httpServletRequest.getServletContext());
-
-        final Set<WebServiceUsage> usages = readWebServiceSecretAuthorizations(pwmApplication, httpServletRequest);
-
-        final int maxChars = Integer.parseInt(pwmApplication.getConfig().readAppProperty(AppProperty.HTTP_BODY_MAXREAD_LENGTH));
-        final String body = PwmHttpRequestWrapper.readRequestBodyAsString(httpServletRequest, maxChars);
-
-        return StandaloneRestRequestBean.builder()
-                .pwmApplication(pwmApplication)
-                .authorizedUsages(Collections.unmodifiableSet(usages))
-                .body(body)
-                .build();
-    }
-
-    private static Set<WebServiceUsage> readWebServiceSecretAuthorizations(
-            final PwmApplication pwmApplication,
-            final HttpServletRequest httpServletRequest
-    )
-    {
-        final Set<WebServiceUsage> usages = new HashSet<>();
-
-        final BasicAuthInfo basicAuthInfo = BasicAuthInfo.parseAuthHeader(pwmApplication, httpServletRequest);
-        if (basicAuthInfo != null) {
-            final Map<String, NamedSecretData> secrets = pwmApplication.getConfig().readSettingAsNamedPasswords(PwmSetting.WEBSERVICES_EXTERNAL_SECRET);
-            final NamedSecretData namedSecretData = secrets.get(basicAuthInfo.getUsername());
-            if (namedSecretData != null) {
-                if (namedSecretData.getPassword().equals(basicAuthInfo.getPassword())) {
-                    final List<WebServiceUsage> namedSecrets = JavaHelper.readEnumListFromStringCollection(WebServiceUsage.class, namedSecretData.getUsage());
-                    usages.addAll(namedSecrets);
-                    LOGGER.trace("REST request to " + httpServletRequest.getRequestURI() + " specified a basic auth username (\""
-                            + basicAuthInfo.getUsername() + "\"), granting usage access to "
-                            + StringHelper.stringCollectionToString(namedSecretData.getUsage(), ","));
-                } else {
-                    LOGGER.trace("REST request to " + httpServletRequest.getRequestURI() + " specified a basic auth username (\""
-                            + basicAuthInfo.getUsername() + "\") with an incorrect password");
-                }
-            } else {
-                LOGGER.trace("REST request to " + httpServletRequest.getRequestURI() + " specified a basic auth username (\""
-                        + basicAuthInfo.getUsername() + "\") that does not correspond to a configured web secret");
-            }
-        }
-
-        return Collections.unmodifiableSet(usages);
-    }
-}

+ 0 - 39
server/src/main/java/password/pwm/ws/server/StandaloneRestRequestBean.java

@@ -1,39 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.ws.server;
-
-import lombok.Builder;
-import lombok.Getter;
-import password.pwm.PwmApplication;
-import password.pwm.config.option.WebServiceUsage;
-
-import java.util.Set;
-
-@Getter
-@Builder
-public class StandaloneRestRequestBean {
-    private final Set<WebServiceUsage> authorizedUsages;
-    private final PwmApplication pwmApplication;
-    private final String body;
-
-}

+ 0 - 41
server/src/main/java/password/pwm/ws/server/rest/AbstractRestServer.java

@@ -1,41 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.ws.server.rest;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.core.Context;
-
-public abstract class AbstractRestServer {
-    @Context
-    HttpServletRequest request;
-
-    @Context
-    HttpServletResponse response;
-
-    @Context
-    ServletContext context;
-
-
-}

+ 0 - 34
server/src/main/java/password/pwm/ws/server/rest/PwmResourceConfig.java

@@ -1,34 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.ws.server.rest;
-
-import org.glassfish.jersey.message.DeflateEncoder;
-import org.glassfish.jersey.message.GZipEncoder;
-import org.glassfish.jersey.server.ResourceConfig;
-import org.glassfish.jersey.server.filter.EncodingFilter;
-
-public class PwmResourceConfig extends ResourceConfig {
-    public PwmResourceConfig() {
-        registerClasses(EncodingFilter.class, GZipEncoder.class, DeflateEncoder.class);
-    }
-}

+ 106 - 216
server/src/main/java/password/pwm/ws/server/rest/RestChallengesServer.java

@@ -28,46 +28,36 @@ import com.novell.ldapchai.cr.Challenge;
 import com.novell.ldapchai.cr.ChallengeSet;
 import com.novell.ldapchai.cr.ResponseSet;
 import com.novell.ldapchai.cr.bean.ChallengeBean;
-import password.pwm.Permission;
+import lombok.Data;
+import password.pwm.PwmConstants;
 import password.pwm.bean.ResponseInfoBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
+import password.pwm.config.option.WebServiceUsage;
 import password.pwm.config.profile.ChallengeProfile;
-import password.pwm.config.profile.HelpdeskProfile;
 import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
-import password.pwm.error.PwmException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.i18n.Message;
 import password.pwm.ldap.LdapOperationsHelper;
-import password.pwm.svc.event.AuditEvent;
-import password.pwm.svc.event.AuditRecordFactory;
-import password.pwm.svc.event.HelpdeskAuditRecord;
-import password.pwm.svc.event.UserAuditRecord;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.operations.CrService;
 import password.pwm.util.operations.PasswordUtility;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
+
+import javax.servlet.annotation.WebServlet;
+import java.io.IOException;
 import java.io.Serializable;
-import java.net.URISyntaxException;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
@@ -75,21 +65,22 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
-@Path("/challenges")
-public class RestChallengesServer extends AbstractRestServer {
-
-    private static final ServicePermissions SERVICE_PERMISSIONS = ServicePermissions.builder()
-            .adminOnly(false)
-            .authRequired(true)
-            .blockExternal(true)
-            .build();
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/challenges"
+        }
+)
+@RestWebServer(webService = WebServiceUsage.CheckPassword, requireAuthentication = true)
+public class RestChallengesServer extends RestServlet {
 
+    @Data
     public static class Policy {
         public List<ChallengeBean> challenges;
         public List<ChallengeBean> helpdeskChallenges;
         public int minimumRandoms;
     }
 
+    @Data
     public static class JsonChallengesData implements Serializable {
         public String username;
         public List<ChallengeBean> challenges;
@@ -138,71 +129,55 @@ public class RestChallengesServer extends AbstractRestServer {
         }
     }
 
-    @Context
-    HttpServletRequest request;
-
-    @GET
-    @Produces(MediaType.TEXT_HTML)
-    public javax.ws.rs.core.Response doHtmlRedirect() throws URISyntaxException {
-        return RestServerHelper.handleHtmlRequest();
+    @Override
+    public void preCheckRequest(final RestRequest request) throws PwmUnrecoverableException {
     }
 
-    @GET
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doFormGetChallengeData(
-            @QueryParam("answers") final boolean answers,
-            @QueryParam("helpdesk") final boolean helpdesk,
-            @QueryParam("username") final String username
-    )
+    @RestMethodHandler(method = HttpMethod.GET, produces = HttpContentType.json)
+    public RestResultBean doFormGetChallengeData(final RestRequest restRequest)
+
             throws PwmUnrecoverableException
     {
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
+        final boolean answers = restRequest.readParameterAsBoolean("answers");
+        final boolean helpdesk = restRequest.readParameterAsBoolean("helpdesk");
+        final String username = restRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation);
 
         try {
-            if (answers && !restRequestBean.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.ENABLE_WEBSERVICES_READANSWERS)) {
+            if (answers && !restRequest.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.ENABLE_WEBSERVICES_READANSWERS)) {
                 throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE,"retrieval of answers is not permitted"));
             }
 
+            final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, username);
+
             // gather data
             final ResponseSet responseSet;
             final ChallengeSet challengeSet;
             final ChallengeSet helpdeskChallengeSet;
             final String outputUsername;
 
-            if (restRequestBean.getUserIdentity() == null) {
-                final ChaiUser chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication());
-                final CrService crService = restRequestBean.getPwmApplication().getCrService();
-                responseSet = crService.readUserResponseSet(restRequestBean.getPwmSession().getLabel(), restRequestBean.getPwmSession().getUserInfo().getUserIdentity(), chaiUser);
-                challengeSet = restRequestBean.getPwmSession().getUserInfo().getChallengeProfile().getChallengeSet();
-                helpdeskChallengeSet = restRequestBean.getPwmSession().getUserInfo().getChallengeProfile().getHelpdeskChallengeSet();
-                outputUsername = restRequestBean.getPwmSession().getUserInfo().getUserIdentity().getLdapProfileID();
-            } else {
-                final ChaiUser chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),restRequestBean.getUserIdentity());
-                final Locale userLocale = restRequestBean.getPwmSession().getSessionStateBean().getLocale();
-                final CrService crService = restRequestBean.getPwmApplication().getCrService();
-                responseSet = crService.readUserResponseSet(restRequestBean.getPwmSession().getLabel(),restRequestBean.getUserIdentity(), chaiUser);
-                final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
-                        restRequestBean.getPwmApplication(),
-                        restRequestBean.getPwmSession().getLabel(),
-                        restRequestBean.getUserIdentity(),
-                        chaiUser,userLocale
-                );
-                final ChallengeProfile challengeProfile = crService.readUserChallengeProfile(
-                        restRequestBean.getPwmSession().getLabel(),
-                        restRequestBean.getUserIdentity(),
-                        chaiUser,
-                        passwordPolicy,
-                        userLocale
-                );
-                challengeSet = challengeProfile.getChallengeSet();
-                helpdeskChallengeSet = challengeProfile.getHelpdeskChallengeSet();
-                outputUsername = restRequestBean.getUserIdentity().toDelimitedKey();
-            }
+            final ChaiUser chaiUser = targetUserIdentity.getChaiUser();
+            final Locale userLocale = restRequest.getLocale();
+            final CrService crService = restRequest.getPwmApplication().getCrService();
+            responseSet = crService.readUserResponseSet(restRequest.getSessionLabel(), targetUserIdentity.getUserIdentity(), chaiUser);
+
+            final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity(),
+                    chaiUser,
+                    userLocale
+            );
+            final ChallengeProfile challengeProfile = crService.readUserChallengeProfile(
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity(),
+                    chaiUser,
+                    passwordPolicy,
+                    userLocale
+            );
+
+            challengeSet = challengeProfile.getChallengeSet();
+            helpdeskChallengeSet = challengeProfile.getHelpdeskChallengeSet();
+            outputUsername = targetUserIdentity.getUserIdentity().toDelimitedKey();
 
             // build output
             final JsonChallengesData jsonData = new JsonChallengesData();
@@ -229,182 +204,97 @@ public class RestChallengesServer extends AbstractRestServer {
             }
 
             // update statistics
-            if (!restRequestBean.isExternal()) {
-                StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_CHALLENGES);
-            }
-
-            final RestResultBean resultBean = new RestResultBean();
-            resultBean.setData(jsonData);
-            return resultBean.asJsonResponse();
-        } catch (PwmException e) {
-            return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();
+            StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_CHALLENGES);
+            return RestResultBean.withData(jsonData);
         } catch (Exception e) {
             final String errorMsg = "unexpected error building json response: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
-    @POST
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response doSetChallengeDataJson(
-            final JsonChallengesData jsonInput
-    )
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
+    public RestResultBean doSetChallengeDataJson(final RestRequest restRequest)
+            throws IOException, PwmUnrecoverableException
     {
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, jsonInput.username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
+        final JsonChallengesData jsonInput = deserializeJsonBody(restRequest, JsonChallengesData.class);
 
-        try {
-            if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.SETUP_RESPONSE)) {
-                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"actor does not have required permission"));
-            }
+        final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, jsonInput.getUsername());
 
+        try {
             final ChaiUser chaiUser;
             final String userGUID;
             final String csIdentifer;
             final UserIdentity userIdentity;
-            final CrService crService = restRequestBean.getPwmApplication().getCrService();
-
-            if (restRequestBean.getUserIdentity() == null) {
-                chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication());
-                userIdentity = restRequestBean.getPwmSession().getUserInfo().getUserIdentity();
-                userGUID = restRequestBean.getPwmSession().getUserInfo().getUserGuid();
-                csIdentifer = restRequestBean.getPwmSession().getUserInfo().getChallengeProfile().getChallengeSet().getIdentifier();
-            } else {
-                userIdentity = restRequestBean.getUserIdentity();
-                chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),userIdentity);
-                userGUID = LdapOperationsHelper.readLdapGuidValue(
-                        restRequestBean.getPwmApplication(),
-                        restRequestBean.getPwmSession().getLabel(),
-                        userIdentity,
-                        false);
-                final ChallengeProfile challengeProfile = crService.readUserChallengeProfile(
-                        restRequestBean.getPwmSession().getLabel(),
-                        userIdentity,
-                        chaiUser,
-                        PwmPasswordPolicy.defaultPolicy(),
-                        request.getLocale());
-                csIdentifer = challengeProfile.getChallengeSet().getIdentifier();
-            }
+            final CrService crService = restRequest.getPwmApplication().getCrService();
+
+            userIdentity = targetUserIdentity.getUserIdentity();
+            chaiUser = targetUserIdentity.getChaiUser();
+            userGUID = LdapOperationsHelper.readLdapGuidValue(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    userIdentity,
+                    false
+            );
+            final ChallengeProfile challengeProfile = crService.readUserChallengeProfile(
+                    restRequest.getSessionLabel(),
+                    userIdentity,
+                    chaiUser,
+                    PwmPasswordPolicy.defaultPolicy(),
+                    restRequest.getLocale()
+            );
 
-            final ResponseInfoBean responseInfoBean = jsonInput.toResponseInfoBean(request.getLocale(), csIdentifer);
+            csIdentifer = challengeProfile.getChallengeSet().getIdentifier();
+
+            final ResponseInfoBean responseInfoBean = jsonInput.toResponseInfoBean(restRequest.getLocale(), csIdentifer);
             crService.writeResponses(userIdentity, chaiUser,userGUID,responseInfoBean);
 
             // update statistics
-            if (!restRequestBean.isExternal()) {
-                StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_CHALLENGES);
-            }
+            StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_CHALLENGES);
 
-            final String successMsg = Message.Success_SetupResponse.getLocalizedMessage(request.getLocale(),restRequestBean.getPwmApplication().getConfig());
-            final RestResultBean resultBean = new RestResultBean();
-            resultBean.setError(false);
-            resultBean.setSuccessMessage(successMsg);
-            return resultBean.asJsonResponse();
-        } catch (PwmException e) {
-            return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();
+            return RestResultBean.forSuccessMessage(restRequest, Message.Success_SetupResponse);
         } catch (Exception e) {
             final String errorMsg = "unexpected error reading json input: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
-    @DELETE
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doDeleteChallengeData(
-            @QueryParam("username") final String username
-    )
+    @RestMethodHandler(method = HttpMethod.DELETE, produces = HttpContentType.json)
+    public RestResultBean doDeleteChallengeData(final RestRequest restRequest)
+            throws IOException, PwmUnrecoverableException
     {
-        final RestRequestBean restRequestBean;
-        try {
-            final ServicePermissions servicePermissions = SERVICE_PERMISSIONS.toBuilder()
-                    .helpdeskPermitted(true)
-                    .build();
+        final String username = restRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation);
 
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
+        final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, username);
 
         try {
-            final HelpdeskProfile helpdeskProfile = restRequestBean.getPwmSession().getSessionManager().getHelpdeskProfile(restRequestBean.getPwmApplication());
-            if (restRequestBean.getUserIdentity() == null) {
-                if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.SETUP_RESPONSE)) {
-                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"actor does not have required permission"));
-                }
-            } else {
-                if (helpdeskProfile == null) {
-                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"actor does not have required permission"));
-                }
-            }
-
             final ChaiUser chaiUser;
             final String userGUID;
-            if (restRequestBean.getUserIdentity() == null) {
-                /* clear self */
-                chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication());
-                userGUID = restRequestBean.getPwmSession().getUserInfo().getUserGuid();
-
-                // mark the event log
-                final UserAuditRecord auditRecord = new AuditRecordFactory(restRequestBean.getPwmApplication(), restRequestBean.getPwmSession()).createUserAuditRecord(
-                        AuditEvent.CLEAR_RESPONSES,
-                        restRequestBean.getPwmSession().getUserInfo(),
-                        restRequestBean.getPwmSession()
-                );
-                restRequestBean.getPwmApplication().getAuditManager().submit(auditRecord);
-            } else {
-                /* clear 3rd party (helpdesk) */
-                final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
-                chaiUser = useProxy
-                        ? restRequestBean.getPwmApplication().getProxiedChaiUser(restRequestBean.getUserIdentity())
-                        : restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),restRequestBean.getUserIdentity());
-                userGUID = LdapOperationsHelper.readLdapGuidValue(
-                        restRequestBean.getPwmApplication(),
-                        restRequestBean.getPwmSession().getLabel(),
-                        restRequestBean.getUserIdentity(),
-                        false);
-
-                // mark the event log
-                final HelpdeskAuditRecord auditRecord = new AuditRecordFactory(restRequestBean.getPwmApplication(), restRequestBean.getPwmSession()).createHelpdeskAuditRecord(
-                        AuditEvent.HELPDESK_CLEAR_RESPONSES,
-                        restRequestBean.getPwmSession().getUserInfo().getUserIdentity(),
-                        null,
-                        restRequestBean.getUserIdentity(),
-                        restRequestBean.getPwmSession().getSessionStateBean().getSrcAddress(),
-                        restRequestBean.getPwmSession().getSessionStateBean().getSrcHostname()
-                );
-                restRequestBean.getPwmApplication().getAuditManager().submit(auditRecord);
-            }
 
-            final CrService crService = restRequestBean.getPwmApplication().getCrService();
+            chaiUser = targetUserIdentity.getChaiUser();
+            userGUID = LdapOperationsHelper.readLdapGuidValue(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity(),
+                    false);
+
+            final CrService crService = restRequest.getPwmApplication().getCrService();
             crService.clearResponses(
-                    restRequestBean.getPwmSession().getLabel(),
-                    restRequestBean.getUserIdentity(),
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity(),
                     chaiUser,
                     userGUID
             );
 
             // update statistics
-            if (!restRequestBean.isExternal()) {
-                StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_CHALLENGES);
-            }
+            StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_CHALLENGES);
 
-            final String successMsg = Message.Success_Unknown.getLocalizedMessage(request.getLocale(),restRequestBean.getPwmApplication().getConfig());
-            final RestResultBean resultBean = new RestResultBean();
-            resultBean.setError(false);
-            resultBean.setSuccessMessage(successMsg);
-            return resultBean.asJsonResponse();
-        } catch (PwmException e) {
-            return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();
+            return RestResultBean.forSuccessMessage(restRequest, Message.Success_Unknown);
         } catch (Exception e) {
             final String errorMsg = "unexpected error delete responses: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 

+ 80 - 152
server/src/main/java/password/pwm/ws/server/rest/RestCheckPasswordServer.java

@@ -23,55 +23,54 @@
 package password.pwm.ws.server.rest;
 
 
-import com.novell.ldapchai.ChaiUser;
-import com.novell.ldapchai.exception.ChaiUnavailableException;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
-import password.pwm.PwmApplication;
-import password.pwm.bean.LoginInfoBean;
+import password.pwm.PwmConstants;
 import password.pwm.bean.UserIdentity;
-import password.pwm.config.PwmSetting;
-import password.pwm.config.profile.HelpdeskProfile;
+import password.pwm.config.option.WebServiceUsage;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.PwmSession;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfoFactory;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.util.PasswordData;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.operations.PasswordUtility;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.FormParam;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import javax.servlet.annotation.WebServlet;
+import java.io.IOException;
 import java.io.Serializable;
 import java.time.Instant;
 
-@Path("/checkpassword")
-public class RestCheckPasswordServer extends AbstractRestServer {
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/checkpassword"
+        }
+)
+@RestWebServer(webService = WebServiceUsage.CheckPassword, requireAuthentication = true)
+public class RestCheckPasswordServer extends RestServlet {
     private static final PwmLogger LOGGER = PwmLogger.forClass(RestCheckPasswordServer.class);
 
     @Getter
     @Setter
     @AllArgsConstructor
     @NoArgsConstructor
-    public static class JsonInput implements Serializable
-    {
+    public static class JsonInput implements Serializable {
         public String password1;
         public String password2;
         public String username;
@@ -81,8 +80,7 @@ public class RestCheckPasswordServer extends AbstractRestServer {
     @Setter
     @AllArgsConstructor
     @NoArgsConstructor
-    public static class JsonOutput implements Serializable
-    {
+    public static class JsonOutput implements Serializable {
         public int version;
         public int strength;
         public PasswordUtility.PasswordCheckInfo.MatchStatus match;
@@ -104,164 +102,94 @@ public class RestCheckPasswordServer extends AbstractRestServer {
         }
     }
 
-    @POST
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response doPasswordRuleCheckFormPost(
-            @FormParam("password1") final String password1,
-            @FormParam("password2") final String password2,
-            @FormParam("username") final String username
-    )
-            throws PwmUnrecoverableException
-    {
-        final JsonInput jsonInput = new JsonInput(password1, password2, username);
-        return doOperation(jsonInput);
+    @Override
+    public void preCheckRequest(final RestRequest request) throws PwmUnrecoverableException {
+
     }
 
-    @POST
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response doPasswordRuleCheckJsonPost(final JsonInput jsonInput)
-            throws PwmUnrecoverableException
-    {
-        return doOperation(jsonInput);
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.form, produces = HttpContentType.json)
+    public RestResultBean doPasswordRuleCheckFormPost(final RestRequest restRequest)
+            throws PwmUnrecoverableException {
+
+
+        final JsonInput jsonInput = new JsonInput(
+                restRequest.readParameterAsString("password1", PwmHttpRequestWrapper.Flag.BypassValidation),
+                restRequest.readParameterAsString("password2", PwmHttpRequestWrapper.Flag.BypassValidation),
+                restRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation)
+        );
+        return doOperation(restRequest, jsonInput);
     }
 
-    public Response doOperation(final JsonInput jsonInput)
-            throws PwmUnrecoverableException
-    {
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
+    public RestResultBean doPasswordRuleCheckJsonPost(final RestRequest restRequest)
+            throws PwmUnrecoverableException, IOException {
+        final JsonInput jsonInput = deserializeJsonBody(restRequest, JsonInput.class);
+        return doOperation(restRequest, jsonInput);
+    }
+
+    public RestResultBean doOperation(final RestRequest restRequest, final JsonInput jsonInput)
+            throws PwmUnrecoverableException {
         final Instant startTime = Instant.now();
-        final RestRequestBean restRequestBean;
-        try {
-            final ServicePermissions servicePermissions = ServicePermissions.builder()
-                    .adminOnly(false)
-                    .authRequired(true)
-                    .blockExternal(true)
-                    .helpdeskPermitted(true)
-                    .build();
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, jsonInput.username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
-        LOGGER.trace("beginning check password operation for user " + restRequestBean.getPwmSession().getUserInfo().getUserIdentity());
 
-        if (jsonInput.password1 == null || jsonInput.password1.length() < 1) {
+        if (StringUtil.isEmpty(jsonInput.getPassword1())) {
             final String errorMessage = "missing field 'password1'";
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_FIELD_REQUIRED, errorMessage, new String[]{"password1"});
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
 
         try {
-            final UserIdentity userIdentity;
-            final UserInfo userInfo;
-            if (restRequestBean.getUserIdentity() != null) { // check for another user
-                userIdentity = restRequestBean.getUserIdentity();
-                userInfo = UserInfoFactory.newUserInfo(
-                        restRequestBean.getPwmApplication(),
-                        restRequestBean.getPwmSession().getLabel(),
-                        restRequestBean.getPwmSession().getSessionStateBean().getLocale(),
-                        userIdentity,
-                        restRequestBean.getPwmSession().getSessionManager().getChaiProvider()
-                );
-            } else { // self check
-                userIdentity = restRequestBean.getPwmSession().getUserInfo().getUserIdentity();
-                userInfo = restRequestBean.getPwmSession().getUserInfo();
-            }
+
+            final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, jsonInput.getUsername());
+            final UserInfo userInfo = UserInfoFactory.newUserInfo(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    restRequest.getLocale(),
+                    targetUserIdentity.getUserIdentity(),
+                    targetUserIdentity.getChaiProvider()
+            );
 
             final PasswordCheckRequest checkRequest = new PasswordCheckRequest(
-                    userIdentity,
-                    jsonInput.password1 == null || jsonInput.password1.isEmpty() ? null : new PasswordData(jsonInput.password1),
-                    jsonInput.password2 == null || jsonInput.password2.isEmpty() ? null : new PasswordData(jsonInput.password2),
+                    targetUserIdentity.getUserIdentity(),
+                    StringUtil.isEmpty(jsonInput.getPassword1()) ? null : new PasswordData(jsonInput.getPassword1()),
+                    StringUtil.isEmpty(jsonInput.getPassword2()) ? null : new PasswordData(jsonInput.getPassword2()),
                     userInfo
             );
 
-            if (restRequestBean.isExternal()) {
-                restRequestBean.getPwmApplication().getStatisticsManager().incrementValue(Statistic.REST_CHECKPASSWORD);
-            } else {
-                restRequestBean.getPwmApplication().getStatisticsManager().incrementValue(Statistic.PASSWORD_RULE_CHECKS);
-            }
+            restRequest.getPwmApplication().getStatisticsManager().incrementValue(Statistic.REST_CHECKPASSWORD);
+
+            final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
+                    restRequest.getPwmApplication(),
+                    restRequest.getLocale(),
+                    targetUserIdentity.getChaiUser(),
+                    checkRequest.getUserInfo(),
+                    null,
+                    checkRequest.getPassword1(),
+                    checkRequest.getPassword2()
+            );
 
-            final JsonOutput jsonData = doPasswordRuleCheck(restRequestBean.getPwmApplication(), restRequestBean.getPwmSession(), checkRequest);
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(jsonData);
+            final JsonOutput jsonOutput = JsonOutput.fromPasswordCheckInfo(passwordCheckInfo);
+            final RestResultBean restResultBean = RestResultBean.withData(jsonOutput);
             final TimeDuration timeDuration = TimeDuration.fromCurrent(startTime);
-            LOGGER.trace(restRequestBean.getPwmSession(), "REST /checkpassword response (" + timeDuration.asCompactString() + "): " + JsonUtil.serialize(jsonData));
-            return restResultBean.asJsonResponse();
+            LOGGER.trace(restRequest.getSessionLabel(), "REST /checkpassword response (" + timeDuration.asCompactString() + "): " + JsonUtil.serialize(jsonOutput));
+            return restResultBean;
         } catch (PwmException e) {
-            LOGGER.debug(restRequestBean.getPwmSession(), "REST /checkpassword error during execution: " + e.getMessage(), e);
-            return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();
+            LOGGER.debug(restRequest.getSessionLabel(), "REST /checkpassword error during execution: " + e.getMessage());
+            return RestResultBean.fromError(restRequest, e.getErrorInformation());
         } catch (Exception e) {
             final String errorMessage = "unexpected error executing web service: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
-            LOGGER.error(restRequestBean.getPwmSession(),errorInformation.toDebugStr(),e);
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
+            LOGGER.error(restRequest.getSessionLabel(), errorInformation.toDebugStr(), e);
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
+    @Getter
+    @AllArgsConstructor
     public static class PasswordCheckRequest {
         final UserIdentity userIdentity;
         final PasswordData password1;
         final PasswordData password2;
         final UserInfo userInfo;
-
-        public PasswordCheckRequest(
-                final UserIdentity userDN,
-                final PasswordData password1,
-                final PasswordData password2,
-                final UserInfo userInfo
-        ) {
-            this.userIdentity = userDN;
-            this.password1 = password1;
-            this.password2 = password2;
-            this.userInfo = userInfo;
-        }
-
-        public UserIdentity getUserIdentity() {
-            return userIdentity;
-        }
-
-        public PasswordData getPassword1() {
-            return password1;
-        }
-
-        public PasswordData getPassword2() {
-            return password2;
-        }
-
-        public UserInfo getUserInfo() {
-            return userInfo;
-        }
-    }
-
-
-    public static JsonOutput doPasswordRuleCheck(
-            final PwmApplication pwmApplication,
-            final PwmSession pwmSession,
-            final PasswordCheckRequest checkRequest
-    )
-            throws PwmUnrecoverableException, ChaiUnavailableException
-    {
-        final HelpdeskProfile helpdeskProfile = pwmSession.getSessionManager().getHelpdeskProfile(pwmApplication);
-        final boolean useProxy = helpdeskProfile != null && helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
-        final boolean thirdParty = checkRequest.getUserIdentity() != null
-                && !checkRequest.getUserIdentity().canonicalEquals(pwmSession.getUserInfo().getUserIdentity(), pwmApplication);
-
-        final ChaiUser user = useProxy && thirdParty
-                ? pwmApplication.getProxiedChaiUser(checkRequest.getUserIdentity())
-                : pwmSession.getSessionManager().getActor(pwmApplication, checkRequest.getUserIdentity());
-        final LoginInfoBean loginInfoBean = thirdParty
-                ? null
-                : pwmSession.getLoginInfoBean();
-        final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
-                pwmApplication,
-                pwmSession.getSessionStateBean().getLocale(),
-                user,
-                checkRequest.getUserInfo(),
-                loginInfoBean,
-                checkRequest.getPassword1(),
-                checkRequest.getPassword2()
-        );
-
-        return JsonOutput.fromPasswordCheckInfo(passwordCheckInfo);
     }
 }
+

+ 25 - 17
server/src/main/java/password/pwm/ws/server/rest/RestFormSigningServer.java

@@ -31,36 +31,49 @@ import password.pwm.config.option.WebServiceUsage;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
 import password.pwm.http.HttpMethod;
-import password.pwm.util.java.JsonUtil;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.secure.SecureService;
-import password.pwm.ws.server.PwmRestServlet;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.StandaloneRestRequestBean;
+import password.pwm.ws.server.RestAuthenticationType;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
 
 import javax.servlet.annotation.WebServlet;
+import java.io.IOException;
 import java.io.Serializable;
 import java.time.Instant;
-import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 @WebServlet(
-        name="RestFormSigningServer",
         urlPatterns={
                 PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/signing/form",
         }
 )
-public class RestFormSigningServer extends PwmRestServlet {
-
+@RestWebServer(webService = WebServiceUsage.SigningForm, requireAuthentication = true)
+public class RestFormSigningServer extends RestServlet {
 
     @Override
-    public RestResultBean invokeWebService(final StandaloneRestRequestBean restRequestBean) {
-        final Map<String,String> inputFormData = JsonUtil.deserializeStringMap(restRequestBean.getBody());
+    public void preCheckRequest(final RestRequest restRequest)
+            throws PwmUnrecoverableException {
+        if (restRequest.getRestAuthentication().getType() == RestAuthenticationType.PUBLIC) {
+            throw new PwmUnrecoverableException(PwmError.ERROR_AUTHENTICATION_REQUIRED);
+        }
+    }
+
+    @RestMethodHandler(method = HttpMethod.POST, produces = HttpContentType.json)
+    private RestResultBean handleRestJsonPostRequest(final RestRequest restRequest)
+            throws IOException, PwmUnrecoverableException
+    {
 
+        final Map<String,String> inputFormData = restRequest.readBodyAsJsonStringMap(PwmHttpRequestWrapper.Flag.BypassValidation);
 
-        if (!restRequestBean.getAuthorizedUsages().contains(WebServiceUsage.SigningForm)) {
+        if (!restRequest.getRestAuthentication().getUsages().contains(WebServiceUsage.SigningForm)) {
             final String errorMsg = "request is not authenticated with permission for SigningForm";
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNAUTHORIZED, errorMsg);
             return RestResultBean.fromError(errorInformation);
@@ -68,10 +81,10 @@ public class RestFormSigningServer extends PwmRestServlet {
 
         try {
             if (inputFormData != null) {
-                final SecureService securityService = restRequestBean.getPwmApplication().getSecureService();
+                final SecureService securityService = restRequest.getPwmApplication().getSecureService();
                 final SignedFormData signedFormData = new SignedFormData(Instant.now(), inputFormData);
                 final String signedValue = securityService.encryptObjectToString(signedFormData);
-                final RestResultBean restResultBean = new RestResultBean(signedValue);
+                final RestResultBean restResultBean = RestResultBean.withData(signedValue);
                 return restResultBean;
             }
             return RestResultBean.fromError(new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"no json form in body"));
@@ -107,9 +120,4 @@ public class RestFormSigningServer extends PwmRestServlet {
         private Instant timestamp;
         private Map<String,String> formData;
     }
-
-    @Override
-    public PwmRestServlet.ServiceInfo getServiceInfo() {
-        return new ServiceInfo(Collections.singleton(HttpMethod.POST));
-    }
 }

+ 37 - 94
server/src/main/java/password/pwm/ws/server/rest/RestHealthServer.java

@@ -22,120 +22,76 @@
 
 package password.pwm.ws.server.rest;
 
-import com.novell.ldapchai.exception.ChaiUnavailableException;
 import password.pwm.PwmApplication;
-import password.pwm.config.Configuration;
-import password.pwm.config.PwmSetting;
+import password.pwm.PwmConstants;
 import password.pwm.config.option.WebServiceUsage;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
-import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthMonitor;
-import password.pwm.http.ContextManager;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
-import password.pwm.ws.server.StandaloneRestHelper;
-import password.pwm.ws.server.StandaloneRestRequestBean;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
 import password.pwm.ws.server.rest.bean.HealthData;
 import password.pwm.ws.server.rest.bean.HealthRecord;
 
-import javax.servlet.ServletException;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
-@Path("/health")
-public class RestHealthServer extends AbstractRestServer {
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/health",
+        }
+)
+@RestWebServer(webService = WebServiceUsage.Health, requireAuthentication = false)
+public class RestHealthServer extends RestServlet {
     private static final PwmLogger LOGGER = PwmLogger.forClass(RestHealthServer.class);
 
     private static final String PARAM_IMMEDIATE_REFRESH = "refreshImmediate";
 
-    @GET
-    @Produces(MediaType.TEXT_PLAIN)
-    public String doPwmHealthPlainGet(
-            @QueryParam(PARAM_IMMEDIATE_REFRESH) final boolean requestImmediateParam
-    ) {
-
-        final PwmApplication pwmApplication;
-        try {
-            pwmApplication = processAuthentication();
-        } catch (IOException e) {
-            return e.getMessage();
-        } catch (PwmUnrecoverableException e) {
-            RestServerHelper.handleNonJsonErrorResult(e.getErrorInformation());
-            return null;
-        }
-
-        try {
-            final HealthMonitor.CheckTimeliness timeliness = determineDataTimeliness(requestImmediateParam);
-            final String resultString = pwmApplication.getHealthMonitor().getMostSevereHealthStatus(timeliness).toString() + "\n";
-            StatisticsManager.incrementStat(pwmApplication, Statistic.REST_HEALTH);
-            return resultString;
-        } catch (Exception e) {
-            final String errorMessage = "unexpected error executing web service: " + e.getMessage();
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
-            RestServerHelper.handleNonJsonErrorResult(errorInformation);
-            return null;
+    @Override
+    public void preCheckRequest(final RestRequest restRequest) throws PwmUnrecoverableException {
+        if (!restRequest.getRestAuthentication().getUsages().contains(WebServiceUsage.Health)) {
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_SERVICE_NOT_AVAILABLE,"public health service is not enabled");
         }
     }
 
-    @GET
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doPwmHealthJsonGet(
-            @QueryParam(PARAM_IMMEDIATE_REFRESH) final boolean requestImmediateParam
-    ) {
-        final PwmApplication pwmApplication;
-        try {
-            pwmApplication = processAuthentication();
-        } catch (IOException e) {
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setError(true);
-            restResultBean.setErrorMessage(e.getMessage());
-            return restResultBean.asJsonResponse();
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
+    @RestMethodHandler(method = HttpMethod.GET, produces = HttpContentType.plain)
+    private RestResultBean doPwmHealthPlainGet(final RestRequest restRequest)
+            throws PwmUnrecoverableException
+    {
+        final boolean requestImmediateParam = restRequest.readParameterAsBoolean(PARAM_IMMEDIATE_REFRESH);
 
         try {
-            final HealthData jsonOutput = processGetHealthCheckData(pwmApplication, request.getLocale(), requestImmediateParam);
-            StatisticsManager.incrementStat(pwmApplication, Statistic.REST_HEALTH);
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(jsonOutput);
-            return restResultBean.asJsonResponse();
-        } catch (PwmException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
+            final HealthMonitor.CheckTimeliness timeliness = determineDataTimeliness(requestImmediateParam);
+            final String resultString = restRequest.getPwmApplication().getHealthMonitor().getMostSevereHealthStatus(timeliness).toString() + "\n";
+            StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_HEALTH);
+            return RestResultBean.withData(resultString);
         } catch (Exception e) {
             final String errorMessage = "unexpected error executing web service: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
-            return RestResultBean.fromError(errorInformation).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
-    private PwmApplication processAuthentication() throws PwmUnrecoverableException, IOException
-    {
-        final ServicePermissions servicePermissions = figurePermissions();
-        {
-            final StandaloneRestRequestBean standaloneRestRequestBean = StandaloneRestHelper.initialize(request);
-            if (standaloneRestRequestBean.getAuthorizedUsages().contains(WebServiceUsage.Health)) {
-                return standaloneRestRequestBean.getPwmApplication();
-            }
-        }
+    @RestMethodHandler(method = HttpMethod.GET, consumes = HttpContentType.json, produces = HttpContentType.json)
+    private RestResultBean doPwmHealthJsonGet(final RestRequest restRequest)
+            throws PwmUnrecoverableException, IOException {
+        final boolean requestImmediateParam = restRequest.readParameterAsBoolean(PARAM_IMMEDIATE_REFRESH);
 
-        final RestRequestBean restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, null);
-        return restRequestBean.getPwmApplication();
+        final HealthData jsonOutput = processGetHealthCheckData(restRequest.getPwmApplication(), restRequest.getLocale(), requestImmediateParam);
+        StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_HEALTH);
+        return RestResultBean.withData(jsonOutput);
     }
 
     private static HealthMonitor.CheckTimeliness determineDataTimeliness(
@@ -153,7 +109,7 @@ public class RestHealthServer extends AbstractRestServer {
             final Locale locale,
             final boolean refreshImmediate
     )
-            throws ChaiUnavailableException, IOException, ServletException, PwmUnrecoverableException
+            throws IOException, PwmUnrecoverableException
     {
         final HealthMonitor healthMonitor = pwmApplication.getHealthMonitor();
         final HealthMonitor.CheckTimeliness timeliness = determineDataTimeliness(refreshImmediate);
@@ -166,17 +122,4 @@ public class RestHealthServer extends AbstractRestServer {
         healthData.records = healthRecordBeans;
         return healthData;
     }
-
-    private ServicePermissions figurePermissions() {
-        ServicePermissions servicePermissions = ServicePermissions.ADMIN_OR_CONFIGMODE;
-        try {
-            final Configuration config = ContextManager.getContextManager(context).getPwmApplication().getConfig();
-            if (config.readSettingAsBoolean(PwmSetting.PUBLIC_HEALTH_STATS_WEBSERVICES)) {
-                servicePermissions = ServicePermissions.PUBLIC;
-            }
-        } catch (PwmUnrecoverableException e) {
-            LOGGER.error("unable to read service permissions, defaulting to non-public; error: " + e.getMessage());
-        }
-        return servicePermissions;
-    }
 }

+ 134 - 88
server/src/main/java/password/pwm/ws/server/rest/RestProfileServer.java

@@ -22,89 +22,105 @@
 
 package password.pwm.ws.server.rest;
 
-import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
-import password.pwm.Permission;
+import lombok.Data;
+import password.pwm.PwmConstants;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.option.WebServiceUsage;
+import password.pwm.config.profile.ProfileType;
+import password.pwm.config.profile.ProfileUtility;
 import password.pwm.config.profile.UpdateAttributesProfile;
 import password.pwm.config.value.data.FormConfiguration;
-import password.pwm.util.form.FormUtility;
-import password.pwm.config.PwmSetting;
+import password.pwm.config.value.data.UserPermission;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.PwmRequest;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.http.servlet.UpdateProfileServlet;
 import password.pwm.i18n.Message;
+import password.pwm.ldap.LdapPermissionTester;
 import password.pwm.ldap.UserInfo;
+import password.pwm.ldap.UserInfoFactory;
 import password.pwm.util.FormMap;
-import password.pwm.ws.server.RestRequestBean;
+import password.pwm.util.form.FormUtility;
+import password.pwm.util.java.StringUtil;
+import password.pwm.util.macro.MacroMachine;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
+
+import javax.servlet.annotation.WebServlet;
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-@Path("/profile")
-public class RestProfileServer extends AbstractRestServer {
-
-    private static final ServicePermissions SERVICE_PERMISSIONS = ServicePermissions.builder()
-            .adminOnly(false)
-            .authRequired(true)
-            .blockExternal(true)
-            .build();
-
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/profile",
+        }
+)
+@RestWebServer(webService = WebServiceUsage.RandomPassword, requireAuthentication = false)
+public class RestProfileServer extends RestServlet {
 
+    @Data
     public static class JsonProfileData implements Serializable {
-        public String username;
-        public Map<String,String> profile;
-        public List<FormConfiguration> formDefinition;
+        private String username;
+        private Map<String,String> profile;
+        private List<FormConfiguration> formDefinition;
+    }
+
+    @Override
+    public void preCheckRequest(final RestRequest request) throws PwmUnrecoverableException {
+        if (!request.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_ENABLE)) {
+            throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "update profile module is not enabled");
+        }
     }
 
-    @GET
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doGetProfileJsonData(
-            @QueryParam("username") final String username
-    ) {
+    @RestMethodHandler(method = HttpMethod.GET, produces = HttpContentType.json)
+    public RestResultBean doGetProfileJsonData(final RestRequest restRequest)
+            throws PwmUnrecoverableException
+    {
+        final String username = restRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation);
+
         try {
-            final RestResultBean restResultBean = doGetProfileDataImpl(request,response,username);
-            return restResultBean.asJsonResponse();
+            return doGetProfileDataImpl(restRequest,username);
         } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
+            return RestResultBean.fromError(restRequest, e.getErrorInformation());
         } catch (Exception e) {
             final String errorMsg = "unexpected error building json response: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
-    protected static RestResultBean doGetProfileDataImpl(
-            final HttpServletRequest request,
-            final HttpServletResponse response,
+    private static RestResultBean doGetProfileDataImpl(
+            final RestRequest restRequest,
             final String username
     )
             throws PwmUnrecoverableException, ChaiUnavailableException
     {
-        final RestRequestBean restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, username);
-        final UpdateAttributesProfile updateAttributesProfile = restRequestBean.getPwmSession().getSessionManager().getUpdateAttributeProfile(restRequestBean.getPwmApplication());
+        final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, username);
+
+        final String updateProfileID = ProfileUtility.discoverProfileIDforUser(
+                restRequest.getPwmApplication(),
+                restRequest.getSessionLabel(),
+                targetUserIdentity.getUserIdentity(),
+                ProfileType.UpdateAttributes
+        );
 
-        if (!restRequestBean.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_ENABLE)) {
-            throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE);
+        if (StringUtil.isEmpty(updateProfileID)) {
+            throw new PwmUnrecoverableException(PwmError.ERROR_NO_PROFILE_ASSIGNED);
         }
 
+        final UpdateAttributesProfile updateAttributesProfile = restRequest.getPwmApplication().getConfig().getUpdateAttributesProfile().get(updateProfileID);
+
         final Map<String,String> profileData = new HashMap<>();
         {
             final Map<FormConfiguration,String> formData = new HashMap<>();
@@ -113,12 +129,14 @@ public class RestProfileServer extends AbstractRestServer {
             }
             final List<FormConfiguration> formFields = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
 
-            if (restRequestBean.getUserIdentity() != null) {
-                final UserInfo userInfo = restRequestBean.getPwmSession().getUserInfo();
-                FormUtility.populateFormMapFromLdap(formFields, restRequestBean.getPwmSession().getLabel(), formData, userInfo);
-            } else {
-                throw new PwmUnrecoverableException(PwmError.ERROR_NO_PROFILE_ASSIGNED);
-            }
+            final UserInfo userInfo = UserInfoFactory.newUserInfo(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    restRequest.getLocale(),
+                    targetUserIdentity.getUserIdentity(),
+                    targetUserIdentity.getChaiProvider()
+            );
+            FormUtility.populateFormMapFromLdap(formFields, restRequest.getSessionLabel(), formData, userInfo);
 
             for (final FormConfiguration formConfig : formData.keySet()) {
                 profileData.put(formConfig.getName(),formData.get(formConfig));
@@ -128,68 +146,96 @@ public class RestProfileServer extends AbstractRestServer {
         final JsonProfileData outputData = new JsonProfileData();
         outputData.profile = profileData;
         outputData.formDefinition = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(outputData);
+        final RestResultBean restResultBean = RestResultBean.withData(outputData);
         return restResultBean;
     }
 
-    @POST
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doPostProfileData(
-            final JsonProfileData jsonInput
-    ) {
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
+    public RestResultBean doPostProfileData(final RestRequest restRequest) throws IOException, PwmUnrecoverableException {
+
+        final JsonProfileData jsonInput = deserializeJsonBody(restRequest, JsonProfileData.class);
+
         try {
-            final RestResultBean restResultBean = doPostProfileDataImpl(request, response, jsonInput);
-            return restResultBean.asJsonResponse();
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
+            return doPostProfileDataImpl(restRequest, jsonInput);
         } catch (Exception e) {
             final String errorMsg = "unexpected error building json response: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
     public static RestResultBean doPostProfileDataImpl(
-            final HttpServletRequest request,
-            final HttpServletResponse response,
+            final RestRequest restRequest,
             final JsonProfileData jsonInput
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
     {
-        final RestRequestBean restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, jsonInput.username);
+        final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, jsonInput.getUsername());
+
+        final String updateProfileID = ProfileUtility.discoverProfileIDforUser(
+                restRequest.getPwmApplication(),
+                restRequest.getSessionLabel(),
+                targetUserIdentity.getUserIdentity(),
+                ProfileType.UpdateAttributes
+        );
 
-        if (!restRequestBean.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_ENABLE)) {
-            throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE);
+        if (StringUtil.isEmpty(updateProfileID)) {
+            throw new PwmUnrecoverableException(PwmError.ERROR_NO_PROFILE_ASSIGNED);
         }
 
-        if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.PROFILE_UPDATE)) {
-            throw new PwmUnrecoverableException(PwmError.ERROR_UNAUTHORIZED);
+        final UpdateAttributesProfile updateAttributesProfile = restRequest.getPwmApplication().getConfig().getUpdateAttributesProfile().get(updateProfileID);
+
+        {
+            final List<UserPermission> userPermission = updateAttributesProfile.readSettingAsUserPermission(PwmSetting.UPDATE_PROFILE_QUERY_MATCH);
+            final boolean result = LdapPermissionTester.testUserPermissions(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity(),
+                    userPermission
+            );
+
+            if (!result) {
+                throw new PwmUnrecoverableException(PwmError.ERROR_UNAUTHORIZED);
+            }
         }
 
+
         final FormMap inputFormData = new FormMap(jsonInput.profile);
-        final List<FormConfiguration> profileForm = restRequestBean.getPwmApplication().getConfig().readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
+        final List<FormConfiguration> profileForm = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
         final Map<FormConfiguration,String> profileFormData = new HashMap<>();
         for (final FormConfiguration formConfiguration : profileForm) {
             if (!formConfiguration.isReadonly() && inputFormData.containsKey(formConfiguration.getName())) {
                 profileFormData.put(formConfiguration,inputFormData.get(formConfiguration.getName()));
             }
         }
-        final PwmRequest pwmRequest = PwmRequest.forRequest(request, response);
-        if (restRequestBean.getUserIdentity() != null) {
-            final ChaiUser theUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),restRequestBean.getUserIdentity());
-            UpdateProfileServlet.doProfileUpdate(pwmRequest, FormUtility.asStringMap(profileFormData), theUser);
-        } else {
-            final ChaiUser theUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication());
-            UpdateProfileServlet.doProfileUpdate(pwmRequest, FormUtility.asStringMap(profileFormData), theUser);
-        }
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setSuccessMessage(Message.getLocalizedMessage(
-                restRequestBean.getPwmSession().getSessionStateBean().getLocale(),
-                Message.Success_UpdateProfile,
-                restRequestBean.getPwmApplication().getConfig()
-        ));
+
+        final UserInfo userInfo = UserInfoFactory.newUserInfo(
+                restRequest.getPwmApplication(),
+                restRequest.getSessionLabel(),
+                restRequest.getLocale(),
+                targetUserIdentity.getUserIdentity(),
+                targetUserIdentity.getChaiProvider()
+        );
+
+        final MacroMachine macroMachine = MacroMachine.forUser(
+                restRequest.getPwmApplication(),
+                restRequest.getLocale(),
+                restRequest.getSessionLabel(),
+                targetUserIdentity.getUserIdentity()
+        );
+
+        UpdateProfileServlet.doProfileUpdate(
+                restRequest.getPwmApplication(),
+                restRequest.getSessionLabel(),
+                restRequest.getLocale(),
+                userInfo,
+                macroMachine,
+                updateAttributesProfile,
+                FormUtility.asStringMap(profileFormData),
+                targetUserIdentity.getChaiUser()
+        );
+
+        final RestResultBean restResultBean = RestResultBean.forSuccessMessage(restRequest, Message.Success_UpdateProfile);
         return restResultBean;
     }
 }

+ 76 - 144
server/src/main/java/password/pwm/ws/server/rest/RestRandomPasswordServer.java

@@ -22,66 +22,52 @@
 
 package password.pwm.ws.server.rest;
 
-import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import password.pwm.bean.UserIdentity;
-import password.pwm.config.Configuration;
-import password.pwm.config.PwmSetting;
-import password.pwm.config.profile.HelpdeskProfile;
+import lombok.Data;
+import password.pwm.PwmConstants;
+import password.pwm.config.option.WebServiceUsage;
+import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.PasswordData;
 import password.pwm.util.RandomPasswordGenerator;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.operations.PasswordUtility;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.FormParam;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import javax.servlet.annotation.WebServlet;
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 
-@Path("/randompassword")
-public class RestRandomPasswordServer extends AbstractRestServer {
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/randompassword",
+        }
+)
+@RestWebServer(webService = WebServiceUsage.RandomPassword, requireAuthentication = false)
+public class RestRandomPasswordServer extends RestServlet {
     private static final PwmLogger LOGGER = PwmLogger.forClass(RestRandomPasswordServer.class);
 
-    private static final ServicePermissions SERVICE_PERMISSIONS = ServicePermissions.builder()
-            .adminOnly(false)
-            .authRequired(false)
-            .blockExternal(true)
-            .helpdeskPermitted(true)
-            .publicDuringConfig(true)
-            .build();
-
-    @Getter
-    @Setter
-    @NoArgsConstructor
+    @Data
     public static class JsonOutput implements Serializable
     {
         private String password;
     }
 
-    @Getter
-    @Setter
-    @NoArgsConstructor
+    @Data
     public static class JsonInput implements Serializable
     {
         private String username;
@@ -92,122 +78,82 @@ public class RestRandomPasswordServer extends AbstractRestServer {
         private boolean noUser;
     }
 
-    @POST
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doPostRandomPasswordForm(
-            @FormParam("username") final String username,
-            @FormParam("strength") final int strength,
-            @FormParam("maxLength") final int maxLength,
-            @FormParam("minLength") final int minLength,
-            @FormParam("chars") final String chars,
-            @FormParam("noUser") final boolean noUser
-    )
+    @Override
+    public void preCheckRequest(final RestRequest request) throws PwmUnrecoverableException {
+    }
+
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.form, produces = HttpContentType.json)
+    public RestResultBean doPostRandomPasswordForm(final RestRequest restRequest)
             throws PwmUnrecoverableException
     {
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
-
         final JsonInput jsonInput = new JsonInput();
-        jsonInput.username = username;
-        jsonInput.strength = strength;
-        jsonInput.maxLength = maxLength;
-        jsonInput.minLength = minLength;
-        jsonInput.chars = chars;
-        jsonInput.noUser = noUser;
+        jsonInput.username = restRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation);
+        jsonInput.strength = restRequest.readParameterAsInt("strength", 0);
+        jsonInput.maxLength = restRequest.readParameterAsInt("maxLength", 0);
+        jsonInput.minLength = restRequest.readParameterAsInt("minLength", 0);
+        jsonInput.chars = restRequest.readParameterAsString("chars", PwmHttpRequestWrapper.Flag.BypassValidation);
+        jsonInput.noUser = restRequest.readParameterAsBoolean("noUser");
 
         try {
-            final JsonOutput jsonOutput = doOperation(restRequestBean, jsonInput);
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(jsonOutput);
-            return restResultBean.asJsonResponse();
+            final JsonOutput jsonOutput = doOperation(restRequest, jsonInput);
+            final RestResultBean restResultBean = RestResultBean.withData(jsonOutput);
+            return restResultBean;
         } catch (PwmException e) {
-            LOGGER.error(restRequestBean.getPwmSession(),"error executing rest-json random password request: " + e.getMessage(),e);
-            return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();
+            LOGGER.error(restRequest.getSessionLabel(),"error executing rest-json random password request: " + e.getMessage(),e);
+            return RestResultBean.fromError(restRequest, e.getErrorInformation());
         } catch (Exception e) {
             final String errorMessage = "unexpected error executing web service: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
     // This method is called if TEXT_PLAIN is request
-    @GET
-    @Produces(MediaType.TEXT_PLAIN)
-    public String doPlainRandomPassword(
-            @QueryParam("username") final String username,
-            @QueryParam("strength") final int strength,
-            @QueryParam("minLength") final int minLength,
-            @QueryParam("maxLength") final int maxLength,
-            @QueryParam("chars") final String chars,
-            @QueryParam("noUser") final boolean noUser
-    )
+    @RestMethodHandler(method = HttpMethod.GET, produces = HttpContentType.plain)
+    public RestResultBean doPlainRandomPassword(final RestRequest restRequest)
             throws PwmUnrecoverableException
     {
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, username);
-        } catch (PwmUnrecoverableException e) {
-            RestServerHelper.handleNonJsonErrorResult(e.getErrorInformation());
-            return null;
-        }
-
         final JsonInput jsonInput = new JsonInput();
-        jsonInput.username = username;
-        jsonInput.strength = strength;
-        jsonInput.maxLength = maxLength;
-        jsonInput.minLength = minLength;
-        jsonInput.chars = chars;
-        jsonInput.noUser = noUser;
+        jsonInput.username = restRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation);
+        jsonInput.strength = restRequest.readParameterAsInt("strength", 0);
+        jsonInput.maxLength = restRequest.readParameterAsInt("maxLength", 0);
+        jsonInput.minLength = restRequest.readParameterAsInt("minLength", 0);
+        jsonInput.chars = restRequest.readParameterAsString("chars", PwmHttpRequestWrapper.Flag.BypassValidation);
+        jsonInput.noUser = restRequest.readParameterAsBoolean("noUser");
 
         try {
-            return doOperation(restRequestBean, jsonInput).password;
+            return RestResultBean.withData(doOperation(restRequest, jsonInput));
         } catch (Exception e) {
-            LOGGER.error(restRequestBean.getPwmSession(),"error executing rest-json random password request: " + e.getMessage(),e);
+            LOGGER.error(restRequest.getSessionLabel(),"error executing rest-json random password request: " + e.getMessage(),e);
             final String errorMessage = "unexpected error executing web service: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
-            RestServerHelper.handleNonJsonErrorResult(errorInformation);
-            return null;
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
-    @POST
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response doPostRandomPasswordJson(
-            final JsonInput jsonInput
-    )
-            throws PwmUnrecoverableException
+
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
+    public RestResultBean doPostRandomPasswordJson(final RestRequest restRequest)
+            throws PwmUnrecoverableException, IOException
     {
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, jsonInput.username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
+        final JsonInput jsonInput = deserializeJsonBody(restRequest, JsonInput.class);
 
         try {
-            final JsonOutput jsonOutput = doOperation(restRequestBean, jsonInput);
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setData(jsonOutput);
-            return restResultBean.asJsonResponse();
+            final JsonOutput jsonOutput = doOperation(restRequest, jsonInput);
+            return RestResultBean.withData(jsonOutput);
         } catch (PwmException e) {
-            LOGGER.error(restRequestBean.getPwmSession(),"error executing rest-form random password request: " + e.getMessage(),e);
-            return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();
+            LOGGER.error(restRequest.getSessionLabel(),"error executing rest-form random password request: " + e.getMessage(),e);
+            return RestResultBean.fromError(restRequest, e.getErrorInformation());
         } catch (Exception e) {
-            LOGGER.error(restRequestBean.getPwmSession(),"error executing rest-form random password request: " + e.getMessage(),e);
+            LOGGER.error(restRequest.getSessionLabel(),"error executing rest-form random password request: " + e.getMessage(),e);
             final String errorMessage = "unexpected error executing web service: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 
     private static JsonOutput doOperation(
-            final RestRequestBean restRequestBean,
+            final RestRequest restRequest,
             final JsonInput jsonInput
     )
             throws ChaiUnavailableException, PwmUnrecoverableException
@@ -231,40 +177,26 @@ public class RestRandomPasswordServer extends AbstractRestServer {
             randomConfigBuilder.seedlistPhrases(charValues);
         }
 
-        if (!jsonInput.noUser && restRequestBean.getPwmSession().isAuthenticated()) {
-            if (jsonInput.username != null && !jsonInput.username.isEmpty()) {
-                final UserIdentity userIdentity = restRequestBean.getUserIdentity();
-                final HelpdeskProfile helpdeskProfile = restRequestBean.getPwmSession().getSessionManager().getHelpdeskProfile(restRequestBean.getPwmApplication());
-                final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
-
-                final ChaiUser theUser = useProxy
-                        ? restRequestBean.getPwmApplication().getProxiedChaiUser(restRequestBean.getUserIdentity())
-                        : restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),userIdentity);
-
-                randomConfigBuilder.passwordPolicy(PasswordUtility.readPasswordPolicyForUser(
-                        restRequestBean.getPwmApplication(),
-                        restRequestBean.getPwmSession().getLabel(),
-                        restRequestBean.getUserIdentity(),
-                        theUser,
-                        restRequestBean.getPwmSession().getSessionStateBean().getLocale()));
-            } else {
-                randomConfigBuilder.passwordPolicy(restRequestBean.getPwmSession().getUserInfo().getPasswordPolicy());
-            }
+        if (jsonInput.isNoUser()) {
+            randomConfigBuilder.passwordPolicy(PwmPasswordPolicy.defaultPolicy());
         } else {
-            final Configuration config  = restRequestBean.getPwmApplication().getConfig();
-            randomConfigBuilder.passwordPolicy(config.getPasswordPolicy(
-                    config.getPasswordProfileIDs().iterator().next(),
-                    restRequestBean.getPwmSession().getSessionStateBean().getLocale()));
+            final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, jsonInput.getUsername());
+            final PwmPasswordPolicy pwmPasswordPolicy = PasswordUtility.readPasswordPolicyForUser(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity(),
+                    targetUserIdentity.getChaiUser(),
+                    restRequest.getLocale()
+            );
+            randomConfigBuilder.passwordPolicy(pwmPasswordPolicy);
         }
 
         final RandomPasswordGenerator.RandomGeneratorConfig randomConfig = randomConfigBuilder.build();
-        final PasswordData randomPassword = RandomPasswordGenerator.createRandomPassword(restRequestBean.getPwmSession().getLabel(), randomConfig, restRequestBean.getPwmApplication());
+        final PasswordData randomPassword = RandomPasswordGenerator.createRandomPassword(restRequest.getSessionLabel(), randomConfig, restRequest.getPwmApplication());
         final JsonOutput outputMap = new JsonOutput();
         outputMap.password = randomPassword.getStringValue();
 
-        if (restRequestBean.isExternal()) {
-            StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_SETPASSWORD);
-        }
+        StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_SETPASSWORD);
 
         return outputMap;
     }

+ 80 - 131
server/src/main/java/password/pwm/ws/server/rest/RestSetPasswordServer.java

@@ -22,48 +22,45 @@
 
 package password.pwm.ws.server.rest;
 
-import com.novell.ldapchai.ChaiUser;
-import lombok.Getter;
-import lombok.Setter;
-import password.pwm.Permission;
-import password.pwm.config.PwmSetting;
-import password.pwm.config.profile.HelpdeskProfile;
+import lombok.Data;
+import password.pwm.PwmConstants;
+import password.pwm.config.option.WebServiceUsage;
 import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.i18n.Message;
-import password.pwm.svc.event.AuditEvent;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.util.BasicAuthInfo;
 import password.pwm.util.PasswordData;
 import password.pwm.util.RandomPasswordGenerator;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.operations.PasswordUtility;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.FormParam;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
+
+import javax.servlet.annotation.WebServlet;
+import java.io.IOException;
 import java.io.Serializable;
 
-@Path("/setpassword")
-public class RestSetPasswordServer extends AbstractRestServer {
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/setpassword"
+        }
+)
+@RestWebServer(webService = WebServiceUsage.SetPassword, requireAuthentication = true)
+public class RestSetPasswordServer extends RestServlet {
 
     public static final PwmLogger LOGGER = PwmLogger.forClass(RestSetPasswordServer.class);
 
-    @Getter
-    @Setter
+    @Data
     public static class JsonInputData implements Serializable
     {
         public String username;
@@ -71,153 +68,105 @@ public class RestSetPasswordServer extends AbstractRestServer {
         public boolean random;
     }
 
-    @POST
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doPostSetPasswordForm(
-            @FormParam("username") final String username,
-            @FormParam("password") final String password,
-            @FormParam("random") final boolean random
+    @Override
+    public void preCheckRequest(final RestRequest request) throws PwmUnrecoverableException {
+
+    }
+
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.form, produces = HttpContentType.json)
+    public RestResultBean doPostSetPasswordForm(
+            final RestRequest restRequest
     )
             throws PwmUnrecoverableException
     {
         final JsonInputData jsonInputData = new JsonInputData();
-        jsonInputData.username = username;
-        jsonInputData.password = password;
-        jsonInputData.random = random;
+        jsonInputData.username = restRequest.readParameterAsString("username", PwmHttpRequestWrapper.Flag.BypassValidation);
+        jsonInputData.password = restRequest.readParameterAsString("password", PwmHttpRequestWrapper.Flag.BypassValidation);
+        jsonInputData.random = restRequest.readParameterAsBoolean("random");
 
-        return doSetPassword(request, response, jsonInputData);
+        return doSetPassword(restRequest, jsonInputData);
     }
 
-    @POST
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response doPostSetPasswordJson(
-            final JsonInputData jsonInputData
-    )
-            throws PwmUnrecoverableException
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
+    public RestResultBean doPostSetPasswordJson(final RestRequest restRequest)
+            throws IOException, PwmUnrecoverableException
     {
-        return doSetPassword(request, response, jsonInputData);
+        final JsonInputData jsonInputData = deserializeJsonBody(restRequest, JsonInputData.class);
+        return doSetPassword(restRequest, jsonInputData);
     }
 
-    private static Response doSetPassword(
-            final HttpServletRequest request,
-            final HttpServletResponse response,
+    private static RestResultBean doSetPassword(
+            final RestRequest restRequest,
             final JsonInputData jsonInputData
 
     ) throws PwmUnrecoverableException
     {
-        final RestRequestBean restRequestBean;
-        try {
-            final ServicePermissions servicePermissions = ServicePermissions.builder()
-                    .adminOnly(false)
-                    .authRequired(true)
-                    .blockExternal(true)
-                    .helpdeskPermitted(true)
-                    .build();
-
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, jsonInputData.username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
-
         final String password = jsonInputData.password;
         final boolean random = jsonInputData.random;
 
         if ((password == null || password.length() < 1) && !random) {
             final String errorMessage = "field 'password' must have a value or field 'random' must be set to true";
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, errorMessage, new String[]{"password"});
-            return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
 
         if ((password != null && password.length() > 0) && random) {
             final String errorMessage = "field 'password' cannot have a value or field 'random' must be set to true";
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER, errorMessage, new String[]{"password"});
-            return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
 
-        final HelpdeskProfile helpdeskProfile = restRequestBean.getPwmSession().getSessionManager().getHelpdeskProfile(restRequestBean.getPwmApplication());
         try {
-            if (restRequestBean.getUserIdentity() == null) {
-                if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(
-                        restRequestBean.getPwmApplication(), Permission.CHANGE_PASSWORD)) {
-                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,
-                            "actor does not have required permission"));
-                }
-            } else {
-                if (helpdeskProfile == null) {
-                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,
-                            "actor does not have required permission"));
-                }
-            }
-
             final JsonInputData jsonResultData = new JsonInputData();
             jsonResultData.random = random;
 
-            /* helpdesk set password */
-            if (restRequestBean.getUserIdentity() != null) {
-
-                final boolean useProxy = helpdeskProfile.readSettingAsBoolean(PwmSetting.HELPDESK_USE_PROXY);
-                final ChaiUser chaiUser = useProxy
-                        ? restRequestBean.getPwmApplication().getProxiedChaiUser(restRequestBean.getUserIdentity())
-                        : restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),restRequestBean.getUserIdentity());
-                final PasswordData newPassword;
-                if (random) {
-                    final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
-                            restRequestBean.getPwmApplication(),
-                            restRequestBean.getPwmSession().getLabel(),
-                            restRequestBean.getUserIdentity(),
-                            chaiUser,
-                            restRequestBean.getPwmSession().getSessionStateBean().getLocale()
-                    );
-                    newPassword = RandomPasswordGenerator.createRandomPassword(restRequestBean.getPwmSession().getLabel(), passwordPolicy, restRequestBean.getPwmApplication());
-                } else {
-                    newPassword = new PasswordData(password);
-                }
-                PasswordUtility.helpdeskSetUserPassword(
-                        restRequestBean.getPwmSession(),
-                        chaiUser,
-                        restRequestBean.getUserIdentity(),
-                        restRequestBean.getPwmApplication(),
-                        newPassword
+            final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, jsonInputData.username);
+
+            final PasswordData newPassword;
+            if (random) {
+                final PwmPasswordPolicy passwordPolicy = PasswordUtility.readPasswordPolicyForUser(
+                        restRequest.getPwmApplication(),
+                        restRequest.getSessionLabel(),
+                        targetUserIdentity.getUserIdentity(),
+                        targetUserIdentity.getChaiUser(),
+                        restRequest.getLocale()
+                );
+                newPassword = RandomPasswordGenerator.createRandomPassword(
+                        restRequest.getSessionLabel(),
+                        passwordPolicy, restRequest.getPwmApplication()
                 );
-                jsonResultData.password = null;
-                jsonResultData.username = restRequestBean.getUserIdentity().toDelimitedKey();
             } else {
-                final PasswordData newPassword;
-                if (random) {
-                    newPassword = RandomPasswordGenerator.createRandomPassword(restRequestBean.getPwmSession(), restRequestBean.getPwmApplication());
-                } else {
-                    newPassword = new PasswordData(password);
-                }
-                PasswordUtility.setActorPassword(restRequestBean.getPwmSession(), restRequestBean.getPwmApplication(),
-                        newPassword);
-                restRequestBean.getPwmApplication().getAuditManager().submit(AuditEvent.CHANGE_PASSWORD, restRequestBean.getPwmSession().getUserInfo(), restRequestBean.getPwmSession());
-                jsonResultData.password = null;
-                jsonResultData.username = restRequestBean.getPwmSession().getUserInfo().getUserIdentity().toDelimitedKey();
+                newPassword = new PasswordData(password);
             }
-            if (restRequestBean.isExternal()) {
-                StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_SETPASSWORD);
+
+            final PasswordData oldPassword;
+            if (targetUserIdentity.isSelf()) {
+                final BasicAuthInfo basicAuthInfo = BasicAuthInfo.parseAuthHeader(restRequest.getPwmApplication(), restRequest.getHttpServletRequest());
+                oldPassword = basicAuthInfo.getPassword();
+            } else {
+                oldPassword = null;
             }
-            final RestResultBean restResultBean = new RestResultBean();
-            restResultBean.setError(false);
-            restResultBean.setData(jsonResultData);
-            final String msg = Message.getLocalizedMessage(
-                    restRequestBean.getPwmSession().getSessionStateBean().getLocale(),
-                    Message.Success_ChangedHelpdeskPassword,
-                    restRequestBean.getPwmApplication().getConfig());
-            String[] strs = jsonResultData.username.split("=");
-            strs = strs[1].split(",");
-            restResultBean.setSuccessMessage(msg + strs[0]);
-            return restResultBean.asJsonResponse();
+
+            PasswordUtility.setPassword(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getChaiProvider(),
+                    targetUserIdentity.getUserIdentity(),
+                    oldPassword,
+                    newPassword
+            );
+
+            StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_SETPASSWORD);
+            final RestResultBean restResultBean = RestResultBean.withData(jsonResultData);
+            return restResultBean;
         } catch (PwmException e) {
             LOGGER.error("error during set password REST operation: " + e.getMessage());
-            return RestResultBean.fromError(e.getErrorInformation(),restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, e.getErrorInformation());
         } catch (Exception e) {
             final String errorMessage = "unexpected error executing web service: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMessage);
             LOGGER.error("error during set password REST operation: " + e.getMessage(),e);
-            return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 }

+ 41 - 70
server/src/main/java/password/pwm/ws/server/rest/RestStatisticsServer.java

@@ -23,34 +23,27 @@
 package password.pwm.ws.server.rest;
 
 import lombok.Data;
-import password.pwm.config.Configuration;
-import password.pwm.config.PwmSetting;
+import password.pwm.PwmConstants;
+import password.pwm.config.option.WebServiceUsage;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.http.ContextManager;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
+import password.pwm.http.PwmHttpRequestWrapper;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsBundle;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.ws.server.RestRequestBean;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
+
+import javax.servlet.annotation.WebServlet;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -58,46 +51,39 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 
-@Path("/statistics")
-public class RestStatisticsServer extends AbstractRestServer {
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/statistics"
+        }
+)
+@RestWebServer(webService = WebServiceUsage.Statistics, requireAuthentication = true)
+public class RestStatisticsServer extends RestServlet {
     private static final PwmLogger LOGGER = PwmLogger.forClass(RestStatisticsServer.class);
 
-    @Context
-    HttpServletRequest request;
-
-    @Context
-    HttpServletResponse response;
-
-    @Context
-    ServletContext context;
-
     @Data
-    public static class JsonOutput implements Serializable
-    {
-        public Map<String,String> EPS;
-        public Map<String,Object> nameData;
-        public Map<String,Object> keyData;
+    public static class JsonOutput implements Serializable {
+        public Map<String, String> EPS;
+        public Map<String, Object> nameData;
+        public Map<String, Object> keyData;
     }
 
-    @GET
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response doPwmStatisticJsonGet(
-            @QueryParam("statKey") final String statKey,
-            @QueryParam("statName") final String statName,
-            @QueryParam("days") final String days
-    )
-    {
-        final ServicePermissions servicePermissions = figurePermissions();
-        final RestRequestBean restRequestBean;
-        try {
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, null);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
+    @Override
+    public void preCheckRequest(final RestRequest restRequest) throws PwmUnrecoverableException {
+        if (!restRequest.getRestAuthentication().getUsages().contains(WebServiceUsage.Health)) {
+            throw PwmUnrecoverableException.newException(PwmError.ERROR_SERVICE_NOT_AVAILABLE,"public statistics service is not enabled");
         }
+    }
+
+    @RestMethodHandler(method = HttpMethod.GET, consumes = HttpContentType.form, produces = HttpContentType.json)
+    public RestResultBean doPwmStatisticJsonGet(final RestRequest restRequest)
+            throws PwmUnrecoverableException
+    {
+        final String statKey = restRequest.readParameterAsString("statKey", PwmHttpRequestWrapper.Flag.BypassValidation);
+        final String statName = restRequest.readParameterAsString("statName", PwmHttpRequestWrapper.Flag.BypassValidation);
+        final String days = restRequest.readParameterAsString("days", PwmHttpRequestWrapper.Flag.BypassValidation);
 
         try {
-            final StatisticsManager statisticsManager = restRequestBean.getPwmApplication().getStatisticsManager();
+            final StatisticsManager statisticsManager = restRequest.getPwmApplication().getStatisticsManager();
             final JsonOutput jsonOutput = new JsonOutput();
             jsonOutput.EPS = addEpsStats(statisticsManager);
 
@@ -107,21 +93,19 @@ public class RestStatisticsServer extends AbstractRestServer {
                 jsonOutput.keyData = doKeyStat(statisticsManager, statKey);
             }
 
-            if (restRequestBean.isExternal()) {
-                StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_STATISTICS);
-            }
+            StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_STATISTICS);
 
-            final RestResultBean resultBean = new RestResultBean();
-            resultBean.setData(jsonOutput);
-            return resultBean.asJsonResponse();
+            final RestResultBean resultBean = RestResultBean.withData(jsonOutput);
+            return resultBean;
         } catch (Exception e) {
             final String errorMsg = "unexpected error building json response: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
 
         }
     }
 
+
     public static Map<String,Object> doNameStat(final StatisticsManager statisticsManager, final String statName, final String days) {
         final Statistic statistic = Statistic.valueOf(statName);
         final int historyDays = StringUtil.convertStrToInt(days, 30);
@@ -157,17 +141,4 @@ public class RestStatisticsServer extends AbstractRestServer {
 
         return outputMap;
     }
-
-    private ServicePermissions figurePermissions() {
-        ServicePermissions servicePermissions = ServicePermissions.ADMIN_OR_CONFIGMODE;
-        try {
-            final Configuration config = ContextManager.getContextManager(context).getPwmApplication().getConfig();
-            if (config.readSettingAsBoolean(PwmSetting.PUBLIC_HEALTH_STATS_WEBSERVICES)) {
-                servicePermissions = ServicePermissions.PUBLIC;
-            }
-        } catch (PwmUnrecoverableException e) {
-            LOGGER.error("unable to read service permissions, defaulting to non-public; error: " + e.getMessage());
-        }
-        return servicePermissions;
-    }
 }

+ 52 - 62
server/src/main/java/password/pwm/ws/server/rest/RestStatusServer.java

@@ -22,101 +22,91 @@
 
 package password.pwm.ws.server.rest;
 
+import com.novell.ldapchai.provider.ChaiProvider;
+import password.pwm.PwmConstants;
 import password.pwm.bean.pub.PublicUserInfoBean;
+import password.pwm.config.option.WebServiceUsage;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfoFactory;
-import password.pwm.svc.PwmService;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
 
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.net.URISyntaxException;
+import javax.servlet.annotation.WebServlet;
 import java.time.Instant;
 
-@Path("/status")
-public class RestStatusServer extends AbstractRestServer {
+
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/status",
+        }
+)
+@RestWebServer(webService = WebServiceUsage.Status, requireAuthentication = true)
+public class RestStatusServer extends RestServlet {
     public static final PwmLogger LOGGER = PwmLogger.forClass(RestStatusServer.class);
 
-    @GET
-    @Produces(MediaType.TEXT_HTML)
-    public javax.ws.rs.core.Response doHtmlRedirect() throws URISyntaxException {
-        return RestServerHelper.handleHtmlRequest();
+    @Override
+    public void preCheckRequest(final RestRequest restRequest) throws PwmUnrecoverableException {
     }
 
-    @GET
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doGetStatusData(
-            @QueryParam("username") final String username
-    ) {
+    @RestMethodHandler(method = HttpMethod.GET, produces = HttpContentType.json, consumes = HttpContentType.json)
+    public RestResultBean doGetStatusData(final RestRequest restRequest)
+            throws PwmUnrecoverableException
+    {
         final Instant startTime = Instant.now();
-        final RestRequestBean restRequestBean;
-        try {
-            final ServicePermissions servicePermissions = ServicePermissions.builder()
-                    .adminOnly(false)
-                    .authRequired(true)
-                    .blockExternal(true)
-                    .build();
 
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
+        final String username = restRequest.readParameterAsString("username");
+        final TargetUserIdentity targetUserIdentity = RestServlet.resolveRequestedUsername(restRequest, username);
 
         try {
-            final UserInfo userInfo;
-            if (restRequestBean.getUserIdentity() != null) {
-                userInfo = UserInfoFactory.newUserInfo(
-                        restRequestBean.getPwmApplication(),
-                        restRequestBean.getPwmSession().getLabel(),
-                        restRequestBean.getPwmSession().getSessionStateBean().getLocale(),
-                        restRequestBean.getUserIdentity(),
-                        restRequestBean.getPwmSession().getSessionManager().getChaiProvider()
-                );
-            } else {
-                userInfo = restRequestBean.getPwmSession().getUserInfo();
-            }
-            final RestResultBean restResultBean = new RestResultBean();
-            final MacroMachine macroMachine = restRequestBean.getPwmSession().getSessionManager().getMacroMachine(restRequestBean.getPwmApplication());
-            restResultBean.setData(PublicUserInfoBean.fromUserInfoBean(
+            final ChaiProvider chaiProvider = targetUserIdentity.getChaiProvider();
+            final UserInfo userInfo = UserInfoFactory.newUserInfo(
+                    restRequest.getPwmApplication(),
+                    restRequest.getSessionLabel(),
+                    restRequest.getLocale(),
+                    targetUserIdentity.getUserIdentity(),
+                    chaiProvider
+            );
+            final MacroMachine macroMachine = MacroMachine.forUser(
+                    restRequest.getPwmApplication(),
+                    restRequest.getLocale(),
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity()
+            );
+
+            final PublicUserInfoBean publicUserInfoBean = PublicUserInfoBean.fromUserInfoBean(
                     userInfo,
-                    restRequestBean.getPwmApplication().getConfig(),
-                    restRequestBean.getPwmSession().getSessionStateBean().getLocale(),
+                    restRequest.getPwmApplication().getConfig(),
+                    restRequest.getLocale(),
                     macroMachine
-            ));
+            );
 
-            final StatisticsManager statsMgr = restRequestBean.getPwmApplication().getStatisticsManager();
-            if (statsMgr != null && statsMgr.status() == PwmService.STATUS.OPEN) {
-                if (restRequestBean.isExternal()) {
-                    statsMgr.incrementValue(Statistic.REST_STATUS);
-                }
-            }
+            StatisticsManager.incrementStat(restRequest.getPwmApplication(), Statistic.REST_STATUS);
 
-            final TimeDuration timeDuration = TimeDuration.fromCurrent(startTime);
-            LOGGER.debug(restRequestBean.getPwmSession(),"completed REST status request in " + timeDuration.asCompactString() + ", result=" + JsonUtil.serialize(restResultBean));
-            return restResultBean.asJsonResponse();
+            final RestResultBean restResultBean = RestResultBean.withData(publicUserInfoBean);
+            LOGGER.debug(restRequest.getSessionLabel(),"completed REST status request in "
+                    + TimeDuration.compactFromCurrent(startTime) + ", result=" + JsonUtil.serialize(restResultBean));
+            return restResultBean;
         } catch (PwmException e) {
-            return RestResultBean.fromError(e.getErrorInformation(), restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(e.getErrorInformation());
         } catch (Exception e) {
             final String errorMsg = "unexpected error building json response: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
     }
 }

+ 0 - 109
server/src/main/java/password/pwm/ws/server/rest/RestUserReportServer.java

@@ -1,109 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.ws.server.rest;
-
-import com.novell.ldapchai.exception.ChaiUnavailableException;
-import password.pwm.Permission;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
-import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.svc.report.ReportService;
-import password.pwm.svc.report.UserCacheRecord;
-import password.pwm.util.java.ClosableIterator;
-import password.pwm.util.localdb.LocalDBException;
-import password.pwm.ws.server.RestRequestBean;
-import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-@Path("/report")
-public class RestUserReportServer extends AbstractRestServer {
-    @Context
-    HttpServletRequest request;
-
-    @GET
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    public Response doReportDetailData(
-            @QueryParam("maximum") final int maximum
-    )
-            throws ChaiUnavailableException, PwmUnrecoverableException, LocalDBException
-    {
-        final int max = (maximum > 0)
-                ? maximum
-                : 10 * 1000;
-
-        final RestRequestBean restRequestBean;
-        try {
-            final ServicePermissions servicePermissions = ServicePermissions.ADMIN_LOCAL_OR_EXTERNAL;
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, null);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
-
-        if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.PWMADMIN)) {
-            final ErrorInformation errorInformation = PwmError.ERROR_UNAUTHORIZED.toInfo();
-            return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
-        }
-
-        final ReportService reportService = restRequestBean.getPwmApplication().getReportService();
-        //if (reportService.getReportStatusInfo().getCurrentProcess() != ReportStatusInfo.ReportEngineProcess.None) {
-        //    final String errorMsg = "report data not available, engine is busy.  Try again later.  status=" + reportService.getReportStatusInfo().getCurrentProcess();
-        //    final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, errorMsg);
-        //    return RestResultBean.fromError(errorInformation, restRequestBean).asJsonResponse();
-        //}
-
-        final ArrayList<UserCacheRecord> reportData = new ArrayList<>();
-        ClosableIterator<UserCacheRecord> cacheBeanIterator = null;
-        try {
-            cacheBeanIterator = reportService.iterator();
-            while (cacheBeanIterator.hasNext() && reportData.size() < max) {
-                final UserCacheRecord userCacheRecord = cacheBeanIterator.next();
-                if (userCacheRecord != null) {
-                    reportData.add(userCacheRecord);
-                }
-            }
-        } finally {
-            if (cacheBeanIterator != null) {
-                cacheBeanIterator.close();
-            }
-        }
-
-        final HashMap<String,Object> returnData = new HashMap<>();
-        returnData.put("users",reportData);
-
-        final RestResultBean restResultBean = new RestResultBean();
-        restResultBean.setData(returnData);
-        return restResultBean.asJsonResponse();
-    }
-}

+ 37 - 68
server/src/main/java/password/pwm/ws/server/rest/RestVerifyOtpServer.java

@@ -22,105 +22,74 @@
 
 package password.pwm.ws.server.rest;
 
-import com.novell.ldapchai.ChaiUser;
-import com.novell.ldapchai.exception.ChaiOperationException;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
-import password.pwm.Permission;
-import password.pwm.bean.UserIdentity;
+import lombok.Data;
+import password.pwm.PwmConstants;
+import password.pwm.config.option.WebServiceUsage;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
 import password.pwm.i18n.Message;
-import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.util.operations.OtpService;
 import password.pwm.util.operations.otp.OTPUserRecord;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import javax.servlet.annotation.WebServlet;
+import java.io.IOException;
 import java.io.Serializable;
-import java.net.URISyntaxException;
 
-@Path("/verifyotp")
-public class RestVerifyOtpServer extends AbstractRestServer {
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/verifyotp",
+        }
+)
+@RestWebServer(webService = WebServiceUsage.VerifyOtp, requireAuthentication = true)
+public class RestVerifyOtpServer extends RestServlet {
 
+    @Data
     public static class JsonPutOtpInput implements Serializable {
         public String token;
         public String username;
     }
 
-    @GET
-    @Produces(MediaType.TEXT_HTML)
-    public Response doHtmlRedirect() throws URISyntaxException {
-        return RestServerHelper.handleHtmlRequest();
+    @Override
+    public void preCheckRequest(final RestRequest request) throws PwmUnrecoverableException {
     }
 
-    @POST
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response doSetOtpDataJson(final RestVerifyOtpServer.JsonPutOtpInput jsonInput) {
-        final RestRequestBean restRequestBean;
-        try {
-            final ServicePermissions servicePermissions = ServicePermissions.builder()
-                    .adminOnly(false)
-                    .authRequired(true)
-                    .blockExternal(true)
-                    .build();
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
+    public RestResultBean doSetOtpDataJson(final RestRequest restRequest)
+            throws IOException, PwmUnrecoverableException
+    {
+        final RestVerifyOtpServer.JsonPutOtpInput jsonInput = deserializeJsonBody(restRequest, JsonPutOtpInput.class);
 
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, jsonInput.username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
-        try {
-            if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.SETUP_OTP_SECRET)) {
-                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"actor does not have required permission"));
-            }
+        final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, jsonInput.getUsername());
 
-            final UserSearchEngine userSearchEngine = restRequestBean.getPwmApplication().getUserSearchEngine();
-            UserIdentity userIdentity = restRequestBean.getUserIdentity();
-            if (userIdentity == null) {
-                final ChaiUser chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication());
-                userIdentity = userSearchEngine.resolveUsername(chaiUser.readUsername(), null, null, restRequestBean.getPwmSession().getLabel());
-            }
+        try {
+            final OtpService otpService = restRequest.getPwmApplication().getOtpService();
+            final OTPUserRecord otpUserRecord = otpService.readOTPUserConfiguration(restRequest.getSessionLabel(), targetUserIdentity.getUserIdentity());
 
-            final OtpService otpService = restRequestBean.getPwmApplication().getOtpService();
-            final OTPUserRecord otpUserRecord = otpService.readOTPUserConfiguration(restRequestBean.getPwmSession().getLabel(),userIdentity);
             final boolean verified = otpUserRecord !=null && otpService.validateToken(
-                    restRequestBean.getPwmSession(),
-                    userIdentity,
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity(),
                     otpUserRecord,
-                    jsonInput.token,
+                    jsonInput.getToken(),
                     false
             );
-            final String successMsg = Message.Success_Unknown.getLocalizedMessage(request.getLocale(),restRequestBean.getPwmApplication().getConfig());
-            final RestResultBean resultBean = new RestResultBean();
-            resultBean.setError(false);
-            resultBean.setData(verified);
-            resultBean.setSuccessMessage(successMsg);
-            return resultBean.asJsonResponse();
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation(),restRequestBean).asJsonResponse();
+
+            return RestResultBean.forSuccessMessage(verified, restRequest, Message.Success_Unknown);
         } catch (ChaiUnavailableException e) {
-            final String errorMsg = "unexpected error reading json input: " + e.getMessage();
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
-        } catch (ChaiOperationException e) {
-            final String errorMsg = "unexpected error reading json input: " + e.getMessage();
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
+            throw PwmUnrecoverableException.fromChaiException(e);
         } catch (PwmOperationalException e) {
             final String errorMsg = "unexpected error reading json input: " + e.getMessage();
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
+            return RestResultBean.fromError(restRequest, errorInformation);
         }
 
     }

+ 49 - 80
server/src/main/java/password/pwm/ws/server/rest/RestVerifyResponsesServer.java

@@ -22,53 +22,50 @@
 
 package password.pwm.ws.server.rest;
 
-import com.novell.ldapchai.ChaiUser;
 import com.novell.ldapchai.cr.ChaiChallenge;
 import com.novell.ldapchai.cr.Challenge;
 import com.novell.ldapchai.cr.ResponseSet;
 import com.novell.ldapchai.cr.bean.ChallengeBean;
-import password.pwm.Permission;
-import password.pwm.bean.UserIdentity;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
-import password.pwm.error.PwmException;
+import com.novell.ldapchai.exception.ChaiUnavailableException;
+import lombok.Data;
+import password.pwm.PwmConstants;
+import password.pwm.config.option.WebServiceUsage;
 import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.i18n.Message;
-import password.pwm.svc.stats.Statistic;
-import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.http.HttpContentType;
+import password.pwm.http.HttpMethod;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
-import password.pwm.ws.server.RestRequestBean;
 import password.pwm.ws.server.RestResultBean;
-import password.pwm.ws.server.RestServerHelper;
-import password.pwm.ws.server.ServicePermissions;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
+import password.pwm.ws.server.RestMethodHandler;
+import password.pwm.ws.server.RestRequest;
+import password.pwm.ws.server.RestServlet;
+import password.pwm.ws.server.RestWebServer;
+
+import javax.servlet.annotation.WebServlet;
+import java.io.IOException;
 import java.io.Serializable;
-import java.net.URISyntaxException;
 import java.time.Instant;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
-@Path("/verifyresponses")
-public class RestVerifyResponsesServer extends AbstractRestServer {
+@WebServlet(
+        urlPatterns={
+                PwmConstants.URL_PREFIX_PUBLIC + PwmConstants.URL_PREFIX_REST + "/verifyresponses",
+        }
+)
+@RestWebServer(webService = WebServiceUsage.VerifyResponses, requireAuthentication = true)
+public class RestVerifyResponsesServer extends RestServlet {
     private static final PwmLogger LOGGER = PwmLogger.forClass(RestVerifyResponsesServer.class);
 
+    @Data
     public static class JsonPutChallengesInput implements Serializable {
         public List<ChallengeBean> challenges;
         public String username;
 
-        public Map<Challenge,String> toCrMap()
-        {
-            final Map<Challenge,String> crMap = new LinkedHashMap<>();
+        public Map<Challenge, String> toCrMap() {
+            final Map<Challenge, String> crMap = new LinkedHashMap<>();
             if (challenges != null) {
                 for (final ChallengeBean challengeBean : challenges) {
                     if (challengeBean.getAnswer() == null) {
@@ -79,7 +76,7 @@ public class RestVerifyResponsesServer extends AbstractRestServer {
                     }
                     final String answerText = challengeBean.getAnswer().getAnswerText();
                     final Challenge challenge = ChaiChallenge.fromChallengeBean(challengeBean);
-                    crMap.put(challenge,answerText);
+                    crMap.put(challenge, answerText);
                 }
             }
 
@@ -87,69 +84,41 @@ public class RestVerifyResponsesServer extends AbstractRestServer {
         }
     }
 
-    @GET
-    @Produces(MediaType.TEXT_HTML)
-    public Response doHtmlRedirect() throws URISyntaxException {
-        return RestServerHelper.handleHtmlRequest();
+    @Override
+    public void preCheckRequest(final RestRequest request) throws PwmUnrecoverableException {
     }
 
-    @POST
-    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response doSetChallengeDataJson(
-            final JsonPutChallengesInput jsonInput
-    )
-    {
+    @RestMethodHandler(method = HttpMethod.POST, consumes = HttpContentType.json, produces = HttpContentType.json)
+    public RestResultBean doSetChallengeDataJson(final RestRequest restRequest) throws IOException, PwmUnrecoverableException {
         final Instant startTime = Instant.now();
-        final RestRequestBean restRequestBean;
-        try {
-            final ServicePermissions servicePermissions = ServicePermissions.builder()
-                    .adminOnly(false)
-                    .authRequired(true)
-                    .blockExternal(true)
-                    .build();
-
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, jsonInput.username);
-        } catch (PwmUnrecoverableException e) {
-            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
-        }
 
-        LOGGER.debug(restRequestBean.getPwmSession(),"beginning /verifyresponses REST service against "
-                + (restRequestBean.getUserIdentity() == null ? "self" : restRequestBean.getUserIdentity().toDisplayString()));
+        final JsonPutChallengesInput jsonInput = deserializeJsonBody(restRequest, JsonPutChallengesInput.class);
 
-        try {
-            if (!restRequestBean.getPwmSession().getSessionManager().checkPermission(restRequestBean.getPwmApplication(), Permission.CHANGE_PASSWORD)) {
-                throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNAUTHORIZED,"actor does not have required permission"));
-            }
+        final TargetUserIdentity targetUserIdentity = resolveRequestedUsername(restRequest, jsonInput.getUsername());
 
-            final ChaiUser chaiUser;
-            if (restRequestBean.getUserIdentity() == null) {
-                chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication());
-            } else {
-                final UserIdentity userIdentity = restRequestBean.getUserIdentity();
-                chaiUser = restRequestBean.getPwmSession().getSessionManager().getActor(restRequestBean.getPwmApplication(),userIdentity);
-            }
+        LOGGER.debug(restRequest.getSessionLabel(), "beginning /verifyresponses REST service against "
+                + (targetUserIdentity.isSelf() ? "self" : targetUserIdentity.getUserIdentity().toDisplayString()));
+
+        try {
+            final ResponseSet responseSet = restRequest.getPwmApplication().getCrService().readUserResponseSet(
+                    restRequest.getSessionLabel(),
+                    targetUserIdentity.getUserIdentity(),
+                    targetUserIdentity.getChaiUser()
+            );
 
-            final ResponseSet responseSet = restRequestBean.getPwmApplication().getCrService().readUserResponseSet(restRequestBean.getPwmSession().getLabel(), restRequestBean.getUserIdentity(), chaiUser);
             final boolean verified = responseSet.test(jsonInput.toCrMap());
 
-            if (restRequestBean.isExternal()) {
-                StatisticsManager.incrementStat(restRequestBean.getPwmApplication(), Statistic.REST_SETPASSWORD);
-            }
+            final RestResultBean restResultBean = RestResultBean.withData(verified);
 
-            final String successMsg = Message.Success_Unknown.getLocalizedMessage(request.getLocale(),restRequestBean.getPwmApplication().getConfig());
-            final RestResultBean resultBean = new RestResultBean();
-            resultBean.setError(false);
-            resultBean.setData(verified);
-            resultBean.setSuccessMessage(successMsg);
-            LOGGER.debug(restRequestBean.getPwmSession(),"completed /verifyresponses REST service in " + TimeDuration.fromCurrent(startTime).asCompactString() + ", response: " + JsonUtil.serialize(resultBean));
-            return resultBean.asJsonResponse();
-        } catch (PwmException e) {
-            return RestResultBean.fromError(e.getErrorInformation(),restRequestBean).asJsonResponse();
-        } catch (Exception e) {
-            final String errorMsg = "unexpected error reading json input: " + e.getMessage();
-            final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
-            return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
+            LOGGER.debug(restRequest.getSessionLabel(), "completed /verifyresponses REST service in "
+                    + TimeDuration.fromCurrent(startTime).asCompactString()
+                    + ", response: " + JsonUtil.serialize(restResultBean));
+
+            return restResultBean;
+
+        } catch (ChaiUnavailableException e) {
+            throw PwmUnrecoverableException.fromChaiException(e);
         }
     }
 }
+

+ 0 - 20
server/src/main/java/password/pwm/ws/server/rest/bean/AppDashboardData.java

@@ -95,26 +95,6 @@ public class AppDashboardData implements Serializable {
                     l.forKey("Field_AppVersion", PwmConstants.PWM_APP_NAME),
                     PwmConstants.SERVLET_VERSION
             ));
-            if (config.readSettingAsBoolean(PwmSetting.VERSION_CHECK_ENABLE)) {
-                if (pwmApplication.getVersionChecker() != null) {
-                    final Date readDate = pwmApplication.getVersionChecker().lastReadTimestamp();
-                    if (readDate != null) {
-                        data.add(new DataElement(
-                                "currentPubVersion",
-                                Type.timestamp,
-                                l.forKey("Field_CurrentPubVersion"),
-                                readDate
-                        ));
-                    } else {
-                        data.add(new DataElement(
-                                "currentPubVersion",
-                                Type.string,
-                                l.forKey("Field_CurrentPubVersion"),
-                                NA_VALUE
-                        ));
-                    }
-                }
-            }
             data.add(new DataElement(
                     "currentTime",
                     Type.timestamp,

+ 1 - 1
server/src/main/resources/password/pwm/AppProperty.properties

@@ -69,6 +69,7 @@ db.jdbcLoadStrategy=AppPathFileLoader,Classpath
 db.connections.max=5
 db.connections.timeoutMs=30000
 db.connections.watchdogFrequencySeconds=30
+db.schema.keyLength=128
 download.filename.statistics.csv=Statistics.csv
 download.filename.reportSummary.csv=UserReportSummary.csv
 download.filename.reportRecords.csv=UserReportRecords.csv
@@ -253,7 +254,6 @@ security.responses.hashIterations=100000
 security.input.trim=true
 security.input.password.trim=false
 security.input.themeMatchRegex=^[0-9a-zA-Z-_]*$
-security.ws.rest.clientKeyLength=32
 security.ws.rest.server.secretKeyHeader=AuthorizationSecret
 security.sharedHistory.hashIterations=100000
 security.sharedHistory.hashName=SHA-512

+ 0 - 5
server/src/main/resources/password/pwm/PwmConstants.properties

@@ -28,20 +28,15 @@ locale.defaultLocale=en
 locale.defaultDateTimeFormat=yyyy-MM-dd'T'HH:mm:ss'Z'
 locale.defaultTimeZone=Zulu
 httpHeaderAuthorizationBasic=Basic
-httpRestClientKey=X-RestClientKey
 defaultBadPasswordAttempt=BADPASSWORDATTEMPT
 log.removedValue=*hidden*
 pwm.appName=PWM
 trial=false
 url.pwm-home=https://github.com/pwm-project/pwm
 url.pwm-cloud=https://pwm-cloud.appspot.com
-versionCheckFrequencyMs=129600000
-versionCheckFailRetryMs=1800000
-statisticsPublishFrequencyMs=259200000
 paramName.token=token
 defaultConfigFilename=PwmConfiguration.xml
 enableEulaDisplay=false
-databaseAccessor.keyLength=128
 missingVersionString=[Version Missing]
 applicationPathInfoFile=applicationPath.properties
 includedLocales=["","en_CA","ca","cs","da","de","el","es","fi","fr","fr_CA","hu","it","iw","ja","ko","nl","nn","no","pl","pt","pt_BR","sk","sv","th","tr","zh_CN","zh_TW"]

+ 11 - 7
server/src/main/resources/password/pwm/config/PwmSetting.xml

@@ -66,11 +66,6 @@
         <example>https://www.example.com/example</example>
         <default/>
     </setting>
-    <setting hidden="true" key="pwm.versionCheck.enable" level="1" required="true">
-        <default>
-            <value>false</value>
-        </default>
-    </setting>
     <setting hidden="false" key="pwm.publishStats.enable" level="1" required="true">
         <default>
             <value>false</value>
@@ -3660,8 +3655,17 @@
     <setting hidden="false" key="webservices.external.secrets" level="2">
         <default/>
         <options>
-            <option value="SigningForm">Signing Form Service - /signing/form</option>
-            <option value="Health">Health Service - /health</option>
+            <option value="Challenges">Challenges - /challenges</option>
+            <option value="CheckPassword">Check Password - /checkpassword</option>
+            <option value="Health">Health - /health</option>
+            <option value="Profile">Profile - /profile</option>
+            <option value="RandomPassword">Random Password - /randompassword</option>
+            <option value="SetPassword">Set Password - /setpassword</option>
+            <option value="SigningForm">Signing (Form) - /signing/form</option>
+            <option value="Statistics">Statistics - /statistics</option>
+            <option value="Status">User Status - /status</option>
+            <option value="VerifyOtp">Verify OTP - /verifyotp</option>
+            <option value="VerifyResponses">Verify Responses - /verifyresponses</option>
         </options>
     </setting>
     <setting hidden="false" key="webservices.queryMatch" level="2">

+ 6 - 8
server/src/main/resources/password/pwm/i18n/PwmSetting.properties

@@ -586,7 +586,6 @@ Setting_Description_pwm.publishStats.siteDescription=This optional value can be
 Setting_Description_pwm.securityKey=<p>Specify a Security Key used for cryptographic functions such as the token verification. @PwmAppName@ requires a value if you enabled tokens for any of modules and configured a token storage method. @PwmAppName@ uses this value similar to how a cryptographic security certificate uses the private key.</p> <p>If configured, this value must be at least 32 characters in length.  The longer and more random this value, the more secure its uses are.  If multiple instances are in use, you must configure each instance with the same value.</p><p>Upon initial setup, @PwmAppName@ assigns a random security key to this value that you can change at any time, however, any outstanding tokens or other material generated by an old security key become invalid.</p>
 Setting_Description_pwm.seedlist.location=Specify the location of the seed list in the form of a valid URL. When @PwmAppName@ randomly generates passwords, it can generate a "friendly", random password suggestions to users.  It does this by using a "seed" word or words, and then modifying that word randomly until it is sufficiently complex and meets the configured rules computed for the user.<br/><br/>The value must be a valid URL, using the protocol "file" (local file system), "http", or "https".
 Setting_Description_pwm.selfURL=<p>The URL to this application, as seen by users. @PwmAppName@ uses the value in email macros and other user-facing communications.</p><p>The URL must use a valid fully qualified hostname. Do not use a network address.</p><p>In simple environments, the URL will be the base of the URL in the browser you are currently using to view this page, however in more complex environments the URL will typically be an upstream proxy, gateway or network device.</p>
-Setting_Description_pwm.versionCheck.enable=Enable this option to allow for periodical checks for new versions.  If @PwmAppName@ detects a new version, the health check displays an item indicating a new version is available.
 Setting_Description_pwm.wordlist.location=Specify a word list file URL for dictionary checking to prevent users from using commonly used words as passwords.   Using word lists is an important part of password security.  Word lists are used by intruders to guess common passwords.   The default word list included contains commonly used English passwords.  <br/><br/>The first time a startup occurs with a new word list setting, it takes some time to compile the word list into a database.  See the status screen and logs for progress information.  The word list file format is one or more text files containing a single word per line, enclosed in a ZIP file.  The String <i>\!\#comment\:</i> at the beginning of a line indicates a comment. <br/><br/>The value must be a valid URL, using the protocol "file" (local file system), "http", or "https".
 Setting_Description_recovery.action=Add actions to take when the user completes the forgotten password process.
 Setting_Description_recovery.allowWhenLocked=Enable this option to allow users to use the forgotten password feature when the account is intruder locked in LDAP.  This feature is not available when a user is using NMAS stored responses.
@@ -832,7 +831,7 @@ Setting_Label_external.macros.urls=External Macro REST Server URLs
 Setting_Label_external.pwcheck.urls=External Password Check REST Server URLs
 Setting_Label_external.remoteData.url=Remote Form Data Service
 Setting_Label_external.remoteResponses.url=External Remote Responses REST Server URL
-Setting_Label_external.webservices.enable=Enable External Web Services
+Setting_Label_external.webservices.enable=Enable Web Services
 Setting_Label_forceBasicAuth=Force Basic Authentication
 Setting_Label_forgottenUsername.enable=Enable Forgotten User Name
 Setting_Label_forgottenUsername.form=Forgotten User Name Form
@@ -1070,7 +1069,6 @@ Setting_Label_pwm.publishStats.siteDescription=Site Description
 Setting_Label_pwm.securityKey=Security Key
 Setting_Label_pwm.seedlist.location=Seed List File URL
 Setting_Label_pwm.selfURL=Site URL
-Setting_Label_pwm.versionCheck.enable=Enable Version Checking
 Setting_Label_pwm.wordlist.location=Word List File URL
 Setting_Label_recovery.action=Forgotten Password Recovery Mode
 Setting_Label_recovery.allowWhenLocked=Allow Forgotten Password when Locked
@@ -1163,15 +1161,15 @@ Setting_Label_updateAttributes.showConfirmation=Show Update Profile Confirmation
 Setting_Label_updateAttributes.sms.verification=Enable SMS Verification
 Setting_Label_updateAttributes.token.lifetime=Update Profile Email Token Maximum Lifetime
 Setting_Label_updateAttributes.token.lifetime.sms=Update Profile SMS Token Maximum Lifetime
-Setting_Label_updateAttributes.customLinks=Custom Links 
+Setting_Label_updateAttributes.customLinks=Custom Links
 Setting_Label_updateAttributes.writeAttributes=Update Profile Actions
 Setting_Label_urlshortener.classname=Enable URL Shortening Service Class
 Setting_Label_urlshortener.parameters=Configuration Parameters for URL Shortening Service
 Setting_Label_useXForwardedForHeader=Use X-Forwarded-For Header
-Setting_Label_webservices.enableReadAnswers=Allow Web Services to Read Answers
-Setting_Label_webservices.external.secrets=External Web Services Secrets
+Setting_Label_webservices.enableReadAnswers=Allow Challenge Service to Read Answers
+Setting_Label_webservices.external.secrets=Web Services Secrets
 Setting_Label_webservices.healthStats.makePublic=Enable Public Health and Statistics Web Services
-Setting_Label_webservices.queryMatch=External Web Services Permissions
-Setting_Label_webservices.thirdParty.queryMatch=Web Services Third Party Permissions
+Setting_Label_webservices.queryMatch=Web Services LDAP Authentication Permissions
+Setting_Label_webservices.thirdParty.queryMatch=Web Services LDAP Third Party Permissions
 Setting_Label_webservice.userAttributes=Web Service User Attributes
 Setting_Label_wordlistCaseSensitive=Word List Case Sensitivity

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است