package com.hardening;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
UserDetailsService userDetailsService(Settings settings) {
String adminPassword = settings.adminPassword();
if (adminPassword == null || adminPassword.length() < 12) {
throw new IllegalArgumentException(
"hardening.admin-password must contain at least 12 characters"
);
}
PasswordEncoder encoder = passwordEncoder();
List<UserDetails> users = new ArrayList<>();
users.add(
User.withUsername("admin")
.password(encoder.encode(adminPassword))
.roles("ADMIN", "USER")
.build()
);
// Old optional operator account. Target Oracle access will not use this.
if (!settings.demo()
&& settings.userPassword() != null
&& !settings.userPassword().isBlank()) {
users.add(
User.withUsername("operator")
.password(encoder.encode(settings.userPassword()))
.roles("USER")
.build()
);
}
return new InMemoryUserDetailsManager(users);
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/**"))
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/",
"/index.html",
"/assets/**",
"/favicon.svg",
"/error"
).permitAll()
.requestMatchers("/api/login").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/**").hasAnyRole("ADMIN", "USER")
.anyRequest().authenticated()
)
.formLogin(login -> login
.loginProcessingUrl("/api/login")
.successHandler((request, response, authentication) -> {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write("{\"message\":\"ok\"}");
})
.failureHandler((request, response, exception) -> {
response.setStatus(401);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(
"{\"message\":\"Sign-in failed. Check the configured application account.\"}"
);
})
.permitAll()
)
.httpBasic(basic -> basic.disable());
return http.build();
}
}
Top comments (0)