SOAP Faults using an Access Controlled Custom SOAP Processor

Greetings
According to the SOAP Developers guide, in order to set up a ‘Challenge/Response’ interaction (ie HTTP 401) with a calling client, webMethods requires an Access Controlled SOAP processor to be implemented with an appropriate ACL rule. Such a beastie should have 1 and only 1 line of code in it (otherwise the known world will explode by the sound of it). This line of code calls either the default doc lit SOAP processor or the rpc SOAP processor.

This is all very fine and we have managed to get this working for both doc literal and rpc encoded services … except when it comes to handling soap faults.

Here is what we are trying to do:

If a low level flow service has an error we want to throw an exception (eg via the Exit $flow signal Failure call with an appropriate Error Message set). This exception is passed back up the calling hierarchy till eventually it hits the SOAP processors. We do see a soap fault comming back, but the only thing we see is a ‘Cannot process soap body’ type soap fault with nothing meaningful (eg the error message set by the original service that threw the exception is nowhere to be seen. If we take out the access controlled SOAP processor and only use the default/rpc processor then we do get a meaningful SOAP fault. Therefore something is happening between the default/rpc SOAP processor and the Access Controlled SOAP processor.

Question 1: how can we create meaningful soap faults using an access controlled soap processor?

Question 2: why is an access controlled custom soap processor only allowed 1 line of code in it, ie to call either the default or rpc processors. Has anyone dared write any extra code in this type of processor (eg acting like any other custom soap processor and bypassing the default/rpc processors).

My soap box whine:
The way that webMethods handles soap/web service calls sucks majorly. It is really, really counter-intuitive and downright bodgy especially when it comes to access controlled soap processors. When are they going to fix this mess!

I understand how the Soap Developer’s Guide might give the impression that an access-controlled soap processor can only invoke one of the built-in soap processors, however, this is not the case. A custom soap processor is just a Flow or Java service that acts as an interceptor or pre-processor and can perform any set of actions prior to invoking the requested service.

There are several posts in this forum on various approaches to building custom soap processors especially around the best ways to convert the soap body to a document type that can be validated using pub.schema:validate before being passed to the requested service as input.

Given the above, your custom soap processor can catch any exceptions thrown by requested services, inspect the exceptions and throw customized soap faults as desired. Not only is this a good way to return more customer friendly soap fault data, but it is also a good security measure to hide service implementation details exposed by raw stack traces from would be hackers.

One way to create custom soap faults from an exception is to populate the Soap-fault document type convert it to string, then to node and add that as the body entry of the soap response. See Flow code generated by a web services connector for a soap-fault doc type that you can copy. The soap spec has details on what is permissable inside the fault actor, fault code and fault detail elements.

Use pub.flow:setResponse to ensure that the only data returned is the soap response containing your custom fault. Use pub.flow:setResponseCode to set the HTTP return code to 500.

Plans are well underway to “fix this mess”. However, I don’t think that will happen until Q1 2007 at the very earliest. Did I mention that IS web services support will have had no major changes in 4 years by that time?

HTH,

Mark

Mark

Rather than ‘giving an impression’ that at Access Controlled custom SOAP processor should only have 1 line of code in it the SOAP Developers guide categorically states this, and even has a big WARNING message to reinforce it. So it is hardly surprising that we are a tad confused.

Anyway … thanks for your reply.

Am I to believe that we can create our own Access Controlled Custom SOAP processor that does NOT need to call either of the default processors and in fact can do all of the requisite processing itself.
This would be a good thing.

What should I search for to find the info on how to set up such a thing?
(Obviously not the SOAP Developers Guide).

Ah, well, the docs are correct assuming that you want only the behavior of the default soap processor but with access-control enforced.

However, what you appear to need is not only access control enforcement, but also custom soap fault handling. In the strictest sense and “Access-Controlled Soap Processor” doesn’t do that. Picky, but true.

Yes, you can create your own custom soap processor (it will enforce access control by default), to perform not only the functions provided by the default soap processor (service lookup and invocation), but also any other logic you need to perform before or after your service is invoked such as XML validation, WS-Security enforcement or custom soap fault handling.

Chapter 6 of the SOAP Developer’s Guide does cover how to build a custom soap processor and you will find additional discussion in the Web Services forum here at wMUsers.

Mark

Mark

I am making some slooooow progress towards building an Access Controlled custom SOAP processor.
But I am having a hugely difficult time passing back a SOAP Fault.
I just cannot seem to get the SOAP-ENV:Fault construct to add to the SOAP body. It keeps saying Invalid node: must be well formed XML.

I’ve even resorted to hard coding the XML into the xmldata string as follows:
SOAP-ENV:Fault
Server
Server has thrown an error
zzCraig.tcms.pub:myService
</SOAP-ENV:Fault>

The xmlStringToXMLNode uses the above as input into xmldata and has isXML set to true.

The soapResponseData is the normal xml object generated by webMethods, ie

<?xml version="1.0" encoding="UTF-8"?>

<SOAP-ENV:Envelope xmlns:SOAP-ENV=“[URL=“http://schemas.xmlsoap.org/soap/envelope/”]http://schemas.xmlsoap.org/soap/envelope/[/URL]” xmlns:SOAP-ENC=“[URL=“http://schemas.xmlsoap.org/soap/encoding/”]http://schemas.xmlsoap.org/soap/encoding/[/URL]” xmlns:xsd=“[URL=“XML Schema”]http://www.w3.org/2001/XMLSchema[/URL]” xmlns:xsi=“[URL=“http://www.w3.org/2001/XMLSchema-instance”]http://www.w3.org/2001/XMLSchema-instance[/URL]”>
SOAP-ENV:Body
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The addBodyEntry combines the soapResponse and the node object from the xmlStringToXMLNode. But it just ain’t working.
Can you please give some hints.
PS: This is really starting to annoy big time!

By default and per the soap spec, items in the soap header and soap body must be namespace qualified. My guess is that your “SOAP-ENV:Fault” document contains a prefix but no namespace value.

To correct this, populate the nsdecls input parameter on your pub.xml:documentToXMLString call to associate the prefix “SOAP-ENV” with the namespace “[URL=“http://schemas.xmlsoap.org/soap/envelope/”]http://schemas.xmlsoap.org/soap/envelope/[/URL]”.

HTH,

Mark

Mark, I followed your suggestion, and hey presto, I am able to add the SOAP fault. However, it does mean that the resulting SOAP message looks ‘strange’ in that the SOAP-ENV:Fault tag is given an xmlns redundantly (as SOAP-ENV is already defined previously in the envelope section). The reason I didn’t try this was that the examples in the SOAP Developers guide shows the SOAP-ENV:Fault without a namespace so I figured it must be possible to do this.

And there is…

Looking in Appendix C of the SOAP Developers Guide, there is a SOAP related server paramater called watt.server.SOAP.enforceMsgPartNS.
If this is set to true (the default) then the server will throw an exception if the pub.soap.utils:addHeaderEntry or pub.soap.utils:addBodyEntry service attempts to insert a non-namespace qualified element into a SOAP message. If false, non-namespace qualified elements are permitted.

On another note:
Elsewhere you have indicated that the following approach should be adopted when processing a SOAP fault:

  1. Populate a SOAP-ENV:Fault document using the desired customvalues.
  2. Convert this document to an XML String using pub.xml:documentToString
  3. Convert the xmldata to an xmlnode using pub.xml:xmlStringToXMLNode
  4. Create an empty SOAP document using pub.soap.utils:createSoapData
  5. Add the node as a body entry using pub.soap.utils:addBodyEntry
  6. Map the result to the soapResponseData object
  7. Cleanup the pipeline so that only soapResponseData remains
  8. Use the pub.flow:setResponse to set the HTTP response to soapResponseData
  9. Use the pub.flow:setResponseCode to set the HTTP return code to “500”

The part I don’t quite get is the use of setResponse and setResponseCode. Should the last thing be to exit $flow with SUCCESS (or FAILURE?) or should it be to simply to let the flow service end normally. In the latter case, what do you do with the response and responseCode variables? Do these simply get dropped at the last pipeline cleanup step?

Mark

Remember what we are trying to do is to have a challenge-response (ie HTTP 401) scenario for our clients . The problem we are seeing is that if an Access Controlled custom soap processor (ACCSP) is used to invoke the default processor, the soap fault always comes back with a ‘cannot process soap body’ type error (ie the original error is lost). The alternative is to code up our own fully blown ACCSP and not use the default at all.

A few ideas out of left field…

What if the ACCSP were to invoke the default processor via a SOAP call, not as a flow service call. That way the ACCSP does the challenge-response and we get to use the default processor in its ‘standard mode’ (ie via the http port listener and message handler) which should hopefully pass back a valid soap fault.

Another idea is to simply change the ACL on the default processor from anonymous to something else to force an LDAP basic authorisation check.

Any comments please…

Well, specifying the namespace again is not necessary but is still perfectly valid XML. Setting the “enforceMessagePartNS” to false is a great solution as long as you consistently specify the namespace when it has not already been declared earlier in the message or envelope. Many folks don’t understand the need to namespace qualify soap message parts, hence the default behavior.

Your soap processor should exit normally. If you exit with failure, the default soap fault will be returned wasting all of your effort to customize it.

SetResponse will ensure that only your soapResponseData will be returned and setResponseCode will set the HTTP return code appropriately. You should drop all other pipeline variables, but by using setResponse, nothing else will be returned.

I’m not quite sure I follow your idea of invoking the default soap processor without using an invoke. I almost always find a need to pre-process the soap messages in order to perform tasks consistently across all soap-based invocations (logging soap requests and responses, performing ws-security checks, validating requests and responses against schemas, etc.). In my projects having a solid custom soap processor is a given.

HTH,

Mark

Our administrator set the watt.server.SOAP.enforceMsgPartNS parameter to false but with no effect. I still cannot get a SOAP-ENV:Fault element to be added to a soap body without also specifying an xmlns entry for it, as in
<SOAP-ENV:Fault xmlns:SOAPENV=“[URL=“http://schemas.xmlsoap.org/soap/envelope/”]http://schemas.xmlsoap.org/soap/envelope/[/URL]”) >

I should be able to create an element that looks like this:

SOAP-ENV:Fault

because the SOAP-ENV prefix has already been previously defined earlier in the soap message.

Ah well, I think I will give up on that one and just live with it even though it looks ‘ugly’. If you can show me how you have done this without having to specify the namespace again, I’d appreciate it.

On a different note:
We’ve decided to identify the target service using the nodeName and namespace from the extracted soap body to look up the universal name.
The namespace and nodeName will be constructed (by convention) such that the fully qualified document type name of the request document can be determined. With your renameDocument java service we can then get the correct document type name and pass this into another java service to perform a doInvoke to call the target service.

The question is, what happens inside such a java service if the called program throws and exception? Will the custom soap processor be able to call getLastError to get the relevant details to put into a soap fault?

On another note:
All of the doc/lit processing I’ve seen thus far relies on extracting key information from the request soap message to determine the service to call and the document type to use. But what happens if the service in question has no input parameters and therefore no request document?

We just add the namespace to the soap-fault and don’t worry about the “ugliness” since it is valid XML.

Rather than having services exposed as web services exit with error (throw exceptions), we populate a standard ResponseStatus structure that includes a returnCode indicating success or failure and an optional document list called ResponseMessages that contains an error code, a severity and a description of the error. The soap processor checks the ResponseStatus/returnCode to determine whether the expected response document should be present. If the service failed, it might not return the response document. You can optionally choose to throw soap faults on certain types of errors or to reformat the errors to hide implementation details or convert error messages to more customer-friendly values.

The soap request body should contain an empty request document. The soap processor would have to take this into account when creating an input document to pass to the requested service, so that it did not throw an exception when attempting to create the input doc.

Mark

Hi Mark

I have finally been able to get my custom soap processor to do the following:
- receive a soapRequestData object and extract from it the soap body
- from the soap body get the namespace and root node name (which by convention can be pieced together to make up the fully qualified name of the request document type).
- convert the soap body into the right document type and, using your java code, remove the namespace prefix from it
- use the namespace and root node name to get the fully qualified name of the target flow service using the universal name find routine
- use a java service to doInvoke the target flow service and get back the response document
- use a modified version of your java code to put a namespace prefix back into the response document
- convert the document into xml and add this into the body of the soapResponseData

Phew!

I couldn’t have done this without your help. Thanks very much … your efforts are greatly appreciated.

The main issue now is to get the java service that does the doInvoke to handle exceptions and pass back the error message set by the called service (or one of its children) when it performed the exit $flow process so that this can be incorporated into a soap fault. Any comments on how “best” to do this would be appreciated. One idea is to apply some sort of structure to the error message so that we can parse it and pick out individual pieces of information to put into the soap fault construct.

According to the documentation the call stack is not available when doInvoke is used. Do you know of any way around this?

Also, the default processor is able to work out how to set the faultcode in the soap fault (eg to SOAP-ENV:Client if a client type error has occurred or to SOAP-ENV:Server if a server error has occurred). Do you have any idea how it can work this out (so that we can do the same)?

Craig

Craig,

Glad you’re making progress!

We modify the services exposed as web services to return a standard structure containing a return code and an optional array of messages. The services return details about any errors which occured using this array of messages. You could modify this approach to return a call stack, I suppose, but generally, one would not want to expose this level of detail to a service consumer.

The soap spec details the types of soap faults that should be considered client or server faults. You can follow those guidelines to determine how to populate the faultcode.

Mark

I am encountering a similar problem. I’ve created a custom SOAP processor in order to produce custom SOAP fault messages, instead of the wemMethods stack trace faults.

My custom processor simply wraps: pub.soap.processor:processRPCMessage, using the soapRequestData and soapResponseData objects provided to my custom SOAP processor.

Wrapping the service runs fine, unless the service that is actually called when soap request is made throws an exception. For some reason pub.soap.processor:processRPCMessage create a webMethods soap fault saying that it could not process the body of the message, if the service it’s trying to call throws an exception.

This was the problem the original poster had, but he abandoned wrapping a call to the pub.soap.processor:processRPCMessage / processMessage because he didn’t really need to call them, I would like to, because I don’t really want to deal with unwrapping the incoming soap message and processing it into a call to my service that should be providing the processing for the request, it should be done for me. I just want to provide the extra functionality of intercepting the platform soap fault that is generated when an exception is thrown, and inject a custom fault message instead.

jwickard,

You didn’t mention what release of IS you are using, but fix IS_6-5_SP1_WebSvcsXML_Fix1 (now included in IS_6-5_SP1_WebSvcsXML_Fix2) may be related:

HTH,

Mark