diff --git a/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java b/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java index 6266e2ee75..342bb2ab6a 100755 --- a/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java +++ b/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java @@ -173,10 +173,15 @@ public class KeycloakServletExtension implements ServletExtension { } }); - log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath()); - ServletSessionConfig cookieConfig = new ServletSessionConfig(); - cookieConfig.setPath(deploymentInfo.getContextPath()); - deploymentInfo.setServletSessionConfig(cookieConfig); + ServletSessionConfig cookieConfig = deploymentInfo.getServletSessionConfig(); + if (cookieConfig == null) { + cookieConfig = new ServletSessionConfig(); + } + if (cookieConfig.getPath() == null) { + log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath()); + cookieConfig.setPath(deploymentInfo.getContextPath()); + deploymentInfo.setServletSessionConfig(cookieConfig); + } ChangeSessionId.turnOffChangeSessionIdOnLogin(deploymentInfo); deploymentInfo.addListener(new ListenerInfo(UndertowNodesRegistrationManagementWrapper.class, new InstanceFactory() { diff --git a/adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java b/adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java index 9d2902e671..0e6a1a1f80 100755 --- a/adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java +++ b/adapters/saml/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java @@ -182,10 +182,15 @@ public class SamlServletExtension implements ServletExtension { } }); - log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath()); - ServletSessionConfig cookieConfig = new ServletSessionConfig(); - cookieConfig.setPath(deploymentInfo.getContextPath()); - deploymentInfo.setServletSessionConfig(cookieConfig); + ServletSessionConfig cookieConfig = deploymentInfo.getServletSessionConfig(); + if (cookieConfig == null) { + cookieConfig = new ServletSessionConfig(); + } + if (cookieConfig.getPath() == null) { + log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath()); + cookieConfig.setPath(deploymentInfo.getContextPath()); + deploymentInfo.setServletSessionConfig(cookieConfig); + } addEndpointConstraint(deploymentInfo); ChangeSessionId.turnOffChangeSessionIdOnLogin(deploymentInfo); diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SecurePortalWithCustomSessionConfig.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SecurePortalWithCustomSessionConfig.java new file mode 100644 index 0000000000..47b51ddd13 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SecurePortalWithCustomSessionConfig.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016 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.testsuite.adapter.page; + +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl; + +import java.net.URL; + +/** + * + * @author tkyjovsk + */ +public class SecurePortalWithCustomSessionConfig extends AbstractPageWithInjectedUrl { + + public static final String DEPLOYMENT_NAME = "secure-portal-with-custom-session-config"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + return url; + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java index fde26a1514..cf50cce83f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java @@ -24,6 +24,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; + import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.common.Version; @@ -38,14 +39,7 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest; import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter; -import org.keycloak.testsuite.adapter.page.BasicAuth; -import org.keycloak.testsuite.adapter.page.CustomerDb; -import org.keycloak.testsuite.adapter.page.CustomerDbErrorPage; -import org.keycloak.testsuite.adapter.page.CustomerPortal; -import org.keycloak.testsuite.adapter.page.InputPortal; -import org.keycloak.testsuite.adapter.page.ProductPortal; -import org.keycloak.testsuite.adapter.page.SecurePortal; -import org.keycloak.testsuite.adapter.page.TokenMinTTLPage; +import org.keycloak.testsuite.adapter.page.*; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.auth.page.account.Applications; import org.keycloak.testsuite.auth.page.login.OAuthGrant; @@ -53,6 +47,7 @@ import org.keycloak.testsuite.console.page.events.Config; import org.keycloak.testsuite.console.page.events.LoginEvents; import org.keycloak.testsuite.util.URLUtils; import org.keycloak.util.BasicAuthHelper; + import org.openqa.selenium.By; import org.openqa.selenium.WebElement; @@ -73,12 +68,13 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import org.keycloak.testsuite.adapter.page.CustomerPortalNoConf; +import static org.junit.Assert.*; + +import org.keycloak.testsuite.util.Matchers; + +import javax.ws.rs.core.Response.Status; + +import static org.hamcrest.Matchers.*; import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf; @@ -98,6 +94,8 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd @Page private SecurePortal securePortal; @Page + private SecurePortalWithCustomSessionConfig securePortalWithCustomSessionConfig; + @Page private CustomerDb customerDb; @Page private CustomerDbErrorPage customerDbErrorPage; @@ -133,6 +131,11 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd return servletDeployment(SecurePortal.DEPLOYMENT_NAME, CallAuthenticatedServlet.class); } + @Deployment(name = SecurePortalWithCustomSessionConfig.DEPLOYMENT_NAME) + protected static WebArchive securePortalWithCustomSessionConfig() { + return servletDeployment(SecurePortalWithCustomSessionConfig.DEPLOYMENT_NAME, CallAuthenticatedServlet.class); + } + @Deployment(name = CustomerDb.DEPLOYMENT_NAME) protected static WebArchive customerDb() { return servletDeployment(CustomerDb.DEPLOYMENT_NAME, AdapterActionsFilter.class, CustomerDatabaseServlet.class); @@ -479,6 +482,27 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); } + @Test + public void testAuthenticatedWithCustomSessionConfig() { + // test login to customer-portal which does a bearer request to customer-db + securePortalWithCustomSessionConfig.navigateTo(); + assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); + testRealmLoginPage.form().login("bburke@redhat.com", "password"); + assertCurrentUrlEquals(securePortalWithCustomSessionConfig); + + assertThat("Cookie CUSTOM_JSESSION_ID_NAME should exist", driver.manage().getCookieNamed("CUSTOM_JSESSION_ID_NAME"), notNullValue()); + + String pageSource = driver.getPageSource(); + assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); + // test logout + String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()) + .queryParam(OAuth2Constants.REDIRECT_URI, securePortalWithCustomSessionConfig.toString()).build("demo").toString(); + driver.navigate().to(logoutUri); + assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); + securePortalWithCustomSessionConfig.navigateTo(); + assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); + } + // Tests "token-minimum-time-to-live" adapter configuration option @Test public void testTokenMinTTL() { @@ -549,23 +573,19 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd Response response = client.target(basicAuthPage .setTemplateValues("mposolda", "password", value).buildUri()).request().get(); - assertEquals(200, response.getStatus()); + assertThat(response, Matchers.statusCodeIs(Status.OK)); assertEquals(value, response.readEntity(String.class)); response.close(); response = client.target(basicAuthPage .setTemplateValues("invalid-user", "password", value).buildUri()).request().get(); - assertEquals(401, response.getStatus()); - String readResponse = response.readEntity(String.class); - assertTrue(readResponse.contains("Unauthorized") || readResponse.contains("Status 401")); - response.close(); + assertThat(response, Matchers.statusCodeIs(Status.UNAUTHORIZED)); + assertThat(response, Matchers.body(anyOf(containsString("Unauthorized"), containsString("Status 401")))); response = client.target(basicAuthPage .setTemplateValues("admin", "invalid-password", value).buildUri()).request().get(); - assertEquals(401, response.getStatus()); - readResponse = response.readEntity(String.class); - assertTrue(readResponse.contains("Unauthorized") || readResponse.contains("Status 401")); - response.close(); + assertThat(response, Matchers.statusCodeIs(Status.UNAUTHORIZED)); + assertThat(response, Matchers.body(anyOf(containsString("Unauthorized"), containsString("Status 401")))); client.close(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UndertowDemoFilterServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UndertowDemoFilterServletAdapterTest.java index e910a2ac9c..25ba365379 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UndertowDemoFilterServletAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UndertowDemoFilterServletAdapterTest.java @@ -20,9 +20,16 @@ package org.keycloak.testsuite.adapter.undertow.servlet; import org.keycloak.testsuite.adapter.servlet.AbstractDemoFilterServletAdapterTest; import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; +import org.junit.Ignore; + /** * @author Marek Posolda */ @AppServerContainer("auth-server-undertow") public class UndertowDemoFilterServletAdapterTest extends AbstractDemoFilterServletAdapterTest { + @Ignore + @Override + public void testAuthenticatedWithCustomSessionConfig() { + // Undertow deployment ignores session cookie settings in web.xml, see org.keycloak.testsuite.arquillian.undertow.SimpleWebXmlParser class + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UndertowDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UndertowDemoServletsAdapterTest.java index c1be0ca889..5849a118bb 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UndertowDemoServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UndertowDemoServletsAdapterTest.java @@ -20,9 +20,17 @@ package org.keycloak.testsuite.adapter.undertow.servlet; import org.keycloak.testsuite.adapter.servlet.AbstractDemoServletsAdapterTest; import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; +import org.junit.Ignore; + /** * @author Marek Posolda */ @AppServerContainer("auth-server-undertow") public class UndertowDemoServletsAdapterTest extends AbstractDemoServletsAdapterTest { + + @Ignore + @Override + public void testAuthenticatedWithCustomSessionConfig() { + // Undertow deployment ignores session cookie settings in web.xml, see org.keycloak.testsuite.arquillian.undertow.SimpleWebXmlParser class + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json index 072ca402bb..a0468278df 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json @@ -214,6 +214,19 @@ "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg==" } }, + { + "clientId": "secure-portal-with-custom-session-config", + "enabled": true, + "adminUrl": "/secure-portal-with-custom-session-config", + "baseUrl": "/secure-portal-with-custom-session-config", + "clientAuthenticatorType": "client-jwt", + "redirectUris": [ + "/secure-portal-with-custom-session-config/*" + ], + "attributes" : { + "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg==" + } + }, { "clientId": "session-portal", "enabled": true, diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/META-INF/context.xml new file mode 100644 index 0000000000..b4ddcce386 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/META-INF/context.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/jetty-web.xml new file mode 100644 index 0000000000..8c59313878 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/jetty-web.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/keycloak.json new file mode 100644 index 0000000000..443a08cfdf --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/keycloak.json @@ -0,0 +1,16 @@ +{ + "realm": "demo", + "auth-server-url": "http://localhost:8180/auth", + "ssl-required": "external", + "resource": "secure-portal-with-custom-session-config", + "credentials": { + "jwt": { + "client-key-password": "password", + "client-keystore-file": "classpath:keystore.jks", + "client-keystore-password": "password", + "client-key-alias": "secure-portal", + "token-timeout": 10, + "client-keystore-type": "jks" + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/keystore.jks new file mode 100644 index 0000000000..399be7a48a Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/keystore.jks differ diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/web.xml new file mode 100644 index 0000000000..bfcc1db59b --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal-with-custom-session-config/WEB-INF/web.xml @@ -0,0 +1,64 @@ + + + + + + secure-portal-with-custom-session-config + + + Servlet + org.keycloak.testsuite.adapter.servlet.CallAuthenticatedServlet + + + + Servlet + /* + + + + + Permit all + /* + + + * + + + + + + true + CUSTOM_JSESSION_ID_NAME + + + + + KEYCLOAK + demo + + + + admin + + + user + +