diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/config/PasswordEncoderConfig.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/config/PasswordEncoderConfig.java new file mode 100644 index 0000000..46607b9 --- /dev/null +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/config/PasswordEncoderConfig.java @@ -0,0 +1,29 @@ +package net.shyshkin.study.fullstack.supportportal.backend.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.DelegatingPasswordEncoder; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; +import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class PasswordEncoderConfig { + + @Bean + PasswordEncoder passwordEncoder() { + String idForEncode = "bcrypt"; + Map encoders = new HashMap<>(); + encoders.put(idForEncode, new BCryptPasswordEncoder()); + encoders.put("noop", NoOpPasswordEncoder.getInstance()); + encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); + encoders.put("scrypt", new SCryptPasswordEncoder()); + + return new DelegatingPasswordEncoder(idForEncode, encoders); + } +} 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 4043c09..0fdba14 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 @@ -5,7 +5,6 @@ import net.shyshkin.study.fullstack.supportportal.backend.filter.JwtAccessDenied 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; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -13,26 +12,19 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.DelegatingPasswordEncoder; -import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; -import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import java.util.HashMap; -import java.util.Map; - @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) @RequiredArgsConstructor public class SecurityConfig extends WebSecurityConfigurerAdapter { private final JwtAuthorizationFilter jwtAuthorizationFilter; - private final UserDetailsService userService; + private final UserDetailsService userDetailsService; private final JwtAccessDeniedHandler jwtAccessDeniedHandler; private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final PasswordEncoder passwordEncoder; @Value("${app.public-urls}") private String[] publicUrls; @@ -60,20 +52,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth - .userDetailsService(userService) - .passwordEncoder(passwordEncoder()); + .userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder); } - - @Bean - PasswordEncoder passwordEncoder() { - String idForEncode = "bcrypt"; - Map encoders = new HashMap<>(); - encoders.put(idForEncode, new BCryptPasswordEncoder()); - encoders.put("noop", NoOpPasswordEncoder.getInstance()); - encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); - encoders.put("scrypt", new SCryptPasswordEncoder()); - - return new DelegatingPasswordEncoder(idForEncode, encoders); - } - } diff --git a/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/common/BaseUserTest.java b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/common/BaseUserTest.java index be7e7ff..f373769 100644 --- a/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/common/BaseUserTest.java +++ b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/common/BaseUserTest.java @@ -19,7 +19,7 @@ public abstract class BaseUserTest { @Autowired protected UserRepository userRepository; - protected User user; + protected static User user; protected User createRandomUser() { return User.builder() diff --git a/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResourceTest.java b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResourceTest.java index bc5e03a..72972aa 100644 --- a/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResourceTest.java +++ b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResourceTest.java @@ -6,20 +6,24 @@ import net.shyshkin.study.fullstack.supportportal.backend.domain.HttpResponse; import net.shyshkin.study.fullstack.supportportal.backend.domain.User; import net.shyshkin.study.fullstack.supportportal.backend.domain.UserPrincipal; import net.shyshkin.study.fullstack.supportportal.backend.utility.JwtTokenProvider; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; 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.http.HttpHeaders; import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import static org.springframework.http.HttpStatus.FORBIDDEN; -import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.HttpStatus.*; @Slf4j @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestMethodOrder(value = MethodOrderer.OrderAnnotation.class) class UserResourceTest extends BaseUserTest { @Autowired @@ -29,6 +33,7 @@ class UserResourceTest extends BaseUserTest { JwtTokenProvider jwtTokenProvider; @Test + @Order(10) void showUserHome_forbidden() { //when @@ -49,6 +54,7 @@ class UserResourceTest extends BaseUserTest { } @Test + @Order(20) void showUserHome_correctToken() { //given @@ -71,4 +77,86 @@ class UserResourceTest extends BaseUserTest { .isNotNull() .isEqualTo("Application works"); } + + @Test + @Order(30) + void registerUser_new() { + + //given + User fakeUser = createRandomUser(); + + //when + ResponseEntity responseEntity = restTemplate.postForEntity("/user/register", fakeUser, User.class); + + //then + log.debug("Response Entity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(OK); + User registeredUser = responseEntity.getBody(); + assertThat(registeredUser) + .isNotNull() + .hasNoNullFieldsOrPropertiesExcept("lastLoginDate", "lastLoginDateDisplay") + .hasFieldOrPropertyWithValue("username", fakeUser.getUsername()) + .hasFieldOrPropertyWithValue("email", fakeUser.getEmail()) + .hasFieldOrPropertyWithValue("firstName", fakeUser.getFirstName()) + .hasFieldOrPropertyWithValue("lastName", fakeUser.getLastName()) + .hasFieldOrPropertyWithValue("isActive", true) + .hasFieldOrPropertyWithValue("isNotLocked", true) + .hasFieldOrPropertyWithValue("role", "ROLE_USER") + ; + user = registeredUser; + } + + @Test + @Order(40) + void registerUser_usernameExists() { + + //given + User fakeUser = createRandomUser(); + String username = user.getUsername(); + fakeUser.setUsername(username); + String expectedMessage = ("Username `" + username + "` is already taken. Please select another one").toUpperCase(); + + //when + ResponseEntity responseEntity = restTemplate.postForEntity("/user/register", fakeUser, HttpResponse.class); + + //then + log.debug("Response Entity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + assertThat(responseEntity.getBody()) + .isNotNull() + .hasNoNullFieldsOrProperties() + .satisfies(httpResponse -> assertAll( + () -> assertThat(httpResponse.getHttpStatusCode()).isEqualTo(400), + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(BAD_REQUEST), + () -> assertThat(httpResponse.getReason()).isEqualTo("BAD REQUEST"), + () -> assertThat(httpResponse.getMessage()).isEqualTo(expectedMessage) + )); + } + + @Test + @Order(41) + void registerUser_emailExists() { + + //given + User fakeUser = createRandomUser(); + String email = user.getEmail(); + fakeUser.setEmail(email); + String expectedMessage = ("User with email `" + email + "` is already registered").toUpperCase(); + + //when + ResponseEntity responseEntity = restTemplate.postForEntity("/user/register", fakeUser, HttpResponse.class); + + //then + log.debug("Response Entity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + assertThat(responseEntity.getBody()) + .isNotNull() + .hasNoNullFieldsOrProperties() + .satisfies(httpResponse -> assertAll( + () -> assertThat(httpResponse.getHttpStatusCode()).isEqualTo(400), + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(BAD_REQUEST), + () -> assertThat(httpResponse.getReason()).isEqualTo("BAD REQUEST"), + () -> assertThat(httpResponse.getMessage()).isEqualTo(expectedMessage) + )); + } } \ No newline at end of file