This is an automated email from the ASF dual-hosted git repository.
rzo1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomee.git
The following commit(s) were added to refs/heads/main by this push:
new fca9dbddff Cover all HTTP methods in generated JAX-WS webservice
contexts (#3066)
fca9dbddff is described below
commit fca9dbddff1d704d03bcfc77bf8e90e3a9aa24f6
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
---
.../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();
+ }
+ }
+}