This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch tomee-10.x in repository https://gitbox.apache.org/repos/asf/tomee.git
commit 2018a8222742d1805d689278fff21a2e29e753ce Author: Markus Jung <[email protected]> AuthorDate: Sat Aug 29 09:30:51 2026 +0200 make anonymous GET handling on basic auth http listeners configurable (cherry picked from commit 76da08aa4676310b7dc1b2dcf551b08a8fc0d5f4) --- .../server/httpd/BasicAuthHttpListenerWrapper.java | 19 +++++- .../httpd/BasicAuthHttpListenerWrapperTest.java | 71 ++++++++++++++++++++++ .../server/webservices/OpenEJBHttpWsRegistry.java | 2 +- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/BasicAuthHttpListenerWrapper.java b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/BasicAuthHttpListenerWrapper.java index f9c32d5762..673e00e79a 100644 --- a/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/BasicAuthHttpListenerWrapper.java +++ b/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/BasicAuthHttpListenerWrapper.java @@ -21,6 +21,8 @@ import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.spi.SecurityService; import org.apache.openejb.util.Base64; +import jakarta.servlet.http.HttpServletResponse; + import javax.security.auth.login.LoginException; import java.util.Locale; @@ -28,10 +30,20 @@ public class BasicAuthHttpListenerWrapper implements HttpListener { private final HttpListener httpListener; private final String realmName; + private final boolean anonymousGet; public BasicAuthHttpListenerWrapper(final HttpListener httpListener, final String realmName) { + this(httpListener, realmName, false); + } + + /** + * @param anonymousGet when true, GET requests are dispatched without credentials + * (used for wsdl/xsd retrieval on webservice endpoints) + */ + public BasicAuthHttpListenerWrapper(final HttpListener httpListener, final String realmName, final boolean anonymousGet) { this.httpListener = httpListener; this.realmName = realmName; + this.anonymousGet = anonymousGet; } @Override @@ -44,7 +56,7 @@ public class BasicAuthHttpListenerWrapper implements HttpListener { if (auth.toUpperCase(Locale.ENGLISH).startsWith("BASIC ")) { auth = auth.substring(6); final String decoded = new String(Base64.decodeBase64(auth.getBytes())); - final String[] parts = decoded.split(":"); + final String[] parts = decoded.split(":", 2); if (parts.length == 2) { final String username = parts[0]; final String password = parts[1]; @@ -63,10 +75,11 @@ public class BasicAuthHttpListenerWrapper implements HttpListener { } try { - if (token != null || HttpRequest.Method.GET.name().equals(request.getMethod())) { + if (token != null || (anonymousGet && HttpRequest.Method.GET.name().equals(request.getMethod()))) { httpListener.onMessage(request, response); } else { - // login failed, return 401 + response.setHeader("WWW-Authenticate", "Basic realm=\"" + (realmName == null ? "" : realmName) + "\""); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } finally { if (token != null) { diff --git a/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/BasicAuthHttpListenerWrapperTest.java b/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/BasicAuthHttpListenerWrapperTest.java new file mode 100644 index 0000000000..47ccdfb469 --- /dev/null +++ b/server/openejb-http/src/test/java/org/apache/openejb/server/httpd/BasicAuthHttpListenerWrapperTest.java @@ -0,0 +1,71 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.server.httpd; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class BasicAuthHttpListenerWrapperTest { + @Test + public void getWithoutCredentialsIsChallenged() throws Exception { + final AtomicBoolean dispatched = new AtomicBoolean(false); + final BasicAuthHttpListenerWrapper wrapper = new BasicAuthHttpListenerWrapper(listener(dispatched), "TestRealm"); + final HttpResponseImpl response = new HttpResponseImpl(); + + wrapper.onMessage(get(), response); + + assertFalse(dispatched.get()); + assertEquals(401, response.getStatus()); + assertEquals("Basic realm=\"TestRealm\"", response.getHeader("WWW-Authenticate")); + } + + @Test + public void anonymousGetIsDispatchedWhenEnabled() throws Exception { + final AtomicBoolean dispatched = new AtomicBoolean(false); + final BasicAuthHttpListenerWrapper wrapper = new BasicAuthHttpListenerWrapper(listener(dispatched), "TestRealm", true); + final HttpResponseImpl response = new HttpResponseImpl(); + + wrapper.onMessage(get(), response); + + assertTrue(dispatched.get()); + assertEquals(200, response.getStatus()); + } + + private static HttpListener listener(final AtomicBoolean dispatched) { + return new HttpListener() { + @Override + public void onMessage(final HttpRequest request, final HttpResponse response) { + dispatched.set(true); + } + }; + } + + private static HttpRequestImpl get() throws Exception { + final HttpRequestImpl request = new HttpRequestImpl(new URI("http://localhost:4204")); + assertTrue(request.readMessage(new ByteArrayInputStream( + "GET /app/api/customers HTTP/1.1\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1)))); + return request; + } +} diff --git a/server/openejb-webservices/src/main/java/org/apache/openejb/server/webservices/OpenEJBHttpWsRegistry.java b/server/openejb-webservices/src/main/java/org/apache/openejb/server/webservices/OpenEJBHttpWsRegistry.java index bfd0abdfa7..39e2bf54d3 100644 --- a/server/openejb-webservices/src/main/java/org/apache/openejb/server/webservices/OpenEJBHttpWsRegistry.java +++ b/server/openejb-webservices/src/main/java/org/apache/openejb/server/webservices/OpenEJBHttpWsRegistry.java @@ -59,7 +59,7 @@ public class OpenEJBHttpWsRegistry extends OpenEJBHttpRegistry implements WsRegi if (httpListener == null) throw new NullPointerException("httpListener is null"); if ("BASIC".equals(authMethod)) { - httpListener = new BasicAuthHttpListenerWrapper(httpListener, realmName); + httpListener = new BasicAuthHttpListenerWrapper(httpListener, realmName, true); } final StringBuilder deployedPath = new StringBuilder("");
