Wednesday, March 28, 2018

Kotlin and JUnit 5 @BeforeAll

Introduction

In Kotlin, classes do not have static methods. A Java equivalent semantic can be provided using the concept of a companion object though. This post will go into details of what it takes to support a JUnit 5 @BeforeAll and @AfterAll annotation which depend on the presence of a static methods in test classes.


BeforeAll and AfterAll in Java

Junit 5 @BeforeAll annotated methods are executed before all tests and @AfterAll is exected after all tests. These annotations are expected to be applied to static methods:

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Junit5BeforeAllTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(Junit5BeforeAllTest.class);
    
    @BeforeAll
    static void beforeAll() {
        LOGGER.info("beforeAll called");    
    }
    
    @Test
    public void aTest1() {
        LOGGER.info("aTest1 called");
        LOGGER.info(this.toString());        
    }
    
    @Test
    public void aTest2() {
        LOGGER.info("aTest2 called");
        LOGGER.info(this.toString());
    }
    
    @AfterAll
    static void afterAll() {
        LOGGER.info("afterAll called");        
    }
}

A rough flow is - the JUnit platform calls the "@BeforeAll" annotated methods, then for each test it creates an instance of the test class and invokes the test. After all tests are executed, the "@AfterAll" annotated static methods are called, this is borne out by the logs, see how the instance ids(from toString() of Object) is different:

2018-03-28 17:22:03.618  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : beforeAll called
2018-03-28 17:22:03.652  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : aTest1 called
2018-03-28 17:22:03.653  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : com.pivotalservices.cookbook.Junit5BeforeAllTest@7bc1a03d
2018-03-28 17:22:03.663  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : aTest2 called
2018-03-28 17:22:03.664  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : com.pivotalservices.cookbook.Junit5BeforeAllTest@6591f517
2018-03-28 17:22:03.669  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : afterAll called



This default lifecycle of a JUnit 5 test can be changed by an annotation though if the test class is annotated the following way:

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class Junit5BeforeAllTest {
....
}

The advantage now is that the @BeforeAll and @AfterAll annotations can be placed on non-static methods, as the JUnit 5 platform can guarantee that these methods are exactly once before ALL tests. The catch though is that any instance-level state will not be reset before each test.

BeforeAll and AfterAll in Kotlin

So how does this translate to Kotlin -
For the default case of a new test instance per test, an equivalent Kotlin test code looks like this:

import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.slf4j.LoggerFactory

class Junit5BeforeAllKotlinTest {

    @Test
    fun aTest1() {
        LOGGER.info("aTest1 called")
        LOGGER.info(this.toString())
    }

    @Test
    fun aTest2() {
        LOGGER.info("aTest2 called")
        LOGGER.info(this.toString())
    }

    companion object {
        private val LOGGER = LoggerFactory.getLogger(Junit5BeforeAllTest::class.java)


        @BeforeAll
        @JvmStatic
        internal fun beforeAll() {
            LOGGER.info("beforeAll called")
        }

        @AfterAll
        @JvmStatic
        internal fun afterAll() {
            LOGGER.info("afterAll called")
        }
    }
}

A Kotlin companion object with methods annotated with @JvmStatic does the job.

Simpler is the case where the lifecycle is modified:

import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.slf4j.LoggerFactory

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Junit5BeforeAllKotlinTest {

    private val LOGGER = LoggerFactory.getLogger(Junit5BeforeAllTest::class.java)

    @BeforeAll
    internal fun beforeAll() {
        LOGGER.info("beforeAll called")
    }

    @Test
    fun aTest1() {
        LOGGER.info("aTest1 called")
        LOGGER.info(this.toString())
    }

    @Test
    fun aTest2() {
        LOGGER.info("aTest2 called")
        LOGGER.info(this.toString())
    }


    @AfterAll
    internal fun afterAll() {
        LOGGER.info("afterAll called")
    }
}


My personal preference is for the companion object approach as I like the idea of a deterministic state of the test instance before the test method is executed. Another advantage of the approach is with Spring Boot based tests where you want Spring to act on the test instance (inject dependencies, resolve properties etc) only after @BeforeAll annotated method is called, to make this more concrete consider the following example:

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.junit.jupiter.SpringExtension


@ExtendWith(SpringExtension::class)
@SpringBootTest
class BeforeAllSampleTest {

    @Value("\${some.key}")
    private lateinit var someKey: String

    
    companion object {
        @BeforeAll
        @JvmStatic
        fun beforeClass() {
            System.setProperty("some.key", "some-value")
        }

        @AfterAll
        @JvmStatic
        fun afterClass() {
            System.clearProperty("some.key")
        }
    }

    @Test
    fun testValidateProperties() {
        assertThat(someKey).isEqualTo("some-value")
    }

    @Configuration
    class SpringConfig
}

This kind of a test will not work at all if the lifecycle were changed to "@TestInstance(TestInstance.Lifecycle.PER_CLASS)"

Correction
Per comments from the one and only S├ębastien Deleuze, the previous test can be simplified by injecting in dependencies and properties via constructor injection, so the test can be re-written as:

@ExtendWith(SpringExtension::class)
@SpringBootTest
class BeforeAllSampleTest(@Value("\${some.key}") val someKey: String) {
 
    companion object {
        @BeforeAll
        @JvmStatic
        fun beforeClass() {
            System.setProperty("some.key", "some-value")
        }
 
        @AfterAll
        @JvmStatic
        fun afterClass() {
            System.clearProperty("some.key")
        }
    }
 
    @Test
    fun testValidateProperties() {
        assertThat(someKey).isEqualTo("some-value")
    }
 
    @Configuration
    class SpringConfig
}

Reference

This stackoverflow answer was instrumental in my understanding of the nuances of JUnit 5 with Kotlin.

Tuesday, March 6, 2018

Spring Boot 2 native approach to SSO with OAuth 2/OpenID Connect

This post is the final part of a 3 post series exploring ways to enable SSO with an OAuth2 provider for Spring Boot 2 based applications. The 3 posts are:

1. Ways to bootstrap an OpenID Connect compliant OAuth2 Authorization Server/OpenID Provider
2. Legacy Spring Boot/Spring 5 approach to integrating with an OAuth2 Authorization Server/OpenID Provider
3. Newer Spring Boot 2/Spring 5 approach to integrating with an OAuth2 Authorization Server/OpenID Connect Provider - this post

This post will explore the shiny new way to enable SSO for a Spring Boot 2 application using the native OAuth2 support in Spring Security.

The post again assumes that everything described in the first post is completed.


Spring Boot 2 Auto-configuration


Spring Boot 2 provides an auto-configuration for native OAuth2 support in Spring Security ( see class org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration).
The auto-configuration is activated by the presence of "spring-security-oauth2-client" library available via the following gradle coordinates:

compile "org.springframework.security:spring-security-oauth2-client"

This auto-configuration works off a set of properties, for the UAA Identity provider that has been started up, the set of properties are the following:

uaa-base-url: http://localhost:8080/uaa

spring:
  security:
    oauth2:
      client:
        registration:
          uaa:
            client-id: client1
            client-secret: client1
            authorizationGrantType: authorization_code
            redirect_uri_template: "{baseUrl}/login/oauth2/code/{registrationId}"
            scope: resource.read,resource.write,openid,profile
            clientName: oauth2-sample-client
        provider:
          uaa:
            token-uri: ${uaa-base-url}/oauth/token
            authorization-uri: ${uaa-base-url}/oauth/authorize
            user-info-uri: ${uaa-base-url}/userinfo
            jwk-set-uri: ${uaa-base-url}/token_keys
            userNameAttribute: user_name

If I were to depend on Spring Boot 2 auto-configuration support for native OAuth2 support to do its magic and were to start the application up, I would be presented with this page on accessing the application:


Note that this login page is a default page created by Spring Security OAuth2 and by default presents the list of registrations.

Clicking on "oauth2-sample-client" presents the login page of the Identity provider, UAA in this instance:

For an OpenID Connect based flow, applications are issued an ID Token along with an Access Token which I am decoding and presenting on a page:



Customizations

One of the quick customizations that I want to make is to redirect to UAA on access of any secured page specified via a "/secured" uri pattern, the following is a set of configuration that should enable this:

package sample.oauth2.config

import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.builders.WebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

@Configuration
class OAuth2SecurityConfig : WebSecurityConfigurerAdapter() {
    override fun configure(web: WebSecurity) {
        super.configure(web)
        web.ignoring()
                .mvcMatchers(
                        "/favicon.ico",
                        "/webjars/**",
                        "/css/**"
                )
    }

    override fun configure(http: HttpSecurity) {
        http.csrf().disable()

        http.authorizeRequests()
                .antMatchers("/secured/**")
                    .authenticated()
                .antMatchers("/", "/custom_login")
                    .permitAll()
                .anyRequest()
                    .authenticated()
                .and()
                    .oauth2Login()
                    .loginPage("/custom_login")
    }
}

See the "/custom_login" being set as the URI above, which in turn simply hands over control to OAuth2 controlled endpoints which know to set the appropriate parameters and redirect to UAA:

@Controller
class LoginController {

    @RequestMapping("/custom_login")
    fun loginPage(): String {
        return "redirect:/oauth2/authorization/uaa"
    }
}



This concludes the exploration of native OAuth2 support in Spring Boo2 applications.

All of the samples are available in my github repo - https://github.com/bijukunjummen/oauth2-boot2


The following references were helpful in understanding the OAuth2 support:

1. Spring Security Documentation - https://docs.spring.io/spring-security/site/docs/current/reference/html/
2. Joe Grandja's Spring One Platform 2017 Presentation - https://www.youtube.com/watch?v=WhrOCurxFWU