keycloak-memoizeit

Details

diff --git a/events/api/src/main/java/org/keycloak/events/EventType.java b/events/api/src/main/java/org/keycloak/events/EventType.java
index b049b2a..250ac9f 100755
--- a/events/api/src/main/java/org/keycloak/events/EventType.java
+++ b/events/api/src/main/java/org/keycloak/events/EventType.java
@@ -67,7 +67,8 @@ public enum EventType {
     IDENTITY_PROVIDER_ACCCOUNT_LINKING(false),
     IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR(false),
     IMPERSONATE(true),
-    CUSTOM_REQUIRED_ACTION(true);
+    CUSTOM_REQUIRED_ACTION(true),
+    CUSTOM_REQUIRED_ACTION_ERROR(true);
 
     private boolean saveByDefault;
 
diff --git a/forms/common-themes/src/main/resources/theme/base/login/info.ftl b/forms/common-themes/src/main/resources/theme/base/login/info.ftl
index f4de855..ca50401 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/info.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/info.ftl
@@ -7,8 +7,11 @@
     <#elseif section = "form">
     <div id="kc-info-message">
         <p class="instruction">${message.summary}</p>
-        <#if client.baseUrl??>
-        <p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p>
+        <#if skipLink??>
+        <#else>
+            <#if client.baseUrl??>
+                <p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p>
+            </#if>
         </#if>
     </div>
     </#if>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-update-password.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-update-password.ftl
index d13729e..bad990c 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/login-update-password.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-update-password.ftl
@@ -5,7 +5,7 @@
     <#elseif section = "header">
         ${msg("updatePasswordTitle")}
     <#elseif section = "form">
-        <form id="kc-passwd-update-form" class="${properties.kcFormClass!}" action="${url.loginUpdatePasswordUrl}" method="post">
+        <form id="kc-passwd-update-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
                     <label for="password-new" class="${properties.kcLabelClass!}">${msg("passwordNew")}</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 f88cdc8..1521efa 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
@@ -5,14 +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.ModelException;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
 import org.keycloak.util.Time;
 
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import java.util.concurrent.TimeUnit;
 
@@ -49,17 +58,48 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
 
     @Override
     public void requiredActionChallenge(RequiredActionContext context) {
-        LoginFormsProvider loginFormsProvider = context.getSession()
-                .getProvider(LoginFormsProvider.class)
-                .setClientSessionCode(context.generateAccessCode(UserModel.RequiredAction.UPDATE_PASSWORD.name()))
-                .setUser(context.getUser());
-        Response challenge = loginFormsProvider.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
+        Response challenge = context.form().createForm("login-update-password.ftl");
         context.challenge(challenge);
     }
 
     @Override
     public void processAction(RequiredActionContext context) {
-        context.failure();
+        EventBuilder event = context.getEvent();
+        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+        event.event(EventType.UPDATE_PASSWORD);
+        String passwordNew = formData.getFirst("password-new");
+        String passwordConfirm = formData.getFirst("password-confirm");
+
+        if (Validation.isBlank(passwordNew)) {
+            Response challenge = context.form()
+                    .setError(Messages.MISSING_PASSWORD)
+                    .createForm("login-update-password.ftl");
+            context.challenge(challenge);
+            return;
+        } else if (!passwordNew.equals(passwordConfirm)) {
+            Response challenge = context.form()
+                    .setError(Messages.NOTMATCH_PASSWORD)
+                    .createForm("login-update-password.ftl");
+            context.challenge(challenge);
+            return;
+        }
+
+        try {
+            context.getSession().users().updateCredential(context.getRealm(), context.getUser(), UserCredentialModel.password(passwordNew));
+            context.success();
+        } catch (ModelException me) {
+            Response challenge = context.form()
+                    .setError(me.getMessage(), me.getParameters())
+                    .createForm("login-update-password.ftl");
+            context.challenge(challenge);
+            return;
+        } catch (Exception ape) {
+            Response challenge = context.form()
+                    .setError(ape.getMessage())
+                    .createForm("login-update-password.ftl");
+            context.challenge(challenge);
+            return;
+        }
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 52cca07..70f10e7 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -32,6 +32,7 @@ import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.RestartLoginCookie;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.IdentityBrokerService;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.Urls;
@@ -57,6 +58,7 @@ import java.util.Set;
  * @version $Revision: 1 $
  */
 public class AuthenticationManager {
+    public static final String END_AFTER_REQUIRED_ACTIONS = "END_AFTER_REQUIRED_ACTIONS";
     protected static Logger logger = Logger.getLogger(AuthenticationManager.class);
     public static final String FORM_USERNAME = "username";
     // used for auth login
@@ -409,6 +411,15 @@ public class AuthenticationManager {
                                                   HttpRequest request, UriInfo uriInfo, EventBuilder event) {
         Response requiredAction = actionRequired(session, userSession, clientSession, clientConnection, request, uriInfo, event);
         if (requiredAction != null) return requiredAction;
+        if (clientSession.getNote(END_AFTER_REQUIRED_ACTIONS) != null) {
+            Response response = session.getProvider(LoginFormsProvider.class)
+                    .setAttribute("skipLink", true)
+                    .setSuccess(Messages.ACCOUNT_UPDATED)
+                    .createInfoPage();
+            session.sessions().removeUserSession(session.getContext().getRealm(), userSession);
+            return response;
+
+        }
         event.success();
         RealmModel realm = clientSession.getRealm();
         return redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, clientConnection);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 066ce30..6d55bc2 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -854,6 +854,7 @@ public class UsersResource {
         accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
 
         try {
+            user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
             UriBuilder builder = Urls.recoverPasswordBuilder(uriInfo.getBaseUri());
             builder.queryParam("key", accessCode.getCode());
 
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 dd2c90d..eea2499 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -628,67 +628,6 @@ public class LoginActionsService {
         return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
     }
 
-    @Path("password")
-    @POST
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response updatePassword(@QueryParam("code") String code,
-                                   final MultivaluedMap<String, String> formData) {
-        event.event(EventType.UPDATE_PASSWORD);
-        Checks checks = new Checks();
-        if (!checks.verifyCode(code, ClientSessionModel.Action.UPDATE_PASSWORD.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
-            return checks.response;
-        }
-        ClientSessionCode accessCode = checks.clientCode;
-        ClientSessionModel clientSession = accessCode.getClientSession();
-        UserSessionModel userSession = clientSession.getUserSession();
-        UserModel user = userSession.getUser();
-
-        initEvent(clientSession);
-
-        String passwordNew = formData.getFirst("password-new");
-        String passwordConfirm = formData.getFirst("password-confirm");
-
-        LoginFormsProvider loginForms = session.getProvider(LoginFormsProvider.class)
-                .setUser(user);
-        if (Validation.isBlank(passwordNew)) {
-            return loginForms.setError(Messages.MISSING_PASSWORD)
-                    .setClientSessionCode(accessCode.getCode())
-                    .createResponse(RequiredAction.UPDATE_PASSWORD);
-        } else if (!passwordNew.equals(passwordConfirm)) {
-            return loginForms.setError(Messages.NOTMATCH_PASSWORD)
-                    .setClientSessionCode(accessCode.getCode())
-                    .createResponse(RequiredAction.UPDATE_PASSWORD);
-        }
-
-        try {
-            session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
-        } catch (ModelException me) {
-            return loginForms.setError(me.getMessage(), me.getParameters())
-                    .setClientSessionCode(accessCode.getCode())
-                    .createResponse(RequiredAction.UPDATE_PASSWORD);
-        } catch (Exception ape) {
-            return loginForms.setError(ape.getMessage())
-                    .setClientSessionCode(accessCode.getCode())
-                    .createResponse(RequiredAction.UPDATE_PASSWORD);
-        }
-
-        user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
-
-        event.event(EventType.UPDATE_PASSWORD).success();
-
-        if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
-            session.sessions().removeClientSession(realm, clientSession);
-            return session.getProvider(LoginFormsProvider.class)
-                    .setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED)
-                    .createInfoPage();
-        }
-
-        event = event.clone().event(EventType.LOGIN);
-
-        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) {
@@ -752,13 +691,11 @@ public class LoginActionsService {
         if (key != null) {
             Checks checks = new Checks();
             if (!checks.verifyCode(key, ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
-                event.error(Errors.RESET_CREDENTIAL_DISABLED);
-                return ErrorPage.error(session, Messages.INVALID_CODE);
+                return checks.response;
             }
-            ClientSessionCode accessCode = checks.clientCode;
-            return session.getProvider(LoginFormsProvider.class)
-                    .setClientSessionCode(accessCode.getCode())
-                    .createResponse(RequiredAction.UPDATE_PASSWORD);
+            ClientSessionModel clientSession = checks.clientCode.getClientSession();
+            clientSession.setNote("END_AFTER_REQUIRED_ACTIONS", "true");
+            return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
         } else {
             event.error(Errors.RESET_CREDENTIAL_DISABLED);
             return ErrorPage.error(session, Messages.INVALID_CODE);
@@ -843,6 +780,7 @@ public class LoginActionsService {
         }
 
         initEvent(clientSession);
+        event.event(EventType.CUSTOM_REQUIRED_ACTION);
 
 
         RequiredActionContextResult context = new RequiredActionContextResult(clientSession.getUserSession(), clientSession, realm, event, session, request, clientSession.getUserSession().getUser(), factory) {
@@ -865,9 +803,9 @@ public class LoginActionsService {
         };
         provider.processAction(context);
         if (context.getStatus() == RequiredActionContext.Status.SUCCESS) {
-            event.clone().event(EventType.CUSTOM_REQUIRED_ACTION)
-                         .detail(Details.CUSTOM_REQUIRED_ACTION, action).success();
+            event.clone().success();
             clientSession.getUserSession().getUser().removeRequiredAction(factory.getId());
+            event.event(EventType.LOGIN);
             return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
         }
         if (context.getStatus() == RequiredActionContext.Status.CHALLENGE) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/TermsAndConditionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/TermsAndConditionsTest.java
index a1636c8..f758900 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/TermsAndConditionsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/TermsAndConditionsTest.java
@@ -108,7 +108,7 @@ public class TermsAndConditionsTest {
 
         termsPage.declineTerms();
 
-        events.expectLogin().detail(Details.CUSTOM_REQUIRED_ACTION, TermsAndConditions.PROVIDER_ID)
+        events.expectLogin().event(EventType.CUSTOM_REQUIRED_ACTION_ERROR).detail(Details.CUSTOM_REQUIRED_ACTION, TermsAndConditions.PROVIDER_ID)
                 .error(Errors.REJECTED_BY_USER)
                 .removeDetail(Details.CONSENT)
                 .assertEvent();