Remove servlet filter saml adapters
Closes #28786 Signed-off-by: Douglas Palmer <dpalmer@redhat.com>
This commit is contained in:
parent
a4a7d023a7
commit
b2f09feebf
17 changed files with 0 additions and 845 deletions
|
@ -36,7 +36,6 @@
|
|||
<module>core-jakarta</module>
|
||||
<module>undertow</module>
|
||||
<module>wildfly</module>
|
||||
<module>servlet-filter</module>
|
||||
<module>jakarta-servlet-filter</module>
|
||||
<module>wildfly-elytron</module>
|
||||
<module>wildfly-elytron-jakarta</module>
|
||||
|
|
|
@ -1,82 +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-saml-servlet-filter-adapter</artifactId>
|
||||
<name>Keycloak SAML Servlet Filter</name>
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-common</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.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-adapter-api-public</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-adapter-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-crypto-default</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>
|
||||
|
||||
</project>
|
|
@ -1,176 +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.saml.servlet;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.saml.SamlDeployment;
|
||||
import org.keycloak.adapters.saml.SamlSession;
|
||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
||||
import org.keycloak.adapters.saml.SamlUtil;
|
||||
import org.keycloak.adapters.servlet.FilterSessionStore;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.KeycloakAccount;
|
||||
import org.keycloak.adapters.spi.SessionIdMapper;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FilterSamlSessionStore extends FilterSessionStore implements SamlSessionStore {
|
||||
protected static Logger log = Logger.getLogger(SamlSessionStore.class);
|
||||
protected final SessionIdMapper idMapper;
|
||||
private final SamlDeployment deployment;
|
||||
|
||||
public FilterSamlSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer, SessionIdMapper idMapper, SamlDeployment deployment) {
|
||||
super(request, facade, maxBuffer);
|
||||
this.idMapper = idMapper;
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentAction(CurrentAction action) {
|
||||
if (action == CurrentAction.NONE && request.getSession(false) == null) return;
|
||||
request.getSession().setAttribute(CURRENT_ACTION, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggingIn() {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) return false;
|
||||
CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
|
||||
return action == CurrentAction.LOGGING_IN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggingOut() {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) return false;
|
||||
CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
|
||||
return action == CurrentAction.LOGGING_OUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logoutAccount() {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) return;
|
||||
if (session != null) {
|
||||
if (idMapper != null) idMapper.removeSession(session.getId());
|
||||
SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
|
||||
if (samlSession != null) {
|
||||
session.removeAttribute(SamlSession.class.getName());
|
||||
}
|
||||
clearSavedRequest(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logoutByPrincipal(String principal) {
|
||||
SamlSession account = getAccount();
|
||||
if (account != null && account.getPrincipal().getSamlSubject().equals(principal)) {
|
||||
logoutAccount();
|
||||
}
|
||||
if (idMapper != null) {
|
||||
Set<String> sessions = idMapper.getUserSessions(principal);
|
||||
if (sessions != null) {
|
||||
List<String> ids = new LinkedList<String>();
|
||||
ids.addAll(sessions);
|
||||
for (String id : ids) {
|
||||
idMapper.removeSession(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logoutBySsoId(List<String> ssoIds) {
|
||||
SamlSession account = getAccount();
|
||||
for (String ssoId : ssoIds) {
|
||||
if (account != null && account.getSessionIndex().equals(ssoId)) {
|
||||
logoutAccount();
|
||||
} else if (idMapper != null) {
|
||||
String sessionId = idMapper.getSessionFromSSO(ssoId);
|
||||
idMapper.removeSession(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggedIn() {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) {
|
||||
log.debug("session was null, returning false");
|
||||
return false;
|
||||
}
|
||||
final SamlSession samlSession = SamlUtil.validateSamlSession(session.getAttribute(SamlSession.class.getName()), deployment);
|
||||
if (samlSession == null) {
|
||||
log.debug("SamlSession was not in session, returning null");
|
||||
return false;
|
||||
}
|
||||
if (idMapper != null && !idMapper.hasSession(session.getId())) {
|
||||
logoutAccount();
|
||||
return false;
|
||||
}
|
||||
|
||||
needRequestRestore = restoreRequest();
|
||||
return true;
|
||||
}
|
||||
|
||||
public HttpServletRequestWrapper getWrap() {
|
||||
HttpSession session = request.getSession(true);
|
||||
final SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
|
||||
final KeycloakAccount account = samlSession;
|
||||
return buildWrapper(session, account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAccount(SamlSession account) {
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute(SamlSession.class.getName(), account);
|
||||
if (idMapper != null) idMapper.map(account.getSessionIndex(), account.getPrincipal().getSamlSubject(), session.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SamlSession getAccount() {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) return null;
|
||||
return (SamlSession)session.getAttribute(SamlSession.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) return null;
|
||||
String redirect = (String)session.getAttribute(REDIRECT_URI);
|
||||
if (redirect == null) {
|
||||
String contextPath = request.getContextPath();
|
||||
String baseUri = KeycloakUriBuilder.fromUri(request.getRequestURL().toString()).replacePath(contextPath).build().toString();
|
||||
return SamlUtil.getRedirectTo(facade, contextPath, baseUri);
|
||||
}
|
||||
return redirect;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,220 +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.saml.servlet;
|
||||
|
||||
import org.keycloak.adapters.saml.DefaultSamlDeployment;
|
||||
import org.keycloak.adapters.saml.SamlAuthenticator;
|
||||
import org.keycloak.adapters.saml.SamlConfigResolver;
|
||||
import org.keycloak.adapters.saml.SamlDeployment;
|
||||
import org.keycloak.adapters.saml.SamlDeploymentContext;
|
||||
import org.keycloak.adapters.saml.SamlSession;
|
||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
||||
import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
|
||||
import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
|
||||
import org.keycloak.adapters.saml.profile.SamlAuthenticationHandler;
|
||||
import org.keycloak.adapters.saml.profile.webbrowsersso.BrowserHandler;
|
||||
import org.keycloak.adapters.saml.profile.webbrowsersso.SamlEndpoint;
|
||||
import org.keycloak.adapters.servlet.ServletHttpFacade;
|
||||
import org.keycloak.adapters.spi.AuthChallenge;
|
||||
import org.keycloak.adapters.spi.AuthOutcome;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.InMemorySessionIdMapper;
|
||||
import org.keycloak.adapters.spi.SessionIdMapper;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
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.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 SamlFilter implements Filter {
|
||||
protected SamlDeploymentContext deploymentContext;
|
||||
protected SessionIdMapper idMapper;
|
||||
private final static Logger log = Logger.getLogger("" + SamlFilter.class);
|
||||
private static final Pattern PROTOCOL_PATTERN = Pattern.compile("^[a-zA-Z][a-zA-Z0-9+.-]*:");
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
deploymentContext = (SamlDeploymentContext)filterConfig.getServletContext().getAttribute(SamlDeploymentContext.class.getName());
|
||||
if (deploymentContext != null) {
|
||||
idMapper = (SessionIdMapper)filterConfig.getServletContext().getAttribute(SessionIdMapper.class.getName());
|
||||
return;
|
||||
}
|
||||
String configResolverClass = filterConfig.getInitParameter("keycloak.config.resolver");
|
||||
if (configResolverClass != null) {
|
||||
try {
|
||||
SamlConfigResolver configResolver = (SamlConfigResolver) getClass().getClassLoader().loadClass(configResolverClass).newInstance();
|
||||
deploymentContext = new SamlDeploymentContext(configResolver);
|
||||
log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
|
||||
} catch (Exception ex) {
|
||||
log.log(Level.WARNING, "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 SamlDeploymentContext(new DefaultSamlDeployment());
|
||||
}
|
||||
} else {
|
||||
String fp = filterConfig.getInitParameter("keycloak.config.file");
|
||||
InputStream is = null;
|
||||
if (fp != null) {
|
||||
try {
|
||||
is = new FileInputStream(fp);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
String path = "/WEB-INF/keycloak-saml.xml";
|
||||
String pathParam = filterConfig.getInitParameter("keycloak.config.path");
|
||||
if (pathParam != null)
|
||||
path = pathParam;
|
||||
is = filterConfig.getServletContext().getResourceAsStream(path);
|
||||
}
|
||||
final SamlDeployment deployment;
|
||||
if (is == null) {
|
||||
log.info("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
|
||||
deployment = new DefaultSamlDeployment();
|
||||
} else {
|
||||
try {
|
||||
ResourceLoader loader = new ResourceLoader() {
|
||||
@Override
|
||||
public InputStream getResourceAsStream(String resource) {
|
||||
return filterConfig.getServletContext().getResourceAsStream(resource);
|
||||
}
|
||||
};
|
||||
deployment = new DeploymentBuilder().build(is, loader);
|
||||
} catch (ParsingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
deploymentContext = new SamlDeploymentContext(deployment);
|
||||
log.fine("Keycloak is using a per-deployment configuration.");
|
||||
}
|
||||
idMapper = new InMemorySessionIdMapper();
|
||||
filterConfig.getServletContext().setAttribute(SamlDeploymentContext.class.getName(), deploymentContext);
|
||||
filterConfig.getServletContext().setAttribute(SessionIdMapper.class.getName(), idMapper);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
HttpServletResponse response = (HttpServletResponse) res;
|
||||
ServletHttpFacade facade = new ServletHttpFacade(request, response);
|
||||
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
if (deployment == null || !deployment.isConfigured()) {
|
||||
response.sendError(403);
|
||||
log.fine("deployment not configured");
|
||||
return;
|
||||
}
|
||||
FilterSamlSessionStore tokenStore = new FilterSamlSessionStore(request, facade, 100000, idMapper, deployment);
|
||||
boolean isEndpoint = request.getRequestURI().substring(request.getContextPath().length()).endsWith("/saml");
|
||||
SamlAuthenticator authenticator;
|
||||
if (isEndpoint) {
|
||||
authenticator = new SamlAuthenticator(facade, deployment, tokenStore) {
|
||||
@Override
|
||||
protected void completeAuthentication(SamlSession account) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SamlAuthenticationHandler createBrowserHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
|
||||
return new SamlEndpoint(facade, deployment, sessionStore);
|
||||
}
|
||||
};
|
||||
|
||||
} else {
|
||||
authenticator = new SamlAuthenticator(facade, deployment, tokenStore) {
|
||||
@Override
|
||||
protected void completeAuthentication(SamlSession account) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SamlAuthenticationHandler createBrowserHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
|
||||
return new BrowserHandler(facade, deployment, sessionStore);
|
||||
}
|
||||
};
|
||||
}
|
||||
AuthOutcome outcome = authenticator.authenticate();
|
||||
if (outcome == AuthOutcome.AUTHENTICATED) {
|
||||
log.fine("AUTHENTICATED");
|
||||
if (facade.isEnded()) {
|
||||
return;
|
||||
}
|
||||
HttpServletRequestWrapper wrapper = tokenStore.getWrap();
|
||||
chain.doFilter(wrapper, res);
|
||||
return;
|
||||
}
|
||||
if (outcome == AuthOutcome.LOGGED_OUT) {
|
||||
tokenStore.logoutAccount();
|
||||
String logoutPage = deployment.getLogoutPage();
|
||||
if (logoutPage != null) {
|
||||
if (PROTOCOL_PATTERN.matcher(logoutPage).find()) {
|
||||
response.sendRedirect(logoutPage);
|
||||
log.log(Level.FINE, "Redirected to logout page {0}", logoutPage);
|
||||
} else {
|
||||
RequestDispatcher disp = req.getRequestDispatcher(logoutPage);
|
||||
disp.forward(req, res);
|
||||
}
|
||||
return;
|
||||
}
|
||||
chain.doFilter(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
AuthChallenge challenge = authenticator.getChallenge();
|
||||
if (challenge != null) {
|
||||
log.fine("challenge");
|
||||
challenge.challenge(facade);
|
||||
return;
|
||||
}
|
||||
|
||||
if (deployment.isIsPassive() && outcome == AuthOutcome.NOT_AUTHENTICATED) {
|
||||
log.fine("PASSIVE_NOT_AUTHENTICATED");
|
||||
if (facade.isEnded()) {
|
||||
return;
|
||||
}
|
||||
chain.doFilter(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!facade.isEnded()) {
|
||||
response.sendError(403);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ ifeval::[{project_community}==true]
|
|||
* {quickstartRepo_link}/tree/latest/jakarta/servlet-authz-client[Wildfly Elytron OIDC]
|
||||
* {quickstartRepo_link}/tree/latest/spring/rest-authz-resource-server[Spring Boot]
|
||||
* <<_jboss_adapter, {project_name} Wildfly Adapter>> (Deprecated)
|
||||
* <<_servlet_filter_adapter,{project_name} Servlet Filter>> (Deprecated)
|
||||
endif::[]
|
||||
|
||||
===== JavaScript (client-side)
|
||||
|
@ -49,9 +48,6 @@ endif::[]
|
|||
ifeval::[{project_community}==true]
|
||||
* <<_saml_jboss_adapter,WildFly>>
|
||||
endif::[]
|
||||
ifeval::[{project_community}==true]
|
||||
* <<_java-servlet-filter-adapter,Servlet filter>>
|
||||
endif::[]
|
||||
|
||||
ifeval::[{project_community}==true]
|
||||
===== Apache HTTP Server
|
||||
|
|
|
@ -25,7 +25,6 @@ include::jboss-adapter/securing_wars.adoc[]
|
|||
ifeval::[{project_community}==true]
|
||||
endif::[]
|
||||
|
||||
include::servlet-filter-adapter.adoc[]
|
||||
include::idp-registration.adoc[]
|
||||
include::logout.adoc[]
|
||||
include::assertion-api.adoc[]
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
[[_java-servlet-filter-adapter]]
|
||||
==== Java Servlet filter adapter
|
||||
|
||||
If you want to use SAML with a Java servlet application that doesn't have an adapter for that servlet platform, you can
|
||||
opt to use the servlet filter adapter that {project_name} has.
|
||||
This adapter works a little differently than the other adapters.
|
||||
You still have to specify a `/WEB-INF/keycloak-saml.xml` file as defined in
|
||||
the <<_saml-general-config,General Adapter Config>> section, but
|
||||
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.
|
||||
|
||||
NOTE: Backchannel logout works a bit differently than the standard adapters.
|
||||
Instead of invalidating the http session it instead marks the session ID as logged out.
|
||||
There's just no way of arbitrarily invalidating an HTTP session based on a session ID.
|
||||
|
||||
WARNING: Backchannel logout does not currently work when you have a clustered application that uses the SAML filter.
|
||||
|
||||
[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>customer-portal</module-name>
|
||||
|
||||
<filter>
|
||||
<filter-name>Keycloak Filter</filter-name>
|
||||
<filter-class>org.keycloak.adapters.saml.servlet.SamlFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>Keycloak Filter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
</web-app>
|
||||
----
|
||||
|
||||
The {project_name} filter has the same configuration parameters available as the other adapters except you must
|
||||
define them as filter init params instead of context params.
|
||||
|
||||
You can define multiple filter mappings if you have various different secure and unsecure url patterns.
|
||||
|
||||
WARNING: You must have a filter mapping that covers `/saml`.
|
||||
This mapping covers all server callbacks.
|
||||
|
||||
When registering SPs with an IdP, you must register `http[s]://hostname/{context-root}/saml` as your Assert Consumer Service URL and Single Logout Service URL.
|
||||
|
||||
To use this filter, include this maven artifact in your WAR poms:
|
||||
|
||||
[source,xml,subs="attributes+"]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
|
||||
<version>{project_versionMvn}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
In order to use <<_saml_multi_tenancy,Multi Tenancy>> the `keycloak.config.resolver` parameter should be passed as a filter parameter.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<filter>
|
||||
<filter-name>Keycloak Filter</filter-name>
|
||||
<filter-class>org.keycloak.adapters.saml.servlet.SamlFilter</filter-class>
|
||||
<init-param>
|
||||
<param-name>keycloak.config.resolver</param-name>
|
||||
<param-value>example.SamlMultiTenantResolver</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
----
|
5
pom.xml
5
pom.xml
|
@ -1084,11 +1084,6 @@
|
|||
<artifactId>keycloak-js-adapter-jar</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-jakarta-servlet-filter-adapter</artifactId>
|
||||
|
|
|
@ -29,10 +29,6 @@
|
|||
|
||||
<artifactId>integration-arquillian-servers-app-server-jetty-common</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-servers-app-server-spi</artifactId>
|
||||
|
|
|
@ -38,10 +38,6 @@
|
|||
<artifactId>keycloak-saml-undertow-adapter-jakarta</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-servers-app-server-spi</artifactId>
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023 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.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.utils.annotation.UseServletFilter;
|
||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||
|
||||
@AppServerContainer(ContainerConstants.APP_SERVER_EAP8)
|
||||
@UseServletFilter(filterName = "saml-filter", filterClass = "org.keycloak.adapters.saml.servlet.SamlFilter",
|
||||
filterDependency = "org.keycloak:keycloak-saml-jakarta-servlet-filter-adapter")
|
||||
public class SAMLFilterJakartaLoginResponseHandlingTest extends SAMLFilterLoginResponseHandlingTest {
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
@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 = "saml-filter", filterClass = "org.keycloak.adapters.saml.servlet.SamlFilter",
|
||||
filterDependency = "org.keycloak:keycloak-saml-servlet-filter-adapter")
|
||||
public class SAMLFilterLoginResponseHandlingTest extends SAMLLoginResponseHandlingTest {
|
||||
@Test
|
||||
@Override
|
||||
@Ignore
|
||||
public void testErrorHandlingUnsigned() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
@Ignore
|
||||
public void testErrorHandlingSigned() {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
package org.keycloak.testsuite.adapter.servlet;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.util.ContainerAssume;
|
||||
import org.keycloak.testsuite.utils.annotation.UseServletFilter;
|
||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
@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 = "saml-filter", filterClass = "org.keycloak.adapters.saml.servlet.SamlFilter",
|
||||
filterDependency = "org.keycloak:keycloak-saml-servlet-filter-adapter")
|
||||
public class SAMLFilterServletAdapterTest extends SAMLServletAdapterTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() {
|
||||
String appServerJavaHome = System.getProperty("app.server.java.home", "");
|
||||
Assume.assumeFalse(appServerJavaHome.contains("1.7") || appServerJavaHome.contains("ibm-java-70"));
|
||||
|
||||
// SAMLServletAdapterTest has too many deployments, with so many deployments (with filter dependency in each
|
||||
// of them) it is impossible to reload container after TLS is enabled, GC time limit exceeds
|
||||
ContainerAssume.assumeNotAppServerSSL();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void checkRoles() {
|
||||
badClientSalesPostSigServletPage.checkRoles(true);
|
||||
badRealmSalesPostSigServletPage.checkRoles(true);
|
||||
employeeAcsServletPage.checkRoles(true);
|
||||
employeeSigServletPage.checkRoles(true);
|
||||
employeeSigFrontServletPage.checkRoles(true);
|
||||
salesMetadataServletPage.checkRoles(true);
|
||||
salesPostServletPage.checkRoles(true);
|
||||
salesPostEncServletPage.checkRoles(true);
|
||||
salesPostEncSignAssertionsOnlyServletPage.checkRoles(true);
|
||||
salesPostSigServletPage.checkRoles(true);
|
||||
salesPostPassiveServletPage.checkRoles(true);
|
||||
salesPostSigPersistentServletPage.checkRoles(true);
|
||||
salesPostSigTransientServletPage.checkRoles(true);
|
||||
salesPostAssertionAndResponseSigPage.checkRoles(true);
|
||||
employeeSigPostNoIdpKeyServletPage.checkRoles(true);
|
||||
employeeSigRedirNoIdpKeyServletPage.checkRoles(true);
|
||||
employeeSigRedirOptNoIdpKeyServletPage.checkRoles(true);
|
||||
employeeRoleMappingPage.setupLoginInfo(testRealmSAMLPostLoginPage, bburkeUser);
|
||||
|
||||
//using endpoint instead of query param because we are not able to put query param to IDP initiated login
|
||||
employee2ServletPage.navigateTo();
|
||||
testRealmLoginPage.form().login(bburkeUser);
|
||||
employee2ServletPage.checkRolesEndPoint(true);
|
||||
employee2ServletPage.logout();
|
||||
|
||||
salesPostSigEmailServletPage.navigateTo();
|
||||
testRealmLoginPage.form().login(bburkeUser);
|
||||
salesPostSigEmailServletPage.checkRolesEndPoint(true);
|
||||
salesPostSigEmailServletPage.logout();
|
||||
}
|
||||
|
||||
@After
|
||||
public void uncheckRoles() {
|
||||
badClientSalesPostSigServletPage.checkRoles(false);
|
||||
badRealmSalesPostSigServletPage.checkRoles(false);
|
||||
employeeAcsServletPage.checkRoles(false);
|
||||
employee2ServletPage.checkRoles(false);
|
||||
employeeSigServletPage.checkRoles(false);
|
||||
employeeSigFrontServletPage.checkRoles(false);
|
||||
salesMetadataServletPage.checkRoles(false);
|
||||
salesPostServletPage.checkRoles(false);
|
||||
salesPostEncServletPage.checkRoles(false);
|
||||
salesPostEncSignAssertionsOnlyServletPage.checkRoles(false);
|
||||
salesPostSigServletPage.checkRoles(false);
|
||||
salesPostPassiveServletPage.checkRoles(false);
|
||||
salesPostSigEmailServletPage.checkRoles(false);
|
||||
salesPostSigPersistentServletPage.checkRoles(false);
|
||||
salesPostSigTransientServletPage.checkRoles(false);
|
||||
employeeSigPostNoIdpKeyServletPage.checkRoles(false);
|
||||
employeeSigRedirNoIdpKeyServletPage.checkRoles(false);
|
||||
employeeSigRedirOptNoIdpKeyServletPage.checkRoles(false);
|
||||
employeeRoleMappingPage.clearLoginInfo();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
@Ignore
|
||||
public void testSavedPostRequest() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
@Ignore
|
||||
public void multiTenant1SamlTest() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
@Ignore
|
||||
public void multiTenant2SamlTest() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the adapter is using the configured role mappings provider to map the roles extracted from the assertion
|
||||
* into roles that exist in the application domain. For this test a {@link org.keycloak.adapters.saml.PropertiesBasedRoleMapper}
|
||||
* has been setup in the adapter, performing the mappings as specified in the {@code role-mappings.properties} file.
|
||||
*
|
||||
* @throws Exception if an error occurs while running the test.
|
||||
*/
|
||||
@Test
|
||||
@Override
|
||||
public void testAdapterRoleMappings() throws Exception {
|
||||
try {
|
||||
employeeRoleMappingPage.setRolesToCheck("manager,coordinator,team-lead,employee");
|
||||
super.testAdapterRoleMappings();
|
||||
} finally {
|
||||
employeeRoleMappingPage.checkRolesEndPoint(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023 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.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.utils.annotation.UseServletFilter;
|
||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||
|
||||
@AppServerContainer(ContainerConstants.APP_SERVER_EAP8)
|
||||
@UseServletFilter(filterName = "saml-filter", filterClass = "org.keycloak.adapters.saml.servlet.SamlFilter",
|
||||
filterDependency = "org.keycloak:keycloak-saml-jakarta-servlet-filter-adapter")
|
||||
public class SAMLFilterServletJakartaAdapterTest extends SAMLFilterServletAdapterTest {
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023 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.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.utils.annotation.UseServletFilter;
|
||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||
|
||||
@AppServerContainer(ContainerConstants.APP_SERVER_EAP8)
|
||||
@UseServletFilter(filterName = "saml-filter", filterClass = "org.keycloak.adapters.saml.servlet.SamlFilter",
|
||||
filterDependency = "org.keycloak:keycloak-saml-jakarta-servlet-filter-adapter")
|
||||
public class SAMLFilterServletJakartaSessionTimeoutTest extends SAMLFilterServletSessionTimeoutTest {
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package org.keycloak.testsuite.adapter.servlet;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.utils.annotation.UseServletFilter;
|
||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
@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 = "saml-filter", filterClass = "org.keycloak.adapters.saml.servlet.SamlFilter",
|
||||
filterDependency = "org.keycloak:keycloak-saml-servlet-filter-adapter")
|
||||
public class SAMLFilterServletSessionTimeoutTest extends SAMLServletSessionTimeoutTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() {
|
||||
String appServerJavaHome = System.getProperty("app.server.java.home", "");
|
||||
Assume.assumeFalse(appServerJavaHome.contains("1.7") || appServerJavaHome.contains("ibm-java-70"));
|
||||
}
|
||||
}
|
|
@ -186,10 +186,6 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-authz-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-servlet-filter-adapter</artifactId>
|
||||
|
|
Loading…
Reference in a new issue