diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 24a4a388252..ac7311a363e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,6 +2,7 @@ version: 2 updates: - package-ecosystem: "maven" directory: "/" + open-pull-requests-limit: 20 target-branch: "jetty-9.4.x" schedule: interval: "daily" @@ -21,9 +22,12 @@ updates: versions: [ ">=2.0.0" ] - dependency-name: "javax.websocket:*" versions: [ ">=1.1.0" ] + - dependency-name: "org.infinispan:*" + versions: [ ">= 12" ] - package-ecosystem: "maven" directory: "/" + open-pull-requests-limit: 20 target-branch: "jetty-10.0.x" schedule: interval: "daily" @@ -41,16 +45,22 @@ updates: versions: [ ">=4.0.0" ] - dependency-name: "jakarta.inject:*" versions: [ ">=2.0.0" ] + - dependency-name: "jakarta.interceptor:*" + versions: [ ">=2.0.0" ] - dependency-name: "jakarta.websocket:*" versions: [ ">=2.0.0" ] - dependency-name: "jakarta.servlet.jsp.jstl:*" versions: [ ">=2.0.0" ] - dependency-name: "org.jboss.weld.servlet:*" versions: [ ">=4.0.0" ] + - dependency-name: "jakarta.enterprise:jakarta.enterprise.cdi-api:*" + versions: [ ">=3.0.0" ] - dependency-name: "com.sun.xml.ws:jaxws*" versions: [ ">=3.0.0" ] - dependency-name: "jakarta.transaction:*" versions: [ ">=2.0.0" ] + - dependency-name: "org.infinispan:*" + versions: [ ">= 12" ] # - package-ecosystem: "maven" # directory: "/" diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java index 0eedd60672d..aebdedf5839 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/SecuredRedirectHandler.java @@ -33,6 +33,32 @@ import org.eclipse.jetty.util.URIUtil; */ public class SecuredRedirectHandler extends HandlerWrapper { + /** + * The redirect code to send in response. + */ + private final int _redirectCode; + + /** + * Uses moved temporarily code (302) as the redirect code. + */ + public SecuredRedirectHandler() + { + this(HttpServletResponse.SC_MOVED_TEMPORARILY); + } + + /** + * Use supplied code as the redirect code. + * + * @param code the redirect code to use in the response + * @throws IllegalArgumentException if parameter is an invalid redirect code + */ + public SecuredRedirectHandler(final int code) + { + if (!HttpStatus.isRedirection(code)) + throw new IllegalArgumentException("Not a 3xx redirect code"); + _redirectCode = code; + } + @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { @@ -59,7 +85,7 @@ public class SecuredRedirectHandler extends HandlerWrapper String secureScheme = httpConfig.getSecureScheme(); String url = URIUtil.newURI(secureScheme, baseRequest.getServerName(), securePort, baseRequest.getRequestURI(), baseRequest.getQueryString()); response.setContentLength(0); - baseRequest.getResponse().sendRedirect(HttpServletResponse.SC_MOVED_TEMPORARILY, url, true); + baseRequest.getResponse().sendRedirect(_redirectCode, url, true); } else { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/SecuredRedirectHandlerCodeTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/SecuredRedirectHandlerCodeTest.java new file mode 100644 index 00000000000..615f0aa5e1f --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/SecuredRedirectHandlerCodeTest.java @@ -0,0 +1,177 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.server.handler; + +import java.io.File; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class SecuredRedirectHandlerCodeTest +{ + private Server server; + private HostnameVerifier origVerifier; + private SSLSocketFactory origSsf; + private URI serverHttpUri; + private URI serverHttpsUri; + + @Test + public void testConstructorRedirectRangeValid() + { + assertDoesNotThrow(() -> new SecuredRedirectHandler(300)); + assertDoesNotThrow(() -> new SecuredRedirectHandler(399)); + } + + @Test + public void testConstructorRedirectRangeInvalid() + { + assertThrows(IllegalArgumentException.class, () -> new SecuredRedirectHandler(299)); + assertThrows(IllegalArgumentException.class, () -> new SecuredRedirectHandler(400)); + } + + @Test + public void testRedirectUnsecuredRootMovedTemporarily() throws Exception + { + try + { + startServer(HttpServletResponse.SC_MOVED_TEMPORARILY); + URL url = serverHttpUri.resolve("/").toURL(); + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setInstanceFollowRedirects(false); + connection.setAllowUserInteraction(false); + assertThat("response code", connection.getResponseCode(), is(302)); + assertThat("location header", connection.getHeaderField("Location"), is(serverHttpsUri.resolve("/").toASCIIString())); + connection.disconnect(); + } + finally + { + stopServer(); + } + } + + @Test + public void testRedirectUnsecuredRootMovedPermanently() throws Exception + { + try + { + startServer(HttpServletResponse.SC_MOVED_PERMANENTLY); + URL url = serverHttpUri.resolve("/").toURL(); + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setInstanceFollowRedirects(false); + connection.setAllowUserInteraction(false); + assertThat("response code", connection.getResponseCode(), is(301)); + assertThat("location header", connection.getHeaderField("Location"), is(serverHttpsUri.resolve("/").toASCIIString())); + connection.disconnect(); + } + finally + { + stopServer(); + } + } + + private void startServer(int redirectCode) throws Exception + { + // Setup SSL + File keystore = MavenTestingUtils.getTestResourceFile("keystore.p12"); + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(keystore.getAbsolutePath()); + sslContextFactory.setKeyStorePassword("storepwd"); + + server = new Server(); + + int port = 32080; + int securePort = 32443; + + // Setup HTTP Configuration + HttpConfiguration httpConf = new HttpConfiguration(); + httpConf.setSecurePort(securePort); + httpConf.setSecureScheme("https"); + + ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConf)); + httpConnector.setName("unsecured"); + httpConnector.setPort(port); + + // Setup HTTPS Configuration + HttpConfiguration httpsConf = new HttpConfiguration(httpConf); + httpsConf.addCustomizer(new SecureRequestCustomizer()); + + ServerConnector httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(httpsConf)); + httpsConnector.setName("secured"); + httpsConnector.setPort(securePort); + + // Add connectors + server.setConnectors(new Connector[]{httpConnector, httpsConnector}); + + // Wire up context for unsecure handling to only + // the named 'unsecured' connector + ContextHandler redirectHandler = new ContextHandler(); + redirectHandler.setContextPath("/"); + redirectHandler.setHandler(new SecuredRedirectHandler(redirectCode)); + redirectHandler.setVirtualHosts(new String[]{"@unsecured"}); + + // Establish all handlers that have a context + ContextHandlerCollection contextHandlers = new ContextHandlerCollection(); + contextHandlers.setHandlers(new Handler[]{redirectHandler}); + + // Create server level handler tree + server.setHandler(new HandlerList(contextHandlers, new DefaultHandler())); + + server.start(); + + // calculate serverUri + String host = httpConnector.getHost(); + if (host == null) + { + host = "localhost"; + } + serverHttpUri = new URI(String.format("http://%s:%d/", host, httpConnector.getLocalPort())); + serverHttpsUri = new URI(String.format("https://%s:%d/", host, httpsConnector.getLocalPort())); + + origVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); + origSsf = HttpsURLConnection.getDefaultSSLSocketFactory(); + + HttpsURLConnection.setDefaultHostnameVerifier(new AllowAllVerifier()); + HttpsURLConnection.setDefaultSSLSocketFactory(sslContextFactory.getSslContext().getSocketFactory()); + } + + private void stopServer() throws Exception + { + HttpsURLConnection.setDefaultSSLSocketFactory(origSsf); + HttpsURLConnection.setDefaultHostnameVerifier(origVerifier); + + server.stop(); + server.join(); + } +} diff --git a/pom.xml b/pom.xml index 971714a82d1..1f306e67a11 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ 1.43.2 2.8.9 31.0.1-jre - 5.0.1 + 5.1.0 2.2 2.14.4 4.2.4 @@ -167,7 +167,7 @@ 3.2.1 3.3.2 4.5.3.0 - 2.8.1 + 2.9.0 false