keycloak-aplcache
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java 10(+10 -0)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java 3(+3 -0)
adapters/oidc/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/JettyCookieTokenStore.java 4(+2 -2)
adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java 4(+2 -2)
adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java 4(+2 -2)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java 7(+7 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties 2(+2 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml 2(+2 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java 7(+2 -5)
testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java 4(+4 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerCookiePortalRoot.java 57(+57 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/CookieStoreRootContextTest.java 139(+139 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/DemoServletsAdapterTest.java 10(+5 -5)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/META-INF/context.xml 20(+20 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/jetty-web.xml 46(+46 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/keycloak.json 13(+13 -0)
Details
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
index 8192e71..1584c11 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java
@@ -308,6 +308,16 @@ public class AdapterDeploymentContext {
}
@Override
+ public String getAdapterStateCookiePath() {
+ return delegate.getAdapterStateCookiePath();
+ }
+
+ @Override
+ public void setAdapterStateCookiePath(String adapterStateCookiePath) {
+ delegate.setAdapterStateCookiePath(adapterStateCookiePath);
+ }
+
+ @Override
public String getStateCookieName() {
return delegate.getStateCookieName();
}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
index 1665c92..c13cbfa 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java
@@ -47,7 +47,7 @@ public class CookieTokenStore {
.append(idToken).append(DELIM)
.append(refreshToken).toString();
- String cookiePath = getContextPath(facade);
+ String cookiePath = getCookiePath(deployment, facade);
facade.getResponse().setCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE, cookie, cookiePath, null, -1, deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr()), true);
}
@@ -98,14 +98,29 @@ public class CookieTokenStore {
}
}
- public static void removeCookie(HttpFacade facade) {
- String cookiePath = getContextPath(facade);
+ public static void removeCookie(KeycloakDeployment deployment, HttpFacade facade) {
+ String cookiePath = getCookiePath(deployment, facade);
facade.getResponse().resetCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE, cookiePath);
}
- private static String getContextPath(HttpFacade facade) {
+ static String getCookiePath(KeycloakDeployment deployment, HttpFacade facade) {
+ if (deployment.getAdapterStateCookiePath().startsWith("/")) {
+ return deployment.getAdapterStateCookiePath();
+ }
+ String contextPath = getContextPath(facade);
+ StringBuilder cookiePath = new StringBuilder(contextPath);
+ if (!contextPath.endsWith("/") && !deployment.getAdapterStateCookiePath().isEmpty()) {
+ cookiePath.append("/");
+ }
+ return cookiePath.append(deployment.getAdapterStateCookiePath()).toString();
+ }
+
+ static String getContextPath(HttpFacade facade) {
String uri = facade.getRequest().getURI();
String path = KeycloakUriBuilder.fromUri(uri).getPath();
+ if (path == null || path.isEmpty()) {
+ return "/";
+ }
int index = path.indexOf("/", 1);
return index == -1 ? path : path.substring(0, index);
}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
index 89f38cc..b2d1874 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
@@ -70,6 +70,7 @@ public class KeycloakDeployment {
protected SslRequired sslRequired = SslRequired.ALL;
protected int confidentialPort = -1;
protected TokenStore tokenStore = TokenStore.SESSION;
+ protected String adapterStateCookiePath = "";
protected String stateCookieName = "OAuth_Token_Request_State";
protected boolean useResourceRoleMappings;
protected boolean cors;
@@ -297,6 +298,14 @@ public class KeycloakDeployment {
this.tokenStore = tokenStore;
}
+ public String getAdapterStateCookiePath() {
+ return adapterStateCookiePath;
+ }
+
+ public void setAdapterStateCookiePath(String adapterStateCookiePath) {
+ this.adapterStateCookiePath = adapterStateCookiePath;
+ }
+
public String getStateCookieName() {
return stateCookieName;
}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
index 936c065..c05bcb6 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
@@ -88,6 +88,9 @@ public class KeycloakDeploymentBuilder {
} else {
deployment.setTokenStore(TokenStore.SESSION);
}
+ if (adapterConfig.getTokenCookiePath() != null) {
+ deployment.setAdapterStateCookiePath(adapterConfig.getTokenCookiePath());
+ }
if (adapterConfig.getPrincipalAttribute() != null) deployment.setPrincipalAttribute(adapterConfig.getPrincipalAttribute());
deployment.setResourceCredentials(adapterConfig.getCredentials());
diff --git a/adapters/oidc/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/JettyCookieTokenStore.java b/adapters/oidc/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/JettyCookieTokenStore.java
index 2aa973d..3222998 100755
--- a/adapters/oidc/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/JettyCookieTokenStore.java
+++ b/adapters/oidc/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/JettyCookieTokenStore.java
@@ -85,7 +85,7 @@ public class JettyCookieTokenStore implements AdapterTokenStore {
@Override
public void logout() {
- CookieTokenStore.removeCookie(facade);
+ CookieTokenStore.removeCookie(deployment, facade);
}
@@ -113,7 +113,7 @@ public class JettyCookieTokenStore implements AdapterTokenStore {
if (success && session.isActive()) return principal;
log.debugf("Cleanup and expire cookie for user %s after failed refresh", principal.getName());
- CookieTokenStore.removeCookie(facade);
+ CookieTokenStore.removeCookie(deployment, facade);
return null;
}
diff --git a/adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java b/adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java
index d2b6474..768a4dd 100755
--- a/adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java
+++ b/adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/CatalinaCookieTokenStore.java
@@ -93,7 +93,7 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
@Override
public void logout() {
- CookieTokenStore.removeCookie(facade);
+ CookieTokenStore.removeCookie(deployment, facade);
}
@Override
@@ -132,7 +132,7 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
log.fine("Cleanup and expire cookie for user " + principal.getName() + " after failed refresh");
request.setUserPrincipal(null);
request.setAuthType(null);
- CookieTokenStore.removeCookie(facade);
+ CookieTokenStore.removeCookie(deployment, facade);
return null;
}
}
diff --git a/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java b/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java
index d3556c8..a5287d5 100755
--- a/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java
+++ b/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowCookieTokenStore.java
@@ -74,7 +74,7 @@ public class UndertowCookieTokenStore implements AdapterTokenStore {
return true;
} else {
log.debug("Account was not active, removing cookie and returning false");
- CookieTokenStore.removeCookie(facade);
+ CookieTokenStore.removeCookie(deployment, facade);
return false;
}
}
@@ -90,7 +90,7 @@ public class UndertowCookieTokenStore implements AdapterTokenStore {
KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = CookieTokenStore.getPrincipalFromCookie(deployment, facade, this);
if (principal == null) return;
- CookieTokenStore.removeCookie(facade);
+ CookieTokenStore.removeCookie(deployment, facade);
}
@Override
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java
index bcfd399..55295c6 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java
@@ -101,6 +101,12 @@ abstract class AbstractAdapterConfigurationDefinition extends SimpleResourceDefi
.setAllowExpression(true)
.setValidator(new IntRangeValidator(-1, true))
.build();
+ protected static final SimpleAttributeDefinition COOKIE_PATH =
+ new SimpleAttributeDefinitionBuilder("token-cookie-path", ModelType.STRING, true)
+ .setXmlName("token-cookie-path")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+ .build();
static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@@ -115,6 +121,7 @@ abstract class AbstractAdapterConfigurationDefinition extends SimpleResourceDefi
DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_KEY_CACHE_TTL);
+ DEPLOYMENT_ONLY_ATTRIBUTES.add(COOKIE_PATH);
}
static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList();
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
index 8678ae5..4bff019 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
@@ -98,6 +98,7 @@ keycloak.secure-deployment.public-key-cache-ttl=Maximum time the downloaded publ
keycloak.secure-deployment.ignore-oauth-query-parameter=disable query parameter parsing for access_token
keycloak.secure-deployment.proxy-url=The URL for the HTTP proxy if one is used.
keycloak.secure-deployment.verify-token-audience=If true, then during bearer-only authentication, the adapter will verify if token contains this client name (resource) as an audience
+keycloak.secure-deployment.token-cookie-path=If set, defines the path used in cookies set by the adapter. Useful when deploying the application in the root context path.
keycloak.secure-server=A deployment secured by Keycloak
keycloak.secure-server.add=Add a deployment to be secured by Keycloak
@@ -142,6 +143,7 @@ keycloak.secure-server.public-key-cache-ttl=Maximum time the downloaded public k
keycloak.secure-server.ignore-oauth-query-parameter=disable query parameter parsing for access_token
keycloak.secure-server.proxy-url=The URL for the HTTP proxy if one is used.
keycloak.secure-server.verify-token-audience=If true, then during bearer-only authentication, the adapter will verify if token contains this client name (resource) as an audience
+keycloak.secure-server.token-cookie-path=If set, defines the path used in cookies set by the adapter. Useful when deploying the application in the root context path.
keycloak.secure-deployment.credential=Credential value
keycloak.secure-server.credential=Credential value
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
index 62ce35d..61a5204 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
@@ -122,6 +122,7 @@
<xs:element name="ignore-oauth-query-parameter" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="verify-token-audience" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="token-cookie-path" type="xs:string" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml b/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
index 367aec1..047bc3e 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
@@ -69,6 +69,7 @@
<realm>master</realm>
<resource>http-endpoint</resource>
<use-resource-role-mappings>true</use-resource-role-mappings>
+ <token-cookie-path>/</token-cookie-path>
<realm-public-key>
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4siLKUew0WYxdtq6/rwk4Uj/4amGFFnE/yzIxQVU0PUqz3QBRVkUWpDj0K6ZnS5nzJV/y6DHLEy7hjZTdRDphyF1sq09aDOYnVpzu8o2sIlMM8q5RnUyEfIyUZqwo8pSZDJ90fS0s+IDUJNCSIrAKO3w1lqZDHL6E/YFHXyzkvQIDAQAB
</realm-public-key>
@@ -90,6 +91,7 @@
<realm>jboss-infra</realm>
<resource>wildfly-console</resource>
<public-client>true</public-client>
+ <token-cookie-path>/</token-cookie-path>
<ssl-required>EXTERNAL</ssl-required>
<confidential-port>443</confidential-port>
<proxy-url>http://localhost:9000</proxy-url>
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java
index bbe1046..f5ecd88 100644
--- a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java
@@ -18,12 +18,9 @@
package org.keycloak.adapters.elytron;
-import java.security.Principal;
-
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
-import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.CookieTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OidcKeycloakAccount;
@@ -98,7 +95,7 @@ public class ElytronCookieTokenStore implements ElytronTokeStore {
return true;
} else {
log.debug("Account was not active, removing cookie and returning false");
- CookieTokenStore.removeCookie(httpFacade);
+ CookieTokenStore.removeCookie(deployment, httpFacade);
return false;
}
}
@@ -145,7 +142,7 @@ public class ElytronCookieTokenStore implements ElytronTokeStore {
return;
}
- CookieTokenStore.removeCookie(this.httpFacade);
+ CookieTokenStore.removeCookie(this.httpFacade.getDeployment(), this.httpFacade);
if (glo) {
KeycloakSecurityContext ksc = (KeycloakSecurityContext) principal.getKeycloakSecurityContext();
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
index 095a613..a4f0d6b 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
@@ -37,7 +37,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
"allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
"client-keystore", "client-keystore-password", "client-key-password",
"always-refresh-token",
- "register-node-at-startup", "register-node-period", "token-store", "principal-attribute",
+ "register-node-at-startup", "register-node-period", "token-store", "token-cookie-path", "principal-attribute",
"proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live",
"min-time-between-jwks-requests", "public-key-cache-ttl",
"policy-enforcer", "ignore-oauth-query-parameter", "verify-token-audience"
@@ -68,6 +68,8 @@ public class AdapterConfig extends BaseAdapterConfig implements AdapterHttpClien
protected int registerNodePeriod = -1;
@JsonProperty("token-store")
protected String tokenStore;
+ @JsonProperty("token-cookie-path")
+ protected String tokenCookiePath;
@JsonProperty("principal-attribute")
protected String principalAttribute;
@JsonProperty("turn-off-change-session-id-on-login")
@@ -197,6 +199,14 @@ public class AdapterConfig extends BaseAdapterConfig implements AdapterHttpClien
this.tokenStore = tokenStore;
}
+ public String getTokenCookiePath() {
+ return tokenCookiePath;
+ }
+
+ public void setTokenCookiePath(String tokenCookiePath) {
+ this.tokenCookiePath = tokenCookiePath;
+ }
+
public String getPrincipalAttribute() {
return principalAttribute;
}
diff --git a/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java b/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java
index d9b4ce5..81f9980 100644
--- a/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java
+++ b/testsuite/integration-arquillian/servers/app-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/UndertowAppServer.java
@@ -120,6 +120,10 @@ public class UndertowAppServer implements DeployableContainer<UndertowAppServerC
throw new IllegalArgumentException("UndertowContainer only supports UndertowWebArchive or WebArchive.");
}
+ if ("ROOT.war".equals(archive.getName())) {
+ di.setContextPath("/");
+ }
+
ClassLoader parentCl = Thread.currentThread().getContextClassLoader();
UndertowWarClassLoader classLoader = new UndertowWarClassLoader(parentCl, archive);
Thread.currentThread().setContextClassLoader(classLoader);
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerCookiePortalRoot.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerCookiePortalRoot.java
new file mode 100644
index 0000000..8370c1c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerCookiePortalRoot.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 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 java.net.MalformedURLException;
+import java.net.URL;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import org.keycloak.testsuite.util.URLUtils;
+
+public class CustomerCookiePortalRoot extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "customer-cookie-portal-root";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ try {
+ return new URL(url.toString() + "/");
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String logoutURL() {
+ return getInjectedUrl() + "/logout";
+ }
+
+ public void navigateTo(String relative) {
+ URLUtils.navigateToUri(getInjectedUrl() + relative);
+ }
+
+ @Override
+ public String toString() {
+ return getInjectedUrl().toString();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/CookieStoreRootContextTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/CookieStoreRootContextTest.java
new file mode 100644
index 0000000..a8b23f7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/CookieStoreRootContextTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2018 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.servlet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+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;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.constants.AdapterConstants;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter;
+import org.keycloak.testsuite.adapter.page.CustomerCookiePortalRoot;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+import org.keycloak.testsuite.arquillian.containers.ContainerConstants;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.util.JavascriptBrowser;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Cookie;
+import org.openqa.selenium.WebDriver;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
+@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
+@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY_DEPRECATED)
+@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
+@AppServerContainer(ContainerConstants.APP_SERVER_EAP6)
+public class CookieStoreRootContextTest extends DemoServletsAdapterTest {
+
+ // Javascript browser needed KEYCLOAK-4703
+ @Drone
+ @JavascriptBrowser
+ protected WebDriver jsDriver;
+
+ @Page
+ @JavascriptBrowser
+ protected OIDCLogin jsDriverTestRealmLoginPage;
+
+ @Page
+ private CustomerCookiePortalRoot customerCookiePortalRoot;
+
+ @Rule
+ public AssertEvents assertEvents = new AssertEvents(this);
+
+ @Deployment(name = CustomerCookiePortalRoot.DEPLOYMENT_NAME)
+ protected static WebArchive customerCookiePortalRoot() {
+ WebArchive original = servletDeployment(CustomerCookiePortalRoot.DEPLOYMENT_NAME, AdapterActionsFilter.class, CustomerServlet.class, ErrorServlet.class, ServletTestUtils.class);
+
+ WebArchive archive = ShrinkWrap.create(WebArchive.class, "ROOT.war");
+
+ archive.merge(original);
+
+ return archive;
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ configPage.setConsoleRealm(DEMO);
+ loginEventsPage.setConsoleRealm(DEMO);
+ applicationsPage.setAuthRealm(DEMO);
+ loginEventsPage.setConsoleRealm(DEMO);
+ }
+
+ @Test
+ public void testTokenInCookieSSORoot() {
+ // Login
+ String tokenCookie = loginToCustomerCookiePortalRoot();
+ Cookie cookie = driver.manage().getCookieNamed(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE);
+ assertEquals("/", cookie.getPath());
+
+ // SSO to second app
+ customerPortal.navigateTo();
+ assertLogged();
+
+ customerCookiePortalRoot.navigateTo();
+ assertLogged();
+ cookie = driver.manage().getCookieNamed(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE);
+ String tokenCookie2 = cookie.getValue();
+ assertEquals(tokenCookie, tokenCookie2);
+ assertEquals("/", cookie.getPath());
+
+ // Logout with httpServletRequest
+ logoutFromCustomerCookiePortalRoot();
+
+ // Also should be logged-out from the second app
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ }
+
+ private String loginToCustomerCookiePortalRoot() {
+ customerCookiePortalRoot.navigateTo("relative");
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlEquals(customerCookiePortalRoot.getInjectedUrl().toString() + "relative");
+ assertLogged();
+
+ // Assert no JSESSIONID cookie
+ Assert.assertNull(driver.manage().getCookieNamed("JSESSIONID"));
+
+ return driver.manage().getCookieNamed(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE).getValue();
+ }
+
+ private void logoutFromCustomerCookiePortalRoot() {
+ String logout = customerCookiePortalRoot.logoutURL();
+ driver.navigate().to(logout);
+ WaitUtils.waitUntilElement(By.id("customer_portal_logout")).is().present();
+ assertNull(driver.manage().getCookieNamed(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE));
+ customerCookiePortalRoot.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/DemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/DemoServletsAdapterTest.java
index 2847b2c..da8df41 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/DemoServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/DemoServletsAdapterTest.java
@@ -145,7 +145,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
protected OIDCLogin jsDriverTestRealmLoginPage;
@Page
- private CustomerPortal customerPortal;
+ protected CustomerPortal customerPortal;
@Page
private CustomerPortalNoConf customerPortalNoConf;
@Page
@@ -169,13 +169,13 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
@Page
private OAuthGrant oAuthGrantPage;
@Page
- private Applications applicationsPage;
+ protected Applications applicationsPage;
@Page
- private LoginEvents loginEventsPage;
+ protected LoginEvents loginEventsPage;
@Page
private BasicAuth basicAuthPage;
@Page
- private Config configPage;
+ protected Config configPage;
@Page
private ClientSecretJwtSecurePortal clientSecretJwtSecurePortal;
@Page
@@ -393,7 +393,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
}
- private void assertLogged() {
+ protected void assertLogged() {
assertPageContains("Bill Burke");
assertPageContains("Stian Thorgersen");
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/META-INF/context.xml
new file mode 100644
index 0000000..abff3d2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/META-INF/context.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright 2018 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.
+ -->
+
+<Context path="/customer-portal">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..8df5936
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/jetty-web.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+ ~ Copyright 2018 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.
+ -->
+
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ <Get name="securityHandler">
+ <Set name="authenticator">
+ <New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
+ <!--
+ <Set name="adapterConfig">
+ <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+ <Set name="realm">tomcat</Set>
+ <Set name="resource">customer-portal</Set>
+ <Set name="authServerUrl">http://localhost:8180/auth</Set>
+ <Set name="sslRequired">external</Set>
+ <Set name="credentials">
+ <Map>
+ <Entry>
+ <Item>secret</Item>
+ <Item>password</Item>
+ </Entry>
+ </Map>
+ </Set>
+ <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+ </New>
+ </Set>
+ -->
+ </New>
+ </Set>
+ </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/keycloak.json
new file mode 100644
index 0000000..e699fe6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/keycloak.json
@@ -0,0 +1,13 @@
+{
+ "realm": "demo",
+ "resource": "customer-cookie-portal-root",
+ "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "expose-token": true,
+ "token-store": "cookie",
+ "token-cookie-path": "/",
+ "credentials": {
+ "secret": "password"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/web.xml
new file mode 100644
index 0000000..955cb1f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-cookie-portal-root/WEB-INF/web.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ * Copyright 2018 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.
+ -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <servlet>
+ <servlet-name>Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.CustomerServlet</servlet-class>
+ </servlet>
+ <servlet>
+ <servlet-name>Error Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.ErrorServlet</servlet-class>
+ </servlet>
+
+ <filter>
+ <filter-name>AdapterActionsFilter</filter-name>
+ <filter-class>org.keycloak.testsuite.adapter.filter.AdapterActionsFilter</filter-class>
+ </filter>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
+ <servlet-name>Error Servlet</servlet-name>
+ <url-pattern>/error.html</url-pattern>
+ </servlet-mapping>
+
+ <filter-mapping>
+ <filter-name>AdapterActionsFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Users</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ </security-constraint>
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Errors</web-resource-name>
+ <url-pattern>/error.html</url-pattern>
+ </web-resource-collection>
+ </security-constraint>
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Unsecured</web-resource-name>
+ <url-pattern>/unsecured/*</url-pattern>
+ </web-resource-collection>
+ </security-constraint>
+
+
+ <login-config>
+ <auth-method>KEYCLOAK</auth-method>
+ <realm-name>demo</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
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 a45098c..5afd248 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
@@ -171,6 +171,15 @@
"secret": "password"
},
{
+ "clientId": "customer-cookie-portal-root",
+ "enabled": true,
+ "baseUrl": "/",
+ "redirectUris": [
+ "http://localhost:8280/*"
+ ],
+ "secret": "password"
+ },
+ {
"clientId": "customer-portal-js",
"enabled": true,
"publicClient": true,