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 24a37bbce3e3b7496db03913aa55ea7f99cd3967
Author: Jonathan Gallimore <[email protected]>
AuthorDate: Wed Oct 7 16:48:14 2026 +0100

    Cover all HTTP methods in generated JAX-WS webservice contexts (#3066)
    
    * Add tests
    
    * Test fix
    
    (cherry picked from commit fca9dbddff1d704d03bcfc77bf8e90e3a9aa24f6)
---
 .../arquillian/tests/jaxws/verb/GreeterBean.java   |  36 +++++
 .../arquillian/tests/jaxws/verb/GreeterWs.java     |  25 +++
 .../jaxws/verb/WsVerbSecurityIntrospectTest.java   | 131 +++++++++++++++
 .../tests/jaxws/verb/WsVerbSecurityJarTest.java    | 179 +++++++++++++++++++++
 .../tests/jaxws/verb/WsVerbSecurityWarTest.java    | 170 +++++++++++++++++++
 5 files changed, 541 insertions(+)

diff --git 
a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/GreeterBean.java
 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/GreeterBean.java
new file mode 100644
index 0000000000..9b5e34e708
--- /dev/null
+++ 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/GreeterBean.java
@@ -0,0 +1,36 @@
+/*
+ * 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.arquillian.tests.jaxws.verb;
+
+import jakarta.ejb.Singleton;
+import jakarta.jws.WebMethod;
+import jakarta.jws.WebParam;
+import jakarta.jws.WebService;
+
+@Singleton
+@WebService(name = "Greeter",
+            targetNamespace = 
"http://verb.jaxws.tests.arquillian.openejb.apache.org/";,
+            serviceName = "GreeterService",
+            portName = "GreeterPort")
+public class GreeterBean implements GreeterWs {
+
+    @Override
+    @WebMethod
+    public String greet(@WebParam(name = "name") final String name) {
+        return "Hello, " + name;
+    }
+}
diff --git 
a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/GreeterWs.java
 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/GreeterWs.java
new file mode 100644
index 0000000000..e40486115d
--- /dev/null
+++ 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/GreeterWs.java
@@ -0,0 +1,25 @@
+/*
+ * 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.arquillian.tests.jaxws.verb;
+
+import jakarta.jws.WebParam;
+import jakarta.jws.WebService;
+
+@WebService(targetNamespace = 
"http://verb.jaxws.tests.arquillian.openejb.apache.org/";)
+public interface GreeterWs {
+    String greet(@WebParam(name = "name") final String name);
+}
diff --git 
a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityIntrospectTest.java
 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityIntrospectTest.java
new file mode 100644
index 0000000000..1f847d8322
--- /dev/null
+++ 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityIntrospectTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.arquillian.tests.jaxws.verb;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Service;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.StandardServer;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+import org.apache.tomee.loader.TomcatHelper;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(Arquillian.class)
+public class WsVerbSecurityIntrospectTest {
+
+    @Deployment
+    public static WebArchive war() {
+        final String ejbJar =
+                "<ejb-jar xmlns=\"http://java.sun.com/xml/ns/javaee\"; 
version=\"3.1\" metadata-complete=\"false\">\n" +
+                "  <enterprise-beans>\n" +
+                "    <session>\n" +
+                "      <ejb-name>GreeterBean</ejb-name>\n" +
+                "      
<service-endpoint>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterWs</service-endpoint>\n"
 +
+                "      
<ejb-class>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterBean</ejb-class>\n"
 +
+                "      <session-type>Singleton</session-type>\n" +
+                "      <transaction-type>Container</transaction-type>\n" +
+                "    </session>\n" +
+                "  </enterprise-beans>\n" +
+                "</ejb-jar>";
+
+        final String openejbJar =
+                "<openejb-jar 
xmlns=\"http://www.openejb.org/xml/ns/openejb-jar-2.1\";>\n" +
+                "  <enterprise-beans>\n" +
+                "    <session>\n" +
+                "      <ejb-name>GreeterBean</ejb-name>\n" +
+                "      
<web-service-address>/ws/Greeter</web-service-address>\n" +
+                "      <web-service-security>\n" +
+                "        <security-realm-name/>\n" +
+                "        <transport-guarantee>NONE</transport-guarantee>\n" +
+                "        <auth-method>BASIC</auth-method>\n" +
+                "      </web-service-security>\n" +
+                "    </session>\n" +
+                "  </enterprise-beans>\n" +
+                "</openejb-jar>";
+
+        final String webservices =
+                "<webservices xmlns=\"http://java.sun.com/xml/ns/j2ee\"; 
version=\"1.1\">\n" +
+                "  <webservice-description>\n" +
+                "    
<webservice-description-name>GreeterService</webservice-description-name>\n" +
+                "    <port-component>\n" +
+                "      
<port-component-name>GreeterPort</port-component-name>\n" +
+                "      <wsdl-port>GreeterPort</wsdl-port>\n" +
+                "      
<service-endpoint-interface>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterWs</service-endpoint-interface>\n"
 +
+                "      
<service-impl-bean><ejb-link>GreeterBean</ejb-link></service-impl-bean>\n" +
+                "    </port-component>\n" +
+                "  </webservice-description>\n" +
+                "</webservices>";
+
+        return ShrinkWrap.create(WebArchive.class, "WsVerbSecurityWar.war")
+                .addClasses(GreeterWs.class, GreeterBean.class)
+                .addAsWebInfResource(new StringAsset(ejbJar), "ejb-jar.xml")
+                .addAsWebInfResource(new StringAsset(openejbJar), 
"openejb-jar.xml")
+                .addAsWebInfResource(new StringAsset(webservices), 
"webservices.xml");
+    }
+
+    @Test
+    public void dumpSecurity() {
+        final StandardServer server = TomcatHelper.getServer();
+        for (final Service service : server.findServices()) {
+            if (!(service.getContainer() instanceof Engine)) {
+                continue;
+            }
+            final Engine engine = (Engine) service.getContainer();
+            final Container host = engine.findChild(engine.getDefaultHost());
+            for (final Container child : host.findChildren()) {
+                if (!(child instanceof Context) || 
!child.getName().contains("WsVerbSecurity")) {
+                    continue;
+                }
+                final Context context = (Context) child;
+                System.out.println(">>> CONTEXT " + context.getName());
+
+                final LoginConfig loginConfig = context.getLoginConfig();
+                System.out.println(">>>   loginConfig = " + (loginConfig == 
null ? "null"
+                        : loginConfig.getAuthMethod() + " realm=" + 
loginConfig.getRealmName()));
+
+                final SecurityConstraint[] constraints = 
context.findConstraints();
+                System.out.println(">>>   constraints = " + 
constraints.length);
+                for (final SecurityConstraint sc : constraints) {
+                    for (final SecurityCollection collection : 
sc.findCollections()) {
+                        System.out.println(">>>     collection name=" + 
collection.getName()
+                                + " patterns=" + 
Arrays.toString(collection.findPatterns())
+                                + " methods=" + 
Arrays.toString(collection.findMethods())
+                                + " omitted=" + 
Arrays.toString(collection.findOmittedMethods())
+                                + " authRoles=" + 
Arrays.toString(sc.findAuthRoles())
+                                + " authConstraint=" + sc.getAuthConstraint());
+                    }
+                }
+
+                for (final Valve valve : context.getPipeline().getValves()) {
+                    System.out.println(">>>   valve = " + 
valve.getClass().getName());
+                }
+            }
+        }
+    }
+}
diff --git 
a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityJarTest.java
 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityJarTest.java
new file mode 100644
index 0000000000..6b1da75035
--- /dev/null
+++ 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityJarTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.arquillian.tests.jaxws.verb;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Validates the HTTP-method scope of the security constraint that
+ * {@link org.apache.tomee.webservices.TomcatWsRegistry} applies to a 
JAR-packaged EJB web service
+ * (the {@code deployInFakeWebapp()} / {@code createNewContext()} route).
+ *
+ * <p>{@code createNewContext} builds its {@code SecurityCollection} with
+ * {@code addMethod("GET")} + {@code addMethod("POST")}. Per the servlet spec, 
naming methods in a
+ * web-resource-collection scopes the constraint to <em>only</em> those 
methods; every other verb is
+ * "uncovered" and reachable without authentication.</p>
+ *
+ * <p>So although the endpoint declares BASIC auth, only GET and POST are 
actually guarded:</p>
+ * <ul>
+ *   <li>{@link #postWithoutCredentialsIsUnauthorized()} - POST is challenged 
with 401 (passes),
+ *       proving the endpoint really is secured; and</li>
+ *   <li>{@link #deleteWithoutCredentialsIsUnauthorized()} - DELETE is 
<em>not</em> challenged: it
+ *       slips past the authenticator and reaches the servlet (which answers 
405). This assertion
+ *       fails, highlighting the gap.</li>
+ * </ul>
+ */
+@RunWith(Arquillian.class)
+public class WsVerbSecurityJarTest {
+
+    /** Deployment name; the generated fake webapp context is named after the 
module. */
+    private static final String MODULE = "WsVerbSecurityJar";
+
+    private static final String SOAP_REQUEST =
+            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+            "<soapenv:Envelope 
xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"; +
+            "                  
xmlns:ns=\"http://verb.jaxws.tests.arquillian.openejb.apache.org/\";>\n" +
+            "  <soapenv:Header/>\n" +
+            "  <soapenv:Body>\n" +
+            "    <ns:greet><name>world</name></ns:greet>\n" +
+            "  </soapenv:Body>\n" +
+            "</soapenv:Envelope>";
+
+    @ArquillianResource
+    private URL base;
+
+    @Deployment(testable = false)
+    public static JavaArchive jar() {
+        final String ejbJar =
+                "<ejb-jar xmlns=\"http://java.sun.com/xml/ns/javaee\"\n"; +
+                "         
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"; +
+                "         
xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd\"\n"; +
+                "         version=\"3.1\" metadata-complete=\"false\">\n" +
+                "  <enterprise-beans>\n" +
+                "    <session>\n" +
+                "      <ejb-name>GreeterBean</ejb-name>\n" +
+                "      
<service-endpoint>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterWs</service-endpoint>\n"
 +
+                "      
<ejb-class>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterBean</ejb-class>\n"
 +
+                "      <session-type>Singleton</session-type>\n" +
+                "      <transaction-type>Container</transaction-type>\n" +
+                "    </session>\n" +
+                "  </enterprise-beans>\n" +
+                "</ejb-jar>";
+
+        final String openejbJar =
+                "<openejb-jar 
xmlns=\"http://www.openejb.org/xml/ns/openejb-jar-2.1\";>\n" +
+                "  <enterprise-beans>\n" +
+                "    <session>\n" +
+                "      <ejb-name>GreeterBean</ejb-name>\n" +
+                "      
<web-service-address>/ws/Greeter</web-service-address>\n" +
+                "      <web-service-security>\n" +
+                "        <security-realm-name/>\n" +
+                "        <transport-guarantee>NONE</transport-guarantee>\n" +
+                "        <auth-method>BASIC</auth-method>\n" +
+                "      </web-service-security>\n" +
+                "    </session>\n" +
+                "  </enterprise-beans>\n" +
+                "</openejb-jar>";
+
+        final String webservices =
+                "<webservices xmlns=\"http://java.sun.com/xml/ns/j2ee\"\n"; +
+                "             
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"; +
+                "             
xsi:schemaLocation=\"http://java.sun.com/xml/ns/j2ee 
http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd\"\n"; +
+                "             version=\"1.1\">\n" +
+                "  <webservice-description>\n" +
+                "    
<webservice-description-name>GreeterService</webservice-description-name>\n" +
+                "    <port-component>\n" +
+                "      
<port-component-name>GreeterPort</port-component-name>\n" +
+                "      <wsdl-port>GreeterPort</wsdl-port>\n" +
+                "      
<service-endpoint-interface>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterWs</service-endpoint-interface>\n"
 +
+                "      <service-impl-bean>\n" +
+                "        <ejb-link>GreeterBean</ejb-link>\n" +
+                "      </service-impl-bean>\n" +
+                "    </port-component>\n" +
+                "  </webservice-description>\n" +
+                "</webservices>";
+
+        return ShrinkWrap.create(JavaArchive.class, MODULE + ".jar")
+                .addClasses(GreeterWs.class, GreeterBean.class)
+                .addAsManifestResource(new StringAsset(ejbJar), "ejb-jar.xml")
+                .addAsManifestResource(new StringAsset(openejbJar), 
"openejb-jar.xml")
+                .addAsManifestResource(new StringAsset(webservices), 
"webservices.xml");
+    }
+
+    /** Control: the endpoint really is secured - an unauthenticated POST is 
challenged. */
+    @Test
+    public void postWithoutCredentialsIsUnauthorized() throws Exception {
+        final int status = call("POST", SOAP_REQUEST);
+        assertEquals("An unauthenticated POST must be challenged with 401. 
Actual: " + status,
+                HTTP_UNAUTHORIZED, status);
+    }
+
+    /**
+     * The gap: the same endpoint, same BASIC config, but DELETE is not 
covered by the GET/POST
+     * constraint, so it is never challenged. Expected to fail (actual 405 - 
the request bypassed
+     * authentication and reached the servlet) until createNewContext 
constrains all methods.
+     */
+    @Test
+    public void deleteWithoutCredentialsIsUnauthorized() throws Exception {
+        final int status = call("DELETE", null);
+        assertEquals("An unauthenticated DELETE must be challenged with 401, 
but it bypassed "
+                + "authentication (a non-401 status means it reached the 
servlet). Actual: " + status,
+                HTTP_UNAUTHORIZED, status);
+    }
+
+    private int call(final String method, final String body) throws Exception {
+        // A standalone EJB JAR has no web context of its own, so 
@ArquillianResource only gives a
+        // usable host:port (its path is the app context in embedded but the 
arquillian-protocol
+        // context in the remote adapter). The web service is published in the 
generated fake webapp
+        // named after the module, so address it explicitly rather than 
relative to base.
+        final URL target = new URL(base.getProtocol() + "://" + base.getHost() 
+ ":" + base.getPort()
+                + "/" + MODULE + "/ws/Greeter");
+        final HttpURLConnection connection = (HttpURLConnection) 
target.openConnection();
+        try {
+            connection.setRequestMethod(method);
+            connection.setInstanceFollowRedirects(false);
+            connection.setConnectTimeout(5000);
+            connection.setReadTimeout(5000);
+            if (body != null) {
+                connection.setRequestProperty("Content-Type", "text/xml; 
charset=UTF-8");
+                connection.setRequestProperty("SOAPAction", "\"\"");
+                connection.setDoOutput(true);
+                try (final OutputStream out = connection.getOutputStream()) {
+                    out.write(body.getBytes(UTF_8));
+                }
+            }
+            return connection.getResponseCode();
+        } finally {
+            connection.disconnect();
+        }
+    }
+}
diff --git 
a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityWarTest.java
 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityWarTest.java
new file mode 100644
index 0000000000..f1f7b78e18
--- /dev/null
+++ 
b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/verb/WsVerbSecurityWarTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.arquillian.tests.jaxws.verb;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * The EJB-in-WAR counterpart to {@link WsVerbSecurityJarTest}: the same 
singleton EJB web service,
+ * declaring BASIC auth, but packaged in a WAR so that {@code 
TomcatWsRegistry#addWsContainer} takes
+ * the {@code addServlet} route into the existing web application context.
+ *
+ * <p>Both verbs assert the desired behaviour - an unauthenticated call is 
challenged with 401 -
+ * so the module's result matrix shows how the two deployment styles differ on 
HTTP-method scoping.
+ * On a tree where the addServlet route applies no security to the endpoint, 
both fail; where it
+ * secures the mapping without restricting methods, both pass (unlike the JAR 
route, which leaves
+ * every verb except GET/POST uncovered).</p>
+ */
+@RunWith(Arquillian.class)
+public class WsVerbSecurityWarTest {
+
+    private static final String SOAP_REQUEST =
+            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+            "<soapenv:Envelope 
xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"; +
+            "                  
xmlns:ns=\"http://verb.jaxws.tests.arquillian.openejb.apache.org/\";>\n" +
+            "  <soapenv:Header/>\n" +
+            "  <soapenv:Body>\n" +
+            "    <ns:greet><name>world</name></ns:greet>\n" +
+            "  </soapenv:Body>\n" +
+            "</soapenv:Envelope>";
+
+    @ArquillianResource
+    private URL base;
+
+    @Deployment(testable = false)
+    public static WebArchive war() {
+        final String ejbJar =
+                "<ejb-jar xmlns=\"http://java.sun.com/xml/ns/javaee\"\n"; +
+                "         
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"; +
+                "         
xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd\"\n"; +
+                "         version=\"3.1\" metadata-complete=\"false\">\n" +
+                "  <enterprise-beans>\n" +
+                "    <session>\n" +
+                "      <ejb-name>GreeterBean</ejb-name>\n" +
+                "      
<service-endpoint>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterWs</service-endpoint>\n"
 +
+                "      
<ejb-class>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterBean</ejb-class>\n"
 +
+                "      <session-type>Singleton</session-type>\n" +
+                "      <transaction-type>Container</transaction-type>\n" +
+                "    </session>\n" +
+                "  </enterprise-beans>\n" +
+                "</ejb-jar>";
+
+        final String openejbJar =
+                "<openejb-jar 
xmlns=\"http://www.openejb.org/xml/ns/openejb-jar-2.1\";>\n" +
+                "  <enterprise-beans>\n" +
+                "    <session>\n" +
+                "      <ejb-name>GreeterBean</ejb-name>\n" +
+                "      
<web-service-address>/ws/Greeter</web-service-address>\n" +
+                "      <web-service-security>\n" +
+                "        <security-realm-name/>\n" +
+                "        <transport-guarantee>NONE</transport-guarantee>\n" +
+                "        <auth-method>BASIC</auth-method>\n" +
+                "      </web-service-security>\n" +
+                "    </session>\n" +
+                "  </enterprise-beans>\n" +
+                "</openejb-jar>";
+
+        final String webservices =
+                "<webservices xmlns=\"http://java.sun.com/xml/ns/j2ee\"\n"; +
+                "             
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"; +
+                "             
xsi:schemaLocation=\"http://java.sun.com/xml/ns/j2ee 
http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd\"\n"; +
+                "             version=\"1.1\">\n" +
+                "  <webservice-description>\n" +
+                "    
<webservice-description-name>GreeterService</webservice-description-name>\n" +
+                "    <port-component>\n" +
+                "      
<port-component-name>GreeterPort</port-component-name>\n" +
+                "      <wsdl-port>GreeterPort</wsdl-port>\n" +
+                "      
<service-endpoint-interface>org.apache.openejb.arquillian.tests.jaxws.verb.GreeterWs</service-endpoint-interface>\n"
 +
+                "      <service-impl-bean>\n" +
+                "        <ejb-link>GreeterBean</ejb-link>\n" +
+                "      </service-impl-bean>\n" +
+                "    </port-component>\n" +
+                "  </webservice-description>\n" +
+                "</webservices>";
+
+        final String webXml =
+                "<web-app xmlns=\"https://jakarta.ee/xml/ns/jakartaee\"\n"; +
+                "         
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"; +
+                "         
xsi:schemaLocation=\"https://jakarta.ee/xml/ns/jakartaee 
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd\"\n"; +
+                "         version=\"6.0\">\n" +
+                "  <display-name>WsVerbSecurityWar</display-name>\n" +
+                "</web-app>";
+
+        return ShrinkWrap.create(WebArchive.class, "WsVerbSecurityWar.war")
+                .addClasses(GreeterWs.class, GreeterBean.class)
+                .addAsWebInfResource(new StringAsset(ejbJar), "ejb-jar.xml")
+                .addAsWebInfResource(new StringAsset(openejbJar), 
"openejb-jar.xml")
+                .addAsWebInfResource(new StringAsset(webservices), 
"webservices.xml")
+                .setWebXML(new StringAsset(webXml));
+    }
+
+    @Test
+    public void postWithoutCredentialsIsUnauthorized() throws Exception {
+        final int status = call("POST", SOAP_REQUEST);
+        assertEquals("An unauthenticated POST must be challenged with 401. 
Actual: " + status,
+                HTTP_UNAUTHORIZED, status);
+    }
+
+    @Test
+    public void deleteWithoutCredentialsIsUnauthorized() throws Exception {
+        final int status = call("DELETE", null);
+        assertEquals("An unauthenticated DELETE must be challenged with 401, 
but it bypassed "
+                + "authentication (a non-401 status means it reached the 
servlet). Actual: " + status,
+                HTTP_UNAUTHORIZED, status);
+    }
+
+    private int call(final String method, final String body) throws Exception {
+        String root = base.toExternalForm();
+        if (!root.endsWith("/")) {
+            root += "/";
+        }
+        // WEBSERVICE_SUB_CONTEXT (/webservices) + the web-service-address 
(/ws/Greeter)
+        final HttpURLConnection connection = (HttpURLConnection) new URL(root 
+ "webservices/ws/Greeter").openConnection();
+        try {
+            connection.setRequestMethod(method);
+            connection.setInstanceFollowRedirects(false);
+            connection.setConnectTimeout(5000);
+            connection.setReadTimeout(5000);
+            if (body != null) {
+                connection.setRequestProperty("Content-Type", "text/xml; 
charset=UTF-8");
+                connection.setRequestProperty("SOAPAction", "\"\"");
+                connection.setDoOutput(true);
+                try (final OutputStream out = connection.getOutputStream()) {
+                    out.write(body.getBytes(UTF_8));
+                }
+            }
+            return connection.getResponseCode();
+        } finally {
+            connection.disconnect();
+        }
+    }
+}

Reply via email to