diff --git a/server/pom.xml b/server/pom.xml index 3bb1bd0..469bbaa 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -21,7 +21,7 @@ io.github.mucsi96 kubetools - 1.31-SNAPSHOT + 1.32-SNAPSHOT org.springframework.boot @@ -84,18 +84,22 @@ lombok true - org.springframework.boot spring-boot-starter-test test - com.github.tomakehurst - wiremock - 3.0.1 + org.springframework.security + spring-security-test test + + org.wiremock + wiremock-standalone + 3.3.1 + test + org.testcontainers testcontainers diff --git a/server/src/main/java/mucsi96/traininglog/core/SecurityConfiguration.java b/server/src/main/java/mucsi96/traininglog/core/SecurityConfiguration.java index 996454a..360c605 100644 --- a/server/src/main/java/mucsi96/traininglog/core/SecurityConfiguration.java +++ b/server/src/main/java/mucsi96/traininglog/core/SecurityConfiguration.java @@ -2,7 +2,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; @@ -14,7 +13,6 @@ import org.springframework.security.web.SecurityFilterChain; import io.github.mucsi96.kubetools.security.KubetoolsSecurityConfigurer; -import io.github.mucsi96.kubetools.security.MockSecurityConfigurer; @Configuration @EnableWebSecurity @@ -22,7 +20,6 @@ public class SecurityConfiguration { @Bean - @Profile("prod") SecurityFilterChain securityFilterChain( HttpSecurity http, KubetoolsSecurityConfigurer kubetoolsSecurityConfigurer) throws Exception { @@ -32,17 +29,6 @@ SecurityFilterChain securityFilterChain( .build(); } - @Bean - @Profile("!prod") - SecurityFilterChain mockSecurityFilterChain( - HttpSecurity http, - MockSecurityConfigurer mockSecurityConfigurer) throws Exception { - return http - .securityMatcher("/weight/**", "/ride/**") - .with(mockSecurityConfigurer, Customizer.withDefaults()) - .build(); - } - @Bean public OAuth2AuthorizedClientService oAuth2AuthorizedClientService( JdbcOperations jdbcOperations, ClientRegistrationRepository clientRegistrationRepository) { diff --git a/server/src/main/java/mucsi96/traininglog/strava/StravaConfiguration.java b/server/src/main/java/mucsi96/traininglog/strava/StravaConfiguration.java index fa9fb67..3f0d393 100644 --- a/server/src/main/java/mucsi96/traininglog/strava/StravaConfiguration.java +++ b/server/src/main/java/mucsi96/traininglog/strava/StravaConfiguration.java @@ -5,7 +5,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.core.convert.converter.Converter; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -30,7 +29,6 @@ import org.springframework.util.MultiValueMap; import io.github.mucsi96.kubetools.security.KubetoolsSecurityConfigurer; -import io.github.mucsi96.kubetools.security.MockSecurityConfigurer; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.Data; @@ -47,7 +45,6 @@ public class StravaConfiguration { private String apiUri; @Bean - @Profile("prod") SecurityFilterChain stravaSecurityFilterChain( HttpSecurity http, KubetoolsSecurityConfigurer kubetoolsSecurityConfigurer) throws Exception { @@ -60,20 +57,6 @@ SecurityFilterChain stravaSecurityFilterChain( .build(); } - @Bean - @Profile("!prod") - SecurityFilterChain mockStravaSecurityFilterChain( - HttpSecurity http, - MockSecurityConfigurer mockSecurityConfigurer) throws Exception { - return http - .securityMatcher("/strava/**") - .oauth2Client(configurer -> configurer - .authorizationCodeGrant(customizer -> customizer - .accessTokenResponseClient(stravaAccessTokenResponseClient()))) - .with(mockSecurityConfigurer, Customizer.withDefaults()) - .build(); - } - @Bean OAuth2AuthorizedClientManager stravaAuthorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, diff --git a/server/src/main/java/mucsi96/traininglog/withings/WithingsConfiguration.java b/server/src/main/java/mucsi96/traininglog/withings/WithingsConfiguration.java index 0de072d..71b44df 100644 --- a/server/src/main/java/mucsi96/traininglog/withings/WithingsConfiguration.java +++ b/server/src/main/java/mucsi96/traininglog/withings/WithingsConfiguration.java @@ -7,7 +7,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.core.convert.converter.Converter; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.security.config.Customizer; @@ -41,7 +40,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.github.mucsi96.kubetools.security.KubetoolsSecurityConfigurer; -import io.github.mucsi96.kubetools.security.MockSecurityConfigurer; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.Data; @@ -57,7 +55,6 @@ public class WithingsConfiguration { private String apiUri; @Bean - @Profile("prod") SecurityFilterChain withingsSecurityFilterChain( HttpSecurity http, KubetoolsSecurityConfigurer kubetoolsSecurityConfigurer) throws Exception { @@ -70,20 +67,6 @@ SecurityFilterChain withingsSecurityFilterChain( .build(); } - @Bean - @Profile("!prod") - SecurityFilterChain mockWithingsSecurityFilterChain( - HttpSecurity http, - MockSecurityConfigurer mockSecurityConfigurer) throws Exception { - return http - .securityMatcher("/withings/**") - .oauth2Client(configurer -> configurer - .authorizationCodeGrant(customizer -> customizer - .accessTokenResponseClient(withingsAccessTokenResponseClient()))) - .with(mockSecurityConfigurer, Customizer.withDefaults()) - .build(); - } - @Bean OAuth2AuthorizedClientManager withingsAuthorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, diff --git a/server/src/test/java/mucsi96/traininglog/RideControllerTests.java b/server/src/test/java/mucsi96/traininglog/RideControllerTests.java index 00ef34e..2a9f3e1 100644 --- a/server/src/test/java/mucsi96/traininglog/RideControllerTests.java +++ b/server/src/test/java/mucsi96/traininglog/RideControllerTests.java @@ -79,6 +79,7 @@ public void returns_forbidden_if_user_has_no_user_role() throws Exception { } @Test + @WithMockUserRoles public void returns_today_ride_stats() throws Exception { MockHttpServletResponse response = mockMvc .perform( @@ -94,6 +95,7 @@ public void returns_today_ride_stats() throws Exception { } @Test + @WithMockUserRoles public void returns_one_week_ride_stats() throws Exception { MockHttpServletResponse response = mockMvc .perform( @@ -109,6 +111,7 @@ public void returns_one_week_ride_stats() throws Exception { } @Test + @WithMockUserRoles public void returns_all_time_ride_stats() throws Exception { MockHttpServletResponse response = mockMvc .perform( diff --git a/server/src/test/java/mucsi96/traininglog/StravaControllerTests.java b/server/src/test/java/mucsi96/traininglog/StravaControllerTests.java index faab9aa..90d0482 100644 --- a/server/src/test/java/mucsi96/traininglog/StravaControllerTests.java +++ b/server/src/test/java/mucsi96/traininglog/StravaControllerTests.java @@ -108,6 +108,7 @@ public void returns_forbidden_if_user_has_no_user_role() throws Exception { } @Test + @WithMockUserRoles public void redirects_to_strava_request_authorization_page() throws Exception { MockHttpServletResponse response = mockMvc .perform( @@ -128,6 +129,7 @@ public void redirects_to_strava_request_authorization_page() throws Exception { } @Test + @WithMockUserRoles public void requests_access_token_after_consent_is_granted() throws Exception { mockStravaServer.stubFor(WireMock.post("/oauth/token").willReturn( WireMock.aResponse() @@ -153,7 +155,7 @@ public void requests_access_token_after_consent_is_granted() throws Exception { .andReturn().getResponse(); assertThat(response2.getStatus()).isEqualTo(302); - assertThat(response2.getRedirectedUrl()).isEqualTo("http://localhost/strava/authorize"); + assertThat(response2.getRedirectedUrl()).isEqualTo("http://localhost/strava/authorize?continue"); List requests = mockStravaServer .findAll(WireMock.postRequestedFor(WireMock.urlEqualTo("/oauth/token"))); @@ -176,6 +178,7 @@ public void requests_access_token_after_consent_is_granted() throws Exception { } @Test + @WithMockUserRoles public void requests_new_access_token_if_its_expired() throws Exception { mockStravaServer.stubFor(WireMock.post("/oauth/token").willReturn( WireMock.aResponse() @@ -228,6 +231,7 @@ public void requests_new_access_token_if_its_expired() throws Exception { } @Test + @WithMockUserRoles public void returns_not_authorized_if_refresh_token_is_invalid() throws Exception { mockStravaServer.stubFor(WireMock.post("/oauth/token").willReturn( WireMock.aResponse() @@ -260,6 +264,7 @@ public void returns_not_authorized_if_refresh_token_is_invalid() throws Exceptio } @Test + @WithMockUserRoles public void pulls_todays_weight_from_strava_to_database() throws Exception { authorizeStravaOAuth2Client(); mockStravaServer.stubFor(WireMock @@ -291,7 +296,7 @@ public void pulls_todays_weight_from_strava_to_database() throws Exception { Optional ride = rideRepository.findAll().stream().findFirst(); assertThat(ride.isPresent()).isTrue(); assertThat(ride.get().getCreatedAt().format(DateTimeFormatter.ISO_ZONED_DATE_TIME)) - .isEqualTo("2018-02-16T14:52:54Z[Etc/UTC]"); + .isEqualTo("2018-02-16T14:52:54Z"); assertThat(ride.get().getName()).isEqualTo("Happy Friday"); assertThat(ride.get().getMovingTime()).isEqualTo(4207); assertThat(ride.get().getDistance()).isEqualTo(28099.0f); diff --git a/server/src/test/java/mucsi96/traininglog/WeightControllerTests.java b/server/src/test/java/mucsi96/traininglog/WeightControllerTests.java index 49bc975..c14d0f3 100644 --- a/server/src/test/java/mucsi96/traininglog/WeightControllerTests.java +++ b/server/src/test/java/mucsi96/traininglog/WeightControllerTests.java @@ -70,6 +70,7 @@ public void returns_forbidden_if_user_has_no_user_role() throws Exception { } @Test + @WithMockUserRoles public void returns_today_weight_measurement() throws Exception { MockHttpServletResponse response = mockMvc .perform( @@ -87,6 +88,7 @@ public void returns_today_weight_measurement() throws Exception { } @Test + @WithMockUserRoles public void returns_one_week_weight_measurements() throws Exception { MockHttpServletResponse response = mockMvc .perform( @@ -111,6 +113,7 @@ public void returns_one_week_weight_measurements() throws Exception { } @Test + @WithMockUserRoles public void returns_all_time_weight_measurements() throws Exception { MockHttpServletResponse response = mockMvc .perform( diff --git a/server/src/test/java/mucsi96/traininglog/WithMockUserRoles.java b/server/src/test/java/mucsi96/traininglog/WithMockUserRoles.java new file mode 100644 index 0000000..044c848 --- /dev/null +++ b/server/src/test/java/mucsi96/traininglog/WithMockUserRoles.java @@ -0,0 +1,12 @@ +package mucsi96.traininglog; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.springframework.security.test.context.support.WithSecurityContext; + +@Retention(RetentionPolicy.RUNTIME) +@WithSecurityContext(factory = WithMockUserRolesSecurityContextFactory.class) +public @interface WithMockUserRoles { + String[] value() default { "user" }; +} diff --git a/server/src/test/java/mucsi96/traininglog/WithMockUserRolesSecurityContextFactory.java b/server/src/test/java/mucsi96/traininglog/WithMockUserRolesSecurityContextFactory.java new file mode 100644 index 0000000..b12c18e --- /dev/null +++ b/server/src/test/java/mucsi96/traininglog/WithMockUserRolesSecurityContextFactory.java @@ -0,0 +1,15 @@ +package mucsi96.traininglog; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.test.context.support.WithSecurityContextFactory; + +import io.github.mucsi96.kubetools.security.TestSecurityConfigurer; + +public class WithMockUserRolesSecurityContextFactory implements WithSecurityContextFactory { + + @Override + public SecurityContext createSecurityContext(WithMockUserRoles mockUser) { + return TestSecurityConfigurer.createSecurityContext(mockUser.value()); + } + +} diff --git a/server/src/test/java/mucsi96/traininglog/WithingsControllerTests.java b/server/src/test/java/mucsi96/traininglog/WithingsControllerTests.java index d87efbf..2ed37f0 100644 --- a/server/src/test/java/mucsi96/traininglog/WithingsControllerTests.java +++ b/server/src/test/java/mucsi96/traininglog/WithingsControllerTests.java @@ -96,6 +96,7 @@ private void authorizeWithingsOAuth2Client() { } @Test + @WithMockUserRoles public void returns_not_authorized_if_authorized_client_is_not_found() throws Exception { MockHttpServletResponse response = mockMvc .perform( @@ -121,6 +122,7 @@ public void returns_forbidden_if_user_has_no_user_role() throws Exception { } @Test + @WithMockUserRoles public void redirects_to_withings_request_authorization_page() throws Exception { MockHttpServletResponse response = mockMvc .perform( @@ -141,6 +143,7 @@ public void redirects_to_withings_request_authorization_page() throws Exception } @Test + @WithMockUserRoles public void requests_access_token_after_consent_is_granted() throws Exception { mockWithingsServer.stubFor(WireMock.post("/v2/oauth2").willReturn( WireMock.aResponse() @@ -166,7 +169,7 @@ public void requests_access_token_after_consent_is_granted() throws Exception { .andReturn().getResponse(); assertThat(response2.getStatus()).isEqualTo(302); - assertThat(response2.getRedirectedUrl()).isEqualTo("http://localhost/withings/authorize"); + assertThat(response2.getRedirectedUrl()).isEqualTo("http://localhost/withings/authorize?continue"); List requests = mockWithingsServer .findAll(WireMock.postRequestedFor(WireMock.urlEqualTo("/v2/oauth2"))); @@ -190,6 +193,7 @@ public void requests_access_token_after_consent_is_granted() throws Exception { } @Test + @WithMockUserRoles public void requests_new_access_token_if_its_expired() throws Exception { mockWithingsServer.stubFor(WireMock.post("/v2/oauth2").willReturn( WireMock.aResponse() @@ -230,6 +234,7 @@ public void requests_new_access_token_if_its_expired() throws Exception { } @Test + @WithMockUserRoles public void returns_not_authorized_if_refresh_token_is_invalid() throws Exception { mockWithingsServer.stubFor(WireMock.post("/v2/oauth2").willReturn( WireMock.aResponse() @@ -262,6 +267,7 @@ public void returns_not_authorized_if_refresh_token_is_invalid() throws Exceptio } @Test + @WithMockUserRoles public void pulls_todays_weight_from_withings_to_database() throws Exception { authorizeWithingsOAuth2Client(); mockWithingsServer.stubFor(WireMock @@ -281,7 +287,7 @@ public void pulls_todays_weight_from_withings_to_database() throws Exception { Optional weight = weightRepository.findAll().stream().findFirst(); assertThat(weight.isPresent()).isTrue(); assertThat(weight.get().getCreatedAt().format(DateTimeFormatter.ISO_ZONED_DATE_TIME)) - .isEqualTo("2020-07-08T22:16:40Z[Etc/UTC]"); + .isEqualTo("2020-07-08T22:16:40Z"); assertThat(weight.get().getWeight()).isEqualTo(65.8f); assertThat(weight.get().getFatRatio()).isEqualTo(32.3f); assertThat(weight.get().getFatMassWeight()).isEqualTo(21.8f);