From 4f5123683879bb91152a3bcaca7c4c2355f3faac Mon Sep 17 00:00:00 2001 From: parksey Date: Sun, 3 Dec 2023 14:31:09 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EC=BD=94=EB=94=A9=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=9E=AC=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/application/admin/AdminMapper.java | 4 + .../auth/AuthorizationService.java | 405 +++++------ .../auth/JwtAuthenticationService.java | 80 ++- .../application/ranking/RankingMapper.java | 51 +- .../application/ranking/RankingService.java | 95 +-- .../auth/repository/TokenRepository.java | 51 +- .../auth/filter/AuthorizationFilter.java | 203 +++--- .../moabam/global/auth/filter/CorsFilter.java | 111 ++- .../global/config/AllowOriginConfig.java | 11 +- src/main/resources/static/docs/coupon.html | 12 +- src/main/resources/static/docs/index.html | 2 +- .../resources/static/docs/notification.html | 2 +- .../auth/AuthorizationServiceTest.java | 679 +++++++++--------- .../presentation/RankingControllerTest.java | 131 ++-- .../common/WithoutFilterSupporter.java | 53 +- .../fixture/MemberInfoSearchFixture.java | 62 +- 16 files changed, 984 insertions(+), 968 deletions(-) diff --git a/src/main/java/com/moabam/admin/application/admin/AdminMapper.java b/src/main/java/com/moabam/admin/application/admin/AdminMapper.java index d9cf0b35..648c2e4d 100644 --- a/src/main/java/com/moabam/admin/application/admin/AdminMapper.java +++ b/src/main/java/com/moabam/admin/application/admin/AdminMapper.java @@ -2,6 +2,10 @@ import com.moabam.admin.domain.admin.Admin; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class AdminMapper { public static Admin toAdmin(Long socialId) { diff --git a/src/main/java/com/moabam/api/application/auth/AuthorizationService.java b/src/main/java/com/moabam/api/application/auth/AuthorizationService.java index 4e040d07..3966d2c4 100644 --- a/src/main/java/com/moabam/api/application/auth/AuthorizationService.java +++ b/src/main/java/com/moabam/api/application/auth/AuthorizationService.java @@ -1,5 +1,14 @@ package com.moabam.api.application.auth; +import java.util.Arrays; + +import org.springframework.http.ResponseEntity; +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.util.UriComponentsBuilder; + import com.moabam.admin.application.admin.AdminService; import com.moabam.api.application.auth.mapper.AuthMapper; import com.moabam.api.application.auth.mapper.AuthorizationMapper; @@ -7,7 +16,13 @@ import com.moabam.api.domain.auth.repository.TokenRepository; import com.moabam.api.domain.member.Member; import com.moabam.api.domain.member.Role; -import com.moabam.api.dto.auth.*; +import com.moabam.api.dto.auth.AuthorizationCodeRequest; +import com.moabam.api.dto.auth.AuthorizationCodeResponse; +import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; +import com.moabam.api.dto.auth.AuthorizationTokenRequest; +import com.moabam.api.dto.auth.AuthorizationTokenResponse; +import com.moabam.api.dto.auth.LoginResponse; +import com.moabam.api.dto.auth.TokenSaveValue; import com.moabam.api.infrastructure.fcm.FcmService; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.auth.model.PublicClaim; @@ -19,214 +34,202 @@ import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -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.util.UriComponentsBuilder; - -import java.util.Arrays; @Slf4j @Service @RequiredArgsConstructor public class AuthorizationService { - private final FcmService fcmService; - private final OAuthConfig oAuthConfig; - private final TokenConfig tokenConfig; - private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService; - private final MemberService memberService; - private final AdminService adminService; - private final JwtProviderService jwtProviderService; - private final TokenRepository tokenRepository; - private final AllowOriginConfig allowOriginsConfig; - - public void redirectToLoginPage(HttpServletResponse httpServletResponse) { - String authorizationCodeUri = getAuthorizationCodeUri(); - oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri); - } - - public AuthorizationTokenResponse requestAdminToken(AuthorizationCodeResponse authorizationCodeResponse) { - validAuthorizationGrant(authorizationCodeResponse.code()); - - return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), - oAuthConfig.provider().adminRedirectUri()); - } - - public AuthorizationTokenResponse requestToken(AuthorizationCodeResponse authorizationCodeResponse) { - validAuthorizationGrant(authorizationCodeResponse.code()); - - return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), oAuthConfig.provider().redirectUri()); - } - - public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenResponse authorizationTokenResponse) { - String tokenValue = generateTokenValue(authorizationTokenResponse.accessToken()); - ResponseEntity authorizationTokenInfoResponse = - oauth2AuthorizationServerRequestService.tokenInfoRequest(oAuthConfig.provider().tokenInfo(), tokenValue); - - return authorizationTokenInfoResponse.getBody(); - } - - public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, - AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { - LoginResponse loginResponse = memberService.login(authorizationTokenInfoResponse); - issueServiceToken(httpServletResponse, loginResponse.publicClaim()); - - return loginResponse; - } - - public void issueServiceToken(HttpServletResponse response, PublicClaim publicClaim) { - String accessToken = jwtProviderService.provideAccessToken(publicClaim); - String refreshToken = jwtProviderService.provideRefreshToken(publicClaim.role()); - TokenSaveValue tokenSaveRequest = AuthMapper.toTokenSaveValue(refreshToken, null); - - tokenRepository.saveToken(publicClaim.id(), tokenSaveRequest, publicClaim.role()); - - String domain = getDomain(publicClaim.role()); - - response.addCookie( - CookieUtils.typeCookie("Bearer", tokenConfig.getRefreshExpire(), domain)); - response.addCookie( - CookieUtils.tokenCookie("access_token", accessToken, tokenConfig.getRefreshExpire(), domain)); - response.addCookie( - CookieUtils.tokenCookie("refresh_token", refreshToken, tokenConfig.getRefreshExpire(), domain)); - } - - public void validTokenPair(Long id, String oldRefreshToken, Role role) { - TokenSaveValue tokenSaveValue = tokenRepository.getTokenSaveValue(id, role); - - if (!tokenSaveValue.refreshToken().equals(oldRefreshToken)) { - tokenRepository.delete(id, role); - - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - } - - public void logout(AuthMember authMember, HttpServletRequest httpServletRequest, - HttpServletResponse httpServletResponse) { - removeToken(httpServletRequest, httpServletResponse); - tokenRepository.delete(authMember.id(), authMember.role()); - fcmService.deleteTokenByMemberId(authMember.id()); - } - - public void removeToken(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { - if (httpServletRequest.getCookies() == null) { - return; - } - - Arrays.stream(httpServletRequest.getCookies()) - .forEach(cookie -> { - if (cookie.getName().contains("token")) { - httpServletResponse.addCookie(CookieUtils.deleteCookie(cookie)); - } - }); - } - - @Transactional - public void unLinkMember(AuthMember authMember) { - Member member = memberService.findMemberToDelete(authMember.id()); - unlinkRequest(member.getSocialId()); - memberService.delete(member); - } - - private String getDomain(Role role) { - if (role.equals(Role.ADMIN)) { - return allowOriginsConfig.adminDomain(); - } - - return allowOriginsConfig.domain(); - } - - private void unlinkRequest(String socialId) { - try { - oauth2AuthorizationServerRequestService.unlinkMemberRequest( - oAuthConfig.provider().unlink(), - oAuthConfig.client().adminKey(), - unlinkRequestParam(socialId)); - log.info("회원 탈퇴 성공 : [socialId={}]", socialId); - } catch (BadRequestException badRequestException) { - log.warn("회원 탈퇴요청 실패 : 카카오 연결 오류"); - throw new BadRequestException(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS); - } - } - - private MultiValueMap unlinkRequestParam(String socialId) { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("target_id_type", "user_id"); - params.add("target_id", socialId); - - return params; - } - - private String getAuthorizationCodeUri() { - AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oAuthConfig); - return generateQueryParamsWith(authorizationCodeRequest); - } - - private String generateTokenValue(String token) { - return "Bearer" + GlobalConstant.SPACE + token; - } - - private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCodeRequest) { - UriComponentsBuilder authorizationCodeUri = UriComponentsBuilder.fromUriString( - oAuthConfig.provider().authorizationUri()) - .queryParam("response_type", "code") - .queryParam("client_id", authorizationCodeRequest.clientId()) - .queryParam("redirect_uri", authorizationCodeRequest.redirectUri()); - - if (authorizationCodeRequest.scope() != null - && !authorizationCodeRequest.scope().isEmpty()) { - String scopes = String.join(",", authorizationCodeRequest.scope()); - authorizationCodeUri.queryParam("scope", scopes); - } - - return authorizationCodeUri.toUriString(); - } - - private void validAuthorizationGrant(String code) { - if (code == null) { - throw new BadRequestException(ErrorMessage.GRANT_FAILED); - } - } - - private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code, String redirectUri) { - AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( - oAuthConfig, code, redirectUri); - MultiValueMap uriParams = generateTokenRequest(authorizationTokenRequest); - ResponseEntity authorizationTokenResponse = - oauth2AuthorizationServerRequestService.requestAuthorizationServer(oAuthConfig.provider().tokenUri(), - uriParams); - - return authorizationTokenResponse.getBody(); - } - - private MultiValueMap generateTokenRequest(AuthorizationTokenRequest authorizationTokenRequest) { - MultiValueMap contents = new LinkedMultiValueMap<>(); - contents.add("grant_type", authorizationTokenRequest.grantType()); - contents.add("client_id", authorizationTokenRequest.clientId()); - contents.add("redirect_uri", authorizationTokenRequest.redirectUri()); - contents.add("code", authorizationTokenRequest.code()); - - if (authorizationTokenRequest.clientSecret() != null) { - contents.add("client_secret", authorizationTokenRequest.clientSecret()); - } - - return contents; - } - - public void validMemberExist(Long id, Role role) { - if (role.equals(Role.ADMIN)) { - adminService.findMember(id); - - return; - } - - memberService.findMember(id); - } + private final FcmService fcmService; + private final OAuthConfig oAuthConfig; + private final TokenConfig tokenConfig; + private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService; + private final MemberService memberService; + private final AdminService adminService; + private final JwtProviderService jwtProviderService; + private final TokenRepository tokenRepository; + private final AllowOriginConfig allowOriginsConfig; + + public void redirectToLoginPage(HttpServletResponse httpServletResponse) { + String authorizationCodeUri = getAuthorizationCodeUri(); + oauth2AuthorizationServerRequestService.loginRequest(httpServletResponse, authorizationCodeUri); + } + + public AuthorizationTokenResponse requestAdminToken(AuthorizationCodeResponse authorizationCodeResponse) { + validAuthorizationGrant(authorizationCodeResponse.code()); + + return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), + oAuthConfig.provider().adminRedirectUri()); + } + + public AuthorizationTokenResponse requestToken(AuthorizationCodeResponse authorizationCodeResponse) { + validAuthorizationGrant(authorizationCodeResponse.code()); + + return issueTokenToAuthorizationServer(authorizationCodeResponse.code(), oAuthConfig.provider().redirectUri()); + } + + public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenResponse authorizationTokenResponse) { + String tokenValue = generateTokenValue(authorizationTokenResponse.accessToken()); + ResponseEntity authorizationTokenInfoResponse = + oauth2AuthorizationServerRequestService.tokenInfoRequest(oAuthConfig.provider().tokenInfo(), tokenValue); + + return authorizationTokenInfoResponse.getBody(); + } + + public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, + AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { + LoginResponse loginResponse = memberService.login(authorizationTokenInfoResponse); + issueServiceToken(httpServletResponse, loginResponse.publicClaim()); + + return loginResponse; + } + + public void issueServiceToken(HttpServletResponse response, PublicClaim publicClaim) { + String accessToken = jwtProviderService.provideAccessToken(publicClaim); + String refreshToken = jwtProviderService.provideRefreshToken(publicClaim.role()); + TokenSaveValue tokenSaveRequest = AuthMapper.toTokenSaveValue(refreshToken, null); + + tokenRepository.saveToken(publicClaim.id(), tokenSaveRequest, publicClaim.role()); + + String domain = getDomain(publicClaim.role()); + + response.addCookie(CookieUtils.typeCookie("Bearer", tokenConfig.getRefreshExpire(), domain)); + response.addCookie( + CookieUtils.tokenCookie("access_token", accessToken, tokenConfig.getRefreshExpire(), domain)); + response.addCookie( + CookieUtils.tokenCookie("refresh_token", refreshToken, tokenConfig.getRefreshExpire(), domain)); + } + + public void validTokenPair(Long id, String oldRefreshToken, Role role) { + TokenSaveValue tokenSaveValue = tokenRepository.getTokenSaveValue(id, role); + + if (!tokenSaveValue.refreshToken().equals(oldRefreshToken)) { + tokenRepository.delete(id, role); + + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + } + + public void logout(AuthMember authMember, HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) { + removeToken(httpServletRequest, httpServletResponse); + tokenRepository.delete(authMember.id(), authMember.role()); + fcmService.deleteTokenByMemberId(authMember.id()); + } + + public void removeToken(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + if (httpServletRequest.getCookies() == null) { + return; + } + + Arrays.stream(httpServletRequest.getCookies()).forEach(cookie -> { + if (cookie.getName().contains("token")) { + httpServletResponse.addCookie(CookieUtils.deleteCookie(cookie)); + } + }); + } + + @Transactional + public void unLinkMember(AuthMember authMember) { + Member member = memberService.findMemberToDelete(authMember.id()); + unlinkRequest(member.getSocialId()); + memberService.delete(member); + } + + private String getDomain(Role role) { + if (role.equals(Role.ADMIN)) { + return allowOriginsConfig.adminDomain(); + } + + return allowOriginsConfig.domain(); + } + + private void unlinkRequest(String socialId) { + try { + oauth2AuthorizationServerRequestService.unlinkMemberRequest(oAuthConfig.provider().unlink(), + oAuthConfig.client().adminKey(), unlinkRequestParam(socialId)); + log.info("회원 탈퇴 성공 : [socialId={}]", socialId); + } catch (BadRequestException badRequestException) { + log.warn("회원 탈퇴요청 실패 : 카카오 연결 오류"); + throw new BadRequestException(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS); + } + } + + private MultiValueMap unlinkRequestParam(String socialId) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("target_id_type", "user_id"); + params.add("target_id", socialId); + + return params; + } + + private String getAuthorizationCodeUri() { + AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oAuthConfig); + return generateQueryParamsWith(authorizationCodeRequest); + } + + private String generateTokenValue(String token) { + return "Bearer" + GlobalConstant.SPACE + token; + } + + private String generateQueryParamsWith(AuthorizationCodeRequest authorizationCodeRequest) { + UriComponentsBuilder authorizationCodeUri = UriComponentsBuilder.fromUriString( + oAuthConfig.provider().authorizationUri()) + .queryParam("response_type", "code") + .queryParam("client_id", authorizationCodeRequest.clientId()) + .queryParam("redirect_uri", authorizationCodeRequest.redirectUri()); + + if (authorizationCodeRequest.scope() != null && !authorizationCodeRequest.scope().isEmpty()) { + String scopes = String.join(",", authorizationCodeRequest.scope()); + authorizationCodeUri.queryParam("scope", scopes); + } + + return authorizationCodeUri.toUriString(); + } + + private void validAuthorizationGrant(String code) { + if (code == null) { + throw new BadRequestException(ErrorMessage.GRANT_FAILED); + } + } + + private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code, String redirectUri) { + AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( + oAuthConfig, code, redirectUri); + MultiValueMap uriParams = generateTokenRequest(authorizationTokenRequest); + ResponseEntity authorizationTokenResponse = + oauth2AuthorizationServerRequestService + .requestAuthorizationServer(oAuthConfig.provider().tokenUri(), uriParams); + + return authorizationTokenResponse.getBody(); + } + + private MultiValueMap generateTokenRequest(AuthorizationTokenRequest authorizationTokenRequest) { + MultiValueMap contents = new LinkedMultiValueMap<>(); + contents.add("grant_type", authorizationTokenRequest.grantType()); + contents.add("client_id", authorizationTokenRequest.clientId()); + contents.add("redirect_uri", authorizationTokenRequest.redirectUri()); + contents.add("code", authorizationTokenRequest.code()); + + if (authorizationTokenRequest.clientSecret() != null) { + contents.add("client_secret", authorizationTokenRequest.clientSecret()); + } + + return contents; + } + + public void validMemberExist(Long id, Role role) { + if (role.equals(Role.ADMIN)) { + adminService.findMember(id); + + return; + } + + memberService.findMember(id); + } } diff --git a/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java b/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java index a2b75462..52c652f9 100644 --- a/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java +++ b/src/main/java/com/moabam/api/application/auth/JwtAuthenticationService.java @@ -1,57 +1,59 @@ package com.moabam.api.application.auth; +import java.nio.charset.StandardCharsets; +import java.security.Key; + +import org.json.JSONObject; +import org.springframework.stereotype.Service; + import com.moabam.api.application.auth.mapper.AuthorizationMapper; import com.moabam.api.domain.member.Role; import com.moabam.global.auth.model.PublicClaim; import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; + import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.io.Decoders; import lombok.RequiredArgsConstructor; -import org.json.JSONObject; -import org.springframework.stereotype.Service; - -import java.nio.charset.StandardCharsets; -import java.security.Key; @Service @RequiredArgsConstructor public class JwtAuthenticationService { - private final TokenConfig tokenConfig; - - public boolean isTokenExpire(String token, Role role) { - try { - Key key = getSecret(role); - - Jwts.parserBuilder() - .setSigningKey(key) - .build() - .parseClaimsJws(token); - return false; - } catch (ExpiredJwtException expiredJwtException) { - return true; - } catch (Exception exception) { - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - } - - private Key getSecret(Role role) { - if (role.equals(Role.ADMIN)) { - return tokenConfig.getAdminKey(); - } - - return tokenConfig.getKey(); - } - - public PublicClaim parseClaim(String token) { - String claims = token.split("\\.")[1]; - byte[] claimsBytes = Decoders.BASE64URL.decode(claims); - String decodedClaims = new String(claimsBytes, StandardCharsets.UTF_8); - JSONObject jsonObject = new JSONObject(decodedClaims); - - return AuthorizationMapper.toPublicClaim(jsonObject); - } + private final TokenConfig tokenConfig; + + public boolean isTokenExpire(String token, Role role) { + try { + Key key = getSecret(role); + + Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + return false; + } catch (ExpiredJwtException expiredJwtException) { + return true; + } catch (Exception exception) { + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + } + + private Key getSecret(Role role) { + if (role.equals(Role.ADMIN)) { + return tokenConfig.getAdminKey(); + } + + return tokenConfig.getKey(); + } + + public PublicClaim parseClaim(String token) { + String claims = token.split("\\.")[1]; + byte[] claimsBytes = Decoders.BASE64URL.decode(claims); + String decodedClaims = new String(claimsBytes, StandardCharsets.UTF_8); + JSONObject jsonObject = new JSONObject(decodedClaims); + + return AuthorizationMapper.toPublicClaim(jsonObject); + } } diff --git a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java index 5246815f..817ccdc8 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java @@ -1,42 +1,39 @@ package com.moabam.api.application.ranking; +import java.util.List; + import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; + import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.util.List; - @NoArgsConstructor(access = AccessLevel.PRIVATE) public class RankingMapper { - public static TopRankingInfo topRankingResponse(int rank, long score, RankingInfo rankInfo) { - return TopRankingInfo.builder() - .rank(rank) - .score(score) - .nickname(rankInfo.nickname()) - .image(rankInfo.image()) - .memberId(rankInfo.memberId()) - .build(); - } + public static TopRankingInfo topRankingResponse(int rank, long score, RankingInfo rankInfo) { + return TopRankingInfo.builder() + .rank(rank) + .score(score) + .nickname(rankInfo.nickname()) + .image(rankInfo.image()) + .memberId(rankInfo.memberId()) + .build(); + } - public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRanking) { - return TopRankingInfo.builder() - .rank(rank + 1) - .score(updateRanking.score()) - .nickname(updateRanking.rankingInfo().nickname()) - .image(updateRanking.rankingInfo().image()) - .memberId(updateRanking.rankingInfo().memberId()) - .build(); - } + public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRanking) { + return TopRankingInfo.builder() + .rank(rank + 1) + .score(updateRanking.score()) + .nickname(updateRanking.rankingInfo().nickname()) + .image(updateRanking.rankingInfo().image()) + .memberId(updateRanking.rankingInfo().memberId()) + .build(); + } - public static TopRankingResponse topRankingResponses(TopRankingInfo myRanking, - List topRankings) { - return TopRankingResponse.builder() - .topRankings(topRankings) - .myRanking(myRanking) - .build(); - } + public static TopRankingResponse topRankingResponses(TopRankingInfo myRanking, List topRankings) { + return TopRankingResponse.builder().topRankings(topRankings).myRanking(myRanking).build(); + } } diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java index 77dd7956..65e620a3 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingService.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -1,74 +1,75 @@ package com.moabam.api.application.ranking; +import static java.util.Objects.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Service; + import com.fasterxml.jackson.databind.ObjectMapper; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.data.redis.core.ZSetOperations; -import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static java.util.Objects.requireNonNull; +import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class RankingService { - private static final String RANKING = "Ranking"; - private static final int START_INDEX = 0; - private static final int LIMIT_INDEX = 9; + private static final String RANKING = "Ranking"; + private static final int START_INDEX = 0; + private static final int LIMIT_INDEX = 9; - private final ObjectMapper objectMapper; - private final ZSetRedisRepository zSetRedisRepository; + private final ObjectMapper objectMapper; + private final ZSetRedisRepository zSetRedisRepository; - public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { - zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); - } + public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { + zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); + } - public void updateScores(List updateRankings) { - updateRankings.forEach(updateRanking -> - zSetRedisRepository.add(RANKING, updateRanking.rankingInfo(), updateRanking.score())); - } + public void updateScores(List updateRankings) { + updateRankings.forEach( + updateRanking -> zSetRedisRepository.add(RANKING, updateRanking.rankingInfo(), updateRanking.score())); + } - public void changeInfos(RankingInfo before, RankingInfo after) { - zSetRedisRepository.changeMember(RANKING, before, after); - } + public void changeInfos(RankingInfo before, RankingInfo after) { + zSetRedisRepository.changeMember(RANKING, before, after); + } - public void removeRanking(RankingInfo rankingInfo) { - zSetRedisRepository.delete(RANKING, rankingInfo); - } + public void removeRanking(RankingInfo rankingInfo) { + zSetRedisRepository.delete(RANKING, rankingInfo); + } - public TopRankingResponse getMemberRanking(UpdateRanking myRankingInfo) { - List topRankings = getTopRankings(); - Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); - TopRankingInfo myRankingInfoResponse = - RankingMapper.topRankingResponse(myRanking.intValue(), myRankingInfo); + public TopRankingResponse getMemberRanking(UpdateRanking myRankingInfo) { + List topRankings = getTopRankings(); + Long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()); + TopRankingInfo myRankingInfoResponse = RankingMapper.topRankingResponse(myRanking.intValue(), myRankingInfo); - return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); - } + return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); + } - private List getTopRankings() { - Set> topRankings = - zSetRedisRepository.rangeJson(RANKING, START_INDEX, LIMIT_INDEX); + private List getTopRankings() { + Set> topRankings = zSetRedisRepository.rangeJson(RANKING, START_INDEX, + LIMIT_INDEX); - Set scoreSet = new HashSet<>(); - List topRankingInfo = new ArrayList<>(); + Set scoreSet = new HashSet<>(); + List topRankingInfo = new ArrayList<>(); - for (ZSetOperations.TypedTuple topRanking : topRankings) { - long score = requireNonNull(topRanking.getScore()).longValue(); - scoreSet.add(score); + for (ZSetOperations.TypedTuple topRanking : topRankings) { + long score = requireNonNull(topRanking.getScore()).longValue(); + scoreSet.add(score); - RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); - topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); - } + RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); + topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); + } - return topRankingInfo; - } + return topRankingInfo; + } } diff --git a/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java b/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java index 0e5fba58..e104dad0 100644 --- a/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java +++ b/src/main/java/com/moabam/api/domain/auth/repository/TokenRepository.java @@ -1,42 +1,43 @@ package com.moabam.api.domain.auth.repository; -import com.moabam.api.domain.member.Role; -import com.moabam.api.dto.auth.TokenSaveValue; -import com.moabam.api.infrastructure.redis.HashRedisRepository; +import java.time.Duration; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; -import java.time.Duration; +import com.moabam.api.domain.member.Role; +import com.moabam.api.dto.auth.TokenSaveValue; +import com.moabam.api.infrastructure.redis.HashRedisRepository; @Repository public class TokenRepository { - private static final int EXPIRE_DAYS = 14; + private static final int EXPIRE_DAYS = 14; - private final HashRedisRepository hashRedisRepository; + private final HashRedisRepository hashRedisRepository; - @Autowired - public TokenRepository(HashRedisRepository hashRedisRepository) { - this.hashRedisRepository = hashRedisRepository; - } + @Autowired + public TokenRepository(HashRedisRepository hashRedisRepository) { + this.hashRedisRepository = hashRedisRepository; + } - public void saveToken(Long memberId, TokenSaveValue tokenSaveRequest, Role role) { - String tokenKey = parseTokenKey(memberId, role); + public void saveToken(Long memberId, TokenSaveValue tokenSaveRequest, Role role) { + String tokenKey = parseTokenKey(memberId, role); - hashRedisRepository.save(tokenKey, tokenSaveRequest, Duration.ofDays(EXPIRE_DAYS)); - } + hashRedisRepository.save(tokenKey, tokenSaveRequest, Duration.ofDays(EXPIRE_DAYS)); + } - public TokenSaveValue getTokenSaveValue(Long memberId, Role role) { - String tokenKey = parseTokenKey(memberId, role); - return (TokenSaveValue) hashRedisRepository.get(tokenKey); - } + public TokenSaveValue getTokenSaveValue(Long memberId, Role role) { + String tokenKey = parseTokenKey(memberId, role); + return (TokenSaveValue)hashRedisRepository.get(tokenKey); + } - public void delete(Long memberId, Role role) { - String tokenKey = parseTokenKey(memberId, role); - hashRedisRepository.delete(tokenKey); - } + public void delete(Long memberId, Role role) { + String tokenKey = parseTokenKey(memberId, role); + hashRedisRepository.delete(tokenKey); + } - private String parseTokenKey(Long memberId, Role role) { - return role.name() + "_" + memberId; - } + private String parseTokenKey(Long memberId, Role role) { + return role.name() + "_" + memberId; + } } diff --git a/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java b/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java index ded3a74a..ecc5438e 100644 --- a/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/AuthorizationFilter.java @@ -1,5 +1,15 @@ package com.moabam.global.auth.filter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + import com.moabam.api.application.auth.AuthorizationService; import com.moabam.api.application.auth.JwtAuthenticationService; import com.moabam.api.application.auth.mapper.AuthorizationMapper; @@ -9,6 +19,7 @@ import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; + import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; @@ -17,15 +28,6 @@ import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerExceptionResolver; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Objects; -import java.util.Optional; @Slf4j @Order(2) @@ -33,95 +35,96 @@ @RequiredArgsConstructor public class AuthorizationFilter extends OncePerRequestFilter { - private final HandlerExceptionResolver handlerExceptionResolver; - private final JwtAuthenticationService authenticationService; - private final AuthorizationService authorizationService; - - @Override - protected void doFilterInternal(@NotNull HttpServletRequest httpServletRequest, - @NotNull HttpServletResponse httpServletResponse, - @NotNull FilterChain filterChain) throws ServletException, IOException { - - if (isPermit(httpServletRequest)) { - filterChain.doFilter(httpServletRequest, httpServletResponse); - return; - } - - try { - invoke(httpServletRequest, httpServletResponse); - } catch (UnauthorizedException unauthorizedException) { - authorizationService.removeToken(httpServletRequest, httpServletResponse); - handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, - unauthorizedException); - - return; - } - - filterChain.doFilter(httpServletRequest, httpServletResponse); - } - - private boolean isPermit(HttpServletRequest httpServletRequest) { - Boolean isPermit = (Boolean) httpServletRequest.getAttribute("isPermit"); - - return Objects.nonNull(isPermit) && Boolean.TRUE.equals(isPermit); - } - - private void invoke(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { - Cookie[] cookies = getCookiesOrThrow(httpServletRequest); - - if (!isTokenTypeBearer(cookies)) { - throw new UnauthorizedException(ErrorMessage.GRANT_FAILED); - } - - handleTokenAuthenticate(cookies, httpServletResponse, httpServletRequest); - } - - private boolean isTokenTypeBearer(Cookie[] cookies) { - return "Bearer".equals(extractTokenFromCookie(cookies, "token_type")); - } - - private void handleTokenAuthenticate(Cookie[] cookies, - HttpServletResponse httpServletResponse, HttpServletRequest httpServletRequest) { - String accessToken = extractTokenFromCookie(cookies, "access_token"); - PublicClaim publicClaim = authenticationService.parseClaim(accessToken); - - if (authenticationService.isTokenExpire(accessToken, publicClaim.role())) { - String refreshToken = extractTokenFromCookie(cookies, "refresh_token"); - - if (authenticationService.isTokenExpire(refreshToken, publicClaim.role())) { - throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); - } - - validInvalidMember(publicClaim, refreshToken, httpServletRequest); - authorizationService.issueServiceToken(httpServletResponse, publicClaim); - } - - AuthorizationThreadLocal.setAuthMember(AuthorizationMapper.toAuthMember(publicClaim)); - } - - private void validInvalidMember(PublicClaim publicClaim, String refreshToken, - HttpServletRequest httpServletRequest) { - boolean isAdminPath = httpServletRequest.getRequestURI().contains("admins"); - - if (!((publicClaim.role().equals(Role.ADMIN) && isAdminPath) - || (publicClaim.role().equals(Role.USER) && !isAdminPath))) { - throw new BadRequestException(ErrorMessage.INVALID_REQUEST_ROLE); - } - - authorizationService.validTokenPair(publicClaim.id(), refreshToken, publicClaim.role()); - authorizationService.validMemberExist(publicClaim.id(), publicClaim.role()); - } - - private Cookie[] getCookiesOrThrow(HttpServletRequest httpServletRequest) { - return Optional.ofNullable(httpServletRequest.getCookies()) - .orElseThrow(() -> new UnauthorizedException(ErrorMessage.GRANT_FAILED)); - } - - private String extractTokenFromCookie(Cookie[] cookies, String tokenName) { - return Arrays.stream(cookies) - .filter(cookie -> tokenName.equals(cookie.getName())) - .map(Cookie::getValue) - .findFirst() - .orElseThrow(() -> new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL)); - } + private final HandlerExceptionResolver handlerExceptionResolver; + private final JwtAuthenticationService authenticationService; + private final AuthorizationService authorizationService; + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest httpServletRequest, + @NotNull HttpServletResponse httpServletResponse, @NotNull FilterChain filterChain) throws + ServletException, + IOException { + + if (isPermit(httpServletRequest)) { + filterChain.doFilter(httpServletRequest, httpServletResponse); + return; + } + + try { + invoke(httpServletRequest, httpServletResponse); + } catch (UnauthorizedException unauthorizedException) { + authorizationService.removeToken(httpServletRequest, httpServletResponse); + handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, + unauthorizedException); + + return; + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + private boolean isPermit(HttpServletRequest httpServletRequest) { + Boolean isPermit = (Boolean)httpServletRequest.getAttribute("isPermit"); + + return Objects.nonNull(isPermit) && Boolean.TRUE.equals(isPermit); + } + + private void invoke(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + Cookie[] cookies = getCookiesOrThrow(httpServletRequest); + + if (!isTokenTypeBearer(cookies)) { + throw new UnauthorizedException(ErrorMessage.GRANT_FAILED); + } + + handleTokenAuthenticate(cookies, httpServletResponse, httpServletRequest); + } + + private boolean isTokenTypeBearer(Cookie[] cookies) { + return "Bearer".equals(extractTokenFromCookie(cookies, "token_type")); + } + + private void handleTokenAuthenticate(Cookie[] cookies, HttpServletResponse httpServletResponse, + HttpServletRequest httpServletRequest) { + String accessToken = extractTokenFromCookie(cookies, "access_token"); + PublicClaim publicClaim = authenticationService.parseClaim(accessToken); + + if (authenticationService.isTokenExpire(accessToken, publicClaim.role())) { + String refreshToken = extractTokenFromCookie(cookies, "refresh_token"); + + if (authenticationService.isTokenExpire(refreshToken, publicClaim.role())) { + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + + validInvalidMember(publicClaim, refreshToken, httpServletRequest); + authorizationService.issueServiceToken(httpServletResponse, publicClaim); + } + + AuthorizationThreadLocal.setAuthMember(AuthorizationMapper.toAuthMember(publicClaim)); + } + + private void validInvalidMember(PublicClaim publicClaim, String refreshToken, + HttpServletRequest httpServletRequest) { + boolean isAdminPath = httpServletRequest.getRequestURI().contains("admins"); + + if (!((publicClaim.role().equals(Role.ADMIN) && isAdminPath) || (publicClaim.role().equals(Role.USER) + && !isAdminPath))) { + throw new BadRequestException(ErrorMessage.INVALID_REQUEST_ROLE); + } + + authorizationService.validTokenPair(publicClaim.id(), refreshToken, publicClaim.role()); + authorizationService.validMemberExist(publicClaim.id(), publicClaim.role()); + } + + private Cookie[] getCookiesOrThrow(HttpServletRequest httpServletRequest) { + return Optional.ofNullable(httpServletRequest.getCookies()) + .orElseThrow(() -> new UnauthorizedException(ErrorMessage.GRANT_FAILED)); + } + + private String extractTokenFromCookie(Cookie[] cookies, String tokenName) { + return Arrays.stream(cookies) + .filter(cookie -> tokenName.equals(cookie.getName())) + .map(Cookie::getValue) + .findFirst() + .orElseThrow(() -> new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL)); + } } diff --git a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java index aaafe1b5..70508dbd 100644 --- a/src/main/java/com/moabam/global/auth/filter/CorsFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/CorsFilter.java @@ -1,22 +1,24 @@ package com.moabam.global.auth.filter; +import java.io.IOException; +import java.util.Objects; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + import com.google.cloud.storage.HttpMethod; import com.moabam.global.config.AllowOriginConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; + import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.servlet.HandlerExceptionResolver; - -import java.io.IOException; -import java.util.Objects; @Slf4j @Order(0) @@ -24,53 +26,50 @@ @RequiredArgsConstructor public class CorsFilter extends OncePerRequestFilter { - private static final String ALLOWED_METHOD_NAMES = "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"; - private static final String ALLOWED_HEADERS = "Origin, Accept, Access-Control-Request-Method, " - + "Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer"; - - private final HandlerExceptionResolver handlerExceptionResolver; - - private final AllowOriginConfig allowOriginsConfig; - - @Override - protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, - FilterChain filterChain) throws ServletException, IOException { - String refer = httpServletRequest.getHeader("referer"); - String origin = secureMatch(refer); - - try { - if (Objects.isNull(origin)) { - throw new UnauthorizedException(ErrorMessage.INVALID_REQUEST_URL); - } - } catch (UnauthorizedException unauthorizedException) { - log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOriginsConfig.origin()); - handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, - unauthorizedException); - - return; - } - - httpServletResponse.setHeader("Access-Control-Allow-Origin", origin); - httpServletResponse.setHeader("Access-Control-Allow-Methods", ALLOWED_METHOD_NAMES); - httpServletResponse.setHeader("Access-Control-Allow-Headers", ALLOWED_HEADERS); - httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); - httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); - - if (isOption(httpServletRequest.getMethod())) { - httpServletRequest.setAttribute("isPermit", true); - } - - filterChain.doFilter(httpServletRequest, httpServletResponse); - } - - public String secureMatch(String refer) { - return allowOriginsConfig.origin().stream() - .filter(refer::contains) - .findFirst() - .orElse(null); - } - - public boolean isOption(String method) { - return HttpMethod.OPTIONS.name().equals(method); - } + private static final String ALLOWED_METHOD_NAMES = "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"; + private static final String ALLOWED_HEADERS = "Origin, Accept, Access-Control-Request-Method, " + + "Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer"; + + private final HandlerExceptionResolver handlerExceptionResolver; + + private final AllowOriginConfig allowOriginsConfig; + + @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, + FilterChain filterChain) throws ServletException, IOException { + String refer = httpServletRequest.getHeader("referer"); + String origin = secureMatch(refer); + + try { + if (Objects.isNull(origin)) { + throw new UnauthorizedException(ErrorMessage.INVALID_REQUEST_URL); + } + } catch (UnauthorizedException unauthorizedException) { + log.error("{}, {}", httpServletRequest.getHeader("referer"), allowOriginsConfig.origin()); + handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, + unauthorizedException); + + return; + } + + httpServletResponse.setHeader("Access-Control-Allow-Origin", origin); + httpServletResponse.setHeader("Access-Control-Allow-Methods", ALLOWED_METHOD_NAMES); + httpServletResponse.setHeader("Access-Control-Allow-Headers", ALLOWED_HEADERS); + httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); + + if (isOption(httpServletRequest.getMethod())) { + httpServletRequest.setAttribute("isPermit", true); + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + public String secureMatch(String refer) { + return allowOriginsConfig.origin().stream().filter(refer::contains).findFirst().orElse(null); + } + + public boolean isOption(String method) { + return HttpMethod.OPTIONS.name().equals(method); + } } diff --git a/src/main/java/com/moabam/global/config/AllowOriginConfig.java b/src/main/java/com/moabam/global/config/AllowOriginConfig.java index 3392dd90..b580a99f 100644 --- a/src/main/java/com/moabam/global/config/AllowOriginConfig.java +++ b/src/main/java/com/moabam/global/config/AllowOriginConfig.java @@ -1,14 +1,13 @@ package com.moabam.global.config; -import org.springframework.boot.context.properties.ConfigurationProperties; - import java.util.List; +import org.springframework.boot.context.properties.ConfigurationProperties; + @ConfigurationProperties(prefix = "allows") public record AllowOriginConfig( - String adminDomain, - String domain, - List origin -) { + String adminDomain, + String domain, + List origin) { } diff --git a/src/main/resources/static/docs/coupon.html b/src/main/resources/static/docs/coupon.html index b0e56d22..a52ba6e9 100644 --- a/src/main/resources/static/docs/coupon.html +++ b/src/main/resources/static/docs/coupon.html @@ -461,7 +461,7 @@

요청

POST /admins/coupons HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Content-Length: 178
+Content-Length: 186
 Host: localhost:8080
 
 {
@@ -540,7 +540,7 @@ 

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 192 +Content-Length: 202 { "id" : 24, @@ -571,7 +571,7 @@

요청

POST /coupons/search HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Content-Length: 41
+Content-Length: 44
 Host: localhost:8080
 
 {
@@ -590,7 +590,7 @@ 

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 193 +Content-Length: 203 [ { "id" : 25, @@ -637,7 +637,7 @@

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 63 +Content-Length: 65 { "message" : "이미 쿠폰 발급에 성공했습니다!" @@ -716,7 +716,7 @@

응답

diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 154f140e..4c8f2a12 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -616,7 +616,7 @@

diff --git a/src/main/resources/static/docs/notification.html b/src/main/resources/static/docs/notification.html index a6327a68..d4ddae0c 100644 --- a/src/main/resources/static/docs/notification.html +++ b/src/main/resources/static/docs/notification.html @@ -513,7 +513,7 @@

응답

diff --git a/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java b/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java index 678428ff..b746b6d6 100644 --- a/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java +++ b/src/test/java/com/moabam/api/application/auth/AuthorizationServiceTest.java @@ -1,12 +1,40 @@ package com.moabam.api.application.auth; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; + +import java.util.List; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.util.ReflectionTestUtils; + import com.moabam.admin.application.admin.AdminService; import com.moabam.api.application.auth.mapper.AuthorizationMapper; import com.moabam.api.application.member.MemberService; import com.moabam.api.domain.auth.repository.TokenRepository; import com.moabam.api.domain.member.Member; import com.moabam.api.domain.member.Role; -import com.moabam.api.dto.auth.*; +import com.moabam.api.dto.auth.AuthorizationCodeRequest; +import com.moabam.api.dto.auth.AuthorizationCodeResponse; +import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; +import com.moabam.api.dto.auth.AuthorizationTokenRequest; +import com.moabam.api.dto.auth.AuthorizationTokenResponse; +import com.moabam.api.dto.auth.LoginResponse; import com.moabam.api.infrastructure.fcm.FcmService; import com.moabam.global.auth.model.AuthMember; import com.moabam.global.auth.model.PublicClaim; @@ -23,344 +51,323 @@ import com.moabam.support.fixture.AuthorizationResponseFixture; import com.moabam.support.fixture.MemberFixture; import com.moabam.support.fixture.TokenSaveValueFixture; -import jakarta.servlet.http.Cookie; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.util.ReflectionTestUtils; -import java.util.List; - -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.*; +import jakarta.servlet.http.Cookie; @ExtendWith({MockitoExtension.class, FilterProcessExtension.class}) class AuthorizationServiceTest { - @InjectMocks - AuthorizationService authorizationService; - - @Mock - OAuth2AuthorizationServerRequestService oAuth2AuthorizationServerRequestService; - - @Mock - MemberService memberService; - - @Mock - AdminService adminService; - - @Mock - JwtProviderService jwtProviderService; - - @Mock - FcmService fcmService; - - @Mock - TokenRepository tokenRepository; - - AllowOriginConfig allowOriginsConfig; - OAuthConfig oauthConfig; - TokenConfig tokenConfig; - AuthorizationService noPropertyService; - OAuthConfig noOAuthConfig; - String domain = "Test"; - - @BeforeEach - public void initParams() { - String secretKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; - String adminKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; - - allowOriginsConfig = new AllowOriginConfig(domain, domain, List.of("test", "test")); - ReflectionTestUtils.setField(authorizationService, "allowOriginsConfig", allowOriginsConfig); - tokenConfig = new TokenConfig(null, 100000, 150000, secretKey, adminKey); - ReflectionTestUtils.setField(authorizationService, "tokenConfig", tokenConfig); - - oauthConfig = new OAuthConfig( - new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url", - "http://tokenInfo/url", "https://deleteRequest/url", "https://adminRequest/url"), - new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code", - List.of("profile_nickname", "profile_image"), "adminKey")); - ReflectionTestUtils.setField(authorizationService, "oAuthConfig", oauthConfig); - - noOAuthConfig = new OAuthConfig( - new OAuthConfig.Provider(null, null, null, null, null, null), - new OAuthConfig.Client(null, null, null, null, null, null)); - noPropertyService = new AuthorizationService(fcmService, noOAuthConfig, tokenConfig, - oAuth2AuthorizationServerRequestService, memberService, adminService, - jwtProviderService, tokenRepository, allowOriginsConfig); - } - - @DisplayName("인가코드 URI 생성 매퍼 실패") - @Test - void authorization_code_request_mapping_fail() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationCodeRequest(noOAuthConfig)) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("인가코드 URI 생성 매퍼 성공") - @Test - void authorization_code_request_mapping_success() { - // Given - AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oauthConfig); - - // When + Then - assertThat(authorizationCodeRequest).isNotNull(); - assertAll(() -> assertThat(authorizationCodeRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), - () -> assertThat(authorizationCodeRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri())); - } - - @DisplayName("redirect 로그인페이지 성공") - @Test - void redirect_loginPage_success() { - // given - MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); - - // when - authorizationService.redirectToLoginPage(mockHttpServletResponse); - - // then - verify(oAuth2AuthorizationServerRequestService).loginRequest(eq(mockHttpServletResponse), anyString()); - } - - @DisplayName("인가코드 반환 실패") - @Test - void authorization_grant_fail() { - // Given - AuthorizationCodeResponse authorizationCodeResponse = new AuthorizationCodeResponse(null, "error", - "errorDescription", null); - - // When + Then - assertThatThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)).isInstanceOf( - BadRequestException.class).hasMessage(ErrorMessage.GRANT_FAILED.getMessage()); - } - - @DisplayName("인가코드 반환 성공") - @Test - void authorization_grant_success() { - // Given - AuthorizationCodeResponse authorizationCodeResponse = - new AuthorizationCodeResponse("test", null, null, null); - AuthorizationTokenResponse authorizationTokenResponse = - AuthorizationResponseFixture.authorizationTokenResponse(); - - // When - when(oAuth2AuthorizationServerRequestService.requestAuthorizationServer(anyString(), any())).thenReturn( - new ResponseEntity<>(authorizationTokenResponse, HttpStatus.OK)); - - // When + Then - assertThatNoException().isThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)); - } - - @DisplayName("토큰 요청 매퍼 실패 - code null") - @Test - void token_request_mapping_failBy_code() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, - null, oauthConfig.provider().redirectUri())) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("토큰 요청 매퍼 실패 - config 에러") - @Test - void token_request_mapping_failBy_config() { - // When + Then - Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test", - oauthConfig.provider().redirectUri())) - .isInstanceOf(NullPointerException.class); - } - - @DisplayName("토큰 요청 매퍼 성공") - @Test - void token_request_mapping_success() { - // Given - String code = "Test"; - AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( - oauthConfig, code, oauthConfig.provider().redirectUri()); - - // When + Then - assertThat(authorizationTokenRequest).isNotNull(); - assertAll(() -> assertThat(authorizationTokenRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), - () -> assertThat(authorizationTokenRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri()), - () -> assertThat(authorizationTokenRequest.code()).isEqualTo(code)); - } - - @DisplayName("토큰 변경 성공") - @Test - void generate_token() { - // Given - AuthorizationTokenResponse tokenResponse = AuthorizationResponseFixture.authorizationTokenResponse(); - AuthorizationTokenInfoResponse tokenInfoResponse = - AuthorizationResponseFixture.authorizationTokenInfoResponse(); - - // When - when(oAuth2AuthorizationServerRequestService.tokenInfoRequest(any(String.class), - eq("Bearer " + tokenResponse.accessToken()))).thenReturn( - new ResponseEntity<>(tokenInfoResponse, HttpStatus.OK)); - - // Then - assertThatNoException().isThrownBy(() -> authorizationService.requestTokenInfo(tokenResponse)); - } - - @DisplayName("회원 가입 및 로그인 성공 테스트") - @ParameterizedTest - @ValueSource(booleans = {true, false}) - void signUp_success(boolean isSignUp) { - // given - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - AuthorizationTokenInfoResponse authorizationTokenInfoResponse = - AuthorizationResponseFixture.authorizationTokenInfoResponse(); - LoginResponse loginResponse = LoginResponse.builder() - .publicClaim(PublicClaim.builder().id(1L).nickname("nickname").role(Role.USER).build()) - .isSignUp(isSignUp) - .build(); - - willReturn(loginResponse).given(memberService).login(authorizationTokenInfoResponse); - - // when - LoginResponse result = authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); - - // then - assertThat(loginResponse).isEqualTo(result); - - Cookie tokenType = httpServletResponse.getCookie("token_type"); - assertThat(tokenType).isNotNull(); - assertThat(tokenType.getValue()).isEqualTo("Bearer"); - - Cookie accessCookie = httpServletResponse.getCookie("access_token"); - assertThat(accessCookie).isNotNull(); - assertAll(() -> assertThat(accessCookie.getSecure()).isTrue(), - () -> assertThat(accessCookie.isHttpOnly()).isTrue(), - () -> assertThat(accessCookie.getPath()).isEqualTo("/")); - - Cookie refreshCookie = httpServletResponse.getCookie("refresh_token"); - assertThat(refreshCookie).isNotNull(); - assertAll(() -> assertThat(refreshCookie.getSecure()).isTrue(), - () -> assertThat(refreshCookie.isHttpOnly()).isTrue(), - () -> assertThat(refreshCookie.getPath()).isEqualTo("/")); - } - - @DisplayName("토큰 redis 검증") - @Test - void valid_token_in_redis() { - // Given - willReturn(TokenSaveValueFixture.tokenSaveValue("token")) - .given(tokenRepository).getTokenSaveValue(1L, Role.USER); - - // When + Then - assertThatNoException().isThrownBy(() -> - authorizationService.validTokenPair(1L, "token", Role.USER)); - } - - @DisplayName("이전 토큰과 동일한지 검증") - @Test - void valid_token_failby_notEquals_token() { - // Given - willReturn(TokenSaveValueFixture.tokenSaveValue("token")) - .given(tokenRepository).getTokenSaveValue(1L, Role.USER); - - // When + Then - assertThatThrownBy(() -> authorizationService.validTokenPair(1L, "oldToken", Role.USER)).isInstanceOf( - UnauthorizedException.class).hasMessage(ErrorMessage.AUTHENTICATE_FAIL.getMessage()); - verify(tokenRepository).delete(1L, Role.USER); - } - - @DisplayName("토큰 삭제 성공") - @Test - void error_with_expire_token(@WithMember AuthMember authMember) { - // given - MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); - httpServletRequest.setCookies( - CookieUtils.tokenCookie("access_token", "value", 100000, domain), - CookieUtils.tokenCookie("refresh_token", "value", 100000, domain), - CookieUtils.typeCookie("Bearer", 100000, domain)); - - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - - // When - authorizationService.logout(authMember, httpServletRequest, httpServletResponse); - Cookie cookie = httpServletResponse.getCookie("access_token"); - - // Then - assertThat(cookie).isNotNull(); - assertThat(cookie.getMaxAge()).isZero(); - assertThat(cookie.getValue()).isEqualTo("value"); - - verify(tokenRepository).delete(authMember.id(), Role.USER); - } - - @DisplayName("토큰 없어서 삭제 실패") - @Test - void token_null_delete_fail(@WithMember AuthMember authMember) { - // given - MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); - MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); - - // When - authorizationService.logout(authMember, httpServletRequest, httpServletResponse); - - // Then - assertThat(httpServletResponse.getCookies()).isEmpty(); - } - - @DisplayName("회원 탈퇴 요청 성공") - @Test - void unlink_success(@WithMember AuthMember authMember) { - // given - Member member = MemberFixture.member(); - - willReturn(member) - .given(memberService) - .findMemberToDelete(authMember.id()); - doNothing().when(oAuth2AuthorizationServerRequestService) - .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); - - // When + Then - assertThatNoException().isThrownBy(() -> authorizationService.unLinkMember(authMember)); - } - - @DisplayName("회원이 없어서 찾기 실패") - @Test - void unlink_failBy_find_Member(@WithMember AuthMember authMember) { - // Given + When - willThrow(new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND)) - .given(memberService) - .findMemberToDelete(authMember.id()); - - assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) - .isInstanceOf(NotFoundException.class) - .hasMessage(ErrorMessage.MEMBER_NOT_FOUND.getMessage()); - } - - @DisplayName("소셜 탈퇴 요청 실패로 인한 실패") - @Test - void unlink_failBy_(@WithMember AuthMember authMember) { - // Given - Member member = MemberFixture.member(); - - willReturn(member) - .given(memberService) - .findMemberToDelete(authMember.id()); - willThrow(BadRequestException.class) - .given(oAuth2AuthorizationServerRequestService) - .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); - - // When + Then - assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) - .isInstanceOf(BadRequestException.class) - .hasMessage(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS.getMessage()); - } + @InjectMocks + AuthorizationService authorizationService; + + @Mock + OAuth2AuthorizationServerRequestService oAuth2AuthorizationServerRequestService; + + @Mock + MemberService memberService; + + @Mock + AdminService adminService; + + @Mock + JwtProviderService jwtProviderService; + + @Mock + FcmService fcmService; + + @Mock + TokenRepository tokenRepository; + + AllowOriginConfig allowOriginsConfig; + OAuthConfig oauthConfig; + TokenConfig tokenConfig; + AuthorizationService noPropertyService; + OAuthConfig noOAuthConfig; + String domain = "Test"; + + @BeforeEach + public void initParams() { + String secretKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; + String adminKey = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; + + allowOriginsConfig = new AllowOriginConfig(domain, domain, List.of("test", "test")); + ReflectionTestUtils.setField(authorizationService, "allowOriginsConfig", allowOriginsConfig); + tokenConfig = new TokenConfig(null, 100000, 150000, secretKey, adminKey); + ReflectionTestUtils.setField(authorizationService, "tokenConfig", tokenConfig); + + oauthConfig = new OAuthConfig( + new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url", + "http://tokenInfo/url", "https://deleteRequest/url", "https://adminRequest/url"), + new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code", + List.of("profile_nickname", "profile_image"), "adminKey")); + ReflectionTestUtils.setField(authorizationService, "oAuthConfig", oauthConfig); + + noOAuthConfig = new OAuthConfig( + new OAuthConfig.Provider(null, null, null, null, null, null), + new OAuthConfig.Client(null, null, null, null, null, null)); + noPropertyService = new AuthorizationService(fcmService, noOAuthConfig, tokenConfig, + oAuth2AuthorizationServerRequestService, memberService, adminService, + jwtProviderService, tokenRepository, allowOriginsConfig); + } + + @DisplayName("인가코드 URI 생성 매퍼 실패") + @Test + void authorization_code_request_mapping_fail() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationCodeRequest(noOAuthConfig)) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("인가코드 URI 생성 매퍼 성공") + @Test + void authorization_code_request_mapping_success() { + // Given + AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oauthConfig); + + // When + Then + assertThat(authorizationCodeRequest).isNotNull(); + assertAll(() -> assertThat(authorizationCodeRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), + () -> assertThat(authorizationCodeRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri())); + } + + @DisplayName("redirect 로그인페이지 성공") + @Test + void redirect_loginPage_success() { + // given + MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); + + // when + authorizationService.redirectToLoginPage(mockHttpServletResponse); + + // then + verify(oAuth2AuthorizationServerRequestService).loginRequest(eq(mockHttpServletResponse), anyString()); + } + + @DisplayName("인가코드 반환 실패") + @Test + void authorization_grant_fail() { + // Given + AuthorizationCodeResponse authorizationCodeResponse = new AuthorizationCodeResponse(null, "error", + "errorDescription", null); + + // When + Then + assertThatThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)).isInstanceOf( + BadRequestException.class).hasMessage(ErrorMessage.GRANT_FAILED.getMessage()); + } + + @DisplayName("인가코드 반환 성공") + @Test + void authorization_grant_success() { + // Given + AuthorizationCodeResponse authorizationCodeResponse = + new AuthorizationCodeResponse("test", null, null, null); + AuthorizationTokenResponse authorizationTokenResponse = + AuthorizationResponseFixture.authorizationTokenResponse(); + + // When + when(oAuth2AuthorizationServerRequestService.requestAuthorizationServer(anyString(), any())).thenReturn( + new ResponseEntity<>(authorizationTokenResponse, HttpStatus.OK)); + + // When + Then + assertThatNoException().isThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)); + } + + @DisplayName("토큰 요청 매퍼 실패 - code null") + @Test + void token_request_mapping_failBy_code() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, + null, oauthConfig.provider().redirectUri())) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("토큰 요청 매퍼 실패 - config 에러") + @Test + void token_request_mapping_failBy_config() { + // When + Then + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test", + oauthConfig.provider().redirectUri())) + .isInstanceOf(NullPointerException.class); + } + + @DisplayName("토큰 요청 매퍼 성공") + @Test + void token_request_mapping_success() { + // Given + String code = "Test"; + AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( + oauthConfig, code, oauthConfig.provider().redirectUri()); + + // When + Then + assertThat(authorizationTokenRequest).isNotNull(); + assertAll(() -> assertThat(authorizationTokenRequest.clientId()).isEqualTo(oauthConfig.client().clientId()), + () -> assertThat(authorizationTokenRequest.redirectUri()).isEqualTo(oauthConfig.provider().redirectUri()), + () -> assertThat(authorizationTokenRequest.code()).isEqualTo(code)); + } + + @DisplayName("토큰 변경 성공") + @Test + void generate_token() { + // Given + AuthorizationTokenResponse tokenResponse = AuthorizationResponseFixture.authorizationTokenResponse(); + AuthorizationTokenInfoResponse tokenInfoResponse = + AuthorizationResponseFixture.authorizationTokenInfoResponse(); + + // When + when(oAuth2AuthorizationServerRequestService.tokenInfoRequest(any(String.class), + eq("Bearer " + tokenResponse.accessToken()))).thenReturn( + new ResponseEntity<>(tokenInfoResponse, HttpStatus.OK)); + + // Then + assertThatNoException().isThrownBy(() -> authorizationService.requestTokenInfo(tokenResponse)); + } + + @DisplayName("회원 가입 및 로그인 성공 테스트") + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void signUp_success(boolean isSignUp) { + // given + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + AuthorizationTokenInfoResponse authorizationTokenInfoResponse = + AuthorizationResponseFixture.authorizationTokenInfoResponse(); + LoginResponse loginResponse = LoginResponse.builder() + .publicClaim(PublicClaim.builder().id(1L).nickname("nickname").role(Role.USER).build()) + .isSignUp(isSignUp) + .build(); + + willReturn(loginResponse).given(memberService).login(authorizationTokenInfoResponse); + + // when + LoginResponse result = authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); + + // then + assertThat(loginResponse).isEqualTo(result); + + Cookie tokenType = httpServletResponse.getCookie("token_type"); + assertThat(tokenType).isNotNull(); + assertThat(tokenType.getValue()).isEqualTo("Bearer"); + + Cookie accessCookie = httpServletResponse.getCookie("access_token"); + assertThat(accessCookie).isNotNull(); + assertAll(() -> assertThat(accessCookie.getSecure()).isTrue(), + () -> assertThat(accessCookie.isHttpOnly()).isTrue(), + () -> assertThat(accessCookie.getPath()).isEqualTo("/")); + + Cookie refreshCookie = httpServletResponse.getCookie("refresh_token"); + assertThat(refreshCookie).isNotNull(); + assertAll(() -> assertThat(refreshCookie.getSecure()).isTrue(), + () -> assertThat(refreshCookie.isHttpOnly()).isTrue(), + () -> assertThat(refreshCookie.getPath()).isEqualTo("/")); + } + + @DisplayName("토큰 redis 검증") + @Test + void valid_token_in_redis() { + // Given + willReturn(TokenSaveValueFixture.tokenSaveValue("token")) + .given(tokenRepository).getTokenSaveValue(1L, Role.USER); + + // When + Then + assertThatNoException().isThrownBy(() -> + authorizationService.validTokenPair(1L, "token", Role.USER)); + } + + @DisplayName("이전 토큰과 동일한지 검증") + @Test + void valid_token_failby_notEquals_token() { + // Given + willReturn(TokenSaveValueFixture.tokenSaveValue("token")) + .given(tokenRepository).getTokenSaveValue(1L, Role.USER); + + // When + Then + assertThatThrownBy(() -> authorizationService.validTokenPair(1L, "oldToken", Role.USER)).isInstanceOf( + UnauthorizedException.class).hasMessage(ErrorMessage.AUTHENTICATE_FAIL.getMessage()); + verify(tokenRepository).delete(1L, Role.USER); + } + + @DisplayName("토큰 삭제 성공") + @Test + void error_with_expire_token(@WithMember AuthMember authMember) { + // given + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + httpServletRequest.setCookies( + CookieUtils.tokenCookie("access_token", "value", 100000, domain), + CookieUtils.tokenCookie("refresh_token", "value", 100000, domain), + CookieUtils.typeCookie("Bearer", 100000, domain)); + + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + + // When + authorizationService.logout(authMember, httpServletRequest, httpServletResponse); + Cookie cookie = httpServletResponse.getCookie("access_token"); + + // Then + assertThat(cookie).isNotNull(); + assertThat(cookie.getMaxAge()).isZero(); + assertThat(cookie.getValue()).isEqualTo("value"); + + verify(tokenRepository).delete(authMember.id(), Role.USER); + } + + @DisplayName("토큰 없어서 삭제 실패") + @Test + void token_null_delete_fail(@WithMember AuthMember authMember) { + // given + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + + // When + authorizationService.logout(authMember, httpServletRequest, httpServletResponse); + + // Then + assertThat(httpServletResponse.getCookies()).isEmpty(); + } + + @DisplayName("회원 탈퇴 요청 성공") + @Test + void unlink_success(@WithMember AuthMember authMember) { + // given + Member member = MemberFixture.member(); + + willReturn(member) + .given(memberService) + .findMemberToDelete(authMember.id()); + doNothing().when(oAuth2AuthorizationServerRequestService) + .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); + + // When + Then + assertThatNoException().isThrownBy(() -> authorizationService.unLinkMember(authMember)); + } + + @DisplayName("회원이 없어서 찾기 실패") + @Test + void unlink_failBy_find_Member(@WithMember AuthMember authMember) { + // Given + When + willThrow(new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND)) + .given(memberService) + .findMemberToDelete(authMember.id()); + + assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) + .isInstanceOf(NotFoundException.class) + .hasMessage(ErrorMessage.MEMBER_NOT_FOUND.getMessage()); + } + + @DisplayName("소셜 탈퇴 요청 실패로 인한 실패") + @Test + void unlink_failBy_(@WithMember AuthMember authMember) { + // Given + Member member = MemberFixture.member(); + + willReturn(member) + .given(memberService) + .findMemberToDelete(authMember.id()); + willThrow(BadRequestException.class) + .given(oAuth2AuthorizationServerRequestService) + .unlinkMemberRequest(eq(oauthConfig.provider().unlink()), eq(oauthConfig.client().adminKey()), any()); + + // When + Then + assertThatThrownBy(() -> authorizationService.unLinkMember(authMember)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorMessage.UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS.getMessage()); + } } diff --git a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java index 9ef0d24c..6399e91c 100644 --- a/src/test/java/com/moabam/api/presentation/RankingControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/RankingControllerTest.java @@ -1,12 +1,11 @@ package com.moabam.api.presentation; -import com.moabam.api.domain.member.Member; -import com.moabam.api.domain.member.repository.MemberRepository; -import com.moabam.api.dto.ranking.RankingInfo; -import com.moabam.api.dto.ranking.UpdateRanking; -import com.moabam.support.annotation.WithMember; -import com.moabam.support.common.WithoutFilterSupporter; -import com.moabam.support.fixture.MemberFixture; +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.ArrayList; +import java.util.List; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -19,13 +18,13 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; - -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.member.repository.MemberRepository; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.UpdateRanking; +import com.moabam.support.annotation.WithMember; +import com.moabam.support.common.WithoutFilterSupporter; +import com.moabam.support.fixture.MemberFixture; @Transactional @SpringBootTest @@ -33,57 +32,57 @@ @AutoConfigureRestDocs class RankingControllerTest extends WithoutFilterSupporter { - @Autowired - MockMvc mockMvc; - - @Autowired - MemberRepository memberRepository; - - @Autowired - RedisTemplate redisTemplate; - - @BeforeEach - void init() { - redisTemplate.delete("Ranking"); - } - - @DisplayName("") - @WithMember - @Test - void top_ranking() throws Exception { - // given - List members = new ArrayList<>(); - for (int i = 0; i < 20; i++) { - Member member = MemberFixture.member(String.valueOf(i + 1)); - members.add(member); - - RankingInfo rankingInfo = new RankingInfo((long) (i + 1), member.getNickname(), member.getProfileImage()); - redisTemplate.opsForZSet().add("Ranking", rankingInfo, i + 1); - } - memberRepository.saveAll(members); - - RankingInfo rankingInfo = new RankingInfo(21L, "Hello22", "123"); - redisTemplate.opsForZSet().add("Ranking", rankingInfo, 20); - RankingInfo rankingInfo2 = new RankingInfo(22L, "Hello23", "123"); - redisTemplate.opsForZSet().add("Ranking", rankingInfo2, 19); - - UpdateRanking myRanking = UpdateRanking.builder() - .score(1L) - .rankingInfo(RankingInfo.builder() - .nickname(members.get(0).getNickname()) - .memberId(members.get(0).getId()) - .image(members.get(0).getProfileImage()).build()) - .build(); - - // when - mockMvc.perform(MockMvcRequestBuilders.get("/rankings")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.topRankings", hasSize(10))) - .andExpect(jsonPath("$.myRanking.nickname", is(members.get(0).getNickname()))) - .andExpect(jsonPath("$.myRanking.rank", is(22))); - - // then - - } + @Autowired + MockMvc mockMvc; + + @Autowired + MemberRepository memberRepository; + + @Autowired + RedisTemplate redisTemplate; + + @BeforeEach + void init() { + redisTemplate.delete("Ranking"); + } + + @DisplayName("") + @WithMember + @Test + void top_ranking() throws Exception { + // given + List members = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + Member member = MemberFixture.member(String.valueOf(i + 1)); + members.add(member); + + RankingInfo rankingInfo = new RankingInfo((long)(i + 1), member.getNickname(), member.getProfileImage()); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, i + 1); + } + memberRepository.saveAll(members); + + RankingInfo rankingInfo = new RankingInfo(21L, "Hello22", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo, 20); + RankingInfo rankingInfo2 = new RankingInfo(22L, "Hello23", "123"); + redisTemplate.opsForZSet().add("Ranking", rankingInfo2, 19); + + UpdateRanking myRanking = UpdateRanking.builder() + .score(1L) + .rankingInfo(RankingInfo.builder() + .nickname(members.get(0).getNickname()) + .memberId(members.get(0).getId()) + .image(members.get(0).getProfileImage()).build()) + .build(); + + // when + mockMvc.perform(MockMvcRequestBuilders.get("/rankings")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.topRankings", hasSize(10))) + .andExpect(jsonPath("$.myRanking.nickname", is(members.get(0).getNickname()))) + .andExpect(jsonPath("$.myRanking.rank", is(22))); + + // then + + } } diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java index 011c3514..a58671cb 100644 --- a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -1,9 +1,10 @@ package com.moabam.support.common; -import com.moabam.api.domain.member.Role; -import com.moabam.global.auth.filter.CorsFilter; -import com.moabam.global.auth.handler.PathResolver; -import com.moabam.global.config.AllowOriginConfig; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; + +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.mock.mockito.MockBean; @@ -11,36 +12,36 @@ import org.springframework.context.annotation.Import; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; -import java.util.Optional; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.willReturn; +import com.moabam.api.domain.member.Role; +import com.moabam.global.auth.filter.CorsFilter; +import com.moabam.global.auth.handler.PathResolver; +import com.moabam.global.config.AllowOriginConfig; @Import(DataCleanResolver.class) @ExtendWith({FilterProcessExtension.class, ClearDataExtension.class}) public class WithoutFilterSupporter { - @MockBean - private PathResolver pathResolver; + @MockBean + private PathResolver pathResolver; - @MockBean - private DefaultHandlerExceptionResolver handlerExceptionResolver; + @MockBean + private DefaultHandlerExceptionResolver handlerExceptionResolver; - @SpyBean - private CorsFilter corsFilter; + @SpyBean + private CorsFilter corsFilter; - @MockBean - private AllowOriginConfig allowOriginConfig; + @MockBean + private AllowOriginConfig allowOriginConfig; - @BeforeEach - void setUpMock() { - willReturn("http://localhost:8080") - .given(corsFilter).secureMatch(any()); + @BeforeEach + void setUpMock() { + willReturn("http://localhost:8080") + .given(corsFilter).secureMatch(any()); - willReturn(Optional.of(PathResolver.Path.builder() - .uri("/") - .role(Role.USER) - .build())) - .given(pathResolver).permitPathMatch(any()); - } + willReturn(Optional.of(PathResolver.Path.builder() + .uri("/") + .role(Role.USER) + .build())) + .given(pathResolver).permitPathMatch(any()); + } } diff --git a/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java b/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java index 3c4d35d0..6f43c132 100644 --- a/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java +++ b/src/test/java/com/moabam/support/fixture/MemberInfoSearchFixture.java @@ -1,40 +1,40 @@ package com.moabam.support.fixture; -import com.moabam.api.domain.member.BadgeType; -import com.moabam.api.dto.member.MemberInfo; +import static com.moabam.global.common.util.BaseImageUrl.*; import java.util.List; -import static com.moabam.global.common.util.BaseImageUrl.*; +import com.moabam.api.domain.member.BadgeType; +import com.moabam.api.dto.member.MemberInfo; public class MemberInfoSearchFixture { - private static final String NICKNAME = "nickname"; - private static final String PROFILE_IMAGE = IMAGE_DOMAIN + MEMBER_PROFILE_URL; - private static final String INTRO = "intro"; - private static final long TOTAL_CERTIFY_COUNT = 15; - private static final String MORNING_EGG = IMAGE_DOMAIN + DEFAULT_MORNING_EGG_URL; - private static final String NIGHT_EGG = IMAGE_DOMAIN + DEFAULT_NIGHT_EGG_URL; - - public static List friendMemberInfo() { - return friendMemberInfo(TOTAL_CERTIFY_COUNT); - } - - public static List friendMemberInfo(long total) { - return List.of( - new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.BIRTH, - 0, 0, 0), - new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.LEVEL10, - 0, 0, 0) - ); - } - - public static List myInfo(String morningImage, String nightImage) { - return List.of( - new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, - BadgeType.BIRTH, 0, 0, 0), - new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, - BadgeType.LEVEL10, 0, 0, 0) - ); - } + private static final String NICKNAME = "nickname"; + private static final String PROFILE_IMAGE = IMAGE_DOMAIN + MEMBER_PROFILE_URL; + private static final String INTRO = "intro"; + private static final long TOTAL_CERTIFY_COUNT = 15; + private static final String MORNING_EGG = IMAGE_DOMAIN + DEFAULT_MORNING_EGG_URL; + private static final String NIGHT_EGG = IMAGE_DOMAIN + DEFAULT_NIGHT_EGG_URL; + + public static List friendMemberInfo() { + return friendMemberInfo(TOTAL_CERTIFY_COUNT); + } + + public static List friendMemberInfo(long total) { + return List.of( + new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.BIRTH, + 0, 0, 0), + new MemberInfo(NICKNAME, PROFILE_IMAGE, MORNING_EGG, NIGHT_EGG, INTRO, total, BadgeType.LEVEL10, + 0, 0, 0) + ); + } + + public static List myInfo(String morningImage, String nightImage) { + return List.of( + new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, + BadgeType.BIRTH, 0, 0, 0), + new MemberInfo(NICKNAME, PROFILE_IMAGE, morningImage, nightImage, INTRO, TOTAL_CERTIFY_COUNT, + BadgeType.LEVEL10, 0, 0, 0) + ); + } }