keycloak-memoizeit

Merge pull request #1559 from patriot1burke/master totp

8/21/2015 9:59:51 PM

Details

diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-config-totp.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-config-totp.ftl
index f1effe7..fc264f9 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/login-config-totp.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-config-totp.ftl
@@ -5,7 +5,7 @@
     <#elseif section = "header">
         ${msg("loginTotpTitle")}
     <#elseif section = "form">
-        <form action="${url.loginUpdateTotpUrl}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
+        <form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
                     <label for="otp" class="${properties.kcLabelClass!}">${msg("loginTotpOneTime")}</label>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
index f80909e..2257508 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
@@ -5,7 +5,7 @@
     <#elseif section = "header">
         ${msg("loginProfileTitle")}
     <#elseif section = "form">
-        <form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginUpdateProfileUrl}" method="post">
+        <form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
             <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('email',properties.kcFormGroupErrorClass!)}">
                 <div class="${properties.kcLabelWrapperClass!}">
                     <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
index 1521efa..d336fb9 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
@@ -58,7 +58,8 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
 
     @Override
     public void requiredActionChallenge(RequiredActionContext context) {
-        Response challenge = context.form().createForm("login-update-password.ftl");
+        Response challenge = context.form()
+                .createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
         context.challenge(challenge);
     }
 
@@ -73,13 +74,13 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
         if (Validation.isBlank(passwordNew)) {
             Response challenge = context.form()
                     .setError(Messages.MISSING_PASSWORD)
-                    .createForm("login-update-password.ftl");
+                    .createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
             context.challenge(challenge);
             return;
         } else if (!passwordNew.equals(passwordConfirm)) {
             Response challenge = context.form()
                     .setError(Messages.NOTMATCH_PASSWORD)
-                    .createForm("login-update-password.ftl");
+                    .createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
             context.challenge(challenge);
             return;
         }
@@ -90,13 +91,13 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
         } catch (ModelException me) {
             Response challenge = context.form()
                     .setError(me.getMessage(), me.getParameters())
-                    .createForm("login-update-password.ftl");
+                    .createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
             context.challenge(challenge);
             return;
         } catch (Exception ape) {
             Response challenge = context.form()
                     .setError(ape.getMessage())
-                    .createForm("login-update-password.ftl");
+                    .createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
             context.challenge(challenge);
             return;
         }
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
index 9ca78a6..42c2e02 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
@@ -5,12 +5,25 @@ import org.keycloak.Config;
 import org.keycloak.authentication.RequiredActionContext;
 import org.keycloak.authentication.RequiredActionFactory;
 import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
 import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
-
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.FormMessage;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.resources.AttributeFormDataProcessor;
+import org.keycloak.services.validation.Validation;
+
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+import java.util.List;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -24,16 +37,63 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
 
     @Override
     public void requiredActionChallenge(RequiredActionContext context) {
-        LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
-                .setClientSessionCode(context.generateAccessCode(UserModel.RequiredAction.UPDATE_PROFILE.name()))
-                .setUser(context.getUser());
-        Response challenge = loginFormsProvider.createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
+        Response challenge = context.form()
+                .createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
         context.challenge(challenge);
     }
 
     @Override
     public void processAction(RequiredActionContext context) {
-        context.failure();
+        EventBuilder event = context.getEvent();
+        event.event(EventType.UPDATE_PROFILE);
+        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+        UserModel user = context.getUser();
+        KeycloakSession session = context.getSession();
+        RealmModel realm = context.getRealm();
+
+
+        List<FormMessage> errors = Validation.validateUpdateProfileForm(formData);
+        if (errors != null && !errors.isEmpty()) {
+            Response challenge = context.form()
+                    .setErrors(errors)
+                    .setFormData(formData)
+                    .createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
+            context.challenge(challenge);
+            return;
+        }
+
+        user.setFirstName(formData.getFirst("firstName"));
+        user.setLastName(formData.getFirst("lastName"));
+
+        String email = formData.getFirst("email");
+
+        String oldEmail = user.getEmail();
+        boolean emailChanged = oldEmail != null ? !oldEmail.equals(email) : email != null;
+
+        if (emailChanged) {
+            UserModel userByEmail = session.users().getUserByEmail(email, realm);
+
+            // check for duplicated email
+            if (userByEmail != null && !userByEmail.getId().equals(user.getId())) {
+                Response challenge = context.form()
+                        .setError(Messages.EMAIL_EXISTS)
+                        .setFormData(formData)
+                        .createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
+                context.challenge(challenge);
+                return;
+            }
+
+            user.setEmail(email);
+            user.setEmailVerified(false);
+        }
+
+        AttributeFormDataProcessor.process(formData, realm, user);
+
+        if (emailChanged) {
+            event.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
+        }
+        context.success();
+
     }
 
 
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
index 61b035a..18e6682 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
@@ -5,13 +5,23 @@ import org.keycloak.Config;
 import org.keycloak.authentication.RequiredActionContext;
 import org.keycloak.authentication.RequiredActionFactory;
 import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
 import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.CredentialValidation;
 import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
 
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 
 /**
@@ -26,16 +36,47 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
 
     @Override
     public void requiredActionChallenge(RequiredActionContext context) {
-         LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
-                 .setClientSessionCode(context.generateAccessCode(UserModel.RequiredAction.CONFIGURE_TOTP.name()))
-                .setUser(context.getUser());
-        Response challenge = loginFormsProvider.createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
+        Response challenge = context.form()
+                .createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
         context.challenge(challenge);
     }
 
     @Override
     public void processAction(RequiredActionContext context) {
-        context.failure();
+        EventBuilder event = context.getEvent();
+        event.event(EventType.UPDATE_TOTP);
+        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+        String totp = formData.getFirst("totp");
+        String totpSecret = formData.getFirst("totpSecret");
+
+        if (Validation.isBlank(totp)) {
+            Response challenge = context.form()
+                    .setError(Messages.MISSING_TOTP)
+                    .createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
+            context.challenge(challenge);
+            return;
+        } else if (!CredentialValidation.validOTP(context.getRealm(), totp, totpSecret)) {
+            Response challenge = context.form()
+                    .setError(Messages.INVALID_TOTP)
+                    .createResponse(UserModel.RequiredAction.CONFIGURE_TOTP);
+            context.challenge(challenge);
+            return;
+        }
+
+        UserCredentialModel credentials = new UserCredentialModel();
+        credentials.setType(context.getRealm().getOTPPolicy().getType());
+        credentials.setValue(totpSecret);
+        context.getSession().users().updateCredential(context.getRealm(), context.getUser(), credentials);
+
+
+        // if type is HOTP, to update counter we execute validation based on supplied token
+        UserCredentialModel cred = new UserCredentialModel();
+        cred.setType(context.getRealm().getOTPPolicy().getType());
+        cred.setValue(totp);
+        context.getSession().users().validCredentials(context.getRealm(), context.getUser(), cred);
+
+        context.getUser().setOtpEnabled(true);
+        context.success();
     }
 
 
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index eea2499..0a8d3c6 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -512,122 +512,6 @@ public class LoginActionsService {
         return authManager.redirectAfterSuccessfulFlow(session, realm, userSession, clientSession, request, uriInfo, clientConnection);
     }
 
-    @Path("profile")
-    @POST
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response updateProfile(@QueryParam("code") String code,
-                                  final MultivaluedMap<String, String> formData) {
-        event.event(EventType.UPDATE_PROFILE);
-        Checks checks = new Checks();
-        if (!checks.verifyCode(code, ClientSessionModel.Action.UPDATE_PROFILE.name())) {
-            return checks.response;
-        }
-        ClientSessionCode accessCode = checks.clientCode;
-        ClientSessionModel clientSession = accessCode.getClientSession();
-        UserSessionModel userSession = clientSession.getUserSession();
-        UserModel user = userSession.getUser();
-
-        initEvent(clientSession);
-
-        List<FormMessage> errors = Validation.validateUpdateProfileForm(formData);
-        if (errors != null && !errors.isEmpty()) {
-            return session.getProvider(LoginFormsProvider.class)
-                    .setClientSessionCode(accessCode.getCode())
-                    .setUser(user)
-                    .setErrors(errors)
-                    .setFormData(formData)
-                    .createResponse(RequiredAction.UPDATE_PROFILE);
-        }
-
-        user.setFirstName(formData.getFirst("firstName"));
-        user.setLastName(formData.getFirst("lastName"));
-
-        String email = formData.getFirst("email");
-
-        String oldEmail = user.getEmail();
-        boolean emailChanged = oldEmail != null ? !oldEmail.equals(email) : email != null;
-
-        if (emailChanged) {
-            UserModel userByEmail = session.users().getUserByEmail(email, realm);
-
-            // check for duplicated email
-            if (userByEmail != null && !userByEmail.getId().equals(user.getId())) {
-                return session.getProvider(LoginFormsProvider.class)
-                        .setUser(user)
-                        .setError(Messages.EMAIL_EXISTS)
-                        .setClientSessionCode(accessCode.getCode())
-                        .setFormData(formData)
-                        .createResponse(RequiredAction.UPDATE_PROFILE);
-            }
-
-            user.setEmail(email);
-            user.setEmailVerified(false);
-        }
-
-        AttributeFormDataProcessor.process(formData, realm, user);
-
-        user.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
-        event.clone().event(EventType.UPDATE_PROFILE).success();
-
-        if (emailChanged) {
-            event.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
-        }
-
-        return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
-    }
-
-    @Path("totp")
-    @POST
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response updateTotp(@QueryParam("code") String code,
-                               final MultivaluedMap<String, String> formData) {
-        event.event(EventType.UPDATE_TOTP);
-        Checks checks = new Checks();
-        if (!checks.verifyCode(code, ClientSessionModel.Action.CONFIGURE_TOTP.name())) {
-            return checks.response;
-        }
-        ClientSessionCode accessCode = checks.clientCode;
-        ClientSessionModel clientSession = accessCode.getClientSession();
-        UserSessionModel userSession = clientSession.getUserSession();
-        UserModel user = userSession.getUser();
-
-        initEvent(clientSession);
-
-        String totp = formData.getFirst("totp");
-        String totpSecret = formData.getFirst("totpSecret");
-
-        LoginFormsProvider loginForms = session.getProvider(LoginFormsProvider.class).setUser(user);
-        if (Validation.isBlank(totp)) {
-            return loginForms.setError(Messages.MISSING_TOTP)
-                    .setClientSessionCode(accessCode.getCode())
-                    .createResponse(RequiredAction.CONFIGURE_TOTP);
-        } else if (!CredentialValidation.validOTP(realm, totp, totpSecret)) {
-            return loginForms.setError(Messages.INVALID_TOTP)
-                    .setClientSessionCode(accessCode.getCode())
-                    .createResponse(RequiredAction.CONFIGURE_TOTP);
-        }
-
-        UserCredentialModel credentials = new UserCredentialModel();
-        credentials.setType(realm.getOTPPolicy().getType());
-        credentials.setValue(totpSecret);
-        session.users().updateCredential(realm, user, credentials);
-
-
-        // if type is HOTP, to update counter we execute validation based on supplied token
-        UserCredentialModel cred = new UserCredentialModel();
-        cred.setType(realm.getOTPPolicy().getType());
-        cred.setValue(totp);
-        session.users().validCredentials(realm, user, cred);
-
-        user.setOtpEnabled(true);
-
-        user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
-
-        event.clone().event(EventType.UPDATE_TOTP).success();
-
-        return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
-    }
-
     @Path("email-verification")
     @GET
     public Response emailVerification(@QueryParam("code") String code, @QueryParam("key") String key) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
index 4d16a1b..868a76c 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
@@ -123,12 +123,12 @@ public class RequiredActionMultipleActionsTest {
     public String updateProfile(String sessionId) {
         updateProfilePage.update("New first", "New last", "new@email.com");
 
-        AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_PROFILE);
+        AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com");
         if (sessionId != null) {
             expectedEvent.session(sessionId);
         }
         sessionId = expectedEvent.assertEvent().getSessionId();
-        events.expectRequiredAction(EventType.UPDATE_EMAIL).session(sessionId).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
+        events.expectRequiredAction(EventType.UPDATE_PROFILE).session(sessionId).assertEvent();
         return sessionId;
     }
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
index a7c84a0..e3d6ac9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
@@ -89,8 +89,8 @@ public class RequiredActionUpdateProfileTest {
 
         updateProfilePage.update("New first", "New last", "new@email.com");
 
-        String sessionId = events.expectRequiredAction(EventType.UPDATE_PROFILE).assertEvent().getSessionId();
-        events.expectRequiredAction(EventType.UPDATE_EMAIL).session(sessionId).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
+        String sessionId = events.expectRequiredAction(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent().getSessionId();
+        events.expectRequiredAction(EventType.UPDATE_PROFILE).session(sessionId).assertEvent();
 
         Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());