Remove remaining servlet filter adapter bits

Closes #29225

Signed-off-by: Douglas Palmer <dpalmer@redhat.com>
This commit is contained in:
Douglas Palmer 2024-05-02 09:11:30 -07:00 committed by Marek Posolda
parent e0176a7e31
commit 8a0322cc13
10 changed files with 0 additions and 1756 deletions

View file

@ -1,141 +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-jakarta-servlet-filter-adapter</artifactId>
<name>Keycloak SAML Jakarta Servlet Filter</name>
<description/>
<!--
NOTE: This maven module is generated using original: servlet-filter
@see: ${jakarta-transformer-sources}
Reason is the transition to Jakarta APIs.
-->
<properties>
<jakarta-transformer-sources>${project.basedir}/../servlet-filter/src</jakarta-transformer-sources>
<jakarta-transformer-target>${project.basedir}/src</jakarta-transformer-target>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-common</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jakarta-servlet-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-api-public</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-core-jakarta</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-crypto-default</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>transform</id>
<phase>initialize</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<property name="plugin_classpath" refid="maven.plugin.classpath" />
<java classname="org.eclipse.transformer.jakarta.JakartaTransformer">
<arg value="-o" />
<arg value="${jakarta-transformer-sources}" />
<arg value="${jakarta-transformer-target}" />
<classpath>
<pathelement path="${plugin_classpath}" />
</classpath>
</java>
</target>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.transformer</groupId>
<artifactId>org.eclipse.transformer.cli</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>ant-contrib</groupId>
<artifactId>ant-contrib</artifactId>
<version>1.0b3</version>
<exclusions>
<exclusion>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View file

@ -36,7 +36,6 @@
<module>core-jakarta</module>
<module>undertow</module>
<module>wildfly</module>
<module>jakarta-servlet-filter</module>
<module>wildfly-elytron</module>
<module>wildfly-elytron-jakarta</module>
</modules>

View file

@ -1,105 +0,0 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>999.0.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-jakarta-servlet-adapter-spi</artifactId>
<name>Keycloak Jakarta Servlet Integration</name>
<description/>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.servlet.*
</keycloak.osgi.export>
<keycloak.osgi.import>
*;resolution:=optional
</keycloak.osgi.import>
<keycloak.osgi.fragment>${project.groupId}.keycloak-jakarta-servlet-filter-adapter</keycloak.osgi.fragment>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-common</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
<Fragment-Host>${keycloak.osgi.fragment}</Fragment-Host>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,433 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.servlet;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.common.util.Encode;
import org.keycloak.common.util.MultivaluedHashMap;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.ReadListener;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Principal;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class FilterSessionStore implements AdapterSessionStore {
public static final String REDIRECT_URI = "__REDIRECT_URI";
public static final String SAVED_METHOD = "__SAVED_METHOD";
public static final String SAVED_HEADERS = "__SAVED_HEADERS";
public static final String SAVED_BODY = "__SAVED_BODY";
protected final HttpServletRequest request;
protected final HttpFacade facade;
protected final int maxBuffer;
protected byte[] restoredBuffer = null;
protected boolean needRequestRestore;
public FilterSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer) {
this.request = request;
this.facade = facade;
this.maxBuffer = maxBuffer;
}
public void clearSavedRequest(HttpSession session) {
session.removeAttribute(REDIRECT_URI);
session.removeAttribute(SAVED_METHOD);
session.removeAttribute(SAVED_HEADERS);
session.removeAttribute(SAVED_BODY);
}
public void servletRequestLogout() {
}
public static String getCharsetFromContentType(String contentType) {
if (contentType == null)
return (null);
int start = contentType.indexOf("charset=");
if (start < 0)
return (null);
String encoding = contentType.substring(start + 8);
int end = encoding.indexOf(';');
if (end >= 0)
encoding = encoding.substring(0, end);
encoding = encoding.trim();
if ((encoding.length() > 2) && (encoding.startsWith("\""))
&& (encoding.endsWith("\"")))
encoding = encoding.substring(1, encoding.length() - 1);
return (encoding.trim());
}
public HttpServletRequestWrapper buildWrapper(HttpSession session, final KeycloakAccount account) {
if (needRequestRestore) {
final String method = (String)session.getAttribute(SAVED_METHOD);
final byte[] body = (byte[])session.getAttribute(SAVED_BODY);
final MultivaluedHashMap<String, String> headers = (MultivaluedHashMap<String, String>)session.getAttribute(SAVED_HEADERS);
clearSavedRequest(session);
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
protected MultivaluedHashMap<String, String> parameters;
MultivaluedHashMap<String, String> getParams() {
if (parameters != null) return parameters;
if (body == null) return new MultivaluedHashMap<String, String>();
String contentType = getContentType();
if (contentType != null && contentType.toLowerCase().startsWith("application/x-www-form-urlencoded")) {
ByteArrayInputStream is = new ByteArrayInputStream(body);
try {
parameters = parseForm(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return parameters;
}
@Override
public boolean isUserInRole(String role) {
return account.getRoles().contains(role);
}
@Override
public Principal getUserPrincipal() {
return account.getPrincipal();
}
@Override
public String getMethod() {
if (needRequestRestore) {
return method;
} else {
return super.getMethod();
}
}
@Override
public String getHeader(String name) {
if (needRequestRestore && headers != null) {
return headers.getFirst(name.toLowerCase());
}
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaders(String name) {
if (needRequestRestore && headers != null) {
List<String> values = headers.getList(name.toLowerCase());
if (values == null) return Collections.emptyEnumeration();
else return Collections.enumeration(values);
}
return super.getHeaders(name);
}
@Override
public Enumeration<String> getHeaderNames() {
if (needRequestRestore && headers != null) {
return Collections.enumeration(headers.keySet());
}
return super.getHeaderNames();
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (needRequestRestore && body != null) {
final ByteArrayInputStream is = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return is.read();
}
@Override
public boolean isFinished() {
return is.available() == 0; // Check if the underlying stream has data available.
}
@Override
public boolean isReady() {
return true; // Return true to indicate that the data is always ready to be read.
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
};
}
return super.getInputStream();
}
@Override
public void logout() throws ServletException {
servletRequestLogout();
}
@Override
public long getDateHeader(String name) {
if (!needRequestRestore) return super.getDateHeader(name);
return -1;
}
@Override
public int getIntHeader(String name) {
if (!needRequestRestore) return super.getIntHeader(name);
String value = getHeader(name);
if (value == null) return -1;
return Integer.valueOf(value);
}
@Override
public String[] getParameterValues(String name) {
if (!needRequestRestore) return super.getParameterValues(name);
MultivaluedHashMap<String, String> formParams = getParams();
if (formParams == null) {
return super.getParameterValues(name);
}
String[] values = request.getParameterValues(name);
List<String> list = new LinkedList<>();
if (values != null) {
for (String val : values) list.add(val);
}
List<String> vals = formParams.get(name);
if (vals != null) list.addAll(vals);
return list.toArray(new String[list.size()]);
}
@Override
public Enumeration<String> getParameterNames() {
if (!needRequestRestore) return super.getParameterNames();
MultivaluedHashMap<String, String> formParams = getParams();
if (formParams == null) {
return super.getParameterNames();
}
Set<String> names = new HashSet<>();
Enumeration<String> qnames = super.getParameterNames();
while (qnames.hasMoreElements()) names.add(qnames.nextElement());
names.addAll(formParams.keySet());
return Collections.enumeration(names);
}
@Override
public Map<String, String[]> getParameterMap() {
if (!needRequestRestore) return super.getParameterMap();
MultivaluedHashMap<String, String> formParams = getParams();
if (formParams == null) {
return super.getParameterMap();
}
Map<String, String[]> map = new HashMap<>();
Enumeration<String> names = getParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String[] values = getParameterValues(name);
if (values != null) {
map.put(name, values);
}
}
return map;
}
@Override
public String getParameter(String name) {
if (!needRequestRestore) return super.getParameter(name);
String param = super.getParameter(name);
if (param != null) return param;
MultivaluedHashMap<String, String> formParams = getParams();
if (formParams == null) {
return null;
}
return formParams.getFirst(name);
}
@Override
public BufferedReader getReader() throws IOException {
if (!needRequestRestore) return super.getReader();
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public int getContentLength() {
if (!needRequestRestore) return super.getContentLength();
String header = getHeader("content-length");
if (header == null) return -1;
return Integer.valueOf(header);
}
@Override
public String getContentType() {
if (!needRequestRestore) return super.getContentType();
return getHeader("content-type");
}
@Override
public String getCharacterEncoding() {
if (!needRequestRestore) return super.getCharacterEncoding();
return getCharsetFromContentType(getContentType());
}
};
return wrapper;
} else {
return new HttpServletRequestWrapper(request) {
@Override
public boolean isUserInRole(String role) {
return account.getRoles().contains(role);
}
@Override
public Principal getUserPrincipal() {
if (account == null) return null;
return account.getPrincipal();
}
@Override
public void logout() throws ServletException {
servletRequestLogout();
}
};
}
}
public String getRedirectUri() {
HttpSession session = request.getSession(true);
return (String)session.getAttribute(REDIRECT_URI);
}
@Override
public boolean restoreRequest() {
HttpSession session = request.getSession(false);
if (session == null) return false;
return session.getAttribute(REDIRECT_URI) != null;
}
public static MultivaluedHashMap<String, String> parseForm(InputStream entityStream)
throws IOException
{
char[] buffer = new char[100];
StringBuffer buf = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream));
int wasRead = 0;
do
{
wasRead = reader.read(buffer, 0, 100);
if (wasRead > 0) buf.append(buffer, 0, wasRead);
} while (wasRead > -1);
String form = buf.toString();
MultivaluedHashMap<String, String> formData = new MultivaluedHashMap<String, String>();
if ("".equals(form)) return formData;
String[] params = form.split("&");
for (String param : params)
{
if (param.indexOf('=') >= 0)
{
String[] nv = param.split("=");
String val = nv.length > 1 ? nv[1] : "";
formData.add(Encode.decode(nv[0]), Encode.decode(val));
}
else
{
formData.add(Encode.decode(param), "");
}
}
return formData;
}
@Override
public void saveRequest() {
HttpSession session = request.getSession(true);
session.setAttribute(REDIRECT_URI, facade.getRequest().getURI());
session.setAttribute(SAVED_METHOD, request.getMethod());
MultivaluedHashMap<String, String> headers = new MultivaluedHashMap<>();
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
headers.add(name.toLowerCase(), values.nextElement());
}
}
session.setAttribute(SAVED_HEADERS, headers);
if (request.getMethod().equalsIgnoreCase("GET")) {
return;
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
int totalRead = 0;
try {
InputStream is = request.getInputStream();
while ( (bytesRead = is.read(buffer) ) >= 0) {
os.write(buffer, 0, bytesRead);
totalRead += bytesRead;
if (totalRead > maxBuffer) {
throw new RuntimeException("max buffer reached on a saved request");
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
byte[] body = os.toByteArray();
// Only save the request body if there is something to save
if (body.length > 0) {
session.setAttribute(SAVED_BODY, body);
}
}
}

View file

@ -1,268 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.servlet;
import org.keycloak.adapters.spi.AuthenticationError;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.LogoutError;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.ServerCookie;
import org.keycloak.common.util.UriUtils;
import javax.security.cert.X509Certificate;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ServletHttpFacade implements HttpFacade {
protected final RequestFacade requestFacade = new RequestFacade();
protected final ResponseFacade responseFacade = new ResponseFacade();
protected HttpServletRequest request;
protected HttpServletResponse response;
protected MultivaluedHashMap<String, String> queryParameters;
public ServletHttpFacade(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
protected class RequestFacade implements Request {
private InputStream inputStream;
@Override
public String getMethod() {
return request.getMethod();
}
@Override
public String getURI() {
StringBuffer buf = request.getRequestURL();
if (request.getQueryString() != null) {
buf.append('?').append(request.getQueryString());
}
return buf.toString();
}
@Override
public String getRelativePath() {
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String servletPath = uri.substring(uri.indexOf(contextPath) + contextPath.length());
if ("".equals(servletPath)) {
servletPath = "/";
}
return servletPath;
}
@Override
public boolean isSecure() {
return request.isSecure();
}
@Override
public String getFirstParam(String param) {
return request.getParameter(param);
}
@Override
public String getQueryParamValue(String param) {
if (queryParameters == null) {
queryParameters = UriUtils.decodeQueryString(request.getQueryString());
}
return queryParameters.getFirst(param);
}
public MultivaluedHashMap<String, String> getQueryParameters() {
if (queryParameters == null) {
queryParameters = UriUtils.decodeQueryString(request.getQueryString());
}
return queryParameters;
}
@Override
public Cookie getCookie(String cookieName) {
if (request.getCookies() == null) return null;
jakarta.servlet.http.Cookie cookie = null;
for (jakarta.servlet.http.Cookie c : request.getCookies()) {
if (c.getName().equals(cookieName)) {
cookie = c;
break;
}
}
if (cookie == null) return null;
return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
}
@Override
public String getHeader(String name) {
return request.getHeader(name);
}
@Override
public List<String> getHeaders(String name) {
Enumeration<String> values = request.getHeaders(name);
List<String> list = new LinkedList<>();
while (values.hasMoreElements()) list.add(values.nextElement());
return list;
}
@Override
public InputStream getInputStream() {
return getInputStream(false);
}
@Override
public InputStream getInputStream(boolean buffered) {
if (inputStream != null) {
return inputStream;
}
if (buffered) {
try {
return inputStream = new BufferedInputStream(request.getInputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
try {
return request.getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public String getRemoteAddr() {
return request.getRemoteAddr();
}
@Override
public void setError(AuthenticationError error) {
request.setAttribute(AuthenticationError.class.getName(), error);
}
@Override
public void setError(LogoutError error) {
request.setAttribute(LogoutError.class.getName(), error);
}
}
public boolean isEnded() {
return responseFacade.isEnded();
}
protected class ResponseFacade implements Response {
protected boolean ended;
@Override
public void setStatus(int status) {
response.setStatus(status);
}
@Override
public void addHeader(String name, String value) {
response.addHeader(name, value);
}
@Override
public void setHeader(String name, String value) {
response.setHeader(name, value);
}
@Override
public void resetCookie(String name, String path) {
setCookie(name, "", path, null, 0, false, false);
}
@Override
public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly) {
StringBuilder cookieBuf = new StringBuilder();
ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, null, maxAge, secure, httpOnly, null);
String cookie = cookieBuf.toString();
response.addHeader("Set-Cookie", cookie);
}
@Override
public OutputStream getOutputStream() {
try {
return response.getOutputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@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 void end() {
ended = true;
}
public boolean isEnded() {
return ended;
}
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
}

View file

@ -33,8 +33,6 @@
<modules>
<module>adapter-spi</module>
<module>undertow-adapter-spi</module>
<module>servlet-adapter-spi</module>
<module>jakarta-servlet-adapter-spi</module>
<module>jboss-adapter-core</module>
</modules>
</project>

View file

@ -1,105 +0,0 @@
<?xml version="1.0"?>
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>999.0.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-servlet-adapter-spi</artifactId>
<name>Keycloak Servlet Integration</name>
<description/>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.servlet.*
</keycloak.osgi.export>
<keycloak.osgi.import>
*;resolution:=optional
</keycloak.osgi.import>
<keycloak.osgi.fragment>${project.groupId}.keycloak-servlet-filter-adapter</keycloak.osgi.fragment>
</properties>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</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>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
<Fragment-Host>${keycloak.osgi.fragment}</Fragment-Host>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,418 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.servlet;
import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.common.util.Encode;
import org.keycloak.common.util.MultivaluedHashMap;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Principal;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class FilterSessionStore implements AdapterSessionStore {
public static final String REDIRECT_URI = "__REDIRECT_URI";
public static final String SAVED_METHOD = "__SAVED_METHOD";
public static final String SAVED_HEADERS = "__SAVED_HEADERS";
public static final String SAVED_BODY = "__SAVED_BODY";
protected final HttpServletRequest request;
protected final HttpFacade facade;
protected final int maxBuffer;
protected byte[] restoredBuffer = null;
protected boolean needRequestRestore;
public FilterSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer) {
this.request = request;
this.facade = facade;
this.maxBuffer = maxBuffer;
}
public void clearSavedRequest(HttpSession session) {
session.removeAttribute(REDIRECT_URI);
session.removeAttribute(SAVED_METHOD);
session.removeAttribute(SAVED_HEADERS);
session.removeAttribute(SAVED_BODY);
}
public void servletRequestLogout() {
}
public static String getCharsetFromContentType(String contentType) {
if (contentType == null)
return (null);
int start = contentType.indexOf("charset=");
if (start < 0)
return (null);
String encoding = contentType.substring(start + 8);
int end = encoding.indexOf(';');
if (end >= 0)
encoding = encoding.substring(0, end);
encoding = encoding.trim();
if ((encoding.length() > 2) && (encoding.startsWith("\""))
&& (encoding.endsWith("\"")))
encoding = encoding.substring(1, encoding.length() - 1);
return (encoding.trim());
}
public HttpServletRequestWrapper buildWrapper(HttpSession session, final KeycloakAccount account) {
if (needRequestRestore) {
final String method = (String)session.getAttribute(SAVED_METHOD);
final byte[] body = (byte[])session.getAttribute(SAVED_BODY);
final MultivaluedHashMap<String, String> headers = (MultivaluedHashMap<String, String>)session.getAttribute(SAVED_HEADERS);
clearSavedRequest(session);
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
protected MultivaluedHashMap<String, String> parameters;
MultivaluedHashMap<String, String> getParams() {
if (parameters != null) return parameters;
if (body == null) return new MultivaluedHashMap<String, String>();
String contentType = getContentType();
if (contentType != null && contentType.toLowerCase().startsWith("application/x-www-form-urlencoded")) {
ByteArrayInputStream is = new ByteArrayInputStream(body);
try {
parameters = parseForm(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return parameters;
}
@Override
public boolean isUserInRole(String role) {
return account.getRoles().contains(role);
}
@Override
public Principal getUserPrincipal() {
return account.getPrincipal();
}
@Override
public String getMethod() {
if (needRequestRestore) {
return method;
} else {
return super.getMethod();
}
}
@Override
public String getHeader(String name) {
if (needRequestRestore && headers != null) {
return headers.getFirst(name.toLowerCase());
}
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaders(String name) {
if (needRequestRestore && headers != null) {
List<String> values = headers.getList(name.toLowerCase());
if (values == null) return Collections.emptyEnumeration();
else return Collections.enumeration(values);
}
return super.getHeaders(name);
}
@Override
public Enumeration<String> getHeaderNames() {
if (needRequestRestore && headers != null) {
return Collections.enumeration(headers.keySet());
}
return super.getHeaderNames();
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (needRequestRestore && body != null) {
final ByteArrayInputStream is = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return is.read();
}
};
}
return super.getInputStream();
}
@Override
public void logout() throws ServletException {
servletRequestLogout();
}
@Override
public long getDateHeader(String name) {
if (!needRequestRestore) return super.getDateHeader(name);
return -1;
}
@Override
public int getIntHeader(String name) {
if (!needRequestRestore) return super.getIntHeader(name);
String value = getHeader(name);
if (value == null) return -1;
return Integer.valueOf(value);
}
@Override
public String[] getParameterValues(String name) {
if (!needRequestRestore) return super.getParameterValues(name);
MultivaluedHashMap<String, String> formParams = getParams();
if (formParams == null) {
return super.getParameterValues(name);
}
String[] values = request.getParameterValues(name);
List<String> list = new LinkedList<>();
if (values != null) {
for (String val : values) list.add(val);
}
List<String> vals = formParams.get(name);
if (vals != null) list.addAll(vals);
return list.toArray(new String[list.size()]);
}
@Override
public Enumeration<String> getParameterNames() {
if (!needRequestRestore) return super.getParameterNames();
MultivaluedHashMap<String, String> formParams = getParams();
if (formParams == null) {
return super.getParameterNames();
}
Set<String> names = new HashSet<>();
Enumeration<String> qnames = super.getParameterNames();
while (qnames.hasMoreElements()) names.add(qnames.nextElement());
names.addAll(formParams.keySet());
return Collections.enumeration(names);
}
@Override
public Map<String, String[]> getParameterMap() {
if (!needRequestRestore) return super.getParameterMap();
MultivaluedHashMap<String, String> formParams = getParams();
if (formParams == null) {
return super.getParameterMap();
}
Map<String, String[]> map = new HashMap<>();
Enumeration<String> names = getParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String[] values = getParameterValues(name);
if (values != null) {
map.put(name, values);
}
}
return map;
}
@Override
public String getParameter(String name) {
if (!needRequestRestore) return super.getParameter(name);
String param = super.getParameter(name);
if (param != null) return param;
MultivaluedHashMap<String, String> formParams = getParams();
if (formParams == null) {
return null;
}
return formParams.getFirst(name);
}
@Override
public BufferedReader getReader() throws IOException {
if (!needRequestRestore) return super.getReader();
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public int getContentLength() {
if (!needRequestRestore) return super.getContentLength();
String header = getHeader("content-length");
if (header == null) return -1;
return Integer.valueOf(header);
}
@Override
public String getContentType() {
if (!needRequestRestore) return super.getContentType();
return getHeader("content-type");
}
@Override
public String getCharacterEncoding() {
if (!needRequestRestore) return super.getCharacterEncoding();
return getCharsetFromContentType(getContentType());
}
};
return wrapper;
} else {
return new HttpServletRequestWrapper(request) {
@Override
public boolean isUserInRole(String role) {
return account.getRoles().contains(role);
}
@Override
public Principal getUserPrincipal() {
if (account == null) return null;
return account.getPrincipal();
}
@Override
public void logout() throws ServletException {
servletRequestLogout();
}
};
}
}
public String getRedirectUri() {
HttpSession session = request.getSession(true);
return (String)session.getAttribute(REDIRECT_URI);
}
@Override
public boolean restoreRequest() {
HttpSession session = request.getSession(false);
if (session == null) return false;
return session.getAttribute(REDIRECT_URI) != null;
}
public static MultivaluedHashMap<String, String> parseForm(InputStream entityStream)
throws IOException
{
char[] buffer = new char[100];
StringBuffer buf = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream));
int wasRead = 0;
do
{
wasRead = reader.read(buffer, 0, 100);
if (wasRead > 0) buf.append(buffer, 0, wasRead);
} while (wasRead > -1);
String form = buf.toString();
MultivaluedHashMap<String, String> formData = new MultivaluedHashMap<String, String>();
if ("".equals(form)) return formData;
String[] params = form.split("&");
for (String param : params)
{
if (param.indexOf('=') >= 0)
{
String[] nv = param.split("=");
String val = nv.length > 1 ? nv[1] : "";
formData.add(Encode.decode(nv[0]), Encode.decode(val));
}
else
{
formData.add(Encode.decode(param), "");
}
}
return formData;
}
@Override
public void saveRequest() {
HttpSession session = request.getSession(true);
session.setAttribute(REDIRECT_URI, facade.getRequest().getURI());
session.setAttribute(SAVED_METHOD, request.getMethod());
MultivaluedHashMap<String, String> headers = new MultivaluedHashMap<>();
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
headers.add(name.toLowerCase(), values.nextElement());
}
}
session.setAttribute(SAVED_HEADERS, headers);
if (request.getMethod().equalsIgnoreCase("GET")) {
return;
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
int totalRead = 0;
try {
InputStream is = request.getInputStream();
while ( (bytesRead = is.read(buffer) ) >= 0) {
os.write(buffer, 0, bytesRead);
totalRead += bytesRead;
if (totalRead > maxBuffer) {
throw new RuntimeException("max buffer reached on a saved request");
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
byte[] body = os.toByteArray();
// Only save the request body if there is something to save
if (body.length > 0) {
session.setAttribute(SAVED_BODY, body);
}
}
}

View file

@ -1,268 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.servlet;
import org.keycloak.adapters.spi.AuthenticationError;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.LogoutError;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.ServerCookie;
import org.keycloak.common.util.UriUtils;
import javax.security.cert.X509Certificate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ServletHttpFacade implements HttpFacade {
protected final RequestFacade requestFacade = new RequestFacade();
protected final ResponseFacade responseFacade = new ResponseFacade();
protected HttpServletRequest request;
protected HttpServletResponse response;
protected MultivaluedHashMap<String, String> queryParameters;
public ServletHttpFacade(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
protected class RequestFacade implements Request {
private InputStream inputStream;
@Override
public String getMethod() {
return request.getMethod();
}
@Override
public String getURI() {
StringBuffer buf = request.getRequestURL();
if (request.getQueryString() != null) {
buf.append('?').append(request.getQueryString());
}
return buf.toString();
}
@Override
public String getRelativePath() {
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String servletPath = uri.substring(uri.indexOf(contextPath) + contextPath.length());
if ("".equals(servletPath)) {
servletPath = "/";
}
return servletPath;
}
@Override
public boolean isSecure() {
return request.isSecure();
}
@Override
public String getFirstParam(String param) {
return request.getParameter(param);
}
@Override
public String getQueryParamValue(String param) {
if (queryParameters == null) {
queryParameters = UriUtils.decodeQueryString(request.getQueryString());
}
return queryParameters.getFirst(param);
}
public MultivaluedHashMap<String, String> getQueryParameters() {
if (queryParameters == null) {
queryParameters = UriUtils.decodeQueryString(request.getQueryString());
}
return queryParameters;
}
@Override
public Cookie getCookie(String cookieName) {
if (request.getCookies() == null) return null;
javax.servlet.http.Cookie cookie = null;
for (javax.servlet.http.Cookie c : request.getCookies()) {
if (c.getName().equals(cookieName)) {
cookie = c;
break;
}
}
if (cookie == null) return null;
return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
}
@Override
public String getHeader(String name) {
return request.getHeader(name);
}
@Override
public List<String> getHeaders(String name) {
Enumeration<String> values = request.getHeaders(name);
List<String> list = new LinkedList<>();
while (values.hasMoreElements()) list.add(values.nextElement());
return list;
}
@Override
public InputStream getInputStream() {
return getInputStream(false);
}
@Override
public InputStream getInputStream(boolean buffered) {
if (inputStream != null) {
return inputStream;
}
if (buffered) {
try {
return inputStream = new BufferedInputStream(request.getInputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
try {
return request.getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public String getRemoteAddr() {
return request.getRemoteAddr();
}
@Override
public void setError(AuthenticationError error) {
request.setAttribute(AuthenticationError.class.getName(), error);
}
@Override
public void setError(LogoutError error) {
request.setAttribute(LogoutError.class.getName(), error);
}
}
public boolean isEnded() {
return responseFacade.isEnded();
}
protected class ResponseFacade implements Response {
protected boolean ended;
@Override
public void setStatus(int status) {
response.setStatus(status);
}
@Override
public void addHeader(String name, String value) {
response.addHeader(name, value);
}
@Override
public void setHeader(String name, String value) {
response.setHeader(name, value);
}
@Override
public void resetCookie(String name, String path) {
setCookie(name, "", path, null, 0, false, false);
}
@Override
public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly) {
StringBuilder cookieBuf = new StringBuilder();
ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, null, maxAge, secure, httpOnly, null);
String cookie = cookieBuf.toString();
response.addHeader("Set-Cookie", cookie);
}
@Override
public OutputStream getOutputStream() {
try {
return response.getOutputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@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 void end() {
ended = true;
}
public boolean isEnded() {
return ended;
}
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
}

15
pom.xml
View file

@ -1003,16 +1003,6 @@
<artifactId>keycloak-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jakarta-servlet-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
@ -1063,11 +1053,6 @@
<artifactId>keycloak-js-adapter-jar</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-jakarta-servlet-filter-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-as7-adapter</artifactId>