2424import eu .openanalytics .containerproxy .auth .IAuthenticationBackend ;
2525import eu .openanalytics .containerproxy .auth .UserLogoutHandler ;
2626import eu .openanalytics .containerproxy .util .AppRecoveryFilter ;
27+ import eu .openanalytics .containerproxy .util .EnvironmentUtils ;
28+ import eu .openanalytics .containerproxy .util .OverridingHeaderWriter ;
2729import org .apache .logging .log4j .LogManager ;
2830import org .apache .logging .log4j .Logger ;
2931import org .springframework .beans .factory .annotation .Autowired ;
4345import org .springframework .security .web .access .AccessDeniedHandlerImpl ;
4446import org .springframework .security .web .authentication .www .BasicAuthenticationFilter ;
4547import org .springframework .security .web .csrf .MissingCsrfTokenException ;
48+ import org .springframework .security .web .header .Header ;
4649import org .springframework .security .web .header .writers .StaticHeadersWriter ;
4750import org .springframework .security .web .util .matcher .AntPathRequestMatcher ;
48- import org .springframework .web .servlet .config .annotation .CorsRegistry ;
4951import org .springframework .web .servlet .support .ServletUriComponentsBuilder ;
5052
5153import javax .inject .Inject ;
5254import javax .servlet .ServletException ;
5355import javax .servlet .http .HttpServletRequest ;
5456import javax .servlet .http .HttpServletResponse ;
5557import java .io .IOException ;
58+ import java .util .ArrayList ;
5659import java .util .List ;
5760
5861import static eu .openanalytics .containerproxy .ui .TemplateResolverConfig .PROP_CORS_ALLOWED_ORIGINS ;
@@ -77,7 +80,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
7780
7881 @ Autowired (required =false )
7982 private List <ICustomSecurityConfig > customConfigs ;
80-
83+
84+ public static final String PROP_DISABLE_NO_SNIFF_HEADER = "proxy.api-security.disable-no-sniff-header" ;
85+ public static final String PROP_DISABLE_HSTS_HEADER = "proxy.api-security.disable-hsts-header" ;
86+ public static final String PROP_DISABLE_XSS_PROTECTION_HEADER = "proxy.api-security.disable-xss-protection-header" ;
87+ public static final String PROP_CUSTOM_HEADERS = "proxy.api-security.custom-headers" ;
88+
8189 @ Override
8290 public void configure (WebSecurity web ) {
8391// web
@@ -110,7 +118,7 @@ private void checkForIncorrectConfiguration(HttpServletRequest request) {
110118
111119 @ Override
112120 protected void configure (HttpSecurity http ) throws Exception {
113- if (environment . getProperty ( PROP_CORS_ALLOWED_ORIGINS + "[0]" ) != null ) {
121+ if (EnvironmentUtils . readList ( environment , PROP_CORS_ALLOWED_ORIGINS ) != null ) {
114122 // enable cors
115123 http .cors ();
116124 }
@@ -136,8 +144,25 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Acc
136144 }
137145 });
138146
139- // Always set header: X-Content-Type-Options=nosniff
140- http .headers ().contentTypeOptions ();
147+ if (environment .getProperty (PROP_DISABLE_NO_SNIFF_HEADER , Boolean .class , false )) {
148+ http .headers ().contentTypeOptions ().disable ();
149+ } else {
150+ // set header: X-Content-Type-Options=nosniff
151+ http .headers ().contentTypeOptions ();
152+ }
153+
154+ if (environment .getProperty (PROP_DISABLE_XSS_PROTECTION_HEADER , Boolean .class , false )) {
155+ http .headers ().xssProtection ().disable ();
156+ } else {
157+ http .headers ().xssProtection ();
158+ }
159+
160+ if (environment .getProperty (PROP_DISABLE_HSTS_HEADER , Boolean .class , false )) {
161+ http .headers ().httpStrictTransportSecurity ().disable ();
162+ } else {
163+ http .headers ().httpStrictTransportSecurity ();
164+ }
165+
141166
142167 String frameOptions = environment .getProperty ("server.frame-options" , "disable" );
143168 switch (frameOptions .toUpperCase ()) {
@@ -157,15 +182,19 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Acc
157182 .addHeaderWriter (new StaticHeadersWriter ("X-Frame-Options" , frameOptions ));
158183 }
159184 }
160-
185+
186+ List <Header > headers = getCustomHeaders ();
187+ if (!headers .isEmpty ()) {
188+ http .headers ().addHeaderWriter (new OverridingHeaderWriter (headers ));
189+ }
190+
161191 // Allow public access to health endpoint
162192 http .authorizeRequests ().antMatchers ("/actuator/health" ).permitAll ();
163193 http .authorizeRequests ().antMatchers ("/actuator/health/readiness" ).permitAll ();
164194 http .authorizeRequests ().antMatchers ("/actuator/health/liveness" ).permitAll ();
165195 http .authorizeRequests ().antMatchers ("/actuator/prometheus" ).permitAll ();
166196 http .authorizeRequests ().antMatchers ("/actuator/recyclable" ).permitAll ();
167-
168- http .authorizeRequests ().antMatchers ("/saml/metadata" ).permitAll ();
197+ http .authorizeRequests ().antMatchers ("/saml/metadata" ).permitAll ();
169198
170199 // Note: call early, before http.authorizeRequests().anyRequest().fullyAuthenticated();
171200 if (customConfigs != null ) {
@@ -213,4 +242,24 @@ public AuthenticationManager authenticationManagerBean() throws Exception {
213242 return super .authenticationManagerBean ();
214243 }
215244
245+ private List <Header > getCustomHeaders () {
246+ List <Header > headers = new ArrayList <>();
247+
248+ int i = 0 ;
249+ String headerName = environment .getProperty (String .format (PROP_CUSTOM_HEADERS + "[%d].name" , i ));
250+ while (headerName != null ) {
251+ String headerValue = environment .getProperty (String .format (PROP_CUSTOM_HEADERS + "[%d].value" , i ));
252+ if (headerValue == null ) {
253+ logger .warn ("Missing header value for header {}" , headerName );
254+ i ++;
255+ continue ;
256+ }
257+ headers .add (new Header (headerName , headerValue ));
258+ i ++;
259+ headerName = environment .getProperty (String .format (PROP_CUSTOM_HEADERS + "[%d].name" , i ));
260+ }
261+
262+ return headers ;
263+ }
264+
216265}
0 commit comments