diff --git a/docs/modules/ROOT/pages/includes/quarkus-cxf.adoc b/docs/modules/ROOT/pages/includes/quarkus-cxf.adoc index 1539e66cf..f7a7c45e1 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-cxf.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-cxf.adoc @@ -1766,7 +1766,7 @@ a| [[quarkus-cxf_quarkus.cxf.client.-clients-.username]]`link:#quarkus-cxf_quark [.description] -- -The username for HTTP Basic auth +The username for HTTP Basic authentication ifdef::add-copy-button-to-env-var[] Environment variable: env_var_with_copy_button:+++QUARKUS_CXF_CLIENT__CLIENTS__USERNAME+++[] @@ -1783,7 +1783,7 @@ a| [[quarkus-cxf_quarkus.cxf.client.-clients-.password]]`link:#quarkus-cxf_quark [.description] -- -The password for HTTP Basic auth +The password for HTTP Basic authentication ifdef::add-copy-button-to-env-var[] Environment variable: env_var_with_copy_button:+++QUARKUS_CXF_CLIENT__CLIENTS__PASSWORD+++[] @@ -1795,6 +1795,23 @@ endif::add-copy-button-to-env-var[] | +a| [[quarkus-cxf_quarkus.cxf.client.-clients-.secure-wsdl-access]]`link:#quarkus-cxf_quarkus.cxf.client.-clients-.secure-wsdl-access[quarkus.cxf.client."clients".secure-wsdl-access]` + + +[.description] +-- +If `true`, then the `Authentication` header will be sent preemptively when requesting the WSDL, as long as the `username` is set; otherwise the WSDL will be requested anonymously. + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_CXF_CLIENT__CLIENTS__SECURE_WSDL_ACCESS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_CXF_CLIENT__CLIENTS__SECURE_WSDL_ACCESS+++` +endif::add-copy-button-to-env-var[] +--|boolean +|`false` + + a| [[quarkus-cxf_quarkus.cxf.client.-clients-.logging.enabled]]`link:#quarkus-cxf_quarkus.cxf.client.-clients-.logging.enabled[quarkus.cxf.client."clients".logging.enabled]` diff --git a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java index 8bd653210..0b09d00b2 100644 --- a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java +++ b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/CxfClientProcessor.java @@ -40,8 +40,10 @@ import io.quarkiverse.cxf.CxfFixedConfig; import io.quarkiverse.cxf.CxfFixedConfig.ClientFixedConfig; import io.quarkiverse.cxf.HttpClientHTTPConduitFactory; +import io.quarkiverse.cxf.QuarkusHttpConduitConfigurer; import io.quarkiverse.cxf.annotation.CXFClient; import io.quarkiverse.cxf.graal.QuarkusCxfFeature; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanBuildItem; import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; @@ -523,4 +525,10 @@ private ProxyInfo(List interfaces, boolean isRuntimeInitialized) { } + @BuildStep + void additionalBeans(BuildProducer additionalBeans) { + additionalBeans.produce( + new AdditionalBeanBuildItem(QuarkusHttpConduitConfigurer.class)); + } + } diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java index 2bdd0e568..7773146ed 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java @@ -194,6 +194,8 @@ public class CXFClientInfo { private final SchemaValidationType schemaValidationEnabledFor; + private final boolean secureWsdlAccess; + public CXFClientInfo(CXFClientData other, CxfClientConfig config, String configKey) { Objects.requireNonNull(config); this.sei = other.getSei(); @@ -205,6 +207,7 @@ public CXFClientInfo(CXFClientData other, CxfClientConfig config, String configK this.epName = config.endpointName().orElse(null); this.username = config.username().orElse(null); this.password = config.password().orElse(null); + this.secureWsdlAccess = config.secureWsdlAccess(); this.endpointAddress = config.clientEndpointUrl().orElse(DEFAULT_EP_ADDR + "/" + this.sei.toLowerCase()); this.wsdlUrl = config.wsdlPath().orElse(null); addFeatures(config); @@ -478,4 +481,8 @@ public SchemaValidationType getSchemaValidationEnabledFor() { return schemaValidationEnabledFor; } + public boolean isSecureWsdlAccess() { + return secureWsdlAccess; + } + } diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java index 49f8d406c..3a4040d8d 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java @@ -55,15 +55,24 @@ public interface CxfClientConfig { public Optional endpointName(); /** - * The username for HTTP Basic auth + * The username for HTTP Basic authentication */ public Optional username(); /** - * The password for HTTP Basic auth + * The password for HTTP Basic authentication */ public Optional password(); + /** + * If {@code true}, then the {@code Authentication} header will be sent preemptively when requesting the WSDL, as + * long as the {@code username} is set; otherwise the WSDL will be requested anonymously. + * + * @since 2.7.0 + */ + @WithDefault("false") + public boolean secureWsdlAccess(); + /** * Logging related configuration */ diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java index 2dd839fa3..c323b0ebc 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java @@ -28,6 +28,7 @@ import org.apache.cxf.annotations.SchemaValidation.SchemaValidationType; import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.configuration.security.AuthorizationPolicy; import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; @@ -66,6 +67,9 @@ public abstract class CxfClientProducer { @Inject CxfFixedConfig fixedConfig; + @Inject + QuarkusHttpConduitConfigurer httpConduitConfigurer; + @Inject @Any Instance customizers; @@ -140,6 +144,8 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) { } QuarkusClientFactoryBean quarkusClientFactoryBean = new QuarkusClientFactoryBean(); QuarkusJaxWsProxyFactoryBean factory = new QuarkusJaxWsProxyFactoryBean(quarkusClientFactoryBean, interfaces); + final Map props = new LinkedHashMap<>(); + factory.setProperties(props); factory.setServiceClass(seiClass); LOGGER.debugf("using servicename {%s}%s", cxfClientInfo.getWsNamespace(), cxfClientInfo.getWsName()); factory.setServiceName(new QName(cxfClientInfo.getWsNamespace(), cxfClientInfo.getWsName())); @@ -153,11 +159,25 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) { if (cxfClientInfo.getWsdlUrl() != null && !cxfClientInfo.getWsdlUrl().isEmpty()) { factory.setWsdlURL(cxfClientInfo.getWsdlUrl()); } - if (cxfClientInfo.getUsername() != null) { - factory.setUsername(cxfClientInfo.getUsername()); - } - if (cxfClientInfo.getPassword() != null) { - factory.setPassword(cxfClientInfo.getPassword()); + final String username = cxfClientInfo.getUsername(); + if (username != null) { + final String password = cxfClientInfo.getPassword(); + final AuthorizationPolicy authPolicy = new AuthorizationPolicy(); + authPolicy.setUserName(username); + if (password != null) { + authPolicy.setPassword(password); + } + if (cxfClientInfo.isSecureWsdlAccess()) { + /* + * This is the only way how the AuthorizationPolicy can be set early enough to be effective for the WSDL + * GET request. We do not do it by default because of backwards compatibility and for the user to think + * twice whether his WSDL URL uses HTTPS and only then enable secureWsdlAccess + */ + httpConduitConfigurer.addConfigurer(cxfClientInfo.getEndpointAddress(), + conduit -> conduit.setAuthorization(authPolicy)); + } else { + props.put(AuthorizationPolicy.class.getName(), authPolicy); + } } final String clientString = "client" + (cxfClientInfo.getConfigKey() != null ? (" " + cxfClientInfo.getConfigKey()) : ""); @@ -171,8 +191,6 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) { factory.getOutFaultInterceptors()); CXFRuntimeUtils.addBeans(cxfClientInfo.getInFaultInterceptors(), "inFaultInterceptor", clientString, sei, factory.getInFaultInterceptors()); - final Map props = new LinkedHashMap<>(); - factory.setProperties(props); final HTTPConduitImpl httpConduitImpl = cxfClientInfo.getHttpConduitImpl(); if (httpConduitImpl != null) { diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHttpConduitConfigurer.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHttpConduitConfigurer.java new file mode 100644 index 000000000..3a14d4938 --- /dev/null +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHttpConduitConfigurer.java @@ -0,0 +1,52 @@ +package io.quarkiverse.cxf; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; + +import org.apache.cxf.BusFactory; +import org.apache.cxf.transport.http.HTTPConduit; +import org.apache.cxf.transport.http.HTTPConduitConfigurer; + +/** + * A {@link HTTPConduitConfigurer} able to configure conduits by address. + */ +@ApplicationScoped +public class QuarkusHttpConduitConfigurer implements HTTPConduitConfigurer { + private final Map>> configurersByAddress = new ConcurrentHashMap<>(); + + @PostConstruct + void init() { + BusFactory.getDefaultBus().setExtension(this, HTTPConduitConfigurer.class); + } + + @Override + public void configure(String name, String address, HTTPConduit conduit) { + final List> configurers = configurersByAddress.get(address); + if (configurers != null) { + configurers.forEach(configurer -> configurer.accept(conduit)); + } + } + + /** + * Add a {@code configurer} that will be applied only to the conduit associated with the given {@code address}. + * + * @param address the endpoint address for which the give {@code configurer} should be registered + * @param configurer the {@code configurer} to apply to the conduit associated with the given {@code address}. + */ + public void addConfigurer(String address, Consumer configurer) { + configurersByAddress.compute(address, (k, v) -> { + if (v == null) { + v = new ArrayList<>(); + } + v.add(configurer); + return v; + }); + } + +} diff --git a/integration-tests/client-server/pom.xml b/integration-tests/client-server/pom.xml index 16e3e5ed2..ac624481e 100644 --- a/integration-tests/client-server/pom.xml +++ b/integration-tests/client-server/pom.xml @@ -22,6 +22,10 @@ io.quarkus quarkus-resteasy + + io.quarkus + quarkus-elytron-security-properties-file + io.rest-assured diff --git a/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/HelloService.java b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/HelloService.java new file mode 100644 index 000000000..1fb4203fe --- /dev/null +++ b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/HelloService.java @@ -0,0 +1,12 @@ +package io.quarkiverse.cxf.it; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebService; + +@WebService(serviceName = "HelloService", targetNamespace = HelloService.NS) +public interface HelloService { + public static final String NS = "https://quarkiverse.github.io/quarkiverse-docs/quarkus-cxf/test"; + + @WebMethod + public String hello(String person); +} diff --git a/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/BasicAuthHelloServiceImpl.java b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/BasicAuthHelloServiceImpl.java new file mode 100644 index 000000000..c5ac92777 --- /dev/null +++ b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/BasicAuthHelloServiceImpl.java @@ -0,0 +1,17 @@ +package io.quarkiverse.cxf.it.auth.basic; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.jws.WebService; + +import io.quarkiverse.cxf.it.HelloService; + +@WebService(serviceName = "HelloService", targetNamespace = HelloService.NS) +@RolesAllowed("app-user") +public class BasicAuthHelloServiceImpl implements HelloService { + + @Override + public String hello(String person) { + return "Hello " + person + "!"; + } + +} diff --git a/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/BasicAuthResource.java b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/BasicAuthResource.java new file mode 100644 index 000000000..706db9194 --- /dev/null +++ b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/BasicAuthResource.java @@ -0,0 +1,44 @@ +package io.quarkiverse.cxf.it.auth.basic; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import io.quarkiverse.cxf.annotation.CXFClient; +import io.quarkiverse.cxf.it.HelloService; + +@Path("/client-server/basic-auth") +public class BasicAuthResource { + + @CXFClient("basicAuth") + HelloService basicAuth; + + @CXFClient("basicAuthSecureWsdl") + HelloService basicAuthSecureWsdl; + + @POST + @Path("/{client}/hello") + @Produces(MediaType.TEXT_PLAIN) + public Response hello(String body, @PathParam("client") String client) { + final HelloService helloService = switch (client) { + case "basicAuth": { + yield basicAuth; + } + case "basicAuthSecureWsdl": { + yield basicAuthSecureWsdl; + } + default: + throw new IllegalArgumentException("Unexpected client: " + client); + }; + + try { + return Response.ok(helloService.hello(body)).build(); + } catch (Exception e) { + return Response.serverError().entity(e.getMessage()).build(); + } + } + +} diff --git a/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/WsdlBasicAuthInterceptor.java b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/WsdlBasicAuthInterceptor.java new file mode 100644 index 000000000..4d295ed6d --- /dev/null +++ b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/auth/basic/WsdlBasicAuthInterceptor.java @@ -0,0 +1,51 @@ +package io.quarkiverse.cxf.it.auth.basic; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Named; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.apache.cxf.binding.soap.SoapMessage; +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.phase.AbstractPhaseInterceptor; +import org.apache.cxf.phase.Phase; +import org.apache.cxf.transport.http.AbstractHTTPDestination; + +@ApplicationScoped +@Named("wsdlBasicAuthInterceptor") +public class WsdlBasicAuthInterceptor extends AbstractPhaseInterceptor { + + public WsdlBasicAuthInterceptor() { + super(Phase.RECEIVE); + } + + @Override + public void handleMessage(SoapMessage message) throws Fault { + + final HttpServletRequest req = (HttpServletRequest) message.getExchange().getInMessage() + .get(AbstractHTTPDestination.HTTP_REQUEST); + if ("GET".equals(req.getMethod())) { + /* WSDL is the only thing served through GET */ + try { + auth(); + } catch (io.quarkus.security.UnauthorizedException e) { + handle40x(message, HttpServletResponse.SC_UNAUTHORIZED); + } catch (io.quarkus.security.ForbiddenException e) { + handle40x(message, HttpServletResponse.SC_FORBIDDEN); + } + } + } + + private void handle40x(SoapMessage message, int code) { + HttpServletResponse response = (HttpServletResponse) message.getExchange().getInMessage() + .get(AbstractHTTPDestination.HTTP_RESPONSE); + response.setStatus(code); + message.getInterceptorChain().abort(); + } + + @RolesAllowed("app-user") + void auth() { + + } +} diff --git a/integration-tests/client-server/src/main/resources/application.properties b/integration-tests/client-server/src/main/resources/application.properties index b74f96fed..50f7ec8e5 100644 --- a/integration-tests/client-server/src/main/resources/application.properties +++ b/integration-tests/client-server/src/main/resources/application.properties @@ -1,12 +1,24 @@ +client-server.bob.password = bob234 + +# Basic auth global configuration +quarkus.http.auth.basic = true +quarkus.security.users.embedded.enabled = true +quarkus.security.users.embedded.plain-text = true +quarkus.security.users.embedded.users.alice = alice123 +quarkus.security.users.embedded.roles.alice = admin +quarkus.security.users.embedded.users.bob = ${client-server.bob.password} +quarkus.security.users.embedded.roles.bob = app-user + +quarkus.native.resources.includes = wsdl/* + +# Global Quarkus CXF configuration quarkus.cxf.path = /soap +# Addressing tests quarkus.cxf.endpoint."/addressing-anonymous".implementor = io.quarkiverse.cxf.it.ws.addressing.server.anonymous.AddressingAnonymousImpl - quarkus.cxf.endpoint."/addressing-decoupled".implementor = io.quarkiverse.cxf.it.ws.addressing.server.decoupled.WsAddressingImpl -quarkus.native.resources.includes = wsdl/* - -# XMl Schema validation +# XML Schema validation quarkus.cxf.codegen.wsdl2java.includes = wsdl/*.wsdl quarkus.cxf.codegen.wsdl2java.package-names = io.quarkiverse.cxf.it.server.xml.schema.validation.model quarkus.cxf.codegen.wsdl2java.wsdl-location = classpath:wsdl/calculator.wsdl @@ -29,8 +41,29 @@ quarkus.cxf.endpoint."/unvalidated-calculator".implementor = io.quarkiverse.cxf. quarkus.cxf.endpoint."/unvalidated-calculator".wsdl = wsdl/calculator.wsdl quarkus.cxf.endpoint."/unvalidated-calculator".logging.enabled = true -# Client +# XML Schema validation Client quarkus.cxf.client.application-properties-schema-validated-calculator.wsdl = http://localhost:${quarkus.http.test-port}/soap/unvalidated-calculator?wsdl quarkus.cxf.client.application-properties-schema-validated-calculator.client-endpoint-url = http://localhost:${quarkus.http.test-port}/soap/unvalidated-calculator quarkus.cxf.client.application-properties-schema-validated-calculator.service-interface = io.quarkiverse.cxf.it.server.xml.schema.validation.ApplicationPropertiesSchemaValidatedCalculatorService quarkus.cxf.client.application-properties-schema-validated-calculator.schema-validation.enabled-for = both + + +# WSDL not secured by basic auth +quarkus.cxf.endpoint."/basicAuth".implementor = io.quarkiverse.cxf.it.auth.basic.BasicAuthHelloServiceImpl +quarkus.cxf.endpoint."/basicAuth".logging.enabled = true +# Client +quarkus.cxf.client.basicAuth.wsdl = http://localhost:${quarkus.http.test-port}/soap/basicAuth?wsdl +quarkus.cxf.client.basicAuth.client-endpoint-url = http://localhost:${quarkus.http.test-port}/soap/basicAuth +quarkus.cxf.client.basicAuth.username = bob +quarkus.cxf.client.basicAuth.password = ${client-server.bob.password} + +# WSDL secured by basic auth +quarkus.cxf.endpoint."/basicAuthSecureWsdl".implementor = io.quarkiverse.cxf.it.auth.basic.BasicAuthHelloServiceImpl +quarkus.cxf.endpoint."/basicAuthSecureWsdl".in-interceptors = #wsdlBasicAuthInterceptor +quarkus.cxf.endpoint."/basicAuthSecureWsdl".logging.enabled = true +# Client +quarkus.cxf.client.basicAuthSecureWsdl.wsdl = http://localhost:${quarkus.http.test-port}/soap/basicAuth?wsdl +quarkus.cxf.client.basicAuthSecureWsdl.client-endpoint-url = http://localhost:${quarkus.http.test-port}/soap/basicAuthSecureWsdl +quarkus.cxf.client.basicAuthSecureWsdl.username = bob +quarkus.cxf.client.basicAuthSecureWsdl.password = ${client-server.bob.password} +quarkus.cxf.client.basicAuthSecureWsdl.secure-wsdl-access = true diff --git a/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/auth/basic/wsdl/BasicAuthIT.java b/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/auth/basic/wsdl/BasicAuthIT.java new file mode 100644 index 000000000..148926a7d --- /dev/null +++ b/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/auth/basic/wsdl/BasicAuthIT.java @@ -0,0 +1,8 @@ +package io.quarkiverse.cxf.it.auth.basic.wsdl; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class BasicAuthIT extends BasicAuthTest { + +} \ No newline at end of file diff --git a/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/auth/basic/wsdl/BasicAuthTest.java b/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/auth/basic/wsdl/BasicAuthTest.java new file mode 100644 index 000000000..8ac942318 --- /dev/null +++ b/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/auth/basic/wsdl/BasicAuthTest.java @@ -0,0 +1,68 @@ +package io.quarkiverse.cxf.it.auth.basic.wsdl; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +public class BasicAuthTest { + + @Test + public void getSecureWsdlAnonymous() { + RestAssured.given() + .get("/soap/basicAuthSecureWsdl?wsdl") + .then() + .statusCode(401); + } + + @Test + public void getSecureWsdlBasicGoodUserPreemptive() { + RestAssured + .given() + .auth().preemptive().basic("bob", "bob234") + .get("/soap/basicAuthSecureWsdl?wsdl") + .then() + .statusCode(200) + .body(Matchers.containsString("http://schemas.xmlsoap.org/wsdl/")); + } + + @Test + public void getSecureWsdlBasicBadUser() { + /* The user exists, can authenticate, but does not have the right role */ + RestAssured + .given() + .auth().preemptive().basic("alice", "alice123") + .get("/soap/basicAuthSecureWsdl?wsdl") + .then() + .statusCode(403); + } + + @Test + public void basicAuthSecureWsdlGoodUser() { + /* WSDL secured by basic auth */ + RestAssured + .given() + .body("Mary") + .post("/client-server/basic-auth/basicAuthSecureWsdl/hello") + .then() + .statusCode(200) + .body(Matchers.containsString("Hello Mary!")); + + } + + @Test + public void basicAuthGoodUser() { + /* WSDL not secured by basic auth */ + RestAssured + .given() + .body("Mary") + .post("/client-server/basic-auth/basicAuth/hello") + .then() + .statusCode(200) + .body(Matchers.containsString("Hello Mary!")); + + } + +}