This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit e747e04eb1143c93fac09fa9f3e3c69970d11c44 Author: opencode <[email protected]> AuthorDate: Fri Nov 6 16:34:23 2026 +0100 Add extended OWB integration and unit test coverage Add unit tests for TomcatPlugin and OpenWebBeansInstanceManager, verify negative beans.xml behavior, context-level security valve settings, dynamic deployment, filter/listener lifecycle callbacks and security principal proxy modes. --- modules/owb/README.md | 16 ++- .../tomcat/OpenWebBeansInstanceManagerTest.java | 151 +++++++++++++++++++++ .../web/tomcat/OpenWebBeansSecurityIT.java | 73 ++++++++-- .../tomcat/OpenWebBeansTomcatIntegrationIT.java | 35 +++++ .../apache/webbeans/web/tomcat/OwbTestServer.java | 54 ++++++-- .../webbeans/web/tomcat/TomcatPluginTest.java | 150 ++++++++++++++++++++ .../apache/webbeans/web/tomcat/WebAppBuilder.java | 18 +++ .../web/tomcat/testapp/CustomPrincipal.java} | 19 +-- .../webbeans/web/tomcat/testapp/Cdi41Servlet.java | 5 +- .../tomcat/testapp/LifecycleContextListener.java | 17 +++ .../web/tomcat/testapp/LifecycleFilter.java | 19 +++ .../owb/src/test/webapp/security/WEB-INF/web.xml | 2 + .../webbeans/web/tomcat/testapp/WhoAmIServlet.java | 26 +++- 13 files changed, 537 insertions(+), 48 deletions(-) diff --git a/modules/owb/README.md b/modules/owb/README.md index 8c69eda852..94a2455b5d 100644 --- a/modules/owb/README.md +++ b/modules/owb/README.md @@ -53,11 +53,19 @@ target/tomcat-owb-<owb-version>.jar Run the full integration test suite: +Run the unit tests: + +```bash +mvn test +``` + +Run the unit and integration tests: + ```bash mvn clean verify ``` -Run a single test class: +Run a single integration test class: ```bash mvn -Dit.test=OpenWebBeansSecurityIT verify @@ -129,9 +137,11 @@ provided through the test/common classpath, matching the production | Test class | Coverage | |---|---| +| `TomcatPluginTest` | Servlet spec class handling, managed-bean rejection, injection support and security service registration | +| `OpenWebBeansInstanceManagerTest` | Delegation for all `InstanceManager` creation variants and destruction fallback | | `OpenWebBeansCdi41IT` | CDI injection, request/session scope, `isMatchingBean`, `isMatchingEvent`, `getContexts`, interceptor bindings, method invoker | -| `OpenWebBeansTomcatIntegrationIT` | Server/context listener modes, `startWithoutBeansXml`, alternate `beans.xml` location, security valve configuration, filter/servlet injection, lifecycle callbacks | -| `OpenWebBeansSecurityIT` | BASIC authentication, `@Inject Principal`, security valve principal | +| `OpenWebBeansTomcatIntegrationIT` | Server/context listener modes, `startWithoutBeansXml` positive and negative behavior, alternate `beans.xml` location, security valve configuration, dynamic deployment, filter/listener/servlet injection and lifecycle callbacks | +| `OpenWebBeansSecurityIT` | BASIC authentication, `@Inject Principal`, security valve principal, principal proxy modes and custom proxy APIs | | `OpenWebBeansSecurityFilterIT` | Webapp-level `OpenWebBeansSecurityFilter` | | `OpenWebBeansJspIT` | JSP/EL resolution through OWB | | `OpenWebBeansRedeployIT` | Multiple webapps, classloader isolation, context restart, `@PreDestroy`, listener registration | diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansInstanceManagerTest.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansInstanceManagerTest.java new file mode 100644 index 0000000000..8c65af9eec --- /dev/null +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansInstanceManagerTest.java @@ -0,0 +1,151 @@ +/* + * 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.webbeans.web.tomcat; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import javax.naming.NamingException; + +import org.apache.tomcat.InstanceManager; +import org.junit.jupiter.api.Test; + +class OpenWebBeansInstanceManagerTest { + + @Test + void newInstanceByClassDelegates() throws Exception { + RecordingInstanceManager delegate = new RecordingInstanceManager(); + OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager( + getClass().getClassLoader(), delegate); + + Object instance = manager.newInstance(TestBean.class); + + assertEquals(1, delegate.classInstances); + assertInstanceOf(TestBean.class, instance); + } + + @Test + void newInstanceByClassNameDelegates() throws Exception { + RecordingInstanceManager delegate = new RecordingInstanceManager(); + OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager( + getClass().getClassLoader(), delegate); + + Object instance = manager.newInstance(TestBean.class.getName()); + + assertEquals(1, delegate.stringInstances); + assertInstanceOf(TestBean.class, instance); + } + + @Test + void newInstanceByClassNameAndClassLoaderDelegates() throws Exception { + RecordingInstanceManager delegate = new RecordingInstanceManager(); + OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager( + getClass().getClassLoader(), delegate); + + Object instance = manager.newInstance(TestBean.class.getName(), getClass().getClassLoader()); + + assertEquals(1, delegate.stringLoaderInstances); + assertInstanceOf(TestBean.class, instance); + } + + @Test + void newInstanceByObjectDoesNotDelegateCreation() throws Exception { + RecordingInstanceManager delegate = new RecordingInstanceManager(); + OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager( + getClass().getClassLoader(), delegate); + TestBean instance = new TestBean(); + + manager.newInstance(instance); + + assertEquals(0, delegate.objectInstances); + assertTrue(delegate.destroyed.isEmpty()); + } + + @Test + void destroyInstanceAlwaysDelegates() throws Exception { + RecordingInstanceManager delegate = new RecordingInstanceManager(); + OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager( + getClass().getClassLoader(), delegate); + TestBean instance = new TestBean(); + + manager.destroyInstance(instance); + + assertEquals(List.of(instance), delegate.destroyed); + } + + @Test + void destroyKnownInstanceDelegates() throws Exception { + RecordingInstanceManager delegate = new RecordingInstanceManager(); + OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager( + getClass().getClassLoader(), delegate); + + Object instance = manager.newInstance(TestBean.class); + manager.destroyInstance(instance); + + assertEquals(List.of(instance), delegate.destroyed); + } + + static class TestBean { + } + + static class RecordingInstanceManager implements InstanceManager { + + private int classInstances; + private int stringInstances; + private int stringLoaderInstances; + private int objectInstances; + private final List<Object> destroyed = new ArrayList<>(); + + @Override + public Object newInstance(Class<?> clazz) throws IllegalAccessException, InvocationTargetException, + NamingException, InstantiationException { + classInstances++; + return new TestBean(); + } + + @Override + public Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, + NamingException, InstantiationException, ClassNotFoundException { + stringInstances++; + return new TestBean(); + } + + @Override + public void newInstance(Object object) throws IllegalAccessException, InvocationTargetException, + NamingException { + objectInstances++; + } + + @Override + public Object newInstance(String className, ClassLoader classLoader) + throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, + ClassNotFoundException { + stringLoaderInstances++; + return new TestBean(); + } + + @Override + public void destroyInstance(Object object) throws IllegalAccessException, InvocationTargetException { + destroyed.add(object); + } + } +} diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityIT.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityIT.java index a19d5fad5b..b8f177e6b7 100644 --- a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityIT.java +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityIT.java @@ -17,31 +17,33 @@ package org.apache.webbeans.web.tomcat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.nio.file.Path; +import java.util.function.Consumer; import org.apache.webbeans.web.tomcat.OwbTestServer.ListenerMode; +import org.apache.webbeans.web.tomcat.testapp.CustomPrincipal; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; class OpenWebBeansSecurityIT { + private static final String PRINCIPAL_PROXY_PROPERTY = + "org.apache.webbeans.component.PrincipalBean.proxy"; + private static final String PRINCIPAL_PROXY_APIS_PROPERTY = + "org.apache.webbeans.component.PrincipalBean.proxyApis"; + @TempDir Path tempDir; @Test void basicAuthentication() throws Exception { - Path shadedJar = OwbTestSupport.shadedJar(); - Path webapp = new WebAppBuilder(tempDir.resolve("security"), "security").build(shadedJar); - try (OwbTestServer server = OwbTestServer.start(webapp, ListenerMode.SERVER, false, true, "/owb-security", - tomcat -> { - tomcat.addUser("testuser", "testpass"); - tomcat.addRole("testuser", "test"); - })) { + try (OwbTestServer server = startSecurityServer("security")) { HttpClient client = OwbTestSupport.newClient(); URI uri = server.baseUri().resolve("whoami"); @@ -49,10 +51,59 @@ class OpenWebBeansSecurityIT { assertEquals(401, unauthenticated.statusCode(), unauthenticated.body()); assertTrue(unauthenticated.headers().firstValue("WWW-Authenticate").orElse("").startsWith("Basic")); - HttpResponse<String> authenticated = OwbTestSupport.getResponse(client, uri, - OwbTestSupport.basicAuth("testuser", "testpass")); - assertEquals(200, authenticated.statusCode(), authenticated.body()); - assertEquals("testuser|testuser", authenticated.body().strip()); + assertEquals("testuser|testuser", authenticatedBody(server, "whoami")); } } + + @Test + void principalProxyEnabled() throws Exception { + try (OwbTestServer server = startSecurityServer("security-proxy")) { + String type = authenticatedBody(server, "security-service-principal-type"); + assertTrue(type.contains("TomcatSecurityServicePrincipal"), type); + } + } + + @Test + void principalProxyDisabled() throws Exception { + try (OwbTestServer server = startSecurityServer("security-no-proxy", + builder -> builder.openWebBeansProperty(PRINCIPAL_PROXY_PROPERTY, "false"))) { + String type = authenticatedBody(server, "security-service-principal-type"); + assertFalse(type.contains("TomcatSecurityServicePrincipal"), type); + } + } + + @Test + void customPrincipalProxyApis() throws Exception { + try (OwbTestServer server = startSecurityServer("security-custom-proxy", + builder -> builder + .openWebBeansProperty(PRINCIPAL_PROXY_PROPERTY, "true") + .openWebBeansProperty(PRINCIPAL_PROXY_APIS_PROPERTY, CustomPrincipal.class.getName()))) { + String type = authenticatedBody(server, "security-service-principal-custom"); + assertEquals("custom", type, type); + } + } + + private OwbTestServer startSecurityServer(String name) throws Exception { + return startSecurityServer(name, builder -> { + }); + } + + private OwbTestServer startSecurityServer(String name, Consumer<WebAppBuilder> customizer) throws Exception { + WebAppBuilder builder = new WebAppBuilder(tempDir.resolve(name), "security"); + customizer.accept(builder); + Path webapp = builder.build(OwbTestSupport.shadedJar()); + return OwbTestServer.start(webapp, ListenerMode.SERVER, false, true, "/owb-security", + tomcat -> { + tomcat.addUser("testuser", "testpass"); + tomcat.addRole("testuser", "test"); + }); + } + + private static String authenticatedBody(OwbTestServer server, String path) throws Exception { + HttpClient client = OwbTestSupport.newClient(); + HttpResponse<String> response = OwbTestSupport.getResponse(client, + server.baseUri().resolve(path), OwbTestSupport.basicAuth("testuser", "testpass")); + assertEquals(200, response.statusCode(), response.body()); + return response.body().strip(); + } } diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansTomcatIntegrationIT.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansTomcatIntegrationIT.java index 632297d09e..4abb80cc79 100644 --- a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansTomcatIntegrationIT.java +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansTomcatIntegrationIT.java @@ -64,6 +64,7 @@ class OpenWebBeansTomcatIntegrationIT { .build(shadedJar); try (OwbTestServer server = OwbTestServer.start(webapp, ListenerMode.SERVER, true, true)) { assertHello(server); + assertEquals("injected", status(server)); assertWebBeansListener(server, true); } } @@ -75,6 +76,7 @@ class OpenWebBeansTomcatIntegrationIT { .includeBeansXml(false) .build(shadedJar); try (OwbTestServer server = OwbTestServer.start(webapp, ListenerMode.SERVER, false, true)) { + assertEquals("not-injected", status(server)); assertWebBeansListener(server, false); } } @@ -101,6 +103,32 @@ class OpenWebBeansTomcatIntegrationIT { } } + @Test + void contextSecurityValveDisabled() throws Exception { + Path shadedJar = OwbTestSupport.shadedJar(); + Path webapp = new WebAppBuilder(tempDir.resolve("context-no-security-valve"), "cdi41").build(shadedJar); + try (OwbTestServer server = OwbTestServer.start(webapp, ListenerMode.CONTEXT, false, false)) { + assertHello(server); + assertSecurityValve(server, false); + } + } + + @Test + void dynamicContextDeployment() throws Exception { + Path shadedJar = OwbTestSupport.shadedJar(); + Path initial = new WebAppBuilder(tempDir.resolve("dynamic-initial"), "cdi41").build(shadedJar); + Path dynamic = new WebAppBuilder(tempDir.resolve("dynamic-app"), "multi/one").build(shadedJar); + try (OwbTestServer server = OwbTestServer.start(initial, ListenerMode.SERVER, false, true)) { + assertHello(server); + + server.deploy("/owb-dynamic", dynamic); + + HttpClient client = OwbTestSupport.newClient(); + assertEquals("one", OwbTestSupport.get(client, server.baseUri("/owb-dynamic").resolve("value"))); + assertHello(server); + } + } + @Test void filterListenerAndServletInjection() throws Exception { Path markerDirectory = tempDir.resolve("markers"); @@ -120,6 +148,8 @@ class OpenWebBeansTomcatIntegrationIT { OwbTestSupport.assertMarker(markerDirectory, "postConstruct.marker"); } OwbTestSupport.assertMarker(markerDirectory, "preDestroy.marker"); + OwbTestSupport.assertMarker(markerDirectory, "filterPreDestroy.marker"); + OwbTestSupport.assertMarker(markerDirectory, "listenerPreDestroy.marker"); } finally { if (previousMarkerDirectory == null) { System.clearProperty("owb.test.marker.dir"); @@ -134,6 +164,11 @@ class OpenWebBeansTomcatIntegrationIT { assertEquals("hello-owb-4.1.1", OwbTestSupport.get(client, server.baseUri().resolve("hello"))); } + private static String status(OwbTestServer server) throws Exception { + HttpClient client = OwbTestSupport.newClient(); + return OwbTestSupport.get(client, server.baseUri().resolve("injection-status")); + } + private static void assertSecurityValve(OwbTestServer server, boolean expected) { boolean found = Arrays.stream(server.context().getPipeline().getValves()) .anyMatch(valve -> valve instanceof OpenWebBeansSecurityValve); diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestServer.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestServer.java index d6c8e8723d..7b607afd7d 100644 --- a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestServer.java +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestServer.java @@ -19,7 +19,6 @@ package org.apache.webbeans.web.tomcat; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -45,11 +44,20 @@ final class OwbTestServer implements AutoCloseable { private final Tomcat tomcat; private final Map<String, Context> contexts; private final Map<String, URI> baseUris; + private final ListenerMode listenerMode; + private final boolean startWithoutBeansXml; + private final boolean addSecurityValve; + private final int port; - private OwbTestServer(Tomcat tomcat, Map<String, Context> contexts, Map<String, URI> baseUris) { + private OwbTestServer(Tomcat tomcat, Map<String, Context> contexts, Map<String, URI> baseUris, + ListenerMode listenerMode, boolean startWithoutBeansXml, boolean addSecurityValve, int port) { this.tomcat = tomcat; this.contexts = contexts; this.baseUris = baseUris; + this.listenerMode = listenerMode; + this.startWithoutBeansXml = startWithoutBeansXml; + this.addSecurityValve = addSecurityValve; + this.port = port; } static OwbTestServer start(Path webapp, ListenerMode mode, boolean startWithoutBeansXml, @@ -92,19 +100,8 @@ final class OwbTestServer implements AutoCloseable { Map<String, Context> contexts = new LinkedHashMap<>(); for (Deployment deployment : deployments) { Context context = tomcat.addWebapp(deployment.contextPath(), deployment.webapp().toString()); - if (context instanceof StandardContext standardContext) { - // These checks require --add-opens for java.lang and java.rmi. - // They are not relevant to the module integration tests. - standardContext.setClearReferencesThreadLocals(false); - standardContext.setClearReferencesRmiTargets(false); - } + configureContext(context, mode, startWithoutBeansXml, addSecurityValve); contexts.put(deployment.contextPath(), context); - if (mode == ListenerMode.CONTEXT) { - OpenWebBeansContextLifecycleListener listener = new OpenWebBeansContextLifecycleListener(); - listener.setStartWithoutBeansXml(startWithoutBeansXml); - listener.setAddSecurityValve(addSecurityValve); - context.addLifecycleListener(listener); - } } if (mode == ListenerMode.SERVER) { @@ -126,7 +123,18 @@ final class OwbTestServer implements AutoCloseable { for (String contextPath : contexts.keySet()) { baseUris.put(contextPath, URI.create("http://127.0.0.1:" + port + contextPath + "/")); } - return new OwbTestServer(tomcat, Collections.unmodifiableMap(contexts), Collections.unmodifiableMap(baseUris)); + return new OwbTestServer(tomcat, contexts, baseUris, mode, startWithoutBeansXml, addSecurityValve, port); + } + + Context deploy(String contextPath, Path webapp) throws Exception { + Context context = tomcat.addWebapp(contextPath, webapp.toString()); + configureContext(context, listenerMode, startWithoutBeansXml, addSecurityValve); + if (!context.getState().isAvailable()) { + context.start(); + } + contexts.put(contextPath, context); + baseUris.put(contextPath, URI.create("http://127.0.0.1:" + port + contextPath + "/")); + return context; } Context context() { @@ -166,6 +174,22 @@ final class OwbTestServer implements AutoCloseable { } } + private static void configureContext(Context context, ListenerMode mode, boolean startWithoutBeansXml, + boolean addSecurityValve) { + if (context instanceof StandardContext standardContext) { + // These checks require --add-opens for java.lang and java.rmi. + // They are not relevant to the module integration tests. + standardContext.setClearReferencesThreadLocals(false); + standardContext.setClearReferencesRmiTargets(false); + } + if (mode == ListenerMode.CONTEXT) { + OpenWebBeansContextLifecycleListener listener = new OpenWebBeansContextLifecycleListener(); + listener.setStartWithoutBeansXml(startWithoutBeansXml); + listener.setAddSecurityValve(addSecurityValve); + context.addLifecycleListener(listener); + } + } + private static void closeQuietly(Tomcat tomcat) { try { tomcat.stop(); diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/TomcatPluginTest.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/TomcatPluginTest.java new file mode 100644 index 0000000000..a9e53217a6 --- /dev/null +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/TomcatPluginTest.java @@ -0,0 +1,150 @@ +/* + * 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.webbeans.web.tomcat; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; + +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletRequestEvent; +import jakarta.servlet.ServletRequestListener; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpSessionEvent; +import jakarta.servlet.http.HttpSessionListener; + +import org.apache.webbeans.config.WebBeansContext; +import org.apache.webbeans.exception.WebBeansConfigurationException; +import org.apache.webbeans.spi.SecurityService; +import org.junit.jupiter.api.Test; + +class TomcatPluginTest { + + @Test + void securityServiceRegistration() { + TomcatPlugin plugin = new TomcatPlugin(); + + assertTrue(plugin.supportService(SecurityService.class)); + assertFalse(plugin.supportService(BeanManager.class)); + + SecurityService service = plugin.getSupportedService(SecurityService.class); + assertNotNull(service); + assertSame(WebBeansContext.getInstance().getSecurityService(), service); + assertNull(plugin.getSupportedService(BeanManager.class)); + } + + @Test + void servletSpecClassesAreRejectedAsManagedBeans() { + TomcatPlugin plugin = new TomcatPlugin(); + + assertThrows(WebBeansConfigurationException.class, () -> plugin.isManagedBean(TestServlet.class)); + assertThrows(WebBeansConfigurationException.class, () -> plugin.isManagedBean(TestFilter.class)); + assertThrows(WebBeansConfigurationException.class, + () -> plugin.isManagedBean(TestServletContextListener.class)); + assertThrows(WebBeansConfigurationException.class, () -> plugin.isManagedBean(TestSessionListener.class)); + assertThrows(WebBeansConfigurationException.class, + () -> plugin.isManagedBean(TestServletRequestListener.class)); + } + + @Test + void nonServletClassesAreManagedBeans() { + TomcatPlugin plugin = new TomcatPlugin(); + + assertDoesNotThrow(() -> plugin.isManagedBean(PlainBean.class)); + assertDoesNotThrow(() -> plugin.isManagedBean(Object.class)); + } + + @Test + void javaEeComponentInjectionSupport() { + TomcatPlugin plugin = new TomcatPlugin(); + + assertTrue(plugin.supportsJavaEeComponentInjections(TestServlet.class)); + assertTrue(plugin.supportsJavaEeComponentInjections(TestFilter.class)); + assertTrue(plugin.supportsJavaEeComponentInjections(TestServletContextListener.class)); + assertTrue(plugin.supportsJavaEeComponentInjections(TestSessionListener.class)); + assertTrue(plugin.supportsJavaEeComponentInjections(TestServletRequestListener.class)); + assertFalse(plugin.supportsJavaEeComponentInjections(PlainBean.class)); + assertFalse(plugin.supportsJavaEeComponentInjections(Object.class)); + } + + static class TestServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + } + + static class TestFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + chain.doFilter(request, response); + } + + @Override + public void destroy() { + } + } + + static class TestServletContextListener implements ServletContextListener { + @Override + public void contextInitialized(ServletContextEvent event) { + } + + @Override + public void contextDestroyed(ServletContextEvent event) { + } + } + + static class TestSessionListener implements HttpSessionListener { + @Override + public void sessionCreated(HttpSessionEvent event) { + } + + @Override + public void sessionDestroyed(HttpSessionEvent event) { + } + } + + static class TestServletRequestListener implements ServletRequestListener { + @Override + public void requestInitialized(ServletRequestEvent event) { + } + + @Override + public void requestDestroyed(ServletRequestEvent event) { + } + } + + static class PlainBean { + } +} diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/WebAppBuilder.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/WebAppBuilder.java index c7194f57f6..918508649c 100644 --- a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/WebAppBuilder.java +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/WebAppBuilder.java @@ -23,9 +23,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Stream; @@ -45,6 +47,7 @@ final class WebAppBuilder { private final Path destination; private final Path template; private final Set<Path> sourceRoots = new LinkedHashSet<>(); + private final Map<String, String> openWebBeansProperties = new LinkedHashMap<>(); private boolean includeBeansXml = true; private boolean includeWebXml = true; private boolean beansXmlInClasses; @@ -78,6 +81,11 @@ final class WebAppBuilder { return this; } + WebAppBuilder openWebBeansProperty(String name, String value) { + openWebBeansProperties.put(name, value); + return this; + } + Path build(Path shadedJar) throws IOException { Path webInf = template.resolve("WEB-INF"); if (Files.isDirectory(webInf)) { @@ -106,6 +114,16 @@ final class WebAppBuilder { Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); } + if (!openWebBeansProperties.isEmpty()) { + Path target = classes.resolve("META-INF/openwebbeans/openwebbeans.properties"); + Files.createDirectories(target.getParent()); + StringBuilder properties = new StringBuilder(); + for (Map.Entry<String, String> entry : openWebBeansProperties.entrySet()) { + properties.append(entry.getKey()).append('=').append(entry.getValue()).append('\n'); + } + Files.writeString(target, properties.toString(), StandardCharsets.UTF_8); + } + return destination; } diff --git a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/testapp/CustomPrincipal.java similarity index 55% copy from modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java copy to modules/owb/src/test/java/org/apache/webbeans/web/tomcat/testapp/CustomPrincipal.java index 5d159fc355..dedfd4848d 100644 --- a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/testapp/CustomPrincipal.java @@ -16,19 +16,10 @@ */ package org.apache.webbeans.web.tomcat.testapp; -import jakarta.enterprise.inject.spi.CDI; -import jakarta.servlet.ServletContextEvent; -import jakarta.servlet.ServletContextListener; -import jakarta.servlet.annotation.WebListener; +import java.security.Principal; -@WebListener -public class LifecycleContextListener implements ServletContextListener { - - @Override - public void contextInitialized(ServletContextEvent event) { - // Listeners are instantiated before the CDI container is started, so - // use programmatic lookup rather than field injection here. - LifecycleGreeting greeting = CDI.current().select(LifecycleGreeting.class).get(); - event.getServletContext().setAttribute("owb.listener.greeting", greeting.message()); - } +/** + * Test interface used to verify the Principal proxy API configuration. + */ +public interface CustomPrincipal extends Principal { } diff --git a/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java index a7b759fa28..ce0fabb9f7 100644 --- a/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java +++ b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java @@ -25,7 +25,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet(urlPatterns = { "/hello", "/request", "/session", "/matching", "/matching-event", "/contexts", - "/interceptor", "/invoker" }) + "/interceptor", "/invoker", "/injection-status" }) public class Cdi41Servlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -73,6 +73,9 @@ public class Cdi41Servlet extends HttpServlet { case "/invoker": result = InvokerExtension.invoke(); break; + case "/injection-status": + result = greeting == null ? "not-injected" : "injected"; + break; default: response.sendError(HttpServletResponse.SC_NOT_FOUND); return; diff --git a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java index 5d159fc355..eb7147f16a 100644 --- a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java +++ b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java @@ -16,6 +16,11 @@ */ package org.apache.webbeans.web.tomcat.testapp; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import jakarta.annotation.PreDestroy; import jakarta.enterprise.inject.spi.CDI; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; @@ -31,4 +36,16 @@ public class LifecycleContextListener implements ServletContextListener { LifecycleGreeting greeting = CDI.current().select(LifecycleGreeting.class).get(); event.getServletContext().setAttribute("owb.listener.greeting", greeting.message()); } + + @PreDestroy + void preDestroy() { + String markerDirectory = System.getProperty("owb.test.marker.dir"); + if (markerDirectory != null) { + try { + Files.writeString(Path.of(markerDirectory).resolve("listenerPreDestroy.marker"), "marker"); + } catch (IOException e) { + throw new IllegalStateException("Unable to write listener lifecycle marker", e); + } + } + } } diff --git a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleFilter.java b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleFilter.java index 4565f859e9..68a689eba8 100644 --- a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleFilter.java +++ b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleFilter.java @@ -17,7 +17,10 @@ package org.apache.webbeans.web.tomcat.testapp; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import jakarta.annotation.PreDestroy; import jakarta.inject.Inject; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; @@ -42,4 +45,20 @@ public class LifecycleFilter implements Filter { httpRequest.setAttribute("owb.filter.greeting", greeting.message()); chain.doFilter(request, response); } + + @PreDestroy + void preDestroy() { + writeMarker("filterPreDestroy.marker"); + } + + private static void writeMarker(String name) { + String markerDirectory = System.getProperty("owb.test.marker.dir"); + if (markerDirectory != null) { + try { + Files.writeString(Path.of(markerDirectory).resolve(name), "marker"); + } catch (IOException e) { + throw new IllegalStateException("Unable to write lifecycle marker " + name, e); + } + } + } } diff --git a/modules/owb/src/test/webapp/security/WEB-INF/web.xml b/modules/owb/src/test/webapp/security/WEB-INF/web.xml index 56ce5e3df3..d7f4d6c54e 100644 --- a/modules/owb/src/test/webapp/security/WEB-INF/web.xml +++ b/modules/owb/src/test/webapp/security/WEB-INF/web.xml @@ -26,6 +26,8 @@ <web-resource-collection> <web-resource-name>Protected</web-resource-name> <url-pattern>/whoami</url-pattern> + <url-pattern>/security-service-principal-type</url-pattern> + <url-pattern>/security-service-principal-custom</url-pattern> </web-resource-collection> <auth-constraint> <role-name>test</role-name> diff --git a/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java b/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java index 39f5bc090e..dadac92dfd 100644 --- a/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java +++ b/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java @@ -18,7 +18,6 @@ package org.apache.webbeans.web.tomcat.testapp; import java.io.IOException; import java.security.Principal; -import java.util.Objects; import jakarta.inject.Inject; import jakarta.servlet.annotation.WebServlet; @@ -26,9 +25,11 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.apache.webbeans.config.WebBeansContext; +import org.apache.webbeans.spi.SecurityService; import org.apache.webbeans.web.tomcat.OpenWebBeansSecurityValve; -@WebServlet("/whoami") +@WebServlet(urlPatterns = { "/whoami", "/security-service-principal-type", "/security-service-principal-custom" }) public class WhoAmIServlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -38,9 +39,26 @@ public class WhoAmIServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + String path = request.getServletPath(); + response.setContentType("text/plain"); + + if ("/security-service-principal-type".equals(path)) { + Principal current = securityService().getCurrentPrincipal(); + response.getWriter().println(current.getClass().getName()); + return; + } + if ("/security-service-principal-custom".equals(path)) { + Principal current = securityService().getCurrentPrincipal(); + response.getWriter().println(current instanceof CustomPrincipal ? "custom" : "not-custom"); + return; + } + Principal valvePrincipal = OpenWebBeansSecurityValve.getPrincipal(); String valveName = valvePrincipal == null ? "null" : valvePrincipal.getName(); - response.setContentType("text/plain"); - response.getWriter().println(Objects.requireNonNull(principal).getName() + "|" + valveName); + response.getWriter().println(principal.getName() + "|" + valveName); + } + + private static SecurityService securityService() { + return WebBeansContext.currentInstance().getSecurityService(); } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
