Remove the SAML undertow adapter
Closes #30554 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
6b07b67667
commit
f690947cea
30 changed files with 2 additions and 1530 deletions
|
@ -34,7 +34,6 @@
|
||||||
<module>core-public</module>
|
<module>core-public</module>
|
||||||
<module>core</module>
|
<module>core</module>
|
||||||
<module>core-jakarta</module>
|
<module>core-jakarta</module>
|
||||||
<module>undertow</module>
|
|
||||||
<module>wildfly</module>
|
<module>wildfly</module>
|
||||||
<module>wildfly-elytron</module>
|
<module>wildfly-elytron</module>
|
||||||
<module>wildfly-elytron-jakarta</module>
|
<module>wildfly-elytron-jakarta</module>
|
||||||
|
|
|
@ -1,91 +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-undertow-adapter</artifactId>
|
|
||||||
<name>Keycloak Undertow SAML Adapter</name>
|
|
||||||
<description/>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-core</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-adapter-spi</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-common</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-adapter-api-public</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-adapter-core</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jboss.logging</groupId>
|
|
||||||
<artifactId>jboss-logging</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>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-servlet</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-core</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,175 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.AuthenticationMechanism;
|
|
||||||
import io.undertow.security.api.NotificationReceiver;
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.security.api.SecurityNotification;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.util.AttachmentKey;
|
|
||||||
import io.undertow.util.Headers;
|
|
||||||
import io.undertow.util.StatusCodes;
|
|
||||||
import org.keycloak.adapters.saml.SamlAuthenticator;
|
|
||||||
import org.keycloak.adapters.saml.SamlDeployment;
|
|
||||||
import org.keycloak.adapters.saml.SamlDeploymentContext;
|
|
||||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
|
||||||
import org.keycloak.adapters.spi.AuthChallenge;
|
|
||||||
import org.keycloak.adapters.spi.AuthOutcome;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
import org.keycloak.adapters.undertow.UndertowHttpFacade;
|
|
||||||
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract base class for a Keycloak-enabled Undertow AuthenticationMechanism.
|
|
||||||
*
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
|
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(AbstractSamlAuthMech.class.getName());
|
|
||||||
|
|
||||||
public static final AttachmentKey<AuthChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(AuthChallenge.class);
|
|
||||||
protected SamlDeploymentContext deploymentContext;
|
|
||||||
protected UndertowUserSessionManagement sessionManagement;
|
|
||||||
protected String errorPage;
|
|
||||||
|
|
||||||
public AbstractSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
|
|
||||||
this.deploymentContext = deploymentContext;
|
|
||||||
this.sessionManagement = sessionManagement;
|
|
||||||
this.errorPage = errorPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
|
|
||||||
AuthChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
|
|
||||||
if (challenge != null) {
|
|
||||||
UndertowHttpFacade facade = createFacade(exchange);
|
|
||||||
if (challenge.challenge(facade)) {
|
|
||||||
return new ChallengeResult(true, exchange.getResponseCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new ChallengeResult(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Integer servePage(final HttpServerExchange exchange, final String location) {
|
|
||||||
sendRedirect(exchange, location);
|
|
||||||
return StatusCodes.TEMPORARY_REDIRECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Pattern PROTOCOL_PATTERN = Pattern.compile("^[a-zA-Z][a-zA-Z0-9+.-]*:");
|
|
||||||
|
|
||||||
static void sendRedirect(final HttpServerExchange exchange, final String location) {
|
|
||||||
if (location == null) {
|
|
||||||
LOG.log(Level.WARNING, "Logout page not set.");
|
|
||||||
exchange.setStatusCode(StatusCodes.NOT_FOUND);
|
|
||||||
exchange.endExchange();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PROTOCOL_PATTERN.matcher(location).find()) {
|
|
||||||
exchange.getResponseHeaders().put(Headers.LOCATION, location);
|
|
||||||
} else {
|
|
||||||
String loc = exchange.getRequestScheme() + "://" + exchange.getHostAndPort() + location;
|
|
||||||
exchange.getResponseHeaders().put(Headers.LOCATION, loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void registerNotifications(final SecurityContext securityContext) {
|
|
||||||
|
|
||||||
final NotificationReceiver logoutReceiver = new NotificationReceiver() {
|
|
||||||
@Override
|
|
||||||
public void handleNotification(SecurityNotification notification) {
|
|
||||||
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
HttpServerExchange exchange = notification.getExchange();
|
|
||||||
UndertowHttpFacade facade = createFacade(exchange);
|
|
||||||
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
|
|
||||||
SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
|
|
||||||
sessionStore.logoutAccount();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
securityContext.registerNotificationReceiver(logoutReceiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this inside your authenticate method.
|
|
||||||
*/
|
|
||||||
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
|
||||||
UndertowHttpFacade facade = createFacade(exchange);
|
|
||||||
SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
|
|
||||||
if (!deployment.isConfigured()) {
|
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
|
||||||
}
|
|
||||||
SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
|
|
||||||
SamlAuthenticator authenticator = null;
|
|
||||||
if (exchange.getRequestPath().endsWith("/saml")) {
|
|
||||||
authenticator = new UndertowSamlEndpoint(facade, deploymentContext.resolveDeployment(facade), sessionStore);
|
|
||||||
} else {
|
|
||||||
authenticator = new UndertowSamlAuthenticator(securityContext, facade, deploymentContext.resolveDeployment(facade), sessionStore);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthOutcome outcome = authenticator.authenticate();
|
|
||||||
if (outcome == AuthOutcome.AUTHENTICATED) {
|
|
||||||
registerNotifications(securityContext);
|
|
||||||
return AuthenticationMechanismOutcome.AUTHENTICATED;
|
|
||||||
}
|
|
||||||
if (outcome == AuthOutcome.NOT_AUTHENTICATED) {
|
|
||||||
// we are in passive mode and user is not authenticated, let app server to try another auth mechanism
|
|
||||||
// See KEYCLOAK-2107, AbstractSamlAuthenticationHandler
|
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
|
||||||
}
|
|
||||||
if (outcome == AuthOutcome.LOGGED_OUT) {
|
|
||||||
securityContext.logout();
|
|
||||||
if (deployment.getLogoutPage() != null) {
|
|
||||||
redirectLogout(deployment, exchange);
|
|
||||||
}
|
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
|
||||||
}
|
|
||||||
AuthChallenge challenge = authenticator.getChallenge();
|
|
||||||
if (challenge != null) {
|
|
||||||
exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, challenge);
|
|
||||||
if (authenticator instanceof UndertowSamlEndpoint) {
|
|
||||||
exchange.getSecurityContext().setAuthenticationRequired();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outcome == AuthOutcome.FAILED) {
|
|
||||||
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
|
||||||
}
|
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void redirectLogout(SamlDeployment deployment, HttpServerExchange exchange) {
|
|
||||||
String page = deployment.getLogoutPage();
|
|
||||||
sendRedirect(exchange, page);
|
|
||||||
exchange.setStatusCode(StatusCodes.FOUND);
|
|
||||||
exchange.endExchange();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected UndertowHttpFacade createFacade(HttpServerExchange exchange) {
|
|
||||||
return new UndertowHttpFacade(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext);
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2017 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.undertow;
|
|
||||||
|
|
||||||
import org.keycloak.adapters.saml.SamlSession;
|
|
||||||
import org.keycloak.adapters.spi.SessionIdMapper;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.session.Session;
|
|
||||||
import io.undertow.server.session.SessionListener;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author hmlnarik
|
|
||||||
*/
|
|
||||||
public class IdMapperUpdaterSessionListener implements SessionListener {
|
|
||||||
|
|
||||||
private final SessionIdMapper idMapper;
|
|
||||||
|
|
||||||
public IdMapperUpdaterSessionListener(SessionIdMapper idMapper) {
|
|
||||||
this.idMapper = idMapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sessionCreated(Session session, HttpServerExchange exchange) {
|
|
||||||
Object value = session.getAttribute(SamlSession.class.getName());
|
|
||||||
map(session.getId(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sessionDestroyed(Session session, HttpServerExchange exchange, SessionDestroyedReason reason) {
|
|
||||||
if (reason != SessionDestroyedReason.UNDEPLOY) {
|
|
||||||
unmap(session.getId(), session.getAttribute(SamlSession.class.getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attributeAdded(Session session, String name, Object value) {
|
|
||||||
if (Objects.equals(name, SamlSession.class.getName())) {
|
|
||||||
map(session.getId(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attributeUpdated(Session session, String name, Object newValue, Object oldValue) {
|
|
||||||
if (Objects.equals(name, SamlSession.class.getName())) {
|
|
||||||
unmap(session.getId(), oldValue);
|
|
||||||
map(session.getId(), newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attributeRemoved(Session session, String name, Object oldValue) {
|
|
||||||
if (Objects.equals(name, SamlSession.class.getName())) {
|
|
||||||
unmap(session.getId(), oldValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sessionIdChanged(Session session, String oldSessionId) {
|
|
||||||
Object value = session.getAttribute(SamlSession.class.getName());
|
|
||||||
if (value != null) {
|
|
||||||
unmap(oldSessionId, value);
|
|
||||||
map(session.getId(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void map(String sessionId, Object value) {
|
|
||||||
if (! (value instanceof SamlSession) || sessionId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SamlSession account = (SamlSession) value;
|
|
||||||
|
|
||||||
idMapper.map(account.getSessionIndex(), account.getPrincipal().getSamlSubject(), sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unmap(String sessionId, Object value) {
|
|
||||||
if (! (value instanceof SamlSession) || sessionId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SamlSession samlSession = (SamlSession) value;
|
|
||||||
if (samlSession.getSessionIndex() != null) {
|
|
||||||
idMapper.removeSession(sessionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,223 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.AuthenticationMechanism;
|
|
||||||
import io.undertow.security.api.AuthenticationMechanismFactory;
|
|
||||||
import io.undertow.security.idm.Account;
|
|
||||||
import io.undertow.security.idm.Credential;
|
|
||||||
import io.undertow.security.idm.IdentityManager;
|
|
||||||
import io.undertow.server.handlers.form.FormParserFactory;
|
|
||||||
import io.undertow.servlet.ServletExtension;
|
|
||||||
import io.undertow.servlet.api.AuthMethodConfig;
|
|
||||||
import io.undertow.servlet.api.DeploymentInfo;
|
|
||||||
import io.undertow.servlet.api.LoginConfig;
|
|
||||||
import io.undertow.servlet.api.SecurityConstraint;
|
|
||||||
import io.undertow.servlet.api.ServletSessionConfig;
|
|
||||||
import io.undertow.servlet.api.WebResourceCollection;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.adapters.saml.AdapterConstants;
|
|
||||||
import org.keycloak.adapters.saml.DefaultSamlDeployment;
|
|
||||||
import org.keycloak.adapters.saml.SamlConfigResolver;
|
|
||||||
import org.keycloak.adapters.saml.SamlDeployment;
|
|
||||||
import org.keycloak.adapters.saml.SamlDeploymentContext;
|
|
||||||
import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
|
|
||||||
import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
|
|
||||||
import org.keycloak.adapters.undertow.ChangeSessionId;
|
|
||||||
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
|
||||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class SamlServletExtension implements ServletExtension {
|
|
||||||
|
|
||||||
protected static Logger log = Logger.getLogger(SamlServletExtension.class);
|
|
||||||
|
|
||||||
// todo when this DeploymentInfo method of the same name is fixed.
|
|
||||||
public boolean isAuthenticationMechanismPresent(DeploymentInfo deploymentInfo, final String mechanismName) {
|
|
||||||
LoginConfig loginConfig = deploymentInfo.getLoginConfig();
|
|
||||||
if (loginConfig != null) {
|
|
||||||
for (AuthMethodConfig method : loginConfig.getAuthMethods()) {
|
|
||||||
if (method.getName().equalsIgnoreCase(mechanismName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InputStream getXMLFromServletContext(ServletContext servletContext) {
|
|
||||||
String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
|
|
||||||
if (json == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new ByteArrayInputStream(json.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InputStream getConfigInputStream(ServletContext context) {
|
|
||||||
InputStream is = getXMLFromServletContext(context);
|
|
||||||
if (is == null) {
|
|
||||||
String path = context.getInitParameter("keycloak.config.file");
|
|
||||||
if (path == null) {
|
|
||||||
log.debug("using /WEB-INF/keycloak-saml.xml");
|
|
||||||
is = context.getResourceAsStream("/WEB-INF/keycloak-saml.xml");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
is = new FileInputStream(path);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return is;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("UseSpecificCatch")
|
|
||||||
public void handleDeployment(DeploymentInfo deploymentInfo, final ServletContext servletContext) {
|
|
||||||
if (!isAuthenticationMechanismPresent(deploymentInfo, "KEYCLOAK-SAML")) {
|
|
||||||
log.debug("auth-method is not keycloak saml!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.debug("SamlServletException initialization");
|
|
||||||
|
|
||||||
// Possible scenarios:
|
|
||||||
// 1) The deployment has a keycloak.config.resolver specified and it exists:
|
|
||||||
// Outcome: adapter uses the resolver
|
|
||||||
// 2) The deployment has a keycloak.config.resolver and isn't valid (doesn't exist, isn't a resolver, ...) :
|
|
||||||
// Outcome: adapter is left unconfigured
|
|
||||||
// 3) The deployment doesn't have a keycloak.config.resolver , but has a keycloak.json (or equivalent)
|
|
||||||
// Outcome: adapter uses it
|
|
||||||
// 4) The deployment doesn't have a keycloak.config.resolver nor keycloak.json (or equivalent)
|
|
||||||
// Outcome: adapter is left unconfigured
|
|
||||||
|
|
||||||
SamlConfigResolver configResolver;
|
|
||||||
String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
|
|
||||||
SamlDeploymentContext deploymentContext = null;
|
|
||||||
if (configResolverClass != null) {
|
|
||||||
try {
|
|
||||||
configResolver = (SamlConfigResolver) deploymentInfo.getClassLoader().loadClass(configResolverClass).newInstance();
|
|
||||||
deploymentContext = new SamlDeploymentContext(configResolver);
|
|
||||||
log.infov("Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.warn("The specified resolver " + configResolverClass + " could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: " + ex.getMessage());
|
|
||||||
deploymentContext = new SamlDeploymentContext(new DefaultSamlDeployment());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
InputStream is = getConfigInputStream(servletContext);
|
|
||||||
final SamlDeployment deployment;
|
|
||||||
if (is == null) {
|
|
||||||
log.warn("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 servletContext.getResourceAsStream(resource);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
deployment = new DeploymentBuilder().build(is, loader);
|
|
||||||
} catch (ParsingException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deploymentContext = new SamlDeploymentContext(deployment);
|
|
||||||
log.debug("Keycloak is using a per-deployment configuration.");
|
|
||||||
}
|
|
||||||
|
|
||||||
servletContext.setAttribute(SamlDeploymentContext.class.getName(), deploymentContext);
|
|
||||||
UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
|
|
||||||
final ServletSamlAuthMech mech = createAuthMech(deploymentInfo, deploymentContext, userSessionManagement);
|
|
||||||
mech.addTokenStoreUpdaters(deploymentInfo);
|
|
||||||
|
|
||||||
// setup handlers
|
|
||||||
|
|
||||||
deploymentInfo.addAuthenticationMechanism("KEYCLOAK-SAML", new AuthenticationMechanismFactory() {
|
|
||||||
@Override
|
|
||||||
public AuthenticationMechanism create(String s, FormParserFactory formParserFactory, Map<String, String> stringStringMap) {
|
|
||||||
return mech;
|
|
||||||
}
|
|
||||||
}); // authentication
|
|
||||||
|
|
||||||
deploymentInfo.setIdentityManager(new IdentityManager() {
|
|
||||||
@Override
|
|
||||||
public Account verify(Account account) {
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Account verify(String id, Credential credential) {
|
|
||||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Account verify(Credential credential) {
|
|
||||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ServletSessionConfig cookieConfig = deploymentInfo.getServletSessionConfig();
|
|
||||||
if (cookieConfig == null) {
|
|
||||||
cookieConfig = new ServletSessionConfig();
|
|
||||||
}
|
|
||||||
if (cookieConfig.getPath() == null) {
|
|
||||||
log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath());
|
|
||||||
cookieConfig.setPath(deploymentInfo.getContextPath());
|
|
||||||
deploymentInfo.setServletSessionConfig(cookieConfig);
|
|
||||||
}
|
|
||||||
addEndpointConstraint(deploymentInfo);
|
|
||||||
|
|
||||||
ChangeSessionId.turnOffChangeSessionIdOnLogin(deploymentInfo);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* add security constraint to /saml so that the endpoint can be called and auth mechanism pinged.
|
|
||||||
* @param deploymentInfo
|
|
||||||
*/
|
|
||||||
protected void addEndpointConstraint(DeploymentInfo deploymentInfo) {
|
|
||||||
SecurityConstraint constraint = new SecurityConstraint();
|
|
||||||
WebResourceCollection collection = new WebResourceCollection();
|
|
||||||
collection.addUrlPattern("/saml");
|
|
||||||
constraint.addWebResourceCollection(collection);
|
|
||||||
deploymentInfo.addSecurityConstraint(constraint);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ServletSamlAuthMech createAuthMech(DeploymentInfo deploymentInfo, SamlDeploymentContext deploymentContext, UndertowUserSessionManagement userSessionManagement) {
|
|
||||||
return new ServletSamlAuthMech(deploymentContext, userSessionManagement, getErrorPage(deploymentInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getErrorPage(DeploymentInfo deploymentInfo) {
|
|
||||||
LoginConfig loginConfig = deploymentInfo.getLoginConfig();
|
|
||||||
String errorPage = null;
|
|
||||||
if (loginConfig != null) {
|
|
||||||
errorPage = loginConfig.getErrorPage();
|
|
||||||
}
|
|
||||||
return errorPage;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,152 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import io.undertow.util.Headers;
|
|
||||||
|
|
||||||
import org.keycloak.adapters.saml.SamlDeployment;
|
|
||||||
import org.keycloak.adapters.saml.SamlDeploymentContext;
|
|
||||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
|
||||||
import org.keycloak.adapters.spi.*;
|
|
||||||
import org.keycloak.adapters.undertow.ServletHttpFacade;
|
|
||||||
import org.keycloak.adapters.undertow.UndertowHttpFacade;
|
|
||||||
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
|
||||||
|
|
||||||
import io.undertow.servlet.api.DeploymentInfo;
|
|
||||||
import javax.servlet.RequestDispatcher;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
import javax.servlet.ServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class ServletSamlAuthMech extends AbstractSamlAuthMech {
|
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(ServletSamlAuthMech.class);
|
|
||||||
|
|
||||||
protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
|
|
||||||
protected SessionIdMapperUpdater idMapperUpdater = SessionIdMapperUpdater.DIRECT;
|
|
||||||
|
|
||||||
public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
|
|
||||||
super(deploymentContext, sessionManagement, errorPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTokenStoreUpdaters(DeploymentInfo deploymentInfo) {
|
|
||||||
deploymentInfo.addSessionListener(new IdMapperUpdaterSessionListener(idMapper)); // This takes care of HTTP sessions manipulated locally
|
|
||||||
SessionIdMapperUpdater updater = SessionIdMapperUpdater.EXTERNAL;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Map<String, String> initParameters = deploymentInfo.getInitParameters();
|
|
||||||
String idMapperSessionUpdaterClasses = initParameters == null
|
|
||||||
? null
|
|
||||||
: initParameters.get("keycloak.sessionIdMapperUpdater.classes");
|
|
||||||
if (idMapperSessionUpdaterClasses == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String clazz : idMapperSessionUpdaterClasses.split("\\s*,\\s*")) {
|
|
||||||
if (! clazz.isEmpty()) {
|
|
||||||
updater = invokeAddTokenStoreUpdaterMethod(clazz, deploymentInfo, updater);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
setIdMapperUpdater(updater);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SessionIdMapperUpdater invokeAddTokenStoreUpdaterMethod(String idMapperSessionUpdaterClass, DeploymentInfo deploymentInfo,
|
|
||||||
SessionIdMapperUpdater previousIdMapperUpdater) {
|
|
||||||
try {
|
|
||||||
Class<?> clazz = deploymentInfo.getClassLoader().loadClass(idMapperSessionUpdaterClass);
|
|
||||||
Method addTokenStoreUpdatersMethod = clazz.getMethod("addTokenStoreUpdaters", DeploymentInfo.class, SessionIdMapper.class, SessionIdMapperUpdater.class);
|
|
||||||
if (! Modifier.isStatic(addTokenStoreUpdatersMethod.getModifiers())
|
|
||||||
|| ! Modifier.isPublic(addTokenStoreUpdatersMethod.getModifiers())
|
|
||||||
|| ! SessionIdMapperUpdater.class.isAssignableFrom(addTokenStoreUpdatersMethod.getReturnType())) {
|
|
||||||
LOG.errorv("addTokenStoreUpdaters method in class {0} has to be public static. Ignoring class.", idMapperSessionUpdaterClass);
|
|
||||||
return previousIdMapperUpdater;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.debugv("Initializing sessionIdMapperUpdater class {0}", idMapperSessionUpdaterClass);
|
|
||||||
return (SessionIdMapperUpdater) addTokenStoreUpdatersMethod.invoke(null, deploymentInfo, idMapper, previousIdMapperUpdater);
|
|
||||||
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException ex) {
|
|
||||||
LOG.warnv(ex, "Cannot use sessionIdMapperUpdater class {0}", idMapperSessionUpdaterClass);
|
|
||||||
return previousIdMapperUpdater;
|
|
||||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
|
||||||
LOG.warnv(ex, "Cannot use {0}.addTokenStoreUpdaters(DeploymentInfo, SessionIdMapper) method", idMapperSessionUpdaterClass);
|
|
||||||
return previousIdMapperUpdater;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext) {
|
|
||||||
return new ServletSamlSessionStore(exchange, sessionManagement, securityContext, idMapper, idMapperUpdater, deployment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UndertowHttpFacade createFacade(HttpServerExchange exchange) {
|
|
||||||
return new ServletHttpFacade(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void redirectLogout(SamlDeployment deployment, HttpServerExchange exchange) {
|
|
||||||
exchange.getResponseHeaders().add(Headers.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
|
|
||||||
exchange.getResponseHeaders().add(Headers.PRAGMA, "no-cache");
|
|
||||||
exchange.getResponseHeaders().add(Headers.EXPIRES, "0");
|
|
||||||
|
|
||||||
super.redirectLogout(deployment, exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Integer servePage(HttpServerExchange exchange, String location) {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
ServletRequest req = servletRequestContext.getServletRequest();
|
|
||||||
ServletResponse resp = servletRequestContext.getServletResponse();
|
|
||||||
RequestDispatcher disp = req.getRequestDispatcher(location);
|
|
||||||
//make sure the login page is never cached
|
|
||||||
exchange.getResponseHeaders().add(Headers.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
|
|
||||||
exchange.getResponseHeaders().add(Headers.PRAGMA, "no-cache");
|
|
||||||
exchange.getResponseHeaders().add(Headers.EXPIRES, "0");
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
disp.forward(req, resp);
|
|
||||||
} catch (ServletException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionIdMapperUpdater getIdMapperUpdater() {
|
|
||||||
return idMapperUpdater;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setIdMapperUpdater(SessionIdMapperUpdater idMapperUpdater) {
|
|
||||||
this.idMapperUpdater = idMapperUpdater;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,263 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.security.idm.Account;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.session.SessionManager;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import io.undertow.servlet.spec.HttpSessionImpl;
|
|
||||||
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.spi.SessionIdMapper;
|
|
||||||
import org.keycloak.adapters.spi.SessionIdMapperUpdater;
|
|
||||||
import org.keycloak.adapters.undertow.ChangeSessionId;
|
|
||||||
import org.keycloak.adapters.undertow.SavedRequest;
|
|
||||||
import org.keycloak.adapters.undertow.ServletHttpFacade;
|
|
||||||
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
|
||||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
|
||||||
import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import javax.xml.datatype.DatatypeConstants;
|
|
||||||
import javax.xml.datatype.XMLGregorianCalendar;
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session store manipulation methods per single HTTP exchange.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class ServletSamlSessionStore implements SamlSessionStore {
|
|
||||||
protected static Logger log = Logger.getLogger(SamlSessionStore.class);
|
|
||||||
public static final String SAML_REDIRECT_URI = "SAML_REDIRECT_URI";
|
|
||||||
|
|
||||||
private final HttpServerExchange exchange;
|
|
||||||
private final UndertowUserSessionManagement sessionManagement;
|
|
||||||
private final SecurityContext securityContext;
|
|
||||||
private final SessionIdMapper idMapper;
|
|
||||||
private final SessionIdMapperUpdater idMapperUpdater;
|
|
||||||
protected final SamlDeployment deployment;
|
|
||||||
|
|
||||||
|
|
||||||
public ServletSamlSessionStore(HttpServerExchange exchange, UndertowUserSessionManagement sessionManagement,
|
|
||||||
SecurityContext securityContext,
|
|
||||||
SessionIdMapper idMapper, SessionIdMapperUpdater idMapperUpdater,
|
|
||||||
SamlDeployment deployment) {
|
|
||||||
this.exchange = exchange;
|
|
||||||
this.sessionManagement = sessionManagement;
|
|
||||||
this.securityContext = securityContext;
|
|
||||||
this.idMapper = idMapper;
|
|
||||||
this.deployment = deployment;
|
|
||||||
this.idMapperUpdater = idMapperUpdater;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCurrentAction(CurrentAction action) {
|
|
||||||
if (action == CurrentAction.NONE && getRequest().getSession(false) == null) return;
|
|
||||||
getRequest().getSession().setAttribute(CURRENT_ACTION, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLoggingIn() {
|
|
||||||
HttpSession session = getRequest().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 = getRequest().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 = getSession(false);
|
|
||||||
if (session != null) {
|
|
||||||
SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
|
|
||||||
if (samlSession != null) {
|
|
||||||
if (samlSession.getSessionIndex() != null) {
|
|
||||||
idMapperUpdater.removeSession(idMapper, session.getId());
|
|
||||||
}
|
|
||||||
session.removeAttribute(SamlSession.class.getName());
|
|
||||||
}
|
|
||||||
session.removeAttribute(SAML_REDIRECT_URI);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logoutByPrincipal(String principal) {
|
|
||||||
Set<String> sessions = idMapper.getUserSessions(principal);
|
|
||||||
if (sessions != null) {
|
|
||||||
List<String> ids = new LinkedList<>();
|
|
||||||
ids.addAll(sessions);
|
|
||||||
logoutSessionIds(ids);
|
|
||||||
for (String id : ids) {
|
|
||||||
idMapperUpdater.removeSession(idMapper, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logoutBySsoId(List<String> ssoIds) {
|
|
||||||
if (ssoIds == null) return;
|
|
||||||
List<String> sessionIds = new LinkedList<>();
|
|
||||||
for (String id : ssoIds) {
|
|
||||||
String sessionId = idMapper.getSessionFromSSO(id);
|
|
||||||
if (sessionId != null) {
|
|
||||||
sessionIds.add(sessionId);
|
|
||||||
idMapperUpdater.removeSession(idMapper, sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
logoutSessionIds(sessionIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void logoutSessionIds(List<String> sessionIds) {
|
|
||||||
if (sessionIds == null || sessionIds.isEmpty()) return;
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
SessionManager sessionManager = servletRequestContext.getDeployment().getSessionManager();
|
|
||||||
sessionManagement.logoutHttpSessions(sessionManager, sessionIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLoggedIn() {
|
|
||||||
HttpSession session = getSession(false);
|
|
||||||
if (session == null) {
|
|
||||||
log.debug("Session was not found");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! idMapper.hasSession(session.getId()) && ! idMapperUpdater.refreshMapping(idMapper, session.getId())) {
|
|
||||||
log.debugf("Session %s has expired on some other node", session.getId());
|
|
||||||
session.removeAttribute(SamlSession.class.getName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final SamlSession samlSession = SamlUtil.validateSamlSession(session.getAttribute(SamlSession.class.getName()), deployment);
|
|
||||||
if (samlSession == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Account undertowAccount = new Account() {
|
|
||||||
@Override
|
|
||||||
public Principal getPrincipal() {
|
|
||||||
return samlSession.getPrincipal();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getRoles() {
|
|
||||||
return samlSession.getRoles();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
securityContext.authenticationComplete(undertowAccount, "KEYCLOAK-SAML", false);
|
|
||||||
restoreRequest();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveAccount(SamlSession account) {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpSession session = getSession(true);
|
|
||||||
session.setAttribute(SamlSession.class.getName(), account);
|
|
||||||
sessionManagement.login(servletRequestContext.getDeployment().getSessionManager());
|
|
||||||
String sessionId = changeSessionId(session);
|
|
||||||
idMapperUpdater.map(idMapper, account.getSessionIndex(), account.getPrincipal().getSamlSubject(), sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String changeSessionId(HttpSession session) {
|
|
||||||
if (!deployment.turnOffChangeSessionIdOnLogin()) return ChangeSessionId.changeSessionId(exchange, false);
|
|
||||||
else return session.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SamlSession getAccount() {
|
|
||||||
HttpSession session = getSession(true);
|
|
||||||
return (SamlSession)session.getAttribute(SamlSession.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRedirectUri() {
|
|
||||||
final ServletRequestContext sc = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpSessionImpl session = sc.getCurrentServletContext().getSession(exchange, true);
|
|
||||||
String redirect = (String)session.getAttribute(SAML_REDIRECT_URI);
|
|
||||||
if (redirect == null) {
|
|
||||||
ServletHttpFacade facade = new ServletHttpFacade(exchange);
|
|
||||||
HttpServletRequest req = (HttpServletRequest)sc.getServletRequest();
|
|
||||||
String contextPath = req.getContextPath();
|
|
||||||
String baseUri = KeycloakUriBuilder.fromUri(req.getRequestURL().toString()).replacePath(contextPath).build().toString();
|
|
||||||
return SamlUtil.getRedirectTo(facade, contextPath, baseUri);
|
|
||||||
}
|
|
||||||
return redirect;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveRequest() {
|
|
||||||
SavedRequest.trySaveRequest(exchange);
|
|
||||||
final ServletRequestContext sc = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpSessionImpl session = sc.getCurrentServletContext().getSession(exchange, true);
|
|
||||||
KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(exchange.getRequestURI())
|
|
||||||
.replaceQuery(exchange.getQueryString());
|
|
||||||
if (!exchange.isHostIncludedInRequestURI()) uriBuilder.scheme(exchange.getRequestScheme()).host(exchange.getHostAndPort());
|
|
||||||
String uri = uriBuilder.buildAsString();
|
|
||||||
|
|
||||||
session.setAttribute(SAML_REDIRECT_URI, uri);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean restoreRequest() {
|
|
||||||
HttpSession session = getSession(false);
|
|
||||||
if (session == null) return false;
|
|
||||||
SavedRequest.tryRestoreRequest(exchange, session);
|
|
||||||
session.removeAttribute(SAML_REDIRECT_URI);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HttpSession getSession(boolean create) {
|
|
||||||
HttpServletRequest req = getRequest();
|
|
||||||
return req.getSession(create);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpServletResponse getResponse() {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
return (HttpServletResponse)servletRequestContext.getServletResponse();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpServletRequest getRequest() {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
return (HttpServletRequest) servletRequestContext.getServletRequest();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.security.idm.Account;
|
|
||||||
import org.keycloak.adapters.saml.SamlAuthenticator;
|
|
||||||
import org.keycloak.adapters.saml.SamlDeployment;
|
|
||||||
import org.keycloak.adapters.saml.SamlSession;
|
|
||||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
|
||||||
import org.keycloak.adapters.saml.profile.SamlAuthenticationHandler;
|
|
||||||
import org.keycloak.adapters.saml.profile.webbrowsersso.BrowserHandler;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UndertowSamlAuthenticator extends SamlAuthenticator {
|
|
||||||
protected SecurityContext securityContext;
|
|
||||||
|
|
||||||
public UndertowSamlAuthenticator(SecurityContext securityContext, HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
|
|
||||||
super(facade, deployment, sessionStore);
|
|
||||||
this.securityContext = securityContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeAuthentication(final SamlSession samlSession) {
|
|
||||||
Account undertowAccount = new Account() {
|
|
||||||
@Override
|
|
||||||
public Principal getPrincipal() {
|
|
||||||
return samlSession.getPrincipal();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getRoles() {
|
|
||||||
return samlSession.getRoles();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
securityContext.authenticationComplete(undertowAccount, "KEYCLOAK-SAML", false);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SamlAuthenticationHandler createBrowserHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
|
|
||||||
return new BrowserHandler(facade, deployment, sessionStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,48 +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.undertow;
|
|
||||||
|
|
||||||
import org.keycloak.adapters.saml.SamlAuthenticator;
|
|
||||||
import org.keycloak.adapters.saml.SamlDeployment;
|
|
||||||
import org.keycloak.adapters.saml.SamlSession;
|
|
||||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
|
||||||
import org.keycloak.adapters.saml.profile.SamlAuthenticationHandler;
|
|
||||||
import org.keycloak.adapters.saml.profile.webbrowsersso.SamlEndpoint;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UndertowSamlEndpoint extends SamlAuthenticator {
|
|
||||||
public UndertowSamlEndpoint(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
|
|
||||||
super(facade, deployment, sessionStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeAuthentication(SamlSession samlSession) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SamlAuthenticationHandler createBrowserHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
|
|
||||||
return new SamlEndpoint(facade, deployment, sessionStore);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +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.
|
|
||||||
#
|
|
||||||
|
|
||||||
org.keycloak.adapters.saml.undertow.SamlServletExtension
|
|
|
@ -92,16 +92,6 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Authorization -->
|
<!-- Authorization -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-adapter-spi">
|
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-adapter-spi">
|
||||||
<resources>
|
<resources>
|
||||||
<artifact name="${org.keycloak:keycloak-adapter-spi}"/>
|
<artifact name="${org.keycloak:keycloak-adapter-spi}"/>
|
||||||
<artifact name="${org.keycloak:keycloak-undertow-adapter-spi}"/>
|
|
||||||
</resources>
|
</resources>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<module name="javax.api"/>
|
<module name="javax.api"/>
|
||||||
|
|
|
@ -134,17 +134,6 @@
|
||||||
</exclusions>
|
</exclusions>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-undertow-adapter</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-wildfly-elytron-jakarta-adapter</artifactId>
|
<artifactId>keycloak-saml-wildfly-elytron-jakarta-adapter</artifactId>
|
||||||
|
@ -167,17 +156,6 @@
|
||||||
</exclusions>
|
</exclusions>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${ee.maven.groupId}</groupId>
|
<groupId>${ee.maven.groupId}</groupId>
|
||||||
<artifactId>wildfly-ee-galleon-pack</artifactId>
|
<artifactId>wildfly-ee-galleon-pack</artifactId>
|
||||||
|
|
|
@ -11,16 +11,6 @@
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
|
||||||
<licenses>
|
|
||||||
<license>
|
|
||||||
<name>Apache License 2.0</name>
|
|
||||||
<url>https://raw.githubusercontent.com/keycloak/keycloak/999-SNAPSHOT/LICENSE.txt</url>
|
|
||||||
</license>
|
|
||||||
</licenses>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-common</artifactId>
|
<artifactId>keycloak-common</artifactId>
|
||||||
|
@ -101,16 +91,6 @@
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-undertow-adapter</artifactId>
|
|
||||||
<licenses>
|
|
||||||
<license>
|
|
||||||
<name>Apache License 2.0</name>
|
|
||||||
<url>https://raw.githubusercontent.com/keycloak/keycloak/999-SNAPSHOT/LICENSE.txt</url>
|
|
||||||
</license>
|
|
||||||
</licenses>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-wildfly-elytron-jakarta-adapter</artifactId>
|
<artifactId>keycloak-saml-wildfly-elytron-jakarta-adapter</artifactId>
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-spi">
|
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-spi">
|
||||||
<resources>
|
<resources>
|
||||||
<artifact name="${org.keycloak:keycloak-adapter-spi}"/>
|
<artifact name="${org.keycloak:keycloak-adapter-spi}"/>
|
||||||
<artifact name="${org.keycloak:keycloak-undertow-adapter-spi}"/>
|
|
||||||
</resources>
|
</resources>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<module name="javax.api"/>
|
<module name="javax.api"/>
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
~ 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-undertow-adapter">
|
|
||||||
<properties>
|
|
||||||
<property name="jboss.api" value="private"/>
|
|
||||||
</properties>
|
|
||||||
<resources>
|
|
||||||
<artifact name="${org.keycloak:keycloak-saml-undertow-adapter}"/>
|
|
||||||
</resources>
|
|
||||||
<dependencies>
|
|
||||||
<module name="javax.api"/>
|
|
||||||
<module name="org.bouncycastle" />
|
|
||||||
<module name="jakarta.servlet.api"/>
|
|
||||||
<module name="org.jboss.logging"/>
|
|
||||||
<module name="org.jboss.xnio"/>
|
|
||||||
<module name="io.undertow.core"/>
|
|
||||||
<module name="io.undertow.servlet"/>
|
|
||||||
<module name="org.keycloak.keycloak-adapter-spi"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-core"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-adapter-api-public"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-adapter-core-jakarta"/>
|
|
||||||
<module name="org.keycloak.keycloak-common"/>
|
|
||||||
<module name="org.apache.httpcomponents"/>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</module>
|
|
|
@ -33,7 +33,6 @@
|
||||||
<module name="org.jboss.logging"/>
|
<module name="org.jboss.logging"/>
|
||||||
<module name="io.undertow.core"/>
|
<module name="io.undertow.core"/>
|
||||||
<module name="io.undertow.servlet"/>
|
<module name="io.undertow.servlet"/>
|
||||||
<module name="org.keycloak.keycloak-saml-undertow-adapter"/>
|
|
||||||
<module name="org.keycloak.keycloak-adapter-spi"/>
|
<module name="org.keycloak.keycloak-adapter-spi"/>
|
||||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
<module name="org.keycloak.keycloak-saml-core-public"/>
|
||||||
<module name="org.keycloak.keycloak-saml-core"/>
|
<module name="org.keycloak.keycloak-saml-core"/>
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-adapter-spi">
|
<module-def name="org.keycloak.keycloak-adapter-spi">
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter-spi"/>
|
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-saml-core">
|
<module-def name="org.keycloak.keycloak-saml-core">
|
||||||
|
@ -72,10 +71,6 @@
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-jboss-adapter-core"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-jboss-adapter-core"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-saml-undertow-adapter">
|
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-saml-undertow-adapter"/>
|
|
||||||
</module-def>
|
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-saml-wildfly-jakarta-subsystem">
|
<module-def name="org.keycloak.keycloak-saml-wildfly-jakarta-subsystem">
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-saml-wildfly-jakarta-subsystem"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-saml-wildfly-jakarta-subsystem"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
|
@ -74,16 +74,6 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-core</artifactId>
|
<artifactId>keycloak-saml-core</artifactId>
|
||||||
|
@ -124,16 +114,6 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-undertow-adapter</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-core-public</artifactId>
|
<artifactId>keycloak-saml-core-public</artifactId>
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
~ 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-undertow-adapter">
|
|
||||||
<properties>
|
|
||||||
<property name="jboss.api" value="private"/>
|
|
||||||
</properties>
|
|
||||||
<resources>
|
|
||||||
<!-- Insert resources here -->
|
|
||||||
</resources>
|
|
||||||
<dependencies>
|
|
||||||
<module name="javax.api"/>
|
|
||||||
<module name="org.bouncycastle" />
|
|
||||||
<module name="jakarta.servlet.api"/>
|
|
||||||
<module name="org.jboss.logging"/>
|
|
||||||
<module name="org.jboss.xnio"/>
|
|
||||||
<module name="io.undertow.core"/>
|
|
||||||
<module name="io.undertow.servlet"/>
|
|
||||||
<module name="org.keycloak.keycloak-adapter-spi"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-core"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-adapter-api-public"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-adapter-core-jakarta"/>
|
|
||||||
<module name="org.keycloak.keycloak-common"/>
|
|
||||||
<module name="org.apache.httpcomponents"/>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</module>
|
|
|
@ -33,7 +33,6 @@
|
||||||
<module name="org.jboss.logging"/>
|
<module name="org.jboss.logging"/>
|
||||||
<module name="io.undertow.core"/>
|
<module name="io.undertow.core"/>
|
||||||
<module name="io.undertow.servlet"/>
|
<module name="io.undertow.servlet"/>
|
||||||
<module name="org.keycloak.keycloak-saml-undertow-adapter"/>
|
|
||||||
<module name="org.keycloak.keycloak-adapter-spi"/>
|
<module name="org.keycloak.keycloak-adapter-spi"/>
|
||||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
<module name="org.keycloak.keycloak-saml-core-public"/>
|
||||||
<module name="org.keycloak.keycloak-saml-core"/>
|
<module name="org.keycloak.keycloak-saml-core"/>
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-adapter-spi">
|
<module-def name="org.keycloak.keycloak-adapter-spi">
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter-spi"/>
|
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-saml-core">
|
<module-def name="org.keycloak.keycloak-saml-core">
|
||||||
|
@ -72,10 +71,6 @@
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-jboss-adapter-core"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-jboss-adapter-core"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-saml-undertow-adapter">
|
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-saml-undertow-adapter"/>
|
|
||||||
</module-def>
|
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-saml-wildfly-subsystem">
|
<module-def name="org.keycloak.keycloak-saml-wildfly-subsystem">
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-saml-wildfly-subsystem"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-saml-wildfly-subsystem"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
|
@ -74,16 +74,6 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-core</artifactId>
|
<artifactId>keycloak-saml-core</artifactId>
|
||||||
|
@ -124,16 +114,6 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-undertow-adapter</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-core-public</artifactId>
|
<artifactId>keycloak-saml-core-public</artifactId>
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
~ 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-undertow-adapter">
|
|
||||||
<properties>
|
|
||||||
<property name="jboss.api" value="private"/>
|
|
||||||
</properties>
|
|
||||||
<resources>
|
|
||||||
<!-- Insert resources here -->
|
|
||||||
</resources>
|
|
||||||
<dependencies>
|
|
||||||
<module name="javax.api"/>
|
|
||||||
<module name="org.bouncycastle" />
|
|
||||||
<module name="javax.servlet.api"/>
|
|
||||||
<module name="org.jboss.logging"/>
|
|
||||||
<module name="org.jboss.xnio"/>
|
|
||||||
<module name="io.undertow.core"/>
|
|
||||||
<module name="io.undertow.servlet"/>
|
|
||||||
<module name="org.keycloak.keycloak-adapter-spi"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-core"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-adapter-api-public"/>
|
|
||||||
<module name="org.keycloak.keycloak-saml-adapter-core"/>
|
|
||||||
<module name="org.keycloak.keycloak-common"/>
|
|
||||||
<module name="org.apache.httpcomponents"/>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</module>
|
|
|
@ -34,7 +34,6 @@
|
||||||
<module name="io.undertow.core"/>
|
<module name="io.undertow.core"/>
|
||||||
<module name="io.undertow.servlet"/>
|
<module name="io.undertow.servlet"/>
|
||||||
<module name="org.picketbox"/>
|
<module name="org.picketbox"/>
|
||||||
<module name="org.keycloak.keycloak-saml-undertow-adapter"/>
|
|
||||||
<module name="org.keycloak.keycloak-adapter-spi"/>
|
<module name="org.keycloak.keycloak-adapter-spi"/>
|
||||||
<module name="org.keycloak.keycloak-saml-core-public"/>
|
<module name="org.keycloak.keycloak-saml-core-public"/>
|
||||||
<module name="org.keycloak.keycloak-saml-core"/>
|
<module name="org.keycloak.keycloak-saml-core"/>
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -1133,11 +1133,6 @@
|
||||||
<artifactId>keycloak-saml-wildfly-jakarta-subsystem</artifactId>
|
<artifactId>keycloak-saml-wildfly-jakarta-subsystem</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-undertow-adapter</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-services</artifactId>
|
<artifactId>keycloak-services</artifactId>
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>undertow-adapter-spi-jakarta</module>
|
<module>undertow-adapter-spi-jakarta</module>
|
||||||
<module>undertow-adapter-saml-jakarta</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
src/
|
|
|
@ -1,130 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<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/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
|
||||||
<artifactId>integration-arquillian-servers-adapter-spi</artifactId>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<version>999.0.0-SNAPSHOT</version>
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<artifactId>keycloak-saml-undertow-adapter-jakarta</artifactId>
|
|
||||||
<name>Undertow SAML Adapter (JakartaEE)</name>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<jakarta-transformer-sources>${project.basedir}/../../../../../adapters/saml/undertow</jakarta-transformer-sources>
|
|
||||||
<jakarta-transformer-target>${project.basedir}</jakarta-transformer-target>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-core</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-adapter-spi</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-common</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-adapter-api-public</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-saml-adapter-core</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi-jakarta</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jboss.logging</groupId>
|
|
||||||
<artifactId>jboss-logging</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>jakarta.servlet</groupId>
|
|
||||||
<artifactId>jakarta.servlet-api</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-servlet</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-core</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-antrun-plugin</artifactId>
|
|
||||||
<version>3.1.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.cli.JakartaTransformerCLI" fork="true">
|
|
||||||
<arg value="-o" />
|
|
||||||
<arg value="${jakarta-transformer-sources}" />
|
|
||||||
<arg value="${jakarta-transformer-target}/tmp" />
|
|
||||||
<classpath>
|
|
||||||
<pathelement path="${plugin_classpath}" />
|
|
||||||
</classpath>
|
|
||||||
</java>
|
|
||||||
<touch>
|
|
||||||
<fileset dir="${jakarta-transformer-target}"/>
|
|
||||||
</touch>
|
|
||||||
<copy todir="${jakarta-transformer-target}" overwrite="false">
|
|
||||||
<fileset dir="${jakarta-transformer-target}/tmp"/>
|
|
||||||
</copy>
|
|
||||||
<delete dir="${jakarta-transformer-target}/tmp"/>
|
|
||||||
</target>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.eclipse.transformer</groupId>
|
|
||||||
<artifactId>org.eclipse.transformer.cli</artifactId>
|
|
||||||
<version>0.5.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.ant</groupId>
|
|
||||||
<artifactId>ant</artifactId>
|
|
||||||
<version>1.10.14</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -181,11 +181,6 @@
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-authz-client</artifactId>
|
<artifactId>keycloak-authz-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<artifactId>keycloak-saml-undertow-adapter-jakarta</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</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>
|
||||||
|
|
Loading…
Reference in a new issue