diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/AuthMemberServiceImpl.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/AuthMemberServiceImpl.java new file mode 100644 index 0000000..4be0bf8 --- /dev/null +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/AuthMemberServiceImpl.java @@ -0,0 +1,36 @@ +package com.wypl.wyplcore.auth; + +import java.util.Optional; + +import org.springframework.stereotype.Component; + +import com.wypl.googleoauthclient.GoogleOAuthClient; +import com.wypl.googleoauthclient.service.AuthMemberService; +import com.wypl.googleoauthclient.data.response.GoogleTokenValidationResponse; +import com.wypl.googleoauthclient.domain.AuthMember; +import com.wypl.jpamemberdomain.member.OauthProvider; +import com.wypl.jpamemberdomain.member.domain.SocialMember; +import com.wypl.jpamemberdomain.member.SocialMemberRepository; +import com.wypl.jpamemberdomain.member.exception.MemberErrorCode; +import com.wypl.jpamemberdomain.member.exception.MemberException; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class AuthMemberServiceImpl implements AuthMemberService { + private final GoogleOAuthClient googleOAuthClient; + private final SocialMemberRepository socialMemberRepository; + + @Override + public AuthMember getValidatedMemberId(String accessToken) { + GoogleTokenValidationResponse response = googleOAuthClient.validateToken(accessToken); + Optional optionalSocialMember + = socialMemberRepository.findByOauthProviderAndOauthId(OauthProvider.GOOGLE, response.userId()); + + if (optionalSocialMember.isEmpty()) { + throw new MemberException(MemberErrorCode.NO_SUCH_MEMBER); + } + return AuthMember.of(optionalSocialMember.get().getId(), accessToken); + } +} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/domain/AuthMember.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/domain/AuthMember.java deleted file mode 100644 index fa2466e..0000000 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/domain/AuthMember.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.wypl.wyplcore.auth.domain; - -import com.wypl.jpacalendardomain.calendar.data.ConvertibleScheduleInfo; - -public record AuthMember( - long id -) implements ConvertibleScheduleInfo { - - @Override - public Long getCreatorId() { - return id(); - } - -} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/utils/AuthenticatedArgumentResolver.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/utils/AuthenticatedArgumentResolver.java deleted file mode 100644 index 76d0025..0000000 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/utils/AuthenticatedArgumentResolver.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.wypl.wyplcore.auth.utils; - -import org.springframework.core.MethodParameter; -import org.springframework.stereotype.Component; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.method.support.ModelAndViewContainer; - -import com.wypl.wyplcore.auth.annotation.Authenticated; -import com.wypl.wyplcore.auth.domain.AuthMember; - -@Component -public class AuthenticatedArgumentResolver implements HandlerMethodArgumentResolver { - - @Override - public boolean supportsParameter(MethodParameter parameter) { - boolean hasParameterAnnotation = parameter.hasParameterAnnotation(Authenticated.class); - boolean assignableFrom = AuthMember.class.isAssignableFrom(parameter.getParameterType()); - return hasParameterAnnotation && assignableFrom; - } - - @Override - public AuthMember resolveArgument( - MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory - ) { - return null; - } -} diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/global/config/WebConfig.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/config/WebConfig.java similarity index 53% rename from application/wypl-core/src/main/java/com/wypl/wyplcore/global/config/WebConfig.java rename to application/wypl-core/src/main/java/com/wypl/wyplcore/config/WebConfig.java index d115f3b..c4ee0da 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/global/config/WebConfig.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/config/WebConfig.java @@ -1,4 +1,4 @@ -package com.wypl.wyplcore.global.config; +package com.wypl.wyplcore.config; import java.util.List; @@ -6,13 +6,18 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.wypl.wyplcore.auth.utils.AuthenticatedArgumentResolver; +import com.wypl.googleoauthclient.service.AuthMemberService; +import com.wypl.googleoauthclient.utils.AuthenticatedArgumentResolver; + +import lombok.RequiredArgsConstructor; @Configuration +@RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { + private final AuthMemberService authMemberService; @Override public void addArgumentResolvers(List resolvers) { - resolvers.add(new AuthenticatedArgumentResolver()); + resolvers.add(new AuthenticatedArgumentResolver(authMemberService)); } } diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/controller/ReviewController.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/controller/ReviewController.java index b5c172c..60fb56c 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/controller/ReviewController.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/controller/ReviewController.java @@ -14,8 +14,8 @@ import org.springframework.web.bind.annotation.RestController; import com.wypl.applicationcommon.WyplResponseEntity; -import com.wypl.wyplcore.auth.annotation.Authenticated; -import com.wypl.wyplcore.auth.domain.AuthMember; +import com.wypl.googleoauthclient.annotation.Authenticated; +import com.wypl.googleoauthclient.domain.AuthMember; import com.wypl.wyplcore.review.data.request.ReviewCreateRequest; import com.wypl.wyplcore.review.data.request.ReviewType; import com.wypl.wyplcore.review.data.request.ReviewUpdateRequest; diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/service/ReviewServiceImpl.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/service/ReviewServiceImpl.java index 4296c99..9de4c01 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/service/ReviewServiceImpl.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/service/ReviewServiceImpl.java @@ -11,7 +11,7 @@ import org.springframework.transaction.annotation.Transactional; import com.wypl.jpacalendardomain.calendar.domain.Schedule; -import com.wypl.jpamemberdomain.member.Member; +import com.wypl.jpamemberdomain.member.domain.Member; import com.wypl.jpamongoreviewdomain.review.domain.Review; import com.wypl.jpamongoreviewdomain.review.repository.ReviewRepository; import com.wypl.jpamongoreviewdomain.reviewcontents.domain.BlockType; diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/utils/ReviewUtils.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/utils/ReviewUtils.java index 0c1504c..9a01fef 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/review/utils/ReviewUtils.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/review/utils/ReviewUtils.java @@ -1,7 +1,7 @@ package com.wypl.wyplcore.review.utils; import com.wypl.common.exception.CallConstructorException; -import com.wypl.jpamemberdomain.member.Member; +import com.wypl.jpamemberdomain.member.domain.Member; import com.wypl.jpamongoreviewdomain.review.domain.Review; import com.wypl.jpamongoreviewdomain.review.repository.ReviewRepository; import com.wypl.wyplcore.review.exception.ReviewErrorCode; diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/controller/ScheduleController.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/controller/ScheduleController.java index 5df9d45..369edcf 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/controller/ScheduleController.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/controller/ScheduleController.java @@ -1,16 +1,18 @@ package com.wypl.wyplcore.schedule.controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + import com.wypl.applicationcommon.WyplResponseEntity; -import com.wypl.wyplcore.auth.annotation.Authenticated; -import com.wypl.wyplcore.auth.domain.AuthMember; +import com.wypl.googleoauthclient.annotation.Authenticated; +import com.wypl.googleoauthclient.domain.AuthMember; import com.wypl.wyplcore.schedule.data.request.ScheduleCreateRequest; import com.wypl.wyplcore.schedule.data.response.ScheduleInfoCreateResponse; import com.wypl.wyplcore.schedule.service.ScheduleService; + import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/ScheduleService.java b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/ScheduleService.java index 1de751a..35a669e 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/ScheduleService.java +++ b/application/wypl-core/src/main/java/com/wypl/wyplcore/schedule/service/ScheduleService.java @@ -1,5 +1,9 @@ package com.wypl.wyplcore.schedule.service; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.wypl.googleoauthclient.domain.AuthMember; import com.wypl.jpacalendardomain.calendar.domain.Calendar; import com.wypl.jpacalendardomain.calendar.domain.Schedule; import com.wypl.jpacalendardomain.calendar.domain.ScheduleInfo; @@ -7,13 +11,11 @@ import com.wypl.jpacalendardomain.calendar.mapper.ScheduleMapper; import com.wypl.jpacalendardomain.calendar.repository.ScheduleInfoRepository; import com.wypl.jpacalendardomain.calendar.repository.ScheduleRepository; -import com.wypl.jpamemberdomain.member.Member; -import com.wypl.wyplcore.auth.domain.AuthMember; +import com.wypl.jpamemberdomain.member.domain.Member; import com.wypl.wyplcore.schedule.data.request.ScheduleCreateRequest; import com.wypl.wyplcore.schedule.data.response.ScheduleInfoCreateResponse; + import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Service @@ -29,7 +31,7 @@ public ScheduleInfoCreateResponse createSchedule(AuthMember authMember, Schedule Calendar foundCalendar = null; // FIXME: scheduleInfoRequest의 calendarId로 찾는다. foundCalendar 엔티티 검증 필요. Member foundMember = null; // FIXME: member 엔티티 검증 필요. - ScheduleInfo scheduleInfo = ScheduleInfoMapper.toJpaScheduleInfo(foundCalendar, authMember); + ScheduleInfo scheduleInfo = ScheduleInfoMapper.toJpaScheduleInfo(foundCalendar, authMember.id()); Schedule schedule = ScheduleMapper.toJpaSchedule(scheduleCreateRequest, scheduleInfo); ScheduleInfo savedScheduleInfo = scheduleInfoRepository.save(scheduleInfo); diff --git a/application/wypl-core/src/test/java/com/wypl/wyplcore/auth/AuthMemberServiceImplTest.java b/application/wypl-core/src/test/java/com/wypl/wyplcore/auth/AuthMemberServiceImplTest.java new file mode 100644 index 0000000..8281245 --- /dev/null +++ b/application/wypl-core/src/test/java/com/wypl/wyplcore/auth/AuthMemberServiceImplTest.java @@ -0,0 +1,84 @@ +package com.wypl.wyplcore.auth; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.wypl.googleoauthclient.GoogleOAuthClient; +import com.wypl.googleoauthclient.data.response.GoogleTokenValidationResponse; +import com.wypl.googleoauthclient.domain.AuthMember; +import com.wypl.jpamemberdomain.member.OauthProvider; +import com.wypl.jpamemberdomain.member.SocialMemberRepository; +import com.wypl.jpamemberdomain.member.domain.SocialMember; +import com.wypl.jpamemberdomain.member.exception.MemberErrorCode; +import com.wypl.jpamemberdomain.member.exception.MemberException; + +@ExtendWith(MockitoExtension.class) +class AuthMemberServiceImplTest { + @InjectMocks + private AuthMemberServiceImpl authMemberService; + @Mock + private GoogleOAuthClient googleOAuthClient; + @Mock + private SocialMemberRepository socialMemberRepository; + + @DisplayName("현재의 토큰을 검증한다.") + @Nested + class GetValidatedMemberId { + + private static final String OAUTH_ID = "OAUTH_ID"; + + @BeforeEach + void setUp() { + given(googleOAuthClient.validateToken(anyString())) + .willReturn(new GoogleTokenValidationResponse(OAUTH_ID, "sjhjack@naver.com")); + } + + @DisplayName("AuthMember 정상적으로 생성된다.") + @Test + void success() { + // Given + String accessToken = "accessToken"; + // Todo : Fixture 만들어서 분리하기! + SocialMember mockSocialMember = SocialMember.builder() + .id(1L) + .oauthProvider(OauthProvider.GOOGLE) + .OauthId(OAUTH_ID) + .build(); + + given(socialMemberRepository.findByOauthProviderAndOauthId(any(OauthProvider.class), anyString())) + .willReturn(Optional.of(mockSocialMember)); + + // When + AuthMember authMember = authMemberService.getValidatedMemberId(accessToken); + + // Then + assertThat(authMember).isNotNull(); + assertThat(authMember.id()).isEqualTo(1L); + assertThat(authMember.accessToken()).isEqualTo(accessToken); + } + + @DisplayName("토큰으로 멤버 정보를 조회하지 못한다.") + @Test + void socialMemberEmptyTest() { + // Given + given(socialMemberRepository.findByOauthProviderAndOauthId(any(OauthProvider.class), anyString())) + .willReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> authMemberService.getValidatedMemberId(anyString())) + .isInstanceOf(MemberException.class) + .hasMessageContaining(MemberErrorCode.NO_SUCH_MEMBER.getMessage()); + } + } +} \ No newline at end of file diff --git a/client/google-oauth-client/build.gradle b/client/google-oauth-client/build.gradle index ce4166b..736a2ce 100644 --- a/client/google-oauth-client/build.gradle +++ b/client/google-oauth-client/build.gradle @@ -10,4 +10,48 @@ dependencies { implementation('org.springframework.boot:spring-boot-starter-web') implementation('org.springframework.boot:spring-boot-starter') -} \ No newline at end of file +} + +/* Jacoco Start */ +tasks.withType(JacocoReport).configureEach { + reports { + html.required.set(true) + xml.required.set(true) + html.outputLocation.set(file("reports/jacoco/index.xml")) + xml.outputLocation.set(file("reports/jacoco/test/jacocoTestReport.xml")) + } + + classDirectories.setFrom( + files(classDirectories.files.collect { + fileTree(it) { + exclude( + "**/annotation/**", + "**/data/**", + "**/exception/**", + ) + } + }) + ) +} + +tasks.jacocoTestCoverageVerification { + violationRules { + rule { + enabled = true + element = 'CLASS' + + limit { + counter = 'LINE' + value = 'COVEREDRATIO' + minimum = 0.80D + } + + excludes = [ + "**/annotation/**", + "**/data/**", + "**/exception/**", + ] + } + } +} +/* Jacoco End */ \ No newline at end of file diff --git a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthClient.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthClient.java index 62307e2..41ff231 100644 --- a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthClient.java +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthClient.java @@ -1,5 +1,8 @@ package com.wypl.googleoauthclient; +import java.util.HashMap; +import java.util.Map; + import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -11,9 +14,12 @@ import com.wypl.common.exception.GlobalErrorCode; import com.wypl.common.exception.WyplException; +import com.wypl.googleoauthclient.config.GoogleOAuthProperties; import com.wypl.googleoauthclient.data.response.GoogleTokenResponse; +import com.wypl.googleoauthclient.data.response.GoogleTokenValidationResponse; import com.wypl.googleoauthclient.exception.GoogleOAuthErrorCode; import com.wypl.googleoauthclient.exception.GoogleOAuthException; +import com.wypl.googleoauthclient.utils.GoogleOAuthParamFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -23,6 +29,7 @@ @RequiredArgsConstructor @Component public class GoogleOAuthClient { + private static final String VALIDATION_URI = "https://www.googleapis.com/oauth2/v1/tokeninfo"; private final GoogleOAuthProperties googleOAuthProperties; private final RestTemplate restTemplate; @@ -46,6 +53,19 @@ public GoogleTokenResponse fetchRefreshGoogleOAuthToken(String refreshToken) { return requestToken(params); } + public GoogleTokenValidationResponse validateToken(String accessToken) { + Map params = new HashMap<>(); + params.put("access_token", accessToken); + + try { + return restTemplate.getForObject(VALIDATION_URI + "?access_token={access_token}" + , GoogleTokenValidationResponse.class + , params); + } catch (HttpClientErrorException e) { + throw new GoogleOAuthException(GoogleOAuthErrorCode.INVALID_TOKEN); + } + } + private GoogleTokenResponse requestToken(MultiValueMap params) { HttpEntity> formEntity = getHttpEntity(params); diff --git a/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/annotation/Authenticated.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/annotation/Authenticated.java similarity index 84% rename from application/wypl-core/src/main/java/com/wypl/wyplcore/auth/annotation/Authenticated.java rename to client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/annotation/Authenticated.java index 3e4d12e..f5bd866 100644 --- a/application/wypl-core/src/main/java/com/wypl/wyplcore/auth/annotation/Authenticated.java +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/annotation/Authenticated.java @@ -1,4 +1,4 @@ -package com.wypl.wyplcore.auth.annotation; +package com.wypl.googleoauthclient.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthProperties.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/config/GoogleOAuthProperties.java similarity index 91% rename from client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthProperties.java rename to client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/config/GoogleOAuthProperties.java index 1d48d68..e252d77 100644 --- a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthProperties.java +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/config/GoogleOAuthProperties.java @@ -1,4 +1,4 @@ -package com.wypl.googleoauthclient; +package com.wypl.googleoauthclient.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; diff --git a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/data/response/GoogleTokenValidationResponse.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/data/response/GoogleTokenValidationResponse.java new file mode 100644 index 0000000..39f0a32 --- /dev/null +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/data/response/GoogleTokenValidationResponse.java @@ -0,0 +1,10 @@ +package com.wypl.googleoauthclient.data.response; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record GoogleTokenValidationResponse( + @JsonProperty("user_id") + String userId, + String email +) { +} diff --git a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/domain/AuthMember.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/domain/AuthMember.java new file mode 100644 index 0000000..b4cab41 --- /dev/null +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/domain/AuthMember.java @@ -0,0 +1,10 @@ +package com.wypl.googleoauthclient.domain; + +public record AuthMember( + long id, + String accessToken +) { + public static AuthMember of(long id, String accessToken) { + return new AuthMember(id, accessToken); + } +} \ No newline at end of file diff --git a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/exception/GoogleOAuthErrorCode.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/exception/GoogleOAuthErrorCode.java index 66a47e9..ee2ad78 100644 --- a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/exception/GoogleOAuthErrorCode.java +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/exception/GoogleOAuthErrorCode.java @@ -8,6 +8,8 @@ public enum GoogleOAuthErrorCode implements ServerErrorCode { BAD_REQUEST(400, "GOOGLE_OAUTH_001", "권한이 없습니다."), MALFORMED(400, "GOOGLE_OAUTH_002", "올바르지 않은 토큰입니다."), + INVALID_TOKEN(400, "GOOGLE_OAUTH_002", "올바르지 않은 값입니다."), + NOT_AUTHORIZATION_MEMBER(400, "GOOGLE_OAUTH_002", "인증되지 않은 회원입니다.") ; private final int statusCode; diff --git a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/service/AuthMemberService.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/service/AuthMemberService.java new file mode 100644 index 0000000..b553547 --- /dev/null +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/service/AuthMemberService.java @@ -0,0 +1,7 @@ +package com.wypl.googleoauthclient.service; + +import com.wypl.googleoauthclient.domain.AuthMember; + +public interface AuthMemberService { + AuthMember getValidatedMemberId(String accessToken); +} diff --git a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/utils/AuthenticatedArgumentResolver.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/utils/AuthenticatedArgumentResolver.java new file mode 100644 index 0000000..73872fc --- /dev/null +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/utils/AuthenticatedArgumentResolver.java @@ -0,0 +1,54 @@ +package com.wypl.googleoauthclient.utils; + +import java.util.Objects; + +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import com.wypl.googleoauthclient.annotation.Authenticated; +import com.wypl.googleoauthclient.service.AuthMemberService; +import com.wypl.googleoauthclient.domain.AuthMember; +import com.wypl.googleoauthclient.exception.GoogleOAuthErrorCode; +import com.wypl.googleoauthclient.exception.GoogleOAuthException; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class AuthenticatedArgumentResolver implements HandlerMethodArgumentResolver { + + private static final String AUTHORIZATION_HEADER = "Authorization"; + private final AuthMemberService authMemberService; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + boolean hasParameterAnnotation = parameter.hasParameterAnnotation(Authenticated.class); + boolean assignableFrom = AuthMember.class.isAssignableFrom(parameter.getParameterType()); + return hasParameterAnnotation && assignableFrom; + } + + @Override + public AuthMember resolveArgument( + MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory + ) { + HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); + validateAuthorization(request); + + String accessToken = (Objects.requireNonNull(request)).getHeader(AUTHORIZATION_HEADER); + return authMemberService.getValidatedMemberId(accessToken); + } + + private void validateAuthorization(HttpServletRequest request) { + if (Objects.requireNonNull(request).getHeader(AUTHORIZATION_HEADER) == null) { + throw new GoogleOAuthException(GoogleOAuthErrorCode.NOT_AUTHORIZATION_MEMBER); + } + } +} diff --git a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthParamFactory.java b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/utils/GoogleOAuthParamFactory.java similarity index 92% rename from client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthParamFactory.java rename to client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/utils/GoogleOAuthParamFactory.java index 31d3ff4..57fe2c9 100644 --- a/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/GoogleOAuthParamFactory.java +++ b/client/google-oauth-client/src/main/java/com/wypl/googleoauthclient/utils/GoogleOAuthParamFactory.java @@ -1,4 +1,4 @@ -package com.wypl.googleoauthclient; +package com.wypl.googleoauthclient.utils; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; @@ -6,6 +6,8 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import com.wypl.googleoauthclient.config.GoogleOAuthProperties; + public class GoogleOAuthParamFactory { private final MultiValueMap multiValueMap; diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/MemberCalendar.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/MemberCalendar.java index e783ccd..f9dea0d 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/MemberCalendar.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/domain/MemberCalendar.java @@ -5,7 +5,7 @@ import com.wypl.common.Color; import com.wypl.jpacalendardomain.calendar.data.InviteStatus; -import com.wypl.jpamemberdomain.member.Member; +import com.wypl.jpamemberdomain.member.domain.Member; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleInfoMapper.java b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleInfoMapper.java index 757d9fd..c1435bb 100644 --- a/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleInfoMapper.java +++ b/domain/jpa-calendar-domain/src/main/java/com/wypl/jpacalendardomain/calendar/mapper/ScheduleInfoMapper.java @@ -12,4 +12,11 @@ public static ScheduleInfo toJpaScheduleInfo(Calendar calendar, ConvertibleSched .calendar(calendar) .build(); } + + public static ScheduleInfo toJpaScheduleInfo(Calendar calendar, long id) { + return ScheduleInfo.builder() + .creatorId(id) + .calendar(calendar) + .build(); + } } diff --git a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/SocialMemberRepository.java b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/SocialMemberRepository.java new file mode 100644 index 0000000..d365216 --- /dev/null +++ b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/SocialMemberRepository.java @@ -0,0 +1,11 @@ +package com.wypl.jpamemberdomain.member; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.wypl.jpamemberdomain.member.domain.SocialMember; + +public interface SocialMemberRepository extends JpaRepository { + Optional findByOauthProviderAndOauthId(OauthProvider provider, String oauthId); +} diff --git a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/Member.java b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/domain/Member.java similarity index 92% rename from domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/Member.java rename to domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/domain/Member.java index 6617f04..841272d 100644 --- a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/Member.java +++ b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/domain/Member.java @@ -1,9 +1,10 @@ -package com.wypl.jpamemberdomain.member; +package com.wypl.jpamemberdomain.member.domain; import java.time.LocalDate; import com.wypl.common.Color; import com.wypl.jpacommon.JpaBaseEntity; +import com.wypl.jpamemberdomain.member.TimeZone; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/SocialMember.java b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/domain/SocialMember.java similarity index 90% rename from domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/SocialMember.java rename to domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/domain/SocialMember.java index 5ab1c0e..1b3ee90 100644 --- a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/SocialMember.java +++ b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/domain/SocialMember.java @@ -1,4 +1,6 @@ -package com.wypl.jpamemberdomain.member; +package com.wypl.jpamemberdomain.member.domain; + +import com.wypl.jpamemberdomain.member.OauthProvider; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberErrorCode.java b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberErrorCode.java new file mode 100644 index 0000000..b8da3e3 --- /dev/null +++ b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberErrorCode.java @@ -0,0 +1,21 @@ +package com.wypl.jpamemberdomain.member.exception; + +import com.wypl.common.exception.ServerErrorCode; + +import lombok.Getter; + +@Getter +public enum MemberErrorCode implements ServerErrorCode { + NO_SUCH_MEMBER(400, "MEMBER_001", "존재하지 않는 회원입니다.") + ; + + private final int statusCode; + private final String errorCode; + private final String message; + + MemberErrorCode(int statusCode, String errorCode, String message) { + this.statusCode = statusCode; + this.errorCode = errorCode; + this.message = message; + } +} diff --git a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberException.java b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberException.java new file mode 100644 index 0000000..c143120 --- /dev/null +++ b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/exception/MemberException.java @@ -0,0 +1,9 @@ +package com.wypl.jpamemberdomain.member.exception; + +import com.wypl.common.exception.WyplException; + +public class MemberException extends WyplException { + public MemberException(MemberErrorCode serverErrorCode) { + super(serverErrorCode); + } +} diff --git a/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/repository/MemberRepository.java b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/repository/MemberRepository.java new file mode 100644 index 0000000..d8f0e5e --- /dev/null +++ b/domain/jpa-member-domain/src/main/java/com/wypl/jpamemberdomain/member/repository/MemberRepository.java @@ -0,0 +1,8 @@ +package com.wypl.jpamemberdomain.member.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.wypl.jpamemberdomain.member.domain.Member; + +public interface MemberRepository extends JpaRepository { +} diff --git a/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/review/domain/Review.java b/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/review/domain/Review.java index d1e95fe..2c92548 100644 --- a/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/review/domain/Review.java +++ b/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/review/domain/Review.java @@ -4,7 +4,7 @@ import com.wypl.jpacalendardomain.calendar.domain.Schedule; import com.wypl.jpacommon.JpaBaseEntity; -import com.wypl.jpamemberdomain.member.Member; +import com.wypl.jpamemberdomain.member.domain.Member; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/review/repository/ReviewRepository.java b/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/review/repository/ReviewRepository.java index a9143af..5cd347c 100644 --- a/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/review/repository/ReviewRepository.java +++ b/domain/jpamongo-review-domain/src/main/java/com/wypl/jpamongoreviewdomain/review/repository/ReviewRepository.java @@ -9,7 +9,7 @@ import org.springframework.data.repository.query.Param; import com.wypl.jpacalendardomain.calendar.domain.Schedule; -import com.wypl.jpamemberdomain.member.Member; +import com.wypl.jpamemberdomain.member.domain.Member; import com.wypl.jpamongoreviewdomain.review.domain.Review; public interface ReviewRepository extends JpaRepository {