keycloak-aplcache

Details

diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
index 4c25bb3..b4b3f16 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
@@ -45,6 +45,9 @@ public class UrlBean {
     }
 
     public String getLoginAction() {
+        if (this.actionuri != null) {
+            return this.actionuri.toString();
+        }
         return Urls.realmLoginAction(baseURI, realm).toString();
     }
 
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 5920e10..1ebfa50 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -279,6 +279,10 @@ public class AuthenticationProcessor {
             super(message, cause, enableSuppression, writableStackTrace);
             this.error = error;
         }
+
+        public Error getError() {
+            return error;
+        }
     }
 
     public void logUserFailure() {
@@ -389,6 +393,9 @@ public class AuthenticationProcessor {
                 if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS);
                 clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
                 continue;
+            } else {
+                logger.error("Unknown result status");
+                throw new AuthException(Error.INTERNAL_ERROR);
             }
         }
         return null;
@@ -398,7 +405,7 @@ public class AuthenticationProcessor {
 
     public void validateUser(UserModel authenticatedUser) {
         if (authenticatedUser != null) {
-            if (!clientSession.getAuthenticatedUser().isEnabled()) throw new AuthException(Error.USER_DISABLED);
+            if (!authenticatedUser.isEnabled()) throw new AuthException(Error.USER_DISABLED);
         }
         if (realm.isBruteForceProtected()) {
             if (protector.isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
index f2f2ef2..c678c42 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
@@ -52,6 +52,7 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
             return;
         }
+        context.success();
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
index bcc4c5e..17419a1 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
@@ -53,6 +53,7 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
             return;
         }
+        context.success();
     }
 
     @Override
@@ -62,7 +63,7 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
 
     @Override
     public boolean configuredFor(UserModel user) {
-        return false;
+        return user.configuredForCredentialType(UserCredentialModel.PASSWORD);
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
index 78b9096..56707f9 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
@@ -1,6 +1,7 @@
 package org.keycloak.authentication.authenticators;
 
 import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.AuthenticationProcessor;
 import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorContext;
@@ -51,7 +52,9 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
 
     protected boolean isActionUrl(AuthenticatorContext context) {
         URI expected = LoginActionsService.authenticationFormProcessor(context.getUriInfo()).build(context.getRealm().getName());
-        return expected.getPath().equals(context.getUriInfo().getPath());
+        String current = context.getUriInfo().getAbsolutePath().getPath();
+        String expectedPath = expected.getPath();
+        return expectedPath.equals(current);
 
     }
 
@@ -71,7 +74,11 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
     protected LoginFormsProvider loginForm(AuthenticatorContext context) {
         ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
         code.setAction(ClientSessionModel.Action.AUTHENTICATE);
+        URI action = LoginActionsService.authenticationFormProcessor(context.getUriInfo())
+                .queryParam(OAuth2Constants.CODE, code.getCode())
+                .build(context.getRealm().getName());
         return context.getSession().getProvider(LoginFormsProvider.class)
+                    .setActionUri(action)
                     .setClientSessionCode(code.getCode());
     }
 
@@ -98,6 +105,7 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
         UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
         if (invalidUser(context, user)) return;
         context.setUser(user);
+        context.success();
     }
 
     public boolean invalidUser(AuthenticatorContext context, UserModel user) {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
index 7659c92..d93836f 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
@@ -58,6 +58,7 @@ public class OTPFormAuthenticator implements Authenticator {
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
             return;
         }
+        context.success();
     }
 
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
index 93bf67b..fdb029e 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
@@ -5,12 +5,15 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.ClientConnection;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.authenticators.AuthenticationFlow;
 import org.keycloak.constants.AdapterConstants;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.IdentityProviderModel;
@@ -244,6 +247,32 @@ public class AuthorizationEndpoint {
             return buildRedirectToIdentityProvider(idpHint, accessCode);
         }
 
+        return oldBrowserAuthentication(accessCode);
+    }
+
+    protected Response newBrowserAuthentication(String accessCode) {
+        String flowId = null;
+        for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
+            if (flow.getAlias().equals("browser")) {
+                flowId = flow.getId();
+                break;
+            }
+        }
+        AuthenticationProcessor processor = new AuthenticationProcessor();
+        processor.setClientSession(clientSession)
+                .setFlowId(flowId)
+                .setConnection(clientConnection)
+                .setEventBuilder(event)
+                .setProtector(authManager.getProtector())
+                .setRealm(realm)
+                .setSession(session)
+                .setUriInfo(uriInfo)
+                .setRequest(request);
+
+        return processor.authenticate();
+    }
+
+    protected Response oldBrowserAuthentication(String accessCode) {
         Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
         if (response != null) return response;
 
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 60a49d5..3ada879 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -78,6 +78,10 @@ public class AuthenticationManager {
         this.protector = protector;
     }
 
+    public BruteForceProtector getProtector() {
+        return protector;
+    }
+
     public static boolean isSessionValid(RealmModel realm, UserSessionModel userSession) {
         if (userSession == null) {
             logger.debug("No user session");
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 d1545f2..4329591 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -24,6 +24,7 @@ package org.keycloak.services.resources;
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.ClientConnection;
+import org.keycloak.authentication.AuthenticationProcessor;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
 import org.keycloak.events.Details;
@@ -32,6 +33,7 @@ import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.RoleModel;
@@ -118,7 +120,7 @@ public class LoginActionsService {
     }
 
     public static UriBuilder authenticationFormProcessor(UriInfo uriInfo) {
-        return loginActionsBaseUrl(uriInfo).path("auth-form");
+        return loginActionsBaseUrl(uriInfo).path(LoginActionsService.class, "authForm");
     }
 
     public static UriBuilder loginActionsBaseUrl(UriBuilder baseUriBuilder) {
@@ -270,6 +272,116 @@ public class LoginActionsService {
                 .createRegistration();
     }
 
+        /**
+     * URL called after login page.  YOU SHOULD NEVER INVOKE THIS DIRECTLY!
+     *
+     * @param code
+     * @return
+     */
+    @Path("auth-form")
+    @POST
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public Response authForm(@QueryParam("code") String code) {
+        event.event(EventType.LOGIN);
+        if (!checkSsl()) {
+            event.error(Errors.SSL_REQUIRED);
+            return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
+        }
+
+        if (!realm.isEnabled()) {
+            event.error(Errors.REALM_DISABLED);
+            return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
+        }
+        ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
+        if (clientCode == null) {
+            event.error(Errors.INVALID_CODE);
+            return ErrorPage.error(session, Messages.INVALID_CODE);
+        }
+
+        ClientSessionModel clientSession = clientCode.getClientSession();
+        event.detail(Details.CODE_ID, clientSession.getId());
+
+        if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
+            clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
+            event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
+            return session.getProvider(LoginFormsProvider.class)
+                    .setError(Messages.EXPIRED_CODE)
+                    .setClientSessionCode(clientCode.getCode())
+                    .createLogin();
+        }
+
+        ClientModel client = clientSession.getClient();
+        if (client == null) {
+            event.error(Errors.CLIENT_NOT_FOUND);
+            return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
+        }
+        if (!client.isEnabled()) {
+            event.error(Errors.CLIENT_NOT_FOUND);
+            return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
+        }
+
+        String flowId = null;
+        for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
+            if (flow.getAlias().equals("browser")) {
+                flowId = flow.getId();
+                break;
+            }
+        }
+        AuthenticationProcessor processor = new AuthenticationProcessor();
+        processor.setClientSession(clientSession)
+                .setFlowId(flowId)
+                .setConnection(clientConnection)
+                .setEventBuilder(event)
+                .setProtector(authManager.getProtector())
+                .setRealm(realm)
+                .setSession(session)
+                .setUriInfo(uriInfo)
+                .setRequest(request);
+
+        try {
+            return processor.authenticate();
+        } catch (AuthenticationProcessor.AuthException e) {
+            return handleError(e, code);
+        } catch (Exception e) {
+            logger.error("failed authentication", e);
+            return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
+
+        }
+
+    }
+
+    protected Response handleError(AuthenticationProcessor.AuthException e, String code) {
+        logger.error("failed authentication: " + e.getError().toString(), e);
+        if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
+            event.error(Errors.USER_NOT_FOUND);
+            return session.getProvider(LoginFormsProvider.class)
+                    .setError(Messages.INVALID_USER)
+                    .setClientSessionCode(code)
+                    .createLogin();
+
+        } else if (e.getError() == AuthenticationProcessor.Error.USER_DISABLED) {
+            event.error(Errors.USER_DISABLED);
+            return session.getProvider(LoginFormsProvider.class)
+                    .setError(Messages.ACCOUNT_DISABLED)
+                    .setClientSessionCode(code)
+                    .createLogin();
+
+        } else if (e.getError() == AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED) {
+            event.error(Errors.USER_TEMPORARILY_DISABLED);
+            return session.getProvider(LoginFormsProvider.class)
+                    .setError(Messages.ACCOUNT_TEMPORARILY_DISABLED)
+                    .setClientSessionCode(code)
+                    .createLogin();
+
+        } else {
+            event.error(Errors.INVALID_USER_CREDENTIALS);
+            return session.getProvider(LoginFormsProvider.class)
+                    .setError(Messages.INVALID_USER)
+                    .setClientSessionCode(code)
+                    .createLogin();
+        }
+    }
+
     /**
      * URL called after login page.  YOU SHOULD NEVER INVOKE THIS DIRECTLY!
      *
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index a9b5a54..d2b5ca7 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -2,4 +2,5 @@ org.keycloak.protocol.LoginProtocolSpi
 org.keycloak.protocol.ProtocolMapperSpi
 org.keycloak.exportimport.ClientImportSpi
 org.keycloak.wellknown.WellKnownSpi
-org.keycloak.messages.MessagesSpi
\ No newline at end of file
+org.keycloak.messages.MessagesSpi
+org.keycloak.authentication.AuthenticatorSpi
\ No newline at end of file