diff --git a/pom.xml b/pom.xml index 7936024..dc5dd00 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ 21 21 21 + 6.0.0 @@ -54,6 +55,11 @@ sportshub-infrastructure ${project.version} + + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet-api.version} + diff --git a/sportshub-application/pom.xml b/sportshub-application/pom.xml index 1fb543c..63fa59d 100644 --- a/sportshub-application/pom.xml +++ b/sportshub-application/pom.xml @@ -25,5 +25,13 @@ org.springframework spring-context + + org.springframework.boot + spring-boot-starter-security + + + jakarta.servlet + jakarta.servlet-api + diff --git a/sportshub-application/src/main/java/org/sportshub/application/configuration/SecurityConfiguration.java b/sportshub-application/src/main/java/org/sportshub/application/configuration/SecurityConfiguration.java new file mode 100644 index 0000000..8281378 --- /dev/null +++ b/sportshub-application/src/main/java/org/sportshub/application/configuration/SecurityConfiguration.java @@ -0,0 +1,57 @@ +package org.sportshub.application.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.Customizer; +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.configurers.AbstractHttpConfigurer; +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.util.matcher.AntPathRequestMatcher; + +import jakarta.servlet.DispatcherType; +import jakarta.servlet.http.HttpServletResponse; + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration { + @Bean + public SecurityFilterChain securityFilterChain( + HttpSecurity httpSecurity + ) throws Exception { + httpSecurity + .csrf(AbstractHttpConfigurer::disable) + .httpBasic(Customizer.withDefaults()) + .authorizeHttpRequests(requests -> requests + .dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll() + .requestMatchers( + HttpMethod.GET, + "/api/health/check" + ).permitAll() + .requestMatchers( + HttpMethod.POST, + "/api/users/login" + ).permitAll() + .anyRequest().authenticated() + ) + .exceptionHandling(configurer -> configurer + .defaultAuthenticationEntryPointFor( + (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED), + new AntPathRequestMatcher("/api/**") + ).defaultAccessDeniedHandlerFor( + (request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN), + new AntPathRequestMatcher("/api/**") + ) + ); + + return httpSecurity.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/sportshub-application/src/main/java/org/sportshub/application/user/CustomUserDetails.java b/sportshub-application/src/main/java/org/sportshub/application/user/CustomUserDetails.java new file mode 100644 index 0000000..d91ac08 --- /dev/null +++ b/sportshub-application/src/main/java/org/sportshub/application/user/CustomUserDetails.java @@ -0,0 +1,51 @@ +package org.sportshub.application.user; + +import static java.util.Collections.emptyList; +import java.util.Collection; + +import org.sportshub.domain.user.model.User; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +public class CustomUserDetails implements UserDetails { + private final User user; + + public CustomUserDetails(final User user) { + this.user = user; + } + + @Override + public Collection getAuthorities() { + return emptyList(); + } + + @Override + public String getUsername() { + return user.id().toString(); + } + + @Override + public String getPassword() { + return user.password(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/sportshub-application/src/main/java/org/sportshub/application/user/CustomUserDetailsService.java b/sportshub-application/src/main/java/org/sportshub/application/user/CustomUserDetailsService.java new file mode 100644 index 0000000..5eec933 --- /dev/null +++ b/sportshub-application/src/main/java/org/sportshub/application/user/CustomUserDetailsService.java @@ -0,0 +1,33 @@ +package org.sportshub.application.user; + +import java.util.UUID; + +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +public class CustomUserDetailsService implements UserDetailsService { + private final UserUseCases userUseCases; + + public CustomUserDetailsService(final UserUseCases userUseCases) { + this.userUseCases = userUseCases; + } + + @Override + public UserDetails loadUserByUsername(final String userIdAsString) throws UsernameNotFoundException { + UUID userId = parseUserId(userIdAsString); + return userUseCases.findById(userId) + .map(CustomUserDetails::new) + .orElseThrow(() -> new UsernameNotFoundException(userIdAsString)); + } + + private UUID parseUserId(final String userIdAsString) { + try { + return UUID.fromString(userIdAsString); + } catch (IllegalArgumentException exception) { + throw new UsernameNotFoundException(userIdAsString); + } + } +} diff --git a/sportshub-application/src/main/java/org/sportshub/application/user/UserUseCases.java b/sportshub-application/src/main/java/org/sportshub/application/user/UserUseCases.java index 34ded03..60eaa63 100644 --- a/sportshub-application/src/main/java/org/sportshub/application/user/UserUseCases.java +++ b/sportshub-application/src/main/java/org/sportshub/application/user/UserUseCases.java @@ -1,5 +1,6 @@ package org.sportshub.application.user; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -18,4 +19,8 @@ public class UserUseCases { public Optional findById(UUID userId) { return userPort.findById(userId); } + + public List findAll() { + return userPort.findAll(); + } } diff --git a/sportshub-domain/src/main/java/org/sportshub/domain/user/port/UserPort.java b/sportshub-domain/src/main/java/org/sportshub/domain/user/port/UserPort.java index b1d9872..15f948a 100644 --- a/sportshub-domain/src/main/java/org/sportshub/domain/user/port/UserPort.java +++ b/sportshub-domain/src/main/java/org/sportshub/domain/user/port/UserPort.java @@ -1,5 +1,6 @@ package org.sportshub.domain.user.port; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -7,4 +8,6 @@ import org.sportshub.domain.user.model.User; public interface UserPort { Optional findById(UUID userId); + + List findAll(); } diff --git a/sportshub-exposition/pom.xml b/sportshub-exposition/pom.xml index eee73dc..4240100 100644 --- a/sportshub-exposition/pom.xml +++ b/sportshub-exposition/pom.xml @@ -17,6 +17,14 @@ jar + + org.sportshub + sportshub-application + + + org.springframework.boot + spring-boot-starter-web + @@ -25,11 +33,6 @@ - - org.springframework.boot - spring-boot-starter-web - - diff --git a/sportshub-exposition/src/main/java/org/sportshub/exposition/user/UserController.java b/sportshub-exposition/src/main/java/org/sportshub/exposition/user/UserController.java index 7bc917d..f39ad2d 100644 --- a/sportshub-exposition/src/main/java/org/sportshub/exposition/user/UserController.java +++ b/sportshub-exposition/src/main/java/org/sportshub/exposition/user/UserController.java @@ -1,8 +1,10 @@ package org.sportshub.exposition.user; -import java.util.UUID; +import java.util.List; import org.sportshub.application.user.UserUseCases; +import org.sportshub.domain.user.model.User; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -16,8 +18,13 @@ public class UserController { this.userUseCases = userUseCases; } - @PostMapping + @PostMapping("/login") public String login() { return ""; } + + @GetMapping + public List findAll() { + return userUseCases.findAll(); + } } diff --git a/sportshub-infrastructure/src/main/java/org/sportshub/infrastructure/user/adapter/UserInMemoryAdapter.java b/sportshub-infrastructure/src/main/java/org/sportshub/infrastructure/user/adapter/UserInMemoryAdapter.java index ae8047a..8302c5a 100644 --- a/sportshub-infrastructure/src/main/java/org/sportshub/infrastructure/user/adapter/UserInMemoryAdapter.java +++ b/sportshub-infrastructure/src/main/java/org/sportshub/infrastructure/user/adapter/UserInMemoryAdapter.java @@ -11,9 +11,9 @@ import org.springframework.stereotype.Component; @Component public class UserInMemoryAdapter implements UserPort { private static final List users = List.of( - new User(UUID.fromString("c1a0805f-c618-47dc-bae7-bee70503644e"), "password"), - new User(UUID.fromString("4eff194d-dd8e-463e-974f-034bfd509f84"), "password"), - new User(UUID.fromString("c78d7d7c-0386-415d-86dc-98a470591e07"), "password") + new User(UUID.fromString("c1a0805f-c618-47dc-bae7-bee70503644e"), "$2a$10$WPuLOKpvaQnMotNo5ijPwegBPwmMF1C04XkTNCBpeBFo4r2YJWy.2"), + new User(UUID.fromString("4eff194d-dd8e-463e-974f-034bfd509f84"), "$2a$10$WPuLOKpvaQnMotNo5ijPwegBPwmMF1C04XkTNCBpeBFo4r2YJWy.2"), + new User(UUID.fromString("c78d7d7c-0386-415d-86dc-98a470591e07"), "$2a$10$WPuLOKpvaQnMotNo5ijPwegBPwmMF1C04XkTNCBpeBFo4r2YJWy.2") ); @Override @@ -22,4 +22,9 @@ public class UserInMemoryAdapter implements UserPort { .filter(user -> userId.equals(user.id())) .findFirst(); } + + @Override + public List findAll() { + return users; + } } diff --git a/sportshub-launcher/src/main/resources/application.yml b/sportshub-launcher/src/main/resources/application.yml new file mode 100644 index 0000000..fd51fb8 --- /dev/null +++ b/sportshub-launcher/src/main/resources/application.yml @@ -0,0 +1,3 @@ +logging: + level: + org.springframework.security: DEBUG \ No newline at end of file