diff --git a/cerberus-web/src/main/java/com/nike/cerberus/security/WebSecurityConfiguration.java b/cerberus-web/src/main/java/com/nike/cerberus/security/WebSecurityConfiguration.java index db738e494..95d8a2412 100644 --- a/cerberus-web/src/main/java/com/nike/cerberus/security/WebSecurityConfiguration.java +++ b/cerberus-web/src/main/java/com/nike/cerberus/security/WebSecurityConfiguration.java @@ -16,13 +16,18 @@ package com.nike.cerberus.security; +import static org.apache.commons.lang3.StringUtils.isBlank; + import com.nike.cerberus.service.AuthTokenService; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.List; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -35,6 +40,9 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; @Slf4j @Configuration @@ -45,6 +53,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { static final String HEADER_X_CERBERUS_TOKEN = "X-Cerberus-Token"; static final String LEGACY_AUTH_TOKN_HEADER = "X-Vault-Token"; + @Value("${cerberus.cors.allowedOriginPattern:#{null}}") + private String allowedOriginPattern; + private static final List AUTHENTICATION_NOT_REQUIRED_WHITELIST = List.of( "/", @@ -68,6 +79,10 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired HttpFirewall allowUrlEncodedSlashHttpFirewall; + void setAllowedOriginPattern(String allowedPattern) { + this.allowedOriginPattern = allowedPattern; + } + RequestMatcher getDoesRequestsRequireAuthMatcher() { List whiteListMatchers = @@ -112,6 +127,9 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(AUTHENTICATION_NOT_REQUIRED_WHITELIST.toArray(new String[0])) .permitAll(); + // ALlow OPTIONS for CORS + http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "**").permitAll(); + // Force all other requests to be authenticated http.authorizeRequests().anyRequest().authenticated(); @@ -123,4 +141,28 @@ protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(jwtFilter, dbTokenFilter.getClass()); } + + @Bean + CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + UrlBasedCorsConfigurationSource source = getConfigurationSource(config); + return new CorsFilter(source); + } + + UrlBasedCorsConfigurationSource getConfigurationSource(CorsConfiguration config) { + config.setAllowCredentials(false); + config.setAllowedOrigins(null); + if (!isBlank(allowedOriginPattern)) { + config.addAllowedOriginPattern(allowedOriginPattern); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + } else { + config.setAllowedOriginPatterns(null); + config.setAllowedHeaders(null); + config.setAllowedMethods(null); + } + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return source; + } } diff --git a/cerberus-web/src/test/java/com/nike/cerberus/security/WebSecurityConfigurationTest.java b/cerberus-web/src/test/java/com/nike/cerberus/security/WebSecurityConfigurationTest.java new file mode 100644 index 000000000..98394b7c4 --- /dev/null +++ b/cerberus-web/src/test/java/com/nike/cerberus/security/WebSecurityConfigurationTest.java @@ -0,0 +1,34 @@ +package com.nike.cerberus.security; + +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.web.cors.CorsConfiguration; + +public class WebSecurityConfigurationTest { + + @Test + public void testNoAllowedOriginPattern() { + WebSecurityConfiguration wsc = new WebSecurityConfiguration(); + CorsConfiguration config = new CorsConfiguration(); + wsc.getConfigurationSource(config); + Assert.assertEquals(config.getAllowedOriginPatterns(), null); + Assert.assertEquals(config.getAllowedOrigins(), null); + Assert.assertEquals(config.getAllowedHeaders(), null); + Assert.assertEquals(config.getAllowedMethods(), null); + Assert.assertFalse(config.getAllowCredentials()); + } + + @Test + public void testCustomAllowedOriginPattern() { + WebSecurityConfiguration wsc = new WebSecurityConfiguration(); + wsc.setAllowedOriginPattern("https://*.testdomain.com"); + CorsConfiguration config = new CorsConfiguration(); + wsc.getConfigurationSource(config); + Assert.assertEquals(config.getAllowedOriginPatterns(), List.of("https://*.testdomain.com")); + Assert.assertEquals(config.getAllowedOrigins(), null); + Assert.assertEquals(config.getAllowedHeaders(), List.of("*")); + Assert.assertEquals(config.getAllowedMethods(), List.of("*")); + Assert.assertFalse(config.getAllowCredentials()); + } +}