How do I sign an MTOM SOAP message using SOAPHandlers? - java

Currently, I have two handlers, one for logging and one for signing the SOAP message (which inherently tampers with the SOAP message). Without the handler chain, MTOM works as expected, inserting a reference to the binary content, rather than inlining the base64 binary content.
As soon as I introduce a handler, the MTOM content is now included inline.
Is it possible to use handlers to sign a SOAP message or is there a more appropriate means of doing this?
Update 1
Unable to post the full source. Essentially though, custom SOAPHandler implementation. It performs some basic XMLDsig type operations on a timestamp (in header), custom header and SOAP body. The resultant digest values are then injected into a signature element in the header.
With respect to the logger, it is again a simple SOAPHandler. If either it or the signing handler are used exclusively, the result is the same, an MTOM message with the byte content inlined. The only progress I made was using a MessageHandler for logging. This allowed me to output the SOAP envelope (albeit with the byte content inlined) and still maintain the MTOM separation. So not really a solution but an indication that any modification of the SOAP message needs to occur at a lower level. This is leading me down the path of tubes.
Update 2
The following is an example of the MessageHandler approach. You can see that the raw HTTP dump will contain the multiple part message whereas the actually output inlines the base64. The only difference between this impementation and a SOAPHandler implementation is that the actual HTTP request changes to be a single part inlined MTOM message.
#Override
public boolean handleMessage(MessageHandlerContext context) {
HttpTransportPipe.dump = true;
Boolean isOutgoing = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (isOutgoing) {
System.out.println("\nOutbound message:");
XMLStreamWriter writer = XMLStreamWriterFactory.create(System.out);
try {
context.getMessage().writeTo(writer);
} catch (XMLStreamException e) {
throw new IllegalStateException("Unable to write");
}
} else {
System.out.println("\nInbound message:");
}
return true;
}

I tried to replicate your problem by putting together a simple service that accepts an image transferred by MTOM. I found that if I put the MTOM-enabling code before setting the handler, it encodes the message properly. If I set the handler first, it does not. Here is where I set up the properly functioning client code:
Service service = Service.create(url, qname);
Greeting greeting = service.getPort(Greeting.class);
BindingProvider bp = (BindingProvider) greeting;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);
service.setHandlerResolver(new HandlerResolver() {
#SuppressWarnings("rawtypes")
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new RGBSOAPHandler());
return handlerList;
}
});
Where RGBSOAPHandler is just some example code I took from another SO answer.
EDIT: Also, if I try setting the handler on the binding and not the service then I get the same problem that you do. So if it looks like this:
Service service = Service.create(url, qname);
Greeting greeting = service.getPort(Greeting.class);
BindingProvider bp = (BindingProvider) greeting;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new RGBSOAPHandler());
binding.setHandlerChain(handlerList);
Then my file is encoded in-line. I don't know why this is, but I suppose the answer is "don't do that". Set your handlers on the Service object.

Looks like I'm limited by the framework and the way in which the handlers work. I think at this stage, my only option is to go to a lower level. I did take a look at using tubes but the same behaviour exhibited itself so it looks as though any attempt to work with the XML of the request fails. As such, I'm going to have to abandon handlers for the time being and investigate at a lower level whether I can make use of codecs to do what I'm after. An MTOM implementation sounds like it may do what I'm after at the byte level:
http://jax-ws.java.net/nonav/jax-ws-20-fcs/arch/com/sun/xml/ws/encoding/MtomCodec.html
I imagined this would be a lot less complex to get working but will update with my progress on the codec front.
#David: Thanks for your help on the handler front but looks as though there is no solution at that level.
Update 1
Came up with an alternate solution that works for my purposes.
I sign the necessary parts of the SOAP message using my SOAPHandler.
Wrote a new SOAPHandler that then takes resultant message and manually extracts the incorrectly inlined binary content.
I then create an AttachmentPart and inject the content from Step 2 into that. It takes Base64 encoded text too which is handy. That AttachmentPart then has a reference UUID assigned to it for Content-Id.
I then create a new element in place of the Base64 content in the SOAP body that reference the UUID, along the lines of the following:
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:UUID!!!"></xop:Include>
Will probably write a blog post on this as it's been a bit of an epic journey to this point. It was not the best solution but it was certainly easier than going down the tubes/codec path.

Related

Intercept and modify SQS SendMessageRequest

I'm using Java AWS SDK v1 to send messages to a SQS Queue, and I want to intercept this event to add some MessageAttributes in the message.
I created a new RequestHandler class that extend the RequestHandler2 from the SDK to modify it before the request:
class CustomMessagesAttributesRequestHandler : RequestHandler2() {
override fun beforeRequest(request: Request<*>) {
val originalRequest = request.originalRequestObject
if (originalRequest is SendMessageRequest) {
originalRequest.messageAttributes["my-custom-attribute"] =
MessageAttributeValue().withStringValue("123456789")
}
}
}
The solution above kinda works, it's called before the request, the Message Attribute is added in the originalRequestObject, but when the SQS client send the request it throws an exception:
Unable to calculate the MD5 hash of the message attributes
Digging into AWS SDK code, I see there is a default handler to check the message request with the result to compare both body and attributes MD5, and of course, since I modified the original object they does not match.
Is there a better way to achieve that besides the custom RequestHandler? Or there is a way to recalculate the originalRequestObject MD5?

Simplifying REST URL with JSON Parameters

I have been assigned a task and I really don't know how to even begin solving it, so any help would be appreciated. Consider the following example:
#Path("/v1/{server}:{port}/instance")
public class WSInstance {
private static final Log log = LogFactory.getLog(WSInstance.class);
private final String PLANNING_PROPNAME = "**PLNG_NAME**";
private final String PLANNING_PROPVAL = "**CALENDAR_NAME**";
#GET
#Path("/{instanceName}")
#Produces("text/plain")
public String getInstanceStatus(#Context HttpHeaders headers,
#PathParam("server")String server,
#PathParam("port")int port,
#PathParam("instanceName") String instName,
#DefaultValue("") #QueryParam("date") String date,
#DefaultValue("") #QueryParam("instnum") String numexec)
{
return getInstanceStatus(Utils.extractUserInfo(headers), server, port, instName, numexec, date);
}
An example of a call to the aforementioned method is going to look like this:
/v1/serverName:portNumber/instance/toto?date=21090207&instnum=0000
What the task is asking is to replace all the variables in that url (serverName, portNumber, toto, date and instnum) with json parameters. This is meant to simplify the REST URL.
Any idea where to begin?
** EDIT: Thanks to everyone for their answers, you've certainly helped me a lot. Here's what I have done so far:
I decided to take a "simpler" class and method to familiarize myself with the procedure. So I took this one:
#Path("/v2/{server}:{port}/admin/")
public class WSAdmin {
private static final Log log = LogFactory.getLog(WSAdmin.class);
#PUT
#Path("/device")
#Produces("text/plain")
#Consumes("application/json")
public String putDevice(String jsonObject, #Context HttpHeaders headers,
#PathParam("server")String server,
#PathParam("port")int port)
{
ObjectMapper mapper = new ObjectMapper();
try
{
return updateDevice(mapper.readTree(jsonObject), Utils.extractUserInfo(headers), server, port);
}
catch (JsonProcessingException e)
{
return e.getMessage();
}
catch (IOException e)
{
return e.getMessage();
}
}
I changed it like this:
#Path("/v2/admin/")
public class WSAdmin {
private static final Log log = LogFactory.getLog(WSAdmin.class);
#POST
#Path("/device")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response putDevice(Admin admin)
{
String output = admin.toString();
return Response.status(200).entity(output).build();
}
Then I created the corresponding POJO:
#XmlRootElement
public class Admin {
private String server;
private int port;
private Date date;
private String instnum;
// Constructors + getters + setters
#Override
public String toString() {
return new StringBuffer("Server: ").append(this.server)
.append("Port: ").append(this.port).append("Date: ")
.append(this.date).append("InstNum: ")
.append(this.instnum).toString();
}
}
Then I edited the web.xml file to be able marshal and unmarshal Java Objets:
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
But for some reason, i'm getting the following error when I make the call from postman:
GRAVE [http-nio-8080-exec-5] com.sun.jersey.spi.container.ContainerRequest.getEntity A message body reader for Java class com.ws.v3.models.Admin, and Java type class com.ws.v3.models.Admin, and MIME media type application/json was not found.
The registered message body readers compatible with the MIME media type are:
application/json ->
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$App
*/* ->
com.sun.jersey.core.impl.provider.entity.FormProvider
com.sun.jersey.core.impl.provider.entity.StringProvider
com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
com.sun.jersey.core.impl.provider.entity.FileProvider
com.sun.jersey.core.impl.provider.entity.InputStreamProvider
com.sun.jersey.core.impl.provider.entity.DataSourceProvider
com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.ReaderProvider
com.sun.jersey.core.impl.provider.entity.DocumentProvider
com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
com.sun.jersey.core.impl.provider.entity.SourceProvider$SAXSourceReader
com.sun.jersey.core.impl.provider.entity.SourceProvider$DOMSourceReader
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootObjectProvider$General
com.sun.jersey.core.impl.provider.entity.EntityHolderReader
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General
Those who had a similar error got it to disappear by adding either gerson or jersey-jsonin pom.xml. I've added them but the problem didn't get fixed.
Any idea?
Probably you have to change the method type to POST and pass the data as json in the body of the request.
The GET request
GET /v1/yourServerName:8080/instance/toto?date=21090207&instnum=0000
can become the following POST request
POST /v1/instance
{
"serverName":"yourServerName",
"portNumber":8080,
"date":21090207,
"instnum":"0000"
}
Note that instnum is not a numeric field because you passed the string 0000 that can't be represented as a numeric value. Instead portNumber and date can be numeric values.
Consider using Jackson. Jackson maps JSON <-> objects. Read about how you can use it with Jersey (REST) here:
https://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/
Per specification the payload of a GET request is undefined. You should, therfore, refrain from sending a body with a GET request. As Davide already suggested you should switch to POST here instead, as here the semantics of a payload received are defined by you, the server/API maintainer.
However, as you've tagged your post with rest you should probably consider copying the concepts used in the Web for over 2 decades now and translate it to your API design. First, REST architecture don't care about the structure of your URI. The URI itself is just a pointer to a resource and clients shouldn't need to interpret it nor hack it. All the information needed for a client to make distinctive choices should be provided by the server to start with. As clients shouldn't parse and interpret URIs how do they determine whether a URI is of use for the client or not?
Well, how do we humans interact with URIs in web-pages? Usually they are annotated with human readable text that summarizes the content of that link (as with the specification i.e. above). Such short but meaningful names are usually called link-relation names and should be "attachted" to each URI. A client reading such link-relation names and just invoke the accompanying URI will be able to continue its task if the server ever has the need to change its URI structure. This is one important step towards decoupling clients from servers. Such link relation names should be standardized but may also be specified in common knowlege or specified in media-types itself.
A common mistake many so called "REST APIs" do is to support either application/xml and/or application/json only. Those are very poor media types in a REST architecture as they only define the syntax to use but not the semantics of the respective elements. It is thus hard for a client to determine the intent of such a document and to easy to fall into the typed resource trap and assume that a certain resource has a certain type. If such a (non-standardized) representation changes, chances are high that a client will break to interoperate with that service/API further.
Media types are more or less standardized processing-rules for a certain received payload that should help give the receiver some meaning of the content and what it might do with it. One probably well known media type is HTML which defines when certain elements are feasible and the constraint each element has. It also defines how certain elements are rendered and how it is backward compatible with former versions. It is the defacto standard when it comes to support for links and link-relations. While HAL, Collection+JSON, ... are steps into the right direction in terms of support of links and relation names, they are far from providing the same semantics as HTML does, though they should be preferable to plain JSON as they not only specify a syntax but also semantics of certain elements such as _links i.e. which help the client to differentiate links from content.
Media types are especially important in terms of content-type negotiation where a client requests a server to return a representation format a client understands. If the server is not able to produce such a representation it will inform the client with an expressive enough error code (406). If the server is not able to process the media type provided by the client (on a POST, PUT, PATCH, ... operation) it will also inform the client that it does not understand such a format (415).
A general advice on designing REST APIs would be to think of the API in terms of a Web server and also desing the whole interaction with it like that. I.e. if a client has to perform certian input it should not send just a playin JSON document with some random fields (specified in some external documentation) to the server, but the server should teach the client on how to send such a request to start with. Similar to Web forms where humans should enter text and stuff, REST APIs should return a media-type representing a formular that teaches a client what fields the server expects, which operation to use and the target to send the payload to.
In regards to the actual question, I'm not sure why your employee is so keen on removing parameters from the URI. As mentioned in my first paragraph on sending a payload you'd need to switch to POST and therefore automatically loose the guaranteed safe and idempotent features of the GET operation, besides not being cacheable by default.
What you can do i.e. is allow users or your coworkers to preconfigure certain queries and create short/tiny URLs for these preconfigured URIs. Here you should provide a form-like media type to the client where it can select the options to choose from and enter further necessary data. Once you received such a request you store such a preconfiguration and return a short/tiny URL for that preconfiguration in the Location header of the response. You should also add the preconfigured links in the regular response so that a client is able to invoke it if it didn't store it right after persistence. By doing so you still have the benefits of the GET operation while having the flexibility to add new or customize existing queries on the fly. As mentioned before, a client won't be able to make much use of such links in a plain application/json representation. If the client supports application/hal+json it might at least know what link-relation and links are and therefore be able to lookup and invoke the URI via its accompanying link relation name. This basically gives you the freedom to later on change the URI structure if needed without affecting the client.

how to correctly get inbound message headers with spring integration

I'm receiving email with spring, with a very basic script so far
ApplicationContext ac = new ClassPathXmlApplicationContext("imap.xml");
DirectChannel inputChannel = ac.getBean("receiveChannel", DirectChannel.class);
inputChannel.subscribe(message -> {
System.out.println(message.getHeaders());
System.out.println(message.getPayload());
MessageHeaders headers = message.getHeaders();
String from = (String) headers.get("mail_from");
});
According to the documentation I thought the headers would get parsed automatically, but the headers I get with the first System.out.println(); are just
{id=c65f55aa-c611-71ee-c56d-6bf13c6f71d0, timestamp=1468869891279}
The second output (for getPayload()) is
org.springframework.integration.mail.AbstractMailReceiver$IntegrationMimeMessage#6af5615d
from outputs null...
I then tried to use the MailToStringTransformer
MailToStringTransformer a = transformer();
a.setCharset("utf-8");
System.out.println(a.transform(message));
Which outputs the payload and all the headers I have expected, but (naturally) as a String.
What do I have to do to get the messages headers and text in an object?
Not sure which documentation are you referring, but the current 4.3 version has this:
By default, the payload of messages produced by the inbound adapters is the raw MimeMessage; you can interrogate the headers and content using that object. Starting with version 4.3, you can provide a HeaderMapper<MimeMessage> to map the headers to MessageHeaders; for convenience, a DefaultMailHeaderMapper is provided for this purpose.
And a bit below:
When you do not provide a header mapper, the message payload is the MimeMessage presented by javax.mail. The framework provides a MailToStringTransformer...
If you need some customization on the mapping you always can provide your own HeaderMapper<MimeMessage> implementation or DefaultMailHeaderMapper extension.

set soapaction header in a java client

i have created a java client in netbeans 7.2 from a wsdl
the issue is that the header send Soapaction but the server is expecting to receive SOAPAction
i try to overwrite the properties using this code
BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.SOAPACTION_USE_PROPERTY, false);
prov.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://www.microsoft.com");
but again in the server it receives Soapaction instead of receiving SOAPAction
can someone tell me how can i overright this value?
thank you
I think you try to add it in a wrong place.
BindingProvider is only the stub object, "provides access to the protocol binding and associated context objects for request and response message processing."
What you really need here is an SOAP message interceptor, which you can use to customize your SOAP messages generated by your WS library.
In case you use JAX-WS, you can use for example SOAPHandlers to do this.
Here is an example:
http://www.mkyong.com/webservices/jax-ws/jax-ws-soap-handler-in-client-side/
If this is not your case, please provide more details about your application (what kind of project it is, what kind of WS implementation you are using etc).

JAIN-SIP support for RFC 7339

Is there any way to add custom via header in jain-sip? Adding the oc-parametrs from RFC 7339.
From enter link description hereplace I got the following example, but not sure if it will work. The quote from link:
This could be easily achieved by adding some code to implementation of
javax.sip.message.Message.addHeader(Header header) function.
void addHeader(Header header) {
if(!(header instanceof InternalHeaderObject)
&& header instanceof ExtensionHeader) {
ExtensionHeader extensionHeader = (ExtensionHeader) header;
header = headerFactory.createHeader(extensionHeader.getName(), extensionHeader.getValue());
}
...
}
I will start by saying you can absolutely handle custom via headers in terms of SIP as long as it is valid SIP. For this RFC you just need to use viaHeader.set/getParameter if I am not missing something.
The blog post talks about creating your own header classes, which is not relevant to your needs as far as I can imagine. Custom header classes are tricky and inefficient. For example JAIN SIP will automatically construct it's own ViaHeader instance for inbound messages when parsing them. Plugging a custom header to override the default Via internally will break a lot of validation promises and cause overhead..
If you have a showstopper case for custom header classes I will gladly listen though.

Categories