From a732d9d679287555d140dfba19184a51ef22401f Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 01:52:06 +0900 Subject: [PATCH 01/14] =?UTF-8?q?[FEAT]=20=EC=95=A0=ED=94=8C=20=EC=86=8C?= =?UTF-8?q?=EC=85=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/controller/AuthController.java | 2 +- .../error/CustomAppleLoginError.java | 4 +- .../oauth2/service/AppleService.java | 51 +++++++++++++++++++ .../oauth2/service/KakaoService.java | 8 --- .../repository/UserAppleTokenRepository.java | 4 ++ .../WSSServer/service/UserService.java | 24 +++++++++ 6 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/controller/AuthController.java b/src/main/java/org/websoso/WSSServer/controller/AuthController.java index 269476d65..900b0bcb8 100644 --- a/src/main/java/org/websoso/WSSServer/controller/AuthController.java +++ b/src/main/java/org/websoso/WSSServer/controller/AuthController.java @@ -70,7 +70,7 @@ public ResponseEntity withdrawUser(Principal principal, @Valid @RequestBody WithdrawalRequest withdrawalRequest) { User user = userService.getUserOrException(Long.valueOf(principal.getName())); String refreshToken = withdrawalRequest.refreshToken(); - kakaoService.unlinkFromKakao(user, refreshToken); + userService.withdrawUser(user, refreshToken); return ResponseEntity .status(NO_CONTENT) .build(); diff --git a/src/main/java/org/websoso/WSSServer/exception/error/CustomAppleLoginError.java b/src/main/java/org/websoso/WSSServer/exception/error/CustomAppleLoginError.java index f449d49e8..1970a2f2c 100644 --- a/src/main/java/org/websoso/WSSServer/exception/error/CustomAppleLoginError.java +++ b/src/main/java/org/websoso/WSSServer/exception/error/CustomAppleLoginError.java @@ -2,6 +2,7 @@ import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.http.HttpStatus.NOT_FOUND; import lombok.AllArgsConstructor; import lombok.Getter; @@ -20,7 +21,8 @@ public enum CustomAppleLoginError implements ICustomError { UNSUPPORTED_JWT_TYPE("APPLE-006", "지원되지 않는 jwt 타입입니다.", BAD_REQUEST), EMPTY_JWT("APPLE-007", "비어있는 jwt입니다.", BAD_REQUEST), JWT_VERIFICATION_FAILED("APPLE-008", "jwt 검증 또는 분석에 실패했습니다.", INTERNAL_SERVER_ERROR), - INVALID_APPLE_KEY("APPLE-009", "잘못된 애플 키입니다.", INTERNAL_SERVER_ERROR); + INVALID_APPLE_KEY("APPLE-009", "잘못된 애플 키입니다.", INTERNAL_SERVER_ERROR), + USER_APPLE_REFRESH_TOKEN_NOT_FOUND("APPLE-010", "유저의 애플 리프레시 토큰을 찾을 수 없습니다.", NOT_FOUND); private final String code; private final String description; diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java index 971803282..dbcfb18ee 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java @@ -52,6 +52,7 @@ import org.websoso.WSSServer.dto.auth.AppleTokenResponse; import org.websoso.WSSServer.dto.auth.AuthResponse; import org.websoso.WSSServer.exception.exception.CustomAppleLoginException; +import org.websoso.WSSServer.repository.RefreshTokenRepository; import org.websoso.WSSServer.service.UserService; @Service @@ -68,6 +69,7 @@ public class AppleService { private static final int POSITIVE_SIGN_NUMBER = 1; private final ObjectMapper objectMapper; private final UserService userService; + private final RefreshTokenRepository refreshTokenRepository; @Value("${apple.public-keys-url}") private String applePublicKeysUrl; @@ -111,6 +113,23 @@ public AuthResponse getUserInfoFromApple(AppleLoginRequest request) { appleTokenResponse.getRefreshToken()); } + public void unlinkFromApple(String refreshToken, String appleRefreshToken) { + String clientSecret = createClientSecret(); + AppleTokenResponse appleTokenResponse = requestAppleTokenByRefreshToken(appleRefreshToken, clientSecret); + + if (appleTokenResponse.getAccessToken() != null) { + RestClient restClient = RestClient.create(); + restClient.post() + .uri(appleAuthUrl + "/auth/revoke") + .headers(headers -> headers.add("Content-Type", "application/x-www-form-urlencoded")) + .body(createUserRevokeParams(clientSecret, appleTokenResponse.getAccessToken())) + .retrieve() + .body(String.class); + } + + refreshTokenRepository.findByRefreshToken(refreshToken).ifPresent(refreshTokenRepository::delete); + } + private Map parseAppleTokenHeader(String appleToken) { try { String encodedHeader = appleToken.split(IDENTITY_TOKEN_VALUE_DELIMITER)[HEADER_INDEX]; @@ -253,4 +272,36 @@ private MultiValueMap createTokenRequestParams(String authorizat params.add("redirect_uri", appleRedirectUrl); return params; } + + private AppleTokenResponse requestAppleTokenByRefreshToken(String appleRefreshToken, String clientSecret) { + try { + RestClient restClient = RestClient.create(); + return restClient.post() + .uri(appleAuthUrl + "/auth/token") + .headers(headers -> headers.add("Content-Type", "application/x-www-form-urlencoded")) + .body(createTokenRequestParamsByRefreshToken(appleRefreshToken, clientSecret)) + .retrieve() + .body(AppleTokenResponse.class); + } catch (Exception e) { + throw new CustomAppleLoginException(TOKEN_REQUEST_FAILED, "failed to get token from Apple server"); + } + } + + private MultiValueMap createTokenRequestParamsByRefreshToken(String appleRefreshToken, + String clientSecret) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("grant_type", "authorization_code"); + params.add("client_id", appleClientId); + params.add("client_secret", clientSecret); + params.add("refresh_token", appleRefreshToken); + return params; + } + + private MultiValueMap createUserRevokeParams(String clientSecret, String appleAccessToken) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("client_id", appleClientId); + params.add("client_secret", clientSecret); + params.add("token", appleAccessToken); + return params; + } } diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/KakaoService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/KakaoService.java index 92e246afd..1568960e4 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/KakaoService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/KakaoService.java @@ -19,8 +19,6 @@ import org.websoso.WSSServer.dto.auth.AuthResponse; import org.websoso.WSSServer.exception.exception.CustomKakaoException; import org.websoso.WSSServer.oauth2.dto.KakaoUserInfo; -import org.websoso.WSSServer.repository.CommentRepository; -import org.websoso.WSSServer.repository.FeedRepository; import org.websoso.WSSServer.repository.RefreshTokenRepository; import org.websoso.WSSServer.repository.UserRepository; @@ -30,8 +28,6 @@ public class KakaoService { private final UserRepository userRepository; - private final FeedRepository feedRepository; - private final CommentRepository commentRepository; private final RefreshTokenRepository refreshTokenRepository; private final JwtProvider jwtProvider; @@ -112,10 +108,6 @@ public void unlinkFromKakao(User user, String refreshToken) { String socialId = user.getSocialId(); String kakaoUserInfoId = socialId.replaceFirst("kakao_", ""); - feedRepository.updateUserToUnknown(user.getUserId()); - commentRepository.updateUserToUnknown(user.getUserId()); - userRepository.delete(user); - MultiValueMap withdrawInfoBodies = new LinkedMultiValueMap<>(); withdrawInfoBodies.add("target_id_type", "user_id"); withdrawInfoBodies.add("target_id", kakaoUserInfoId); diff --git a/src/main/java/org/websoso/WSSServer/repository/UserAppleTokenRepository.java b/src/main/java/org/websoso/WSSServer/repository/UserAppleTokenRepository.java index 46a451f63..e15741359 100644 --- a/src/main/java/org/websoso/WSSServer/repository/UserAppleTokenRepository.java +++ b/src/main/java/org/websoso/WSSServer/repository/UserAppleTokenRepository.java @@ -1,9 +1,13 @@ package org.websoso.WSSServer.repository; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import org.websoso.WSSServer.domain.User; import org.websoso.WSSServer.domain.UserAppleToken; @Repository public interface UserAppleTokenRepository extends JpaRepository { + + Optional findByUser(User user); } diff --git a/src/main/java/org/websoso/WSSServer/service/UserService.java b/src/main/java/org/websoso/WSSServer/service/UserService.java index c9fa54a39..f6fa7770a 100644 --- a/src/main/java/org/websoso/WSSServer/service/UserService.java +++ b/src/main/java/org/websoso/WSSServer/service/UserService.java @@ -1,5 +1,6 @@ package org.websoso.WSSServer.service; +import static org.websoso.WSSServer.exception.error.CustomAppleLoginError.USER_APPLE_REFRESH_TOKEN_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomGenreError.GENRE_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.ALREADY_SET_AVATAR; @@ -35,11 +36,15 @@ import org.websoso.WSSServer.dto.user.UserIdAndNicknameResponse; import org.websoso.WSSServer.dto.user.UserInfoGetResponse; import org.websoso.WSSServer.exception.error.CustomUserError; +import org.websoso.WSSServer.exception.exception.CustomAppleLoginException; import org.websoso.WSSServer.exception.exception.CustomAvatarException; import org.websoso.WSSServer.exception.exception.CustomGenreException; import org.websoso.WSSServer.exception.exception.CustomUserException; +import org.websoso.WSSServer.oauth2.service.AppleService; import org.websoso.WSSServer.oauth2.service.KakaoService; import org.websoso.WSSServer.repository.AvatarRepository; +import org.websoso.WSSServer.repository.CommentRepository; +import org.websoso.WSSServer.repository.FeedRepository; import org.websoso.WSSServer.repository.GenrePreferenceRepository; import org.websoso.WSSServer.repository.GenreRepository; import org.websoso.WSSServer.repository.RefreshTokenRepository; @@ -59,7 +64,11 @@ public class UserService { private final RefreshTokenRepository refreshTokenRepository; private final UserAppleTokenRepository userAppleTokenRepository; private final KakaoService kakaoService; + private final AppleService appleService; + private final FeedRepository feedRepository; + private final CommentRepository commentRepository; private static final String KAKAO_PREFIX = "kakao"; + private static final String APPLE_PREFIX = "apple"; @Transactional(readOnly = true) public NicknameValidation isNicknameAvailable(User user, String nickname) { @@ -187,6 +196,21 @@ public void logout(User user, String refreshToken) { } } + public void withdrawUser(User user, String refreshToken) { + if (user.getSocialId().startsWith(KAKAO_PREFIX)) { + kakaoService.unlinkFromKakao(user, refreshToken); + } else if (user.getSocialId().startsWith(APPLE_PREFIX)) { + UserAppleToken userAppleToken = userAppleTokenRepository.findByUser(user).orElseThrow( + () -> new CustomAppleLoginException(USER_APPLE_REFRESH_TOKEN_NOT_FOUND, + "cannot find the user Apple refresh token")); + appleService.unlinkFromApple(refreshToken, userAppleToken.getAppleRefreshToken()); + } + + feedRepository.updateUserToUnknown(user.getUserId()); + commentRepository.updateUserToUnknown(user.getUserId()); + userRepository.delete(user); + } + private void checkNicknameIfAlreadyExist(String nickname) { if (userRepository.existsByNickname(nickname)) { throw new CustomUserException(DUPLICATED_NICKNAME, "nickname is duplicated."); From f9a2cd6aff49c3842cbd1bc0a1e61b6b0840ba87 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 02:00:10 +0900 Subject: [PATCH 02/14] =?UTF-8?q?[REFACTOR]=20=EC=88=9C=ED=99=98=20?= =?UTF-8?q?=EC=B0=B8=EC=A1=B0=20=ED=95=B4=EA=B2=B0=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20Apple=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20AppleService=EB=A1=9C=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/service/AppleService.java | 46 ++++++++++++++++--- .../WSSServer/service/UserService.java | 32 +------------ 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java index dbcfb18ee..a3dceb9ab 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java @@ -9,6 +9,7 @@ import static org.websoso.WSSServer.exception.error.CustomAppleLoginError.PRIVATE_KEY_READ_FAILED; import static org.websoso.WSSServer.exception.error.CustomAppleLoginError.TOKEN_REQUEST_FAILED; import static org.websoso.WSSServer.exception.error.CustomAppleLoginError.UNSUPPORTED_JWT_TYPE; +import static org.websoso.WSSServer.exception.error.CustomAppleLoginError.USER_APPLE_REFRESH_TOKEN_NOT_FOUND; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -43,9 +44,15 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClient; +import org.websoso.WSSServer.config.jwt.JwtProvider; +import org.websoso.WSSServer.config.jwt.UserAuthentication; +import org.websoso.WSSServer.domain.RefreshToken; +import org.websoso.WSSServer.domain.User; +import org.websoso.WSSServer.domain.UserAppleToken; import org.websoso.WSSServer.dto.auth.AppleLoginRequest; import org.websoso.WSSServer.dto.auth.ApplePublicKey; import org.websoso.WSSServer.dto.auth.ApplePublicKeys; @@ -53,8 +60,10 @@ import org.websoso.WSSServer.dto.auth.AuthResponse; import org.websoso.WSSServer.exception.exception.CustomAppleLoginException; import org.websoso.WSSServer.repository.RefreshTokenRepository; -import org.websoso.WSSServer.service.UserService; +import org.websoso.WSSServer.repository.UserAppleTokenRepository; +import org.websoso.WSSServer.repository.UserRepository; +@Transactional @Service @RequiredArgsConstructor public class AppleService { @@ -68,8 +77,10 @@ public class AppleService { private static final String KEY_ID_HEADER = "kid"; private static final int POSITIVE_SIGN_NUMBER = 1; private final ObjectMapper objectMapper; - private final UserService userService; private final RefreshTokenRepository refreshTokenRepository; + private final UserRepository userRepository; + private final UserAppleTokenRepository userAppleTokenRepository; + private final JwtProvider jwtProvider; @Value("${apple.public-keys-url}") private String applePublicKeysUrl; @@ -109,13 +120,17 @@ public AuthResponse getUserInfoFromApple(AppleLoginRequest request) { String customSocialId = APPLE_PREFIX + "_" + userIdentifier; String defaultNickname = APPLE_PREFIX.charAt(0) + "*" + userIdentifier.substring(7, 15); - return userService.authenticateWithApple(customSocialId, email, defaultNickname, - appleTokenResponse.getRefreshToken()); + return authenticate(customSocialId, email, defaultNickname, appleTokenResponse.getRefreshToken()); } - public void unlinkFromApple(String refreshToken, String appleRefreshToken) { + public void unlinkFromApple(User user, String refreshToken) { String clientSecret = createClientSecret(); - AppleTokenResponse appleTokenResponse = requestAppleTokenByRefreshToken(appleRefreshToken, clientSecret); + UserAppleToken userAppleToken = userAppleTokenRepository.findByUser(user).orElseThrow( + () -> new CustomAppleLoginException(USER_APPLE_REFRESH_TOKEN_NOT_FOUND, + "cannot find the user Apple refresh token")); + + AppleTokenResponse appleTokenResponse = requestAppleTokenByRefreshToken(userAppleToken.getAppleRefreshToken(), + clientSecret); if (appleTokenResponse.getAccessToken() != null) { RestClient restClient = RestClient.create(); @@ -273,6 +288,25 @@ private MultiValueMap createTokenRequestParams(String authorizat return params; } + private AuthResponse authenticate(String socialId, String email, String nickname, String appleRefreshToken) { + User user = userRepository.findBySocialId(socialId); + + if (user == null) { + user = userRepository.save(User.createBySocial(socialId, nickname, email)); + userAppleTokenRepository.save(UserAppleToken.create(user, appleRefreshToken)); + } + + UserAuthentication userAuthentication = new UserAuthentication(user.getUserId(), null, null); + String accessToken = jwtProvider.generateAccessToken(userAuthentication); + String refreshToken = jwtProvider.generateRefreshToken(userAuthentication); + + refreshTokenRepository.save(new RefreshToken(refreshToken, user.getUserId())); + + boolean isRegister = !user.getNickname().contains("*"); + + return AuthResponse.of(accessToken, refreshToken, isRegister); + } + private AppleTokenResponse requestAppleTokenByRefreshToken(String appleRefreshToken, String clientSecret) { try { RestClient restClient = RestClient.create(); diff --git a/src/main/java/org/websoso/WSSServer/service/UserService.java b/src/main/java/org/websoso/WSSServer/service/UserService.java index f6fa7770a..af8883f89 100644 --- a/src/main/java/org/websoso/WSSServer/service/UserService.java +++ b/src/main/java/org/websoso/WSSServer/service/UserService.java @@ -1,6 +1,5 @@ package org.websoso.WSSServer.service; -import static org.websoso.WSSServer.exception.error.CustomAppleLoginError.USER_APPLE_REFRESH_TOKEN_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomGenreError.GENRE_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.ALREADY_SET_AVATAR; @@ -20,10 +19,7 @@ import org.websoso.WSSServer.domain.Avatar; import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.GenrePreference; -import org.websoso.WSSServer.domain.RefreshToken; import org.websoso.WSSServer.domain.User; -import org.websoso.WSSServer.domain.UserAppleToken; -import org.websoso.WSSServer.dto.auth.AuthResponse; import org.websoso.WSSServer.dto.user.EditMyInfoRequest; import org.websoso.WSSServer.dto.user.EditProfileStatusRequest; import org.websoso.WSSServer.dto.user.LoginResponse; @@ -36,7 +32,6 @@ import org.websoso.WSSServer.dto.user.UserIdAndNicknameResponse; import org.websoso.WSSServer.dto.user.UserInfoGetResponse; import org.websoso.WSSServer.exception.error.CustomUserError; -import org.websoso.WSSServer.exception.exception.CustomAppleLoginException; import org.websoso.WSSServer.exception.exception.CustomAvatarException; import org.websoso.WSSServer.exception.exception.CustomGenreException; import org.websoso.WSSServer.exception.exception.CustomUserException; @@ -48,7 +43,6 @@ import org.websoso.WSSServer.repository.GenrePreferenceRepository; import org.websoso.WSSServer.repository.GenreRepository; import org.websoso.WSSServer.repository.RefreshTokenRepository; -import org.websoso.WSSServer.repository.UserAppleTokenRepository; import org.websoso.WSSServer.repository.UserRepository; @Service @@ -62,7 +56,6 @@ public class UserService { private final GenrePreferenceRepository genrePreferenceRepository; private final GenreRepository genreRepository; private final RefreshTokenRepository refreshTokenRepository; - private final UserAppleTokenRepository userAppleTokenRepository; private final KakaoService kakaoService; private final AppleService appleService; private final FeedRepository feedRepository; @@ -169,26 +162,6 @@ public void registerUserInfo(User user, RegisterUserInfoRequest registerUserInfo genrePreferenceRepository.saveAll(preferGenres); } - public AuthResponse authenticateWithApple(String socialId, String email, String nickname, - String appleRefreshToken) { - User user = userRepository.findBySocialId(socialId); - - if (user == null) { - user = userRepository.save(User.createBySocial(socialId, nickname, email)); - userAppleTokenRepository.save(UserAppleToken.create(user, appleRefreshToken)); - } - - UserAuthentication userAuthentication = new UserAuthentication(user.getUserId(), null, null); - String accessToken = jwtProvider.generateAccessToken(userAuthentication); - String refreshToken = jwtProvider.generateRefreshToken(userAuthentication); - - refreshTokenRepository.save(new RefreshToken(refreshToken, user.getUserId())); - - boolean isRegister = !user.getNickname().contains("*"); - - return AuthResponse.of(accessToken, refreshToken, isRegister); - } - public void logout(User user, String refreshToken) { refreshTokenRepository.findByRefreshToken(refreshToken).ifPresent(refreshTokenRepository::delete); if (user.getSocialId().startsWith(KAKAO_PREFIX)) { @@ -200,10 +173,7 @@ public void withdrawUser(User user, String refreshToken) { if (user.getSocialId().startsWith(KAKAO_PREFIX)) { kakaoService.unlinkFromKakao(user, refreshToken); } else if (user.getSocialId().startsWith(APPLE_PREFIX)) { - UserAppleToken userAppleToken = userAppleTokenRepository.findByUser(user).orElseThrow( - () -> new CustomAppleLoginException(USER_APPLE_REFRESH_TOKEN_NOT_FOUND, - "cannot find the user Apple refresh token")); - appleService.unlinkFromApple(refreshToken, userAppleToken.getAppleRefreshToken()); + appleService.unlinkFromApple(user, refreshToken); } feedRepository.updateUserToUnknown(user.getUserId()); From 7458a8c2641414f8c79be251130e6d8e38e7eee0 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 02:04:28 +0900 Subject: [PATCH 03/14] =?UTF-8?q?[REFACTOR]=20=EA=B3=B5=ED=86=B5=EB=90=9C?= =?UTF-8?q?=20=EC=9C=A0=EC=A0=80=20=EB=A6=AC=ED=94=84=EB=A0=88=EC=8B=9C=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=A0=9C=EA=B1=B0=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20=ED=86=B5=ED=95=A9=ED=95=98=EC=97=AC=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/websoso/WSSServer/oauth2/service/AppleService.java | 4 +--- .../org/websoso/WSSServer/oauth2/service/KakaoService.java | 4 +--- .../java/org/websoso/WSSServer/service/UserService.java | 7 +++++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java index a3dceb9ab..fe7fae1b5 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java @@ -123,7 +123,7 @@ public AuthResponse getUserInfoFromApple(AppleLoginRequest request) { return authenticate(customSocialId, email, defaultNickname, appleTokenResponse.getRefreshToken()); } - public void unlinkFromApple(User user, String refreshToken) { + public void unlinkFromApple(User user) { String clientSecret = createClientSecret(); UserAppleToken userAppleToken = userAppleTokenRepository.findByUser(user).orElseThrow( () -> new CustomAppleLoginException(USER_APPLE_REFRESH_TOKEN_NOT_FOUND, @@ -141,8 +141,6 @@ public void unlinkFromApple(User user, String refreshToken) { .retrieve() .body(String.class); } - - refreshTokenRepository.findByRefreshToken(refreshToken).ifPresent(refreshTokenRepository::delete); } private Map parseAppleTokenHeader(String appleToken) { diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/KakaoService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/KakaoService.java index 1568960e4..d34d80a84 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/KakaoService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/KakaoService.java @@ -102,9 +102,7 @@ public void kakaoLogout(User user) { .toBodilessEntity(); } - public void unlinkFromKakao(User user, String refreshToken) { - refreshTokenRepository.findByRefreshToken(refreshToken).ifPresent(refreshTokenRepository::delete); - + public void unlinkFromKakao(User user) { String socialId = user.getSocialId(); String kakaoUserInfoId = socialId.replaceFirst("kakao_", ""); diff --git a/src/main/java/org/websoso/WSSServer/service/UserService.java b/src/main/java/org/websoso/WSSServer/service/UserService.java index af8883f89..b880a2daa 100644 --- a/src/main/java/org/websoso/WSSServer/service/UserService.java +++ b/src/main/java/org/websoso/WSSServer/service/UserService.java @@ -171,14 +171,17 @@ public void logout(User user, String refreshToken) { public void withdrawUser(User user, String refreshToken) { if (user.getSocialId().startsWith(KAKAO_PREFIX)) { - kakaoService.unlinkFromKakao(user, refreshToken); + kakaoService.unlinkFromKakao(user); } else if (user.getSocialId().startsWith(APPLE_PREFIX)) { - appleService.unlinkFromApple(user, refreshToken); + appleService.unlinkFromApple(user); } + refreshTokenRepository.findByRefreshToken(refreshToken).ifPresent(refreshTokenRepository::delete); feedRepository.updateUserToUnknown(user.getUserId()); commentRepository.updateUserToUnknown(user.getUserId()); userRepository.delete(user); + + // TODO : 디스코드 웹훅 알림 발송 로직 추가 } private void checkNicknameIfAlreadyExist(String nickname) { From b3a143edae16dac600f51441a89ffd06d054c27e Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 02:07:51 +0900 Subject: [PATCH 04/14] =?UTF-8?q?[REFACTOR]=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=B0=9C=EC=83=9D=20=EA=B0=80=EB=8A=A5=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=EC=B5=9C=EC=9A=B0=EC=84=A0=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=8B=A4=ED=96=89=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=9C=EC=84=9C=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/websoso/WSSServer/oauth2/service/AppleService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java index fe7fae1b5..42b3fd0b3 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java @@ -124,10 +124,10 @@ public AuthResponse getUserInfoFromApple(AppleLoginRequest request) { } public void unlinkFromApple(User user) { - String clientSecret = createClientSecret(); UserAppleToken userAppleToken = userAppleTokenRepository.findByUser(user).orElseThrow( () -> new CustomAppleLoginException(USER_APPLE_REFRESH_TOKEN_NOT_FOUND, "cannot find the user Apple refresh token")); + String clientSecret = createClientSecret(); AppleTokenResponse appleTokenResponse = requestAppleTokenByRefreshToken(userAppleToken.getAppleRefreshToken(), clientSecret); From 055949ec385400a71e155ac76729ed8611f33560 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 02:16:49 +0900 Subject: [PATCH 05/14] =?UTF-8?q?[FIX]=20=EC=95=A0=ED=94=8C=20=EC=86=8C?= =?UTF-8?q?=EC=85=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=83=88=ED=87=B4=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EC=95=A0=ED=94=8C=20=EC=95=A1=EC=84=B8?= =?UTF-8?q?=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EB=8C=80=EC=8B=A0=20=EC=95=A0?= =?UTF-8?q?=ED=94=8C=20=EB=A6=AC=ED=94=84=EB=A0=88=EC=8B=9C=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=EC=9D=84=20=EC=9D=B4=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/service/AppleService.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java index 42b3fd0b3..53c5379c4 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java @@ -127,20 +127,14 @@ public void unlinkFromApple(User user) { UserAppleToken userAppleToken = userAppleTokenRepository.findByUser(user).orElseThrow( () -> new CustomAppleLoginException(USER_APPLE_REFRESH_TOKEN_NOT_FOUND, "cannot find the user Apple refresh token")); - String clientSecret = createClientSecret(); - AppleTokenResponse appleTokenResponse = requestAppleTokenByRefreshToken(userAppleToken.getAppleRefreshToken(), - clientSecret); - - if (appleTokenResponse.getAccessToken() != null) { - RestClient restClient = RestClient.create(); - restClient.post() - .uri(appleAuthUrl + "/auth/revoke") - .headers(headers -> headers.add("Content-Type", "application/x-www-form-urlencoded")) - .body(createUserRevokeParams(clientSecret, appleTokenResponse.getAccessToken())) - .retrieve() - .body(String.class); - } + RestClient restClient = RestClient.create(); + restClient.post() + .uri(appleAuthUrl + "/auth/revoke") + .headers(headers -> headers.add("Content-Type", "application/x-www-form-urlencoded")) + .body(createUserRevokeParams(createClientSecret(), userAppleToken.getAppleRefreshToken())) + .retrieve() + .body(String.class); } private Map parseAppleTokenHeader(String appleToken) { @@ -329,11 +323,13 @@ private MultiValueMap createTokenRequestParamsByRefreshToken(Str return params; } - private MultiValueMap createUserRevokeParams(String clientSecret, String appleAccessToken) { + private MultiValueMap createUserRevokeParams(String clientSecret, String appleRefreshToken) { MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("grant_type", "refresh_token"); params.add("client_id", appleClientId); params.add("client_secret", clientSecret); - params.add("token", appleAccessToken); + params.add("token", appleRefreshToken); + params.add("token_type_hint", "refresh_token"); return params; } } From 8c92091ca93ede4613b0905618051e124a181c39 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 02:18:01 +0900 Subject: [PATCH 06/14] =?UTF-8?q?[REMOVE]=20=ED=83=88=ED=87=B4=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=EB=8D=94=20=EC=9D=B4=EC=83=81=20=EC=82=AC=EC=9A=A9=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/service/AppleService.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java index 53c5379c4..6e1dc8a02 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java @@ -299,30 +299,6 @@ private AuthResponse authenticate(String socialId, String email, String nickname return AuthResponse.of(accessToken, refreshToken, isRegister); } - private AppleTokenResponse requestAppleTokenByRefreshToken(String appleRefreshToken, String clientSecret) { - try { - RestClient restClient = RestClient.create(); - return restClient.post() - .uri(appleAuthUrl + "/auth/token") - .headers(headers -> headers.add("Content-Type", "application/x-www-form-urlencoded")) - .body(createTokenRequestParamsByRefreshToken(appleRefreshToken, clientSecret)) - .retrieve() - .body(AppleTokenResponse.class); - } catch (Exception e) { - throw new CustomAppleLoginException(TOKEN_REQUEST_FAILED, "failed to get token from Apple server"); - } - } - - private MultiValueMap createTokenRequestParamsByRefreshToken(String appleRefreshToken, - String clientSecret) { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("grant_type", "authorization_code"); - params.add("client_id", appleClientId); - params.add("client_secret", clientSecret); - params.add("refresh_token", appleRefreshToken); - return params; - } - private MultiValueMap createUserRevokeParams(String clientSecret, String appleRefreshToken) { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("grant_type", "refresh_token"); From f739ef8c55a2db523424cf17e3b3fddd6e965c30 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 09:30:54 +0900 Subject: [PATCH 07/14] =?UTF-8?q?[FEAT]=20=ED=83=88=ED=87=B4=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EC=8B=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20?= =?UTF-8?q?Request=20DTO=EC=97=90=20=EC=98=88=EC=99=B8=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/websoso/WSSServer/dto/user/WithdrawalRequest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/dto/user/WithdrawalRequest.java b/src/main/java/org/websoso/WSSServer/dto/user/WithdrawalRequest.java index 55aea2fd5..c369451a9 100644 --- a/src/main/java/org/websoso/WSSServer/dto/user/WithdrawalRequest.java +++ b/src/main/java/org/websoso/WSSServer/dto/user/WithdrawalRequest.java @@ -6,8 +6,7 @@ public record WithdrawalRequest( @Size(max = 80, message = "탈퇴 사유는 80자를 초과할 수 없습니다.") String reason, - - @NotBlank + @NotBlank(message = "리프레시 토큰은 null 이거나, 공백일 수 없습니다.") String refreshToken ) { } From 4fbfc0dc8537d1e4ffe898e41a64c83bdca7965f Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 16:14:08 +0900 Subject: [PATCH 08/14] =?UTF-8?q?[FEAT]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EC=8B=9C=20=EB=94=94=EC=8A=A4=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9B=B9=ED=9B=85=EC=9D=84=20=ED=86=B5=ED=95=9C=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/controller/AuthController.java | 3 +-- .../domain/common/DiscordWebhookMessage.java | 9 +++++---- .../WSSServer/service/CommentService.java | 2 +- .../websoso/WSSServer/service/FeedService.java | 3 ++- .../WSSServer/service/MessageFormatter.java | 16 ++++++++++++++++ .../WSSServer/service/MessageService.java | 12 ++++++++---- .../websoso/WSSServer/service/UserService.java | 16 +++++++++++++--- 7 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/controller/AuthController.java b/src/main/java/org/websoso/WSSServer/controller/AuthController.java index 900b0bcb8..2cbd1cfb2 100644 --- a/src/main/java/org/websoso/WSSServer/controller/AuthController.java +++ b/src/main/java/org/websoso/WSSServer/controller/AuthController.java @@ -69,8 +69,7 @@ public ResponseEntity logout(Principal principal, public ResponseEntity withdrawUser(Principal principal, @Valid @RequestBody WithdrawalRequest withdrawalRequest) { User user = userService.getUserOrException(Long.valueOf(principal.getName())); - String refreshToken = withdrawalRequest.refreshToken(); - userService.withdrawUser(user, refreshToken); + userService.withdrawUser(user, withdrawalRequest); return ResponseEntity .status(NO_CONTENT) .build(); diff --git a/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessage.java b/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessage.java index 042f08e66..161adb453 100644 --- a/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessage.java +++ b/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessage.java @@ -1,13 +1,14 @@ package org.websoso.WSSServer.domain.common; public record DiscordWebhookMessage( - String content + String content, + String type ) { - public static DiscordWebhookMessage of(String content) { + + public static DiscordWebhookMessage of(String content, String type) { if (content.length() >= 2000) { content = content.substring(0, 1993) + "\n...```"; } - return new DiscordWebhookMessage(content); + return new DiscordWebhookMessage(content, type); } - } diff --git a/src/main/java/org/websoso/WSSServer/service/CommentService.java b/src/main/java/org/websoso/WSSServer/service/CommentService.java index 517d33b2f..de2761e74 100644 --- a/src/main/java/org/websoso/WSSServer/service/CommentService.java +++ b/src/main/java/org/websoso/WSSServer/service/CommentService.java @@ -98,7 +98,7 @@ public void createReportedComment(Feed feed, Long commentId, User user, Reported messageService.sendDiscordWebhookMessage( DiscordWebhookMessage.of( MessageFormatter.formatCommentReportMessage(comment, reportedType, commentCreatedUser, - reportedCount, shouldHide))); + reportedCount, shouldHide), "report")); } private Comment getCommentOrException(Long commentId) { diff --git a/src/main/java/org/websoso/WSSServer/service/FeedService.java b/src/main/java/org/websoso/WSSServer/service/FeedService.java index d12738f91..f08cd9fea 100644 --- a/src/main/java/org/websoso/WSSServer/service/FeedService.java +++ b/src/main/java/org/websoso/WSSServer/service/FeedService.java @@ -196,7 +196,8 @@ public void reportFeed(User user, Long feedId, ReportedType reportedType) { messageService.sendDiscordWebhookMessage( DiscordWebhookMessage.of( - MessageFormatter.formatFeedReportMessage(feed, reportedType, reportedCount, shouldHide))); + MessageFormatter.formatFeedReportMessage(feed, reportedType, reportedCount, shouldHide), + "report")); } public void reportComment(User user, Long feedId, Long commentId, ReportedType reportedType) { diff --git a/src/main/java/org/websoso/WSSServer/service/MessageFormatter.java b/src/main/java/org/websoso/WSSServer/service/MessageFormatter.java index e4dae63d0..35bbe027c 100644 --- a/src/main/java/org/websoso/WSSServer/service/MessageFormatter.java +++ b/src/main/java/org/websoso/WSSServer/service/MessageFormatter.java @@ -32,6 +32,13 @@ public class MessageFormatter { "[신고 횟수]\n총 신고 횟수 %d회.\n" + "%s\n```"; + private static final String USER_WITHDRAW_MESSAGE = + "```[%s] 사용자가 탈퇴하였습니다.\n\n" + + "[탈퇴한 사용자]\n" + + "유저 아이디 : %d\n" + + "유저 닉네임 : %s\n\n" + + "[탈퇴 사유]\n%s\n\n```"; + public static String formatFeedReportMessage(Feed feed, ReportedType reportedType, int reportedCount, boolean isHidden) { String hiddenMessage = isHidden ? "해당 피드는 숨김 처리되었습니다." : "해당 피드는 숨김 처리되지 않았습니다."; @@ -71,4 +78,13 @@ public static String formatCommentReportMessage(Comment comment, ReportedType re ); } + public static String formatUserWithdrawMessage(Long userId, String userNickname, String reason) { + return String.format( + USER_WITHDRAW_MESSAGE, + LocalDateTime.now().format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)), + userId, + userNickname, + reason + ); + } } diff --git a/src/main/java/org/websoso/WSSServer/service/MessageService.java b/src/main/java/org/websoso/WSSServer/service/MessageService.java index cd229bc1c..8e5bbeb5a 100644 --- a/src/main/java/org/websoso/WSSServer/service/MessageService.java +++ b/src/main/java/org/websoso/WSSServer/service/MessageService.java @@ -16,8 +16,11 @@ @Slf4j public class MessageService { - @Value("${logging.discord.webhook-url}") - String discordWebhookUrl; + @Value("${logging.discord.report-webhook-url}") + private String discordReportWebhookUrl; + + @Value("${logging.discord.withdraw-webhook-url}") + private String discordWithdrawWebhookUrl; public void sendDiscordWebhookMessage(DiscordWebhookMessage message) { try { @@ -27,7 +30,9 @@ public void sendDiscordWebhookMessage(DiscordWebhookMessage message) { RestTemplate template = new RestTemplate(); ResponseEntity response = template.exchange( - discordWebhookUrl, + message.type().equals("report") ? + discordReportWebhookUrl : + discordWithdrawWebhookUrl, POST, messageEntity, String.class @@ -41,5 +46,4 @@ public void sendDiscordWebhookMessage(DiscordWebhookMessage message) { log.error("에러 발생 :: " + e); } } - } diff --git a/src/main/java/org/websoso/WSSServer/service/UserService.java b/src/main/java/org/websoso/WSSServer/service/UserService.java index b880a2daa..492ff8efe 100644 --- a/src/main/java/org/websoso/WSSServer/service/UserService.java +++ b/src/main/java/org/websoso/WSSServer/service/UserService.java @@ -20,6 +20,7 @@ import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.GenrePreference; import org.websoso.WSSServer.domain.User; +import org.websoso.WSSServer.domain.common.DiscordWebhookMessage; import org.websoso.WSSServer.dto.user.EditMyInfoRequest; import org.websoso.WSSServer.dto.user.EditProfileStatusRequest; import org.websoso.WSSServer.dto.user.LoginResponse; @@ -31,6 +32,7 @@ import org.websoso.WSSServer.dto.user.UpdateMyProfileRequest; import org.websoso.WSSServer.dto.user.UserIdAndNicknameResponse; import org.websoso.WSSServer.dto.user.UserInfoGetResponse; +import org.websoso.WSSServer.dto.user.WithdrawalRequest; import org.websoso.WSSServer.exception.error.CustomUserError; import org.websoso.WSSServer.exception.exception.CustomAvatarException; import org.websoso.WSSServer.exception.exception.CustomGenreException; @@ -60,6 +62,7 @@ public class UserService { private final AppleService appleService; private final FeedRepository feedRepository; private final CommentRepository commentRepository; + private final MessageService messageService; private static final String KAKAO_PREFIX = "kakao"; private static final String APPLE_PREFIX = "apple"; @@ -169,19 +172,26 @@ public void logout(User user, String refreshToken) { } } - public void withdrawUser(User user, String refreshToken) { + public void withdrawUser(User user, WithdrawalRequest withdrawalRequest) { if (user.getSocialId().startsWith(KAKAO_PREFIX)) { kakaoService.unlinkFromKakao(user); } else if (user.getSocialId().startsWith(APPLE_PREFIX)) { appleService.unlinkFromApple(user); } - refreshTokenRepository.findByRefreshToken(refreshToken).ifPresent(refreshTokenRepository::delete); + Long userId = user.getUserId(); + String userNickname = user.getNickname(); + + refreshTokenRepository.findByRefreshToken(withdrawalRequest.refreshToken()) + .ifPresent(refreshTokenRepository::delete); feedRepository.updateUserToUnknown(user.getUserId()); commentRepository.updateUserToUnknown(user.getUserId()); userRepository.delete(user); - // TODO : 디스코드 웹훅 알림 발송 로직 추가 + messageService.sendDiscordWebhookMessage( + DiscordWebhookMessage.of( + MessageFormatter.formatUserWithdrawMessage(userId, userNickname, withdrawalRequest.reason()), + "withdraw")); } private void checkNicknameIfAlreadyExist(String nickname) { From 8c59ff21f7aa441b27c8d959a3e640fedc8a9b44 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 16:45:24 +0900 Subject: [PATCH 09/14] =?UTF-8?q?[REFACTOR]=20Discord=20Webhook=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=EC=9D=84=20Enum=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EC=97=B4=20=EC=83=81=EC=88=98=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/domain/common/DiscordWebhookMessage.java | 6 +++--- .../WSSServer/domain/common/DiscordWebhookMessageType.java | 5 +++++ .../java/org/websoso/WSSServer/service/CommentService.java | 3 ++- .../java/org/websoso/WSSServer/service/FeedService.java | 3 ++- .../java/org/websoso/WSSServer/service/MessageService.java | 3 ++- 5 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessageType.java diff --git a/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessage.java b/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessage.java index 161adb453..9aebaa60c 100644 --- a/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessage.java +++ b/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessage.java @@ -2,10 +2,10 @@ public record DiscordWebhookMessage( String content, - String type + DiscordWebhookMessageType type ) { - - public static DiscordWebhookMessage of(String content, String type) { + + public static DiscordWebhookMessage of(String content, DiscordWebhookMessageType type) { if (content.length() >= 2000) { content = content.substring(0, 1993) + "\n...```"; } diff --git a/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessageType.java b/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessageType.java new file mode 100644 index 000000000..12fd0a61e --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/domain/common/DiscordWebhookMessageType.java @@ -0,0 +1,5 @@ +package org.websoso.WSSServer.domain.common; + +public enum DiscordWebhookMessageType { + WITHDRAW, REPORT +} diff --git a/src/main/java/org/websoso/WSSServer/service/CommentService.java b/src/main/java/org/websoso/WSSServer/service/CommentService.java index de2761e74..3fc89c7e0 100644 --- a/src/main/java/org/websoso/WSSServer/service/CommentService.java +++ b/src/main/java/org/websoso/WSSServer/service/CommentService.java @@ -2,6 +2,7 @@ import static org.websoso.WSSServer.domain.common.Action.DELETE; import static org.websoso.WSSServer.domain.common.Action.UPDATE; +import static org.websoso.WSSServer.domain.common.DiscordWebhookMessageType.REPORT; import static org.websoso.WSSServer.domain.common.ReportedType.IMPERTINENCE; import static org.websoso.WSSServer.domain.common.ReportedType.SPOILER; import static org.websoso.WSSServer.exception.error.CustomCommentError.COMMENT_NOT_FOUND; @@ -98,7 +99,7 @@ public void createReportedComment(Feed feed, Long commentId, User user, Reported messageService.sendDiscordWebhookMessage( DiscordWebhookMessage.of( MessageFormatter.formatCommentReportMessage(comment, reportedType, commentCreatedUser, - reportedCount, shouldHide), "report")); + reportedCount, shouldHide), REPORT)); } private Comment getCommentOrException(Long commentId) { diff --git a/src/main/java/org/websoso/WSSServer/service/FeedService.java b/src/main/java/org/websoso/WSSServer/service/FeedService.java index f08cd9fea..b1f50d29c 100644 --- a/src/main/java/org/websoso/WSSServer/service/FeedService.java +++ b/src/main/java/org/websoso/WSSServer/service/FeedService.java @@ -2,6 +2,7 @@ import static org.websoso.WSSServer.domain.common.Action.DELETE; import static org.websoso.WSSServer.domain.common.Action.UPDATE; +import static org.websoso.WSSServer.domain.common.DiscordWebhookMessageType.REPORT; import static org.websoso.WSSServer.exception.error.CustomFeedError.BLOCKED_USER_ACCESS; import static org.websoso.WSSServer.exception.error.CustomFeedError.FEED_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomFeedError.HIDDEN_FEED_ACCESS; @@ -197,7 +198,7 @@ public void reportFeed(User user, Long feedId, ReportedType reportedType) { messageService.sendDiscordWebhookMessage( DiscordWebhookMessage.of( MessageFormatter.formatFeedReportMessage(feed, reportedType, reportedCount, shouldHide), - "report")); + REPORT)); } public void reportComment(User user, Long feedId, Long commentId, ReportedType reportedType) { diff --git a/src/main/java/org/websoso/WSSServer/service/MessageService.java b/src/main/java/org/websoso/WSSServer/service/MessageService.java index 8e5bbeb5a..b06372390 100644 --- a/src/main/java/org/websoso/WSSServer/service/MessageService.java +++ b/src/main/java/org/websoso/WSSServer/service/MessageService.java @@ -2,6 +2,7 @@ import static org.springframework.http.HttpMethod.POST; import static org.springframework.http.HttpStatus.NO_CONTENT; +import static org.websoso.WSSServer.domain.common.DiscordWebhookMessageType.REPORT; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -30,7 +31,7 @@ public void sendDiscordWebhookMessage(DiscordWebhookMessage message) { RestTemplate template = new RestTemplate(); ResponseEntity response = template.exchange( - message.type().equals("report") ? + message.type() == REPORT ? discordReportWebhookUrl : discordWithdrawWebhookUrl, POST, From 63820605a59ecce1f9b6cac8e3d2834358e86711 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Thu, 14 Nov 2024 16:46:31 +0900 Subject: [PATCH 10/14] =?UTF-8?q?[REFACTOR]=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=ED=83=88=ED=87=B4=20=EB=A1=9C=EC=A7=81=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/service/UserService.java | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/service/UserService.java b/src/main/java/org/websoso/WSSServer/service/UserService.java index 492ff8efe..64f9dee22 100644 --- a/src/main/java/org/websoso/WSSServer/service/UserService.java +++ b/src/main/java/org/websoso/WSSServer/service/UserService.java @@ -1,5 +1,6 @@ package org.websoso.WSSServer.service; +import static org.websoso.WSSServer.domain.common.DiscordWebhookMessageType.WITHDRAW; import static org.websoso.WSSServer.exception.error.CustomAvatarError.AVATAR_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomGenreError.GENRE_NOT_FOUND; import static org.websoso.WSSServer.exception.error.CustomUserError.ALREADY_SET_AVATAR; @@ -173,25 +174,15 @@ public void logout(User user, String refreshToken) { } public void withdrawUser(User user, WithdrawalRequest withdrawalRequest) { - if (user.getSocialId().startsWith(KAKAO_PREFIX)) { - kakaoService.unlinkFromKakao(user); - } else if (user.getSocialId().startsWith(APPLE_PREFIX)) { - appleService.unlinkFromApple(user); - } + unlinkSocialAccount(user); - Long userId = user.getUserId(); - String userNickname = user.getNickname(); + String messageContent = MessageFormatter.formatUserWithdrawMessage(user.getUserId(), user.getNickname(), + withdrawalRequest.reason()); - refreshTokenRepository.findByRefreshToken(withdrawalRequest.refreshToken()) - .ifPresent(refreshTokenRepository::delete); - feedRepository.updateUserToUnknown(user.getUserId()); - commentRepository.updateUserToUnknown(user.getUserId()); - userRepository.delete(user); + cleanupUserData(user.getUserId(), withdrawalRequest.refreshToken()); messageService.sendDiscordWebhookMessage( - DiscordWebhookMessage.of( - MessageFormatter.formatUserWithdrawMessage(userId, userNickname, withdrawalRequest.reason()), - "withdraw")); + DiscordWebhookMessage.of(messageContent, WITHDRAW)); } private void checkNicknameIfAlreadyExist(String nickname) { @@ -221,6 +212,22 @@ private Genre findByGenreNameOrThrow(String genreName) { new CustomGenreException(GENRE_NOT_FOUND, "genre with the given genreName is not found")); } + private void unlinkSocialAccount(User user) { + if (user.getSocialId().startsWith(KAKAO_PREFIX)) { + kakaoService.unlinkFromKakao(user); + } else if (user.getSocialId().startsWith(APPLE_PREFIX)) { + appleService.unlinkFromApple(user); + } + } + + private void cleanupUserData(Long userId, String refreshToken) { + refreshTokenRepository.findByRefreshToken(refreshToken) + .ifPresent(refreshTokenRepository::delete); + feedRepository.updateUserToUnknown(userId); + commentRepository.updateUserToUnknown(userId); + userRepository.deleteById(userId); + } + public void editMyInfo(User user, EditMyInfoRequest editMyInfoRequest) { user.editMyInfo(editMyInfoRequest); } From 437b1a7d290754d3197c55d8acc8f970688a4032 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Mon, 18 Nov 2024 09:55:12 +0900 Subject: [PATCH 11/14] =?UTF-8?q?[FEAT]=20=EC=95=A0=ED=94=8C=20=EC=86=8C?= =?UTF-8?q?=EC=85=9C=20=EC=9C=A0=EC=A0=80=20=ED=83=88=ED=87=B4=20=EC=8B=9C?= =?UTF-8?q?=20DB=EB=82=B4=20=ED=95=B4=EB=8B=B9=20=EC=9C=A0=EC=A0=80=20User?= =?UTF-8?q?AppleToken=20=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/websoso/WSSServer/oauth2/service/AppleService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java index 6e1dc8a02..e2d46e551 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java @@ -135,6 +135,8 @@ public void unlinkFromApple(User user) { .body(createUserRevokeParams(createClientSecret(), userAppleToken.getAppleRefreshToken())) .retrieve() .body(String.class); + + userAppleTokenRepository.delete(userAppleToken); } private Map parseAppleTokenHeader(String appleToken) { From 20a3bb81e6d030e031705401785cd47201875000 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Mon, 18 Nov 2024 09:58:35 +0900 Subject: [PATCH 12/14] =?UTF-8?q?[FIX]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EC=8B=9C=20=EC=97=B0=EA=B4=80=20=EA=B4=80?= =?UTF-8?q?=EA=B3=84=EB=A5=BC=20=ED=99=9C=EC=9A=A9=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=8B=A0=EA=B3=A0=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=20=EB=B0=8F=20=EC=8B=A0=EA=B3=A0=20=EB=8C=93=EA=B8=80?= =?UTF-8?q?=EC=9D=B4=20=ED=95=A8=EA=BB=98=20=EC=82=AD=EC=A0=9C=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/websoso/WSSServer/domain/User.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/websoso/WSSServer/domain/User.java b/src/main/java/org/websoso/WSSServer/domain/User.java index 7da7580ba..3fccfe5e2 100644 --- a/src/main/java/org/websoso/WSSServer/domain/User.java +++ b/src/main/java/org/websoso/WSSServer/domain/User.java @@ -84,6 +84,12 @@ public class User { @OneToMany(mappedBy = "user", cascade = ALL, orphanRemoval = true) private List userNovels = new ArrayList<>(); + @OneToMany(mappedBy = "user", cascade = ALL, orphanRemoval = true) + private List reportedFeeds = new ArrayList<>(); + + @OneToMany(mappedBy = "user", cascade = ALL, orphanRemoval = true) + private List reportedComments = new ArrayList<>(); + public void updateProfileStatus(Boolean profileStatus) { this.isProfilePublic = profileStatus; } From 3babe1a0779af5a6af1c94b726a6a640f20e5ed0 Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Mon, 18 Nov 2024 10:23:35 +0900 Subject: [PATCH 13/14] =?UTF-8?q?[FEAT]=20=EC=9C=A0=EC=A0=80=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20=EC=8B=9C=20=ED=83=88=ED=87=B4=20=EC=82=AC=EC=9C=A0?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WSSServer/domain/WithdrawalReason.java | 33 +++++++++++++++++++ .../WithdrawalReasonRepository.java | 9 +++++ .../WSSServer/service/UserService.java | 5 +++ 3 files changed, 47 insertions(+) create mode 100644 src/main/java/org/websoso/WSSServer/domain/WithdrawalReason.java create mode 100644 src/main/java/org/websoso/WSSServer/repository/WithdrawalReasonRepository.java diff --git a/src/main/java/org/websoso/WSSServer/domain/WithdrawalReason.java b/src/main/java/org/websoso/WSSServer/domain/WithdrawalReason.java new file mode 100644 index 000000000..8b3c2d704 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/domain/WithdrawalReason.java @@ -0,0 +1,33 @@ +package org.websoso.WSSServer.domain; + +import static jakarta.persistence.GenerationType.IDENTITY; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class WithdrawalReason { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(nullable = false) + private Long withdrawalReasonId; + + @Column(columnDefinition = "varchar(80)", nullable = false) + private String withdrawalReasonContent; + + private WithdrawalReason(String withdrawalReasonContent) { + this.withdrawalReasonContent = withdrawalReasonContent; + } + + public static WithdrawalReason create(String withdrawalReasonContent) { + return new WithdrawalReason(withdrawalReasonContent); + } +} diff --git a/src/main/java/org/websoso/WSSServer/repository/WithdrawalReasonRepository.java b/src/main/java/org/websoso/WSSServer/repository/WithdrawalReasonRepository.java new file mode 100644 index 000000000..8f32b71a7 --- /dev/null +++ b/src/main/java/org/websoso/WSSServer/repository/WithdrawalReasonRepository.java @@ -0,0 +1,9 @@ +package org.websoso.WSSServer.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.websoso.WSSServer.domain.WithdrawalReason; + +@Repository +public interface WithdrawalReasonRepository extends JpaRepository { +} diff --git a/src/main/java/org/websoso/WSSServer/service/UserService.java b/src/main/java/org/websoso/WSSServer/service/UserService.java index 64f9dee22..9fc6b1c2e 100644 --- a/src/main/java/org/websoso/WSSServer/service/UserService.java +++ b/src/main/java/org/websoso/WSSServer/service/UserService.java @@ -21,6 +21,7 @@ import org.websoso.WSSServer.domain.Genre; import org.websoso.WSSServer.domain.GenrePreference; import org.websoso.WSSServer.domain.User; +import org.websoso.WSSServer.domain.WithdrawalReason; import org.websoso.WSSServer.domain.common.DiscordWebhookMessage; import org.websoso.WSSServer.dto.user.EditMyInfoRequest; import org.websoso.WSSServer.dto.user.EditProfileStatusRequest; @@ -47,6 +48,7 @@ import org.websoso.WSSServer.repository.GenreRepository; import org.websoso.WSSServer.repository.RefreshTokenRepository; import org.websoso.WSSServer.repository.UserRepository; +import org.websoso.WSSServer.repository.WithdrawalReasonRepository; @Service @RequiredArgsConstructor @@ -64,6 +66,7 @@ public class UserService { private final FeedRepository feedRepository; private final CommentRepository commentRepository; private final MessageService messageService; + private final WithdrawalReasonRepository withdrawalReasonRepository; private static final String KAKAO_PREFIX = "kakao"; private static final String APPLE_PREFIX = "apple"; @@ -183,6 +186,8 @@ public void withdrawUser(User user, WithdrawalRequest withdrawalRequest) { messageService.sendDiscordWebhookMessage( DiscordWebhookMessage.of(messageContent, WITHDRAW)); + + withdrawalReasonRepository.save(WithdrawalReason.create(withdrawalRequest.reason())); } private void checkNicknameIfAlreadyExist(String nickname) { From b6fdb862b22d3b5127ba38c456bd45bab45a817b Mon Sep 17 00:00:00 2001 From: ChaeAg Date: Mon, 18 Nov 2024 12:20:38 +0900 Subject: [PATCH 14/14] =?UTF-8?q?[REMOVE]=20=EB=A1=9C=EA=B7=B8=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/websoso/WSSServer/oauth2/service/AppleService.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java index e2d46e551..b156c4636 100644 --- a/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java +++ b/src/main/java/org/websoso/WSSServer/oauth2/service/AppleService.java @@ -198,7 +198,6 @@ private Claims extractClaims(String appleToken, PublicKey publicKey) { } catch (IllegalArgumentException e) { throw new CustomAppleLoginException(EMPTY_JWT, "empty jwt"); } catch (JwtException e) { - System.out.println(e.getMessage()); throw new CustomAppleLoginException(JWT_VERIFICATION_FAILED, "jwt validation or analysis failed"); } } @@ -213,7 +212,6 @@ private String createClientSecret() { return jwt.serialize(); } catch (Exception e) { - System.out.println(e.getMessage()); throw new CustomAppleLoginException(CLIENT_SECRET_CREATION_FAILED, "failed to generate client secret"); } } @@ -239,20 +237,16 @@ private void signJwt(SignedJWT jwt) { JWSSigner signer = new ECDSASigner(ecPrivateKey.getS()); jwt.sign(signer); } catch (Exception e) { - System.out.println(e.getMessage()); throw new CustomAppleLoginException(CLIENT_SECRET_CREATION_FAILED, "failed to create client secret"); } } private byte[] readPrivateKey(String keyPath) { Resource resource = new ClassPathResource(keyPath); - System.out.println("Resource exists: " + resource.exists()); - System.out.println("Resource file path: " + resource.getFilename()); try (PemReader pemReader = new PemReader(new InputStreamReader(resource.getInputStream()))) { PemObject pemObject = pemReader.readPemObject(); return pemObject.getContent(); } catch (IOException e) { - System.out.println(e.getMessage()); throw new CustomAppleLoginException(PRIVATE_KEY_READ_FAILED, "failed to read private key"); } } @@ -267,7 +261,6 @@ private AppleTokenResponse requestAppleToken(String authorizationCode, String cl .retrieve() .body(AppleTokenResponse.class); } catch (Exception e) { - System.out.println(e.getMessage()); throw new CustomAppleLoginException(TOKEN_REQUEST_FAILED, "failed to get token from Apple server"); } }