Skip to content

Commit

Permalink
Merge branch 'develop' into feature/9
Browse files Browse the repository at this point in the history
  • Loading branch information
jhsseonn authored Feb 8, 2024
2 parents 668ab23 + 46a1e6f commit 2259928
Show file tree
Hide file tree
Showing 26 changed files with 543 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/dev-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
host: ${{ secrets.DEV_PUBLIC_IP }}
key: ${{ secrets.DEV_PRIVATE_KEY }}
script: |
sudo /home/ubuntu/deploy.sh
sudo sh /home/ubuntu/deploy.sh
- name: send result to slack
if: always()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.backend.blooming.authentication.application.dto.LoginDto;
import com.backend.blooming.authentication.application.dto.LoginInformationDto;
import com.backend.blooming.authentication.application.dto.LoginUserInformationDto;
import com.backend.blooming.authentication.application.dto.LogoutDto;
import com.backend.blooming.authentication.application.dto.TokenDto;
import com.backend.blooming.authentication.application.util.OAuthClientComposite;
import com.backend.blooming.authentication.infrastructure.exception.InvalidTokenException;
Expand All @@ -13,6 +14,7 @@
import com.backend.blooming.authentication.infrastructure.oauth.OAuthType;
import com.backend.blooming.authentication.infrastructure.oauth.dto.UserInformationDto;
import com.backend.blooming.devicetoken.application.service.DeviceTokenService;
import com.backend.blooming.user.application.exception.NotFoundUserException;
import com.backend.blooming.user.domain.Email;
import com.backend.blooming.user.domain.Name;
import com.backend.blooming.user.domain.User;
Expand All @@ -33,6 +35,7 @@ public class AuthenticationService {
private final OAuthClientComposite oAuthClientComposite;
private final TokenProvider tokenProvider;
private final UserRepository userRepository;
private final BlackListTokenService blackListTokenService;
private final DeviceTokenService deviceTokenService;

public LoginInformationDto login(final OAuthType oAuthType, final LoginDto loginDto) {
Expand Down Expand Up @@ -88,7 +91,7 @@ private TokenDto convertToTokenDto(final User user) {

private void saveOrActiveToken(final User user, final String deviceToken) {
if (deviceToken != null && !deviceToken.isEmpty()) {
deviceTokenService.saveOrActive(user.getId(), deviceToken);
deviceTokenService.saveOrActivate(user.getId(), deviceToken);
}
}

Expand All @@ -107,4 +110,30 @@ private void validateUser(final Long userId) {
throw new InvalidTokenException();
}
}

public void logout(final Long userId, final LogoutDto logoutDto) {
final AuthClaims authClaims = tokenProvider.parseToken(TokenType.REFRESH, logoutDto.refreshToken());
final User user = validateAndGetUser(userId, authClaims);

blackListTokenService.register(logoutDto.refreshToken());
deviceTokenService.deactivate(user.getId(), logoutDto.deviceToken());
}

private User validateAndGetUser(final Long userId, final AuthClaims authClaims) {
if (!userId.equals(authClaims.userId())) {
throw new InvalidTokenException();
}

return userRepository.findById(userId)
.orElseThrow(NotFoundUserException::new);
}

public void withdraw(final Long userId, final String refreshToken) {
final AuthClaims authClaims = tokenProvider.parseToken(TokenType.REFRESH, refreshToken);
final User user = validateAndGetUser(userId, authClaims);

user.delete();
blackListTokenService.register(refreshToken);
deviceTokenService.deactivateAllByUserId(user.getId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.backend.blooming.authentication.application;

import com.backend.blooming.authentication.application.exception.AlreadyRegisterBlackListTokenException;
import com.backend.blooming.authentication.domain.BlackListToken;
import com.backend.blooming.authentication.infrastructure.blacklist.BlackListTokenRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class BlackListTokenService {

private final BlackListTokenRepository blackListTokenRepository;

public Long register(final String token) {
validateToken(token);
final BlackListToken blackListToken = new BlackListToken(token);

return blackListTokenRepository.save(blackListToken)
.getId();
}

private void validateToken(String token) {
if (blackListTokenRepository.existsByToken(token)) {
throw new AlreadyRegisterBlackListTokenException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.backend.blooming.authentication.application.dto;

import com.backend.blooming.authentication.presentation.dto.LogoutRequest;

public record LogoutDto(String refreshToken, String deviceToken) {

public static LogoutDto from(final LogoutRequest logoutRequest) {
return new LogoutDto(logoutRequest.refreshToken(), logoutRequest.deviceToken());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.backend.blooming.authentication.application.exception;

import com.backend.blooming.exception.BloomingException;
import com.backend.blooming.exception.ExceptionMessage;

public class AlreadyRegisterBlackListTokenException extends BloomingException {

public AlreadyRegisterBlackListTokenException() {
super(ExceptionMessage.ALREADY_REGISTER_BLACK_LIST_TOKEN);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ public class AuthenticationWebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/auth/**");
.addPathPatterns("/**");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.backend.blooming.authentication.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@EqualsAndHashCode(of = "id", callSuper = false)
@ToString
@Table
public class BlackListToken {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String token;

public BlackListToken(final String token) {
this.token = token;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.backend.blooming.authentication.infrastructure.blacklist;

import com.backend.blooming.authentication.domain.BlackListToken;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface BlackListTokenRepository extends JpaRepository<BlackListToken, Long> {

Optional<BlackListToken> findByToken(final String token);

boolean existsByToken(final String token);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@
import com.backend.blooming.authentication.application.AuthenticationService;
import com.backend.blooming.authentication.application.dto.LoginDto;
import com.backend.blooming.authentication.application.dto.LoginInformationDto;
import com.backend.blooming.authentication.application.dto.LogoutDto;
import com.backend.blooming.authentication.application.dto.TokenDto;
import com.backend.blooming.authentication.infrastructure.oauth.OAuthType;
import com.backend.blooming.authentication.presentation.anotaion.Authenticated;
import com.backend.blooming.authentication.presentation.argumentresolver.AuthenticatedUser;
import com.backend.blooming.authentication.presentation.dto.LogoutRequest;
import com.backend.blooming.authentication.presentation.dto.WithdrawRequest;
import com.backend.blooming.authentication.presentation.dto.request.ReissueAccessTokenRequest;
import com.backend.blooming.authentication.presentation.dto.response.LoginInformationResponse;
import com.backend.blooming.authentication.presentation.dto.response.SocialLoginRequest;
import com.backend.blooming.authentication.presentation.dto.response.TokenResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -45,4 +51,26 @@ public ResponseEntity<TokenResponse> reissueAccessToken(

return ResponseEntity.ok(TokenResponse.from(tokenDto));
}

@PostMapping(value = "/logout", headers = "X-API-VERSION=1")
public ResponseEntity<Void> logout(
@Authenticated final AuthenticatedUser authenticatedUser,
@RequestBody final LogoutRequest logoutRequest
) {
authenticationService.logout(authenticatedUser.userId(), LogoutDto.from(logoutRequest));

return ResponseEntity.noContent()
.build();
}

@DeleteMapping(headers = "X-API-VERSION=1")
public ResponseEntity<Void> withdraw(
@Authenticated final AuthenticatedUser authenticatedUser,
@RequestBody final WithdrawRequest withdrawRequest
) {
authenticationService.withdraw(authenticatedUser.userId(), withdrawRequest.refreshToken());

return ResponseEntity.noContent()
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.backend.blooming.authentication.presentation.dto;

public record LogoutRequest(String refreshToken, String deviceToken) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.backend.blooming.authentication.presentation.dto;

public record WithdrawRequest(String refreshToken) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Service
@Transactional
Expand All @@ -16,7 +17,7 @@ public class DeviceTokenService {

private final DeviceTokenRepository deviceTokenRepository;

public Long saveOrActive(final Long userId, final String token) {
public Long saveOrActivate(final Long userId, final String token) {
final DeviceToken deviceToken = findOrPersistDeviceToken(userId, token);
activateIfInactive(deviceToken);

Expand Down Expand Up @@ -46,4 +47,14 @@ public ReadDeviceTokensDto readAllByUserId(final Long userId) {

return ReadDeviceTokensDto.from(deviceTokens);
}

public void deactivate(final Long userId, final String token) {
final Optional<DeviceToken> deviceToken = deviceTokenRepository.findByUserIdAndToken(userId, token);
deviceToken.ifPresent(DeviceToken::deactivate);
}

public void deactivateAllByUserId(final Long userId) {
final List<DeviceToken> deviceTokens = deviceTokenRepository.findAllByUserIdAndActiveIsTrue(userId);
deviceTokens.forEach(DeviceToken::deactivate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public enum ExceptionMessage {
EXPIRED_TOKEN("기한이 만료된 토큰입니다."),
UNSUPPORTED_OAUTH_TYPE("지원하지 않는 소셜 로그인 방식입니다."),

// 블랙 리스트 토큰
ALREADY_REGISTER_BLACK_LIST_TOKEN("이미 등록된 블랙 리스트 토큰입니다."),

// 사용자
NOT_FOUND_USER("사용자를 조회할 수 없습니다."),
NULL_OR_EMPTY_EMAIL("이메일은 비어있을 수 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.backend.blooming.exception;

import com.backend.blooming.authentication.application.exception.AlreadyRegisterBlackListTokenException;
import com.backend.blooming.authentication.infrastructure.exception.InvalidTokenException;
import com.backend.blooming.authentication.infrastructure.exception.OAuthException;
import com.backend.blooming.authentication.infrastructure.exception.UnsupportedOAuthTypeException;
Expand Down Expand Up @@ -194,4 +195,14 @@ public ResponseEntity<ExceptionResponse> handleUpdateGoalForbiddenException(
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(new ExceptionResponse(exception.getMessage()));
}

@ExceptionHandler(AlreadyRegisterBlackListTokenException.class)
public ResponseEntity<ExceptionResponse> handleAlreadyRegisterBlackListTokenException(
final AlreadyRegisterBlackListTokenException exception
) {
logger.warn(String.format(LOG_MESSAGE_FORMAT, exception.getClass().getSimpleName(), exception.getMessage()));

return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ExceptionResponse(exception.getMessage()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public class FriendService {
public Long request(final Long userId, final Long friendId) {
validateFriendStatus(userId, friendId);

final User user = findUser(userId);
final User friendUser = findUser(friendId);
final User user = getUser(userId);
final User friendUser = getUser(friendId);
final Friend friend = new Friend(user, friendUser);
friendRepository.save(friend);

Expand All @@ -46,44 +46,44 @@ private void validateFriendStatus(final Long userId, final Long friendId) {
}
}

private User findUser(final Long userId) {
private User getUser(final Long userId) {
return userRepository.findByIdAndDeletedIsFalse(userId)
.orElseThrow(NotFoundUserException::new);
}

@Transactional(readOnly = true)
public ReadFriendsDto readAllByRequestId(final Long userId) {
final User user = findUser(userId);
final User user = getUser(userId);
final List<Friend> requestUsers = friendRepository.findAllByRequestUserId(userId);

return ReadFriendsDto.of(requestUsers, user, FriendType.REQUEST);
}

@Transactional(readOnly = true)
public ReadFriendsDto readAllByRequestedId(final Long userId) {
final User user = findUser(userId);
final User user = getUser(userId);
final List<Friend> requestedUser = friendRepository.findAllByRequestedUserId(userId);

return ReadFriendsDto.of(requestedUser, user, FriendType.REQUESTED);
}

@Transactional(readOnly = true)
public ReadFriendsDto readAllMutualByUserId(final Long userId) {
final User user = findUser(userId);
final User user = getUser(userId);
final List<Friend> friends = friendRepository.findAllByUserIdAndIsFriends(userId);

return ReadFriendsDto.of(friends, user, FriendType.FRIENDS);
}

public void accept(final Long userId, final Long requestId) {
final User user = findUser(userId);
final Friend friend = findFriend(requestId);
final User user = getUser(userId);
final Friend friend = getFriend(requestId);
validateRequestedUser(user, friend);

friend.acceptRequest();
}

private Friend findFriend(final Long requestId) {
private Friend getFriend(final Long requestId) {
return friendRepository.findById(requestId)
.orElseThrow(NotFoundFriendRequestException::new);
}
Expand All @@ -95,8 +95,8 @@ private void validateRequestedUser(final User user, final Friend friend) {
}

public void delete(final Long userId, final Long requestId) {
final User user = findUser(userId);
final Friend friend = findFriend(requestId);
final User user = getUser(userId);
final Friend friend = getFriend(requestId);
validateCanDelete(user, friend);

friendRepository.delete(friend);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public ReadUserDto updateById(final Long userId, final UpdateUserDto updateUserD
final User user = userRepository.findById(userId)
.orElseThrow(NotFoundUserException::new);
updateUserByRequest(user, updateUserDto);
userRepository.flush();

return ReadUserDto.from(user);
}
Expand Down
Loading

0 comments on commit 2259928

Please sign in to comment.