Forwarding requests to external endpoint from an CFX service - web-services

Currently I am fighting with the following problem:
I need to forward SOAP requests to an external service in special cases (decision based on tenantId provided in the SOAP message). I created an interceptor for this task to extract tenantId from the message request, get assignment (each tenantId is assigned to its own service instance running on a different server) and if no assignment is made, I need to process the request just as normal.
Currently I implemented on this way: I create HttpUrlConnection in the interceptor and forward the request to an external endpoint (in case there is an assignment) and take the outputStream of the response and send the response over HttpServletResponse.getOutputStream etc...
I also need to consider that the interceptor be used with various service (tenantId must be provided in the SOAP request).
I also read about Provider and Dispatch objects not sure how this should work.
Is there any way to get target service and port (QNames) from the incoming message?
I cannot use Camel at the moment (only CXF is allowed).

Maybe you can try something like this :
/** Your interceptor */
public void handleMessage(SoapMessage msg) throws Fault {
Exchange exchange = msg.getExchange();
Endpoint ep = exchange.get(Endpoint.class);
// Get the service name
ServiceInfo si = ep.getEndpointInfo().getService();
String serviceName = si.getName().getLocalPart();
XMLStreamReader xr = msg.getContent(XMLStreamReader.class);
if (xr != null) { // If we are not even able to parse the message in the SAAJInInterceptor (CXF internal interceptor) this can be null
// You have the QName
QName name = xr.getName();
SOAPMessage msgSOAP = msg.getContent(SOAPMessage.class);
// Read soap msg
if (msgSOAP != null) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
msgSOAP.writeTo(byteArrayOutputStream);
String encoding = (String) msg.get(Message.ENCODING);
String xmlRequest = new String(byteArrayOutputStream.toByteArray(), encoding);
}
// Forward to external service with JAX-RS implementation
Client client = ClientBuilder.newClient()
.target("http://your-target")
.path("/custom-path")
.request()
.post(Entity.entity(xmlRequest, MediaType.APPLICATION_XML));
}
}
Hope this help.

Related

How to add Authorization Header to client handshake request in Eclipse-jetty-websocket version-8

I want to add Authorization header for basic Authorization to my jetty-client request.
I am running two instances on same browser one is server one is client ,Java-8 is being used as a technology, No DB No Spring etc. using org.eclipse.jetty,jetty-websocket,version-8.1.15.v20140411.
I had a websocketEndPointManager class which contains the initialization method for both Server(dedicated class is CSWebsocket) and client(Cpwebsocket).
How can I achieve basic auth in my scenario where server maintains username and password is to matched in current session after its instance is ready, currently my setup able to perform upgrade to websocket but not basic
Snippet of client websocket start method where handshake occur :
private void startCPWebSocket() throws Exception{
String uri ="";
try {
HandlerRegistry.register(this);
WebSocketClientFactory factory = new WebSocketClientFactory();
Configuration configuration = TestSession.getConfiguration();
uri = configuration.getSystemUnderTestEndpoint();
TestSession.getConfiguration().setToolURL("NA");
TestSession.getConfiguration().setSystemUnderTestEndpoint(uri);
factory.start();
WebSocketClient client = factory.newWebSocketClient();
client.setMaxIdleTime(24*60*60*1000);
client.open(new URI(uri.trim()),new CPWebSocket()).get(24*60*60,
TimeUnit.SECONDS);
LogUtils.logInfo("WebSocket URL : "+uri,true);
}
catch (ExecutionException e) {
LogUtils.logError(e.getMessage(), e,false);
LogUtils.logWarn("Could not establish websocket connection.",true);
//System.exit(0);
TestSession.setTerminated(true);
throw e;

Implement CXF client to read LIST from a REST server

I am trying to implement a rest web service using Apache CXF and I want to return list of object as a response from server. So i have used generic entity to wrap my list on a server and everything is fine when I enter path from browser. It prints XML representation of object because I have used Jackson JAX-B but when i try to use JAX-RS client. I am getting an exception.
Exception in thread "main" javax.ws.rs.client.ResponseProcessingException: Problem with reading the data, class XYZ, ContentType: /.
at org.apache.cxf.jaxrs.impl.ResponseImpl.reportMessageHandlerProblem(ResponseImpl.java:433)
at org.apache.cxf.jaxrs.impl.ResponseImpl.doReadEntity(ResponseImpl.java:378)
at org.apache.cxf.jaxrs.impl.ResponseImpl.readEntity(ResponseImpl.java:325)
at org.apache.cxf.jaxrs.impl.ResponseImpl.readEntity(ResponseImpl.java:313)
at XYZ.ABC()
at XYZ.ABC()
Caused by: javax.ws.rs.core.NoContentException: Message body is empty
at org.apache.cxf.jaxrs.provider.AbstractJAXBProvider.reportEmptyContentLength(AbstractJAXBProvider.java:276)
at org.apache.cxf.jaxrs.provider.JAXBElementProvider.readFrom(JAXBElementProvider.java:166)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(JAXRSUtils.java:1325)
at org.apache.cxf.jaxrs.impl.ResponseImpl.doReadEntity(ResponseImpl.java:369)
... 4 more
I have written a following client code to get data from server
final Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(URI.create(PATH));
Response response = webTarget.request(MediaType.APPLICATION_XML).get();
List<ABC> obj = response.readEntity(new GenericType<List<ABC>> (ABC.class){});
But Apart from it I have tried many code to implement CXF client and get data from server but I am getting a same exception almost all the time. I have tried JAXRSCLIENTFactory also to implement client but the same exception.
I ran into the same problem in unmarshalling using the CXF client. Here is how I did it:
Read the response into a String.
Used Gson to convert from string to list of objects.
Note: You will need a wrapper class for your list of objects.
Example:
If the server returns a list of products, Here is how to unmarshall the list:
Create a wrapper class
public class ProductList {
private List<Product> products;
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
}
Code to unmarshall
String responseBody = response.readEntity(String.class);
ProductList productList = new Gson().fromJson(responseBody, ProductList.class);
if(productList.getProducts() != null)
return productList.getProducts();

Apache CXF Endpoint validation with Interceptor

What I want to do:
Implement a Camel route from a CXF Endpoint to an JMS queue with schema validation in CXF endpoint.
Validation is enabled in CXF endpoint:
/* Set endpoint properties */
Map<String, Object> propertiesMap = new HashMap<String, Object>();
propertiesMap.put("schema-validation-enabled", "true");
/* Create endpoint */
CxfEndpoint cxfEndpoint = new CxfEndpoint();
cxfEndpoint.setWsdlURL("wsdl/input.wsdl");
cxfEndpoint.setDataFormat(DataFormat.CXF_MESSAGE);
cxfEndpoint.setProperties(propertiesMap);
cxfEndpoint.getInInterceptors().add(new FaultInterceptor());
The Camel route:
from(cxfEndpoint)
.routeId("INPUT_ROUTE")
.to("jms:foo.bar");
The CXF interceptor:
public class FaultInterceptor extends AbstractSoapInterceptor {
private static final Logger LOGGER = Logger.getLogger(FaultInterceptor.class);
public FaultInterceptor() {
super(Phase.UNMARSHAL);
}
public void handleMessage(SoapMessage message) throws Fault {
LOGGER.info("handleMessage=" + message.getExchange().getInMessage());
}
#Override
public void handleFault(SoapMessage message) {
Fault fault = (Fault) message.getContent(Exception.class);
LOGGER.info("handleFault='" + fault + "'");
/* Add some header property that says the message is invalid */
}
}
Problem:
The works ok if I send a valid SOAP message. If I send an invalid SOAP message, the handleFault method kicks in, logs the fault and that's all.
For the invalid SOAP message scenario, is it possible that I can log the fault with handleFault method and still route the invalid message to the JMS queue?
This is the only interceptor I've added to the endpoint.
I'm using:
Apache ServiceMix 5.0.0
Apache Camel 2.12.3
Apache CXF 2.7.10
You can't do a try/catch statement since the error happends in the "from" clause.
However, you can use a Dead Letter Channel.
errorHandler(deadLetterChannel("jms:foo.bar.invalid"));
from(cxfEndpoint)
.routeId("INPUT_ROUTE")
.to("jms:foo.bar");

Servlet Filter as Security Proxy for Web Services

Good time.
Suppose there are 8 web-services in the one application. 5 of them require authorization (a client must to provide a JSESSIONID cookie and a corresponding session must not be invalidated), other 3 can be called without the jsessionid cookie. My naive solution is to write a servlet filter which intercepts requests and retrieve their pathInfos (all the services have the same url structure: /service/serviceSuffix). There is a enum which contains the serviceSuffix of each web service that requires authorization. When the request is retrieved the pathInfo is collected; if this pathInfo is contained in the enum and there is the corresponding valid session the request is sent ahead to the filter chain. Otherwise, an error is sent back to a client. After a while I've realized that it is needed to add the possibility to retrieve the wsdl and xsds for the concrete service. So that, two more check were added.
public class SecurityFilter implements Filter {
public static final String WSDL = "wsdl";
public static final String XSD = "xsd=";
/**
* Wittingly left empty
*/
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse)response;
String pathInfo = servletRequest.getPathInfo();
String queryString = servletRequest.getQueryString();
if (pathInfo != null && SecureWebServices.contains(pathInfo)) {
if (queryString != null && (queryString.equals(WSDL) || queryString.startsWith(XSD))) {
// wsdl or xsd is requested
chain.doFilter(request, response);
} else {
// a web service's method is called
HttpSession requestSession = servletRequest.getSession(false);
if (requestSession != null) { // the session is valid
chain.doFilter(request, response);
} else {
servletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
}
} else {
chain.doFilter(request, response);
}
}
/**
* Wittingly left empty
*/
public void destroy() {}
}
It seems that it is not very secure, because if the request's pathInfo is not in the enum, this request is passed on (just in case of some unexpected system calls).
Could you, please, suggest what to do, how to increase the security level. I want to build a configurable system (that is why I have the enum. It is possible just to add a path there to secure the web service and it is not required to duplicate the security code in the each web service). How to increase
Maybe I do not understand but.
jsessionid has nothink to do with security. you simply just get it.
Next I am not sure if you want authentication or authorization. The code as provided will not provide you with security features.
I suppose you are interested in authentication anyway. Security logic can be provided with standard web container features. Just send in authentication data in the header of request and you are done. web container can be configured to secure only selected resources (urls)

MonoTouch Web Service Request over SSL Gets 'authentication or decryption has failed'

A web service request over SSL raises a WebException on Monotouch v4.0.4.1:
'Error getting response stream (Write: The authentication or decryption has failed)'
Since the server's SSL certificate is self-signed (and btw I think it is not X.509), I am bypassing the certificate validation using ServicePointManager.ServerCertificateValidationCallback. The exact same code works fine on Windows .NET, where the web service call returns the correct result. On Monotouch adding a Writeline shows that the ServerCertificateValidationCallback delegate code is never reached.
Note: Although probably not relevant, the content of the request is SOAP with embedded WS-Security UsernameToken.
Has anyone got something like this to work on MonoTouch? Have seen reports of similar symptom but no resolution. The code and stacktrace are below, any comment appreciated. Can email a self-contained test case if wanted.
I gather there is an alternative approach using certmgr.exe to store the self-signed server certificate in the local trust store, but can't seem to find that app in the MonoTouch distribution. Could anyone point me to it?
..
public class Application
{
static void Main (string[] args)
{
UIApplication.Main (args);
}
}
// The name AppDelegate is referenced in the MainWindow.xib file.
public partial class AppDelegate : UIApplicationDelegate
{
// This method is invoked when the application has loaded its UI and its ready to run
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
// If you have defined a view, add it here:
// window.AddSubview (navigationController.View);
string soapResponse;
string soapRequest = #" SOAP envelope is here but omitted for brevity ";
soapResponse = WebService.Invoke("myOperation", soapRequest);
window.MakeKeyAndVisible ();
return true;
}
// This method is required in iPhoneOS 3.0
public override void OnActivated (UIApplication application)
{
}
}
public class WebService
{
public static string Invoke(string operation, string soapRequest)
// Input parameters:
// operation = WS operation name
// soapRequest = SOAP XML request
// Output parameter:
// SOAP XML response
{
HttpWebResponse response;
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, ssl) => true;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://myserver.com:7570/MyEndpoint");
request.Method = "POST";
request.Headers.Add("SOAPAction", "/MyEndpoint/" + operation);
request.ContentType = "text/xml;charset=UTF-8";
request.UserAgent = "Smartphone";
request.ContentLength = soapRequest.Length;
request.GetRequestStream().Write(System.Text.Encoding.UTF8.GetBytes(soapRequest), 0, soapRequest.Length);
request.GetRequestStream().Close();
response = (HttpWebResponse)request.GetResponse();
using (StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
catch (WebException e)
{
throw new WebException(e.Message);
}
}
}
Stack trace (some names changed to protect the innocent, original available on request):
WS.WebService.Invoke (operation="myOperation", soapRequest="<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" \n\txmlns:ns1=\"http://mycompany/Common/Primitives/v1\" \n\txmlns:ns2=\"http://mycompany/Common/actions/externals/Order/v1\" \n\txmlns:ns3=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n\t<SOAP-ENV:Header> <wsse:Security SOAP-ENV:mustUnderstand=\"1\" \n\txmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"> \n\t<wsse:UsernameToken wsu:Id=\"UsernameToken-1\" \n\txmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"> \n\t<wsse:Username>myusername</wsse:Username> <wsse:Password \n\tType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">mypw</wsse:Password> \n\t<wsse:Nonce>{0}</wsse:Nonce> \n\t<wsu:Created xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">{1}</wsu:Created> \n\t</wsse:UsernameToken> </wsse:Security> \n\t</SOAP-ENV:Header><SOAP-ENV:Body><ns2:tp_getOrderDetailRequest><ns2:header><ns1:source>TEAM</ns1:source>\n\t<ns1:userAccessKey>12345678901234567</ns1:userAccessKey></ns2:header>\n\t<ns2:OrderId>myid1</ns2:OrderId>\n\t<ns2:OrderId>myid2</ns2:OrderId>\n\t</ns2:tp_getOrderDetailRequest>\n\t</SOAP-ENV:Body>\n\t</SOAP-ENV:Envelope>") in /Users/billf/Projects/WS/WS/Main.cs:103
WS.AppDelegate.FinishedLaunching (app={MonoTouch.UIKit.UIApplication}, options=(null)) in /Users/billf/Projects/WS/WS/Main.cs:52
MonoTouch.UIKit.UIApplication.Main (args={string[0]}, principalClassName=(null), delegateClassName=(null)) in /Developer/MonoTouch/Source/monotouch/monotouch/UIKit/UIApplication.cs:26
MonoTouch.UIKit.UIApplication.Main (args={string[0]}) in /Developer/MonoTouch/Source/monotouch/monotouch/UIKit/UIApplication.cs:31
WS.Application.Main (args={string[0]}) in /Users/billf/Projects/WS/WS/Main.cs:18
MonoTouch (just like Mono) does not support TLS_DH* cipher suites (like TLS_DHE_DSS_WITH_AES_128_CBC_SHA).
When a server is configured to accept only them then the negotiation stage fails very early (an Alert is received from the server after the Client Hello message is sent) which explains why the callback was never called.
Ensure your server allows the more traditional cipher suites, e.g. the very secure (but slow) TLS_RSA_WITH_AES_256_CBC_SHA or the faster (and very common) Cipher Suite: TLS_RSA_WITH_RC4_128_[MD5|SHA], and Mono[Touch] should work well using them.
Note that this is unrelated to SOAP or web-services (and even X.509 certificates) - it's just plain SSL.
1) An untrusted root certificate is not the only problem that could result in this exception.
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, ssl) => true;
Add a Console.WriteLine in there so you'll see if it gets called (or not).
throw new WebException(e.Message);
and another here, with full stack trace (not just the Message property).
2) Each application is isolated. This means that:
applications cannot updates the global iOS certificate stores (that would create security issues);
if a certmgr tool existed (for MT) it could only use a local (mono) store that would be usable only for itself (which would not be of any help for your own apps)