diff --git a/src/main/java/com/moabam/api/application/auth/mapper/PathMapper.java b/src/main/java/com/moabam/api/application/auth/mapper/PathMapper.java index 2aa36005..4ef9db71 100644 --- a/src/main/java/com/moabam/api/application/auth/mapper/PathMapper.java +++ b/src/main/java/com/moabam/api/application/auth/mapper/PathMapper.java @@ -9,7 +9,6 @@ import com.moabam.api.domain.member.Role; import com.moabam.global.auth.handler.PathResolver; -import jakarta.annotation.Nonnull; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -20,12 +19,12 @@ public static PathResolver.Path parsePath(String uri) { return parsePath(uri, null, null); } - public static PathResolver.Path parsePath(String uri, @Nonnull List params) { - if (!params.isEmpty() && params.get(0) instanceof Role) { - return parsePath(uri, (List)params, null); - } + public static PathResolver.Path pathWithRole(String uri, List params) { + return parsePath(uri, params, null); + } - return parsePath(uri, null, (List)params); + public static PathResolver.Path pathWithMethod(String uri, List params) { + return parsePath(uri, null, params); } private static PathResolver.Path parsePath(String uri, List roles, List methods) { diff --git a/src/main/java/com/moabam/api/presentation/MemberController.java b/src/main/java/com/moabam/api/presentation/MemberController.java index 87e6e0f4..a08590db 100644 --- a/src/main/java/com/moabam/api/presentation/MemberController.java +++ b/src/main/java/com/moabam/api/presentation/MemberController.java @@ -2,7 +2,8 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ModelAttribute; +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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @@ -31,21 +32,21 @@ public void socialLogin(HttpServletResponse httpServletResponse) { authorizationService.redirectToLoginPage(httpServletResponse); } - @GetMapping("/login/kakao/oauth") + @PostMapping("/login/kakao/oauth") @ResponseStatus(HttpStatus.OK) - public LoginResponse authorizationTokenIssue(@ModelAttribute AuthorizationCodeResponse authorizationCodeResponse, + public LoginResponse authorizationTokenIssue(@RequestBody AuthorizationCodeResponse authorizationCodeResponse, HttpServletResponse httpServletResponse) { AuthorizationTokenResponse tokenResponse = authorizationService.requestToken(authorizationCodeResponse); - AuthorizationTokenInfoResponse authorizationTokenInfoResponse = - authorizationService.requestTokenInfo(tokenResponse); + AuthorizationTokenInfoResponse authorizationTokenInfoResponse = authorizationService.requestTokenInfo( + tokenResponse); return authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); } @GetMapping("/logout") @ResponseStatus(HttpStatus.OK) - public void logout(@CurrentMember AuthorizationMember authorizationMember, - HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + public void logout(@CurrentMember AuthorizationMember authorizationMember, HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) { authorizationService.logout(authorizationMember, httpServletRequest, httpServletResponse); } } diff --git a/src/main/java/com/moabam/global/auth/filter/PathFilter.java b/src/main/java/com/moabam/global/auth/filter/PathFilter.java index 9d3c9bc1..8c7266ee 100644 --- a/src/main/java/com/moabam/global/auth/filter/PathFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/PathFilter.java @@ -9,6 +9,7 @@ import com.moabam.global.auth.handler.PathResolver; +import io.grpc.netty.shaded.io.netty.handler.codec.http.HttpMethod; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -34,6 +35,14 @@ public void doFilterInternal(HttpServletRequest request, HttpServletResponse res } }); + if (isOption(request.getMethod())) { + request.setAttribute("isPermit", true); + } + filterChain.doFilter(request, response); } + + public boolean isOption(String method) { + return HttpMethod.OPTIONS.name().equals(method); + } } diff --git a/src/main/java/com/moabam/global/common/util/CookieUtils.java b/src/main/java/com/moabam/global/common/util/CookieUtils.java new file mode 100644 index 00000000..892268da --- /dev/null +++ b/src/main/java/com/moabam/global/common/util/CookieUtils.java @@ -0,0 +1,37 @@ +package com.moabam.global.common.util; + +import jakarta.servlet.http.Cookie; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CookieUtils { + + public static Cookie tokenCookie(String name, String value, long expireTime) { + Cookie cookie = new Cookie(name, value); + cookie.setSecure(true); + cookie.setHttpOnly(true); + cookie.setPath("/"); + cookie.setMaxAge((int)expireTime); + cookie.setAttribute("SameSite", "Lax"); + + return cookie; + } + + public static Cookie typeCookie(String value, long expireTime) { + Cookie cookie = new Cookie("token_type", value); + cookie.setSecure(true); + cookie.setHttpOnly(true); + cookie.setPath("/"); + cookie.setMaxAge((int)expireTime); + cookie.setAttribute("SameSite", "Lax"); + + return cookie; + } + + public static Cookie deleteCookie(Cookie cookie) { + cookie.setMaxAge(0); + cookie.setPath("/"); + return cookie; + } +} diff --git a/src/main/java/com/moabam/global/config/WebConfig.java b/src/main/java/com/moabam/global/config/WebConfig.java index 63d3bfa6..b4371519 100644 --- a/src/main/java/com/moabam/global/config/WebConfig.java +++ b/src/main/java/com/moabam/global/config/WebConfig.java @@ -56,8 +56,8 @@ public PathResolver pathResolver() { PathMapper.parsePath("/webjars/*"), PathMapper.parsePath("/favicon/*"), PathMapper.parsePath("/*/icon-*"), - PathMapper.parsePath("/serverTime", List.of(HttpMethod.GET)) - )) + PathMapper.parsePath("/favicon.ico"), + PathMapper.pathWithMethod("/serverTime", List.of(HttpMethod.GET)))) .build(); return new PathResolver(path); diff --git a/src/main/resources/config b/src/main/resources/config index 2b721299..2a1a59a1 160000 --- a/src/main/resources/config +++ b/src/main/resources/config @@ -1 +1 @@ -Subproject commit 2b7212992e491c6d222f0b6a9b9289525be07008 +Subproject commit 2a1a59a16d8e868185c125a58aec0682f3c53f0d diff --git a/src/main/resources/static/docs/coupon.html b/src/main/resources/static/docs/coupon.html index e4adf5d6..1ac3df18 100644 --- a/src/main/resources/static/docs/coupon.html +++ b/src/main/resources/static/docs/coupon.html @@ -1,464 +1,2135 @@ - - - - -쿠폰(Coupon) - - + + + + + 쿠폰(Coupon) + +
-
-

쿠폰(Coupon)

-
-
-
-
쿠폰에 대해 생성/삭제/조회/발급/사용 기능을 제공합니다.
-
-
-
-
-

쿠폰 생성

-
-
-
관리자가 쿠폰을 생성합니다.
-
-
-

요청

-
-
+
+

쿠폰(Coupon)

+
+
+
+
쿠폰에 대해 생성/삭제/조회/발급/사용 기능을 제공합니다.
+
+
+
+
+

쿠폰 생성

+
+
+
관리자가 쿠폰을 생성합니다.
+
+
+

요청

+
+
POST /admins/coupons HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Content-Length: 192
@@ -473,11 +2144,11 @@ 

요청

"startAt" : "2023-01-01T00:00", "endAt" : "2023-02-01T00:00" }
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 409 Conflict
 Vary: Origin
 Vary: Access-Control-Request-Method
@@ -488,27 +2159,27 @@ 

응답

{ "message" : "쿠폰의 이름이 중복되었습니다." }
-
-
-
-
-
-

쿠폰 삭제

-
-
-
관리자가 쿠폰 ID와 일치하는 쿠폰을 삭제합니다.
-
-
-

요청

-
-
+
+
+
+
+
+

쿠폰 삭제

+
+
+
관리자가 쿠폰 ID와 일치하는 쿠폰을 삭제합니다.
+
+
+

요청

+
+
DELETE /admins/coupons/77777777777 HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 404 Not Found
 Vary: Origin
 Vary: Access-Control-Request-Method
@@ -519,28 +2190,28 @@ 

응답

{ "message" : "존재하지 않는 쿠폰입니다." }
-
-
-
-
-
-

특정 쿠폰 조회

-
-
-
관리자 혹은 사용자가 특정 ID와 일치하는 쿠폰을 조회합니다.
-
-
-
-

요청

-
-
+
+
+
+
+
+

특정 쿠폰 조회

+
+
+
관리자 혹은 사용자가 특정 ID와 일치하는 쿠폰을 조회합니다.
+
+
+
+

요청

+
+
GET /coupons/77777777777 HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 404 Not Found
 Vary: Origin
 Vary: Access-Control-Request-Method
@@ -551,22 +2222,22 @@ 

응답

{ "message" : "존재하지 않는 쿠폰입니다." }
-
-
-
-
-
-
-

상태에 따른 쿠폰들을 조회

-
-
-
관리자 혹은 사용자가 날짜 상태에 따라 쿠폰들을 조회합니다.
-
-
-
-

요청

-
-
+
+
+
+
+
+
+

상태에 따른 쿠폰들을 조회

+
+
+
관리자 혹은 사용자가 날짜 상태에 따라 쿠폰들을 조회합니다.
+
+
+
+

요청

+
+
POST /coupons/search HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Content-Length: 84
@@ -577,11 +2248,11 @@ 

요청

"couponNotStarted" : false, "couponEnded" : false }
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 200 OK
 Vary: Origin
 Vary: Access-Control-Request-Method
@@ -590,33 +2261,33 @@ 

응답

Content-Length: 3 [ ]
-
-
-
-
-
-
-

특정 쿠폰에 대해 발급

-
-
-
사용자가 발급 가능한 쿠폰을 선착순으로 발급 받습니다.
-
-
-
-

요청

-
-
+
+
+
+
+
+
+

특정 쿠폰에 대해 발급

+
+
+
사용자가 발급 가능한 쿠폰을 선착순으로 발급 받습니다.
+
+
+
+

요청

+
+
POST /coupons HTTP/1.1
 Content-Type: application/x-www-form-urlencoded
 Host: localhost:8080
 Content-Length: 32
 
 couponName=Not+found+coupon+name
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 404 Not Found
 Vary: Origin
 Vary: Access-Control-Request-Method
@@ -627,36 +2298,36 @@ 

응답

{ "message" : "존재하지 않는 쿠폰입니다." }
-
-
-
-
-
-
-

특정 사용자의 쿠폰 보관함을 조회

-
-
-
사용자가 자신의 보관함에 있는 쿠폰들을 조회합니다.
-
-
-
-
-
-

쿠폰 사용 (진행 중)

-
-
-
사용자가 자신의 보관함에 있는 쿠폰들을 사용합니다.
-
-
-
-
-
+
+
+
+
+
+
+

특정 사용자의 쿠폰 보관함을 조회

+
+
+
사용자가 자신의 보관함에 있는 쿠폰들을 조회합니다.
+
+
+
+
+
+

쿠폰 사용 (진행 중)

+
+
+
사용자가 자신의 보관함에 있는 쿠폰들을 사용합니다.
+
+
+
+
+
- \ No newline at end of file + diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 62f80fd6..ded2abe8 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -1,630 +1,2322 @@ - - - - -MOABAM API 문서 - - - - + + + + + MOABAM API 문서 + + + +
-
-

1. 개요

-
-
-

이 API 문서는 'MOABAM' 프로젝트의 산출물입니다.

-
-
-

1.1. API 서버 경로

- ----- - - - - - - - - - - - - - - - - - -

환경

DNS

비고

개발(dev)

dev-api.moabam.com

운영(prod)

api.moabam.com

-
- - - - - -
- - -
-

해당 프로젝트 API 문서는 [특이사항]입니다.

-
-
-
-
- - - - - -
- - -
-

해당 프로젝트 API 문서는 [주의사항]입니다.

-
-
-
-
-
-

1.2. 응답형식

-
-

프로젝트는 다음과 같은 응답형식을 제공합니다.

-
-
-

1.2.1. 정상(2XX)

- ---- - - - - - - - - - + + +
응답데이터가 없는 경우응답데이터가 있는 경우
-
+
+

1. 개요

+
+
+

이 API 문서는 'MOABAM' 프로젝트의 산출물입니다.

+
+
+

1.1. API 서버 경로

+ + + + + + + + + + + + + + + + + + + + + + + +

환경

DNS

비고

개발(dev)

dev-api.moabam.com +

운영(prod)

api.moabam.com

+
+ + + + + +
+ + +
+

해당 프로젝트 API 문서는 [특이사항]입니다.

+
+
+
+
+ + + + + +
+ + +
+

해당 프로젝트 API 문서는 [주의사항]입니다.

+
+
+
+
+
+

1.2. 응답형식

+
+

프로젝트는 다음과 같은 응답형식을 제공합니다.

+
+
+

1.2.1. 정상(2XX)

+ + + + + + + + + + + + + + - + - - -
응답데이터가 없는 경우응답데이터가 있는 경우
+
+
+
{
 
 }
-
-
-
+
+
+
+
+
+
+
{
   "name": "Hong-Dosan"
 }
-
-
-
-
-

1.2.2. 상태코드(HttpStatus)

-
-

응답시 다음과 같은 응답상태 헤더, 응답코드 및 응답메시지를 제공합니다.

-
- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HttpStatus설명

OK(200)

정상 응답

CREATED(201)

새로운 리소스 생성

BAD_REQUEST(400)

요청값 누락, 잘못된 기입

UNAUTHORIZED(401)

비인증 요청

NOT_FOUND(404)

요청값 누락, 잘못된 기입, 비인가 접속 등

CONFLICT(409)

요청값 중복

INTERNAL_SERVER_ERROR(500)

알 수 없는 서버 에러가 발생했습니다. 관리자에게 문의하세요.

-
-
-
-
+
+
+
+
+
+
+

1.2.2. 상태코드(HttpStatus)

+
+

응답시 다음과 같은 응답상태 헤더, 응답코드 및 응답메시지를 제공합니다.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HttpStatus설명

OK(200)

+

정상 응답

+ CREATED(201)

새로운 리소스 생성

+ BAD_REQUEST(400)

요청값 누락, 잘못된 기입

+ UNAUTHORIZED(401)

비인증 요청

+ NOT_FOUND(404)

요청값 누락, 잘못된 기입, 비인가 접속 + 등

+ CONFLICT(409)

요청값 중복

INTERNAL_SERVER_ERROR(500) +

알 수 없는 서버 에러가 발생했습니다. + 관리자에게 문의하세요.

+
+
+
+
- \ No newline at end of file + diff --git a/src/main/resources/static/docs/notification.html b/src/main/resources/static/docs/notification.html index 206bb94a..1f39f893 100644 --- a/src/main/resources/static/docs/notification.html +++ b/src/main/resources/static/docs/notification.html @@ -1,473 +1,2144 @@ - - - - -알림(Notification) - - + + + + + 알림(Notification) + +
-
-

알림(Notification)

-
-
-
-
콕 찌르기 알림 기능을 제공합니다.
-
-
-
-

콕 찌르기 알림

-
-
+
+

알림(Notification)

+
+
+
+
콕 찌르기 알림 기능을 제공합니다.
+
+
+
+

콕 찌르기 알림

+
+
1) 특정 방의 사용자가 다른 사용자를 콕 찌릅니다.
 2) 서버에서 콕 찌를 대상의 FCM Token 여부를 검증합니다.
 3) Firebase 서버에 FCM Push Messaing 알림을 비동기로 요청합니다.
 4) Firebase 서버에서 FCM Token으로 식별된 기기에 알림을 보냅니다.
-
-
-

요청

-
-
+
+
+

요청

+
+
GET /notifications/rooms/3/members/3 HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 409 Conflict
 Vary: Origin
 Vary: Access-Control-Request-Method
@@ -478,17 +2149,17 @@ 

응답

{ "message" : "이미 콕 알림을 보낸 대상입니다." }
-
-
-
-
-
+
+
+
+
+
- \ No newline at end of file + diff --git a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java index db6b248e..0ac1187b 100644 --- a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java @@ -112,6 +112,7 @@ void social_login_signUp_request_success() throws Exception { contentParams.add("client_secret", oAuthConfig.client().clientSecret()); AuthorizationCodeResponse authorizationCodeResponse = AuthorizationResponseFixture.successCodeResponse(); + String requestBody = objectMapper.writeValueAsString(authorizationCodeResponse); AuthorizationTokenResponse authorizationTokenResponse = AuthorizationResponseFixture.authorizationTokenResponse(); String response = objectMapper.writeValueAsString(authorizationTokenResponse); @@ -132,8 +133,9 @@ void social_login_signUp_request_success() throws Exception { .andExpect(MockRestRequestMatchers.header("Authorization", "Bearer accessToken")) .andRespond(withSuccess(tokenInfoResponse, MediaType.APPLICATION_JSON)); - mockMvc.perform(get("/members/login/kakao/oauth") - .flashAttr("authorizationCodeResponse", authorizationCodeResponse)) + mockMvc.perform(post("/members/login/kakao/oauth") + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody)) .andExpectAll( status().isOk(), MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON), @@ -161,6 +163,7 @@ void authorization_token_request_fail(int code) throws Exception { contentParams.add("client_secret", oAuthConfig.client().clientSecret()); AuthorizationCodeResponse authorizationCodeResponse = AuthorizationResponseFixture.successCodeResponse(); + String requestBody = objectMapper.writeValueAsString(authorizationCodeResponse); // expected mockRestServiceServer.expect(requestTo(oAuthConfig.provider().tokenUri())) @@ -169,8 +172,9 @@ void authorization_token_request_fail(int code) throws Exception { .andExpect(method(HttpMethod.POST)) .andRespond(withStatus(HttpStatusCode.valueOf(code))); - mockMvc.perform(get("/members/login/kakao/oauth") - .flashAttr("authorizationCodeResponse", authorizationCodeResponse)) + mockMvc.perform(post("/members/login/kakao/oauth") + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody)) .andExpect(status().isBadRequest()); } @@ -191,7 +195,7 @@ void token_info_response_fail(int code) throws Exception { .andExpect(MockRestRequestMatchers.header("Authorization", "Bearer accessToken")) .andRespond(withStatus(HttpStatusCode.valueOf(code))); - mockMvc.perform(get("/members/login/kakao/oauth") + mockMvc.perform(post("/members/login/kakao/oauth") .flashAttr("authorizationCodeResponse", authorizationCodeResponse)) .andExpect(status().isBadRequest()); } diff --git a/src/test/java/com/moabam/global/common/util/CookieMakeTest.java b/src/test/java/com/moabam/global/common/util/CookieMakeTest.java index 00addf88..63cd7a79 100644 --- a/src/test/java/com/moabam/global/common/util/CookieMakeTest.java +++ b/src/test/java/com/moabam/global/common/util/CookieMakeTest.java @@ -3,55 +3,55 @@ import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; - -import com.moabam.global.common.util.cookie.CookieDevUtils; -import com.moabam.global.common.util.cookie.CookieProdUtils; -import com.moabam.global.common.util.cookie.CookieUtils; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import jakarta.servlet.http.Cookie; +@ExtendWith(MockitoExtension.class) class CookieMakeTest { - CookieUtils cookieDevUtils; - CookieUtils cookieProdUtils; - - @BeforeEach - void setUp() { - cookieDevUtils = new CookieDevUtils(); - cookieProdUtils = new CookieProdUtils(); - } - @DisplayName("prod환경에서 cookie 생성 테스트") @Test - void prodUtilsTest() { + void create_test() { // Given - Cookie cookie = cookieProdUtils.tokenCookie("access_token", "value", 10000); + Cookie cookie = CookieUtils.tokenCookie("access_token", "value", 10000); // When + Then assertAll( () -> assertThat(cookie.getSecure()).isTrue(), () -> assertThat(cookie.getSecure()).isTrue(), () -> assertThat(cookie.getPath()).isEqualTo("/"), - () -> assertThat(cookie.getMaxAge()).isEqualTo(10000) + () -> assertThat(cookie.getMaxAge()).isEqualTo(10000), + () -> assertThat(cookie.getAttribute("SameSite")).isEqualTo("Lax") ); } - @DisplayName("dev환경에서 cookie 생성 테스트") + @DisplayName("") @Test - void devUtilsTest() { - // Given - Cookie cookie = cookieDevUtils.tokenCookie("access_token", "value", 10000); + void delete_test() { + // given + Cookie cookie = CookieUtils.tokenCookie("access_token", "value", 10000); - // When + Then + // when + Cookie deletedCookie = CookieUtils.deleteCookie(cookie); + + // then assertAll( - () -> assertThat(cookie.getSecure()).isTrue(), - () -> assertThat(cookie.getSecure()).isTrue(), - () -> assertThat(cookie.getPath()).isEqualTo("/"), - () -> assertThat(cookie.getMaxAge()).isEqualTo(10000), - () -> assertThat(cookie.getAttribute("SameSite")).isEqualTo("None") + () -> assertThat(deletedCookie.getMaxAge()).isZero(), + () -> assertThat(deletedCookie.getPath()).isEqualTo("/") ); } + + @DisplayName("") + @Test + void typeCookie_create_test() { + // Given + When + Cookie cookie = CookieUtils.typeCookie("Bearer", 10000); + + // then + assertThat(cookie.getName()).isEqualTo("token_type"); + } } diff --git a/src/test/java/com/moabam/global/filter/PathFilterTest.java b/src/test/java/com/moabam/global/filter/PathFilterTest.java index 33d24808..9c172127 100644 --- a/src/test/java/com/moabam/global/filter/PathFilterTest.java +++ b/src/test/java/com/moabam/global/filter/PathFilterTest.java @@ -36,7 +36,7 @@ class PathFilterTest { @DisplayName("Authentication을 넘기기 위한 필터 설정") @ParameterizedTest @ValueSource(strings = { - "GET", "POST", "PATCH", "DELETE" + "GET", "POST", "PATCH", "DELETE", "OPTIONS" }) void filter_pass_for_authentication(String method) throws ServletException, IOException { // given