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(); + } + } +}
