package org.xcolab.view.config.spring.beans;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.web.authentication.RememberMeServices;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
import org.springframework.session.web.http.SessionRepositoryFilter;

import org.xcolab.util.autoconfigure.XCoLabProperties;
import org.xcolab.view.auth.handlers.AuthenticationFailureHandler;
import org.xcolab.view.auth.handlers.AuthenticationSuccessHandler;
import org.xcolab.view.auth.handlers.LogoutSuccessHandler;
import org.xcolab.view.auth.login.spring.MemberDetailsService;
import org.xcolab.view.auth.login.spring.MemberPasswordEncoder;
import org.xcolab.view.config.spring.properties.WebProperties;
import org.xcolab.view.config.spring.properties.WebProperties.GuestAccess;

import java.util.Optional;

import javax.servlet.DispatcherType;

@Configuration
@EnableWebSecurity
@SuppressWarnings("ProhibitedExceptionDeclared")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger log = LoggerFactory.getLogger(WebSecurityConfig.class);

    private final RememberMeServices rememberMeServices;
    private final MemberDetailsService memberDetailsService;
    private final AuthenticationSuccessHandler authenticationSuccessHandler;

    private final WebProperties webProperties;
    private final XCoLabProperties xCoLabProperties;

    @Autowired
    public WebSecurityConfig(RememberMeServices rememberMeServices,
            MemberDetailsService memberDetailsService, WebProperties webProperties,
            XCoLabProperties xCoLabProperties,
            AuthenticationSuccessHandler authenticationSuccessHandler) {
        this.rememberMeServices = rememberMeServices;
        this.memberDetailsService = memberDetailsService;
        this.webProperties = webProperties;
        this.xCoLabProperties = xCoLabProperties;
        this.authenticationSuccessHandler = authenticationSuccessHandler;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests()
                .antMatchers("/admin/management/**").hasRole("ADMIN");

        final GuestAccess guestAccessProperties = webProperties.getGuestAccess();

        if (!guestAccessProperties.isAllowSso()) {
            httpSecurity.authorizeRequests()
                    .antMatchers("/sso/**").denyAll();
        }

        if (!guestAccessProperties.isAllowRegistration()) {
            httpSecurity.authorizeRequests()
                    .antMatchers("/register/**").denyAll();
        }

        if (guestAccessProperties.isAllowAll()) {
            httpSecurity.authorizeRequests()
                    .anyRequest().permitAll();
        } else {
            if (guestAccessProperties.isAlwaysAllowHomepage()) {
                httpSecurity.authorizeRequests()
                        .antMatchers("/").permitAll();
            }
            httpSecurity.authorizeRequests()
                    .antMatchers("/images/**").permitAll()
                    .antMatchers("/css/**").permitAll()
                    .antMatchers("/js/**").permitAll()
                    .antMatchers("/vendor/**").permitAll()
                    .antMatchers("/login").permitAll()
                    .antMatchers("/login/**").permitAll()
                    .antMatchers("/reportError**").permitAll()
                    .anyRequest().authenticated();
        }

        httpSecurity
                .formLogin()
                    .loginPage("/login")
                    .permitAll()
                    .successHandler(authenticationSuccessHandler)
                    .failureHandler(new AuthenticationFailureHandler())
                    .and()
                .rememberMe()
                    .rememberMeServices(rememberMeServices)
                    // need to specify same key that is used in rememberMeServices
                    .key(xCoLabProperties.getSecret())
                    .and()
                .logout()
                    .permitAll()
                    .logoutSuccessHandler(new LogoutSuccessHandler())
                    .and()
                .csrf()
                    .ignoringAntMatchers("/webhooks/**")
                    .and()
                .headers().defaultsDisabled()
                    .referrerPolicy(ReferrerPolicy.ORIGIN_WHEN_CROSS_ORIGIN)
                        .and()
                    .contentTypeOptions()
                        .and()
                    .xssProtection()
                        .block(true)
                        .and()
                    .frameOptions()
                        .sameOrigin();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(memberDetailsService)
                .passwordEncoder(new MemberPasswordEncoder());
    }

    @Bean
    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
    public FilterRegistrationBean sessionFilterErrorDispatch(
            Optional<SessionRepositoryFilter> sessionRepositoryFilter) {
        if (sessionRepositoryFilter.isPresent()) {

            final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            registrationBean.setFilter(sessionRepositoryFilter.get());
            //override registration to ensure sessions are initialized correctly for errors
            registrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC,
                    DispatcherType.ERROR);
            registrationBean.setOrder(SessionRepositoryFilter.DEFAULT_ORDER);
            return registrationBean;
        } else {
            log.warn("No SessionRepositoryFilter found - defaulting to regular session.");
            return null;
        }
    }
}

@@ -793,24 +793,96 @@
 MeServices;%0A
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;%0A
 import org.s
@@ -4989,16 +4989,190 @@
 ks/**%22)%0A
+                    //COLAB-2480: Fixes 403 when double clicking login button (CSRF token is reset)%0A                    .csrfTokenRepository(new CookieCsrfTokenRepository())%0A
         

Store CSRF token in cookie