package es.uv.saic.config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

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.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import es.uv.saic.service.AuthProvider;


@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

	@Autowired
	AuthProvider authProvider;
	
	@Value("${saic.actuator.validIp}")
	private String validIp;
	
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.authorizeHttpRequests((auth) -> auth
	        	.requestMatchers("/", "/css/**", "/js/**", "/img/**", "/logos/*", "/logos/**").permitAll()
	        	.requestMatchers("/login**").permitAll()
	        	.requestMatchers("/keepalive").permitAll()
				.requestMatchers("/public/**").permitAll()
	        	.requestMatchers("/actuator/**").access(new WebExpressionAuthorizationManager("hasIpAddress('" + this.validIp + "')"))
				.requestMatchers("/actuator/**").access(new WebExpressionAuthorizationManager("hasIpAddress('127.0.0.1')"))
	        )
	        .authorizeHttpRequests((auth)-> auth
	            .anyRequest().authenticated()
				//.anyRequest().permitAll()
	        )
	        .formLogin((form) -> form
	            .loginPage("/login")
	            .defaultSuccessUrl("/procedures?_new=1",true)
	            .failureUrl("/login?error=noauth")
	            .successHandler(new AuthSuccessHandler())
	            .permitAll()
	        )
	        .logout((logout) -> logout
	        	.logoutSuccessUrl("/login")
	        	.invalidateHttpSession(true)
	        	.clearAuthentication(true)
	        	.deleteCookies("SAICSESSIONID")
                .deleteCookies("SESSION")
             )
	        .csrf((csrf) -> csrf.disable());
		
		http.sessionManagement((session) -> session
			.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
			.sessionFixation().migrateSession()
			.maximumSessions(1)
				.expiredUrl("/login?error=expired")
				.sessionRegistry(sessionRegistry())
		);
	
		http.headers((headers) -> headers
				.frameOptions((options) -> options.sameOrigin())
		    );
	
	    return http.build();
	}
	    
    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
        		   .authenticationProvider(authProvider)
        		   .build();
    }
    
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }
    
    @Bean
    public DefaultSpringSecurityContextSource contextSource() {
        return  new DefaultSpringSecurityContextSource(
                Collections.singletonList("ldap://ldap.uv.es"), "dc=uv,dc=es");
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
     
    @Bean
    public CompositeSessionAuthenticationStrategy concurrentSession() {

         ConcurrentSessionControlAuthenticationStrategy concurrentAuthenticationStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
         concurrentAuthenticationStrategy.setMaximumSessions(1);
         concurrentAuthenticationStrategy.setExceptionIfMaximumExceeded(true);
         List<SessionAuthenticationStrategy> delegateStrategies = new ArrayList<SessionAuthenticationStrategy>();
         delegateStrategies.add(concurrentAuthenticationStrategy);
         delegateStrategies.add(new SessionFixationProtectionStrategy());
         delegateStrategies.add(new RegisterSessionAuthenticationStrategy(sessionRegistry()));

         CompositeSessionAuthenticationStrategy authenticationStrategy =  new CompositeSessionAuthenticationStrategy(delegateStrategies);
         return authenticationStrategy;
     }
	
}
