mockmvc unable to retrieve headers set by WebSecurityConfigurerAdapter - spring-boot

I created a commons library with the intent of injecting headers into the response of every API call. I updated the WebSecurityConfigurerAdaptor and wrote a mockmvc test case to test/assert those headers. However I observed that mockmvc is unable to find/assert those headers in the test even though WebSecurityConfigurerAdaptor injects those headers. Appreciate if you can share your perspective upon any issues you see with this approach.
Below is the WebSecurityConfigurerAdapter snippet:
#Slf4j
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SoaWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Getter
#Setter
private RestTemplateBuilder restTemplateBuilder;
#Override
protected void configure(final HttpSecurity http) throws Exception {
// Disable caching
http.headers().cacheControl();
// default deny framing/embedding
http.headers().frameOptions().deny();
headerConfigurer(http.headers());
}
protected void headerConfigurer(HeadersConfigurer<HttpSecurity> headersConfigurer) {
log.info("SOACustomWebSecurityConfig: header config invoked");
headersConfigurer.addHeaderWriter(
// (HeaderWriter) new StaticHeadersWriter(Arrays.asList(new Header("Access-control-Allow-Origin", "*"),
// new Header("strict-transport-security", "max-age=31536000; includeSubdomains"))));
(HeaderWriter) new StaticHeadersWriter("strict-transport-security",
"max-age=31536000; includeSubdomains"));
}
Below is the corresponding test case:
private void testHeadersPresence(String resourceURl, String origin) throws Exception {
mvc.perform(options(resourceURl).header("Access-Control-Request-Method", "GET").header("Origin", origin))
.andExpect(header().string("strict-transport-security", "max-age=31536000; includeSubdomains"));
}
The test case fails with the error:
Expected "max-age=31536000; includeSubdomains" but was [null]
Note: the change is being made in a common library (a separate repo) which is included in multiple spring boot services (that offers APIs)
Any insight is much appreciated. Thank you

Related

Migration to Spring Boot (Security) 2 from 1.x

In a current migration of a project from Spring boot 1.X to 2.0.9 I am facing a hard time with the Spring Security module. In a first step I have to change the properties to access the datasources (to jdbc-url) and now that part seems to be working just fine, but now the security module seems to be not rightly attached.
So in properties I have tried to add the following:
spring.security.user.name=admin
spring.security.user.password=secret
But even if I comment it or dis-comment it, I face the same result, when I call the webservices, the dialog prompts asking for credentials:
The configuration of the authentication is the following:
class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
#Configuration
#EnableAuthorizationServer // [1]
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private DataSource oauthDataSource;
#Autowired
private PhrqlAuthenticationManager phrqlAuthenticationManager;
/**
* Config clientDetail storage
* #param endpoints
* #throws Exception
*/
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(phrqlAuthenticationManager);
endpoints.tokenStore(tokenStore);
// endpoints.tokenStore(tokenStore).pathMapping("/oauth/token", "/p/oauth/token");
}
#Override // [3]
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(oauthDataSource);
}
}
For some reason, I think that the autoconfig is not accessing to the name-pass, so then the client ask for credentials. Any help will be much appreciated. Thanks in advance!

How to include custom security interceptor in spring boot test

I want to do some end-to-end test for spring boot rest-api application. To achieve this im using spring mock mvc. But i can't get the 200 response because the rest api is using custom security interceptor to validate the token in request. Instead i keep getting 401 as a response. How to include this token validation in my test?
I've tried several configuration by including #ContextConfiguration(classes = {WebMvcConfig.class}) in my test class. WebMvcConfig is configuration class to register the interceptor.
This is my test file
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#SpringBootTest(classes = VeripalServiceApplication.class)
#TestPropertySource(locations="classpath:test.properties")
#Transactional
public class VeripalTextConfigurationTest {
#Autowired
private MockMvc mockMvc;
#Test
public void happpyPath_thenReturns200() throws Exception {
String jsonBody = "some json body";
String endPoint = "/end_point_to_my_api";
HttpHeaders headers = new HttpHeaders();
headers.add("token", "this_is_my_token");
headers.setContentType(aplication/json);
/** Hit the API */
mockMvc.perform(post(endPoint)
.headers(httpHeaders)
.content(jsonBody)
)
.andExpect(status().isOk()).andDo(print());
}
}
And this is the #Configuration
#Configuration
#EnableScheduling
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private ConsumerService consumerService;
#Autowired
private EndpointService endpointService;
#Autowired
private ConsumerConfigurationService consumerConfigurationService;
#Autowired
private AccessLimitService accessLimitService;
#Autowired
private ConfigurationHistoryService configurationHistoryService;
#Autowired
private LimitCarryOverService limitCarryOverService;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor(consumerService, endpointService, consumerConfigurationService, accessLimitService, configurationHistoryService, limitCarryOverService));
}
}
And this is my Interceptor class
public class Interceptor implements HandlerInterceptor {
// some code here ...
}
you need to have a clear picture of request life-cycle in Servlet API and Spring Security framework.
This article might help you to understand this flow http://blog.florian-hopf.de/2017/08/spring-security.html
So, I'm pretty sure, you have an issue in authentication filters, thus you can resolve it in couple ways:
Disable security, for example by using #AutoConfigureMockMvc(secure = false)
Or you need to mock some places (AuthenticationProvider, UserDetailsService, etc) where you can provide Authentication object
Or, it also might help, try to play with #WithMockUser
.
Related posts:
Spring Test & Security: How to mock authentication?
V2: use IoC + Mockito, e.g. stub it for unit tests. I don't see how your code are written, so I believe a snippet below might help you.
// #Import({MyAuthCustomInterceptor.class}) // eq to #Component/#Service to create a bean
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
MyAuthCustomInterceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
}
public class VeripalTextConfigurationTest {
#MockBean
MyAuthCustomInterceptor interceptor;
#SetUp
public void setup(){
Mockito.when(interceptor.preHandle(...)).thenReturn(true);
}
}

Is #Order really needed in Spring Boot 2.0.4 OAuth server?

I have this little OAuth server class and I am using Spring Boot 2.0.4 and the spring-security-oauth2-autoconfigure 2.0.0.RELEASE dependency :
#RestController
#SpringBootApplication
#EnableAuthorizationServer
#Order(200) // really needed ?
public class MyOAuthServerApplication extends WebSecurityConfigurerAdapter {
#RequestMapping({ "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
// #formatter:on
}
}
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails mary =
User.withUsername("mary")
.password("{bcrypt}$2a$10$B3NUb0x.MYnSfx7WJItrvO/ymEQwLCKQNehmCuA8keL1uTyHizI0i")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(mary);
}
public static void main(String[] args) {
SpringApplication.run(MyOAuthServerApplication.class, args);
}
}
This seems to work well with and without the #Order(200) annotation.
So is this annotation really needed ?
The Order annotation is used to define the injection precedence.
Read more her: https://www.baeldung.com/spring-order
In your case it's because of the EnableResourceServer annotation. And you must keep the annotation.
From the doc:
The #EnableResourceServer annotation creates a security filter with
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER-1) by default, so by
moving the main application security to
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) we ensure that the
rule for "/me" takes precedence.
Please find the tutorial here: https://spring.io/guides/tutorials/spring-boot-oauth2/
You need it if you have another WebSecurityConfigurerAdapter configuration.
For example if you allow users to login via login form with a different UserDetailsService and so on. Then this should be tried before your oauth authentification and thus needs a lower order, for example #Order(199).
Another example would be different configuration for your API access.
If you don't have any other configuration, then you don't need to set the order.
Setting the order to 200 also seems to be an arbitrary value, that should simply be higher then the others and thus executed last.

Disable Spring Boot Security #Secured at runtime [duplicate]

Is there a way I can disable the global method security using the boolean securityEnabled from my config.properties? Any other approach?
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled=true)
#PropertySource("classpath:config.properties")
public class SecurityConfig
extends WebSecurityConfigurerAdapter {
#Value("${securityconfig.enabled}")
private boolean securityEnabled;
...
}
The easiest way to do this is:
Extract method security to its own class
Remove the securedEnabled attribute entirely
Override the customMethodSecurityMetadataSource method and return the result based on the configured value.
For example:
#EnableWebSecurity
#Configuration
#PropertySource("classpath:config.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}
#EnableGlobalMethodSecurity
#Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${securityconfig.enabled}")
private boolean securityEnabled;
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return securityEnabled ? new SecuredAnnotationSecurityMetadataSource() : null;
}
}
I've managed this by defining a Spring "securityDisabled" profile and conditionally applying security config based off that. I'm using Spring Boot 2.0.2. I believe this should work if not using Spring Boot and in previous versions of Spring Boot, but I have not tested. It's possible some tweaks may be required to property and class names because I know in Spring 2.0 some of that changed.
// In application.properties
spring.profiles.include=securityDisabled
Then my security config looks like this:
#Configuration
public class SecurityConfig {
// When the securityDisabled profile is applied the following configuration gets used
#Profile("securityDisabled")
#EnableWebSecurity
public class SecurityDisabledConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Configure http as needed from Spring Security defaults when
// NO security is desired
}
}
// When the securityDisabled profile is NOT applied the following configuration gets used
#Profile("!securityDisabled")
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityEnabledConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Configure http as needed from Spring Security defaults when
// security is desired
}
}
}
In Springboot2, a simple solution consists in replacing the security method interceptor by a dummy one when the security is off :
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${disableSecurity}")
private boolean disableSecurity;
public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
return disableSecurity ? new SimpleTraceInterceptor()
: super.methodSecurityInterceptor(methodSecurityMetadataSource);
}
}
Thanks to Rob Winch for the solution. For folks who would like to do something similar but with prePostEnabled i have tried and tested the below similar approach and works just fine.
#EnableGlobalMethodSecurity(securedEnabled = true)
#Configuration
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Value("${security.prePostEnabled}")
private boolean prePostEnabled;
#Autowired
private DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler;
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return prePostEnabled ? new PrePostAnnotationSecurityMetadataSource(new ExpressionBasedAnnotationAttributeFactory(defaultMethodSecurityExpressionHandler)) : null ;
}}
EDIT: In addition to above i realized it is required to add following beans to the class. The below will help using the expression based pre invocation checks along with avoiding "ROLE_" prefix that is defaulted in all the handlers
protected AccessDecisionManager accessDecisionManager() {
AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
//This is required in order to allow expression based Voter to allow access
accessDecisionManager.getDecisionVoters()
.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
//Remove the ROLE_ prefix from RoleVoter for #Secured and hasRole checks on methods
accessDecisionManager.getDecisionVoters().stream()
.filter(RoleVoter.class::isInstance)
.map(RoleVoter.class::cast)
.forEach(it -> it.setRolePrefix(""));
return accessDecisionManager;
}
/**
* Allow skip ROLE_ when check permission using #PreAuthorize, like:
* #PreAuthorize("hasAnyRole('USER', 'SYSTEM_ADMIN')")
* Added all the Beans
*/
#Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
defaultMethodSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultMethodSecurityExpressionHandler;
}

How do I stop spring FilterRegistrationBean from registering my filter on /*

I am using spring-boot and I have declared a filter like so:
public static class ZeroLeggedOAuthProviderProcessingFilter extends ProtectedResourceProcessingFilter {
ZeroLeggedOAuthProviderProcessingFilter(LTIConsumerDetailsService ltiConsumerDetailsService, LTIOAuthNonceServices ltioAuthNonceServices, OAuthProcessingFilterEntryPoint oAuthProcessingFilterEntryPoint, OAuthAuthenticationHandler oAuthAuthenticationHandler, OAuthProviderTokenServices oauthProviderTokenServices) {
super();
log.info("CONSTRUCT Zero Legged OAuth provider");
setAuthenticationEntryPoint(oAuthProcessingFilterEntryPoint);
setAuthHandler(oAuthAuthenticationHandler);
setConsumerDetailsService(ltiConsumerDetailsService);
setNonceServices(ltioAuthNonceServices);
setTokenServices(oauthProviderTokenServices);
//setIgnoreMissingCredentials(false); // die if OAuth params are not included
}
}
#Bean(name = "zeroLeggedOAuthProviderProcessingFilter")
public ZeroLeggedOAuthProviderProcessingFilter oauthProviderProcessingFilter() {
return new ZeroLeggedOAuthProviderProcessingFilter(oauthConsumerDetailsService, oauthNonceServices, oauthAuthenticationEntryPoint(), oauthAuthenticationHandler, oauthProviderTokenServices());
}
I apply that filter to a single path like so (which seems to work):
#Configuration
#Order(1) // HIGHEST
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
ZeroLeggedOAuthProviderProcessingFilter oauthProviderProcessingFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
// filters must be ordered: see http://docs.spring.io/spring-security/site/docs/3.2.0.RELEASE/apidocs/org/springframework/security/config/annotation/web/HttpSecurityBuilder.html#addFilter%28javax.servlet.Filter%29
http.antMatcher("/oauth/**")
.addFilterBefore(oauthProviderProcessingFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().hasRole("OAUTH");
}
}
However, it ends up being applied to all paths and I think it is because of some automatic registration that is happening (based on this in the logs).
o.s.b.c.embedded.FilterRegistrationBean : Mapping filter:
'zeroLeggedOAuthProviderProcessingFilter' to: [/*]
Is there a way to stop my filter from being automatically registered?
I ended up solving it this way instead (since the FilterRegistrationBean caused other problems).
My filter definition:
public static class ZeroLeggedOAuthProviderProcessingFilter extends ProtectedResourceProcessingFilter {
ZeroLeggedOAuthProviderProcessingFilter(LTIConsumerDetailsService ltiConsumerDetailsService, LTIOAuthNonceServices ltioAuthNonceServices, OAuthProcessingFilterEntryPoint oAuthProcessingFilterEntryPoint, OAuthAuthenticationHandler oAuthAuthenticationHandler, OAuthProviderTokenServices oauthProviderTokenServices) {
super();
log.info("CONSTRUCT Zero Legged OAuth provider");
setAuthenticationEntryPoint(oAuthProcessingFilterEntryPoint);
setAuthHandler(oAuthAuthenticationHandler);
setConsumerDetailsService(ltiConsumerDetailsService);
setNonceServices(ltioAuthNonceServices);
setTokenServices(oauthProviderTokenServices);
//setIgnoreMissingCredentials(false); // die if OAuth params are not included
}
}
And my http config which applies that filter to the oauth path only:
#Configuration
#Order(1) // HIGHEST
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private ZeroLeggedOAuthProviderProcessingFilter zeroLeggedOAuthProviderProcessingFilter;
#Autowired
LTIConsumerDetailsService oauthConsumerDetailsService;
#Autowired
LTIOAuthNonceServices oauthNonceServices;
#Autowired
OAuthAuthenticationHandler oauthAuthenticationHandler;
#Autowired
OAuthProcessingFilterEntryPoint oauthProcessingFilterEntryPoint;
#Autowired
OAuthProviderTokenServices oauthProviderTokenServices;
#PostConstruct
public void init() {
zeroLeggedOAuthProviderProcessingFilter = new ZeroLeggedOAuthProviderProcessingFilter(oauthConsumerDetailsService, oauthNonceServices, oauthProcessingFilterEntryPoint, oauthAuthenticationHandler, oauthProviderTokenServices);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// added filters must be ordered: see http://docs.spring.io/spring-security/site/docs/3.2.0.RELEASE/apidocs/org/springframework/security/config/annotation/web/HttpSecurityBuilder.html#addFilter%28javax.servlet.Filter%29
http.antMatcher("/oauth/**")
.addFilterBefore(zeroLeggedOAuthProviderProcessingFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().hasRole("OAUTH");
}
}
This also solved another issue I was having where my filter was appearing before the security filters (which caused other issues) so I think this is the best solution. I wish there was some mechanism to tell spring to ignore a filter in the global filters listing as well but this works in the meantime since that does not exist.

Resources