Spring Boot should autoconfigure RSocket Security

  • It should automatically apply @EnableRSocketSecurity
  • Conditionally create a MapReactiveUserDetailsService with secure random password as it does for WebFlux
  • It should automatically apply Spring Security to ServerRSocketFactory
@Bean
ServerRSocketFactoryCustomizer springSecurityRSocketSecurity(SecuritySocketAcceptorInterceptor interceptor) {
    return builder -> builder.addSocketAcceptorPlugin(interceptor);
}

Comment From: rwinch

Another thing that would be nice for this is if it configured the AuthenticationPrincipalArgumentResolver and the CurrentSecurityContextArgumentResolver. It would look something like this:

@Bean
RSocketMessageHandler messageHandler(RSocketStrategies rSocketStrategies, BeanFactory beanFactory) {
    BeanFactoryResolver resolver = new BeanFactoryResolver(beanFactory);
    AuthenticationPrincipalArgumentResolver principal = new AuthenticationPrincipalArgumentResolver();
    principal.setBeanResolver(resolver);
    CurrentSecurityContextArgumentResolver context = new CurrentSecurityContextArgumentResolver();
    context.setBeanResolver(resolver);
    RSocketMessageHandler messageHandler = new RSocketMessageHandler();
    messageHandler.setRSocketStrategies(rSocketStrategies);
    messageHandler.setRouteMatcher(new PathPatternRouteMatcher());
    ArgumentResolverConfigurer args = messageHandler
                            .getArgumentResolverConfigurer();
    args.addCustomResolver(principal);
    args.addCustomResolver(context);
    return messageHandler;
}

Note that RSocketMessageHandler is currently created in RSocketMessagingAutoConfiguration

Comment From: bclozel

@rwinch @rstoyanchev about that last point, there is a way to achieve that in Spring Boot, but it feels a bit unnatural to have that as an opinion expressed for the developers, rather than a use case that could be supported by the configuration infrastructure.

Right now we're supporting most of the configuration through RSocketStrategies but not the argument resolvers. In WebFlux, we have a method for that: WebFluxConfigurer.configureArgumentResolvers(ArgumentResolverConfigurer configurer).

Is there something to be done at the Spring Framework level that would help here? The alternative in Spring Boot would be to gather HandlerMethodArgumentResolver beans and add them to the RSocketMessageHandler we're creating. Note that this RSocketMessageHandler bean is marked as @ConditionalOnMissingBean so this would not work for developers defining their own.

Comment From: rstoyanchev

It doesn't seem so opinionated to me to have those resolvers applied but on the second point about @ConditoinalOnMissingBean it would indeed be unfortunate to to lose those defaults. Could this be done as part of the @EnableRSocketSecurity setup?

Comment From: bclozel

On the Spring Framework side, we're telling developers to create an RSocketMessageHandler as a bean; this setup is done automatically for Spring Boot users.

I think this could be a two step process: 1. Spring Security should provide those argument resolvers and make it easier for non Spring Boot users to set those up against a custom RSocketMessageHandler 2. Spring Boot could then use this mechanism to auto-configure those resolvers on the RSocketMessageHandler, maybe via another customizer extension point.

Comment From: rwinch

We can expose the argument resolvers as Beans, but this won't be available until Security 5.2.1 since 5.2.0 is out. Perhaps for that reason, I should create another ticket?

Comment From: rwinch

@rstoyanchev I'd like to add...I second @bclozel question:

Is there something to be done at the Spring Framework level that would help here?

In MVC Framework provides WebMvcConfigurer.addArgumentResolvers which allows configuring argument resolvers. In WebFlux Framework provides WebFluxConfigurer.configureArgumentResolvers which allows configuring argument resolvers. For that reason, it is quite surprising that framework doesn't provide something similar for messaging.

Comment From: rstoyanchev

Actually what I had in mind was that Spring Security would use a preProcess hook to find the RSocketMessageHandler bean and access its argumentResolverConfigurer property, and insert those resolves.

The argument resolver options in WebMvc and WebFlux go along with the @Enable annotations, which we don't have for RSocket. I don't think we should keep adding options to RSocketStrategies which currently has some responder-only options but at least is neutral for annotation vs functional programming models.

Comment From: rstoyanchev

As for adding @Enable for RSocket, that is an option, although it would probably be config for 1 bean, or 2 at most. It seemed avoidable at the time.

Comment From: mbhave

Thanks for your inputs, everyone. I think this is a separate issue to the one originally described. From the discussion above, it sounds like it's either something that needs to change in Spring Framework or Spring Security. We can create another Spring Boot issue based on what the outcome is.

Comment From: rwinch

Thanks @mbhave I have moved the discussion to https://github.com/spring-projects/spring-framework/issues/23751

Comment From: joshlong

Hi,

Having resolvers would be a very nice addition in Spring Framework. But I only want to use @AuthenticatedPrincipal to inject the currently authenticated principal into my @MessageMapping-annotated handlers. It's what I would expect when using Spring MVC or WebFlux. This just works in those frameworks.

```package com.example.demo;

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication public class DemoApplication {

@RestController
static class HelloController {

    @GetMapping("/hello")
    String hello(@AuthenticationPrincipal UserDetails userDetails) {
        return "hello, " + userDetails.getUsername() + "!";
    }
}

@Bean
MapReactiveUserDetailsService authentication() {
    var jlong = User.withDefaultPasswordEncoder().username("jlong").password("pw").roles("USER").build();
    return new MapReactiveUserDetailsService(jlong);
}

public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
}

}




It's also preferable to using `ReactiveSecurityContextHolder.getContext().map(auth -> ...)`. 

So, until the more elegant custom resolvers and `@EnableRSocket` machinery lands in Spring Framework, could we please add a bean to the Spring Boot autoconfiguration, like the following:

@Bean RSocketMessageHandler messageHandler(RSocketStrategies strategies) { var rmh = new RSocketMessageHandler(); rmh.getArgumentResolverConfigurer().addCustomResolver(new AuthenticationPrincipalArgumentResolver()); rmh.setRSocketStrategies(strategies); return rmh; } ```

As far as I understand it, nothing would break for the user, and - in addition, this would allow Spring Security to work better? And, if we're worried, I guess users could opt-out (but I would argue it should be enabled by default) with some sort of configuration property?

Comment From: joshlong

Oh. It seems RSocketMessageHandler is a singleton - when I override it, I override the default one provided by Spring Boot. So, maybe Boot could expose a RSocketMessageHandlerCustomizer so that everyone gets a bite at the apple?

Comment From: wilkinsona

Hi, @joshlong. Can you please open a new issue for the above so that it doesn’t get lost?

Comment From: joshlong

I just opened https://github.com/spring-projects/spring-boot/issues/20303 - thanks for your time