From cbc383d4949977fdfe386ba7ab80167df9f0ce3d Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Wed, 1 Oct 2014 14:19:59 -0400 Subject: [PATCH] finish protocol refactoring --- .../protocol/oidc/OpenIDConnectService.java | 116 ++++++------------ .../services/resources/AccountService.java | 2 +- .../resources/LoginActionsService.java | 74 ++++++++++- .../services/resources/flows/Urls.java | 10 +- .../testsuite/account/AccountTest.java | 13 +- 5 files changed, 119 insertions(+), 96 deletions(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java index 674f59427a..71d7b199d3 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java @@ -631,7 +631,6 @@ public class OpenIDConnectService { * */ private class FrontPageInitializer { - protected String code; protected String clientId; protected String redirect; protected String state; @@ -642,11 +641,7 @@ public class OpenIDConnectService { protected ClientSessionModel clientSession; public Response processInput() { - if (code != null) { - event.detail(Details.CODE_ID, code); - } else { - event.client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code"); - } + event.client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code"); if (!checkSsl()) { event.error(Errors.SSL_REQUIRED); return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required"); @@ -657,65 +652,43 @@ public class OpenIDConnectService { } clientSession = null; - if (code != null) { - ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm); - if (clientCode == null) { - event.error(Errors.INVALID_CODE); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application."); - } - if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) { - event.error(Errors.INVALID_CODE); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application."); - } - clientSession = clientCode.getClientSession(); - if (!clientSession.getAuthMethod().equals(OpenIDConnect.LOGIN_PROTOCOL)) { - event.error(Errors.INVALID_CODE); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid protocol, please login again through your application."); - } - state = clientSession.getNote(OpenIDConnect.STATE_PARAM); - scopeParam = clientSession.getNote(OpenIDConnect.SCOPE_PARAM); - responseType = clientSession.getNote(OpenIDConnect.RESPONSE_TYPE_PARAM); - loginHint = clientSession.getNote(OpenIDConnect.LOGIN_HINT_PARAM); - prompt = clientSession.getNote(OpenIDConnect.PROMPT_PARAM); - } else { - if (state == null) { - event.error(Errors.STATE_PARAM_NOT_FOUND); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid state param."); + if (state == null) { + event.error(Errors.STATE_PARAM_NOT_FOUND); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid state param."); - } - ClientModel client = realm.findClient(clientId); - if (client == null) { - event.error(Errors.CLIENT_NOT_FOUND); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester."); - } - - if (!client.isEnabled()) { - event.error(Errors.CLIENT_DISABLED); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled."); - } - if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) { - event.error(Errors.NOT_ALLOWED); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login"); - } - if (client.isDirectGrantsOnly()) { - event.error(Errors.NOT_ALLOWED); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login"); - } - redirect = verifyRedirectUri(uriInfo, redirect, realm, client); - if (redirect == null) { - event.error(Errors.INVALID_REDIRECT_URI); - return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri."); - } - clientSession = session.sessions().createClientSession(realm, client); - clientSession.setAuthMethod(OpenIDConnect.LOGIN_PROTOCOL); - clientSession.setRedirectUri(redirect); - clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE); - clientSession.setNote(OpenIDConnect.STATE_PARAM, state); - if (scopeParam != null) clientSession.setNote(OpenIDConnect.SCOPE_PARAM, scopeParam); - if (responseType != null) clientSession.setNote(OpenIDConnect.RESPONSE_TYPE_PARAM, responseType); - if (loginHint != null) clientSession.setNote(OpenIDConnect.LOGIN_HINT_PARAM, loginHint); - if (prompt != null) clientSession.setNote(OpenIDConnect.PROMPT_PARAM, prompt); } + ClientModel client = realm.findClient(clientId); + if (client == null) { + event.error(Errors.CLIENT_NOT_FOUND); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester."); + } + + if (!client.isEnabled()) { + event.error(Errors.CLIENT_DISABLED); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled."); + } + if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) { + event.error(Errors.NOT_ALLOWED); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login"); + } + if (client.isDirectGrantsOnly()) { + event.error(Errors.NOT_ALLOWED); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login"); + } + redirect = verifyRedirectUri(uriInfo, redirect, realm, client); + if (redirect == null) { + event.error(Errors.INVALID_REDIRECT_URI); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri."); + } + clientSession = session.sessions().createClientSession(realm, client); + clientSession.setAuthMethod(OpenIDConnect.LOGIN_PROTOCOL); + clientSession.setRedirectUri(redirect); + clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE); + clientSession.setNote(OpenIDConnect.STATE_PARAM, state); + if (scopeParam != null) clientSession.setNote(OpenIDConnect.SCOPE_PARAM, scopeParam); + if (responseType != null) clientSession.setNote(OpenIDConnect.RESPONSE_TYPE_PARAM, responseType); + if (loginHint != null) clientSession.setNote(OpenIDConnect.LOGIN_HINT_PARAM, loginHint); + if (prompt != null) clientSession.setNote(OpenIDConnect.PROMPT_PARAM, prompt); return null; } } @@ -726,7 +699,6 @@ public class OpenIDConnectService { * @See http://tools.ietf.org/html/rfc6749#section-4.1 * * - * @param code * @param responseType * @param redirect * @param clientId @@ -737,8 +709,7 @@ public class OpenIDConnectService { */ @Path("login") @GET - public Response loginPage(@QueryParam("code") String code, - @QueryParam(OpenIDConnect.RESPONSE_TYPE_PARAM) String responseType, + public Response loginPage(@QueryParam(OpenIDConnect.RESPONSE_TYPE_PARAM) String responseType, @QueryParam(OpenIDConnect.REDIRECT_URI_PARAM) String redirect, @QueryParam(OpenIDConnect.CLIENT_ID_PARAM) String clientId, @QueryParam(OpenIDConnect.SCOPE_PARAM) String scopeParam, @@ -747,7 +718,6 @@ public class OpenIDConnectService { @QueryParam(OpenIDConnect.LOGIN_HINT_PARAM) String loginHint) { event.event(EventType.LOGIN); FrontPageInitializer pageInitializer = new FrontPageInitializer(); - pageInitializer.code = code; pageInitializer.responseType = responseType; pageInitializer.redirect = redirect; pageInitializer.clientId = clientId; @@ -758,14 +728,6 @@ public class OpenIDConnectService { Response response = pageInitializer.processInput(); if (response != null) return response; ClientSessionModel clientSession = pageInitializer.clientSession; - code = pageInitializer.code; - responseType = pageInitializer.responseType; - redirect = pageInitializer.redirect; - clientId = pageInitializer.clientId ; - scopeParam = pageInitializer.scopeParam; - state = pageInitializer.state; - prompt = pageInitializer.prompt; - loginHint = pageInitializer.loginHint; @@ -822,8 +784,7 @@ public class OpenIDConnectService { */ @Path("registrations") @GET - public Response registerPage(@QueryParam("code") String code, - @QueryParam(OpenIDConnect.RESPONSE_TYPE_PARAM) String responseType, + public Response registerPage(@QueryParam(OpenIDConnect.RESPONSE_TYPE_PARAM) String responseType, @QueryParam(OpenIDConnect.REDIRECT_URI_PARAM) String redirect, @QueryParam(OpenIDConnect.CLIENT_ID_PARAM) String clientId, @QueryParam(OpenIDConnect.SCOPE_PARAM) String scopeParam, @@ -835,7 +796,6 @@ public class OpenIDConnectService { } FrontPageInitializer pageInitializer = new FrontPageInitializer(); - pageInitializer.code = code; pageInitializer.responseType = responseType; pageInitializer.redirect = redirect; pageInitializer.clientId = clientId; diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index 99550a3b6c..3b3e3cfd80 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -742,7 +742,7 @@ public class AccountService { private Response login(String path) { OAuthRedirect oauth = new OAuthRedirect(); - String authUrl = Urls.realmLoginPage(uriInfo.getBaseUri(), realm.getName()).toString(); + String authUrl = OpenIDConnectService.loginPageUrl(uriInfo).build(realm.getName()).toString(); oauth.setAuthUrl(authUrl); oauth.setClientId(Constants.ACCOUNT_MANAGEMENT_APP); 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 3a0e6d6df5..c6bcc8cd51 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -22,6 +22,7 @@ package org.keycloak.services.resources; import org.jboss.logging.Logger; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.ClientConnection; import org.keycloak.email.EmailException; @@ -44,6 +45,7 @@ import org.keycloak.models.UserSessionModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.protocol.LoginProtocol; +import org.keycloak.protocol.oidc.OpenIDConnect; import org.keycloak.protocol.oidc.OpenIDConnectService; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.representations.PasswordToken; @@ -61,6 +63,7 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; +import javax.ws.rs.core.Cookie; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; @@ -153,6 +156,15 @@ public class LoginActionsService { Response response; boolean check(String code, ClientSessionModel.Action requiredAction) { + if (!check(code)) return false; + if (!clientCode.isValid(requiredAction)) { + event.error(Errors.INVALID_CODE); + response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application."); + } + return true; + } + + public boolean check(String code) { if (!checkSsl()) { event.error(Errors.SSL_REQUIRED); response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required"); @@ -169,14 +181,68 @@ public class LoginActionsService { response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application."); return false; } - if (!clientCode.isValid(requiredAction)) { - event.error(Errors.INVALID_CODE); - response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application."); - } return true; } } + /** + * protocol independent login page entry point + * + * + * @param code + * @return + */ + @Path("login") + @GET + public Response loginPage(@QueryParam("code") String code) { + event.event(EventType.LOGIN); + Checks checks = new Checks(); + if (!checks.check(code)) { + return checks.response; + } + event.detail(Details.CODE_ID, code); + ClientSessionCode clientSessionCode = checks.clientCode; + ClientSessionModel clientSession = clientSessionCode.getClientSession(); + + + + LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo) + .setClientSessionCode(clientSessionCode.getCode()); + + return forms.createLogin(); + } + + /** + * protocol independent registration page entry point + * + * @param code + * @return + */ + @Path("registration") + @GET + public Response registerPage(@QueryParam("code") String code) { + event.event(EventType.REGISTER); + if (!realm.isRegistrationAllowed()) { + event.error(Errors.REGISTRATION_DISABLED); + return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed"); + } + + Checks checks = new Checks(); + if (!checks.check(code)) { + return checks.response; + } + event.detail(Details.CODE_ID, code); + ClientSessionCode clientSessionCode = checks.clientCode; + ClientSessionModel clientSession = clientSessionCode.getClientSession(); + + + authManager.expireIdentityCookie(realm, uriInfo, clientConnection); + + return Flows.forms(session, realm, clientSession.getClient(), uriInfo) + .setClientSessionCode(clientSessionCode.getCode()) + .createRegistration(); + } + /** * URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY! * diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java index af0efa4e64..2e4148e27f 100755 --- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java @@ -138,10 +138,10 @@ public class Urls { } public static URI realmLoginPage(URI baseUri, String realmId) { - return tokenBase(baseUri).path(OpenIDConnectService.class, "loginPage").build(realmId); + return requiredActionsBase(baseUri).path(LoginActionsService.class, "loginPage").build(realmId); } - public static UriBuilder realmLogout(URI baseUri) { + private static UriBuilder realmLogout(URI baseUri) { return tokenBase(baseUri).path(OpenIDConnectService.class, "logout"); } @@ -150,7 +150,7 @@ public class Urls { } public static URI realmRegisterPage(URI baseUri, String realmId) { - return tokenBase(baseUri).path(OpenIDConnectService.class, "registerPage").build(realmId); + return requiredActionsBase(baseUri).path(LoginActionsService.class, "registerPage").build(realmId); } public static URI realmInstalledAppUrnCallback(URI baseUri, String realmId) { @@ -161,10 +161,6 @@ public class Urls { return requiredActionsBase(baseUri).path(LoginActionsService.class, "processConsent").build(realmId); } - public static URI realmCode(URI baseUri, String realmId) { - return tokenBase(baseUri).path(OpenIDConnectService.class, "accessCodeToToken").build(realmId); - } - public static UriBuilder socialBase(URI baseUri) { return UriBuilder.fromUri(baseUri).path(SocialResource.class); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 8ee9330898..4fe0131178 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -25,6 +25,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.keycloak.events.Details; @@ -156,12 +157,12 @@ public class AccountTest { }); } -// @Test -// @Ignore -// public void runit() throws Exception { -// Thread.sleep(10000000); -// -// } + @Test + @Ignore + public void runit() throws Exception { + Thread.sleep(10000000); + + }