Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how to use this if Keycloak is on the classpath #2

Closed
wimdeblauwe opened this issue Apr 6, 2020 · 14 comments
Closed

Document how to use this if Keycloak is on the classpath #2

wimdeblauwe opened this issue Apr 6, 2020 · 14 comments

Comments

@wimdeblauwe
Copy link

wimdeblauwe commented Apr 6, 2020

Thank you for this project. It is just what I needed for my @WebMvcTest tests in my Spring Boot project. I am using Spring Boot 2.2.6 with keycloak-spring-boot-starter 9.0.0.

After adding:

        <dependency>
            <groupId>com.c4-soft.springaddons</groupId>
            <artifactId>spring-security-oauth2-test-webmvc-addons</artifactId>
            <version>2.0.0</version>
            <scope>test</scope>
        </dependency>

to my project, I also had to do the following things:

  1. Create a custom meta-annotation for all my webmvc tests:
@Retention(RetentionPolicy.RUNTIME)
@WebMvcTest
@ContextConfiguration(classes = MyProjectRestControllerTestConfiguration.class)
@ActiveProfiles("webmvc-test")
public @interface MyProjectRestControllerTest {
    /**
     * @see WebMvcTest#value
     */
    @AliasFor(annotation = WebMvcTest.class, attribute = "value")
    Class<?>[] value() default {};

    /**
     * @see WebMvcTest#controllers
     */
    @AliasFor(annotation = WebMvcTest.class, attribute = "controllers")
    Class<?>[] controllers() default {};

}
  1. I created a custom @TestConfiguration for my test:
@TestConfiguration
@Import({KeycloakAutoConfiguration.class, WebSecurityConfiguration.class})
public class MyProjectRestControllerTestConfiguration {
    @Bean
    public KeycloakSpringBootConfigResolver keycloakSpringBootConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

Where WebSecurityConfiguration is my actual production security config class:

@KeycloakConfiguration
public class WebSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder builder) {
        KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider();
        provider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        builder.authenticationProvider(provider);
    }

    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
            .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
            .antMatchers("/actuator/info", "/actuator/health").permitAll()
            .anyRequest()
            .hasRole("user");
    }
}
  1. Next, I added application-webmvc-test.properties in src/test/resources:
# Properties are here for proper startup. Keycloak is not actually used in @WebMvcTest tests
keycloak.auth-server-url=http://localhost:8180/auth
keycloak.realm=pegus-digital-test
keycloak.resource=springboot-app
keycloak.public-client=true
keycloak.principal-attribute=preferred_username
  1. Finally my test self:
@MyProjectRestControllerTest(UserRestController.class)
class UserRestControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testOwnUserDetails() throws Exception {
        KeycloakAuthRequestPostProcessor keycloakAuthRequestPostProcessor = new KeycloakAuthRequestPostProcessor()
                .roles("user", "admin")
                .name("wim.deblauwe@example.com")
                .idToken(idToken -> {
                    idToken.setSubject("aac53545-5d5b-4e1f-8c94-0c593d22b5a2");
                    idToken.setGivenName("Wim");
                    idToken.setFamilyName("Deblauwe");
                });
        mockMvc.perform(get("/api/users/me")
                                .with(keycloakAuthRequestPostProcessor))
               .andExpect(status().isOk())
               .andExpect(jsonPath("authorizationServerUserId").value("aac53545-5d5b-4e1f-8c94-0c593d22b5a2"))
               .andExpect(jsonPath("firstName").value("Wim"))
               .andExpect(jsonPath("lastName").value("Deblauwe"))
               .andExpect(jsonPath("email").value("wim.deblauwe@example.com"))
               .andExpect(jsonPath("userRole").value("ADMIN"))
        ;
    }

}

Might be good to add something similar to the documentation, as most people will have Keycloak on the classpath in their project (unlike the example in your repo currently).

@ch4mpy
Copy link
Owner

ch4mpy commented Apr 27, 2020

@wimdeblauwe I'm very sorry, I didn't see your issue until today (wasn't notified).
I'll create an additional project dedicated to keycloak demos.

I'm using Keycloak as authentication server, but not KeycloakAuthenticationToken in spring-security on resource servers side any more (using my OidcIdAuthenticationToken), so more testing / demos would be useful

@ch4mpy
Copy link
Owner

ch4mpy commented Apr 28, 2020

@wimdeblauwe I initiated the sample project with Keycloak libs for resource-server with spring-boot and spring-security: https://github.com/ch4mpy/spring-addons/tree/master/keycloak-sample, but @Controller test configuration is broken (AdapterConfig not injected in KeycloakSpringBootConfigResolver) and I'm pretty busy on another project.

If you find an easy fix...

@pvannierop
Copy link

Really appreciated. I am struggling on how to setup test with Keycloak on the classpath atm. That is how I stumbled on you repo. I will look at your sample project and see whether this clarifies it for me.

@ch4mpy
Copy link
Owner

ch4mpy commented Apr 29, 2020

@pvannierop alternatively to having Keycloak on the classpath, you can have a look at this sample in the repo: https://github.com/ch4mpy/spring-addons/blob/master/spring-security-oauth2-test-webmvc-addons/src/test/java/com/c4_soft/springaddons/samples/webmvc/oidcid/OidcIdServletApp.java

I runs against a Keycloak authorization-server. This how I configure my micro-services.
If you cloned my repo, all you have to do is adapt application.properties to your Keycloak instance and "run as spring-boot-app" to see it in action with Postman or whatever

@pvannierop
Copy link

@ch4mpy I played around with your keycloak sample and I could reproduce your error. What I do not understand is that most tests of the GreetingControllerTest pass when not using the KeycloakConfig.class by changing:

@Import({ MockMvcSupport.class, ServletKeycloakAuthUnitTestingSupport.UnitTestConfig.class, KeycloakConfig.class })

into

@Import({ MockMvcSupport.class, ServletKeycloakAuthUnitTestingSupport.UnitTestConfig.class })

I had to make one trivial update to a test to make it succeed. Change:

content().string(String.format(GREETING, "ch4mpy", List.of("USER", "AUTHORIZED_PERSONNEL"))));

into

content().string(String.format(GREETING, "ch4mpy", List.of("AUTHORIZED_PERSONNEL", "USER"))));

alternatively to having Keycloak on the classpath, you can have a look at this sample in the repo:

So, you are proposing to use spring-security-oauth2 here instead of keycloak adapter, right? I have been thinking about this too. I will try when I have time tonight.

PS Sorry for perhaps not knowing obvious stuff. I work at a open-source company in the Netherlands (The Hyve), but I am a biologist by training. I am working hard to get up to speed on security in spring. Thnx again for your time.

@wimdeblauwe
Copy link
Author

@wimdeblauwe I initiated the sample project with Keycloak libs for resource-server with spring-boot and spring-security: https://github.com/ch4mpy/spring-addons/tree/master/keycloak-sample, but @Controller test configuration is broken (AdapterConfig not injected in KeycloakSpringBootConfigResolver) and I'm pretty busy on another project.

If you find an easy fix...

I had a quick look and it seems you are not importing org.keycloak.adapters.springboot.KeycloakAutoConfiguration in the test. I also find it strange that you have your main class in the tests folder and not in the main folder.

Having said that, things are still not working when I changed these things and I don't really understand why given that my production application does basically the same thing.

@ch4mpy
Copy link
Owner

ch4mpy commented Apr 29, 2020

it seems you are not importing org.keycloak.adapters.springboot.KeycloakAutoConfiguration in the test

I had tried but it didn't trigger proper Keycloak auto-configuration, which would probably be enough to solve the problem

I also find it strange that you have your main class in the tests folder and not in the main folder.

@wimdeblauwe this is a hack to keep produced jars small (not pollute ossrh with useless "fat jars"). The boot apps in this project are intended to run from within an IDE for experiments, not as java - jar ...

So, you are proposing to use spring-security-oauth2 here instead of keycloak adapter, right?

@pvannierop yes. I added more mvc samples to those already demonstrating that.
Some use JwtAuthenticationToken provided by Spring or OidcIdAuthenticationToken I wrote. Caveat: I moved it a bit since my last post to group components and unit tests (now easier to navigate between implementations and tests)

@ch4mpy
Copy link
Owner

ch4mpy commented Apr 29, 2020

@wimdeblauwe I downgraded Keycloak from 9.0.3 to 9.0.0 as you mentioned you were using, and got it working. Will dig why it is now broken tomorrow (almost midnight here)

@pvannierop
Copy link

@ch4mpy @wimdeblauwe Thnx for all your work. I'll test it later tonight.

I also would appreciate your view on why most tests succeeded when disabling the Keycloak integration (see my last post).

@ch4mpy
Copy link
Owner

ch4mpy commented Apr 29, 2020

Just moved Keycloak sample with other webmvc samples under https://github.com/ch4mpy/spring-addons/tree/master/spring-security-oauth2-test-webmvc-addons/src/test/java/com/c4_soft/springaddons/samples/webmvc

I also edited main and spring-security-oauth2-test-webmvc-addons READMEs

The issue with Keycloak 9.0.3 is not solved, but, IMO, this is a separate problem.

@wimdeblauwe may we close this issue?

@wimdeblauwe
Copy link
Author

Sure. Weird that 9.0.3 does not work.

@ch4mpy
Copy link
Owner

ch4mpy commented Apr 29, 2020

I'll try some investigation and maybe open a Keykloak ticket.

@ch4mpy ch4mpy closed this as completed Apr 29, 2020
@ch4mpy
Copy link
Owner

ch4mpy commented Apr 29, 2020

@pvannierop
Copy link

pvannierop commented Apr 30, 2020

@ch4mpy @wimdeblauwe

I have created controller tests similar to the sample project and all works fine!

However ..... when running integration tests that use the full application context I found that one change proposed by @wimdeblauwe is essential which is moving the bean definition of KeyloakSpringBootConfigResolver to a class separate from the projects central KeycloakConfig.

This change involves creating a dedicated class that imports the central config and annotating the bean witrh @primary, like so:

@TestConfiguration
@Import({KeycloakAutoConfiguration.class, KeycloakConfig.class})
public class KeycloakTestConfiguration {
    @Bean
    @Primary
    public KeycloakSpringBootConfigResolver testKeycloakSpringBootConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

If I do this both unit and integration tests run fine. I have no clue why this is any different from the unit level tests.

It would be nice to somehow document this or integrate this into the sample project, just to prevent this problem. B.t.w. for reference the exception thrown when not implementing this is:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'keycloakConfigResolver': Requested bean is currently in creation: Is there an unresolvable circular reference?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants