10 Replies Latest reply on Feb 5, 2015 5:37 PM by kymsh2

    How to get ws-security working for a soap-reference-binding (client, consumer)

    Martin E. Newbie

      In my new SwitchYard-Application (Version: 1.1) i want to consume an extern soap-webservice with a reference and a soap-binding.

      But the remote soap-service is restricted for authenticated users only. The authentication is implemented by WS-Security (Username/Password are expected in the soap-headers)

      So the wsdl has the "wsp:PolicyReference"-tag and the corresponding "wsp:Policy"-tag, like described here: SOAP - SwitchYard - Project Documentation Editor

      But how to define username and password for WS-Secrutity?

      I tried a jaxws-config with "ws-security.username" and "ws-security.password" which i configured by "soap:endpointConfig" in the switchyard.xml.

      But "soap:endpointConfig" seems to be completely ignored on reference, because whatever i do getting only the following exception:

       

      [0m [33m17:38:31,911 WARNING [org.apache.cxf.phase.PhaseInterceptorChain] (http-/0.0.0.0:8080-7) Interceptor for {NameSpace}OperationName#{http://cxf.apache.org/jaxws/dispatch}Invoke has thrown exception, unwinding now: org.apache.cxf.ws.policy.PolicyException: No username available

        at org.apache.cxf.ws.security.wss4j.AbstractTokenInterceptor.policyNotAsserted(AbstractTokenInterceptor.java:229) [cxf-rt-ws-security-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.ws.security.wss4j.UsernameTokenInterceptor.addUsernameToken(UsernameTokenInterceptor.java:339) [cxf-rt-ws-security-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.ws.security.wss4j.UsernameTokenInterceptor.addToken(UsernameTokenInterceptor.java:285) [cxf-rt-ws-security-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.ws.security.wss4j.AbstractTokenInterceptor.handleMessage(AbstractTokenInterceptor.java:95) [cxf-rt-ws-security-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.ws.security.wss4j.AbstractTokenInterceptor.handleMessage(AbstractTokenInterceptor.java:61) [cxf-rt-ws-security-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:262) [cxf-api-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:530) [cxf-api-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:463) [cxf-api-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:366) [cxf-api-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:319) [cxf-api-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.endpoint.ClientImpl.invokeWrapped(ClientImpl.java:354) [cxf-api-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:385) [cxf-rt-frontend-jaxws-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:243) [cxf-rt-frontend-jaxws-2.6.8.redhat-7.jar:2.6.8.redhat-7]

        at org.switchyard.component.soap.OutboundHandler.invokeService(OutboundHandler.java:329) [switchyard-component-soap-1.1.0.Final.jar:1.1.0.Final]

        • 1. Re: How to get ws-security working for a soap-reference-binding (client, consumer)
          Martin E. Newbie

          I also tried "<soap:outInterceptors>" with class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor" but "<soap:outInterceptors>" seems to be ignored on SwitchYard-References (<sca:reference/>) exactly like "soap:endpointConfig" is ignored.

          • 2. Re: How to get ws-security working for a soap-reference-binding (client, consumer)
            Tadayoshi Sato Novice

            Hi,

             

            My understanding is it's not supported yet. You have to wait for SWITCHYARD-2049 being implemented in SY 2.0.

            • 3. Re: How to get ws-security working for a soap-reference-binding (client, consumer)
              Tomas Rohovsky Novice

              Hi Martin,

               

              I was facing to the similar issue some time ago. I just used a SAML assertion instead of credentials. My solution is to assemble security element by my own and then set it as a property, so it will be mapped as a message header.

               

                  @Inject

                  @Reference("OrderServiceExternal")

                  ReferenceInvoker invoker;

               

                  private ReferenceInvocation setSecurityHeader(ReferenceInvocation invocation) {

                      Element securityElement = null;

                      try {

                          securityElement = SAMLHelper.createSecurityElement(SAMLHelper.issueAssertionElement());

                      } catch (Exception e) {

                          e.printStackTrace();

                      }

               

                      // workaround

                      invocation

                              .getMessage()

                              .getContext()

                              .setProperty(

                                      "{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}security",

                                      securityElement);

               

                      return invocation;

                  }

               

                  @Override

                  public void processOneWay(SimpleOrder order) {

                      ReferenceInvocation invocation = invoker.newInvocation("processOneWay");

                      setSecurityHeader(invocation);

                      try {

                          invocation.invoke(order);

                      } catch (Exception e) {

                          e.printStackTrace();

                      }

                  }

               

              public class SAMLHelper {

               

                  /**

                   * Issues the assertion.

                   *

                   * @return

                   * @throws Exception

                   */

                  public static Element issueAssertionElement() throws Exception {

                      WSTrustClient client = new WSTrustClient("PicketLinkSTS",

                              "PicketLinkSTSPort",

                              "http://localhost:8080/picketlink-sts/PicketLinkSTS",

                              new SecurityInfo("admin", "admin"));

               

                      // issuing of the assertion

                      return client.issueToken(SAMLUtil.SAML2_TOKEN_TYPE);

                  }

               

                  /**

                   * Creates the security element. The input is the assertion that will be encapsulated in security element.

                   *

                   * @param assertion

                   * @return

                   * @throws Exception

                   */

                  public static Element createSecurityElement(Element assertion)

                          throws Exception {

                      DocumentBuilderFactory docFactory = DocumentBuilderFactory

                              .newInstance();

                      DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

               

                      // creating of the security element

                      Document newDocument = docBuilder.newDocument();

                      Element securityElement = newDocument

                              .createElementNS(

                              "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security");

                      Element newAssertion = (Element) newDocument.importNode(assertion, true);

                      securityElement.appendChild(newAssertion);

                      return securityElement;

                  }

               

              }

              1 of 1 people found this helpful
              • 5. Re: How to get ws-security working for a soap-reference-binding (client, consumer)
                Martin E. Newbie

                Thank you for your example.

                Finally i found a similarly workaround by using my own custom SOAPMessageComposer. Witch adds the security-header in the decompose-function by using the WSS4JOutInterceptor manually.

                If somebody is interested, i will post more details.

                • 6. Re: How to get ws-security working for a soap-reference-binding (client, consumer)
                  Miguel Buescher Newbie

                  Hello Martin.
                  I would be interessested in your workaround for this.

                  Since we can't wait for Switchyard V2.0 and need a solution ASAP.


                  Thank you!

                  • 7. Re: Re: How to get ws-security working for a soap-reference-binding (client, consumer)
                    Martin E. Newbie

                    Ok here is my custom SoapWssMessageComposer-Class:


                    public class SoapWssMessageComposer extends SOAPMessageComposer {
                      private static final String PASSWORD = "password";
                      private static final String USERNAME = "username";
                    
                    
                      /**
                      * Adds Wsse-Headers to the outgoing SOAPMessage.<p/>
                      * {@inheritDoc}
                      */
                      @Override
                      public SOAPBindingData decompose(Exchange exchange, SOAPBindingData target) throws Exception {
                      SOAPBindingData soapBindingData = super.decompose(exchange, target);
                      SOAPMessage esbSoapMessage = soapBindingData.getSOAPMessage();
                    
                    
                      addWssHeaderToSoapMessage(esbSoapMessage);
                      return soapBindingData;
                      }
                    
                    
                    
                    
                      private void addWssHeaderToSoapMessage(SOAPMessage esbSoapMessage) {
                      WSS4JOutInterceptor wss4JOutHandler = new WSS4JOutInterceptor();
                      PhaseInterceptor<SoapMessage> handler = wss4JOutHandler.createEndingInterceptor();
                    
                    
                      SoapMessage wss4jSoapMessage = new SoapMessage(new MessageImpl());
                      wss4jSoapMessage.setContent(SOAPMessage.class, esbSoapMessage);
                    
                    
                      // add wsse:UsernameToken
                      wss4jSoapMessage.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
                      wss4jSoapMessage.put(WSHandlerConstants.USER, USERNAME);
                      wss4jSoapMessage.put("password", PASSWORD);
                      wss4jSoapMessage.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
                      // add 'wsse:Nonce' and 'wsu:Created'
                      wss4jSoapMessage.put(WSHandlerConstants.ADD_UT_ELEMENTS, "Nonce Created");
                    
                    
                      // add wsse-headers to the soap-message
                      handler.handleMessage(wss4jSoapMessage);
                      }
                    }
                    
                    
                    • 8. Re: How to get ws-security working for a soap-reference-binding (client, consumer)
                      kymsh2 Newbie

                      Hi Martin,

                       

                      I am getting the exact same problem with exact same scenario. I was also using custom SOAPMessageComposer to add the security Headers in the message. However I was still getting the same exception. I tried your code in my MessageComposer  but no difference. Got the same exception ### org.apache.cxf.ws.policy.PolicyException: No username available #####

                      The only way to avoid this exception is to remove the <wsp:PolicyReference URI="#SecurityServicePolicy"/> entry from the WSDL of Reference Service .. But that is not a solution as we have to use the same WSDL as the Service Implementation. Are you using this entry in your Referece Service WSDL ? Also is it possible that you post the switchyard.xml content so that I can compare and see what is the difference ?

                       

                      Will appreciate your help.

                      Thanks.

                      • 9. Re: How to get ws-security working for a soap-reference-binding (client, consumer)
                        Martin E. Newbie

                        No we have none <wsp:PolicyReference/>-tag in our WSDL anymore. It's not required because the custome-SoapWssMessageComposer should always add WS-Secrutity, regardless of whether there is <wsp:PolicyReference/>-tag in the WSDL.

                         

                        This is the relevant part of our switchyard.xml:

                        [...]

                        <sca:reference name="RemoteBaseBinding" multiplicity="0..n" promote="CamelComponent/BaseBinding">
                        <sca:interface.wsdl interface="base_wsdls/base.wsdl#wsdl.porttype(BASE)"/>
                        <soap:binding.soap name="Base">
                          <soap:messageComposer class="org.switchyard.message_composer.SoapWssMessageComposer" />
                          <soap:wsdl>wsdls/base.wsdl</soap:wsdl>
                          <soap:wsdlPort>BASEPort</soap:wsdlPort>
                          <soap:endpointAddress>http//HOST:PORT/base</soap:endpointAddress>
                        </soap:binding.soap>
                        </sca:reference>

                        [...]


                        Are you sure there are security Headers in the request-message?

                        You could see whats going on if you add the jboss-system-property: "org.apache.cxf.logging.enabled" with the value "true" (Maybe you have also to enable logging for org.apache.cxf.services=FINE)

                        Then search the jboss server.log for something like "org.apache.cxf.services", you should find the complete soap requests and responses.

                        • 10. Re: How to get ws-security working for a soap-reference-binding (client, consumer)
                          kymsh2 Newbie

                          Thanks Martin for your quick reply.

                           

                          I guess than this is the difference in WSDL. You dont have this tag <!--  wsp:PolicyReference URI="#SecurityServicePolicy"/ --> in your WSDL and as I said that if I commented out this tag from WSDL used by Reference Service , it works fine and send the message with correct Security Headers in the request added by my custom MessageComposer. And yes I have Enabled Message Trance enabled from the Domain tab of switchyard.xml. We also have CXF logging Interceptors setup so I have see all the request going in and out along with complete Switchyard Message logging.  There I can see the request I generate has correct Security Headers and it even goes and works perfectly if I just commented out that tag from the WSDL.

                          However, we are using common WSDL from common location for both Service Implementation as well as Reference Service.  Since this Service will also be used by external users we need that tags in the WSDL to enforce the Security for requests coming from web service users. And I dont think its good idea to have two different WSDL for the same web service. Red Hat support is not of much help as usual and they are just suggesting the work arounds which I already had figured.

                           

                          Anyway thanks again for your help I am gonna use the standalone soap client rather than Switchyard Reference Service. But than not sure what is the value of having a so called ESB in place. Very disappointing.