Remove keycloak-undertow-adapter-spi
closes #31489 Signed-off-by: mposolda <mposolda@gmail.com>
This commit is contained in:
parent
d9555e4c9f
commit
59e1b4271a
10 changed files with 0 additions and 874 deletions
|
@ -32,7 +32,6 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>adapter-spi</module>
|
<module>adapter-spi</module>
|
||||||
<module>undertow-adapter-spi</module>
|
|
||||||
<module>jboss-adapter-core</module>
|
<module>jboss-adapter-core</module>
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -1,73 +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-undertow-adapter-spi</artifactId>
|
|
||||||
<name>Keycloak Undertow Integration SPI</name>
|
|
||||||
<description/>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<!-- We still need to support EAP 8, set the Java version to 11. -->
|
|
||||||
<maven.compiler.release>11</maven.compiler.release>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jboss.logging</groupId>
|
|
||||||
<artifactId>jboss-logging</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-adapter-spi</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-common</artifactId>
|
|
||||||
</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,68 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.session.Session;
|
|
||||||
import io.undertow.servlet.api.DeploymentInfo;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import io.undertow.servlet.spec.HttpSessionImpl;
|
|
||||||
import io.undertow.servlet.spec.ServletContextImpl;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.security.AccessController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class ChangeSessionId {
|
|
||||||
/**
|
|
||||||
* This is a hack to be backward compatible between Undertow 1.3+ and versions lower. In Undertow 1.3, a new
|
|
||||||
* switch was added setChangeSessionIdOnLogin, this screws up session management for keycloak as after the session id
|
|
||||||
* is uploaded to Keycloak, undertow changes the session id and it can't be invalidated.
|
|
||||||
*
|
|
||||||
* @param deploymentInfo
|
|
||||||
*/
|
|
||||||
public static void turnOffChangeSessionIdOnLogin(DeploymentInfo deploymentInfo) {
|
|
||||||
try {
|
|
||||||
Method method = DeploymentInfo.class.getMethod("setChangeSessionIdOnLogin", boolean.class);
|
|
||||||
method.invoke(deploymentInfo, false);
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String changeSessionId(HttpServerExchange exchange, boolean create) {
|
|
||||||
final ServletRequestContext sc = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
ServletContextImpl currentServletContext = sc.getCurrentServletContext();
|
|
||||||
HttpSessionImpl session = currentServletContext.getSession(exchange, create);
|
|
||||||
if (session == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Session underlyingSession;
|
|
||||||
if(System.getSecurityManager() == null) {
|
|
||||||
underlyingSession = session.getSession();
|
|
||||||
} else {
|
|
||||||
underlyingSession = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(session));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return underlyingSession.changeSessionId(exchange, currentServletContext.getSessionConfig());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.session.Session;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import io.undertow.servlet.spec.HttpSessionImpl;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.security.AccessController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saved servlet request.
|
|
||||||
*
|
|
||||||
* Note bill burke: I had to fork this because Undertow was automatically restoring the request before the code could be
|
|
||||||
* processed and redirected.
|
|
||||||
*
|
|
||||||
* CachedAuthenticatedSessionHandler was restoring the request before the authentication manager could read the code from the URI
|
|
||||||
* Originally, I copied SavedRequest as is, but there are type mismatches between Undertow 1.1.1 and 1.3.10.
|
|
||||||
* So, trySaveRequest calls the same undertow version, removes the saved request, stores it in a different session attribute,
|
|
||||||
* then restores the old attribute later
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Stuart Douglas
|
|
||||||
*/
|
|
||||||
public class SavedRequest implements Serializable {
|
|
||||||
|
|
||||||
private static final String SESSION_KEY = SavedRequest.class.getName();
|
|
||||||
|
|
||||||
public static void trySaveRequest(final HttpServerExchange exchange) {
|
|
||||||
io.undertow.servlet.util.SavedRequest.trySaveRequest(exchange);
|
|
||||||
final ServletRequestContext sc = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpSessionImpl session = sc.getCurrentServletContext().getSession(exchange, true);
|
|
||||||
Session underlyingSession;
|
|
||||||
if(System.getSecurityManager() == null) {
|
|
||||||
underlyingSession = session.getSession();
|
|
||||||
} else {
|
|
||||||
underlyingSession = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(session));
|
|
||||||
}
|
|
||||||
io.undertow.servlet.util.SavedRequest request = (io.undertow.servlet.util.SavedRequest) underlyingSession.removeAttribute(io.undertow.servlet.util.SavedRequest.class.getName());
|
|
||||||
if (request != null) underlyingSession.setAttribute(SESSION_KEY, request);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void tryRestoreRequest(final HttpServerExchange exchange, HttpSession session) {
|
|
||||||
if(session instanceof HttpSessionImpl) {
|
|
||||||
|
|
||||||
Session underlyingSession;
|
|
||||||
if(System.getSecurityManager() == null) {
|
|
||||||
underlyingSession = ((HttpSessionImpl) session).getSession();
|
|
||||||
} else {
|
|
||||||
underlyingSession = AccessController.doPrivileged(new HttpSessionImpl.UnwrapSessionAction(session));
|
|
||||||
}
|
|
||||||
io.undertow.servlet.util.SavedRequest request = (io.undertow.servlet.util.SavedRequest) underlyingSession.removeAttribute(SESSION_KEY);
|
|
||||||
if (request != null) {
|
|
||||||
underlyingSession.setAttribute(io.undertow.servlet.util.SavedRequest.class.getName(), request);
|
|
||||||
io.undertow.servlet.util.SavedRequest.tryRestoreRequest(exchange, session);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,96 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import org.keycloak.adapters.spi.AuthenticationError;
|
|
||||||
import org.keycloak.adapters.spi.LogoutError;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class ServletHttpFacade extends UndertowHttpFacade {
|
|
||||||
protected HttpServletRequest request;
|
|
||||||
protected HttpServletResponse response;
|
|
||||||
|
|
||||||
public ServletHttpFacade(HttpServerExchange exchange) {
|
|
||||||
super(exchange);
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
request = (HttpServletRequest)servletRequestContext.getServletRequest();
|
|
||||||
response = (HttpServletResponse)servletRequestContext.getServletResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class RequestFacade extends UndertowHttpFacade.RequestFacade {
|
|
||||||
@Override
|
|
||||||
public String getFirstParam(String param) {
|
|
||||||
return request.getParameter(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setError(AuthenticationError error) {
|
|
||||||
request.setAttribute(AuthenticationError.class.getName(), error);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setError(LogoutError error) {
|
|
||||||
request.setAttribute(LogoutError.class.getName(), error);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class ResponseFacade extends UndertowHttpFacade.ResponseFacade {
|
|
||||||
// can't call sendError from a challenge. Undertow ends up calling send error.
|
|
||||||
/*
|
|
||||||
@Override
|
|
||||||
public void sendError(int code) {
|
|
||||||
try {
|
|
||||||
response.sendError(code);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendError(int code, String message) {
|
|
||||||
try {
|
|
||||||
response.sendError(code, message);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response getResponse() {
|
|
||||||
return new ResponseFacade();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Request getRequest() {
|
|
||||||
return new RequestFacade();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.session.SessionManager;
|
|
||||||
import org.keycloak.adapters.spi.UserSessionManagement;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class SessionManagementBridge implements UserSessionManagement {
|
|
||||||
|
|
||||||
protected UndertowUserSessionManagement userSessionManagement;
|
|
||||||
protected SessionManager sessionManager;
|
|
||||||
|
|
||||||
public SessionManagementBridge(UndertowUserSessionManagement userSessionManagement, SessionManager sessionManager) {
|
|
||||||
this.userSessionManagement = userSessionManagement;
|
|
||||||
this.sessionManager = sessionManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logoutAll() {
|
|
||||||
userSessionManagement.logoutAll(sessionManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logoutHttpSessions(List<String> ids) {
|
|
||||||
userSessionManagement.logoutHttpSessions(sessionManager, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,298 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.handlers.CookieImpl;
|
|
||||||
import io.undertow.server.handlers.form.FormData;
|
|
||||||
import io.undertow.server.handlers.form.FormData.FormValue;
|
|
||||||
import io.undertow.server.handlers.form.FormDataParser;
|
|
||||||
import io.undertow.server.handlers.form.FormParserFactory;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import io.undertow.util.AttachmentKey;
|
|
||||||
import io.undertow.util.Headers;
|
|
||||||
import io.undertow.util.HttpString;
|
|
||||||
import org.keycloak.adapters.spi.AuthenticationError;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
import org.keycloak.adapters.spi.LogoutError;
|
|
||||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
|
||||||
|
|
||||||
import javax.security.cert.X509Certificate;
|
|
||||||
import javax.servlet.ServletInputStream;
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletRequestWrapper;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UndertowHttpFacade implements HttpFacade {
|
|
||||||
public static final AttachmentKey<AuthenticationError> AUTH_ERROR_ATTACHMENT_KEY = AttachmentKey.create(AuthenticationError.class);
|
|
||||||
public static final AttachmentKey<LogoutError> LOGOUT_ERROR_ATTACHMENT_KEY = AttachmentKey.create(LogoutError.class);
|
|
||||||
|
|
||||||
protected HttpServerExchange exchange;
|
|
||||||
protected RequestFacade requestFacade = new RequestFacade();
|
|
||||||
protected ResponseFacade responseFacade = new ResponseFacade();
|
|
||||||
|
|
||||||
public UndertowHttpFacade(HttpServerExchange exchange) {
|
|
||||||
this.exchange = exchange;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Request getRequest() {
|
|
||||||
return requestFacade;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response getResponse() {
|
|
||||||
return responseFacade;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public X509Certificate[] getCertificateChain() {
|
|
||||||
X509Certificate[] chain = new X509Certificate[0];
|
|
||||||
try {
|
|
||||||
chain = exchange.getConnection().getSslSessionInfo().getPeerCertificateChain();
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
|
|
||||||
}
|
|
||||||
return chain;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class RequestFacade implements Request {
|
|
||||||
|
|
||||||
private InputStream inputStream;
|
|
||||||
private final FormParserFactory formParserFactory = FormParserFactory.builder().build();
|
|
||||||
private FormData formData;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getURI() {
|
|
||||||
KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(exchange.getRequestURI())
|
|
||||||
.replaceQuery(exchange.getQueryString());
|
|
||||||
if (!exchange.isHostIncludedInRequestURI()) uriBuilder.scheme(exchange.getRequestScheme()).host(exchange.getHostAndPort());
|
|
||||||
return uriBuilder.buildAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRelativePath() {
|
|
||||||
return exchange.getRelativePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSecure() {
|
|
||||||
String protocol = exchange.getRequestScheme();
|
|
||||||
return protocol.equalsIgnoreCase("https");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFirstParam(String param) {
|
|
||||||
Deque<String> values = exchange.getQueryParameters().get(param);
|
|
||||||
|
|
||||||
if (values != null && !values.isEmpty()) {
|
|
||||||
return values.getFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formData == null && "post".equalsIgnoreCase(getMethod())) {
|
|
||||||
FormDataParser parser = formParserFactory.createParser(exchange);
|
|
||||||
try {
|
|
||||||
formData = parser.parseBlocking();
|
|
||||||
} catch (IOException cause) {
|
|
||||||
throw new RuntimeException("Failed to parse form parameters", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formData != null) {
|
|
||||||
Deque<FormValue> formValues = formData.get(param);
|
|
||||||
|
|
||||||
if (formValues != null && !formValues.isEmpty()) {
|
|
||||||
FormValue firstValue = formValues.getFirst();
|
|
||||||
|
|
||||||
if (!firstValue.isFile()) {
|
|
||||||
return firstValue.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getQueryParamValue(String param) {
|
|
||||||
Map<String,Deque<String>> queryParameters = exchange.getQueryParameters();
|
|
||||||
if (queryParameters == null) return null;
|
|
||||||
Deque<String> strings = queryParameters.get(param);
|
|
||||||
if (strings == null) return null;
|
|
||||||
return strings.getFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cookie getCookie(String cookieName) {
|
|
||||||
Map<String, io.undertow.server.handlers.Cookie> requestCookies = exchange.getRequestCookies();
|
|
||||||
if (requestCookies == null) return null;
|
|
||||||
io.undertow.server.handlers.Cookie cookie = requestCookies.get(cookieName);
|
|
||||||
if (cookie == null) return null;
|
|
||||||
return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getHeaders(String name) {
|
|
||||||
return exchange.getRequestHeaders().get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMethod() {
|
|
||||||
return exchange.getRequestMethod().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHeader(String name) {
|
|
||||||
return exchange.getRequestHeaders().getFirst(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getInputStream() {
|
|
||||||
return getInputStream(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getInputStream(boolean buffered) {
|
|
||||||
if (!exchange.isBlocking()) exchange.startBlocking();
|
|
||||||
|
|
||||||
if (inputStream != null) {
|
|
||||||
return inputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffered) {
|
|
||||||
ServletRequestContext context = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
ServletRequest servletRequest = context.getServletRequest();
|
|
||||||
|
|
||||||
inputStream = new BufferedInputStream(exchange.getInputStream());
|
|
||||||
|
|
||||||
context.setServletRequest(UndertowHttpServletRequest.setupServletInputStream(servletRequest, inputStream));
|
|
||||||
return inputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
return exchange.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRemoteAddr() {
|
|
||||||
InetSocketAddress sourceAddress = exchange.getSourceAddress();
|
|
||||||
if (sourceAddress == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
InetAddress address = sourceAddress.getAddress();
|
|
||||||
if (address == null) {
|
|
||||||
// this is unresolved, so we just return the host name not exactly spec, but if the name should be
|
|
||||||
// resolved then a PeerNameResolvingHandler should be used and this is probably better than just
|
|
||||||
// returning null
|
|
||||||
return sourceAddress.getHostString();
|
|
||||||
}
|
|
||||||
return address.getHostAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setError(AuthenticationError error) {
|
|
||||||
exchange.putAttachment(AUTH_ERROR_ATTACHMENT_KEY, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setError(LogoutError error) {
|
|
||||||
exchange.putAttachment(LOGOUT_ERROR_ATTACHMENT_KEY, error);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class ResponseFacade implements Response {
|
|
||||||
@Override
|
|
||||||
public void setStatus(int status) {
|
|
||||||
exchange.setResponseCode(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addHeader(String name, String value) {
|
|
||||||
exchange.getResponseHeaders().add(new HttpString(name), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHeader(String name, String value) {
|
|
||||||
exchange.getResponseHeaders().put(new HttpString(name), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetCookie(String name, String path) {
|
|
||||||
CookieImpl cookie = new CookieImpl(name, "");
|
|
||||||
cookie.setMaxAge(0);
|
|
||||||
cookie.setPath(path);
|
|
||||||
exchange.setResponseCookie(cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly) {
|
|
||||||
CookieImpl cookie = new CookieImpl(name, value);
|
|
||||||
cookie.setPath(path);
|
|
||||||
cookie.setDomain(domain);
|
|
||||||
cookie.setMaxAge(maxAge);
|
|
||||||
cookie.setSecure(secure);
|
|
||||||
cookie.setHttpOnly(httpOnly);
|
|
||||||
exchange.setResponseCookie(cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OutputStream getOutputStream() {
|
|
||||||
if (!exchange.isBlocking()) exchange.startBlocking();
|
|
||||||
return exchange.getOutputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendError(int code) {
|
|
||||||
exchange.setResponseCode(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendError(int code, String message) {
|
|
||||||
exchange.setResponseCode(code);
|
|
||||||
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/html");
|
|
||||||
try {
|
|
||||||
exchange.getOutputStream().write(message.getBytes());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
exchange.endExchange();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void end() {
|
|
||||||
exchange.endExchange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +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.adapters.undertow;
|
|
||||||
|
|
||||||
import javax.servlet.ServletInputStream;
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletRequestWrapper;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public class UndertowHttpServletRequest {
|
|
||||||
|
|
||||||
public static HttpServletRequestWrapper setupServletInputStream(ServletRequest servletRequest, final InputStream inputStream) {
|
|
||||||
return new HttpServletRequestWrapper((HttpServletRequest) servletRequest) {
|
|
||||||
@Override
|
|
||||||
public ServletInputStream getInputStream() {
|
|
||||||
inputStream.mark(0);
|
|
||||||
return new ServletInputStream() {
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
return inputStream.read();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,160 +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.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.session.Session;
|
|
||||||
import io.undertow.server.session.SessionConfig;
|
|
||||||
import io.undertow.server.session.SessionListener;
|
|
||||||
import io.undertow.server.session.SessionManager;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages relationship to users and sessions so that forced admin logout can be implemented
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UndertowUserSessionManagement implements SessionListener {
|
|
||||||
private static final Logger log = Logger.getLogger(UndertowUserSessionManagement.class);
|
|
||||||
protected volatile boolean registered;
|
|
||||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
||||||
|
|
||||||
public void login(SessionManager manager) {
|
|
||||||
if (!registered) {
|
|
||||||
manager.registerSessionListener(this);
|
|
||||||
registered = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method runs the given runnable in the current thread if the session manager does not use distributed sessions,
|
|
||||||
* or in a separate thread if it does. This is to work around:
|
|
||||||
* <pre>
|
|
||||||
* org.infinispan.util.concurrent.TimeoutException: ISPN000299: Unable to acquire lock after 15 seconds for key SessionCreationMetaDataKey
|
|
||||||
* </pre>
|
|
||||||
* See https://issues.jboss.org/browse/KEYCLOAK-9822
|
|
||||||
* @param r
|
|
||||||
*/
|
|
||||||
private void workaroundIspnDeadlock(final SessionManager manager, Runnable r) {
|
|
||||||
if (manager.getClass().getName().equals("org.wildfly.clustering.web.undertow.session.DistributableSessionManager")) {
|
|
||||||
executor.submit(r);
|
|
||||||
} else {
|
|
||||||
r.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void logoutAll(final SessionManager manager) {
|
|
||||||
final Set<String> allSessions = manager.getAllSessions();
|
|
||||||
workaroundIspnDeadlock(manager, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (String sessionId : allSessions) logoutSession(manager, sessionId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void logoutHttpSessions(final SessionManager manager, final List<String> sessionIds) {
|
|
||||||
log.debugf("logoutHttpSessions: %s", sessionIds);
|
|
||||||
|
|
||||||
workaroundIspnDeadlock(manager, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (String sessionId : sessionIds) {
|
|
||||||
logoutSession(manager, sessionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void logoutSession(SessionManager manager, String httpSessionId) {
|
|
||||||
log.debugf("logoutHttpSession: %s", httpSessionId);
|
|
||||||
Session session = getSessionById(manager, httpSessionId);
|
|
||||||
try {
|
|
||||||
if (session != null) session.invalidate(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warnf("Session %s not present or already invalidated.", httpSessionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Session getSessionById(SessionManager manager, final String sessionId) {
|
|
||||||
// TODO: Workaround for WFLY-3345. Remove this once we move to wildfly 8.2
|
|
||||||
if (manager.getClass().getName().equals("org.wildfly.clustering.web.undertow.session.DistributableSessionManager")) {
|
|
||||||
return manager.getSession(null, new SessionConfig() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSessionId(HttpServerExchange exchange, String sessionId) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearSession(HttpServerExchange exchange, String sessionId) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String findSessionId(HttpServerExchange exchange) {
|
|
||||||
return sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SessionCookieSource sessionCookieSource(HttpServerExchange exchange) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String rewriteUrl(String originalUrl, String sessionId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return manager.getSession(sessionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sessionCreated(Session session, HttpServerExchange exchange) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sessionDestroyed(Session session, HttpServerExchange exchange, SessionDestroyedReason reason) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sessionIdChanged(Session session, String oldSessionId) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attributeAdded(Session session, String name, Object value) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attributeUpdated(Session session, String name, Object newValue, Object oldValue) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attributeRemoved(Session session, String name, Object oldValue) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
5
pom.xml
5
pom.xml
|
@ -997,11 +997,6 @@
|
||||||
<artifactId>keycloak-saml-as7-subsystem</artifactId>
|
<artifactId>keycloak-saml-as7-subsystem</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-wildfly-elytron-adapter</artifactId>
|
<artifactId>keycloak-saml-wildfly-elytron-adapter</artifactId>
|
||||||
|
|
Loading…
Reference in a new issue