From 920fbe489d11d791cdc8dca3410b6176050e587b Mon Sep 17 00:00:00 2001 From: Florian THIERRY Date: Thu, 30 Nov 2023 14:56:03 +0100 Subject: [PATCH] Add security on endpoints and handle 403 responses. --- .../configuration/SecurityConfiguration.java | 33 +++++++++++-------- .../security/model/CustomUserDetails.java | 10 ++++-- .../org/sportshub/domain/user/model/User.java | 4 ++- .../sportshub/domain/user/model/UserRole.java | 6 ++++ .../exposition/user/UserController.java | 2 -- .../user/adapter/UserInMemoryAdapter.java | 20 +++++++++-- .../src/main/resources/application.yml | 8 ++++- 7 files changed, 60 insertions(+), 23 deletions(-) create mode 100644 sportshub-domain/src/main/java/org/sportshub/domain/user/model/UserRole.java 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 index 62ca73b..c351f7e 100644 --- a/sportshub-application/src/main/java/org/sportshub/application/configuration/SecurityConfiguration.java +++ b/sportshub-application/src/main/java/org/sportshub/application/configuration/SecurityConfiguration.java @@ -1,9 +1,13 @@ package org.sportshub.application.configuration; +import static org.sportshub.domain.user.model.UserRole.ADMIN; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.OPTIONS; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; import org.sportshub.application.security.JwtAuthenticationFilter; 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; @@ -12,7 +16,6 @@ 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 org.springframework.security.web.util.matcher.AntPathRequestMatcher; import static jakarta.servlet.DispatcherType.FORWARD; import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN; @@ -29,27 +32,29 @@ public class SecurityConfiguration { httpSecurity .csrf(AbstractHttpConfigurer::disable) .httpBasic(Customizer.withDefaults()) + .exceptionHandling(configurer -> configurer + .authenticationEntryPoint((request, response, authException) -> response.sendError(SC_UNAUTHORIZED)) + .accessDeniedHandler((request, response, accessDeniedException) -> response.sendError(SC_FORBIDDEN)) + ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .sessionManagement(customizer -> customizer.sessionCreationPolicy(STATELESS)) .authorizeHttpRequests(requests -> requests .dispatcherTypeMatchers(FORWARD).permitAll() .requestMatchers( - HttpMethod.GET, - "/api/health/check" + GET, + "/api/health/check", + "/error" ).permitAll() .requestMatchers( - HttpMethod.POST, + GET, + "/api/users" + ).hasAuthority(ADMIN.name()) + .requestMatchers( + POST, "/api/users/login" ).permitAll() + .requestMatchers(OPTIONS).permitAll() .anyRequest().authenticated() - ) - .exceptionHandling(configurer -> configurer - .defaultAuthenticationEntryPointFor( - (request, response, authException) -> response.sendError(SC_UNAUTHORIZED), - new AntPathRequestMatcher("/api/**") - ).defaultAccessDeniedHandlerFor( - (request, response, accessDeniedException) -> response.sendError(SC_FORBIDDEN), - new AntPathRequestMatcher("/api/**") - ) ); return httpSecurity.build(); diff --git a/sportshub-application/src/main/java/org/sportshub/application/security/model/CustomUserDetails.java b/sportshub-application/src/main/java/org/sportshub/application/security/model/CustomUserDetails.java index 9ed740d..0e2d0e3 100644 --- a/sportshub-application/src/main/java/org/sportshub/application/security/model/CustomUserDetails.java +++ b/sportshub-application/src/main/java/org/sportshub/application/security/model/CustomUserDetails.java @@ -1,10 +1,11 @@ package org.sportshub.application.security.model; -import static java.util.Collections.emptyList; import java.util.Collection; import org.sportshub.domain.user.model.User; +import org.sportshub.domain.user.model.UserRole; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class CustomUserDetails implements UserDetails { @@ -16,7 +17,12 @@ public class CustomUserDetails implements UserDetails { @Override public Collection getAuthorities() { - return emptyList(); + return user.roles() + .stream() + .map(UserRole::name) + .map(role -> "ROLE_" + role) + .map(SimpleGrantedAuthority::new) + .toList(); } @Override diff --git a/sportshub-domain/src/main/java/org/sportshub/domain/user/model/User.java b/sportshub-domain/src/main/java/org/sportshub/domain/user/model/User.java index 556753d..3816e72 100644 --- a/sportshub-domain/src/main/java/org/sportshub/domain/user/model/User.java +++ b/sportshub-domain/src/main/java/org/sportshub/domain/user/model/User.java @@ -1,8 +1,10 @@ package org.sportshub.domain.user.model; +import java.util.List; import java.util.UUID; public record User( UUID id, - String password + String password, + List roles ) {} diff --git a/sportshub-domain/src/main/java/org/sportshub/domain/user/model/UserRole.java b/sportshub-domain/src/main/java/org/sportshub/domain/user/model/UserRole.java new file mode 100644 index 0000000..d699aa4 --- /dev/null +++ b/sportshub-domain/src/main/java/org/sportshub/domain/user/model/UserRole.java @@ -0,0 +1,6 @@ +package org.sportshub.domain.user.model; + +public enum UserRole { + STANDARD, + ADMIN +} 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 7592ddc..f17cf22 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,12 +1,10 @@ package org.sportshub.exposition.user; import java.util.List; -import java.util.Optional; import org.sportshub.application.user.UserUseCases; import org.sportshub.domain.user.model.User; import org.sportshub.exposition.user.model.LoginRequest; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; 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 8302c5a..c275a17 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 @@ -4,6 +4,8 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import static org.sportshub.domain.user.model.UserRole.ADMIN; +import static org.sportshub.domain.user.model.UserRole.STANDARD; import org.sportshub.domain.user.model.User; import org.sportshub.domain.user.port.UserPort; import org.springframework.stereotype.Component; @@ -11,9 +13,21 @@ 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"), "$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") + new User( + UUID.fromString("c1a0805f-c618-47dc-bae7-bee70503644e"), + "$2a$10$WPuLOKpvaQnMotNo5ijPwegBPwmMF1C04XkTNCBpeBFo4r2YJWy.2", + List.of(STANDARD) + ), + new User( + UUID.fromString("4eff194d-dd8e-463e-974f-034bfd509f84"), + "$2a$10$WPuLOKpvaQnMotNo5ijPwegBPwmMF1C04XkTNCBpeBFo4r2YJWy.2", + List.of(STANDARD) + ), + new User( + UUID.fromString("c78d7d7c-0386-415d-86dc-98a470591e07"), + "$2a$10$WPuLOKpvaQnMotNo5ijPwegBPwmMF1C04XkTNCBpeBFo4r2YJWy.2", + List.of(STANDARD, ADMIN) + ) ); @Override diff --git a/sportshub-launcher/src/main/resources/application.yml b/sportshub-launcher/src/main/resources/application.yml index 9d5e816..9672836 100644 --- a/sportshub-launcher/src/main/resources/application.yml +++ b/sportshub-launcher/src/main/resources/application.yml @@ -5,4 +5,10 @@ application: logging: level: - org.springframework.security: DEBUG \ No newline at end of file + org.springframework.security: DEBUG + +server: + error: + whitelabel: + enabled: false # Disable html error responses. + include-stacktrace: never \ No newline at end of file