SecurityConfig.java

/**
 * @file SecurityConfig.java
 * @brief Configuration class for Spring Security and internationalization.
 */

package com.deusto.deuspotify.config;

import com.deusto.deuspotify.services.ProfileService;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

import java.util.List;
import java.util.Locale;

/**
 * @class SecurityConfig
 * @brief Configures Spring Security filters, authentication, password encoding, and internationalization.
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final ProfileService profileService;

    /**
     * @brief Constructor that injects the ProfileService for authentication.
     * @param profileService Service used to retrieve user details.
     */
    public SecurityConfig(ProfileService profileService) {
        this.profileService = profileService;
    }

    /**
     * @brief Configures the security filter chain for HTTP requests.
     * @param http The HttpSecurity object to configure.
     * @return The configured SecurityFilterChain.
     * @throws Exception if configuration fails.
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(
                    "/", "/login", "/login.html", "/register.html",
                    "/styles.css", "/static/**", "/api/i18n",
                    "/api/profiles", "/auth/register", "/auth/login"
                ).permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/index.html", true)
                .failureUrl("/login.html?error=true")
            )
            .logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login.html")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
            )
            .csrf(csrf -> csrf
                .ignoringRequestMatchers("/login", "/logout", "/api/**", "/auth/**")
            );

        return http.build();
    }

    /**
     * @brief Provides a custom AuthenticationManager with a DAO authentication provider.
     * @param authenticationConfiguration The authentication configuration.
     * @return A configured AuthenticationManager.
     * @throws Exception if setup fails.
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(profileService);
        authProvider.setPasswordEncoder(passwordEncoder());

        return new ProviderManager(List.of(authProvider));
    }

    /**
     * @brief Bean for password encoding using BCrypt.
     * @return A BCryptPasswordEncoder instance.
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // ----------------- Internationalization -----------------

    /**
     * @brief Configures the message source for i18n.
     * @return A ResourceBundleMessageSource configured with UTF-8.
     */
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

    /**
     * @brief Configures the default locale resolver.
     * @return A LocaleResolver with default locale set to Spanish.
     */
    @Bean
    public LocaleResolver localeResolver() {
        AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
        resolver.setDefaultLocale(new Locale("es"));
        return resolver;
    }
}