apache cxf LoggingFeature mask senstive information - java

I'm using Logging feature to log in/out message to my cxf rest server on Spring boot. Similarly using the same to log outward Rest API connections initiated by cxf WebClient.
I came across few parameters which I do not want to be logged in to the log file. either completely removing them or masking them is sufficient for my case.
I found on the internet that previous (now depreciated) LoginIntercepter had transform operation to modify the log entry. I was not able to find a solution to mask/truncate the log entries wirg LoggingFeature.
any help would be appreciated
Current configuration of the server's logging feature is like below.
factory.setProviders(providers);
LoggingFeature loggingFeature = new LoggingFeature();
loggingFeature.setPrettyLogging(true);
loggingFeature.setLogBinary(false);
loggingFeature.setLogMultipart(false);
factory.getFeatures().add(loggingFeature);
Server server = factory.create();
web client configuration is as below
LoggingFeature loggingFeature = new LoggingFeature();
loggingFeature.setPrettyLogging(true);
WebClient client = WebClient.create(url, Collections.singletonList(new JacksonJsonProvider()),
Arrays.asList(loggingFeature), null);

To change the log message, you can ...
Write a custom LogSender and set it on the LoggingFeature to do custom
logging. All meta data can be access[ed] from the class LogEvent.
(Source: http://cxf.apache.org/docs/message-logging.html)
With a sender like:
// ...
import org.apache.cxf.ext.logging.event.LogEvent;
class MyLogEventSender implements org.apache.cxf.ext.logging.event.LogEventSender {
#Override
public void send(LogEvent event) {
event.setPayload(maskSensibleParameters(event.getPayload()));
}
private String maskSensibleParameters(String pIn) {
// here goes the tricky part
// ... but no details on this in your question
// ... here you can stick to "old" LogInterceptor examples
// ... and also to PrettyLoggingFilter.
}
}
A code example is given by (the default) PrettyLoggingFilter.
To mask (hide) it (completely) is easier and guaranteed more performant, it depends on the used "logging framework" (java.util, log4j or slf4j) and accomplished with an according "logger configuration". (see here)

Related

How to extend expiration time of SOAP header message timestamp

Is there a way to prolong time to live(expiration time) of a SOAP message header timestamp to prevent
"WSSecurityException: The message has expired" exception. The default is set to 300 seconds(5 minutes). We would like to extend it to 10 minutes.
We have a java ee(jdk 1.8.0_77) service that uses SOAP messages to communicate with android client.
It runs on Jboss Wildfly 10.0.0. server, and uses Apache CXF(version 3.1.4) WS security to handle messages. wss4j security policy is version 2.1.4
We have wsdl file, Custom WS-Security Endpoint (server-endpoint-config.xml file), and our Schema.xsd file. I don't know what parts of those files are relevant, so I'll provide them if someone needs more info.
I've tried using a custom interceptor that extends WSS4JInInterceptor, but that part of code never gets executed.
#WebService
(
portName = "HelloPort",
serviceName = "BampayService",
wsdlLocation = "WEB-INF/BampayService.wsdl",
targetNamespace = "http://bampay.bamcard.ba/",
endpointInterface = "ba.bamcard.bampay.Hello"
)
#EndpointConfig
(
configFile = "WEB-INF/server-endpoint-config.xml",
configName = "Custom WS-Security Endpoint"
)
#InInterceptors(// I added this part
interceptors = {"ba.bamcard.helpers.MyInterceptor"}
)
and the code of MyInterceptor
public class MyInterceptor extends WSS4JInInterceptor {
#Override
public int decodeTimeToLive(RequestData reqData, boolean timestamp) {
// return super.decodeTimeToLive(reqData, timestamp);
return 600;
}
}
If needed, I will gladly provide any additional information.
If anyone can help me, I would be very grateful.
Add this property to your server-side security settings to the value you want.
ws-security.timestamp.timeToLive
If you think you are seeing clock-skew between the client(s) and the server as part of the reason for the errors, you'll also want to account for clients that send messages with timestamps in the future.
ws-security.timestamp.futureTimeToLive
ref: https://cxf.apache.org/docs/ws-securitypolicy.html

Store Soap Response Message in Citrus Framework

Good Morning:
I'm new in Citrus Framework. Actually i work in a Test Case that consumes one soap webservice. I can send request message from a xml file and i need to store response message from server into another xml file for trazability and audit.
I try some options but still not working. Can you help me with posibles solutions to this requirement?
My test look like this:
public class DummyIT extends TestNGCitrusTestDesigner {
#Autowired
private WebServiceClient DummyClient;
#Test
#CitrusTest
public void dummyTest() {
soap()
.client(DummyClient)
.send()
.messageType(MessageType.XML)
.charset("UTF-8")
.contentType("text/xml")
.payload(new ClassPathResource("templates/DummyRequest.xml"));
soap()
.client(DummyClient)
.receive()
.schemaValidation(false);
}
I'm using Citrus Framework version 2.7.2.
Thanks for your help.
You can add a message tracing test listener to the Spring application context. This listener is called with all inbound and/or outbound messages. With a custom implementation you can write the message content as file to an external folder.
There is a default message listener implementation available that is a good starting point. See if this default tracing listener fits your requirements. Otherwise you would have to implement the listener logic on your own.
You can add the default listener to the application context as bean:
#Bean
public MessageTracingTestListener tracingTestListener() {
return new MessageTracingTestListener();
}
After that you should see .msgs files in target/citrus-logs/trace/messages folder containing all exchanged inbound and outbound messages.
Here is the default implementation: https://github.com/citrusframework/citrus/blob/master/modules/citrus-core/src/main/java/com/consol/citrus/report/MessageTracingTestListener.java

Spring Remoting with AMQP - Client is not seeing the beans exposed from Server

I'm trying to run example from http://www.baeldung.com/spring-remoting-amqp, even when I set up the connection to the dedicated vhost to my RabbitMQ broker, I can only send the request from client (I see it in RabbitMQ UI), but I never get the answer from the server.
The server seems to bean the service (the returning Impl class) with getBeanDefinitionNames(), but I definitly do not see those beans on the client side. I use annotations to set up beans, not the .xml file.
So the question is - why my client is not seeing the Server beans, I discover it more a less in following way:
#Autowired
private ApplicationContext appContext;
public GetResponse get(String id) {
Service service = appContext.getBean(Service.class);
System.out.println(service.ping());
return new GetResponse();
}
The answer which I get on the level of webservice is:
{
"timestamp": "2018-02-01T10:09:00.809Z",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.remoting.RemoteProxyFailureException",
"message": "No reply received from 'toString' with arguments '[]' - perhaps a timeout in the template?",
"path": "/v3/app/r"
}
Service:
public interface Service extends Serializable{
String ping();
}
Service Impl:
public class ServiceImpl implements Service {
#Override
public String ping() {
System.out.println("ponged");
return "pong";
}
#Override
public String toString() {
return "to string";
}
EDITED + BOUNTY
In the link you can find extracted modules which I want to connect together. I suppose that it is still about 'not seeing' the beans from one module in the second one.
The action can be trigerd with GET http://localhost:8081/v3/app/u The RabbitMQ settings has to be adjusted to your set-up.
https://bitbucket.org/herbatnic/springremotingexample/overview
I think you shouldn't set the routing key in your client, in amqpFactoryBean (and the one you set seems invalid):
https://bitbucket.org/herbatnic/springremotingexample/src/b1f08a5398889525a0b1a439b9bb4943f345ffd1/Mod1/src/main/java/simpleremoting/mod1/messaging/Caller.java?at=master&fileviewer=file-view-default
Did you try to run their example?
https://github.com/eugenp/tutorials/tree/master/spring-remoting/remoting-amqp
Just stumbled upon this question 3 years later.. trying to run the Baeldung example!
I tried debugging the issue and as far as I can tell, something internal in the AMQP implementation of spring remoting is not using the correct Routing Key when sending the client message, meaning the payload arrives at the broker and is never put into the queue for processing, we then timeout after 5s (default) on the client.
I tried the other answer by Syl to remove the routingKey however it doesn't seem to allow us to create a binding without one, and even when creating a binding directly on the broker management page (without a routing key) it doesn't route the messages.
I have not managed to make the example work, however I found a blog post on fatalerrors.org that shows a custom implementation of the AmqpProxyFactoryBean and it has custom handling for the routing key, this one works.
I've create this gist with the example that is working for me in case the blog post above goes under.
One other thing to note is that on the Baeldung example they are using a DirectExchange, while here we are using a TopicExchange.

Maintain correlationId through RabbitMQ

I've been looking at using RabbitMQ for cross-service messaging. I've been able to configure our Exchanges / Queues / DLX etc. using Spring annotations. Example (simple) queue listener:
#RabbitListener(queues = RabbitMessageType.QueueNames.SMS_NOTIFICATIONS)
public void receive1(Message message) throws Exception {
RabbitMessageDto messageDto = OBJECT_MAPPER.readValue(message.getBody(), RabbitMessageDto.class);
SmsNotificationDto payload = OBJECT_MAPPER.readValue(messageDto.getPayload(), SmsNotificationDto.class);
log.info(payload.getMessage());
}
I'm using spring-cloud-sleuth to generate correlationIds / traceIds, which are preserved when using HTTP requests to talk to other services, enabling us to trace the given ID throughout the logs of our various microservices.
While I can get the current traceId and insert that into my DTO:
#Autowired
private Tracer tracer;
private RabbitMessageDto createRabbitMessageWithPayload(String messageType,
String messageVersion,
Object payload) {
return new RabbitMessageDto.Builder()
.withTraceId(tracer.getCurrentSpan().getTraceId())
.withDtoName(messageType)
.withDtoVersion(messageVersion)
.withPayload(payload)
.build();
}
I cannot find a way to set the traceId in the receiving method.
Googling keeps bringing me to spring-cloud-stream and spring-cloud-stream-starter-rabbit; documentation seems to indicate that it's possible automatically insert / set traceIds, but I'm not familiar with spring-cloud-stream at all, and don't find the documentation particularly helpful.
So, I would love answers to the following:
Using SpanAdjuster or Tracer etc; can I set the traceId based on the value in my DTO?
Using spring-cloud-stream, can I automagically insert / retrieve the traceId, and where would I start?
So, incase someone comes across this looking to set the sleuth traceId context, we came up with the following solution:
#Autowired Tracer tracer;
private void someMethod(long traceId) {
Span span = Span.builder()
.traceId(traceId)
.spanId(new Random().nextLong())
.build();
tracer.continueSpan(span);
// do work
tracer.closeSpan(span);
}
It should be noted that all the documentation says that a span should be closed once you've finished with it. The do work section above should be wrapped with a try / catch / finally block to ensure this is closed.
Any methods called with the span still open will inherit the traceId.
EDIT
I should also say, it seems the better solution would be to replace the Spring AMQP library with spring-cloud-stream; from what I can tell, this should automatically include the traceId in rabbit messages (correlationId) and set it at the other end. I haven't had the opportunity to test this, however.

How to use ws-security info per sending soap message in CXF?

I like to be able to dynamically change/set the ws-security info per sending soap message in cxf. How can this best be done.
Details: I want to change settings like keystore name, keystore alias, password, host name, etc.. during runtime, preferable per sending message.
Currently I am using: jaxws client with WSS4JOutInterceptor and WSS4JInInterceptor interceptor for signing. And I am using http conduit with tls client parameters for SSL/TLS communications.
Both the jaxws client and http conduit are configured in spring and have their configuration like keystore name, alias and password set in the Spring config.
I see options:
1) I change these settings during runtime through a global property.
2) I change these settings per sending message (preferable).
2) Is preferable but most difficult I think.
How should I do this?
I was thinking about:
a) jaxws client: make my own in- and out interceptor that intercepts a message and use the correct (cached) WSS4JOutInterceptor interceptor depending on the security settings that that message requires. If the WSS4JOutInterceptor interceptor doesn't exists in cache, it's created (I probably have max 5 WSS4JOutInterceptor instances cached).
However, how can I determine which settings are required in my interceptor as that is known in an other part of the app when talking to the #Webservice proxy to create and send the soap request/message...
Maybe I could be able to add some kind of securityInfo object to the soap message through the JaxWsClientProxy, but how ?...
Or maybe I can set the interceptors when sending/creating the soap message when this security info is still know. This will then be a light weight interceptor containing the required settings, linking to the cached interceptors, that is selected depending on the settings...
b) http conduit: using a ConduitSelector (never used it but will find out), such that I am able to select the correct http conduit, but I have the same problem as in (a): "How to determine which settings I should use" as they are known when creating/sending a soap message and the interceptors are set later...
Probably I have to set an conduit selector per sending message..
At the end the above become a big story ;), but I hope it's clear you have can give some advice?
I have found the org.apache.cxf packages to be very helpful in keeping me from having to write custom classes and have been able to stick to the basic interface.
For your instance, the approach might look like:
use a JAX-WS or wsimport client
use the cxf WSS4JOutInterceptor with dynamic properties
use a dynamic CallbackHandler class for access to the keystore
A typical (wsimport'd) client public API might look like:
public class SomeServiceClient
{
public SomeService getSomeService( URL url )
{
SomeService_Service svc = new SomeService_Service();
SomeService someService = svc.getSomeServicePort();
Client client = ClientProxy.getClient( someService );
Endpoint cxfEP = client.getEndpoint();
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put( WSHandlerConstants.ACTION, "Signature" );
outProps.put( WSHandlerConstants.USER, "foo" );
outProps.put( WSHandlerConstants.PW_CALLBACK_CLASS, SomeClientCallbackHandler.class.getName() );
outProps.put( WSHandlerConstants.SIG_PROP_FILE, "client-sign.properties" );
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor( outProps );
cxfEP.getOutInterceptors().add( wssOut );
return someService;
}
}
You could provide some logic to determine which WSHandlerConstants.USER value to pass, which PW_CALLBACK_CLASS to use, and which SIG_PROP_FILE to use.
The properties file could look like the following. You could simply have multiple files to choose from, or you could just dynamically add these properties in the class:
# properties for accessing the java keystore using Merlin
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=bar
org.apache.ws.security.crypto.merlin.keystore.alias=foo
org.apache.ws.security.crypto.merlin.keystore.file=foobar.keystore
Finally, your CallbackHandler will need to provide the password for the alias of the cert you identified in the keystore. This could also have some dynamic logic in it.
public class SomeClientCallbackHandler implements CallbackHandler
{
#Override
public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException
{
for( Callback thisCallback : callbacks )
{
WSPasswordCallback pwcb = (WSPasswordCallback)thisCallback;
String user = pwcb.getIdentifier();
int usage = pwcb.getUsage();
if( usage == WSPasswordCallback.SIGNATURE )
{
if( "foo".equals( user ) ) pwcb.setPassword( "bar" );
}
}
}
}
In the end, there is not much more work to make this all 'dynamic' to handle multiple keystores versus the work needed to just do it once. It might be preferable over coding your own 'in' or 'out' interceptors.
The accepted answers to this question appear to be (from http://www.mail-archive.com/users#cxf.apache.org/msg29804.html):
Hi Ed,
As I already wrote, you don't even need the interceptor to change the
properties. You can do it in your client by sending message:
AddNumbers port = (AddNumbers)service.getPort(portName,
AddNumbers.class);
((BindingProvider)port).getRequestContext().put(SecurityConstants.ENCRYPT_PROPERTIES,
).
Although, If you would like to do it in interceptor, you can pass
necessary information with message properties using the same technic.
The most standard and recommended way to control security in CXF is
using WS-Policy. You also can apply it dynamically at the runtime. If
this way is interesting for you, I can provide further information
how to set WS-Policy dynamically.
Cheers, Andrei.
(from http://www.mail-archive.com/users#cxf.apache.org/msg29809.html):
If you do:
((BindingProvider)port).getRequestContext().put("thread.local.request.context",
"true");
than future calls to getRequestContext() will use a thread local
request context. That allows the request context to be threadsafe.
(Note: the response context is always thread local in CXF).
Details in
http://cxf.apache.org/faq.html#FAQ-AreJAXWSclientproxiesthreadsafe%3F
Cheers, Andrei.

Categories

Resources