diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..13566b81b --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/be/issue/src/main/java/codesquad/issueTracker/user/controller/UserController.java b/be/issue/src/main/java/codesquad/issueTracker/user/controller/UserController.java index 5a4b6a950..45eefcd26 100644 --- a/be/issue/src/main/java/codesquad/issueTracker/user/controller/UserController.java +++ b/be/issue/src/main/java/codesquad/issueTracker/user/controller/UserController.java @@ -15,6 +15,7 @@ import codesquad.issueTracker.global.ApiResponse; import codesquad.issueTracker.jwt.dto.RequestRefreshTokenDto; +import codesquad.issueTracker.jwt.dto.ResponseAccessToken; import codesquad.issueTracker.oauth.service.OAuthService; import codesquad.issueTracker.user.dto.LoginRequestDto; import codesquad.issueTracker.user.dto.LoginResponseDto; @@ -44,8 +45,8 @@ public ApiResponse login(@Valid @RequestBody LoginRequestDto l } @PostMapping("/reissue/token") - public ApiResponse reissueToken(@RequestBody RequestRefreshTokenDto requestRefreshTokenDto) { - String accessToken = userService.reissueAccessToken(requestRefreshTokenDto); + public ApiResponse reissueToken(@RequestBody RequestRefreshTokenDto requestRefreshTokenDto) { + ResponseAccessToken accessToken = userService.reissueAccessToken(requestRefreshTokenDto); return ApiResponse.success(SUCCESS.getStatus(), accessToken); } diff --git a/be/issue/src/main/java/codesquad/issueTracker/user/domain/User.java b/be/issue/src/main/java/codesquad/issueTracker/user/domain/User.java index 0aa94bd62..060e8bb5c 100644 --- a/be/issue/src/main/java/codesquad/issueTracker/user/domain/User.java +++ b/be/issue/src/main/java/codesquad/issueTracker/user/domain/User.java @@ -30,7 +30,7 @@ public User(Long id, String email, String password, String profileImg, String na } public void validateLoginUser(LoginRequestDto loginRequestDto) { - if (password == null) { + if (password == null || !loginType.equals(LoginType.LOCAL)) { throw new CustomException(ErrorCode.GITHUB_LOGIN_USER); } if (!loginRequestDto.getEmail().equals(email) diff --git a/be/issue/src/main/java/codesquad/issueTracker/user/dto/SignUpRequestDto.java b/be/issue/src/main/java/codesquad/issueTracker/user/dto/SignUpRequestDto.java index b67b36921..cb2029418 100644 --- a/be/issue/src/main/java/codesquad/issueTracker/user/dto/SignUpRequestDto.java +++ b/be/issue/src/main/java/codesquad/issueTracker/user/dto/SignUpRequestDto.java @@ -18,7 +18,7 @@ public class SignUpRequestDto { @Size(min = 6, max = 12, message = "비밀번호는 6자리에서 12자리까지 입력할 수 있습니다.") private final String password; - @Size(min = 6, max = 16, message = "아이디는 최소 6자리에서 16자리까지 입력할 수 있다.") + @Size(min = 2, max = 12, message = "이름은 최소 6자리에서 16자리까지 입력할 수 있다.") private final String name; public static User toEntity(SignUpRequestDto signUpRequestDto, String encodedPassword, String profileImg) { diff --git a/be/issue/src/main/java/codesquad/issueTracker/user/service/UserService.java b/be/issue/src/main/java/codesquad/issueTracker/user/service/UserService.java index 92ebdc67d..3282f427e 100644 --- a/be/issue/src/main/java/codesquad/issueTracker/user/service/UserService.java +++ b/be/issue/src/main/java/codesquad/issueTracker/user/service/UserService.java @@ -13,8 +13,8 @@ import codesquad.issueTracker.jwt.domain.Jwt; import codesquad.issueTracker.jwt.domain.Token; import codesquad.issueTracker.jwt.dto.RequestRefreshTokenDto; +import codesquad.issueTracker.jwt.dto.ResponseAccessToken; import codesquad.issueTracker.jwt.util.JwtProvider; -import codesquad.issueTracker.user.domain.LoginType; import codesquad.issueTracker.user.domain.User; import codesquad.issueTracker.user.dto.LoginRequestDto; import codesquad.issueTracker.user.dto.LoginResponseDto; @@ -51,8 +51,6 @@ public LoginResponseDto login(LoginRequestDto loginRequestDto) { .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER)); user.validateLoginUser(loginRequestDto); - userValidator.validateLoginType(LoginType.LOCAL, user.getLoginType()); - Jwt jwt = jwtProvider.createJwt(Map.of("userId", user.getId())); insertOrUpdateToken(user.getId(), jwt); @@ -89,12 +87,13 @@ public User findExistedOrInsertedUser(User user) { * 2. DB에 없는 리프레시 토큰이면 예외처리 */ @Transactional(readOnly = true) - public String reissueAccessToken(RequestRefreshTokenDto refreshTokenDto) { + public ResponseAccessToken reissueAccessToken(RequestRefreshTokenDto refreshTokenDto) { jwtProvider.getClaims(refreshTokenDto.getRefreshToken()); Token token = userRepository.findTokenByUserToken(refreshTokenDto.getRefreshToken()) .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_REFRESH_TOKEN)); - return jwtProvider.reissueAccessToken(Map.of("userId", token.getUserId())); + return new ResponseAccessToken(jwtProvider.reissueAccessToken(Map.of("userId", token.getUserId()))); + } public void logout(HttpServletRequest request) { diff --git a/be/issue/src/main/java/codesquad/issueTracker/user/service/UserValidator.java b/be/issue/src/main/java/codesquad/issueTracker/user/service/UserValidator.java index 7c8927af6..b2c12e4f4 100644 --- a/be/issue/src/main/java/codesquad/issueTracker/user/service/UserValidator.java +++ b/be/issue/src/main/java/codesquad/issueTracker/user/service/UserValidator.java @@ -15,7 +15,7 @@ public class UserValidator { private final UserRepository userRepository; public void validateLoginType(LoginType inputLoginType, LoginType existLoginType) { - if (inputLoginType != existLoginType) { + if (!inputLoginType.equals(existLoginType)) { throw new CustomException(ErrorCode.FAILED_LOGIN_USER); } } diff --git a/be/issue/src/test/java/codesquad/issueTracker/user/service/UserServiceTest.java b/be/issue/src/test/java/codesquad/issueTracker/user/service/UserServiceTest.java new file mode 100644 index 000000000..f49901e2e --- /dev/null +++ b/be/issue/src/test/java/codesquad/issueTracker/user/service/UserServiceTest.java @@ -0,0 +1,153 @@ +package codesquad.issueTracker.user.service; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mindrot.jbcrypt.BCrypt; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import annotation.ServiceTest; +import codesquad.issueTracker.global.exception.CustomException; +import codesquad.issueTracker.jwt.domain.Jwt; +import codesquad.issueTracker.jwt.domain.Token; +import codesquad.issueTracker.jwt.util.JwtProvider; +import codesquad.issueTracker.user.domain.LoginType; +import codesquad.issueTracker.user.domain.User; +import codesquad.issueTracker.user.dto.LoginRequestDto; +import codesquad.issueTracker.user.dto.LoginResponseDto; +import codesquad.issueTracker.user.repository.UserRepository; + +@ServiceTest +public class UserServiceTest { + + @InjectMocks + UserService userService; + @Mock + UserRepository userRepository; + @Mock + UserValidator userValidator; + @Mock + JwtProvider jwtProvider; + + @DisplayName("로그인 성공") + @Test + void loginSuccess() { + //given + User mockUser = User.builder() + .id(1L) + .email("rkarbf@gmail.com") + .password(BCrypt.hashpw("password", BCrypt.gensalt())) + .loginType(LoginType.LOCAL) + .build(); + + Jwt mockJwt = new Jwt("accessToken", "refreshToken"); + + given(userRepository.findByEmail(mockUser.getEmail())).willReturn(Optional.of(mockUser)); + given(jwtProvider.createJwt(anyMap())).willReturn(mockJwt); + + LoginRequestDto loginRequestDto = new LoginRequestDto("rkarbf@gmail.com", "password"); + + //when + LoginResponseDto response = userService.login(loginRequestDto); + + //then + assertAll( + () -> assertEquals(mockUser.getId(), response.getUserId()), + () -> assertEquals("accessToken", response.getAccessToken()), + () -> assertEquals("refreshToken", response.getRefreshToken()) + ); + } + + @DisplayName("존재하지 않는 유저 로그인 실패") + @Test + void loginFailedByNotFoundUser() { + given(userRepository.findByEmail(any())).willReturn(Optional.empty()); + LoginRequestDto loginRequestDto = new LoginRequestDto("rkarbf@gmail.com", "password"); + + //when + assertThrows(CustomException.class, () -> userService.login(loginRequestDto)); + } + + @DisplayName("비밀번호 불일치 로그인 실패") + @Test + void loginFailedByWrongPassword() { + User mockUser = User.builder() + .id(1L) + .email("rkarbf@gmail.com") + .password(BCrypt.hashpw("password", BCrypt.gensalt())) + .loginType(LoginType.LOCAL) + .build(); + + given(userRepository.findByEmail(mockUser.getEmail())).willReturn(Optional.of(mockUser)); + LoginRequestDto loginRequestDto = new LoginRequestDto("rkarbf@gmail.com", "passwor"); + + //when + assertThrows(CustomException.class, () -> userService.login(loginRequestDto)); + } + + @DisplayName("로그인타입 불일치 로그인 실패") + @Test + void loginFailedByLoginTypeDiff() { + User mockUser = User.builder() + .id(1L) + .email("rkarbf@gmail.com") + .password(BCrypt.hashpw("password", BCrypt.gensalt())) + .loginType(LoginType.GITHUB) + .build(); + + given(userRepository.findByEmail(mockUser.getEmail())).willReturn(Optional.of(mockUser)); + LoginRequestDto loginRequestDto = new LoginRequestDto("rkarbf@gmail.com", "password"); + + //when + assertThrows(CustomException.class, () -> userService.login(loginRequestDto)); + } + + @DisplayName("로그인 시 리프레쉬 토큰이 존재하지 않을 때 토큰을 생성하는지 테스트") + @Test + void loginWhenRefreshTokenIsNull() { + User mockUser = User.builder() + .id(1L) + .email("rkarbf@gmail.com") + .password(BCrypt.hashpw("password", BCrypt.gensalt())) + .loginType(LoginType.LOCAL) + .build(); + + Jwt mockJwt = new Jwt("accessToken", "refreshToken"); + + given(userRepository.findByEmail(mockUser.getEmail())).willReturn(Optional.of(mockUser)); + given(jwtProvider.createJwt(anyMap())).willReturn(mockJwt); + given(userRepository.findTokenByUserId(1L)).willReturn(Optional.empty()); + + LoginRequestDto loginRequestDto = new LoginRequestDto("rkarbf@gmail.com", "password"); + userService.login(loginRequestDto); + verify(userRepository, times(1)).insertRefreshToken(1L, mockJwt.getRefreshToken()); + verify(userRepository, times(0)).updateRefreshToken(1L, mockJwt.getRefreshToken()); + } + + @DisplayName("로그인 시 리프레쉬 토큰이 존재할 때 토큰을 업데이트하는지 테스트") + @Test + void loginWhenRefreshTokenIsExist() { + User mockUser = User.builder() + .id(1L) + .email("rkarbf@gmail.com") + .password(BCrypt.hashpw("password", BCrypt.gensalt())) + .loginType(LoginType.LOCAL) + .build(); + + Jwt mockJwt = new Jwt("accessToken", "refreshToken"); + + given(userRepository.findByEmail(mockUser.getEmail())).willReturn(Optional.of(mockUser)); + given(jwtProvider.createJwt(anyMap())).willReturn(mockJwt); + given(userRepository.findTokenByUserId(1L)).willReturn(Optional.of(new Token(1L, 1L, "dummy"))); + + LoginRequestDto loginRequestDto = new LoginRequestDto("rkarbf@gmail.com", "password"); + userService.login(loginRequestDto); + verify(userRepository, times(0)).insertRefreshToken(1L, mockJwt.getRefreshToken()); + verify(userRepository, times(1)).updateRefreshToken(1L, mockJwt.getRefreshToken()); + } +} diff --git a/be/issue/src/test/java/codesquad/issueTracker/user/service/UserValidatorTest.java b/be/issue/src/test/java/codesquad/issueTracker/user/service/UserValidatorTest.java index 7d3ddcc56..510570c2b 100644 --- a/be/issue/src/test/java/codesquad/issueTracker/user/service/UserValidatorTest.java +++ b/be/issue/src/test/java/codesquad/issueTracker/user/service/UserValidatorTest.java @@ -52,10 +52,11 @@ void validateLoginUserFailed() { .id(1L) .email("asdfff123@ddd.com") .password(BCrypt.hashpw("12345678", BCrypt.gensalt())) + .loginType(LoginType.LOCAL) .build(); - given(userRepository.findByEmail("asd123@ddd.com")).willReturn(Optional.of(user)); - User findUser = userRepository.findByEmail("asd123@ddd.com").orElseThrow(); + given(userRepository.findByEmail("asd123@dddd.com")).willReturn(Optional.of(user)); + User findUser = userRepository.findByEmail("asd123@dddd.com").orElseThrow(); LoginRequestDto loginRequestDto = new LoginRequestDto("asd123@dddd.com", "123dd45678"); assertThrows(CustomException.class, () -> { @@ -77,7 +78,7 @@ public void validateLoginTypeSuccess() { given(userRepository.findByEmail(any())).willReturn(Optional.ofNullable(existUser)); LoginType existUserLoginType = userRepository.findByEmail(existUser.getEmail()).get().getLoginType(); assertDoesNotThrow(() -> { - // userValidator.validateLoginType(input, existUserLoginType); + userValidator.validateLoginType(input, existUserLoginType); }); } @@ -96,7 +97,7 @@ public void validateLoginTypeFailed() { given(userRepository.findByEmail(any())).willReturn(Optional.ofNullable(existUser)); LoginType existUserLoginType = userRepository.findByEmail(existUser.getEmail()).get().getLoginType(); assertThrows(CustomException.class, () -> { - // userValidator.validateLoginType(input, existUserLoginType); + userValidator.validateLoginType(input, existUserLoginType); }); } @@ -113,7 +114,7 @@ public void validateDuplicatedEmailSuccess() { given(userRepository.findByEmail(any())).willReturn(Optional.empty()); assertDoesNotThrow(() -> { - // userValidator.validateDuplicatedEmail(signUpRequestDto); + userValidator.validateDuplicatedEmail(signUpRequestDto); }); } @@ -129,7 +130,7 @@ public void validateDuplicatedEmailFailed() { given(userRepository.findByEmail(any())).willReturn(Optional.ofNullable(existUser)); assertThrows(CustomException.class, () -> { - // userValidator.validateDuplicatedEmail(signUpRequestDto); + userValidator.validateDuplicatedEmail(signUpRequestDto); }); }