Remove jaxrs-oauth-client and OIDC servlet-filter adapters

Closes #28784

Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Jon Koops 2024-04-25 12:57:45 +02:00 committed by Marek Posolda
parent aeed5eee17
commit a6e2ab5523
24 changed files with 0 additions and 2327 deletions

View file

@ -1,191 +0,0 @@
<?xml version="1.0"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>999.0.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-jakarta-servlet-filter-adapter</artifactId>
<name>Keycloak Servlet Filter Adapter Integration</name>
<description/>
<!--
NOTE: This maven module is generated using original: servlet-filter
@see: ${jakarta-transformer-sources}
Reason is the transition to Jakarta APIs.
-->
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.servlet.*
</keycloak.osgi.export>
<keycloak.osgi.import>
jakarta.servlet.*;version="[3.1,5)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
<jakarta-transformer-sources>${project.basedir}/../servlet-filter/src</jakarta-transformer-sources>
<jakarta-transformer-target>${project.basedir}/src</jakarta-transformer-target>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jakarta-servlet-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-policy-enforcer</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Jakarta Transformer Plugin -->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>transform</id>
<phase>initialize</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<property name="plugin_classpath" refid="maven.plugin.classpath" />
<java classname="org.eclipse.transformer.jakarta.JakartaTransformer">
<arg value="-o" />
<arg value="${jakarta-transformer-sources}" />
<arg value="${jakarta-transformer-target}" />
<classpath>
<pathelement path="${plugin_classpath}" />
</classpath>
</java>
</target>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.transformer</groupId>
<artifactId>org.eclipse.transformer.cli</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>ant-contrib</groupId>
<artifactId>ant-contrib</artifactId>
<version>1.0b3</version>
<exclusions>
<exclusion>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,96 +0,0 @@
<?xml version="1.0"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>999.0.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
<name>Keycloak JAX-RS OAuth Client</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -1,35 +0,0 @@
/*
* 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.jaxrs;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
* @deprecated Class is deprecated and may be removed in the future. If you want to maintain this class for Keycloak community, please
* contact Keycloak team on keycloak-dev mailing list. You can fork it into your github repository and
* Keycloak team will reference it from "Keycloak Extensions" page.
*/
@PreMatching
@Priority(Priorities.AUTHENTICATION)
@Deprecated
public interface JaxrsBearerTokenFilter extends ContainerRequestFilter {
}

View file

@ -1,317 +0,0 @@
/*
* 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.jaxrs;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.BasicAuthRequestAuthenticator;
import org.keycloak.adapters.BearerTokenRequestAuthenticator;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.PreAuthActionsHandler;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.UserSessionManagement;
import org.keycloak.common.constants.GenericConstants;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.Principal;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
* @deprecated Class is deprecated and may be removed in the future. If you want to maintain this class for Keycloak community, please
* contact Keycloak team on keycloak-dev mailing list. You can fork it into your github repository and
* Keycloak team will reference it from "Keycloak Extensions" page.
*/
@PreMatching
@Priority(Priorities.AUTHENTICATION)
@Deprecated
public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
private final static Logger log = Logger.getLogger("" + JaxrsBearerTokenFilterImpl.class);
private String keycloakConfigFile;
private String keycloakConfigResolverClass;
protected volatile boolean started;
protected AdapterDeploymentContext deploymentContext;
// TODO: Should also somehow handle stop lifecycle for de-registration
protected NodesRegistrationManagement nodesRegistrationManagement;
protected UserSessionManagement userSessionManagement = new EmptyUserSessionManagement();
public void setKeycloakConfigFile(String configFile) {
this.keycloakConfigFile = configFile;
attemptStart();
}
public String getKeycloakConfigFile() {
return this.keycloakConfigFile;
}
public String getKeycloakConfigResolverClass() {
return keycloakConfigResolverClass;
}
public void setKeycloakConfigResolverClass(String keycloakConfigResolverClass) {
this.keycloakConfigResolverClass = keycloakConfigResolverClass;
attemptStart();
}
// INITIALIZATION AND STARTUP
protected void attemptStart() {
if (started) {
throw new IllegalStateException("Filter already started. Make sure to specify just keycloakConfigResolver or keycloakConfigFile but not both");
}
if (isInitialized()) {
start();
} else {
log.fine("Not yet initialized");
}
}
protected boolean isInitialized() {
return this.keycloakConfigFile != null || this.keycloakConfigResolverClass != null;
}
protected void start() {
if (started) {
throw new IllegalStateException("Filter already started. Make sure to specify just keycloakConfigResolver or keycloakConfigFile but not both");
}
if (keycloakConfigResolverClass != null) {
Class<? extends KeycloakConfigResolver> resolverClass = loadResolverClass();
try {
KeycloakConfigResolver resolver = resolverClass.newInstance();
log.info("Using " + resolver + " to resolve Keycloak configuration on a per-request basis.");
this.deploymentContext = new AdapterDeploymentContext(resolver);
} catch (Exception e) {
throw new RuntimeException("Unable to instantiate resolver " + resolverClass);
}
} else {
if (keycloakConfigFile == null) {
throw new IllegalArgumentException("You need to specify either keycloakConfigResolverClass or keycloakConfigFile in configuration");
}
InputStream is = loadKeycloakConfigFile();
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
deploymentContext = new AdapterDeploymentContext(kd);
log.info("Keycloak is using a per-deployment configuration loaded from: " + keycloakConfigFile);
}
nodesRegistrationManagement = new NodesRegistrationManagement();
started = true;
}
// TODO: Use 'Reflections.classForName'
protected Class<? extends KeycloakConfigResolver> loadResolverClass() {
try {
return (Class<? extends KeycloakConfigResolver>)getClass().getClassLoader().loadClass(keycloakConfigResolverClass);
} catch (ClassNotFoundException cnfe) {
// Fallback to tccl
try {
return (Class<? extends KeycloakConfigResolver>)Thread.currentThread().getContextClassLoader().loadClass(keycloakConfigResolverClass);
} catch (ClassNotFoundException cnfe2) {
throw new RuntimeException("Unable to find resolver class: " + keycloakConfigResolverClass);
}
}
}
protected InputStream loadKeycloakConfigFile() {
if (keycloakConfigFile.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
String classPathLocation = keycloakConfigFile.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
log.fine("Loading config from classpath on location: " + classPathLocation);
// Try current class classloader first
InputStream is = getClass().getClassLoader().getResourceAsStream(classPathLocation);
if (is == null) {
is = Thread.currentThread().getContextClassLoader().getResourceAsStream(classPathLocation);
}
if (is != null) {
return is;
} else {
throw new RuntimeException("Unable to find config from classpath: " + keycloakConfigFile);
}
} else {
// Fallback to file
try {
log.fine("Loading config from file: " + keycloakConfigFile);
return new FileInputStream(keycloakConfigFile);
} catch (FileNotFoundException fnfe) {
log.severe("Config not found on " + keycloakConfigFile);
throw new RuntimeException(fnfe);
}
}
}
// REQUEST HANDLING
@Override
public void filter(ContainerRequestContext request) throws IOException {
SecurityContext securityContext = getRequestSecurityContext(request);
JaxrsHttpFacade facade = new JaxrsHttpFacade(request, securityContext);
if (handlePreauth(facade)) {
return;
}
KeycloakDeployment resolvedDeployment = deploymentContext.resolveDeployment(facade);
nodesRegistrationManagement.tryRegister(resolvedDeployment);
bearerAuthentication(facade, request, resolvedDeployment);
}
protected boolean handlePreauth(JaxrsHttpFacade facade) {
PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
if (handler.handleRequest()) {
// Send response now (if not already sent)
if (!facade.isResponseFinished()) {
facade.getResponse().end();
}
return true;
}
return false;
}
protected void bearerAuthentication(JaxrsHttpFacade facade, ContainerRequestContext request, KeycloakDeployment resolvedDeployment) {
BearerTokenRequestAuthenticator authenticator = new BearerTokenRequestAuthenticator(resolvedDeployment);
AuthOutcome outcome = authenticator.authenticate(facade);
if (outcome == AuthOutcome.NOT_ATTEMPTED && resolvedDeployment.isEnableBasicAuth()) {
authenticator = new BasicAuthRequestAuthenticator(resolvedDeployment);
outcome = authenticator.authenticate(facade);
}
if (outcome == AuthOutcome.FAILED || outcome == AuthOutcome.NOT_ATTEMPTED) {
AuthChallenge challenge = authenticator.getChallenge();
log.fine("Authentication outcome: " + outcome);
boolean challengeSent = challenge.challenge(facade);
if (!challengeSent) {
// Use some default status code
facade.getResponse().setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
}
// Send response now (if not already sent)
if (!facade.isResponseFinished()) {
facade.getResponse().end();
}
return;
} else {
if (verifySslFailed(facade, resolvedDeployment)) {
return;
}
}
propagateSecurityContext(facade, request, resolvedDeployment, authenticator);
handleAuthActions(facade, resolvedDeployment);
}
protected void propagateSecurityContext(JaxrsHttpFacade facade, ContainerRequestContext request, KeycloakDeployment resolvedDeployment, BearerTokenRequestAuthenticator bearer) {
RefreshableKeycloakSecurityContext skSession = new RefreshableKeycloakSecurityContext(resolvedDeployment, null, bearer.getTokenString(), bearer.getToken(), null, null, null);
// Not needed to do resteasy specifics as KeycloakSecurityContext can be always retrieved from SecurityContext by typecast SecurityContext.getUserPrincipal to KeycloakPrincipal
// ResteasyProviderFactory.pushContext(KeycloakSecurityContext.class, skSession);
facade.setSecurityContext(skSession);
String principalName = AdapterUtils.getPrincipalName(resolvedDeployment, bearer.getToken());
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(principalName, skSession);
SecurityContext anonymousSecurityContext = getRequestSecurityContext(request);
final boolean isSecure = anonymousSecurityContext.isSecure();
final Set<String> roles = AdapterUtils.getRolesFromSecurityContext(skSession);
SecurityContext ctx = new SecurityContext() {
@Override
public Principal getUserPrincipal() {
return principal;
}
@Override
public boolean isUserInRole(String role) {
return roles.contains(role);
}
@Override
public boolean isSecure() {
return isSecure;
}
@Override
public String getAuthenticationScheme() {
return "OAUTH_BEARER";
}
};
request.setSecurityContext(ctx);
}
protected boolean verifySslFailed(JaxrsHttpFacade facade, KeycloakDeployment deployment) {
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
log.warning("SSL is required to authenticate, but request is not secured");
facade.getResponse().sendError(403, "SSL required!");
return true;
}
return false;
}
protected SecurityContext getRequestSecurityContext(ContainerRequestContext request) {
return request.getSecurityContext();
}
protected void handleAuthActions(JaxrsHttpFacade facade, KeycloakDeployment deployment) {
AuthenticatedActionsHandler authActionsHandler = new AuthenticatedActionsHandler(deployment, facade);
if (authActionsHandler.handledRequest()) {
// Send response now (if not already sent)
if (!facade.isResponseFinished()) {
facade.getResponse().end();
}
}
}
// We don't have any sessions to manage with pure jaxrs filter
private static class EmptyUserSessionManagement implements UserSessionManagement {
@Override
public void logoutAll() {
}
@Override
public void logoutHttpSessions(List<String> ids) {
}
}
}

View file

@ -1,240 +0,0 @@
/*
* 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.jaxrs;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.AuthenticationError;
import org.keycloak.adapters.spi.LogoutError;
import org.keycloak.common.util.HostUtils;
import javax.security.cert.X509Certificate;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.SecurityContext;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
* @deprecated Class is deprecated and may be removed in the future. If you want to maintain this class for Keycloak community, please
* contact Keycloak team on keycloak-dev mailing list. You can fork it into your github repository and
* Keycloak team will reference it from "Keycloak Extensions" page.
*/
@Deprecated
public class JaxrsHttpFacade implements OIDCHttpFacade {
protected final ContainerRequestContext requestContext;
protected final SecurityContext securityContext;
protected final RequestFacade requestFacade = new RequestFacade();
protected final ResponseFacade responseFacade = new ResponseFacade();
protected KeycloakSecurityContext keycloakSecurityContext;
protected boolean responseFinished;
public JaxrsHttpFacade(ContainerRequestContext containerRequestContext, SecurityContext securityContext) {
this.requestContext = containerRequestContext;
this.securityContext = securityContext;
}
protected class RequestFacade implements OIDCHttpFacade.Request {
private InputStream inputStream;
@Override
public String getFirstParam(String param) {
throw new RuntimeException("NOT IMPLEMENTED");
}
@Override
public String getMethod() {
return requestContext.getMethod();
}
@Override
public String getURI() {
return requestContext.getUriInfo().getRequestUri().toString();
}
@Override
public String getRelativePath() {
return requestContext.getUriInfo().getPath();
}
@Override
public boolean isSecure() {
return securityContext.isSecure();
}
@Override
public String getQueryParamValue(String param) {
MultivaluedMap<String, String> queryParams = requestContext.getUriInfo().getQueryParameters();
if (queryParams == null)
return null;
return queryParams.getFirst(param);
}
@Override
public Cookie getCookie(String cookieName) {
Map<String, javax.ws.rs.core.Cookie> cookies = requestContext.getCookies();
if (cookies == null)
return null;
javax.ws.rs.core.Cookie cookie = cookies.get(cookieName);
if (cookie == null)
return null;
return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
}
@Override
public String getHeader(String name) {
return requestContext.getHeaderString(name);
}
@Override
public List<String> getHeaders(String name) {
MultivaluedMap<String, String> headers = requestContext.getHeaders();
return (headers == null) ? null : headers.get(name);
}
@Override
public InputStream getInputStream() {
return getInputStream(false);
}
@Override
public InputStream getInputStream(boolean buffered) {
if (inputStream != null) {
return inputStream;
}
if (buffered) {
return inputStream = new BufferedInputStream(requestContext.getEntityStream());
}
return requestContext.getEntityStream();
}
@Override
public String getRemoteAddr() {
// TODO: implement properly
return HostUtils.getIpAddress();
}
@Override
public void setError(AuthenticationError error) {
requestContext.setProperty(AuthenticationError.class.getName(), error);
}
@Override
public void setError(LogoutError error) {
requestContext.setProperty(LogoutError.class.getName(), error);
}
}
protected class ResponseFacade implements OIDCHttpFacade.Response {
private javax.ws.rs.core.Response.ResponseBuilder responseBuilder = javax.ws.rs.core.Response.status(204);
@Override
public void setStatus(int status) {
responseBuilder.status(status);
}
@Override
public void addHeader(String name, String value) {
responseBuilder.header(name, value);
}
@Override
public void setHeader(String name, String value) {
responseBuilder.header(name, value);
}
@Override
public void resetCookie(String name, String path) {
// For now doesn't need to be supported
throw new IllegalStateException("Not supported yet");
}
@Override
public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly) {
// For now doesn't need to be supported
throw new IllegalStateException("Not supported yet");
}
@Override
public OutputStream getOutputStream() {
// For now doesn't need to be supported
throw new IllegalStateException("Not supported yet");
}
@Override
public void sendError(int code) {
javax.ws.rs.core.Response response = responseBuilder.status(code).build();
requestContext.abortWith(response);
responseFinished = true;
}
@Override
public void sendError(int code, String message) {
javax.ws.rs.core.Response response = responseBuilder.status(code).entity(message).build();
requestContext.abortWith(response);
responseFinished = true;
}
@Override
public void end() {
javax.ws.rs.core.Response response = responseBuilder.build();
requestContext.abortWith(response);
responseFinished = true;
}
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return keycloakSecurityContext;
}
public void setSecurityContext(KeycloakSecurityContext securityContext) {
this.keycloakSecurityContext = securityContext;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
public boolean isResponseFinished() {
return responseFinished;
}
}

View file

@ -1,142 +0,0 @@
/*
* 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.jaxrs;
import org.keycloak.AbstractOAuthClient;
import org.keycloak.OAuth2Constants;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.Map;
import java.util.logging.Logger;
/**
* Helper code to obtain oauth access tokens via browser redirects
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
* @deprecated Class is deprecated and may be removed in the future. If you want to maintain this class for Keycloak community, please
* contact Keycloak team on keycloak-dev mailing list. You can fork it into your github repository and
* Keycloak team will reference it from "Keycloak Extensions" page.
*/
@Deprecated
public class JaxrsOAuthClient extends AbstractOAuthClient {
private final static Logger logger = Logger.getLogger("" + JaxrsOAuthClient.class);
protected Client client;
/**
* closes client
*/
public void stop() {
if (client != null) client.close();
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public String resolveBearerToken(String redirectUri, String code) {
redirectUri = stripOauthParametersFromRedirect(redirectUri);
Form codeForm = new Form()
.param(OAuth2Constants.GRANT_TYPE, "authorization_code")
.param(OAuth2Constants.CODE, code)
.param(OAuth2Constants.CLIENT_ID, clientId)
.param(OAuth2Constants.REDIRECT_URI, redirectUri);
for (Map.Entry<String, Object> entry : credentials.entrySet()) {
codeForm.param(entry.getKey(), (String) entry.getValue());
}
Response res = client.target(tokenUrl).request().post(Entity.form(codeForm));
try {
if (res.getStatus() == 400) {
throw new BadRequestException();
} else if (res.getStatus() != 200) {
throw new InternalServerErrorException(new Exception("Unknown error when getting acess token"));
}
AccessTokenResponse tokenResponse = res.readEntity(AccessTokenResponse.class);
return tokenResponse.getToken();
} finally {
res.close();
}
}
public Response redirect(UriInfo uriInfo, String redirectUri) {
String state = getStateCode();
String scopeParam = TokenUtil.attachOIDCScope(scope);
UriBuilder uriBuilder = UriBuilder.fromUri(authUrl)
.queryParam(OAuth2Constants.CLIENT_ID, clientId)
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.STATE, state)
.queryParam(OAuth2Constants.SCOPE, scopeParam);
URI url = uriBuilder.build();
NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure, true);
logger.fine("NewCookie: " + cookie.toString());
logger.fine("Oauth Redirect to: " + url);
return Response.status(302)
.location(url)
.cookie(cookie).build();
}
public String getStateCookiePath(UriInfo uriInfo) {
if (stateCookiePath != null) return stateCookiePath;
return uriInfo.getBaseUri().getRawPath();
}
public String getBearerToken(UriInfo uriInfo, HttpHeaders headers) throws BadRequestException, InternalServerErrorException {
String error = getError(uriInfo);
if (error != null) throw new BadRequestException(new Exception("OAuth error: " + error));
checkStateCookie(uriInfo, headers);
String code = getAccessCode(uriInfo);
if (code == null) throw new BadRequestException(new Exception("code parameter was null"));
return resolveBearerToken(uriInfo.getRequestUri().toString(), code);
}
public String getError(UriInfo uriInfo) {
return uriInfo.getQueryParameters().getFirst(OAuth2Constants.ERROR);
}
public String getAccessCode(UriInfo uriInfo) {
return uriInfo.getQueryParameters().getFirst(OAuth2Constants.CODE);
}
public void checkStateCookie(UriInfo uriInfo, HttpHeaders headers) {
Cookie stateCookie = headers.getCookies().get(stateCookieName);
if (stateCookie == null) throw new BadRequestException("state cookie not set");
String state = uriInfo.getQueryParameters().getFirst(OAuth2Constants.STATE);
if (state == null) throw new BadRequestException("state parameter was null");
if (!state.equals(stateCookie.getValue())) {
throw new BadRequestException("state parameter invalid");
}
}
}

View file

@ -1,98 +0,0 @@
/*
* 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.jaxrs;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.common.constants.GenericConstants;
import org.osgi.framework.BundleContext;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.PreMatching;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.logging.Logger;
/**
* Variant of JaxrsBearerTokenFilter, which can be used to properly use resources from current osgi bundle
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
* @deprecated Class is deprecated and may be removed in the future. If you want to maintain this class for Keycloak community, please
* contact Keycloak team on keycloak-dev mailing list. You can fork it into your github repository and
* Keycloak team will reference it from "Keycloak Extensions" page.
*/
@PreMatching
@Priority(Priorities.AUTHENTICATION)
@Deprecated
public class OsgiJaxrsBearerTokenFilterImpl extends JaxrsBearerTokenFilterImpl {
private final static Logger log = Logger.getLogger("" + JaxrsBearerTokenFilterImpl.class);
private BundleContext bundleContext;
public BundleContext getBundleContext() {
return bundleContext;
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
attemptStart();
}
@Override
protected boolean isInitialized() {
return super.isInitialized() && bundleContext != null;
}
@Override
protected Class<? extends KeycloakConfigResolver> loadResolverClass() {
String resolverClass = getKeycloakConfigResolverClass();
try {
return (Class<? extends KeycloakConfigResolver>) bundleContext.getBundle().loadClass(resolverClass);
} catch (ClassNotFoundException cnfe) {
log.warning("Not able to find class from bundleContext. Fallback to current classloader");
return super.loadResolverClass();
}
}
@Override
protected InputStream loadKeycloakConfigFile() {
String keycloakConfigFile = getKeycloakConfigFile();
if (keycloakConfigFile.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
// Load from classpath of current bundle
String classPathLocation = keycloakConfigFile.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
log.fine("Loading config from classpath on location: " + classPathLocation);
URL cfgUrl = bundleContext.getBundle().getResource(classPathLocation);
if (cfgUrl == null) {
log.warning("Not able to find configFile from bundleContext. Fallback to current classloader");
return super.loadKeycloakConfigFile();
}
try {
return cfgUrl.openStream();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
} else {
return super.loadKeycloakConfigFile();
}
}
}

View file

@ -32,10 +32,7 @@
<modules> <modules>
<module>adapter-core</module> <module>adapter-core</module>
<module>jaxrs-oauth-client</module>
<module>js</module> <module>js</module>
<module>servlet-filter</module>
<module>jakarta-servlet-filter</module>
<module>undertow</module> <module>undertow</module>
<module>wildfly</module> <module>wildfly</module>
<module>wildfly-elytron</module> <module>wildfly-elytron</module>

View file

@ -1,138 +0,0 @@
<?xml version="1.0"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>999.0.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
<name>Keycloak Servlet Filter Adapter Integration</name>
<description/>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.servlet.*
</keycloak.osgi.export>
<keycloak.osgi.import>
javax.servlet.*;version="[3.1,5)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-policy-enforcer</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,124 +0,0 @@
/*
* 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.adapters.servlet;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OAuthRequestAuthenticator;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.OidcKeycloakAccount;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.KeycloakAccount;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.security.Principal;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a>
* @version $Revision: 1 $
*/
public class FilterRequestAuthenticator extends RequestAuthenticator {
private static final Logger log = Logger.getLogger(""+FilterRequestAuthenticator.class);
protected HttpServletRequest request;
public FilterRequestAuthenticator(KeycloakDeployment deployment,
AdapterTokenStore tokenStore,
OIDCHttpFacade facade,
HttpServletRequest request,
int sslRedirectPort) {
super(facade, deployment, tokenStore, sslRedirectPort);
this.request = request;
}
@Override
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
}
@Override
protected void completeOAuthAuthentication(final KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) {
final RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext();
final Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
OidcKeycloakAccount account = new OidcKeycloakAccount() {
@Override
public Principal getPrincipal() {
return skp;
}
@Override
public Set<String> getRoles() {
return roles;
}
@Override
public KeycloakSecurityContext getKeycloakSecurityContext() {
return securityContext;
}
};
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
this.tokenStore.saveAccountInfo(account);
}
@Override
protected void completeBearerAuthentication(final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
final RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
final Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
if (log.isLoggable(Level.FINE)) {
log.fine("Completing bearer authentication. Bearer roles: " + roles);
}
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
OidcKeycloakAccount account = new OidcKeycloakAccount() {
@Override
public Principal getPrincipal() {
return principal;
}
@Override
public Set<String> getRoles() {
return roles;
}
@Override
public KeycloakSecurityContext getKeycloakSecurityContext() {
return securityContext;
}
};
// need this here to obtain UserPrincipal
request.setAttribute(KeycloakAccount.class.getName(), account);
}
@Override
protected String changeHttpSessionId(boolean create) {
HttpSession session = request.getSession(create);
return session != null ? session.getId() : null;
}
}

View file

@ -1,273 +0,0 @@
/*
* 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.adapters.servlet;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AuthenticatedActionsHandler;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.NodesRegistrationManagement;
import org.keycloak.adapters.PreAuthActionsHandler;
import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.InMemorySessionIdMapper;
import org.keycloak.adapters.spi.SessionIdMapper;
import org.keycloak.adapters.spi.UserSessionManagement;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakOIDCFilter implements Filter {
private final static Logger log = Logger.getLogger("" + KeycloakOIDCFilter.class);
public static final String SKIP_PATTERN_PARAM = "keycloak.config.skipPattern";
public static final String ID_MAPPER_PARAM = "keycloak.config.idMapper";
public static final String CONFIG_RESOLVER_PARAM = "keycloak.config.resolver";
public static final String CONFIG_FILE_PARAM = "keycloak.config.file";
public static final String CONFIG_PATH_PARAM = "keycloak.config.path";
protected AdapterDeploymentContext deploymentContext;
protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
protected NodesRegistrationManagement nodesRegistrationManagement;
protected Pattern skipPattern;
private final KeycloakConfigResolver definedconfigResolver;
/**
* Constructor that can be used to define a {@code KeycloakConfigResolver} that will be used at initialization to
* provide the {@code KeycloakDeployment}.
* @param definedconfigResolver the resolver
*/
public KeycloakOIDCFilter(KeycloakConfigResolver definedconfigResolver) {
this.definedconfigResolver = definedconfigResolver;
}
public KeycloakOIDCFilter() {
this(null);
}
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
String skipPatternDefinition = filterConfig.getInitParameter(SKIP_PATTERN_PARAM);
if (skipPatternDefinition != null) {
skipPattern = Pattern.compile(skipPatternDefinition, Pattern.DOTALL);
}
String idMapperClassName = filterConfig.getInitParameter(ID_MAPPER_PARAM);
if (idMapperClassName != null) {
try {
final Class<?> idMapperClass = getClass().getClassLoader().loadClass(idMapperClassName);
final Constructor<?> idMapperConstructor = idMapperClass.getDeclaredConstructor();
Object idMapperInstance = null;
// for KEYCLOAK-13745 test
if (idMapperConstructor.getModifiers() == Modifier.PRIVATE) {
idMapperInstance = idMapperClass.getMethod("getInstance").invoke(null);
} else {
idMapperInstance = idMapperConstructor.newInstance();
}
if(idMapperInstance instanceof SessionIdMapper) {
this.idMapper = (SessionIdMapper) idMapperInstance;
} else {
log.log(Level.WARNING, "SessionIdMapper class {0} is not instance of org.keycloak.adapters.spi.SessionIdMapper", idMapperClassName);
}
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
log.log(Level.WARNING, "SessionIdMapper class could not be instanced", e);
}
}
if (definedconfigResolver != null) {
deploymentContext = new AdapterDeploymentContext(definedconfigResolver);
log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", definedconfigResolver.getClass());
} else {
String configResolverClass = filterConfig.getInitParameter(CONFIG_RESOLVER_PARAM);
if (configResolverClass != null) {
try {
KeycloakConfigResolver configResolver = (KeycloakConfigResolver) getClass().getClassLoader().loadClass(configResolverClass).newInstance();
deploymentContext = new AdapterDeploymentContext(configResolver);
log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
} catch (Exception ex) {
log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()});
deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
}
} else {
String fp = filterConfig.getInitParameter(CONFIG_FILE_PARAM);
InputStream is = null;
if (fp != null) {
try {
is = new FileInputStream(fp);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
} else {
String path = "/WEB-INF/keycloak.json";
String pathParam = filterConfig.getInitParameter(CONFIG_PATH_PARAM);
if (pathParam != null) path = pathParam;
is = filterConfig.getServletContext().getResourceAsStream(path);
}
KeycloakDeployment kd = createKeycloakDeploymentFrom(is);
deploymentContext = new AdapterDeploymentContext(kd);
log.fine("Keycloak is using a per-deployment configuration.");
}
}
filterConfig.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
nodesRegistrationManagement = new NodesRegistrationManagement();
}
private KeycloakDeployment createKeycloakDeploymentFrom(InputStream is) {
if (is == null) {
log.fine("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
return new KeycloakDeployment();
}
return KeycloakDeploymentBuilder.build(is);
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
log.fine("Keycloak OIDC Filter");
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (shouldSkip(request)) {
chain.doFilter(req, res);
return;
}
OIDCServletHttpFacade facade = new OIDCServletHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) {
response.sendError(403);
log.fine("deployment not configured");
return;
}
PreAuthActionsHandler preActions = new PreAuthActionsHandler(new IdMapperUserSessionManagement(), deploymentContext, facade);
if (preActions.handleRequest()) {
//System.err.println("**************** preActions.handleRequest happened!");
return;
}
nodesRegistrationManagement.tryRegister(deployment);
OIDCFilterSessionStore tokenStore = new OIDCFilterSessionStore(request, facade, 100000, deployment, idMapper);
tokenStore.checkCurrentToken();
FilterRequestAuthenticator authenticator = new FilterRequestAuthenticator(deployment, tokenStore, facade, request, 8443);
AuthOutcome outcome = authenticator.authenticate();
if (outcome == AuthOutcome.AUTHENTICATED) {
log.fine("AUTHENTICATED");
if (facade.isEnded()) {
return;
}
AuthenticatedActionsHandler actions = new AuthenticatedActionsHandler(deployment, facade);
if (actions.handledRequest()) {
return;
} else {
HttpServletRequestWrapper wrapper = tokenStore.buildWrapper();
chain.doFilter(wrapper, res);
return;
}
}
AuthChallenge challenge = authenticator.getChallenge();
if (challenge != null) {
log.fine("challenge");
challenge.challenge(facade);
return;
}
response.sendError(403);
}
/**
* Decides whether this {@link Filter} should skip the given {@link HttpServletRequest} based on the configured {@link KeycloakOIDCFilter#skipPattern}.
* Patterns are matched against the {@link HttpServletRequest#getRequestURI() requestURI} of a request without the context-path.
* A request for {@code /myapp/index.html} would be tested with {@code /index.html} against the skip pattern.
* Skipped requests will not be processed further by {@link KeycloakOIDCFilter} and immediately delegated to the {@link FilterChain}.
*
* @param request the request to check
* @return {@code true} if the request should not be handled,
* {@code false} otherwise.
*/
private boolean shouldSkip(HttpServletRequest request) {
if (skipPattern == null) {
return false;
}
String requestPath = request.getRequestURI().substring(request.getContextPath().length());
return skipPattern.matcher(requestPath).matches();
}
@Override
public void destroy() {
}
private class IdMapperUserSessionManagement implements UserSessionManagement {
@Override
public void logoutAll() {
if (idMapper != null) {
idMapper.clear();
}
}
@Override
public void logoutHttpSessions(List<String> ids) {
log.fine("**************** logoutHttpSessions");
//System.err.println("**************** logoutHttpSessions");
for (String id : ids) {
log.finest("removed idMapper: " + id);
idMapper.removeSession(id);
}
}
}
}

View file

@ -1,213 +0,0 @@
/*
* 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.adapters.servlet;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OidcKeycloakAccount;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.RequestAuthenticator;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.adapters.spi.SessionIdMapper;
import org.keycloak.common.util.DelegatingSerializationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.security.Principal;
import java.util.Set;
import java.util.logging.Logger;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OIDCFilterSessionStore extends FilterSessionStore implements AdapterTokenStore {
protected final KeycloakDeployment deployment;
private static final Logger log = Logger.getLogger("" + OIDCFilterSessionStore.class);
protected final SessionIdMapper idMapper;
public OIDCFilterSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer, KeycloakDeployment deployment, SessionIdMapper idMapper) {
super(request, facade, maxBuffer);
this.deployment = deployment;
this.idMapper = idMapper;
}
public HttpServletRequestWrapper buildWrapper() {
HttpSession session = request.getSession(false);
KeycloakAccount account = null;
if (session != null) {
account = (KeycloakAccount) session.getAttribute(KeycloakAccount.class.getName());
if (account == null) {
account = (KeycloakAccount) request.getAttribute(KeycloakAccount.class.getName());
}
}
if (account == null) {
account = (KeycloakAccount) request.getAttribute(KeycloakAccount.class.getName());
}
return buildWrapper(session, account);
}
@Override
public void checkCurrentToken() {
HttpSession httpSession = request.getSession(false);
if (httpSession == null) return;
SerializableKeycloakAccount account = (SerializableKeycloakAccount)httpSession.getAttribute(KeycloakAccount.class.getName());
if (account == null) {
return;
}
RefreshableKeycloakSecurityContext session = account.getKeycloakSecurityContext();
if (session == null) return;
// just in case session got serialized
if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
// FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
// not be updated
boolean success = session.refreshExpiredToken(false);
if (success && session.isActive()) return;
// Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
//log.fine("Cleanup and expire session " + httpSession.getId() + " after failed refresh");
cleanSession(httpSession);
httpSession.invalidate();
}
protected void cleanSession(HttpSession session) {
session.removeAttribute(KeycloakAccount.class.getName());
session.removeAttribute(KeycloakSecurityContext.class.getName());
clearSavedRequest(session);
}
@Override
public boolean isCached(RequestAuthenticator authenticator) {
HttpSession httpSession = request.getSession(false);
if (httpSession == null) return false;
SerializableKeycloakAccount account = (SerializableKeycloakAccount) httpSession.getAttribute(KeycloakAccount.class.getName());
if (account == null) {
return false;
}
log.fine("remote logged in already. Establish state from session");
RefreshableKeycloakSecurityContext securityContext = account.getKeycloakSecurityContext();
if (!deployment.getRealm().equals(securityContext.getRealm())) {
log.fine("Account from cookie is from a different realm than for the request.");
cleanSession(httpSession);
return false;
}
if (idMapper != null && !idMapper.hasSession(httpSession.getId())) {
log.fine("idMapper does not have session: " + httpSession.getId());
//System.err.println("idMapper does not have session: " + httpSession.getId());
cleanSession(httpSession);
return false;
}
securityContext.setCurrentRequestInfo(deployment, this);
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
needRequestRestore = restoreRequest();
return true;
}
public static class SerializableKeycloakAccount implements OidcKeycloakAccount, Serializable {
protected Set<String> roles;
protected Principal principal;
protected RefreshableKeycloakSecurityContext securityContext;
public SerializableKeycloakAccount(Set<String> roles, Principal principal, RefreshableKeycloakSecurityContext securityContext) {
this.roles = roles;
this.principal = principal;
this.securityContext = securityContext;
}
@Override
public Principal getPrincipal() {
return principal;
}
@Override
public Set<String> getRoles() {
return roles;
}
@Override
public RefreshableKeycloakSecurityContext getKeycloakSecurityContext() {
return securityContext;
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
DelegatingSerializationFilter.builder()
.addAllowedClass(OIDCFilterSessionStore.SerializableKeycloakAccount.class)
.addAllowedClass(RefreshableKeycloakSecurityContext.class)
.addAllowedClass(KeycloakSecurityContext.class)
.addAllowedClass(KeycloakPrincipal.class)
.setFilter(in);
in.defaultReadObject();
}
}
@Override
public void saveAccountInfo(OidcKeycloakAccount account) {
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) account.getKeycloakSecurityContext();
Set<String> roles = account.getRoles();
SerializableKeycloakAccount sAccount = new SerializableKeycloakAccount(roles, account.getPrincipal(), securityContext);
HttpSession httpSession = request.getSession();
httpSession.setAttribute(KeycloakAccount.class.getName(), sAccount);
httpSession.setAttribute(KeycloakSecurityContext.class.getName(), sAccount.getKeycloakSecurityContext());
if (idMapper != null) idMapper.map(account.getKeycloakSecurityContext().getToken().getSessionState(), account.getPrincipal().getName(), httpSession.getId());
//String username = securityContext.getToken().getSubject();
//log.fine("userSessionManagement.login: " + username);
}
@Override
public void logout() {
HttpSession httpSession = request.getSession(false);
if (httpSession != null) {
SerializableKeycloakAccount account = (SerializableKeycloakAccount) httpSession.getAttribute(KeycloakAccount.class.getName());
if (account != null) {
account.getKeycloakSecurityContext().logout(deployment);
}
cleanSession(httpSession);
}
}
@Override
public void servletRequestLogout() {
logout();
}
@Override
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
// no-op
}
}

View file

@ -1,40 +0,0 @@
/*
* 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.adapters.servlet;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OIDCServletHttpFacade extends ServletHttpFacade implements OIDCHttpFacade {
public OIDCServletHttpFacade(HttpServletRequest request, HttpServletResponse response) {
super(request, response);
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
}
}

View file

@ -17,10 +17,6 @@ include::java-adapter-config.adoc[]
endif::[] endif::[]
include::jboss-adapter.adoc[] include::jboss-adapter.adoc[]
ifeval::[{project_community}==true]
include::servlet-filter-adapter.adoc[]
endif::[]
ifeval::[{project_community}==true] ifeval::[{project_community}==true]
include::adapter-context.adoc[] include::adapter-context.adoc[]
include::adapter_error_handling.adoc[] include::adapter_error_handling.adoc[]

View file

@ -1,173 +0,0 @@
[[_servlet_filter_adapter]]
==== Java servlet filter adapter
include::adapter-deprecation-notice.adoc[]
If you are deploying your Java Servlet application on a platform where there is no {project_name} adapter you opt to use the servlet filter adapter.
This adapter works a bit differently than the other adapters. You do not define security constraints in web.xml.
Instead you define a filter mapping using the {project_name} servlet filter adapter to secure the url patterns you want to secure.
WARNING: Backchannel logout works a bit differently than the standard adapters.
Instead of invalidating the HTTP session it marks the session id as logged out.
There's no standard way to invalidate an HTTP session based on a session id.
[source,xml]
----
<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">
<module-name>application</module-name>
<filter>
<filter-name>Keycloak Filter</filter-name>
<filter-class>org.keycloak.adapters.servlet.KeycloakOIDCFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Keycloak Filter</filter-name>
<url-pattern>/keycloak/*</url-pattern>
<url-pattern>/protected/*</url-pattern>
</filter-mapping>
</web-app>
----
In the snippet above there are two url-patterns.
_/protected/*_ are the files we want protected, while the _/keycloak/*_ url-pattern handles callbacks from the {project_name} server.
If you need to exclude some paths beneath the configured `url-patterns` you can use the Filter init-param `keycloak.config.skipPattern` to configure
a regular expression that describes a path-pattern for which the keycloak filter should immediately delegate to the filter-chain.
By default no skipPattern is configured.
Patterns are matched against the `requestURI` without the `context-path`. Given the context-path `/myapp` a request for `/myapp/index.html` will be matched with `/index.html` against the skip pattern.
[source,xml]
----
<init-param>
<param-name>keycloak.config.skipPattern</param-name>
<param-value>^/(path1|path2|path3).*</param-value>
</init-param>
----
Note that you should configure your client in the {project_name} Admin Console with an Admin URL that points to a secured section covered by the filter's url-pattern.
The Admin URL will make callbacks to the Admin URL to do things like backchannel logout.
So, the Admin URL in this example should be `http[s]://hostname/{context-root}/keycloak`.
If you need to customize the session ID mapper, you can configure the fully qualified name of the class in the Filter init-param keycloak.config.idMapper. Session ID mapper is a mapper that is used to map user IDs and session IDs. By default org.keycloak.adapters.spi.InMemorySessionIdMapper is configured.
[source,xml]
----
<init-param>
<param-name>keycloak.config.idMapper</param-name>
<param-value>org.keycloak.adapters.spi.InMemorySessionIdMapper</param-value>
</init-param>
----
The {project_name} filter has the same configuration parameters as the other adapters except you must define them as filter init params instead of context params.
To use this filter, include this maven artifact in your WAR poms:
[source,xml,subs="attributes+"]
----
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
<version>{project_versionMvn}</version>
</dependency>
----
ifeval::[{project_community}==true]
===== Using on OSGi
The servlet filter adapter is packaged as an OSGi bundle, and thus is usable in a generic OSGi environment (R6 and above) with HTTP Service and HTTP Whiteboard.
====== Installation
The adapter and its dependencies are distributed as Maven artifacts, so you'll need either working Internet connection to access Maven Central, or have the artifacts cached in your local Maven repo.
If you are using Apache Karaf, you can simply install a feature from the Keycloak feature repo:
[source,subs="attributes+"]
----
karaf@root()> feature:repo-add mvn:org.keycloak/keycloak-osgi-features/{project_versionMvn}/xml/features
karaf@root()> feature:install keycloak-servlet-filter-adapter
----
For other OSGi runtimes, please refer to the runtime documentation on how to install the adapter bundle and its dependencies.
NOTE: If your OSGi platform is Apache Karaf with Pax Web, you should consider using https://access.redhat.com/products/red-hat-single-sign-on/[Red Hat Single Sign-On] 7.x JBoss Fuse 7 adapters instead.
====== Configuration
First, the adapter needs to be registered as a servlet filter with the OSGi HTTP Service. The most common ways to do this are programmatic (for example via bundle activator) and declarative (using OSGi annotations).
We recommend using the latter since it simplifies the process of dynamically registering and un-registering the filter:
[source,java]
----
package mypackage;
import javax.servlet.Filter;
import org.keycloak.adapters.servlet.KeycloakOIDCFilter;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
@Component(
immediate = true,
service = Filter.class,
property = {
KeycloakOIDCFilter.CONFIG_FILE_PARAM + "=" + "keycloak.json",
HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN + "=" +"/*",
HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT + "=" + "(osgi.http.whiteboard.context.name=mycontext)"
}
)
public class KeycloakFilter extends KeycloakOIDCFilter {
//
}
----
The above snippet uses OSGi declarative service specification to expose the filter as an OSGI service under `javax.servlet.Filter` class.
Once the class is published in the OSGi service registry, it is going to be picked up by OSGi HTTP Service implementation and used for filtering requests for the specified servlet context. This will trigger Keycloak adapter for every request that matches servlet context path + filter path.
Since the component is put under the control of OSGi Configuration Admin Service, it's properties can be configured dynamically.
To do that, either create a `mypackage.KeycloakFilter.cfg` file under the standard config location for your OSGi runtime:
[source]
----
keycloak.config.file = /path/to/keycloak.json
osgi.http.whiteboard.filter.pattern = /secure/*
----
or use interactive console, if your runtime allows for that:
[source]
----
karaf@root()> config:edit mypackage.KeycloakFilter
karaf@root()> config:property-set keycloak.config.file '${karaf.etc}/keycloak.json'
karaf@root()> config:update
----
If you need more control, for example, providing custom `KeycloakConfigResolver` to implement <<_multi_tenancy,multi tenancy>>, you can register the filter programmatically:
[source,java]
----
public class Activator implements BundleActivator {
private ServiceRegistration registration;
public void start(BundleContext context) throws Exception {
Hashtable props = new Hashtable();
props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/secure/*");
props.put(KeycloakOIDCFilter.CONFIG_RESOLVER_PARAM, new MyConfigResolver());
this.registration = context.registerService(Filter.class.getName(), new KeycloakOIDCFilter(), props);
}
public void stop(BundleContext context) throws Exception {
this.registration.unregister();
}
}
----
Please refer to https://github.com/apache/felix-dev/tree/master/http#using-the-osgi-http-whiteboard[Apache Felix HTTP Service] for more info on programmatic registration.
endif::[]

15
pom.xml
View file

@ -1044,11 +1044,6 @@
<artifactId>keycloak-as7-adapter-spi</artifactId> <artifactId>keycloak-as7-adapter-spi</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-jboss-adapter-core</artifactId> <artifactId>keycloak-jboss-adapter-core</artifactId>
@ -1084,16 +1079,6 @@
<artifactId>keycloak-saml-jakarta-servlet-filter-adapter</artifactId> <artifactId>keycloak-saml-jakarta-servlet-filter-adapter</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jakarta-servlet-filter-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-as7-adapter</artifactId> <artifactId>keycloak-saml-as7-adapter</artifactId>

View file

@ -75,10 +75,6 @@
<artifactId>integration-arquillian-testsuite-providers</artifactId> <artifactId>integration-arquillian-testsuite-providers</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jakarta-servlet-filter-adapter</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId> <artifactId>keycloak-services</artifactId>

View file

@ -30,7 +30,6 @@ import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.resolver.api.maven.Maven; import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.jboss.shrinkwrap.resolver.api.maven.MavenFormatStage; import org.jboss.shrinkwrap.resolver.api.maven.MavenFormatStage;
import org.jboss.shrinkwrap.resolver.api.maven.MavenResolverSystem; import org.jboss.shrinkwrap.resolver.api.maven.MavenResolverSystem;
import org.keycloak.adapters.servlet.KeycloakOIDCFilter;
import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.testsuite.utils.annotation.UseServletFilter; import org.keycloak.testsuite.utils.annotation.UseServletFilter;
import org.keycloak.testsuite.utils.io.IOUtil; import org.keycloak.testsuite.utils.io.IOUtil;
@ -249,10 +248,6 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
filter.appendChild(initParam); filter.appendChild(initParam);
} }
appendChildInDocument(webXmlDoc, "web-app", filter);
addInitParam(webXmlDoc, filter, KeycloakOIDCFilter.SKIP_PATTERN_PARAM, testClass.getAnnotation(UseServletFilter.class).skipPattern());
addInitParam(webXmlDoc, filter, KeycloakOIDCFilter.ID_MAPPER_PARAM, testClass.getAnnotation(UseServletFilter.class).idMapper());
appendChildInDocument(webXmlDoc, "web-app", filter); appendChildInDocument(webXmlDoc, "web-app", filter);
Element filterMapping = webXmlDoc.createElement("filter-mapping"); Element filterMapping = webXmlDoc.createElement("filter-mapping");

View file

@ -1,96 +0,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.
*/
package org.keycloak.testsuite.adapter.servlet;
import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
import org.keycloak.testsuite.utils.annotation.UseServletFilter;
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
@AppServerContainer(ContainerConstants.APP_SERVER_EAP6)
@AppServerContainer(ContainerConstants.APP_SERVER_EAP71)
@UseServletFilter(filterName = "oidc-filter", filterClass = "org.keycloak.adapters.servlet.KeycloakOIDCFilter",
filterDependency = "org.keycloak:keycloak-servlet-filter-adapter", skipPattern = "/error.html")
public class DemoFilterServletAdapterTest extends DemoServletsAdapterTest {
@Test
@Override
@Ignore(value = "Can't test because of the way filter works")
public void testNullBearerTokenCustomErrorPage() {
}
@Test
@Override
@Ignore(value = "Don't need to test this because HttpServletRequest.authenticate "
+ "doesn't make sense with filter implementation")
public void testAuthenticated() {
}
@Test
@Override
@Ignore
public void testAuthenticatedWithCustomSessionConfig() {
}
@Test
@Override
@Ignore
public void testOIDCParamsForwarding() {
}
@Test
@Override
@Ignore
public void testOIDCUiLocalesParamForwarding() {
}
@Test
@Override
@Ignore
public void testTokenInCookieSSO() {
}
@Test
@Override
@Ignore
public void testInvalidTokenCookie() {
}
@Test
@Override
@Ignore
public void testTokenInCookieRefresh() {
}
@Test
@Override
@Ignore
public void testTokenInCookieSSORoot() {
}
}

View file

@ -1,67 +0,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.
*/
package org.keycloak.testsuite.adapter.servlet;
import org.junit.Test;
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
import org.keycloak.testsuite.adapter.page.CustomerPortal;
import org.keycloak.testsuite.adapter.spi.TestSessionIdMapper;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
import org.keycloak.testsuite.util.JavascriptBrowser;
import org.keycloak.testsuite.utils.annotation.UseServletFilter;
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
import org.openqa.selenium.WebDriver;
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.spec.WebArchive;
import static org.junit.Assert.assertTrue;
@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
/* Execute with JBoss-based app servers after resolving this issue: https://github.com/keycloak/keycloak/issues/19809
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
@AppServerContainer(ContainerConstants.APP_SERVER_EAP6)
@AppServerContainer(ContainerConstants.APP_SERVER_EAP71)
*/
@UseServletFilter(filterName = "oidc-filter", filterClass = "org.keycloak.adapters.servlet.KeycloakOIDCFilter",
filterDependency = "org.keycloak:keycloak-servlet-filter-adapter", skipPattern = "/error.html",
idMapper = "org.keycloak.testsuite.adapter.spi.TestSessionIdMapper")
public class DemoFilterServletAdapterTestForCustomizedIdMapper extends AbstractServletsAdapterTest {
@Drone
@JavascriptBrowser
protected WebDriver jsDriver;
@Page
protected CustomerPortal customerPortal;
@Deployment(name = CustomerPortal.DEPLOYMENT_NAME)
protected static WebArchive customerPortal() {
return servletDeployment(CustomerPortal.DEPLOYMENT_NAME, CustomerServlet.class, ErrorServlet.class, ServletTestUtils.class);
}
// KEYCLOAK-13745
@Test
public void testCustomizedSessionIdMapper() {
customerPortal.navigateTo();
TestSessionIdMapper singleton = TestSessionIdMapper.getInstance();
assertTrue(singleton.isCalledBy(getClass().getAnnotation(UseServletFilter.class).filterClass()));
singleton.clear();
}
}

View file

@ -1,34 +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.testsuite.adapter.servlet.cluster;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
import org.keycloak.testsuite.utils.annotation.UseServletFilter;
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY_CLUSTER)
@AppServerContainer(ContainerConstants.APP_SERVER_EAP_CLUSTER)
@AppServerContainer(ContainerConstants.APP_SERVER_EAP6_CLUSTER)
@UseServletFilter(filterName = "oidc-filter", filterClass = "org.keycloak.adapters.servlet.KeycloakOIDCFilter",
filterDependency = "org.keycloak:keycloak-servlet-filter-adapter", skipPattern = "/error.html")
public class OIDCFilterAdapterClusterTest extends OIDCAdapterClusterTest {
}

View file

@ -38,10 +38,6 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak.testsuite</groupId> <groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-testsuite-providers</artifactId> <artifactId>integration-arquillian-testsuite-providers</artifactId>

View file

@ -23,7 +23,6 @@ import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.asset.Asset; import org.jboss.shrinkwrap.api.asset.Asset;
import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.keycloak.adapters.servlet.KeycloakOIDCFilter;
import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.testsuite.utils.annotation.UseServletFilter; import org.keycloak.testsuite.utils.annotation.UseServletFilter;
import org.keycloak.testsuite.utils.io.IOUtil; import org.keycloak.testsuite.utils.io.IOUtil;
@ -127,12 +126,6 @@ public class DeploymentArchiveProcessorUtils {
filter.appendChild(initParam); filter.appendChild(initParam);
} }
// Limitation that all deployments of annotated class use same skipPattern. Refactor if
// something more flexible is needed (would require more tricky web.xml parsing though...)
addInitParam(webXmlDoc, filter, KeycloakOIDCFilter.SKIP_PATTERN_PARAM, testClass.getAnnotation(UseServletFilter.class).skipPattern());
addInitParam(webXmlDoc, filter, KeycloakOIDCFilter.ID_MAPPER_PARAM, testClass.getAnnotation(UseServletFilter.class).idMapper());
IOUtil.appendChildInDocument(webXmlDoc, "web-app", filter); IOUtil.appendChildInDocument(webXmlDoc, "web-app", filter);
Element filterMapping = webXmlDoc.createElement("filter-mapping"); Element filterMapping = webXmlDoc.createElement("filter-mapping");

View file

@ -186,19 +186,11 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-authz-client</artifactId> <artifactId>keycloak-authz-client</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak.testsuite</groupId> <groupId>org.keycloak.testsuite</groupId>
<artifactId>keycloak-saml-undertow-adapter-jakarta</artifactId> <artifactId>keycloak-saml-undertow-adapter-jakarta</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
</dependency>
<!-- Dependency on services from integration-arquillian --> <!-- Dependency on services from integration-arquillian -->
<dependency> <dependency>
<groupId>org.keycloak.testsuite</groupId> <groupId>org.keycloak.testsuite</groupId>