package com.redkite.plantcare.configurations;

import static com.google.common.collect.ImmutableList.of;
import static com.redkite.plantcare.configurations.WebConstants.LOGIN_ENTRY_POINT;
import static com.redkite.plantcare.configurations.WebConstants.LOGOUT_ENTRY_POINT;
import static com.redkite.plantcare.configurations.WebConstants.LOG_DATA_ENTRY_POINT;
import static com.redkite.plantcare.configurations.WebConstants.REFRESH_TOKEN_ENTRY_POINT;
import static com.redkite.plantcare.configurations.WebConstants.TOKEN_BASED_AUTH_ENTRY_POINT;
import static com.redkite.plantcare.configurations.WebConstants.USERS_ENTRY_POINT;
import static com.redkite.plantcare.filter.RestApi.a;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.redkite.plantcare.filter.AjaxLoginProcessingFilter;
import com.redkite.plantcare.filter.JwtTokenAuthenticationProcessingFilter;
import com.redkite.plantcare.filter.RestApi;
import com.redkite.plantcare.filter.SkipPathRequestMatcher;
import com.redkite.plantcare.security.AjaxAuthenticationProvider;
import com.redkite.plantcare.security.JwtAuthenticationProvider;
import com.redkite.plantcare.security.RestAuthenticationEntryPoint;
import com.redkite.plantcare.security.token.tools.JwtTokenExtractor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.util.List;

import javax.validation.Validator;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Autowired
  private RestAuthenticationEntryPoint authenticationEntryPoint;

  @Autowired
  private AuthenticationSuccessHandler successHandler;

  @Autowired
  private AuthenticationFailureHandler failureHandler;

  @Autowired
  private AjaxAuthenticationProvider ajaxAuthenticationProvider;

  @Autowired
  private JwtAuthenticationProvider jwtAuthenticationProvider;

  @Autowired
  private JwtTokenExtractor jwtTokenExtractor;

  @Autowired
  private AuthenticationManager authenticationManager;

  @Autowired
  private ObjectMapper objectMapper;

  @Autowired
  private Validator validator;


  protected AjaxLoginProcessingFilter buildAjaxLoginProcessingFilter() throws Exception {
    AjaxLoginProcessingFilter filter = new AjaxLoginProcessingFilter(new AntPathRequestMatcher(LOGIN_ENTRY_POINT, HttpMethod.POST.name()),
                    successHandler, failureHandler, objectMapper, validator);
    filter.setAuthenticationManager(this.authenticationManager);
    return filter;
  }

  protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
    List<RestApi> apisToSkip = of(a(REFRESH_TOKEN_ENTRY_POINT), a(LOGIN_ENTRY_POINT),
                    a(LOGOUT_ENTRY_POINT), a(USERS_ENTRY_POINT, HttpMethod.POST), a(LOG_DATA_ENTRY_POINT, HttpMethod.POST));
    SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(apisToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
    JwtTokenAuthenticationProcessingFilter filter
            = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtTokenExtractor, matcher);
    filter.setAuthenticationManager(this.authenticationManager);
    return filter;
  }


  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(ajaxAuthenticationProvider);
    auth.authenticationProvider(jwtAuthenticationProvider);
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable() // We don't need CSRF for JWT based authentication
          .exceptionHandling()
          .authenticationEntryPoint(this.authenticationEntryPoint)
        .and()
          .sessionManagement()
          .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
          .antMatchers(LOGIN_ENTRY_POINT).permitAll() // Login end-point
          .antMatchers(REFRESH_TOKEN_ENTRY_POINT).permitAll() // Token refresh end-point
          .antMatchers(LOGOUT_ENTRY_POINT).permitAll() // Token refresh end-point
        .and()
        .authorizeRequests()
            .antMatchers(HttpMethod.POST, USERS_ENTRY_POINT).permitAll()
            .antMatchers(HttpMethod.POST, LOG_DATA_ENTRY_POINT).permitAll()
            .antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points
        .and()
          .addFilterBefore(buildAjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
          .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
  }
}

@@ -3632,24 +3632,16 @@
         
-        
 successH
@@ -3686,24 +3686,24 @@
 validator);%0A
+
     filter.s
@@ -3929,16 +3929,94 @@
 ip = of(
+%0A            a(TOKEN_BASED_AUTH_ENTRY_POINT, HttpMethod.OPTIONS),%0A            
 a(REFRES
@@ -4036,16 +4036,28 @@
 _POINT),
+%0A           
  a(LOGIN
@@ -4079,32 +4079,24 @@
             
-  
-      
 a(LOGOUT_ENT
@@ -4105,16 +4105,28 @@
 _POINT),
+%0A           
  a(USERS
@@ -4156,16 +4156,28 @@
 d.POST),
+%0A           
  a(LOG_D
@@ -5174,16 +5174,18 @@
         
+  
 .excepti
@@ -5197,16 +5197,18 @@
 dling()%0A
+  
         
@@ -5266,32 +5266,36 @@
 yPoint)%0A        
+    
 .and()%0A         
@@ -5287,32 +5287,34 @@
 and()%0A          
+ 
+ 
 .sessionManageme
@@ -5318,16 +5318,18 @@
 ement()%0A
+  
         
@@ -5386,32 +5386,36 @@
 TELESS)%0A        
+    
 .and()%0A        .
@@ -5405,32 +5405,36 @@
  .and()%0A        
+ 
+   
 .authorizeReques
@@ -5430,32 +5430,34 @@
 orizeRequests()%0A
+  
           .antMa
@@ -5515,32 +5515,34 @@
 point%0A          
+  
 .antMatchers(REF
@@ -5596,32 +5596,34 @@
 fresh end-point%0A
+  
           .antMa
@@ -5688,39 +5688,47 @@
 d-point%0A        
+    
 .and()%0A
+    
         .authori
@@ -5988,39 +5988,45 @@
 -points%0A        
+    
 .and()%0A
+  
           .addFi
@@ -6114,16 +6114,18 @@
 .class)%0A
+  
         

Fix with http method options