package se.bilhalsning.config; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; 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.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import se.bilhalsning.dto.ErrorResponse; import se.bilhalsning.security.JwtAuthenticationFilter; import se.bilhalsning.security.JwtService; @Configuration @EnableWebSecurity public class SecurityConfig { static final String UNAUTHENTICATED_MESSAGE = "Din session har löpt ut eller är ogiltig. Logga in igen."; static final String FORBIDDEN_MESSAGE = "Du har inte behörighet att utföra denna åtgärd."; private final ObjectMapper objectMapper = new ObjectMapper(); @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtService jwtService(@Value("${app.jwt.secret}") String secret) { return new JwtService(secret); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception { http .csrf(csrf -> csrf.disable()) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers( "/api/auth/register", "/api/auth/login", "/api/auth/forgot-password", "/api/auth/reset-password", "/api/auth/confirm-email-change") .permitAll() .requestMatchers("/api/webhooks/**").permitAll() .requestMatchers("/api/payment/swish-info").permitAll() .requestMatchers("/api/vehicles/**").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated()) .exceptionHandling(eh -> eh .authenticationEntryPoint((request, response, ex) -> writeError(response, HttpStatus.UNAUTHORIZED, UNAUTHENTICATED_MESSAGE)) .accessDeniedHandler((request, response, ex) -> writeError(response, HttpStatus.FORBIDDEN, FORBIDDEN_MESSAGE))) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } private void writeError(HttpServletResponse response, HttpStatus status, String message) throws java.io.IOException { response.setStatus(status.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(new ErrorResponse(message))); } }