001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.ws.security;
015
016import org.gbif.ws.server.filter.AppIdentityFilter;
017import org.gbif.ws.server.filter.HttpServletRequestWrapperFilter;
018import org.gbif.ws.server.filter.IdentityFilter;
019import org.gbif.ws.server.filter.RequestHeaderParamUpdateFilter;
020
021import java.util.Arrays;
022import java.util.Collections;
023
024import org.springframework.context.ApplicationContext;
025import org.springframework.context.annotation.Bean;
026import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
027import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
028import org.springframework.security.config.annotation.web.builders.HttpSecurity;
029import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
030import org.springframework.security.config.http.SessionCreationPolicy;
031import org.springframework.security.core.userdetails.UserDetailsService;
032import org.springframework.security.crypto.password.PasswordEncoder;
033import org.springframework.security.web.csrf.CsrfFilter;
034import org.springframework.web.cors.CorsConfiguration;
035import org.springframework.web.cors.CorsConfigurationSource;
036import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
037
038/**
039 * Security Adapter that disables the authentication redirect and use GBIF identity filters for secure endpoints.
040 * UserDetailsService and PasswordEncoder must be supplied by the SpringContext.
041 * This class is not annotated to avoid automatic instantiation. To use it create a subclass of it:
042 * <pre>
043 *   @Configuration
044 *   @EnableWebSecurity
045 *   public static class ValidatorWebSecurity extends NoAuthWebSecurityConfigurer {
046 *
047 *     public ValidatorWebSecurity(
048 *       UserDetailsService userDetailsService, ApplicationContext context, PasswordEncoder passwordEncoder
049 *     ) {
050 *       super(userDetailsService, context, passwordEncoder);
051 *     }
052 *  }
053 * <pre/>
054 */
055public class NoAuthWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
056
057  private final UserDetailsService userDetailsService;
058
059  private final PasswordEncoder passwordEncoder;
060
061  public NoAuthWebSecurityConfigurer(
062      UserDetailsService userDetailsService,
063      ApplicationContext context,
064      PasswordEncoder passwordEncoder) {
065    this.userDetailsService = userDetailsService;
066    setApplicationContext(context);
067    this.passwordEncoder = passwordEncoder;
068  }
069
070  @Override
071  protected void configure(AuthenticationManagerBuilder auth) {
072    auth.authenticationProvider(dbAuthenticationProvider());
073  }
074
075  private DaoAuthenticationProvider dbAuthenticationProvider() {
076    final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
077    authProvider.setUserDetailsService(userDetailsService);
078    authProvider.setPasswordEncoder(passwordEncoder);
079    return authProvider;
080  }
081
082  @Override
083  protected void configure(HttpSecurity http) throws Exception {
084    http.httpBasic()
085        .disable()
086        .addFilterAfter(
087            getApplicationContext().getBean(HttpServletRequestWrapperFilter.class),
088            CsrfFilter.class)
089        .addFilterAfter(
090            getApplicationContext().getBean(RequestHeaderParamUpdateFilter.class),
091            HttpServletRequestWrapperFilter.class)
092        .addFilterAfter(
093            getApplicationContext().getBean(IdentityFilter.class),
094            RequestHeaderParamUpdateFilter.class)
095        .addFilterAfter(
096            getApplicationContext().getBean(AppIdentityFilter.class), IdentityFilter.class)
097        .csrf()
098        .disable()
099        .cors()
100        .and()
101        .authorizeRequests()
102        .anyRequest()
103        .authenticated();
104
105    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
106  }
107
108  @Bean
109  CorsConfigurationSource corsConfigurationSource() {
110    // CorsFilter only applies this if the origin header is present in the request
111    CorsConfiguration configuration = new CorsConfiguration();
112    configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type"));
113    configuration.setAllowedOrigins(Collections.singletonList("*"));
114    configuration.setAllowedMethods(
115        Arrays.asList("HEAD", "GET", "POST", "DELETE", "PUT", "OPTIONS"));
116    configuration.setExposedHeaders(
117        Arrays.asList(
118            "Access-Control-Allow-Origin",
119            "Access-Control-Allow-Methods",
120            "Access-Control-Allow-Headers"));
121    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
122    source.registerCorsConfiguration("/**", configuration);
123    return source;
124  }
125}