diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/config/SecurityConfig.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/config/SecurityConfig.java index d4cb06e..4043c09 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/config/SecurityConfig.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/config/SecurityConfig.java @@ -1,10 +1,10 @@ package net.shyshkin.study.fullstack.supportportal.backend.config; import lombok.RequiredArgsConstructor; -import net.shyshkin.study.fullstack.supportportal.backend.constant.SecurityConstants; import net.shyshkin.study.fullstack.supportportal.backend.filter.JwtAccessDeniedHandler; import net.shyshkin.study.fullstack.supportportal.backend.filter.JwtAuthenticationEntryPoint; import net.shyshkin.study.fullstack.supportportal.backend.filter.JwtAuthorizationFilter; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @@ -34,6 +34,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { private final JwtAccessDeniedHandler jwtAccessDeniedHandler; private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + @Value("${app.public-urls}") + private String[] publicUrls; + @Override protected void configure(HttpSecurity http) throws Exception { @@ -44,7 +47,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.authorizeRequests() - .antMatchers(SecurityConstants.PUBLIC_URLS).permitAll() + .antMatchers(publicUrls).permitAll() .anyRequest().authenticated(); http.exceptionHandling() diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/constant/SecurityConstants.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/constant/SecurityConstants.java index 7b1a2f2..f4e8974 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/constant/SecurityConstants.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/constant/SecurityConstants.java @@ -11,5 +11,4 @@ public class SecurityConstants { public static final String FORBIDDEN_MESSAGE = "You need to log in to access this page"; public static final String ACCESS_DENIED_MESSAGE = "You do not have permission to access this page"; public static final String OPTIONS_HTTP_METHOD = "OPTIONS"; - public static final String[] PUBLIC_URLS = { "/user/login", "/user/register", "/user/image/**" }; } diff --git a/support-portal-backend/src/main/resources/application.yml b/support-portal-backend/src/main/resources/application.yml index b3fb32a..d06f313 100644 --- a/support-portal-backend/src/main/resources/application.yml +++ b/support-portal-backend/src/main/resources/application.yml @@ -11,6 +11,7 @@ spring: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect app: + public-urls: /user/login,/user/register,/user/image/** jwt: secret: VeRy_5ecretP@55W0rd! # secret: ${random.value} #Does not work - every time generates new value diff --git a/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/ExceptionsController.java b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/ExceptionsController.java new file mode 100644 index 0000000..26447a9 --- /dev/null +++ b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/ExceptionsController.java @@ -0,0 +1,22 @@ +package net.shyshkin.study.fullstack.supportportal.backend.controller; + +import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.EmailExistsException; +import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.UserNotFoundException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/exceptions") +public class ExceptionsController { + + @GetMapping("/emailExists") + public String emailExistsException() throws EmailExistsException { + throw new EmailExistsException("This email is already taken"); + } + + @GetMapping("/userNotFound") + public String userNotFoundException() throws UserNotFoundException { + throw new UserNotFoundException("The user was not found"); + } +} diff --git a/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/ExceptionsControllerTest.java b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/ExceptionsControllerTest.java new file mode 100644 index 0000000..1a33414 --- /dev/null +++ b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/ExceptionsControllerTest.java @@ -0,0 +1,56 @@ +package net.shyshkin.study.fullstack.supportportal.backend.controller; + +import lombok.extern.slf4j.Slf4j; +import net.shyshkin.study.fullstack.supportportal.backend.domain.HttpResponse; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.within; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +@Slf4j +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestPropertySource(properties = { + "app.public-urls=**" +}) +@ActiveProfiles("local") +class ExceptionsControllerTest { + + @Autowired + TestRestTemplate restTemplate; + + @ParameterizedTest + @CsvSource({ + "emailExists,THIS EMAIL IS ALREADY TAKEN", + "userNotFound,THE USER WAS NOT FOUND" + }) + void badRequestException(String endpoint, String expectedMessage) { + + //when + var responseEntity = restTemplate.getForEntity("/exceptions/{endpoint}", HttpResponse.class, endpoint); + + //then + log.debug("Response entity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + assertThat(responseEntity.getBody()) + .isNotNull() + .hasNoNullFieldsOrProperties() + .satisfies(httpResponse -> assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(BAD_REQUEST), + () -> assertThat(httpResponse.getHttpStatusCode()).isEqualTo(BAD_REQUEST.value()), + () -> assertThat(httpResponse.getMessage()).isEqualTo(expectedMessage), + () -> assertThat(httpResponse.getReason()).isEqualTo(BAD_REQUEST.getReasonPhrase().toUpperCase()), + () -> assertThat(httpResponse.getTimestamp()).isCloseTo(LocalDateTime.now(), within(200, ChronoUnit.MILLIS)) + )); + } +} \ No newline at end of file