From e52bd7a9c76d2fc82dca9e9acc10587f1327825a Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 15:08:49 +1000 Subject: [PATCH 01/10] Jetty 12.0.x Simplify tests with OpenId by using an exising openid provider and avoid extra not necessary maven modules (#12274) * use keycloak as openid provider for testing * merge all test-distribution into a single one --------- Signed-off-by: Olivier Lamy --- tests/test-distribution/pom.xml | 2 - .../test-distribution-common/pom.xml | 44 +- .../distribution/DisableUrlCacheTest.java | 3 +- .../jetty/tests/distribution/OpenIdTests.java | 203 +++++++++ .../test/resources/jetty-logging.properties | 3 + .../test-ee10-distribution/pom.xml | 71 ---- .../ee10/tests/distribution/OpenIdTests.java | 119 ------ .../tests/distribution/openid/JwtEncoder.java | 53 --- .../distribution/openid/OpenIdProvider.java | 402 ------------------ .../test-ee9-distribution/pom.xml | 72 ---- .../ee9/tests/distribution/OpenIdTests.java | 120 ------ .../tests/distribution/openid/JwtEncoder.java | 53 --- .../distribution/openid/OpenIdProvider.java | 402 ------------------ 13 files changed, 239 insertions(+), 1308 deletions(-) rename tests/test-distribution/{test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10 => test-distribution-common/src/test/java/org/eclipse/jetty}/tests/distribution/DisableUrlCacheTest.java (98%) create mode 100644 tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/OpenIdTests.java delete mode 100644 tests/test-distribution/test-ee10-distribution/pom.xml delete mode 100644 tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/OpenIdTests.java delete mode 100644 tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/openid/JwtEncoder.java delete mode 100644 tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/openid/OpenIdProvider.java delete mode 100644 tests/test-distribution/test-ee9-distribution/pom.xml delete mode 100644 tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/OpenIdTests.java delete mode 100644 tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/openid/JwtEncoder.java delete mode 100644 tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/openid/OpenIdProvider.java diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml index 245bcb68740..18e45fd929b 100644 --- a/tests/test-distribution/pom.xml +++ b/tests/test-distribution/pom.xml @@ -13,8 +13,6 @@ test-distribution-common - test-ee10-distribution - test-ee9-distribution diff --git a/tests/test-distribution/test-distribution-common/pom.xml b/tests/test-distribution/test-distribution-common/pom.xml index b83a3ba2775..eb1f703aed0 100644 --- a/tests/test-distribution/test-distribution-common/pom.xml +++ b/tests/test-distribution/test-distribution-common/pom.xml @@ -15,9 +15,21 @@ ${project.groupId}.tests.distribution.common 2 + 3.4.0 + + com.github.dasniko + testcontainers-keycloak + ${testcontainers-keycloak.version} + + + io.quarkus + quarkus-junit4-mock + + + org.apache.maven maven-artifact @@ -138,6 +150,12 @@ org.eclipse.jetty jetty-infinispan-common test + + + io.smallrye + jandex + + org.eclipse.jetty @@ -155,6 +173,20 @@ jetty-util-ajax test + + org.eclipse.jetty.ee10 + jetty-ee10-test-log4j2-webapp + ${project.version} + war + test + + + org.eclipse.jetty.ee9 + jetty-ee9-test-openid-webapp + ${project.version} + war + test + org.eclipse.jetty.http2 jetty-http2-client @@ -244,18 +276,6 @@ - - org.apache.maven.plugins - maven-jar-plugin - ${maven.jar.plugin.version} - - - - test-jar - - - - org.apache.maven.plugins maven-surefire-plugin diff --git a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/DisableUrlCacheTest.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java similarity index 98% rename from tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/DisableUrlCacheTest.java rename to tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java index 81299ef8038..9b8a2919088 100644 --- a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/DisableUrlCacheTest.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.ee10.tests.distribution; +package org.eclipse.jetty.tests.distribution; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -23,7 +23,6 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest; import org.eclipse.jetty.tests.testers.JettyHomeTester; import org.eclipse.jetty.tests.testers.Tester; import org.eclipse.jetty.toolchain.test.FS; diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/OpenIdTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/OpenIdTests.java new file mode 100644 index 00000000000..c96fffe5cff --- /dev/null +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/OpenIdTests.java @@ -0,0 +1,203 @@ +// +// ======================================================================== +// Copyright (c) 1995 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.tests.distribution; + +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +import dasniko.testcontainers.keycloak.KeycloakContainer; +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.tests.testers.JettyHomeTester; +import org.eclipse.jetty.tests.testers.Tester; +import org.eclipse.jetty.util.Fields; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.keycloak.admin.client.CreatedResponseUtil; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class OpenIdTests extends AbstractJettyHomeTest +{ + private static final Logger LOGGER = LoggerFactory.getLogger(OpenIdTests.class); + + private static final KeycloakContainer KEYCLOAK_CONTAINER = new KeycloakContainer(); + + private static final String clientId = "jetty-api"; + private static final String clientSecret = "JettyRocks!"; + + private static final String userName = "jetty"; + private static final String password = "JettyRocks!Really"; + + private static final String firstName = "John"; + private static final String lastName = "Doe"; + private static final String email = "jetty@jetty.org"; + + private static String userId; + + @BeforeAll + public static void startKeycloak() + { + KEYCLOAK_CONTAINER.start(); + // init keycloak + try (Keycloak keycloak = KEYCLOAK_CONTAINER.getKeycloakAdminClient()) + { + RealmRepresentation jettyRealm = new RealmRepresentation(); + jettyRealm.setId("jetty"); + jettyRealm.setRealm("jetty"); + jettyRealm.setEnabled(true); + keycloak.realms().create(jettyRealm); + + ClientRepresentation clientRepresentation = new ClientRepresentation(); + clientRepresentation.setClientId(clientId); + clientRepresentation.setSecret(clientSecret); + clientRepresentation.setRedirectUris(List.of("http://localhost:*")); + clientRepresentation.setEnabled(true); + clientRepresentation.setPublicClient(Boolean.TRUE); + keycloak.realm("jetty").clients().create(clientRepresentation); + + UserRepresentation user = new UserRepresentation(); + user.setEnabled(true); + user.setFirstName(firstName); + user.setLastName(lastName); + user.setUsername(userName); + user.setEmail(email); + + userId = CreatedResponseUtil.getCreatedId(keycloak.realm("jetty").users().create(user)); + + CredentialRepresentation passwordCred = new CredentialRepresentation(); + passwordCred.setTemporary(false); + passwordCred.setType(CredentialRepresentation.PASSWORD); + passwordCred.setValue(password); + + // Set password credential + keycloak.realm("jetty").users().get(userId).resetPassword(passwordCred); + } + } + + @AfterAll + public static void stopKeycloak() + { + if (KEYCLOAK_CONTAINER.isRunning()) + { + KEYCLOAK_CONTAINER.stop(); + } + } + + public static Stream tests() + { + return Stream.of( + Arguments.of("ee9", "ee9-openid"), + Arguments.of("ee10", "openid") + ); + } + + @ParameterizedTest + @MethodSource("tests") + public void testOpenID(String env, String openIdModule) throws Exception + { + Path jettyBase = newTestJettyBaseDirectory(); + String jettyVersion = System.getProperty("jettyVersion"); + JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .jettyBase(jettyBase) + .build(); + + String[] args1 = { + "--create-startd", + "--approve-all-licenses", + "--add-to-start=http," + toEnvironment("webapp", env) + "," + toEnvironment("deploy", env) + "," + openIdModule + }; + + try (JettyHomeTester.Run run1 = distribution.start(args1)) + { + assertTrue(run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + Path webApp = distribution.resolveArtifact("org.eclipse.jetty." + env + ":jetty-" + env + "-test-openid-webapp:war:" + jettyVersion); + distribution.installWar(webApp, "test"); + String openIdProvider = KEYCLOAK_CONTAINER.getAuthServerUrl() + "/realms/jetty"; + LOGGER.info("openIdProvider: {}", openIdProvider); + + int port = Tester.freePort(); + String[] args2 = { + "jetty.http.port=" + port, + "jetty.ssl.port=" + port, + "jetty.openid.provider=" + openIdProvider, + "jetty.openid.clientId=" + clientId, + "jetty.openid.clientSecret=" + clientSecret, + //"jetty.server.dumpAfterStart=true", + }; + + try (JettyHomeTester.Run run2 = distribution.start(args2)) + { + assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS)); + String uri = "http://localhost:" + port + "/test"; + // Initially not authenticated + startHttpClient(); + ContentResponse contentResponse = client.GET(uri + "/"); + assertThat(contentResponse.getStatus(), is(HttpStatus.OK_200)); + assertThat(contentResponse.getContentAsString(), containsString("not authenticated")); + + // Request to login is success + contentResponse = client.GET(uri + "/login"); + assertThat(contentResponse.getStatus(), is(HttpStatus.OK_200)); + // need to extract form + String html = contentResponse.getContentAsString(); + // need this attribute
- - - 4.0.0 - - org.eclipse.jetty.tests - test-distribution - 12.0.14-SNAPSHOT - - test-ee10-distribution - jar - Tests :: Distribution :: EE10 - - - ${project.groupId}.ee10.distribution - - - - - org.eclipse.jetty - jetty-home - zip - - - * - * - - - - - org.eclipse.jetty - jetty-client - test - - - org.eclipse.jetty - jetty-openid - test - - - org.eclipse.jetty - jetty-slf4j-impl - test - - - org.eclipse.jetty.ee10 - jetty-ee10-servlet - ${project.version} - test - - - org.eclipse.jetty.ee10 - jetty-ee10-test-openid-webapp - ${project.version} - war - test - - - org.eclipse.jetty.tests - jetty-testers - test - - - org.eclipse.jetty.tests - test-distribution-common - test-jar - test - - - - diff --git a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/OpenIdTests.java b/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/OpenIdTests.java deleted file mode 100644 index 0e06385efd3..00000000000 --- a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/OpenIdTests.java +++ /dev/null @@ -1,119 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 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.ee10.tests.distribution; - -import java.nio.file.Path; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jetty.client.ContentResponse; -import org.eclipse.jetty.ee10.tests.distribution.openid.OpenIdProvider; -import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest; -import org.eclipse.jetty.tests.testers.JettyHomeTester; -import org.eclipse.jetty.tests.testers.Tester; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.Isolated; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Isolated -public class OpenIdTests extends AbstractJettyHomeTest -{ - @Test - public void testOpenID() throws Exception - { - Path jettyBase = newTestJettyBaseDirectory(); - String jettyVersion = System.getProperty("jettyVersion"); - JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() - .jettyVersion(jettyVersion) - .jettyBase(jettyBase) - .build(); - - String[] args1 = { - "--create-startd", - "--approve-all-licenses", - "--add-to-start=http,ee10-webapp,ee10-deploy,openid" - }; - - String clientId = "clientId123"; - String clientSecret = "clientSecret456"; - OpenIdProvider openIdProvider = new OpenIdProvider(clientId, clientSecret); - try (JettyHomeTester.Run run1 = distribution.start(args1)) - { - assertTrue(run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); - assertEquals(0, run1.getExitValue()); - - Path webApp = distribution.resolveArtifact("org.eclipse.jetty.ee10:jetty-ee10-test-openid-webapp:war:" + jettyVersion); - distribution.installWar(webApp, "test"); - - int port = Tester.freePort(); - openIdProvider.addRedirectUri("http://localhost:" + port + "/test/j_security_check"); - openIdProvider.start(); - String[] args2 = { - "jetty.http.port=" + port, - "jetty.ssl.port=" + port, - "jetty.openid.provider=" + openIdProvider.getProvider(), - "jetty.openid.clientId=" + clientId, - "jetty.openid.clientSecret=" + clientSecret, - //"jetty.server.dumpAfterStart=true", - }; - - try (JettyHomeTester.Run run2 = distribution.start(args2)) - { - assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS)); - startHttpClient(false); - String uri = "http://localhost:" + port + "/test"; - openIdProvider.setUser(new OpenIdProvider.User("123456789", "Alice")); - - // Initially not authenticated - ContentResponse response = client.GET(uri + "/"); - assertThat(response.getStatus(), is(HttpStatus.OK_200)); - String content = response.getContentAsString(); - assertThat(content, containsString("not authenticated")); - - // Request to login is success - response = client.GET(uri + "/login"); - assertThat(response.getStatus(), is(HttpStatus.OK_200)); - content = response.getContentAsString(); - assertThat(content, containsString("success")); - - // Now authenticated we can get info - response = client.GET(uri + "/"); - assertThat(response.getStatus(), is(HttpStatus.OK_200)); - content = response.getContentAsString(); - assertThat(content, containsString("userId: 123456789")); - assertThat(content, containsString("name: Alice")); - assertThat(content, containsString("email: Alice@example.com")); - - // Request to admin page gives 403 as we do not have admin role - response = client.GET(uri + "/admin"); - assertThat(response.getStatus(), is(HttpStatus.FORBIDDEN_403)); - - // We are no longer authenticated after logging out - response = client.GET(uri + "/logout"); - assertThat(response.getStatus(), is(HttpStatus.OK_200)); - content = response.getContentAsString(); - assertThat(content, containsString("not authenticated")); - } - } - finally - { - openIdProvider.stop(); - } - } -} diff --git a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/openid/JwtEncoder.java b/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/openid/JwtEncoder.java deleted file mode 100644 index 91c5f9709e0..00000000000 --- a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/openid/JwtEncoder.java +++ /dev/null @@ -1,53 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 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.ee10.tests.distribution.openid; - -import java.util.Base64; - -/** - * A basic JWT encoder for testing purposes. - */ -public class JwtEncoder -{ - private static final Base64.Encoder ENCODER = Base64.getUrlEncoder(); - private static final String DEFAULT_HEADER = "{\"INFO\": \"this is not used or checked in our implementation\"}"; - private static final String DEFAULT_SIGNATURE = "we do not validate signature as we use the authorization code flow"; - - public static String encode(String idToken) - { - return stripPadding(ENCODER.encodeToString(DEFAULT_HEADER.getBytes())) + "." + - stripPadding(ENCODER.encodeToString(idToken.getBytes())) + "." + - stripPadding(ENCODER.encodeToString(DEFAULT_SIGNATURE.getBytes())); - } - - private static String stripPadding(String paddedBase64) - { - return paddedBase64.split("=")[0]; - } - - /** - * Create a basic JWT for testing using argument supplied attributes. - */ - public static String createIdToken(String provider, String clientId, String subject, String name, long expiry) - { - return "{" + - "\"iss\": \"" + provider + "\"," + - "\"sub\": \"" + subject + "\"," + - "\"aud\": \"" + clientId + "\"," + - "\"exp\": " + expiry + "," + - "\"name\": \"" + name + "\"," + - "\"email\": \"" + name + "@example.com" + "\"" + - "}"; - } -} diff --git a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/openid/OpenIdProvider.java b/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/openid/OpenIdProvider.java deleted file mode 100644 index f1e2f27324b..00000000000 --- a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/openid/OpenIdProvider.java +++ /dev/null @@ -1,402 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 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.ee10.tests.distribution.openid; - -import java.io.IOException; -import java.io.PrintWriter; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.eclipse.jetty.ee10.servlet.ServletContextHandler; -import org.eclipse.jetty.ee10.servlet.ServletHolder; -import org.eclipse.jetty.security.openid.OpenIdConfiguration; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.util.component.ContainerLifeCycle; -import org.eclipse.jetty.util.statistic.CounterStatistic; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class OpenIdProvider extends ContainerLifeCycle -{ - private static final Logger LOG = LoggerFactory.getLogger(OpenIdProvider.class); - - private static final String CONFIG_PATH = "/.well-known/openid-configuration"; - private static final String AUTH_PATH = "/auth"; - private static final String TOKEN_PATH = "/token"; - private static final String END_SESSION_PATH = "/end_session"; - private final Map issuedAuthCodes = new HashMap<>(); - - protected final String clientId; - protected final String clientSecret; - protected final List redirectUris = new ArrayList<>(); - private final ServerConnector connector; - private final Server server; - private int port = 0; - private String provider; - private User preAuthedUser; - private final CounterStatistic loggedInUsers = new CounterStatistic(); - private long _idTokenDuration = Duration.ofSeconds(10).toMillis(); - - public static void main(String[] args) throws Exception - { - String clientId = "CLIENT_ID123"; - String clientSecret = "PASSWORD123"; - int port = 5771; - String redirectUri = "http://localhost:8080/j_security_check"; - - OpenIdProvider openIdProvider = new OpenIdProvider(clientId, clientSecret); - openIdProvider.addRedirectUri(redirectUri); - openIdProvider.setPort(port); - openIdProvider.start(); - try - { - openIdProvider.join(); - } - finally - { - openIdProvider.stop(); - } - } - - public OpenIdProvider(String clientId, String clientSecret) - { - this.clientId = clientId; - this.clientSecret = clientSecret; - - server = new Server(); - connector = new ServerConnector(server); - server.addConnector(connector); - - ServletContextHandler contextHandler = new ServletContextHandler(); - contextHandler.setContextPath("/"); - contextHandler.addServlet(new ServletHolder(new ConfigServlet()), CONFIG_PATH); - contextHandler.addServlet(new ServletHolder(new AuthEndpoint()), AUTH_PATH); - contextHandler.addServlet(new ServletHolder(new TokenEndpoint()), TOKEN_PATH); - contextHandler.addServlet(new ServletHolder(new EndSessionEndpoint()), END_SESSION_PATH); - server.setHandler(contextHandler); - - addBean(server); - } - - public void setIdTokenDuration(long duration) - { - _idTokenDuration = duration; - } - - public long getIdTokenDuration() - { - return _idTokenDuration; - } - - public void join() throws InterruptedException - { - server.join(); - } - - public OpenIdConfiguration getOpenIdConfiguration() - { - String provider = getProvider(); - String authEndpoint = provider + AUTH_PATH; - String tokenEndpoint = provider + TOKEN_PATH; - return new OpenIdConfiguration(provider, authEndpoint, tokenEndpoint, clientId, clientSecret, null); - } - - public CounterStatistic getLoggedInUsers() - { - return loggedInUsers; - } - - @Override - protected void doStart() throws Exception - { - connector.setPort(port); - super.doStart(); - provider = "http://localhost:" + connector.getLocalPort(); - } - - public void setPort(int port) - { - if (isStarted()) - throw new IllegalStateException(); - this.port = port; - } - - public void setUser(User user) - { - this.preAuthedUser = user; - } - - public String getProvider() - { - if (!isStarted() && port == 0) - throw new IllegalStateException("Port of OpenIdProvider not configured"); - return provider; - } - - public void addRedirectUri(String uri) - { - redirectUris.add(uri); - } - - public class AuthEndpoint extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - if (!clientId.equals(req.getParameter("client_id"))) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid client_id"); - return; - } - - String redirectUri = req.getParameter("redirect_uri"); - if (!redirectUris.contains(redirectUri)) - { - LOG.warn("invalid redirectUri {}", redirectUri); - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); - return; - } - - String scopeString = req.getParameter("scope"); - List scopes = (scopeString == null) ? Collections.emptyList() : Arrays.asList(scopeString.split(" ")); - if (!scopes.contains("openid")) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no openid scope"); - return; - } - - if (!"code".equals(req.getParameter("response_type"))) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "response_type must be code"); - return; - } - - String state = req.getParameter("state"); - if (state == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); - return; - } - - if (preAuthedUser == null) - { - PrintWriter writer = resp.getWriter(); - resp.setContentType("text/html"); - writer.println("

Login to OpenID Connect Provider

"); - writer.println(""); - writer.println(""); - writer.println(""); - writer.println(""); - writer.println(""); - writer.println(""); - } - else - { - redirectUser(resp, preAuthedUser, redirectUri, state); - } - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - String redirectUri = req.getParameter("redirectUri"); - if (!redirectUris.contains(redirectUri)) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); - return; - } - - String state = req.getParameter("state"); - if (state == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); - return; - } - - String username = req.getParameter("username"); - if (username == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no username"); - return; - } - - User user = new User(username); - redirectUser(resp, user, redirectUri, state); - } - - public void redirectUser(HttpServletResponse response, User user, String redirectUri, String state) throws IOException - { - String authCode = UUID.randomUUID().toString().replace("-", ""); - issuedAuthCodes.put(authCode, user); - - try - { - redirectUri += "?code=" + authCode + "&state=" + state; - response.sendRedirect(response.encodeRedirectURL(redirectUri)); - } - catch (Throwable t) - { - issuedAuthCodes.remove(authCode); - throw t; - } - } - } - - private class TokenEndpoint extends HttpServlet - { - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - String code = req.getParameter("code"); - - if (!clientId.equals(req.getParameter("client_id")) || - !clientSecret.equals(req.getParameter("client_secret")) || - !redirectUris.contains(req.getParameter("redirect_uri")) || - !"authorization_code".equals(req.getParameter("grant_type")) || - code == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "bad auth request"); - return; - } - - User user = issuedAuthCodes.remove(code); - if (user == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid auth code"); - return; - } - - String accessToken = "ABCDEFG"; - long accessTokenDuration = Duration.ofMinutes(10).toSeconds(); - String response = "{" + - "\"access_token\": \"" + accessToken + "\"," + - "\"id_token\": \"" + JwtEncoder.encode(user.getIdToken(provider, clientId, _idTokenDuration)) + "\"," + - "\"expires_in\": " + accessTokenDuration + "," + - "\"token_type\": \"Bearer\"" + - "}"; - - loggedInUsers.increment(); - resp.setContentType("text/plain"); - resp.getWriter().print(response); - } - } - - private class EndSessionEndpoint extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - doPost(req, resp); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - String idToken = req.getParameter("id_token_hint"); - if (idToken == null) - { - resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "no id_token_hint"); - return; - } - - String logoutRedirect = req.getParameter("post_logout_redirect_uri"); - if (logoutRedirect == null) - { - resp.setStatus(HttpServletResponse.SC_OK); - resp.getWriter().println("logout success on end_session_endpoint"); - return; - } - - loggedInUsers.decrement(); - resp.setContentType("text/plain"); - resp.sendRedirect(logoutRedirect); - } - } - - private class ConfigServlet extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - String discoveryDocument = "{" + - "\"issuer\": \"" + provider + "\"," + - "\"authorization_endpoint\": \"" + provider + AUTH_PATH + "\"," + - "\"token_endpoint\": \"" + provider + TOKEN_PATH + "\"," + - "\"end_session_endpoint\": \"" + provider + END_SESSION_PATH + "\"," + - "}"; - - resp.getWriter().write(discoveryDocument); - } - } - - public static class User - { - private final String subject; - private final String name; - - public User(String name) - { - this(UUID.nameUUIDFromBytes(name.getBytes()).toString(), name); - } - - public User(String subject, String name) - { - this.subject = subject; - this.name = name; - } - - public String getName() - { - return name; - } - - public String getSubject() - { - return subject; - } - - public String getIdToken(String provider, String clientId, long duration) - { - long expiryTime = Instant.now().plusMillis(duration).getEpochSecond(); - return JwtEncoder.createIdToken(provider, clientId, subject, name, expiryTime); - } - - @Override - public boolean equals(Object obj) - { - if (!(obj instanceof User)) - return false; - return Objects.equals(subject, ((User)obj).subject) && Objects.equals(name, ((User)obj).name); - } - - @Override - public int hashCode() - { - return Objects.hash(subject, name); - } - } -} diff --git a/tests/test-distribution/test-ee9-distribution/pom.xml b/tests/test-distribution/test-ee9-distribution/pom.xml deleted file mode 100644 index c49ace26941..00000000000 --- a/tests/test-distribution/test-ee9-distribution/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - 4.0.0 - - org.eclipse.jetty.tests - test-distribution - 12.0.14-SNAPSHOT - - test-ee9-distribution - jar - Tests :: Distribution :: EE9 - - - ${project.groupId}.ee9.distribution - - - - - org.eclipse.jetty - jetty-home - zip - - - * - * - - - - - org.eclipse.jetty - jetty-client - test - - - org.eclipse.jetty - jetty-slf4j-impl - test - - - org.eclipse.jetty.ee9 - jetty-ee9-openid - ${project.version} - test - - - org.eclipse.jetty.ee9 - jetty-ee9-servlet - ${project.version} - test - - - org.eclipse.jetty.ee9 - jetty-ee9-test-openid-webapp - ${project.version} - war - test - - - org.eclipse.jetty.tests - jetty-testers - test - - - org.eclipse.jetty.tests - test-distribution-common - test-jar - test - - - - diff --git a/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/OpenIdTests.java b/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/OpenIdTests.java deleted file mode 100644 index 78744609f36..00000000000 --- a/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/OpenIdTests.java +++ /dev/null @@ -1,120 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 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.ee9.tests.distribution; - -import java.nio.file.Path; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jetty.client.ContentResponse; -import org.eclipse.jetty.ee9.tests.distribution.openid.OpenIdProvider; -import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest; -import org.eclipse.jetty.tests.testers.JettyHomeTester; -import org.eclipse.jetty.tests.testers.Tester; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.parallel.Isolated; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Isolated -public class OpenIdTests extends AbstractJettyHomeTest -{ - @Test - public void testOpenID() throws Exception - { - Path jettyBase = newTestJettyBaseDirectory(); - String jettyVersion = System.getProperty("jettyVersion"); - JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() - .jettyVersion(jettyVersion) - .jettyBase(jettyBase) - .build(); - - String[] args1 = { - "--create-startd", - "--approve-all-licenses", - "--add-to-start=http,ee9-webapp,ee9-deploy,ee9-openid" - }; - - String clientId = "clientId123"; - String clientSecret = "clientSecret456"; - OpenIdProvider openIdProvider = new OpenIdProvider(clientId, clientSecret); - try (JettyHomeTester.Run run1 = distribution.start(args1)) - { - assertTrue(run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); - assertEquals(0, run1.getExitValue()); - - Path webApp = distribution.resolveArtifact("org.eclipse.jetty.ee9:jetty-ee9-test-openid-webapp:war:" + jettyVersion); - distribution.installWar(webApp, "test"); - - int port = Tester.freePort(); - openIdProvider.addRedirectUri("http://localhost:" + port + "/test/j_security_check"); - openIdProvider.start(); - String[] args2 = { - "jetty.http.port=" + port, - "jetty.ssl.port=" + port, - "jetty.openid.provider=" + openIdProvider.getProvider(), - "jetty.openid.clientId=" + clientId, - "jetty.openid.clientSecret=" + clientSecret, - //"jetty.server.dumpAfterStart=true", - }; - - try (JettyHomeTester.Run run2 = distribution.start(args2)) - { - assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS)); - startHttpClient(false); - String uri = "http://localhost:" + port + "/test"; - openIdProvider.setUser(new OpenIdProvider.User("123456789", "Alice")); - - // Initially not authenticated - ContentResponse response = client.GET(uri + "/"); - assertThat(response.getStatus(), is(HttpStatus.OK_200)); - String content = response.getContentAsString(); - assertThat(content, containsString("not authenticated")); - - // Request to login is success - response = client.GET(uri + "/login"); - assertThat(response.getStatus(), is(HttpStatus.OK_200)); - content = response.getContentAsString(); - assertThat(content, containsString("success")); - - // Now authenticated we can get info - response = client.GET(uri + "/"); - assertThat(response.getStatus(), is(HttpStatus.OK_200)); - content = response.getContentAsString(); - assertThat(content, containsString("userId: 123456789")); - assertThat(content, containsString("name: Alice")); - assertThat(content, containsString("email: Alice@example.com")); - - // Request to admin page gives 403 as we do not have admin role - response = client.GET(uri + "/admin"); - assertThat(response.getStatus(), is(HttpStatus.FORBIDDEN_403)); - - // We are no longer authenticated after logging out - response = client.GET(uri + "/logout"); - assertThat(response.getStatus(), is(HttpStatus.OK_200)); - content = response.getContentAsString(); - assertThat(content, containsString("not authenticated")); - - } - } - finally - { - openIdProvider.stop(); - } - } -} diff --git a/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/openid/JwtEncoder.java b/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/openid/JwtEncoder.java deleted file mode 100644 index 62b6f1dacfc..00000000000 --- a/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/openid/JwtEncoder.java +++ /dev/null @@ -1,53 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 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.ee9.tests.distribution.openid; - -import java.util.Base64; - -/** - * A basic JWT encoder for testing purposes. - */ -public class JwtEncoder -{ - private static final Base64.Encoder ENCODER = Base64.getUrlEncoder(); - private static final String DEFAULT_HEADER = "{\"INFO\": \"this is not used or checked in our implementation\"}"; - private static final String DEFAULT_SIGNATURE = "we do not validate signature as we use the authorization code flow"; - - public static String encode(String idToken) - { - return stripPadding(ENCODER.encodeToString(DEFAULT_HEADER.getBytes())) + "." + - stripPadding(ENCODER.encodeToString(idToken.getBytes())) + "." + - stripPadding(ENCODER.encodeToString(DEFAULT_SIGNATURE.getBytes())); - } - - private static String stripPadding(String paddedBase64) - { - return paddedBase64.split("=")[0]; - } - - /** - * Create a basic JWT for testing using argument supplied attributes. - */ - public static String createIdToken(String provider, String clientId, String subject, String name, long expiry) - { - return "{" + - "\"iss\": \"" + provider + "\"," + - "\"sub\": \"" + subject + "\"," + - "\"aud\": \"" + clientId + "\"," + - "\"exp\": " + expiry + "," + - "\"name\": \"" + name + "\"," + - "\"email\": \"" + name + "@example.com" + "\"" + - "}"; - } -} diff --git a/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/openid/OpenIdProvider.java b/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/openid/OpenIdProvider.java deleted file mode 100644 index 33ee1dc7dc9..00000000000 --- a/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/openid/OpenIdProvider.java +++ /dev/null @@ -1,402 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 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.ee9.tests.distribution.openid; - -import java.io.IOException; -import java.io.PrintWriter; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.eclipse.jetty.ee9.servlet.ServletContextHandler; -import org.eclipse.jetty.ee9.servlet.ServletHolder; -import org.eclipse.jetty.security.openid.OpenIdConfiguration; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.util.component.ContainerLifeCycle; -import org.eclipse.jetty.util.statistic.CounterStatistic; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class OpenIdProvider extends ContainerLifeCycle -{ - private static final Logger LOG = LoggerFactory.getLogger(OpenIdProvider.class); - - private static final String CONFIG_PATH = "/.well-known/openid-configuration"; - private static final String AUTH_PATH = "/auth"; - private static final String TOKEN_PATH = "/token"; - private static final String END_SESSION_PATH = "/end_session"; - private final Map issuedAuthCodes = new HashMap<>(); - - protected final String clientId; - protected final String clientSecret; - protected final List redirectUris = new ArrayList<>(); - private final ServerConnector connector; - private final Server server; - private int port = 0; - private String provider; - private User preAuthedUser; - private final CounterStatistic loggedInUsers = new CounterStatistic(); - private long _idTokenDuration = Duration.ofSeconds(10).toMillis(); - - public static void main(String[] args) throws Exception - { - String clientId = "CLIENT_ID123"; - String clientSecret = "PASSWORD123"; - int port = 5771; - String redirectUri = "http://localhost:8080/j_security_check"; - - OpenIdProvider openIdProvider = new OpenIdProvider(clientId, clientSecret); - openIdProvider.addRedirectUri(redirectUri); - openIdProvider.setPort(port); - openIdProvider.start(); - try - { - openIdProvider.join(); - } - finally - { - openIdProvider.stop(); - } - } - - public OpenIdProvider(String clientId, String clientSecret) - { - this.clientId = clientId; - this.clientSecret = clientSecret; - - server = new Server(); - connector = new ServerConnector(server); - server.addConnector(connector); - - ServletContextHandler contextHandler = new ServletContextHandler(); - contextHandler.setContextPath("/"); - contextHandler.addServlet(new ServletHolder(new ConfigServlet()), CONFIG_PATH); - contextHandler.addServlet(new ServletHolder(new AuthEndpoint()), AUTH_PATH); - contextHandler.addServlet(new ServletHolder(new TokenEndpoint()), TOKEN_PATH); - contextHandler.addServlet(new ServletHolder(new EndSessionEndpoint()), END_SESSION_PATH); - server.setHandler(contextHandler); - - addBean(server); - } - - public void setIdTokenDuration(long duration) - { - _idTokenDuration = duration; - } - - public long getIdTokenDuration() - { - return _idTokenDuration; - } - - public void join() throws InterruptedException - { - server.join(); - } - - public OpenIdConfiguration getOpenIdConfiguration() - { - String provider = getProvider(); - String authEndpoint = provider + AUTH_PATH; - String tokenEndpoint = provider + TOKEN_PATH; - return new OpenIdConfiguration(provider, authEndpoint, tokenEndpoint, clientId, clientSecret, null); - } - - public CounterStatistic getLoggedInUsers() - { - return loggedInUsers; - } - - @Override - protected void doStart() throws Exception - { - connector.setPort(port); - super.doStart(); - provider = "http://localhost:" + connector.getLocalPort(); - } - - public void setPort(int port) - { - if (isStarted()) - throw new IllegalStateException(); - this.port = port; - } - - public void setUser(User user) - { - this.preAuthedUser = user; - } - - public String getProvider() - { - if (!isStarted() && port == 0) - throw new IllegalStateException("Port of OpenIdProvider not configured"); - return provider; - } - - public void addRedirectUri(String uri) - { - redirectUris.add(uri); - } - - public class AuthEndpoint extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - if (!clientId.equals(req.getParameter("client_id"))) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid client_id"); - return; - } - - String redirectUri = req.getParameter("redirect_uri"); - if (!redirectUris.contains(redirectUri)) - { - LOG.warn("invalid redirectUri {}", redirectUri); - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); - return; - } - - String scopeString = req.getParameter("scope"); - List scopes = (scopeString == null) ? Collections.emptyList() : Arrays.asList(scopeString.split(" ")); - if (!scopes.contains("openid")) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no openid scope"); - return; - } - - if (!"code".equals(req.getParameter("response_type"))) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "response_type must be code"); - return; - } - - String state = req.getParameter("state"); - if (state == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); - return; - } - - if (preAuthedUser == null) - { - PrintWriter writer = resp.getWriter(); - resp.setContentType("text/html"); - writer.println("

Login to OpenID Connect Provider

"); - writer.println("
"); - writer.println(""); - writer.println(""); - writer.println(""); - writer.println(""); - writer.println("
"); - } - else - { - redirectUser(resp, preAuthedUser, redirectUri, state); - } - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - String redirectUri = req.getParameter("redirectUri"); - if (!redirectUris.contains(redirectUri)) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); - return; - } - - String state = req.getParameter("state"); - if (state == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); - return; - } - - String username = req.getParameter("username"); - if (username == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no username"); - return; - } - - User user = new User(username); - redirectUser(resp, user, redirectUri, state); - } - - public void redirectUser(HttpServletResponse response, User user, String redirectUri, String state) throws IOException - { - String authCode = UUID.randomUUID().toString().replace("-", ""); - issuedAuthCodes.put(authCode, user); - - try - { - redirectUri += "?code=" + authCode + "&state=" + state; - response.sendRedirect(response.encodeRedirectURL(redirectUri)); - } - catch (Throwable t) - { - issuedAuthCodes.remove(authCode); - throw t; - } - } - } - - private class TokenEndpoint extends HttpServlet - { - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - String code = req.getParameter("code"); - - if (!clientId.equals(req.getParameter("client_id")) || - !clientSecret.equals(req.getParameter("client_secret")) || - !redirectUris.contains(req.getParameter("redirect_uri")) || - !"authorization_code".equals(req.getParameter("grant_type")) || - code == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "bad auth request"); - return; - } - - User user = issuedAuthCodes.remove(code); - if (user == null) - { - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid auth code"); - return; - } - - String accessToken = "ABCDEFG"; - long accessTokenDuration = Duration.ofMinutes(10).toSeconds(); - String response = "{" + - "\"access_token\": \"" + accessToken + "\"," + - "\"id_token\": \"" + JwtEncoder.encode(user.getIdToken(provider, clientId, _idTokenDuration)) + "\"," + - "\"expires_in\": " + accessTokenDuration + "," + - "\"token_type\": \"Bearer\"" + - "}"; - - loggedInUsers.increment(); - resp.setContentType("text/plain"); - resp.getWriter().print(response); - } - } - - private class EndSessionEndpoint extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - doPost(req, resp); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - String idToken = req.getParameter("id_token_hint"); - if (idToken == null) - { - resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "no id_token_hint"); - return; - } - - String logoutRedirect = req.getParameter("post_logout_redirect_uri"); - if (logoutRedirect == null) - { - resp.setStatus(HttpServletResponse.SC_OK); - resp.getWriter().println("logout success on end_session_endpoint"); - return; - } - - loggedInUsers.decrement(); - resp.setContentType("text/plain"); - resp.sendRedirect(logoutRedirect); - } - } - - private class ConfigServlet extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - String discoveryDocument = "{" + - "\"issuer\": \"" + provider + "\"," + - "\"authorization_endpoint\": \"" + provider + AUTH_PATH + "\"," + - "\"token_endpoint\": \"" + provider + TOKEN_PATH + "\"," + - "\"end_session_endpoint\": \"" + provider + END_SESSION_PATH + "\"," + - "}"; - - resp.getWriter().write(discoveryDocument); - } - } - - public static class User - { - private final String subject; - private final String name; - - public User(String name) - { - this(UUID.nameUUIDFromBytes(name.getBytes()).toString(), name); - } - - public User(String subject, String name) - { - this.subject = subject; - this.name = name; - } - - public String getName() - { - return name; - } - - public String getSubject() - { - return subject; - } - - public String getIdToken(String provider, String clientId, long duration) - { - long expiryTime = Instant.now().plusMillis(duration).getEpochSecond(); - return JwtEncoder.createIdToken(provider, clientId, subject, name, expiryTime); - } - - @Override - public boolean equals(Object obj) - { - if (!(obj instanceof User)) - return false; - return Objects.equals(subject, ((User)obj).subject) && Objects.equals(name, ((User)obj).name); - } - - @Override - public int hashCode() - { - return Objects.hash(subject, name); - } - } -} From 6f693831885e6c341937af0cd1741c8a7c0a6807 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 16:20:31 +1000 Subject: [PATCH 02/10] fix merge Signed-off-by: Olivier Lamy --- .../test-distribution-common/pom.xml | 14 --------- .../distribution/DisableUrlCacheTest.java | 31 ++++++++++--------- .../jetty/tests/distribution/OpenIdTests.java | 3 +- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/tests/test-distribution/test-distribution-common/pom.xml b/tests/test-distribution/test-distribution-common/pom.xml index 535c035944f..65c35e81de5 100644 --- a/tests/test-distribution/test-distribution-common/pom.xml +++ b/tests/test-distribution/test-distribution-common/pom.xml @@ -278,20 +278,6 @@ org.apache.maven.plugins -<<<<<<< HEAD - maven-jar-plugin - - - - test-jar - - - - - - org.apache.maven.plugins -======= ->>>>>>> jetty-12.0.x maven-surefire-plugin diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java index 9b8a2919088..4ca94c4d05b 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java @@ -19,6 +19,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.ContentResponse; @@ -28,6 +29,8 @@ import org.eclipse.jetty.tests.testers.Tester; import org.eclipse.jetty.toolchain.test.FS; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Isolated; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,8 +45,9 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest { private static final Logger LOG = LoggerFactory.getLogger(DisableUrlCacheTest.class); - @Test - public void testReloadWebAppWithLog4j2() throws Exception + @ParameterizedTest + @ValueSource(strings = {"ee10", "ee11"}) + public void testReloadWebAppWithLog4j2(String env) throws Exception { Path jettyBase = newTestJettyBaseDirectory(); String jettyVersion = System.getProperty("jettyVersion"); @@ -54,7 +58,7 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest .build(); String[] setupArgs = { - "--add-modules=http,ee10-webapp,ee10-deploy,disable-urlcache" + "--add-modules=http,"+toEnvironment("webapp", env)+","+toEnvironment("deploy", env)+",disable-urlcache" }; try (JettyHomeTester.Run setupRun = distribution.start(setupArgs)) @@ -62,7 +66,7 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest assertTrue(setupRun.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); assertEquals(0, setupRun.getExitValue()); - Path webApp = distribution.resolveArtifact("org.eclipse.jetty.ee10:jetty-ee10-test-log4j2-webapp:war:" + jettyVersion); + Path webApp = distribution.resolveArtifact("org.eclipse.jetty."+env+":jetty-"+env+"-test-log4j2-webapp:war:" + jettyVersion); Path testWebApp = distribution.getJettyBase().resolve("webapps/test.war"); Files.copy(webApp, testWebApp); @@ -74,16 +78,15 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest FS.ensureEmpty(resourcesDir); Path webappsDir = distribution.getJettyBase().resolve("webapps"); - String warXml = """ - - - - /test - /test.war - /work/test - false - - """; + String warXml = + "" + + "" + + "" + + " /test" + + " /test.war" + + " /work/test" + + " false" + + ""; Path warXmlPath = webappsDir.resolve("test.xml"); Files.writeString(warXmlPath, warXml, StandardCharsets.UTF_8); diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/OpenIdTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/OpenIdTests.java index c96fffe5cff..113500ff6ee 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/OpenIdTests.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/OpenIdTests.java @@ -116,7 +116,8 @@ public class OpenIdTests extends AbstractJettyHomeTest { return Stream.of( Arguments.of("ee9", "ee9-openid"), - Arguments.of("ee10", "openid") + Arguments.of("ee10", "openid"), + Arguments.of("ee11", "openid") ); } From ac18df08afb623ffd97557275394f10aa9b7db7c Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 16:55:16 +1000 Subject: [PATCH 03/10] restore jetty-test-common as it is used in multiple places Signed-off-by: Olivier Lamy --- tests/jetty-test-common/pom.xml | 29 ++ .../org/eclipse/jetty/tests/JwtEncoder.java | 53 +++ .../eclipse/jetty/tests/OpenIdProvider.java | 421 ++++++++++++++++++ tests/pom.xml | 1 + .../test-distribution-common/pom.xml | 14 + 5 files changed, 518 insertions(+) create mode 100644 tests/jetty-test-common/pom.xml create mode 100644 tests/jetty-test-common/src/main/java/org/eclipse/jetty/tests/JwtEncoder.java create mode 100644 tests/jetty-test-common/src/main/java/org/eclipse/jetty/tests/OpenIdProvider.java diff --git a/tests/jetty-test-common/pom.xml b/tests/jetty-test-common/pom.xml new file mode 100644 index 00000000000..d820ada4c30 --- /dev/null +++ b/tests/jetty-test-common/pom.xml @@ -0,0 +1,29 @@ + + + + 4.0.0 + + org.eclipse.jetty.tests + tests + 12.1.0-SNAPSHOT + + jetty-test-common + jar + Tests :: Test Utilities + + + ${project.groupId}.testers + + + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-util + + + + diff --git a/tests/jetty-test-common/src/main/java/org/eclipse/jetty/tests/JwtEncoder.java b/tests/jetty-test-common/src/main/java/org/eclipse/jetty/tests/JwtEncoder.java new file mode 100644 index 00000000000..d4611f07a77 --- /dev/null +++ b/tests/jetty-test-common/src/main/java/org/eclipse/jetty/tests/JwtEncoder.java @@ -0,0 +1,53 @@ +// +// ======================================================================== +// Copyright (c) 1995 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.tests; + +import java.util.Base64; + +/** + * A basic JWT encoder for testing purposes. + */ +public class JwtEncoder +{ + private static final Base64.Encoder ENCODER = Base64.getUrlEncoder(); + private static final String DEFAULT_HEADER = "{\"INFO\": \"this is not used or checked in our implementation\"}"; + private static final String DEFAULT_SIGNATURE = "we do not validate signature as we use the authorization code flow"; + + public static String encode(String idToken) + { + return stripPadding(ENCODER.encodeToString(DEFAULT_HEADER.getBytes())) + "." + + stripPadding(ENCODER.encodeToString(idToken.getBytes())) + "." + + stripPadding(ENCODER.encodeToString(DEFAULT_SIGNATURE.getBytes())); + } + + private static String stripPadding(String paddedBase64) + { + return paddedBase64.split("=")[0]; + } + + /** + * Create a basic JWT for testing using argument supplied attributes. + */ + public static String createIdToken(String provider, String clientId, String subject, String name, long expiry) + { + return "{" + + "\"iss\": \"" + provider + "\"," + + "\"sub\": \"" + subject + "\"," + + "\"aud\": \"" + clientId + "\"," + + "\"exp\": " + expiry + "," + + "\"name\": \"" + name + "\"," + + "\"email\": \"" + name + "@example.com" + "\"" + + "}"; + } +} diff --git a/tests/jetty-test-common/src/main/java/org/eclipse/jetty/tests/OpenIdProvider.java b/tests/jetty-test-common/src/main/java/org/eclipse/jetty/tests/OpenIdProvider.java new file mode 100644 index 00000000000..aba79c2a0c3 --- /dev/null +++ b/tests/jetty-test-common/src/main/java/org/eclipse/jetty/tests/OpenIdProvider.java @@ -0,0 +1,421 @@ +// +// ======================================================================== +// Copyright (c) 1995 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.tests; + +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.jetty.http.BadMessageException; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.Fields; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.statistic.CounterStatistic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OpenIdProvider extends ContainerLifeCycle +{ + private static final Logger LOG = LoggerFactory.getLogger(OpenIdProvider.class); + + private static final String CONFIG_PATH = "/.well-known/openid-configuration"; + private static final String AUTH_PATH = "/auth"; + private static final String TOKEN_PATH = "/token"; + private static final String END_SESSION_PATH = "/end_session"; + private final Map issuedAuthCodes = new HashMap<>(); + + protected final String clientId; + protected final String clientSecret; + protected final List redirectUris = new ArrayList<>(); + private final ServerConnector connector; + private final Server server; + private int port = 0; + private String provider; + private User preAuthedUser; + private final CounterStatistic loggedInUsers = new CounterStatistic(); + private long _idTokenDuration = Duration.ofSeconds(10).toMillis(); + + public static void main(String[] args) throws Exception + { + String clientId = "CLIENT_ID123"; + String clientSecret = "PASSWORD123"; + int port = 5771; + String redirectUri = "http://localhost:8080/j_security_check"; + + OpenIdProvider openIdProvider = new OpenIdProvider(clientId, clientSecret); + openIdProvider.addRedirectUri(redirectUri); + openIdProvider.setPort(port); + openIdProvider.start(); + try + { + openIdProvider.join(); + } + finally + { + openIdProvider.stop(); + } + } + + public OpenIdProvider() + { + this("clientId" + StringUtil.randomAlphaNumeric(4), StringUtil.randomAlphaNumeric(10)); + } + + public OpenIdProvider(String clientId, String clientSecret) + { + this.clientId = clientId; + this.clientSecret = clientSecret; + + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + + server.setHandler(new OpenIdProviderHandler()); + addBean(server); + } + + public String getClientId() + { + return clientId; + } + + public String getClientSecret() + { + return clientSecret; + } + + public void setIdTokenDuration(long duration) + { + _idTokenDuration = duration; + } + + public long getIdTokenDuration() + { + return _idTokenDuration; + } + + public void join() throws InterruptedException + { + server.join(); + } + + public CounterStatistic getLoggedInUsers() + { + return loggedInUsers; + } + + @Override + protected void doStart() throws Exception + { + connector.setPort(port); + super.doStart(); + provider = "http://localhost:" + connector.getLocalPort(); + } + + public void setPort(int port) + { + if (isStarted()) + throw new IllegalStateException(); + this.port = port; + } + + public void setUser(User user) + { + this.preAuthedUser = user; + } + + public String getProvider() + { + if (!isStarted() && port == 0) + throw new IllegalStateException("Port of OpenIdProvider not configured"); + return provider; + } + + public void addRedirectUri(String uri) + { + redirectUris.add(uri); + } + + public class OpenIdProviderHandler extends Handler.Abstract + { + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception + { + String pathInContext = Request.getPathInContext(request); + switch (pathInContext) + { + case CONFIG_PATH -> doGetConfigServlet(request, response, callback); + case AUTH_PATH -> doAuthEndpoint(request, response, callback); + case TOKEN_PATH -> doTokenEndpoint(request, response, callback); + case END_SESSION_PATH -> doEndSessionEndpoint(request, response, callback); + default -> Response.writeError(request, response, callback, HttpStatus.NOT_FOUND_404); + } + + return true; + } + } + + protected void doAuthEndpoint(Request request, Response response, Callback callback) throws Exception + { + String method = request.getMethod(); + switch (method) + { + case "GET" -> doGetAuthEndpoint(request, response, callback); + case "POST" -> doPostAuthEndpoint(request, response, callback); + default -> throw new BadMessageException("Unsupported HTTP method: " + method); + } + } + + protected void doGetAuthEndpoint(Request request, Response response, Callback callback) throws Exception + { + Fields parameters = Request.getParameters(request); + + if (!clientId.equals(parameters.getValue("client_id"))) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "invalid client_id"); + return; + } + + String redirectUri = parameters.getValue("redirect_uri"); + if (!redirectUris.contains(redirectUri)) + { + LOG.warn("invalid redirectUri {}", redirectUri); + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "invalid redirect_uri"); + return; + } + + String scopeString = parameters.getValue("scope"); + List scopes = (scopeString == null) ? Collections.emptyList() : Arrays.asList(scopeString.split(" ")); + if (!scopes.contains("openid")) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "no openid scope"); + return; + } + + if (!"code".equals(parameters.getValue("response_type"))) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "response_type must be code"); + return; + } + + String state = parameters.getValue("state"); + if (state == null) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "no state param"); + return; + } + + if (preAuthedUser == null) + { + String responseContent = String.format(""" +

Login to OpenID Connect Provider

+
+ + + + +
+ """, AUTH_PATH, redirectUri, state); + response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/html"); + response.write(true, BufferUtil.toBuffer(responseContent), callback); + } + else + { + redirectUser(request, response, callback, preAuthedUser, redirectUri, state); + } + } + + protected void doPostAuthEndpoint(Request request, Response response, Callback callback) throws Exception + { + Fields parameters = Request.getParameters(request); + String redirectUri = parameters.getValue("redirectUri"); + if (!redirectUris.contains(redirectUri)) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "invalid redirect_uri"); + return; + } + + String state = parameters.getValue("state"); + if (state == null) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "no state param"); + return; + } + + String username = parameters.getValue("username"); + if (username == null) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "no username"); + return; + } + + User user = new User(username); + redirectUser(request, response, callback, user, redirectUri, state); + } + + public void redirectUser(Request request, Response response, Callback callback, User user, String redirectUri, String state) throws IOException + { + String authCode = UUID.randomUUID().toString().replace("-", ""); + issuedAuthCodes.put(authCode, user); + + try + { + redirectUri += "?code=" + authCode + "&state=" + state; + Response.sendRedirect(request, response, callback, redirectUri); + } + catch (Throwable t) + { + issuedAuthCodes.remove(authCode); + throw t; + } + } + + protected void doTokenEndpoint(Request request, Response response, Callback callback) throws Exception + { + if (!HttpMethod.POST.is(request.getMethod())) + throw new BadMessageException("Unsupported HTTP method for token Endpoint: " + request.getMethod()); + + Fields parameters = Request.getParameters(request); + String code = parameters.getValue("code"); + + if (!clientId.equals(parameters.getValue("client_id")) || + !clientSecret.equals(parameters.getValue("client_secret")) || + !redirectUris.contains(parameters.getValue("redirect_uri")) || + !"authorization_code".equals(parameters.getValue("grant_type")) || + code == null) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "bad auth request"); + return; + } + + User user = issuedAuthCodes.remove(code); + if (user == null) + { + Response.writeError(request, response, callback, HttpStatus.FORBIDDEN_403, "invalid auth code"); + return; + } + + String accessToken = "ABCDEFG"; + long accessTokenDuration = Duration.ofMinutes(10).toSeconds(); + String responseContent = "{" + + "\"access_token\": \"" + accessToken + "\"," + + "\"id_token\": \"" + JwtEncoder.encode(user.getIdToken(provider, clientId, _idTokenDuration)) + "\"," + + "\"expires_in\": " + accessTokenDuration + "," + + "\"token_type\": \"Bearer\"" + + "}"; + + loggedInUsers.increment(); + response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain"); + response.write(true, BufferUtil.toBuffer(responseContent), callback); + } + + protected void doEndSessionEndpoint(Request request, Response response, Callback callback) throws Exception + { + Fields parameters = Request.getParameters(request); + String idToken = parameters.getValue("id_token_hint"); + if (idToken == null) + { + Response.writeError(request, response, callback, HttpStatus.BAD_REQUEST_400, "no id_token_hint"); + return; + } + + String logoutRedirect = parameters.getValue("post_logout_redirect_uri"); + if (logoutRedirect == null) + { + response.setStatus(HttpStatus.OK_200); + response.write(true, BufferUtil.toBuffer("logout success on end_session_endpoint"), callback); + return; + } + + loggedInUsers.decrement(); + Response.sendRedirect(request, response, callback, logoutRedirect); + } + + protected void doGetConfigServlet(Request request, Response response, Callback callback) throws IOException + { + String discoveryDocument = "{" + + "\"issuer\": \"" + provider + "\"," + + "\"authorization_endpoint\": \"" + provider + AUTH_PATH + "\"," + + "\"token_endpoint\": \"" + provider + TOKEN_PATH + "\"," + + "\"end_session_endpoint\": \"" + provider + END_SESSION_PATH + "\"," + + "}"; + + response.write(true, BufferUtil.toBuffer(discoveryDocument), callback); + } + + public static class User + { + private final String subject; + private final String name; + + public User(String name) + { + this(UUID.nameUUIDFromBytes(name.getBytes()).toString(), name); + } + + public User(String subject, String name) + { + this.subject = subject; + this.name = name; + } + + public String getName() + { + return name; + } + + public String getSubject() + { + return subject; + } + + public String getIdToken(String provider, String clientId, long duration) + { + long expiryTime = Instant.now().plusMillis(duration).getEpochSecond(); + return JwtEncoder.createIdToken(provider, clientId, subject, name, expiryTime); + } + + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof User)) + return false; + return Objects.equals(subject, ((User)obj).subject) && Objects.equals(name, ((User)obj).name); + } + + @Override + public int hashCode() + { + return Objects.hash(subject, name); + } + } +} diff --git a/tests/pom.xml b/tests/pom.xml index 0ec5773e2db..73a94d115ce 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -13,6 +13,7 @@ jetty-testers jetty-jmh + jetty-test-common jetty-test-multipart jetty-test-session-common test-cross-context-dispatch diff --git a/tests/test-distribution/test-distribution-common/pom.xml b/tests/test-distribution/test-distribution-common/pom.xml index 65c35e81de5..4fc3b4b3450 100644 --- a/tests/test-distribution/test-distribution-common/pom.xml +++ b/tests/test-distribution/test-distribution-common/pom.xml @@ -187,6 +187,20 @@ war test
+ + org.eclipse.jetty.ee10 + jetty-ee10-test-openid-webapp + ${project.version} + war + test + + + org.eclipse.jetty.ee11 + jetty-ee11-test-openid-webapp + ${project.version} + war + test + org.eclipse.jetty.http2 jetty-http2-client From b152630eb607808940d6c3e2d0c7921dcfadde68 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 17:50:46 +1000 Subject: [PATCH 04/10] spotless Signed-off-by: Olivier Lamy --- .../test-distribution-common/pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test-distribution/test-distribution-common/pom.xml b/tests/test-distribution/test-distribution-common/pom.xml index 4fc3b4b3450..1e463ce5656 100644 --- a/tests/test-distribution/test-distribution-common/pom.xml +++ b/tests/test-distribution/test-distribution-common/pom.xml @@ -180,13 +180,6 @@ war test - - org.eclipse.jetty.ee9 - jetty-ee9-test-openid-webapp - ${project.version} - war - test - org.eclipse.jetty.ee10 jetty-ee10-test-openid-webapp @@ -201,6 +194,13 @@ war test + + org.eclipse.jetty.ee9 + jetty-ee9-test-openid-webapp + ${project.version} + war + test + org.eclipse.jetty.http2 jetty-http2-client From efcddd330a2fbfda4d043e152ecfaac2ac56619d Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 18:22:25 +1000 Subject: [PATCH 05/10] checkstyle Signed-off-by: Olivier Lamy --- .../eclipse/jetty/tests/distribution/DisableUrlCacheTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java index 4ca94c4d05b..186ba543924 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java @@ -58,7 +58,7 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest .build(); String[] setupArgs = { - "--add-modules=http,"+toEnvironment("webapp", env)+","+toEnvironment("deploy", env)+",disable-urlcache" + "--add-modules=http," + toEnvironment("webapp", env) + "," + toEnvironment("deploy", env) + ",disable-urlcache" }; try (JettyHomeTester.Run setupRun = distribution.start(setupArgs)) @@ -81,7 +81,7 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest String warXml = "" + "" + - "" + + "" + " /test" + " /test.war" + " /work/test" + From 0c31ab7ca6251958761fa1d41979a052117d1234 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 18:24:53 +1000 Subject: [PATCH 06/10] checkstyle Signed-off-by: Olivier Lamy --- .../eclipse/jetty/tests/distribution/DisableUrlCacheTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java index 186ba543924..c36c302255f 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java @@ -66,7 +66,7 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest assertTrue(setupRun.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); assertEquals(0, setupRun.getExitValue()); - Path webApp = distribution.resolveArtifact("org.eclipse.jetty."+env+":jetty-"+env+"-test-log4j2-webapp:war:" + jettyVersion); + Path webApp = distribution.resolveArtifact("org.eclipse.jetty." + env + ":jetty-" + env + "-test-log4j2-webapp:war:" + jettyVersion); Path testWebApp = distribution.getJettyBase().resolve("webapps/test.war"); Files.copy(webApp, testWebApp); From b89f767be60d3e04faad36146327207f971593e0 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 18:38:48 +1000 Subject: [PATCH 07/10] needed dependencies Signed-off-by: Olivier Lamy --- tests/test-distribution/test-distribution-common/pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test-distribution/test-distribution-common/pom.xml b/tests/test-distribution/test-distribution-common/pom.xml index 1e463ce5656..ab98e612ad8 100644 --- a/tests/test-distribution/test-distribution-common/pom.xml +++ b/tests/test-distribution/test-distribution-common/pom.xml @@ -66,6 +66,10 @@ org.apache.maven.resolver maven-resolver-util + + org.bouncycastle + bcprov-jdk15to18 + org.codehaus.plexus plexus-xml @@ -76,6 +80,10 @@ + + org.eclipse.jetty + jetty-ethereum + org.eclipse.jetty jetty-home From fb8e237dcaa26ec598f0dd94915146f899e65f62 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 19:08:41 +1000 Subject: [PATCH 08/10] need this dependency Signed-off-by: Olivier Lamy --- tests/test-distribution/test-distribution-common/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test-distribution/test-distribution-common/pom.xml b/tests/test-distribution/test-distribution-common/pom.xml index ab98e612ad8..1899042a9d0 100644 --- a/tests/test-distribution/test-distribution-common/pom.xml +++ b/tests/test-distribution/test-distribution-common/pom.xml @@ -195,6 +195,13 @@ war test + + org.eclipse.jetty.ee11 + jetty-ee11-test-log4j2-webapp + ${project.version} + war + test + org.eclipse.jetty.ee11 jetty-ee11-test-openid-webapp From 4a0ee278c1679f516c04997f557402b86159f184 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 19:54:56 +1000 Subject: [PATCH 09/10] fix logging Signed-off-by: Olivier Lamy --- .../jetty/tests/distribution/DisableUrlCacheTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java index c36c302255f..ef42a1ff81b 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java @@ -94,10 +94,11 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest String loggingConfig = """ org.eclipse.jetty.LEVEL=INFO org.eclipse.jetty.deploy.LEVEL=DEBUG - org.eclipse.jetty.ee10.webapp.LEVEL=DEBUG - org.eclipse.jetty.ee10.webapp.WebAppClassLoader.LEVEL=INFO - org.eclipse.jetty.ee10.servlet.LEVEL=DEBUG + org.eclipse.jetty.eexx.webapp.LEVEL=DEBUG + org.eclipse.jetty.eexx.webapp.WebAppClassLoader.LEVEL=INFO + org.eclipse.jetty.exx.servlet.LEVEL=DEBUG """; + loggingConfig = loggingConfig.replace("eexx", env); Files.writeString(loggingFile, loggingConfig, StandardCharsets.UTF_8); From a9513c0ae85579d80f4227b4254523856206577a Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Tue, 17 Sep 2024 20:12:43 +1000 Subject: [PATCH 10/10] log to search for restart can be different depending on eexx Signed-off-by: Olivier Lamy --- .../tests/distribution/DisableUrlCacheTest.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java index ef42a1ff81b..02d13fcdd45 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DisableUrlCacheTest.java @@ -21,6 +21,7 @@ import java.nio.file.attribute.FileTime; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.http.HttpStatus; @@ -30,6 +31,8 @@ import org.eclipse.jetty.toolchain.test.FS; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Isolated; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,9 +48,17 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest { private static final Logger LOG = LoggerFactory.getLogger(DisableUrlCacheTest.class); + public static Stream tests() + { + return Stream.of( + Arguments.of("ee10", "Started oeje10w.WebAppContext@"), + Arguments.of("ee11", "Started oeje11w.WebAppContext@") + ); + } + @ParameterizedTest - @ValueSource(strings = {"ee10", "ee11"}) - public void testReloadWebAppWithLog4j2(String env) throws Exception + @MethodSource("tests") + public void testReloadWebAppWithLog4j2(String env, String logToSearch) throws Exception { Path jettyBase = newTestJettyBaseDirectory(); String jettyVersion = System.getProperty("jettyVersion"); @@ -124,7 +135,7 @@ public class DisableUrlCacheTest extends AbstractJettyHomeTest touch(warXmlPath); // Wait for reload to start context - assertTrue(run2.awaitConsoleLogsFor("Started oeje10w.WebAppContext@", START_TIMEOUT, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor(logToSearch, START_TIMEOUT, TimeUnit.SECONDS)); // wait for deployer node to complete so context is Started not Starting assertTrue(run2.awaitConsoleLogsFor("Executing Node Node[started]", START_TIMEOUT, TimeUnit.SECONDS));