Przeglądaj źródła

form value signing and injection into new user form

Jason Rivard 8 lat temu
rodzic
commit
7bf52299e2

+ 3 - 1
src/main/java/password/pwm/http/bean/NewUserBean.java

@@ -46,6 +46,7 @@ public class NewUserBean extends PwmSessionBean {
     private String profileID;
     private NewUserForm newUserForm;
 
+    private Map<String,String> remoteInputData;
     private boolean agreementPassed;
     private boolean formPassed;
     private Instant createStartTime;
@@ -54,7 +55,8 @@ public class NewUserBean extends PwmSessionBean {
 
     @Getter
     @AllArgsConstructor
-    public static class NewUserForm implements Serializable {
+    public static class
+    NewUserForm implements Serializable {
         private final Map<String,String> formData;
         private final PasswordData newUserPassword;
         private final PasswordData confirmPassword;

+ 11 - 2
src/main/java/password/pwm/http/servlet/newuser/NewUserFormUtils.java

@@ -52,7 +52,9 @@ class NewUserFormUtils {
 
 
     static NewUserBean.NewUserForm readFromRequest(
-            final PwmRequest pwmRequest
+            final PwmRequest pwmRequest,
+            final NewUserBean newUserBean
+
     )
             throws PwmDataValidationException, PwmUnrecoverableException
     {
@@ -74,7 +76,13 @@ class NewUserFormUtils {
             passwordData1 = password;
             passwordData2 = password;
         }
-        return new NewUserBean.NewUserForm(FormUtility.asStringMap(userFormValues), passwordData1, passwordData2);
+
+        final Map<String,String> mergedData = new LinkedHashMap<>(FormUtility.asStringMap(userFormValues));
+        if (newUserBean.getRemoteInputData() != null) {
+            mergedData.putAll(newUserBean.getRemoteInputData());
+        }
+
+        return new NewUserBean.NewUserForm(mergedData, passwordData1, passwordData2);
     }
 
     static NewUserBean.NewUserForm readFromJsonRequest(
@@ -83,6 +91,7 @@ class NewUserFormUtils {
             throws IOException, PwmUnrecoverableException, PwmDataValidationException
     {
         final NewUserProfile newUserProfile = NewUserServlet.getNewUserProfile(pwmRequest);
+
         final boolean promptForPassword = newUserProfile.readSettingAsBoolean(PwmSetting.NEWUSER_PROMPT_FOR_PASSWORD);
 
         final Locale userLocale = pwmRequest.getLocale();

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

@@ -39,6 +39,7 @@ import password.pwm.error.PwmUnrecoverableException;
 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.PwmSession;
@@ -61,6 +62,7 @@ import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.operations.PasswordUtility;
 import password.pwm.ws.server.RestResultBean;
 import password.pwm.ws.server.rest.RestCheckPasswordServer;
+import password.pwm.ws.server.rest.RestSigningServer;
 
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
@@ -98,7 +100,7 @@ public class NewUserServlet extends ControlledPwmServlet {
     static final String TOKEN_PAYLOAD_ATTR = "_______profileID";
 
     public enum NewUserAction implements AbstractPwmServlet.ProcessAction {
-        profileChoice(HttpMethod.POST),
+        profileChoice(HttpMethod.POST, HttpMethod.POST),
         checkProgress(HttpMethod.GET),
         complete(HttpMethod.GET),
         processForm(HttpMethod.POST),
@@ -144,6 +146,13 @@ public class NewUserServlet extends ControlledPwmServlet {
 
         final NewUserBean newUserBean = pwmApplication.getSessionStateService().getBean(pwmRequest, NewUserBean.class);
 
+        final String signedFormData = pwmRequest.readParameterAsString("signedForm", PwmHttpRequestWrapper.Flag.BypassValidation);
+        if (!StringUtil.isEmpty(signedFormData)) {
+            LOGGER.trace("detected signedForm parameter in request, will read and place in bean.");
+            final Map<String,String> jsonForm = RestSigningServer.readSignedFormValue(pwmApplication, signedFormData);
+            newUserBean.setRemoteInputData(jsonForm);
+        }
+
         // convert a url command like /public/newuser/profile/xxx to set profile.
         if (readProfileFromUrl(pwmRequest, newUserBean)) {
             return ProcessStatus.Halt;
@@ -470,7 +479,7 @@ public class NewUserServlet extends ControlledPwmServlet {
         newUserBean.setNewUserForm(null);
 
         try {
-            final NewUserBean.NewUserForm newUserForm = NewUserFormUtils.readFromRequest(pwmRequest);
+            final NewUserBean.NewUserForm newUserForm = NewUserFormUtils.readFromRequest(pwmRequest, newUserBean);
             final PasswordUtility.PasswordCheckInfo passwordCheckInfo = verifyForm(pwmRequest, newUserForm, true);
             NewUserUtils.passwordCheckInfoToException(passwordCheckInfo);
             newUserBean.setNewUserForm(newUserForm);

+ 34 - 98
src/main/java/password/pwm/ws/server/ServicePermissions.java

@@ -22,111 +22,47 @@
 
 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;
-    public static final ServicePermissions ADMIN_OR_CONFIGMODE;
-    public static final ServicePermissions ADMIN_LOCAL_OR_EXTERNAL;
-
-    static {
-        {
-            final ServicePermissions permission = new ServicePermissions();
-            permission.setAdminOnly(false);
-            permission.setAuthRequired(false);
-            permission.setBlockExternal(false);
-            permission.setHelpdeskPermitted(true);
-            permission.lock();
-            PUBLIC = permission;
-        }
-        {
-            final ServicePermissions permission = new ServicePermissions();
-            permission.setAdminOnly(false);
-            permission.setAuthRequired(false);
-            permission.setBlockExternal(true);
-            permission.setPublicDuringConfig(true);
-            permission.lock();
-            ADMIN_OR_CONFIGMODE = permission;
-        }
-        {
-            final ServicePermissions permission = new ServicePermissions();
-            permission.setAdminOnly(true);
-            permission.setAuthRequired(true);
-            permission.setBlockExternal(false);
-            permission.lock();
-            ADMIN_LOCAL_OR_EXTERNAL = permission;
-        }
-    }
-
+    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;
-    private boolean adminOnly = true;
-    private boolean blockExternal = true;
-    private boolean publicDuringConfig = false;
-    private boolean helpdeskPermitted = false;
-
-    private boolean locked = false;
-
-    private void preModifyCheck() {
-        if (locked) {
-            throw new UnsupportedOperationException("ServicePermission is locked");
-        }
-    }
-
-    public boolean isAuthRequired() {
-        return authRequired;
-    }
-
-    public void setAuthRequired(final boolean authRequired) {
-        preModifyCheck();
-        this.authRequired = authRequired;
-    }
 
-    public boolean isBlockExternal() {
-        return blockExternal;
-    }
-
-    public void setBlockExternal(final boolean blockExternal) {
-        preModifyCheck();
-        this.blockExternal = blockExternal;
-    }
-
-    public boolean isAdminOnly() {
-        return adminOnly;
-    }
-
-    public void setAdminOnly(final boolean adminOnly) {
-        preModifyCheck();
-        this.adminOnly = adminOnly;
-    }
-
-    public boolean isHelpdeskPermitted()
-    {
-        return helpdeskPermitted;
-    }
-
-    public void setHelpdeskPermitted(final boolean helpdeskPermitted)
-    {
-        preModifyCheck();
-        this.helpdeskPermitted = helpdeskPermitted;
-    }
-
-    public boolean isLocked()
-    {
-        return locked;
-    }
+    @Builder.Default
+    private boolean adminOnly = true;
 
-    public void lock() {
-        locked = true;
-    }
+    @Builder.Default
+    private boolean blockExternal = true;
 
-    public boolean isPublicDuringConfig()
-    {
-        return publicDuringConfig;
-    }
+    @Builder.Default
+    private boolean publicDuringConfig = false;
 
-    public void setPublicDuringConfig(final boolean publicDuringConfig)
-    {
-        this.publicDuringConfig = publicDuringConfig;
-    }
+    @Builder.Default
+    private boolean helpdeskPermitted = false;
 }

+ 9 - 15
src/main/java/password/pwm/ws/server/rest/RestAppDataServer.java

@@ -91,6 +91,12 @@ public class RestAppDataServer extends AbstractRestServer {
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(RestAppDataServer.class);
 
+    private static final ServicePermissions SERVICE_PERMISSIONS = ServicePermissions.builder()
+            .adminOnly(true)
+            .authRequired(true)
+            .blockExternal(true)
+            .build();
+
     public static class AppData implements Serializable {
         public Map<String,Object> PWM_GLOBAL;
     }
@@ -117,11 +123,7 @@ public class RestAppDataServer extends AbstractRestServer {
 
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(true);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, null);
+            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, null);
         } catch (PwmUnrecoverableException e) {
             return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
         }
@@ -166,11 +168,7 @@ public class RestAppDataServer extends AbstractRestServer {
                 : 10 * 1000;
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(true);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, null);
+            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, null);
         } catch (PwmUnrecoverableException e) {
             return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
         }
@@ -206,11 +204,7 @@ public class RestAppDataServer extends AbstractRestServer {
 
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(true);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, null);
+            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, null);
         } catch (PwmUnrecoverableException e) {
             return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
         }

+ 13 - 15
src/main/java/password/pwm/ws/server/rest/RestChallengesServer.java

@@ -77,6 +77,13 @@ 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();
+
     public static class Policy {
         public List<ChallengeBean> challenges;
         public List<ChallengeBean> helpdeskChallenges;
@@ -151,11 +158,7 @@ public class RestChallengesServer extends AbstractRestServer {
     {
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(false);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, username);
+            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, username);
         } catch (PwmUnrecoverableException e) {
             return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
         }
@@ -251,11 +254,7 @@ public class RestChallengesServer extends AbstractRestServer {
     {
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(false);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
-            restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, jsonInput.username);
+            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, jsonInput.username);
         } catch (PwmUnrecoverableException e) {
             return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
         }
@@ -323,11 +322,10 @@ public class RestChallengesServer extends AbstractRestServer {
     {
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(false);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
-            servicePermissions.setHelpdeskPermitted(true);
+            final ServicePermissions servicePermissions = SERVICE_PERMISSIONS.toBuilder()
+                    .helpdeskPermitted(true)
+                    .build();
+
             restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, username);
         } catch (PwmUnrecoverableException e) {
             return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();

+ 6 - 5
src/main/java/password/pwm/ws/server/rest/RestCheckPasswordServer.java

@@ -135,11 +135,12 @@ public class RestCheckPasswordServer extends AbstractRestServer {
         final Instant startTime = Instant.now();
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(false);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
-            servicePermissions.setHelpdeskPermitted(true);
+            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();

+ 9 - 11
src/main/java/password/pwm/ws/server/rest/RestProfileServer.java

@@ -61,6 +61,13 @@ 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();
+
+
     public static class JsonProfileData implements Serializable {
         public String username;
         public Map<String,String> profile;
@@ -91,11 +98,7 @@ public class RestProfileServer extends AbstractRestServer {
     )
             throws PwmUnrecoverableException, ChaiUnavailableException
     {
-        final ServicePermissions servicePermissions = new ServicePermissions();
-        servicePermissions.setAdminOnly(false);
-        servicePermissions.setAuthRequired(true);
-        servicePermissions.setBlockExternal(true);
-        final RestRequestBean restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, username);
+        final RestRequestBean restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, username);
 
         if (!restRequestBean.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_ENABLE)) {
             throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE);
@@ -165,12 +168,7 @@ public class RestProfileServer extends AbstractRestServer {
     )
             throws PwmUnrecoverableException, ChaiUnavailableException, PwmOperationalException
     {
-
-        final ServicePermissions servicePermissions = new ServicePermissions();
-        servicePermissions.setAdminOnly(false);
-        servicePermissions.setAuthRequired(true);
-        servicePermissions.setBlockExternal(true);
-        final RestRequestBean restRequestBean = RestServerHelper.initializeRestRequest(request, response, servicePermissions, jsonInput.username);
+        final RestRequestBean restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, jsonInput.username);
 
         if (!restRequestBean.getPwmApplication().getConfig().readSettingAsBoolean(PwmSetting.UPDATE_PROFILE_ENABLE)) {
             throw new PwmUnrecoverableException(PwmError.ERROR_SERVICE_NOT_AVAILABLE);

+ 7 - 8
src/main/java/password/pwm/ws/server/rest/RestRandomPasswordServer.java

@@ -59,15 +59,14 @@ import java.util.List;
 @Path("/randompassword")
 public class RestRandomPasswordServer extends AbstractRestServer {
     private static final PwmLogger LOGGER = PwmLogger.forClass(RestRandomPasswordServer.class);
-    private static final ServicePermissions SERVICE_PERMISSIONS = new ServicePermissions();
 
-    static {
-        SERVICE_PERMISSIONS.setAdminOnly(false);
-        SERVICE_PERMISSIONS.setAuthRequired(false);
-        SERVICE_PERMISSIONS.setBlockExternal(true);
-        SERVICE_PERMISSIONS.setHelpdeskPermitted(true);
-        SERVICE_PERMISSIONS.setPublicDuringConfig(true);
-    }
+    private static final ServicePermissions SERVICE_PERMISSIONS = ServicePermissions.builder()
+            .adminOnly(false)
+            .authRequired(false)
+            .blockExternal(true)
+            .helpdeskPermitted(true)
+            .publicDuringConfig(true)
+            .build();
 
     public static class JsonOutput implements Serializable
     {

+ 7 - 6
src/main/java/password/pwm/ws/server/rest/RestSetPasswordServer.java

@@ -59,7 +59,6 @@ import java.io.Serializable;
 public class RestSetPasswordServer extends AbstractRestServer {
 
     public static final PwmLogger LOGGER = PwmLogger.forClass(RestSetPasswordServer.class);
-    private static Serializable restResultBeanData;
 
     public static class JsonInputData implements Serializable
     {
@@ -106,11 +105,13 @@ public class RestSetPasswordServer extends AbstractRestServer {
     {
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(false);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
-            servicePermissions.setHelpdeskPermitted(true);
+            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();

+ 116 - 0
src/main/java/password/pwm/ws/server/rest/RestSigningServer.java

@@ -0,0 +1,116 @@
+/*
+ * 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 lombok.AllArgsConstructor;
+import lombok.Getter;
+import password.pwm.PwmApplication;
+import password.pwm.error.ErrorInformation;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.java.TimeDuration;
+import password.pwm.util.secure.SecureService;
+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.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Path("/signing")
+public class RestSigningServer extends AbstractRestServer {
+    private static final ServicePermissions SERVICE_PERMISSIONS = ServicePermissions.builder()
+            .adminOnly(false)
+            .authRequired(false)
+            .blockExternal(false)
+            .build();
+
+
+    @POST
+    @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Path("/form")
+    public Response doGetProfileJsonData(
+            final Map<String,String> inputFormData
+
+    )
+            throws PwmUnrecoverableException
+    {
+        final RestRequestBean restRequestBean;
+        try {
+            restRequestBean = RestServerHelper.initializeRestRequest(request, response, SERVICE_PERMISSIONS, null);
+        } catch (PwmUnrecoverableException e) {
+            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
+        }
+
+        try {
+            if (inputFormData != null) {
+                final SecureService securityService = restRequestBean.getPwmApplication().getSecureService();
+                final SignedFormData signedFormData = new SignedFormData(Instant.now(), inputFormData);
+                final String signedValue = securityService.encryptObjectToString(signedFormData);
+                final RestResultBean restResultBean = new RestResultBean(signedValue);
+                return restResultBean.asJsonResponse();
+            }
+            return RestResultBean.fromError(new ErrorInformation(PwmError.ERROR_MISSING_PARAMETER,"no json form in body")).asJsonResponse();
+        } catch (PwmUnrecoverableException e) {
+            return RestResultBean.fromError(e.getErrorInformation()).asJsonResponse();
+        } 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();
+        }
+    }
+
+    public static Map<String,String> readSignedFormValue(final PwmApplication pwmApplication, final String input) throws PwmUnrecoverableException
+    {
+        final TimeDuration MAX_FORM_AGE = new TimeDuration(5, TimeUnit.MINUTES);
+        final SignedFormData signedFormData = pwmApplication.getSecureService().decryptObject(input, SignedFormData.class);
+        if (signedFormData != null) {
+            if (signedFormData.getTimestamp() != null) {
+                if (TimeDuration.fromCurrent(signedFormData.getTimestamp()).isLongerThan(MAX_FORM_AGE)) {
+                    throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_SECURITY_VIOLATION,"signedForm data is too old"));
+                }
+
+                return signedFormData.getFormData();
+            }
+        }
+        return null;
+    }
+
+    @Getter
+    @AllArgsConstructor
+    private static class SignedFormData implements Serializable  {
+        private Instant timestamp;
+        private Map<String,String> formData;
+    }
+
+}

+ 6 - 4
src/main/java/password/pwm/ws/server/rest/RestStatusServer.java

@@ -68,10 +68,12 @@ public class RestStatusServer extends AbstractRestServer {
         final Instant startTime = Instant.now();
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(false);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
+            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();

+ 8 - 6
src/main/java/password/pwm/ws/server/rest/RestVerifyOtpServer.java

@@ -70,10 +70,12 @@ public class RestVerifyOtpServer extends AbstractRestServer {
     public Response doSetOtpDataJson(final RestVerifyOtpServer.JsonPutOtpInput jsonInput) {
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(false);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
+            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();
@@ -104,7 +106,7 @@ public class RestVerifyOtpServer extends AbstractRestServer {
             resultBean.setError(false);
             resultBean.setData(verified);
             resultBean.setSuccessMessage(successMsg);
-            return resultBean.asJsonResponse();                    
+            return resultBean.asJsonResponse();
         } catch (PwmUnrecoverableException e) {
             return RestResultBean.fromError(e.getErrorInformation(),restRequestBean).asJsonResponse();
         } catch (ChaiUnavailableException e) {
@@ -120,7 +122,7 @@ public class RestVerifyOtpServer extends AbstractRestServer {
             final ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_UNKNOWN, errorMsg);
             return RestResultBean.fromError(errorInformation,restRequestBean).asJsonResponse();
         }
-        
+
     }
 
 }

+ 8 - 6
src/main/java/password/pwm/ws/server/rest/RestVerifyResponsesServer.java

@@ -61,7 +61,7 @@ import java.util.Map;
 @Path("/verifyresponses")
 public class RestVerifyResponsesServer extends AbstractRestServer {
     private static final PwmLogger LOGGER = PwmLogger.forClass(RestVerifyResponsesServer.class);
-    
+
     public static class JsonPutChallengesInput implements Serializable {
         public List<ChallengeBean> challenges;
         public String username;
@@ -103,10 +103,12 @@ public class RestVerifyResponsesServer extends AbstractRestServer {
         final Instant startTime = Instant.now();
         final RestRequestBean restRequestBean;
         try {
-            final ServicePermissions servicePermissions = new ServicePermissions();
-            servicePermissions.setAdminOnly(false);
-            servicePermissions.setAuthRequired(true);
-            servicePermissions.setBlockExternal(true);
+            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();
@@ -114,7 +116,7 @@ public class RestVerifyResponsesServer extends AbstractRestServer {
 
         LOGGER.debug(restRequestBean.getPwmSession(),"beginning /verifyresponses REST service against "
                 + (restRequestBean.getUserIdentity() == null ? "self" : restRequestBean.getUserIdentity().toDisplayString()));
-        
+
         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"));