diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/DetachedInfoStateChecker.java b/services/src/main/java/org/keycloak/forms/login/freemarker/DetachedInfoStateChecker.java index 9aecd3d48c1d..10d4edaaa170 100644 --- a/services/src/main/java/org/keycloak/forms/login/freemarker/DetachedInfoStateChecker.java +++ b/services/src/main/java/org/keycloak/forms/login/freemarker/DetachedInfoStateChecker.java @@ -82,7 +82,7 @@ public DetachedInfoStateCookie generateAndSetCookie(String messageKey, String me } public DetachedInfoStateCookie verifyStateCheckerParameter(String stateCheckerParam) throws VerificationException { - Set cookieVal = CookieHelper.getCookieValue(session, STATE_CHECKER_COOKIE_NAME); + String cookieVal = CookieHelper.getCookieValue(session, STATE_CHECKER_COOKIE_NAME); if (cookieVal == null || cookieVal.isEmpty()) { throw new VerificationException("State checker cookie is empty"); } @@ -90,8 +90,7 @@ public DetachedInfoStateCookie verifyStateCheckerParameter(String stateCheckerPa throw new VerificationException("State checker parameter is empty"); } - String cookieEncoded = cookieVal.iterator().next(); - DetachedInfoStateCookie cookie = session.tokens().decode(cookieEncoded, DetachedInfoStateCookie.class); + DetachedInfoStateCookie cookie = session.tokens().decode(cookieVal, DetachedInfoStateCookie.class); if (cookie == null) { throw new VerificationException("Failed to verify DetachedInfoStateCookie"); } 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 f9c3fe9a1c2b..74b33fa62a46 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -114,7 +114,6 @@ import static org.keycloak.models.light.LightweightUserAdapter.isLightweightUser; import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID; import static org.keycloak.protocol.oidc.grants.device.DeviceGrantType.isOAuth2DeviceVerificationFlow; -import static org.keycloak.services.util.CookieHelper.getCookie; /** * Stateless object that manages authentication @@ -211,9 +210,8 @@ public static boolean isOfflineSessionValid(RealmModel realm, UserSessionModel u public static boolean expireUserSessionCookie(KeycloakSession session, UserSessionModel userSession, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, ClientConnection connection) { try { // check to see if any identity cookie is set with the same session and expire it if necessary - Cookie cookie = CookieHelper.getCookie(headers.getCookies(), KEYCLOAK_IDENTITY_COOKIE); - if (cookie == null) return true; - String tokenString = cookie.getValue(); + String tokenString = CookieHelper.getCookieValue(session, KEYCLOAK_IDENTITY_COOKIE); + if (tokenString == null) return true; TokenVerifier verifier = TokenVerifier.create(tokenString, AccessToken.class) .realmUrl(Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName())) @@ -777,7 +775,7 @@ public static IdentityCookieToken createIdentityToken(KeycloakSession keycloakSe } public static void createLoginCookie(KeycloakSession keycloakSession, RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, ClientConnection connection) { - String cookiePath = getIdentityCookiePath(realm, uriInfo); + String cookiePath = getRealmCookiePath(realm, uriInfo); String issuer = Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()); IdentityCookieToken identityCookieToken = createIdentityToken(keycloakSession, realm, user, session, issuer); String encoded = keycloakSession.tokens().encode(identityCookieToken); @@ -805,7 +803,7 @@ public static void createRememberMeCookie(String username, UriInfo uriInfo, Keyc KeycloakContext context = session.getContext(); RealmModel realm = context.getRealm(); ClientConnection connection = context.getConnection(); - String path = getIdentityCookiePath(realm, uriInfo); + String path = getRealmCookiePath(realm, uriInfo); boolean secureOnly = realm.getSslRequired().isRequired(connection); // remember me cookie should be persistent (hardcoded to 365 days for now) //NewCookie cookie = new NewCookie(KEYCLOAK_REMEMBER_ME, "true", path, null, null, realm.getCentralLoginLifespan(), secureOnly);// todo httponly , true); @@ -837,39 +835,19 @@ public static String getRememberMeUsername(RealmModel realm, HttpHeaders headers public static void expireIdentityCookie(RealmModel realm, UriInfo uriInfo, KeycloakSession session) { ClientConnection connection = session.getContext().getConnection(); logger.debug("Expiring identity cookie"); - String path = getIdentityCookiePath(realm, uriInfo); + String path = getRealmCookiePath(realm, uriInfo); expireCookie(realm, KEYCLOAK_IDENTITY_COOKIE, path, true, connection, SameSiteAttributeValue.NONE, session); expireCookie(realm, KEYCLOAK_SESSION_COOKIE, path, false, connection, SameSiteAttributeValue.NONE, session); - - String oldPath = getOldCookiePath(realm, uriInfo); - expireCookie(realm, KEYCLOAK_IDENTITY_COOKIE, oldPath, true, connection, SameSiteAttributeValue.NONE, session); - expireCookie(realm, KEYCLOAK_SESSION_COOKIE, oldPath, false, connection, SameSiteAttributeValue.NONE, session); } - public static void expireOldIdentityCookie(RealmModel realm, UriInfo uriInfo, KeycloakSession session) { - ClientConnection connection = session.getContext().getConnection(); - logger.debug("Expiring old identity cookie with wrong path"); - - String oldPath = getOldCookiePath(realm, uriInfo); - expireCookie(realm, KEYCLOAK_IDENTITY_COOKIE, oldPath, true, connection, SameSiteAttributeValue.NONE, session); - expireCookie(realm, KEYCLOAK_SESSION_COOKIE, oldPath, false, connection, SameSiteAttributeValue.NONE, session); - } - public static void expireRememberMeCookie(RealmModel realm, UriInfo uriInfo, KeycloakSession session) { ClientConnection connection = session.getContext().getConnection(); logger.debug("Expiring remember me cookie"); - String path = getIdentityCookiePath(realm, uriInfo); + String path = getRealmCookiePath(realm, uriInfo); String cookieName = KEYCLOAK_REMEMBER_ME; expireCookie(realm, cookieName, path, true, connection, null, session); } - public static void expireOldAuthSessionCookie(RealmModel realm, UriInfo uriInfo, KeycloakSession session) { - logger.debugv("Expire {1} cookie .", AuthenticationSessionManager.AUTH_SESSION_ID); - ClientConnection connection = session.getContext().getConnection(); - String oldPath = getOldCookiePath(realm, uriInfo); - expireCookie(realm, AuthenticationSessionManager.AUTH_SESSION_ID, oldPath, true, connection, SameSiteAttributeValue.NONE, session); - } - public static void expireAuthSessionCookie(RealmModel realm, UriInfo uriInfo, KeycloakSession session) { logger.debugv("Expire {1} cookie .", AuthenticationSessionManager.AUTH_SESSION_ID); ClientConnection connection = session.getContext().getConnection(); @@ -877,26 +855,12 @@ public static void expireAuthSessionCookie(RealmModel realm, UriInfo uriInfo, Ke expireCookie(realm, AuthenticationSessionManager.AUTH_SESSION_ID, oldPath, true, connection, SameSiteAttributeValue.NONE, session); } - protected static String getIdentityCookiePath(RealmModel realm, UriInfo uriInfo) { - return getRealmCookiePath(realm, uriInfo); - } - public static String getRealmCookiePath(RealmModel realm, UriInfo uriInfo) { URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()); // KEYCLOAK-5270 return uri.getRawPath() + "/"; } - public static String getOldCookiePath(RealmModel realm, UriInfo uriInfo) { - URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()); - return uri.getRawPath(); - } - - public static String getAccountCookiePath(RealmModel realm, UriInfo uriInfo) { - URI uri = RealmsResource.accountUrl(uriInfo.getBaseUriBuilder()).build(realm.getName()); - return uri.getRawPath(); - } - public static void expireCookie(RealmModel realm, String cookieName, String path, boolean httpOnly, ClientConnection connection, SameSiteAttributeValue sameSite, KeycloakSession session) { logger.debugf("Expiring cookie: %s path: %s", cookieName, path); boolean secureOnly = realm.getSslRequired().isRequired(connection);; @@ -908,13 +872,12 @@ public AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel } public static AuthResult authenticateIdentityCookie(KeycloakSession session, RealmModel realm, boolean checkActive) { - Cookie cookie = CookieHelper.getCookie(session.getContext().getRequestHeaders().getCookies(), KEYCLOAK_IDENTITY_COOKIE); - if (cookie == null || "".equals(cookie.getValue())) { + String tokenString = CookieHelper.getCookieValue(session, KEYCLOAK_IDENTITY_COOKIE); + if (tokenString == null || tokenString.isEmpty()) { logger.debugv("Could not find cookie: {0}", KEYCLOAK_IDENTITY_COOKIE); return null; } - String tokenString = cookie.getValue(); AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, null, true, tokenString, session.getContext().getRequestHeaders(), VALIDATE_IDENTITY_COOKIE); if (authResult == null) { expireIdentityCookie(realm, session.getContext().getUri(), session); @@ -942,10 +905,10 @@ public static Response redirectAfterSuccessfulFlow(KeycloakSession session, Real ClientSessionContext clientSessionCtx, HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection, EventBuilder event, AuthenticationSessionModel authSession, LoginProtocol protocol) { - Cookie sessionCookie = getCookie(request.getHttpHeaders().getCookies(), AuthenticationManager.KEYCLOAK_SESSION_COOKIE); + String sessionCookie = CookieHelper.getCookieValue(session, AuthenticationManager.KEYCLOAK_SESSION_COOKIE); if (sessionCookie != null) { - String[] split = sessionCookie.getValue().split("/"); + String[] split = sessionCookie.split("/"); if (split.length >= 3) { String oldSessionId = split[2]; if (!oldSessionId.equals(userSession.getId())) { @@ -991,13 +954,13 @@ public static Response redirectAfterSuccessfulFlow(KeycloakSession session, Real } public static String getSessionIdFromSessionCookie(KeycloakSession session) { - Cookie cookie = getCookie(session.getContext().getRequestHeaders().getCookies(), KEYCLOAK_SESSION_COOKIE); - if (cookie == null || "".equals(cookie.getValue())) { + String cookie = CookieHelper.getCookieValue(session, KEYCLOAK_SESSION_COOKIE); + if (cookie == null || cookie.isEmpty()) { logger.debugv("Could not find cookie: {0}", KEYCLOAK_SESSION_COOKIE); return null; } - String[] parts = cookie.getValue().split("/", 3); + String[] parts = cookie.split("/", 3); if (parts.length != 3) { logger.debugv("Cannot parse session value from: {0}", KEYCLOAK_SESSION_COOKIE); return null; diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java index 92b4025d1838..6f4db0e5ca1b 100644 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java @@ -187,12 +187,7 @@ void reencodeAuthSessionCookie(String oldEncodedAuthSessionId, AuthSessionId new * @return list of the values of AUTH_SESSION_ID cookies. It is assumed that values could be encoded with route added (EG. "5e161e00-d426-4ea6-98e9-52eb9844e2d7.node1" ) */ List getAuthSessionCookies(RealmModel realm) { - Set cookiesVal = CookieHelper.getCookieValue(session, AUTH_SESSION_ID); - - if (cookiesVal.size() > 1) { - AuthenticationManager.expireOldAuthSessionCookie(realm, session.getContext().getUri(), session); - } - + Set cookiesVal = CookieHelper.getCookieValues(session, AUTH_SESSION_ID); List authSessionIds = cookiesVal.stream().limit(AUTH_SESSION_COOKIE_LIMIT).collect(Collectors.toList()); if (authSessionIds.isEmpty()) { diff --git a/services/src/main/java/org/keycloak/services/util/CookieHelper.java b/services/src/main/java/org/keycloak/services/util/CookieHelper.java index 00700a71f63f..cc679faf4c00 100755 --- a/services/src/main/java/org/keycloak/services/util/CookieHelper.java +++ b/services/src/main/java/org/keycloak/services/util/CookieHelper.java @@ -91,8 +91,17 @@ public static void addCookie(String name, String value, String path, String doma addCookie(name, value, path, domain, comment, maxAge, secure, httpOnly, null, session); } + public static String getCookieValue(KeycloakSession session, String name) { + Map cookies = session.getContext().getRequestHeaders().getCookies(); + Cookie cookie = cookies.get(name); + if (cookie == null) { + String legacy = name + LEGACY_COOKIE; + cookie = cookies.get(legacy); + } + return cookie != null ? cookie.getValue() : null; + } - public static Set getCookieValue(KeycloakSession session, String name) { + public static Set getCookieValues(KeycloakSession session, String name) { Set ret = getInternalCookieValue(session, name); if (ret.size() == 0) { String legacy = name + LEGACY_COOKIE; @@ -120,8 +129,7 @@ private static Set getInternalCookieValue(KeycloakSession session, Strin return cookiesVal; } - - public static Set parseCookie(String header, String name) { + private static Set parseCookie(String header, String name) { if (header == null || name == null) { return Collections.emptySet(); } @@ -138,15 +146,4 @@ public static Set parseCookie(String header, String name) { return values; } - public static Cookie getCookie(Map cookies, String name) { - Cookie cookie = cookies.get(name); - if (cookie != null) { - return cookie; - } - else { - String legacy = name + LEGACY_COOKIE; - logger.debugv("Could not find cookie {0}, trying {1}", name, legacy); - return cookies.get(legacy); - } - } } diff --git a/services/src/test/java/org/keycloak/utils/CookieHelperTest.java b/services/src/test/java/org/keycloak/utils/CookieHelperTest.java deleted file mode 100644 index d091553bb8ed..000000000000 --- a/services/src/test/java/org/keycloak/utils/CookieHelperTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2020 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.utils; - -import java.util.Set; - -import org.junit.Assert; -import org.junit.Test; -import org.keycloak.services.util.CookieHelper; - -public class CookieHelperTest { - - @Test - public void testParseCookies() { - Set values = CookieHelper.parseCookie( - "terms_user=; KC_RESTART=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhZDUyMjdhMy1iY2ZkLTRjZjAtYTdiNi0zOTk4MzVhMDg1NjYifQ.eyJjaWQiOiJodHRwczovL3Nzby5qYm9zcy5vcmciLCJwdHkiOiJzYW1sIiwicnVyaSI6Imh0dHBzOi8vc3NvLmpib3NzLm9yZy9sb2dpbj9wcm92aWRlcj1SZWRIYXRFeHRlcm5hbFByb3ZpZGVyIiwiYWN0IjoiQVVUSEVOVElDQVRFIiwibm90ZXMiOnsiU0FNTF9SRVFVRVNUX0lEIjoibXBmbXBhYWxkampqa2ZmcG5oYmJoYWdmZmJwam1rbGFqbWVlb2lsaiIsInNhbWxfYmluZGluZyI6InBvc3QifX0.d0QJSOQ6pJGzqcjqDTRwkRpU6fwYeICedL6R9Gqs8CQ; AUTH_SESSION_ID=451ec4be-a0c8-430e-b489-6580f195ccf0; AUTH_SESSION_ID=55000981-8b5e-4c8d-853f-ee4c582c1d0d;AUTH_SESSION_ID=451ec4be-a0c8-430e-b489-6580f195ccf0; AUTH_SESSION_ID=55000981-8b5e-4c8d-853f-ee4c582c1d0d;AUTH_SESSION_ID=451ec4be-a0c8-430e-b489-6580f195ccf0; AUTH_SESSION_ID=55000981-8b5e-4c8d-853f-ee4c582c1d0d4;", "AUTH_SESSION_ID"); - - Assert.assertEquals(3, values.size()); - } -} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java index 336c7dc9113e..415b968c05a1 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java @@ -584,10 +584,7 @@ private AuthDetails repToModel(AuthDetailsRepresentation rep) { @Path("/get-sso-cookie") @Produces(MediaType.APPLICATION_JSON) public String getSSOCookieValue() { - Map cookies = request.getHttpHeaders().getCookies(); - Cookie cookie = CookieHelper.getCookie(cookies, AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE); - if (cookie == null) return null; - return cookie.getValue(); + return CookieHelper.getCookieValue(session, AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java index 27ef4d45b570..4add18f6706e 100755 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java @@ -45,18 +45,23 @@ public class KeycloakTestingClient implements AutoCloseable { if (resteasyClient != null) { client = resteasyClient; } else { - ResteasyClientBuilder resteasyClientBuilder = (ResteasyClientBuilder) ResteasyClientBuilder.newBuilder(); - resteasyClientBuilder.connectionPoolSize(10); - if (serverUrl.startsWith("https")) { - // Disable PKIX path validation errors when running tests using SSL - resteasyClientBuilder.disableTrustManager().hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY); - } - resteasyClientBuilder.httpEngine(AdminClientUtil.getCustomClientHttpEngine(resteasyClientBuilder, 10, null)); + ResteasyClientBuilder resteasyClientBuilder = getRestEasyClientBuilder(serverUrl); client = resteasyClientBuilder.build(); } target = client.target(serverUrl); } + public static ResteasyClientBuilder getRestEasyClientBuilder(String serverUrl) { + ResteasyClientBuilder resteasyClientBuilder = (ResteasyClientBuilder) ResteasyClientBuilder.newBuilder(); + resteasyClientBuilder.connectionPoolSize(10); + if (serverUrl.startsWith("https")) { + // Disable PKIX path validation errors when running tests using SSL + resteasyClientBuilder.disableTrustManager().hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY); + } + resteasyClientBuilder.httpEngine(AdminClientUtil.getCustomClientHttpEngine(resteasyClientBuilder, 10, null)); + return resteasyClientBuilder; + } + public static KeycloakTestingClient getInstance(String serverUrl) { return new KeycloakTestingClient(serverUrl, null); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookieHelperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookieHelperTest.java new file mode 100644 index 000000000000..fbe2a47c134a --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookieHelperTest.java @@ -0,0 +1,95 @@ +package org.keycloak.testsuite.cookies; + +import jakarta.ws.rs.client.ClientRequestContext; +import jakarta.ws.rs.client.ClientRequestFilter; +import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.services.util.CookieHelper; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.client.KeycloakTestingClient; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +public class CookieHelperTest extends AbstractKeycloakTest { + + private KeycloakTestingClient testing; + private SetHeaderFilter filter; + + @Before + public void before() { + filter = new SetHeaderFilter(); + String serverUrl = suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth"; + ResteasyClientBuilder restEasyClientBuilder = KeycloakTestingClient.getRestEasyClientBuilder(serverUrl); + ResteasyClient resteasyClient = restEasyClientBuilder.build(); + resteasyClient.register(filter); + testing = KeycloakTestingClient.getInstance(serverUrl, resteasyClient); + } + + @After + public void after() { + testing.close(); + } + + @Test + public void testCookieHeaderWithSpaces() { + filter.setHeader("Cookie", "terms_user=; KC_RESTART=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhZDUyMjdhMy1iY2ZkLTRjZjAtYTdiNi0zOTk4MzVhMDg1NjYifQ.eyJjaWQiOiJodHRwczovL3Nzby5qYm9zcy5vcmciLCJwdHkiOiJzYW1sIiwicnVyaSI6Imh0dHBzOi8vc3NvLmpib3NzLm9yZy9sb2dpbj9wcm92aWRlcj1SZWRIYXRFeHRlcm5hbFByb3ZpZGVyIiwiYWN0IjoiQVVUSEVOVElDQVRFIiwibm90ZXMiOnsiU0FNTF9SRVFVRVNUX0lEIjoibXBmbXBhYWxkampqa2ZmcG5oYmJoYWdmZmJwam1rbGFqbWVlb2lsaiIsInNhbWxfYmluZGluZyI6InBvc3QifX0.d0QJSOQ6pJGzqcjqDTRwkRpU6fwYeICedL6R9Gqs8CQ; AUTH_SESSION_ID=451ec4be-a0c8-430e-b489-6580f195ccf0; AUTH_SESSION_ID=55000981-8b5e-4c8d-853f-ee4c582c1d0d;AUTH_SESSION_ID=451ec4be-a0c8-430e-b489-6580f195ccf0; AUTH_SESSION_ID=55000981-8b5e-4c8d-853f-ee4c582c1d0d;AUTH_SESSION_ID=451ec4be-a0c8-430e-b489-6580f195ccf0; AUTH_SESSION_ID=55000981-8b5e-4c8d-853f-ee4c582c1d0d4;"); + + testing.server().run(session -> { + Set authSessionIds = CookieHelper.getCookieValues(session, "AUTH_SESSION_ID"); + Assert.assertEquals(3, authSessionIds.size()); + }); + } + + @Test + public void testLegacyCookie() { + filter.setHeader("Cookie", "MYCOOKIE=new;MYCOOKIE_LEGACY=legacy"); + + testing.server().run(session -> { + Assert.assertEquals("new", CookieHelper.getCookieValue(session, "MYCOOKIE")); + + Set cookieValues = CookieHelper.getCookieValues(session, "MYCOOKIE"); + Assert.assertEquals(1, cookieValues.size()); + Assert.assertEquals("new", cookieValues.iterator().next()); + }); + + filter.setHeader("Cookie", "MYCOOKIE_LEGACY=legacy"); + + testing.server().run(session -> { + Assert.assertEquals("legacy", CookieHelper.getCookieValue(session, "MYCOOKIE")); + + Set cookieValues = CookieHelper.getCookieValues(session, "MYCOOKIE"); + Assert.assertEquals(1, cookieValues.size()); + Assert.assertEquals("legacy", cookieValues.iterator().next()); + }); + } + + @Override + public void addTestRealms(List testRealms) { + } + + public static class SetHeaderFilter implements ClientRequestFilter { + + private String key; + private String value; + + public void setHeader(String key, String value) { + this.key = key; + this.value = value; + } + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + if (key != null && value != null) { + requestContext.getHeaders().add(key, value); + } + } + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookiesPathTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookiesPathTest.java index 875e84a3fcf9..09e5a6c9c969 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookiesPathTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookiesPathTest.java @@ -17,33 +17,31 @@ import org.apache.http.protocol.HttpCoreContext; import org.hamcrest.Matchers; import org.jboss.arquillian.graphene.page.Page; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.managers.AuthenticationSessionManager; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.ActionURIUtils; import org.keycloak.testsuite.pages.LoginPage; -import org.keycloak.testsuite.util.ContainerAssume; import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.UserBuilder; import org.openqa.selenium.Cookie; import java.io.IOException; -import java.util.List; import java.util.Arrays; -import java.util.Set; import java.util.Calendar; import java.util.LinkedList; -import java.util.stream.Collectors; +import java.util.List; +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.hamcrest.MatcherAssert.assertThat; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_HOST; -import org.junit.After; -import org.junit.Before; - /** * @author Martin Kanis @@ -52,12 +50,6 @@ public class CookiesPathTest extends AbstractKeycloakTest { @Page protected LoginPage loginPage; - public static final String AUTH_SESSION_VALUE = "1869c345-2f90-4724-936d-a1a1ef41dea7"; - - public static final String AUTH_SESSION_VALUE_NODE = "1869c345-2f90-4724-936d-a1a1ef41dea7.host"; - - public static final String OLD_COOKIE_PATH = "/auth/realms/foo"; - public static final String KC_RESTART = "KC_RESTART"; private CloseableHttpClient httpClient = null; @@ -129,110 +121,6 @@ public void testCookiesPath() { .forEach(cookie -> assertThat(cookie.getPath(), Matchers.endsWith("/auth/realms/foo/"))); } - @Test - public void testMultipleCookies() throws IOException { - setOAuthUri("foo"); - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.DAY_OF_YEAR, 1); - - // create old cookie with wrong path - BasicClientCookie wrongCookie = new BasicClientCookie(AuthenticationSessionManager.AUTH_SESSION_ID, AUTH_SESSION_VALUE); - wrongCookie.setDomain(AUTH_SERVER_HOST); - wrongCookie.setPath(OLD_COOKIE_PATH); - wrongCookie.setExpiryDate(calendar.getTime()); - - // obtain new cookies - CookieStore cookieStore = getCorrectCookies(oauth.getLoginFormUrl()); - cookieStore.addCookie(wrongCookie); - - assertThat(cookieStore.getCookies(), Matchers.hasSize(3)); - - login(oauth.getLoginFormUrl(), cookieStore); - - // old cookie has been removed - // now we have AUTH_SESSION_ID, KEYCLOAK_IDENTITY, KEYCLOAK_SESSION - assertThat(cookieStore.getCookies().stream().map(org.apache.http.cookie.Cookie::getName).collect(Collectors.toList()), - Matchers.hasItems("AUTH_SESSION_ID", "KEYCLOAK_IDENTITY", "KEYCLOAK_SESSION")); - - // does each cookie's path end with "/" - cookieStore.getCookies().stream().filter(c -> !"OAuth_Token_Request_State".equals(c.getName())).map(org.apache.http.cookie.Cookie::getPath).forEach(path -> assertThat(path, Matchers.endsWith("/"))); - - // KEYCLOAK_SESSION should end by AUTH_SESSION_ID value - String authSessionId = cookieStore.getCookies().stream().filter(c -> "AUTH_SESSION_ID".equals(c.getName())).findFirst().get().getValue(); - String KCSessionId = cookieStore.getCookies().stream().filter(c -> "KEYCLOAK_SESSION".equals(c.getName())).findFirst().get().getValue(); - String KCSessionSuffix = KCSessionId.split("/")[2]; - assertThat(authSessionId, Matchers.containsString(KCSessionSuffix)); - } - - @Test - public void testOldCookieWithWrongPath() { - ContainerAssume.assumeAuthServerSSL(); - - Cookie wrongCookie = new Cookie(AuthenticationSessionManager.AUTH_SESSION_ID, AUTH_SESSION_VALUE, - null, OLD_COOKIE_PATH, null, false, true); - - navigateToLoginPage("foo"); - driver.manage().deleteAllCookies(); - - // add old cookie with wrong path - driver.manage().addCookie(wrongCookie); - Set cookies = driver.manage().getCookies(); - assertThat(cookies, Matchers.hasSize(1)); - - driver.navigate().refresh(); - loginPage.login("foo", "password"); - - // old cookie has been removed and new cookies have been added - cookies = driver.manage().getCookies().stream() - .filter(cookie -> KEYCLOAK_COOKIE_NAMES.contains(cookie.getName())) - .collect(Collectors.toSet()); - assertThat(cookies, Matchers.hasSize(3)); - - // does each cookie's path end with "/" - cookies.stream().map(Cookie::getPath).forEach(path -> assertThat(path, Matchers.endsWith("/"))); - - // KEYCLOAK_SESSION should end by AUTH_SESSION_ID value - String authSessionId = cookies.stream().filter(c -> "AUTH_SESSION_ID".equals(c.getName())).findFirst().get().getValue(); - String KCSessionId = cookies.stream().filter(c -> "KEYCLOAK_SESSION".equals(c.getName())).findFirst().get().getValue(); - String KCSessionSuffix = KCSessionId.split("/")[2]; - assertThat(authSessionId, Matchers.containsString(KCSessionSuffix)); - } - - @Test - public void testOldCookieWithNodeInValue() throws IOException { - setOAuthUri("foo"); - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.DAY_OF_YEAR, 1); - - // create old cookie with wrong path - BasicClientCookie wrongCookie = new BasicClientCookie(AuthenticationSessionManager.AUTH_SESSION_ID, AUTH_SESSION_VALUE_NODE); - wrongCookie.setDomain(AUTH_SERVER_HOST); - wrongCookie.setPath(OLD_COOKIE_PATH); - wrongCookie.setExpiryDate(calendar.getTime()); - - // obtain new cookies - CookieStore cookieStore = getCorrectCookies(oauth.getLoginFormUrl()); - cookieStore.addCookie(wrongCookie); - - assertThat(cookieStore.getCookies(), Matchers.hasSize(3)); - - login(oauth.getLoginFormUrl(), cookieStore); - - // old cookie has been removed - // now we have AUTH_SESSION_ID, KEYCLOAK_IDENTITY, KEYCLOAK_SESSION, OAuth_Token_Request_State - assertThat(cookieStore.getCookies().stream().map(org.apache.http.cookie.Cookie::getName).collect(Collectors.toList()), - Matchers.hasItems("AUTH_SESSION_ID", "KEYCLOAK_IDENTITY", "KEYCLOAK_SESSION")); - - // does each cookie's path end with "/" - cookieStore.getCookies().stream().filter(c -> !"OAuth_Token_Request_State".equals(c.getName())).map(org.apache.http.cookie.Cookie::getPath).forEach(path -> assertThat(path, Matchers.endsWith("/"))); - - // KEYCLOAK_SESSION should end by AUTH_SESSION_ID value - String authSessionId = cookieStore.getCookies().stream().filter(c -> "AUTH_SESSION_ID".equals(c.getName())).findFirst().get().getValue(); - String KCSessionId = cookieStore.getCookies().stream().filter(c -> "KEYCLOAK_SESSION".equals(c.getName())).findFirst().get().getValue(); - String KCSessionSuffix = KCSessionId.split("/")[2]; - assertThat(authSessionId, Matchers.containsString(KCSessionSuffix)); - } - /** * Add two realms which names are overlapping i.e foo and foobar * @param testRealms