/** * 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.cxf.systest.ws.security; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.ws.Binding; import javax.xml.ws.BindingProvider; import javax.xml.ws.Dispatch; import javax.xml.ws.Service; import javax.xml.ws.handler.Handler; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.http.HTTPBinding; import javax.xml.ws.soap.AddressingFeature; import javax.xml.ws.soap.SOAPBinding; import javax.xml.ws.soap.SOAPFaultException; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; import org.apache.cxf.bus.spring.SpringBusFactory; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.jaxws.DispatchImpl; import org.apache.cxf.systest.ws.common.SecurityTestUtil; import org.apache.cxf.systest.ws.common.TestParam; import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor; import org.apache.cxf.ws.security.wss4j.WSS4JStaxOutInterceptor; import org.apache.hello_world_soap_http.Greeter; import org.apache.wss4j.common.ext.WSSecurityException; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized.Parameters; /** * */ @RunWith(value = org.junit.runners.Parameterized.class) public class WSSecurityClientTest extends AbstractBusClientServerTestBase { public static final String PORT = allocatePort(Server.class); public static final String STAX_PORT = allocatePort(StaxServer.class); public static final String DEC_PORT = allocatePort(WSSecurityClientTest.class); private static final java.net.URL WSDL_LOC; static { java.net.URL tmp = null; try { tmp = WSSecurityClientTest.class.getClassLoader().getResource( "org/apache/cxf/systest/ws/security/hello_world.wsdl" ); } catch (final Exception e) { e.printStackTrace(); } WSDL_LOC = tmp; } private static final QName GREETER_SERVICE_QNAME = new QName( "http://apache.org/hello_world_soap_http", "GreeterService" ); private static final QName TIMESTAMP_SIGN_ENCRYPT_PORT_QNAME = new QName( "http://apache.org/hello_world_soap_http", "TimestampSignEncryptPort" ); private static final QName USERNAME_TOKEN_PORT_QNAME = new QName( "http://apache.org/hello_world_soap_http", "UsernameTokenPort" ); final TestParam test; public WSSecurityClientTest(TestParam type) { this.test = type; } @BeforeClass public static void startServers() throws Exception { assertTrue( "Server failed to launch", // run the server in the same process // set this to false to fork launchServer(Server.class, true) ); assertTrue( "Server failed to launch", // run the server in the same process // set this to false to fork launchServer(StaxServer.class, true) ); createStaticBus(); } @Parameters(name = "{0}") public static Collection<TestParam[]> data() { return Arrays.asList(new TestParam[][] {{new TestParam(PORT, false)}, {new TestParam(STAX_PORT, true)}, }); } @org.junit.AfterClass public static void cleanup() throws Exception { SecurityTestUtil.cleanup(); stopAllServers(); } @Test public void testUsernameToken() throws Exception { final javax.xml.ws.Service svc = javax.xml.ws.Service.create(WSDL_LOC, GREETER_SERVICE_QNAME); final Greeter greeter = svc.getPort(USERNAME_TOKEN_PORT_QNAME, Greeter.class); updateAddressPort(greeter, test.getPort()); Client client = ClientProxy.getClient(greeter); Map<String, Object> props = new HashMap<>(); props.put("action", "UsernameToken"); props.put("user", "alice"); props.put("passwordType", "PasswordText"); WSS4JOutInterceptor wss4jOut = new WSS4JOutInterceptor(props); client.getOutInterceptors().add(wss4jOut); ((BindingProvider)greeter).getRequestContext().put("password", "password"); try { greeter.greetMe("CXF"); fail("should fail because of password text instead of digest"); } catch (Exception ex) { //expected } props.put("passwordType", "PasswordDigest"); String s = greeter.greetMe("CXF"); assertEquals("Hello CXF", s); try { ((BindingProvider)greeter).getRequestContext().put("password", "foo"); greeter.greetMe("CXF"); fail("should fail"); } catch (Exception ex) { //expected } try { props.put("passwordType", "PasswordText"); ((BindingProvider)greeter).getRequestContext().put("password", "password"); greeter.greetMe("CXF"); fail("should fail"); } catch (Exception ex) { //expected } ((java.io.Closeable)greeter).close(); } @Test public void testUsernameTokenStreaming() throws Exception { final javax.xml.ws.Service svc = javax.xml.ws.Service.create(WSDL_LOC, GREETER_SERVICE_QNAME); final Greeter greeter = svc.getPort(USERNAME_TOKEN_PORT_QNAME, Greeter.class); updateAddressPort(greeter, test.getPort()); Client client = ClientProxy.getClient(greeter); Map<String, Object> props = new HashMap<>(); props.put("action", "UsernameToken"); props.put("user", "alice"); props.put("passwordType", "PasswordText"); WSS4JStaxOutInterceptor wss4jOut = new WSS4JStaxOutInterceptor(props); client.getOutInterceptors().add(wss4jOut); ((BindingProvider)greeter).getRequestContext().put("password", "password"); try { greeter.greetMe("CXF"); fail("should fail because of password text instead of digest"); } catch (Exception ex) { //expected } client.getOutInterceptors().remove(wss4jOut); props.put("passwordType", "PasswordDigest"); wss4jOut = new WSS4JStaxOutInterceptor(props); client.getOutInterceptors().add(wss4jOut); String s = greeter.greetMe("CXF"); assertEquals("Hello CXF", s); client.getOutInterceptors().remove(wss4jOut); try { ((BindingProvider)greeter).getRequestContext().put("password", "foo"); wss4jOut = new WSS4JStaxOutInterceptor(props); client.getOutInterceptors().add(wss4jOut); greeter.greetMe("CXF"); fail("should fail"); } catch (Exception ex) { //expected } client.getOutInterceptors().remove(wss4jOut); try { props.put("passwordType", "PasswordText"); wss4jOut = new WSS4JStaxOutInterceptor(props); client.getOutInterceptors().add(wss4jOut); ((BindingProvider)greeter).getRequestContext().put("password", "password"); greeter.greetMe("CXF"); fail("should fail"); } catch (Exception ex) { //expected } client.getOutInterceptors().remove(wss4jOut); ((java.io.Closeable)greeter).close(); } @Test public void testTimestampSignEncrypt() throws Exception { Bus b = new SpringBusFactory() .createBus("org/apache/cxf/systest/ws/security/client.xml"); BusFactory.setDefaultBus(b); final javax.xml.ws.Service svc = javax.xml.ws.Service.create( WSDL_LOC, GREETER_SERVICE_QNAME ); final Greeter greeter = svc.getPort( TIMESTAMP_SIGN_ENCRYPT_PORT_QNAME, Greeter.class ); updateAddressPort(greeter, test.getPort()); // Add a No-Op JAX-WS SoapHandler to the dispatch chain to // verify that the SoapHandlerInterceptor can peacefully co-exist // with the explicitly configured SAAJOutInterceptor // @SuppressWarnings("rawtypes") List<Handler> handlerChain = new ArrayList<>(); Binding binding = ((BindingProvider)greeter).getBinding(); TestOutHandler handler = new TestOutHandler(); handlerChain.add(handler); binding.setHandlerChain(handlerChain); greeter.sayHi(); assertTrue("expected Handler.handleMessage() to be called", handler.handleMessageCalledOutbound); assertFalse("expected Handler.handleFault() not to be called", handler.handleFaultCalledOutbound); ((java.io.Closeable)greeter).close(); b.shutdown(true); BusFactory.setDefaultBus(getStaticBus()); } @Test public void testMalformedSecurityHeaders() throws java.lang.Exception { Dispatch<Source> dispatcher = null; java.io.InputStream is = null; String result = null; // // Old Created Date should result in a Fault // dispatcher = createUsernameTokenDispatcher(test.getPort()); is = getClass().getResourceAsStream( "test-data/UsernameTokenRequest.xml" ); result = source2String(dispatcher.invoke(new StreamSource(is))); assertTrue(result.indexOf("Fault") != -1); // // Sending no security headers should result in a Fault // dispatcher = createUsernameTokenDispatcher(test.getPort()); is = getClass().getResourceAsStream( "test-data/NoHeadersRequest.xml" ); result = source2String(dispatcher.invoke(new StreamSource(is))); assertTrue(result.indexOf("Fault") != -1); // // Sending and empty header should result in a Fault // dispatcher = createUsernameTokenDispatcher(test.getPort()); is = getClass().getResourceAsStream( "test-data/EmptyHeaderRequest.xml" ); result = source2String(dispatcher.invoke(new StreamSource(is))); assertTrue(result.indexOf("Fault") != -1); // // Sending and empty security header should result in a Fault // dispatcher = createUsernameTokenDispatcher(test.getPort()); is = getClass().getResourceAsStream( "test-data/EmptySecurityHeaderRequest.xml" ); result = source2String(dispatcher.invoke(new StreamSource(is))); assertTrue(result.indexOf("Fault") != -1); } @Test public void testDecoupledFaultFromSecurity() throws Exception { Dispatch<Source> dispatcher = null; java.io.InputStream is = null; // // Sending no security headers should result in a Fault // dispatcher = createUsernameTokenDispatcher(true, test.getPort()); is = getClass().getResourceAsStream("test-data/NoHeadersRequest.xml"); try { dispatcher.invoke(new StreamSource(is)); fail("exception should have been generated"); } catch (SOAPFaultException ex) { assertTrue(ex.getMessage().equals(WSSecurityException.UNIFIED_SECURITY_ERR)); } // // Sending and empty header should result in a Fault // dispatcher = createUsernameTokenDispatcher(true, test.getPort()); is = getClass().getResourceAsStream("test-data/EmptyHeaderRequest.xml"); try { dispatcher.invoke(new StreamSource(is)); fail("exception should have been generated"); } catch (SOAPFaultException ex) { assertTrue(ex.getMessage().equals(WSSecurityException.UNIFIED_SECURITY_ERR)); } // // Sending and empty security header should result in a Fault // dispatcher = createUsernameTokenDispatcher(true, test.getPort()); is = getClass().getResourceAsStream("test-data/EmptySecurityHeaderRequest.xml"); try { dispatcher.invoke(new StreamSource(is)); fail("exception should have been generated"); } catch (SOAPFaultException ex) { assertTrue(ex.getMessage().equals(WSSecurityException.UNIFIED_SECURITY_ERR)); } } private static Dispatch<Source> createUsernameTokenDispatcher(String port) { return createUsernameTokenDispatcher(false, port); } private static Dispatch<Source> createUsernameTokenDispatcher(boolean decoupled, String port) { final Service service = Service.create( GREETER_SERVICE_QNAME ); service.addPort( USERNAME_TOKEN_PORT_QNAME, decoupled ? SOAPBinding.SOAP11HTTP_BINDING : HTTPBinding.HTTP_BINDING, "http://localhost:" + port + "/GreeterService/UsernameTokenPort" ); final Dispatch<Source> dispatcher = service.createDispatch( USERNAME_TOKEN_PORT_QNAME, Source.class, Service.Mode.MESSAGE, new AddressingFeature(decoupled, decoupled) ); final java.util.Map<String, Object> requestContext = dispatcher.getRequestContext(); requestContext.put( MessageContext.HTTP_REQUEST_METHOD, "POST" ); if (decoupled) { HTTPConduit cond = (HTTPConduit)((DispatchImpl<?>)dispatcher).getClient().getConduit(); cond.getClient().setDecoupledEndpoint("http://localhost:" + DEC_PORT + "/decoupled"); } return dispatcher; } private static String source2String(Source source) throws Exception { final java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream(); final StreamResult sr = new StreamResult(bos); TransformerFactory transformerFactory = TransformerFactory.newInstance(); transformerFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); Transformer transformer = transformerFactory.newTransformer(); final java.util.Properties oprops = new java.util.Properties(); oprops.put(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.setOutputProperties(oprops); transformer.transform(source, sr); return bos.toString(); } }