Saturday, July 14, 2012

WS-Security mustUnderstand in webmethods

WS-Security mustUnderstand

Environment: webmethods 8.0.1

Problem

A client has asked us to enable ws-security when using a particular webservice he exposes (webmethods policy "Consumer policy for Username"). However when we call his webservice with the appropriate message level authentication, we receive the following soap fault:

Unprocessed 'mustUnderstand' header element: {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security

Analyzing the problem

Using wireshark/fiddler/.. you can inspect the soap that webmethods sends along and you see this:

...
<wsse:Security xmlns:wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:mustUnderstand="1"
      xmlns:wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
      <wsse:UsernameToken wsu:Id = "UsernameToken-123456">
            <wsse:Username>user</wsse:Username>
            <wsse:Password Type = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">pass</wsse:Password>
      </wsse:UsernameToken>
</wsse:Security>
...

As you can see, the "mustUnderstand" header is indeed set. Transmitting the entire soap sans mustUnderstand via soapUI works. A quick google showed us that we are not the only ones experiencing this but no solutions were forthcoming. A succinct explanation of mustUnderstand can be found here.

The most likely cause of the issue is a bug in the client webservice implementation where he does interpret the security header but does not clear the mustUnderstand attribute which would result in the error we are seeing.

Solution

The simplest solution is of course for us not to send along the attribute but unfortunately we found no reference in the documentation regarding this particular functionality. Instead we opted to write a custom soap handler which strips the attribute when found. The webservice functionality has been subjected to a large overhaul in 8.0.1 so instead of extending javax.xml.rpc.handler.GenericHandler to add some functionality, you can work with actual services. This makes it much easier to test changes on the fly.

We create a handler service that implements the spec pub.soap.handler:handlerSpec:

The java service DgCommon.admin.utils:removeMustUnderstand uses the public org.w3c.dom api to strip the mustUnderstand:

IDataCursor cursor = pipeline.getCursor();
org.w3c.dom.Element element = (org.w3c.dom.Element) IDataUtil.get(cursor, "header");
element.removeAttribute("mustUnderstand");
IDataUtil.put(cursor, "header", element);
cursor.destroy();

This will however not work and give you a rather vague error:

com.wm.dom.DOMExceptionImpl: [ISC.133.8] Bundle:com.wm.dom.resources.DOMMessageBundle Key:133.8
      at com.wm.lang.xml.ElementNode.removeAttribute(ElementNode.java:606)
...

The exception is not entirely according to the spec so I assume this is an error in the webmethods implementation. Anyway, removing it with a fully qualified namespace does work:

element.removeAttributeNS("http://schemas.xmlsoap.org/soap/envelope/", "mustUnderstand");

You can register the handler using pub.soap.handler:registerWmConsumer (note that this must be re-registered on reboot, so do it in a startup service).

The last step is to actually add it to your list of handlers:

Author: Alexander Verbruggen

2 comments:

  1. Did I do this correctly? I am getting the Bundle:com.wm.dom.resources.DOMMessageBundle Key:133.8 error.

    IDataCursor cursor = pipeline.getCursor();
    org.w3c.dom.Element element = (org.w3c.dom.Element) IDataUtil.get(cursor, "header");
    element.removeAttributeNS("http://schemas.xmlsoap.org/soap/envelope/", "mustUnderstand");
    IDataUtil.put(cursor, "header", element);
    cursor.destroy();

    ReplyDelete
  2. The code looks good. For simplicity's sake, the code does not take into account additional headers that do not have a mustUnderstand, it is possible that if you try to call removeAttributeNS() on an element that does not have the attribute to begin with, it could result in an exception.

    I have not tested this and it is against the specifications so I can't be sure. You could check if the header has the required attribute before attempting to remove it.

    ReplyDelete