17 sept. 2015

Apache Camel - Unit Testing Spring route

Apache Camel is an awesome framework, also providing a unit testing framework.

I found it quite tough to dive into that testing framework as testing a route is not the same than testing a regular Java class.
Here are some pattern when testing your Spring route :

Base code

The spring route

Assuming this route in camel.xml :

 

 
  
 

 

 
 

Test class skeleton

And this JUnit test class, referencing
  1. The XML route file
  2. The @DirtiesContext allowing us to manipulate the Camel Context on tests without effects outside of each test
   
@ContextConfiguration("classpath:camel.xml")
@DirtiesContext
public class MyTest extends AbstractJUnit4SpringContextTests{

    @Autowired
    private ModelCamelContext context;

    @Test
    public void testRouteId() throws Exception {
       // introspect route
       // create expectations
       // execute route
       // asserts
    }
}

Patterns to test the route

Test a component

Here we are mocking the parameters provided to the WebService :
  1. Create a MockEndpoint
  2. Insert endpoint in the route
  3. Add expectations for the endpoint :
    1. The number of messages
    2. The type of the message
    3. The content of the message
  4. At the end of the test method, trigger the assertion

    @EndpointInject(uri = "mock:assertWsParams")
    protected MockEndpoint assertWsParams;
...

//introspection
context.getRouteDefinition("route-id").adviceWith(context, new AdviceWithRouteBuilder() {
 @Override
 public void configure() throws Exception {
  weaveById("cxfWs").before().to(assertWsParams);
 }
});

//expectations
assertWsParams.expectedMessageCount(1);
assertWsParams.expectedBodyReceived().body().isInstanceOf(RequestType.class);
assertWsParams.expectedMessagesMatches(new Predicate() {
 @Override
 public boolean matches(Exchange exchange) {
  return ((RequestType) exchange.getIn().getBody()).getId().equals(EXPECTED_ID);
 }
});

//execution
...

//assertion
assertWsParams.assertIsSatisfied();

Mock a component

Here we are mocking the cxf WebService with a static response :
  1. Find the "node" with the weaveById
  2. replace() it
  3. with a process()
context.getRouteDefinition("route-id").adviceWith(context, new AdviceWithRouteBuilder() {
 @Override
 public void configure() throws Exception {
  weaveById("cxfWs").replace().process(new Processor() {
   @Override
   public void process(Exchange exchange) throws Exception {
    ReponseType res = new ResponseType();
    res.setCode("200");
    res.setMessage("OK");
    exchange.getOut().setBody(res);
   }
  }).id("cxfWsMock");
 }
});

Run the test

Here we are running the test, injecting some data in the route to test it and the assertions :
  1. Create a producer on a uri
  2. Send some data
@Produce(uri = "direct:hello")
protected ProducerTemplate producer;
...

producer.sendBody("Hello");

Conclusion

My tests are mainly based on these 3 patterns, be sure to understand the logic.

More information on Camel test documentation

12 juil. 2015

Security - Tips to develop a secured Java web application

I recently had to build a web application aiming to handle critical and confidential user data.

Security was the main challenge of this application.

Of course, security is a wide topic, so I 'll present here the security measures I had to take in the code to ensure data privacy and pass the security audit (black box and gray box)

OWASP

The bible of Web Security is the OWASP web site

The measures I had to take to counter the top 10 risk :

Web tiers

  • Detect and reject some patterns on form submission ("<script" for example) (A3)
  • Include a one time token inside every form, and challenge it server side on form submission (A8)
  • Include a one time token inside every link that perform an write operation in database, and challenge it server side on action (A8)
  • Configure and test the HTML escaping of variables of the framework rendering engine. (A3)
  • Develop a full set of features to keep ids from being transfered client side. My strategy was to store ids in a "session context" and rely on it, transfering indexes instead - in the case of tables, for example (A4)
    • Beware, sometimes the magic of your framework car introduce this vulnerability without even noticing!
  • Restrict every entry point for the role allowed - based on a standard RBAC.
  • Initiate redirects server side, redirecting to a static declared URL (no computation based on user input) (A10)
  • Set the pragma:no-cache header

Database tiers

  • 100% names queries. No concatenation. Never. (A1)
  • Escaping of data rendered in PDF report. (A1)

App tiers

  • Check the application server session cookie value algorithm (java.secure.Random) (A2)
  • Set session timeout to an accurate duration (A2)
  • Set the cookie-config to secure in web.xml - https only app (A2)
  • Hide every HTTP response header that could give information to attacker

Other

  • Configure iptables as "deny by default" with rules rejecting connection on unallowed port (A5)
  • Enforce authorization between software components, according to the vendor specification(restrict by IP for example) (A5)
  • Include operations in security configuration (A5) 
  • Detect and protect sensitive data - including project owner in the process (A6)
  • Stay updated on your technologies - mostly version updates and known vulnerabilities (A9)

Conclusion

This is obviously not exhaustive, common sense is important as well as training developers.

Anyway, we passed 3 security audit (code, web security, server vulnerability), not so bad !

28 mai 2015

Ionic : differences between desktop browser and android web view

Ionic is a powerful framework to develop hybrid mobile applications on top of angularjs.

Here is what i learned from my first android tests - based on Ionic 1.0.0.rc5 - simply running ionic run android command

Error : device is not defined

The main issue i met. Everything was correctly defined, including cordova.js, the javascript import order etc

The cause of the issue was actually that I perform a synchronization on application launch, triggered by the first controller - to be accurate.

Controllers are not evaluated once ionicPlatform is ready :(.

So to skip that error, the synchronization has to be wrapped "on platform ready"

$ionicPlatform.ready(function(){
   syncService.sync();
});
 

Full width/height Background invisible

My application displays a beautiful background, unfortunately android displayed nothing.
This is because android doesn't seem to enjoy a full CSS declaration...

Just change the declaration and everything becomes alright.
/* fail */
.scroll-content{
    background: url("../img/background.png") no-repeat scroll center center / 100% 100% transparent
}
/* success */
.scroll-content{
    background-image: url("../img/background.png");
    background-repeat: no-repeat;
    background-size: 100% 100%;
}

Dates gap

The dates displayed by the application are transmitted by the server as ISO 8601, easily converted through moment.js.

Except the timezone in my device was not the same one than my desktop browser.
Be sure to set this correctly.

Ionic application reboot after a camera call

A though error once again : on corodova camera callback, the application reboot and becomes "ko".

This is a configuration of the device. In your device settings, just uncheck the "do not keep activities" configuration. Just do it now.

Clarification on grid system

I had some issues by not clearly declaring the "col" CSS class on "columns".

Instead of :

...
Declare :

...

Tabs are top instead of bottom

Something weird : tabs, that are displayed bottom on desktop, moved to top in android!

Add this to your angularjs .config function  :


$ionicConfigProvider.tabs.position("bottom");//force bottom
$ionicConfigProvider.tabs.style("standard"); //force same look and feel

Header view title not centered !

Similar issue.
Add this to your angularjs .config function  :


$ionicConfigProvider.navBar.alignTitle('center');

4 mai 2015

Spring Boot your Java batch

What I hate with batch development is the packaging phase.
Where Java EE normalizes everything, batches have been totally forgotten.

As often, Spring comes with a solution and makes our life easier with Spring Boot.

A revolution.

It's "magic".

1. Your batch

Develop your batch, as usual. Using Spring Batch framework is 'not' a requirement. It simply eases the integration and the configuration after packaging.
I personnally rely on maven. Just add the following lines in you pom.xml

 org.springframework.boot
 spring-boot-starter-batch
 1.2.3.RELEASE
 
  
   org.springframework.boot
   spring-boot-starter-logging
  
 



 
  
   org.springframework.boot
   spring-boot-maven-plugin
   1.2.3.RELEASE
   
    
     
      repackage
     
    
   
  
 

Running mvn package now create 2 jar :
- .jar.original -> the jar before Spring Boot complete it
- .jar -> the runnable fat jar built by Spring Boot

You can run it using the command :
java -jar myfatjar.jar


2. If your batch is developed with Spring

Name your configuration file application.properties.
No need to load it by Spring configuration. This file name is a "keyword".
More details about this "trick" in pitfall section.

3. Write you "main" class

Just write it the following way.
Notice the Spring loading. You can provide a Configuration Bean or a XML file. Depends on your habits.
@SpringBootApplication
public class HelloApp {
    public static void main(String[] args) {
        SpringApplication.run(new Object[]{HelloApp.class, "spring-boot.xml"}, args);
    }
}

4. Run it with external configuration

Externalizing the configuration is not an option for production, it's a requirement.

Run your batch with the following arguments allow you overwrite any configuration present in your application.properties.

... --spring.config.location=file:conf/file1.properties,file:conf/file2.conf ...

5. And external logging

To externalize the logging (log4j.xml file in this example), this requires a bit of additionnal configuration.
As you may have noticed, spring-boot-starter-logging has been excluded from the pom.xml.
The reason is simple : the default transitive dependency run with slf4j, whereas I work with log4j.

Add this dependency instead :

...

 org.springframework.boot
 spring-boot-starter-log4j
 1.2.3.RELEASE

...

Reference the log4j file as an argument on batch startup command line.

... --logging.config=file:conf/log4j.xml 

6. Final packaging and runtime configuration

Finally, we end up with the following run command line (Windows style) :

cd %~dp0
java -jar myfatjar.jar --spring.config.location=file:conf/file1.properties,file:conf/file2.conf --logging.config=file:conf/log4j.xml


And the following production file structure :

7. Pitfall

I had some issues with overriding property file loaded in my Spring configuration.
The internal jar configuration took precedence over configuration provided as arguments during launch.
I don't think this loading order is logical, I may have missed something... I fell back naming my configuration file application.properties
Fourni par Blogger.