From 95948a376966de1f6688e815652fab5e9f79df6e Mon Sep 17 00:00:00 2001 From: leegwichan Date: Tue, 31 Dec 2024 16:48:20 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20API=20=EA=B5=AC=ED=98=84=20#451?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ddangkong/controller/admin/Admin.java | 13 +++++ .../controller/admin/AdminController.java | 47 +++++++++++++++++++ .../admin/dto/AdminLoginRequest.java | 4 ++ .../controller/exception/ErrorResponse.java | 5 ++ .../exception/GlobalExceptionHandler.java | 10 ++++ .../ddangkong/exception/ClientErrorCode.java | 4 ++ .../exception/UnauthorizedException.java | 10 ++++ .../admin/NotExistAdminSessionException.java | 17 +++++++ .../admin/NotMatchAdminPasswordException.java | 17 +++++++ .../ddangkong/service/admin/AdminService.java | 21 +++++++++ .../src/main/resources/application-dev.yml | 4 ++ .../src/main/resources/application-prod.yml | 4 ++ .../controller/admin/AdminControllerTest.java | 43 +++++++++++++++++ .../admin/BaseAdminControllerTest.java | 29 ++++++++++++ .../service/admin/AdminServiceTest.java | 41 ++++++++++++++++ backend/src/test/resources/application.yml | 4 ++ 16 files changed, 273 insertions(+) create mode 100644 backend/src/main/java/ddangkong/controller/admin/Admin.java create mode 100644 backend/src/main/java/ddangkong/controller/admin/AdminController.java create mode 100644 backend/src/main/java/ddangkong/controller/admin/dto/AdminLoginRequest.java create mode 100644 backend/src/main/java/ddangkong/exception/UnauthorizedException.java create mode 100644 backend/src/main/java/ddangkong/exception/admin/NotExistAdminSessionException.java create mode 100644 backend/src/main/java/ddangkong/exception/admin/NotMatchAdminPasswordException.java create mode 100644 backend/src/main/java/ddangkong/service/admin/AdminService.java create mode 100644 backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java create mode 100644 backend/src/test/java/ddangkong/controller/admin/BaseAdminControllerTest.java create mode 100644 backend/src/test/java/ddangkong/service/admin/AdminServiceTest.java diff --git a/backend/src/main/java/ddangkong/controller/admin/Admin.java b/backend/src/main/java/ddangkong/controller/admin/Admin.java new file mode 100644 index 000000000..b97f5fd39 --- /dev/null +++ b/backend/src/main/java/ddangkong/controller/admin/Admin.java @@ -0,0 +1,13 @@ +package ddangkong.controller.admin; + +import lombok.Getter; + +@Getter +public class Admin { + + private final String nickname; + + public Admin(String nickname) { + this.nickname = nickname; + } +} diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminController.java b/backend/src/main/java/ddangkong/controller/admin/AdminController.java new file mode 100644 index 000000000..cbda61c6f --- /dev/null +++ b/backend/src/main/java/ddangkong/controller/admin/AdminController.java @@ -0,0 +1,47 @@ +package ddangkong.controller.admin; + +import ddangkong.controller.admin.dto.AdminLoginRequest; +import ddangkong.service.admin.AdminService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +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; + +@Slf4j +@RestController +@RequestMapping("/api") +public class AdminController { + + private final AdminService adminService; + + private final String sessionKey; + + public AdminController(AdminService adminService, @Value("${admin.session-key}") String sessionKey) { + this.adminService = adminService; + this.sessionKey = sessionKey; + } + + @PostMapping("/admin/login") + public void login(@RequestBody AdminLoginRequest loginRequest, HttpServletRequest request) { + adminService.validatePassword(loginRequest.password()); + + Admin admin = new Admin(loginRequest.nickname()); + HttpSession session = request.getSession(); + session.setAttribute(sessionKey, admin); + log.info("어드민이 로그인 했습니다. nickname = {}, session = {}", loginRequest.nickname(), session.getId()); + } + + @PostMapping("/admin/logout") + public void logout(HttpServletRequest request) { + HttpSession session = request.getSession(false); + if (session != null) { + Admin admin = (Admin) session.getAttribute(sessionKey); + session.invalidate(); + log.info("어드민이 로그아웃 했습니다. nickname = {}, session = {}", admin.getNickname(), session.getId()); + } + } +} diff --git a/backend/src/main/java/ddangkong/controller/admin/dto/AdminLoginRequest.java b/backend/src/main/java/ddangkong/controller/admin/dto/AdminLoginRequest.java new file mode 100644 index 000000000..56eebc416 --- /dev/null +++ b/backend/src/main/java/ddangkong/controller/admin/dto/AdminLoginRequest.java @@ -0,0 +1,4 @@ +package ddangkong.controller.admin.dto; + +public record AdminLoginRequest(String nickname, String password) { +} diff --git a/backend/src/main/java/ddangkong/controller/exception/ErrorResponse.java b/backend/src/main/java/ddangkong/controller/exception/ErrorResponse.java index 5b553869a..865fa5194 100644 --- a/backend/src/main/java/ddangkong/controller/exception/ErrorResponse.java +++ b/backend/src/main/java/ddangkong/controller/exception/ErrorResponse.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import ddangkong.exception.BadRequestException; import ddangkong.exception.ClientErrorCode; +import ddangkong.exception.UnauthorizedException; import jakarta.validation.ConstraintViolation; import jakarta.validation.Path; import java.util.List; @@ -24,6 +25,10 @@ public ErrorResponse(T e) { this(e.getErrorCode(), e.getMessage(), null, null); } + public ErrorResponse(T e) { + this(e.getErrorCode(), e.getMessage(), null, null); + } + public ErrorResponse(ClientErrorCode errorCode) { this(errorCode, null, null); } diff --git a/backend/src/main/java/ddangkong/controller/exception/GlobalExceptionHandler.java b/backend/src/main/java/ddangkong/controller/exception/GlobalExceptionHandler.java index 04fe40b57..1e7434957 100644 --- a/backend/src/main/java/ddangkong/controller/exception/GlobalExceptionHandler.java +++ b/backend/src/main/java/ddangkong/controller/exception/GlobalExceptionHandler.java @@ -3,6 +3,7 @@ import ddangkong.exception.BadRequestException; import ddangkong.exception.ClientErrorCode; import ddangkong.exception.InternalServerException; +import ddangkong.exception.UnauthorizedException; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.connector.ClientAbortException; @@ -77,6 +78,15 @@ public ErrorResponse handleNoResourceFoundException(NoResourceFoundException e) return new ErrorResponse(ClientErrorCode.NO_RESOURCE_FOUND); } + @ExceptionHandler + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public ErrorResponse handleUnauthorizedException(UnauthorizedException e) { + log.warn(e.getMessage()); + + return new ErrorResponse(e); + } + + @ExceptionHandler @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) public ErrorResponse handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { diff --git a/backend/src/main/java/ddangkong/exception/ClientErrorCode.java b/backend/src/main/java/ddangkong/exception/ClientErrorCode.java index 8f5f49bfb..49958fc91 100644 --- a/backend/src/main/java/ddangkong/exception/ClientErrorCode.java +++ b/backend/src/main/java/ddangkong/exception/ClientErrorCode.java @@ -53,6 +53,10 @@ public enum ClientErrorCode { NOT_FOUND_COOKIE("일치하는 쿠키가 없습니다."), INVALID_COOKIE("유효하지 않는 쿠키입니다."), + // ADMIN TODO Enum 분리, Docs 분리 + NOT_MATCH_ADMIN_PASSWORD("어드민 비밀번호가 일치하지 않습니다."), + NOT_EXIST_ADMIN_SESSION("어드민으로 로그인하지 않았습니다."), + // Common FIELD_ERROR("입력이 잘못되었습니다."), URL_PARAMETER_ERROR("입력이 잘못되었습니다."), diff --git a/backend/src/main/java/ddangkong/exception/UnauthorizedException.java b/backend/src/main/java/ddangkong/exception/UnauthorizedException.java new file mode 100644 index 000000000..f8ca5ccc1 --- /dev/null +++ b/backend/src/main/java/ddangkong/exception/UnauthorizedException.java @@ -0,0 +1,10 @@ +package ddangkong.exception; + +public abstract class UnauthorizedException extends RuntimeException { + + public UnauthorizedException(String message) { + super(message); + } + + public abstract String getErrorCode(); +} diff --git a/backend/src/main/java/ddangkong/exception/admin/NotExistAdminSessionException.java b/backend/src/main/java/ddangkong/exception/admin/NotExistAdminSessionException.java new file mode 100644 index 000000000..862e6b04c --- /dev/null +++ b/backend/src/main/java/ddangkong/exception/admin/NotExistAdminSessionException.java @@ -0,0 +1,17 @@ +package ddangkong.exception.admin; + +import static ddangkong.exception.ClientErrorCode.NOT_EXIST_ADMIN_SESSION; + +import ddangkong.exception.UnauthorizedException; + +public class NotExistAdminSessionException extends UnauthorizedException { + + public NotExistAdminSessionException() { + super(NOT_EXIST_ADMIN_SESSION.getMessage()); + } + + @Override + public String getErrorCode() { + return NOT_EXIST_ADMIN_SESSION.name(); + } +} diff --git a/backend/src/main/java/ddangkong/exception/admin/NotMatchAdminPasswordException.java b/backend/src/main/java/ddangkong/exception/admin/NotMatchAdminPasswordException.java new file mode 100644 index 000000000..2c985229b --- /dev/null +++ b/backend/src/main/java/ddangkong/exception/admin/NotMatchAdminPasswordException.java @@ -0,0 +1,17 @@ +package ddangkong.exception.admin; + +import static ddangkong.exception.ClientErrorCode.NOT_MATCH_ADMIN_PASSWORD; + +import ddangkong.exception.BadRequestException; + +public class NotMatchAdminPasswordException extends BadRequestException { + + public NotMatchAdminPasswordException() { + super(NOT_MATCH_ADMIN_PASSWORD.getMessage()); + } + + @Override + public String getErrorCode() { + return NOT_MATCH_ADMIN_PASSWORD.name(); + } +} diff --git a/backend/src/main/java/ddangkong/service/admin/AdminService.java b/backend/src/main/java/ddangkong/service/admin/AdminService.java new file mode 100644 index 000000000..aa369e315 --- /dev/null +++ b/backend/src/main/java/ddangkong/service/admin/AdminService.java @@ -0,0 +1,21 @@ +package ddangkong.service.admin; + +import ddangkong.exception.admin.NotMatchAdminPasswordException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class AdminService { + + private final String adminPassword; + + public AdminService(@Value("${admin.password}") String adminPassword) { + this.adminPassword = adminPassword; + } + + public void validatePassword(String password) { + if (!adminPassword.equals(password)) { + throw new NotMatchAdminPasswordException(); + } + } +} diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index fb150d2e2..e7853e0f6 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -28,6 +28,10 @@ encrypt: secret-key: ${secret.encrypt.secret-key} algorithm: ${secret.encrypt.algorithm} +admin: + session-key : admin + password : ${secret.admin.password} + logging: config: classpath:logback-dev.xml location : ${secret.application.log.location} diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index cb8e17aa6..d7369aa59 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -30,6 +30,10 @@ encrypt: secret-key: ${secret.encrypt.secret-key} algorithm: ${secret.encrypt.algorithm} +admin: + session-key : admin + password : ${secret.admin.password} + logging: config: classpath:logback-prod.xml discord: diff --git a/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java b/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java new file mode 100644 index 000000000..6a9f80a24 --- /dev/null +++ b/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java @@ -0,0 +1,43 @@ +package ddangkong.controller.admin; + +import ddangkong.controller.admin.dto.AdminLoginRequest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class AdminControllerTest extends BaseAdminControllerTest { + + @Nested + class 어드민_로그인 { + + @Test + void 비밀번호가_일치하면_로그인_할_수_있다() { + // given + AdminLoginRequest body = new AdminLoginRequest("이든", adminPassword); + + // when & then + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(body) + .when().post("/api/admin/login") + .then().log().all() + .statusCode(200) + .cookie("JSESSIONID"); + } + } + + @Nested + class 어드민_로그아웃 { + + @Test + void 이미_로그인_한_유저는_로그아웃_할_수_있다() { + // when & then + RestAssured.given().log().all() + .cookie("JSESSIONID", sessionId) + .when().post("/api/admin/logout") + .then().log().all() + .statusCode(200); + } + } +} diff --git a/backend/src/test/java/ddangkong/controller/admin/BaseAdminControllerTest.java b/backend/src/test/java/ddangkong/controller/admin/BaseAdminControllerTest.java new file mode 100644 index 000000000..9dfbd1a2b --- /dev/null +++ b/backend/src/test/java/ddangkong/controller/admin/BaseAdminControllerTest.java @@ -0,0 +1,29 @@ +package ddangkong.controller.admin; + +import ddangkong.controller.BaseControllerTest; +import ddangkong.controller.admin.dto.AdminLoginRequest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Value; + +public abstract class BaseAdminControllerTest extends BaseControllerTest { + + @Value("${admin.password}") + protected String adminPassword; + + protected String sessionId; + + @BeforeEach + void 세션_발급() { + AdminLoginRequest body = new AdminLoginRequest("이든", adminPassword); + sessionId = RestAssured.given() + .contentType(ContentType.JSON) + .body(body) + .when().post("/api/admin/login") + .then() + .statusCode(200) + .extract() + .cookie("JSESSIONID"); + } +} diff --git a/backend/src/test/java/ddangkong/service/admin/AdminServiceTest.java b/backend/src/test/java/ddangkong/service/admin/AdminServiceTest.java new file mode 100644 index 000000000..9791a2670 --- /dev/null +++ b/backend/src/test/java/ddangkong/service/admin/AdminServiceTest.java @@ -0,0 +1,41 @@ +package ddangkong.service.admin; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import ddangkong.exception.admin.NotMatchAdminPasswordException; +import ddangkong.facade.BaseServiceTest; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +class AdminServiceTest extends BaseServiceTest { + + @Value("${admin.password}") + private String adminPassword; + + @Autowired + private AdminService adminService; + + @Nested + class 어드민_비밀번호_검증 { + + @Test + void 어드민_비밀번호가_일치하면_아무_일도_일어나지_않는다() { + // when & then + assertThatCode(() -> adminService.validatePassword(adminPassword)) + .doesNotThrowAnyException(); + } + + @Test + void 어드민_비밀번호가_다르면_예외가_발생한다() { + // given + String notMatchPassword = "no-password"; + + // when & then + assertThatThrownBy(() -> adminService.validatePassword(notMatchPassword)) + .isInstanceOf(NotMatchAdminPasswordException.class); + } + } +} diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml index 05895ee4c..a14bc8cee 100644 --- a/backend/src/test/resources/application.yml +++ b/backend/src/test/resources/application.yml @@ -20,6 +20,10 @@ encrypt: secret-key: 1234567890123456 algorithm: AES +admin: + session-key : admin + password : 1234 + logging: level: org: From 38bbbb5add491abfbcd7bcb148ad55c79ea152a3 Mon Sep 17 00:00:00 2001 From: leegwichan Date: Tue, 31 Dec 2024 16:49:44 +0900 Subject: [PATCH 2/7] =?UTF-8?q?docs:=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20API=20=EB=AC=B8=EC=84=9C=ED=99=94=20#451?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/docs/asciidoc/admin.adoc | 39 +++++++++++ backend/src/docs/asciidoc/index.adoc | 1 + .../admin/AdminDocumentationTest.java | 67 +++++++++++++++++++ .../admin/BaseAdminDocumentationTest.java | 21 ++++++ 4 files changed, 128 insertions(+) create mode 100644 backend/src/docs/asciidoc/admin.adoc create mode 100644 backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java create mode 100644 backend/src/test/java/ddangkong/documentation/admin/BaseAdminDocumentationTest.java diff --git a/backend/src/docs/asciidoc/admin.adoc b/backend/src/docs/asciidoc/admin.adoc new file mode 100644 index 000000000..74e881f41 --- /dev/null +++ b/backend/src/docs/asciidoc/admin.adoc @@ -0,0 +1,39 @@ +== 어드민 API + +=== 어드민 로그인 + +==== curl + +include::{snippets}/admin/auth/login/curl-request.adoc[] + +==== request + +include::{snippets}/admin/auth/login/http-request.adoc[] + +request fields + +include::{snippets}/admin/auth/login/request-fields.adoc[] + +==== response + +include::{snippets}/admin/auth/login/http-response.adoc[] + +''' + +=== 어드민 로그아웃 + +CAUTION: 해당 API는 '어드민 로그인'으로 세션을 발급 받은 후에 이용해야 합니다 + +==== curl + +include::{snippets}/admin/auth/logout/curl-request.adoc[] + +==== request + +include::{snippets}/admin/auth/logout/http-request.adoc[] + +==== response + +include::{snippets}/admin/auth/logout/http-response.adoc[] + +''' diff --git a/backend/src/docs/asciidoc/index.adoc b/backend/src/docs/asciidoc/index.adoc index f3ecc5b3e..92f1db757 100644 --- a/backend/src/docs/asciidoc/index.adoc +++ b/backend/src/docs/asciidoc/index.adoc @@ -13,3 +13,4 @@ include::room.adoc[] include::roomContent.adoc[] include::roomBalanceVote.adoc[] include::balance.adoc[] +include::admin.adoc[] diff --git a/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java b/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java new file mode 100644 index 000000000..b31af66eb --- /dev/null +++ b/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java @@ -0,0 +1,67 @@ +package ddangkong.documentation.admin; + +import static org.mockito.Mockito.doNothing; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import ddangkong.controller.admin.AdminController; +import ddangkong.controller.admin.dto.AdminLoginRequest; +import ddangkong.documentation.BaseDocumentationTest; +import ddangkong.service.admin.AdminService; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; + +@WebMvcTest(value = AdminController.class) +public class AdminDocumentationTest extends BaseDocumentationTest { + + @MockBean + private AdminService adminService; + + @Nested + class 어드민_로그인 { + + private static final String ENDPOINT = "/api/admin/login"; + + @Test + void 로그인_할_수_있다() throws Exception { + // given + AdminLoginRequest request = new AdminLoginRequest("이든", "password"); + String content = objectMapper.writeValueAsString(request); + doNothing().when(adminService).validatePassword(request.password()); + + // when & then + mockMvc.perform(post(ENDPOINT) + .content(content) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andDo(document("admin/auth/login", + requestFields( + fieldWithPath("nickname").type(STRING).description("닉네임"), + fieldWithPath("password").type(STRING).description("어드민 비밀번호") + ) + )); + } + } + + @Nested + class 어드민_로그아웃 { + + private static final String ENDPOINT = "/api/admin/logout"; + + @Test + void 로그아웃_할_수_있다() throws Exception { + // when & then + mockMvc.perform(post(ENDPOINT)) + .andExpect(status().isOk()) + .andDo(document("admin/auth/logout")); + } + } +} diff --git a/backend/src/test/java/ddangkong/documentation/admin/BaseAdminDocumentationTest.java b/backend/src/test/java/ddangkong/documentation/admin/BaseAdminDocumentationTest.java new file mode 100644 index 000000000..b2f517aa9 --- /dev/null +++ b/backend/src/test/java/ddangkong/documentation/admin/BaseAdminDocumentationTest.java @@ -0,0 +1,21 @@ +package ddangkong.documentation.admin; + +import ddangkong.controller.admin.Admin; +import ddangkong.documentation.BaseDocumentationTest; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockHttpSession; + +public abstract class BaseAdminDocumentationTest extends BaseDocumentationTest { + + @Value("${admin.session-key}") + private String sessionKey; + + protected MockHttpSession session; + + @BeforeEach + void setSession() { + session = new MockHttpSession(); + session.setAttribute(sessionKey, new Admin("admin")); + } +} From 01de41906cd8d5efe15a20bac879ec9cb2c9bf66 Mon Sep 17 00:00:00 2001 From: leegwichan Date: Tue, 31 Dec 2024 16:51:05 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20`@AdminAuth`=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EC=9D=B8=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 추후 #450과 합쳐지며 어드민 전용 API 에 인증 적용 예정 --- .../ddangkong/config/AdminAuthConfig.java | 19 ++++++++ .../ddangkong/controller/admin/AdminAuth.java | 13 ++++++ .../admin/AdminAuthorizationInterceptor.java | 46 +++++++++++++++++++ .../controller/admin/AdminController.java | 1 + .../admin/AdminDocumentationTest.java | 6 +-- 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 backend/src/main/java/ddangkong/config/AdminAuthConfig.java create mode 100644 backend/src/main/java/ddangkong/controller/admin/AdminAuth.java create mode 100644 backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java diff --git a/backend/src/main/java/ddangkong/config/AdminAuthConfig.java b/backend/src/main/java/ddangkong/config/AdminAuthConfig.java new file mode 100644 index 000000000..37c9b0fe9 --- /dev/null +++ b/backend/src/main/java/ddangkong/config/AdminAuthConfig.java @@ -0,0 +1,19 @@ +package ddangkong.config; + +import ddangkong.controller.admin.AdminAuthorizationInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@RequiredArgsConstructor +public class AdminAuthConfig implements WebMvcConfigurer { + + private final AdminAuthorizationInterceptor adminAuthorizationInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(adminAuthorizationInterceptor); + } +} diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java b/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java new file mode 100644 index 000000000..b79d52579 --- /dev/null +++ b/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java @@ -0,0 +1,13 @@ +package ddangkong.controller.admin; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface AdminAuth { +} diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java b/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java new file mode 100644 index 000000000..56fdae513 --- /dev/null +++ b/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java @@ -0,0 +1,46 @@ +package ddangkong.controller.admin; + +import ddangkong.exception.admin.NotExistAdminSessionException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; + +@Component +public class AdminAuthorizationInterceptor implements HandlerInterceptor { + + private final String sessionKey; + + public AdminAuthorizationInterceptor(@Value("${admin.session-key}") String sessionKey) { + this.sessionKey = sessionKey; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + if (hasAnnotation(handler, AdminAuth.class)) { + authorizeAdmin(request); + } + return true; + } + + private boolean hasAnnotation(Object handler, Class authClass) { + if (handler instanceof ResourceHttpRequestHandler) { + return false; + } + + HandlerMethod handlerMethod = (HandlerMethod) handler; + return handlerMethod.getMethodAnnotation(authClass) != null || + handlerMethod.getBeanType().getAnnotation(authClass) != null; + } + + private void authorizeAdmin(HttpServletRequest request) { + HttpSession session = request.getSession(false); + if (session == null || session.getAttribute(sessionKey) == null) { + throw new NotExistAdminSessionException(); + } + } +} diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminController.java b/backend/src/main/java/ddangkong/controller/admin/AdminController.java index cbda61c6f..ec8866d7a 100644 --- a/backend/src/main/java/ddangkong/controller/admin/AdminController.java +++ b/backend/src/main/java/ddangkong/controller/admin/AdminController.java @@ -35,6 +35,7 @@ public void login(@RequestBody AdminLoginRequest loginRequest, HttpServletReques log.info("어드민이 로그인 했습니다. nickname = {}, session = {}", loginRequest.nickname(), session.getId()); } + @AdminAuth @PostMapping("/admin/logout") public void logout(HttpServletRequest request) { HttpSession session = request.getSession(false); diff --git a/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java b/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java index b31af66eb..e4bb2a5a7 100644 --- a/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java +++ b/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java @@ -10,7 +10,6 @@ import ddangkong.controller.admin.AdminController; import ddangkong.controller.admin.dto.AdminLoginRequest; -import ddangkong.documentation.BaseDocumentationTest; import ddangkong.service.admin.AdminService; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -19,7 +18,7 @@ import org.springframework.http.MediaType; @WebMvcTest(value = AdminController.class) -public class AdminDocumentationTest extends BaseDocumentationTest { +public class AdminDocumentationTest extends BaseAdminDocumentationTest { @MockBean private AdminService adminService; @@ -59,7 +58,8 @@ class 어드민_로그아웃 { @Test void 로그아웃_할_수_있다() throws Exception { // when & then - mockMvc.perform(post(ENDPOINT)) + mockMvc.perform(post(ENDPOINT) + .session(session)) .andExpect(status().isOk()) .andDo(document("admin/auth/logout")); } From e319b1aac8180cf41f38b50c25d2cfa7d9710817 Mon Sep 17 00:00:00 2001 From: leegwichan Date: Fri, 3 Jan 2025 18:38:06 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refactor=20:=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20?= =?UTF-8?q?=EC=8B=A4=EC=8B=9C=20#451?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/ddangkong/controller/admin/AdminAuth.java | 5 ++--- .../controller/admin/AdminAuthorizationInterceptor.java | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java b/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java index b79d52579..c006a8231 100644 --- a/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java +++ b/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java @@ -1,13 +1,12 @@ package ddangkong.controller.admin; -import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Documented -@Retention(RetentionPolicy.RUNTIME) +@Retention(RetentionPolicy.CLASS) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface AdminAuth { + } diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java b/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java index 56fdae513..5e63eb19e 100644 --- a/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java +++ b/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java @@ -21,17 +21,18 @@ public AdminAuthorizationInterceptor(@Value("${admin.session-key}") String sessi @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - if (hasAnnotation(handler, AdminAuth.class)) { + if (hasAdminAuthAnnotation(handler)) { authorizeAdmin(request); } return true; } - private boolean hasAnnotation(Object handler, Class authClass) { + private boolean hasAdminAuthAnnotation(Object handler) { if (handler instanceof ResourceHttpRequestHandler) { return false; } + Class authClass = AdminAuth.class; HandlerMethod handlerMethod = (HandlerMethod) handler; return handlerMethod.getMethodAnnotation(authClass) != null || handlerMethod.getBeanType().getAnnotation(authClass) != null; From 9261d1588ac5c934a4539152702caa6e6a42e4f7 Mon Sep 17 00:00:00 2001 From: leegwichan Date: Sun, 5 Jan 2025 21:03:13 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20AdminService=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20?= =?UTF-8?q?=EB=B0=8F=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?#451?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Controller 는 facade 계층만 참조할 수 있는 규칙을 지키기 위해 패키지 변경 - dto 를 facade 계층에 통일성있게 두기 위해 AdminService 에서 인자에 dto 를 받게 함 --- .../ddangkong/controller/admin/AdminController.java | 10 +++++----- .../{service => facade}/admin/AdminService.java | 7 ++++--- .../admin/dto/AdminLoginRequest.java | 2 +- .../controller/admin/AdminControllerTest.java | 2 +- .../controller/admin/BaseAdminControllerTest.java | 2 +- .../documentation/admin/AdminDocumentationTest.java | 6 +++--- .../{service => facade}/admin/AdminServiceTest.java | 12 ++++++++---- 7 files changed, 23 insertions(+), 18 deletions(-) rename backend/src/main/java/ddangkong/{service => facade}/admin/AdminService.java (68%) rename backend/src/main/java/ddangkong/{controller => facade}/admin/dto/AdminLoginRequest.java (63%) rename backend/src/test/java/ddangkong/{service => facade}/admin/AdminServiceTest.java (75%) diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminController.java b/backend/src/main/java/ddangkong/controller/admin/AdminController.java index ec8866d7a..dfeb5a140 100644 --- a/backend/src/main/java/ddangkong/controller/admin/AdminController.java +++ b/backend/src/main/java/ddangkong/controller/admin/AdminController.java @@ -1,7 +1,7 @@ package ddangkong.controller.admin; -import ddangkong.controller.admin.dto.AdminLoginRequest; -import ddangkong.service.admin.AdminService; +import ddangkong.facade.admin.AdminService; +import ddangkong.facade.admin.dto.AdminLoginRequest; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; @@ -26,11 +26,11 @@ public AdminController(AdminService adminService, @Value("${admin.session-key}") } @PostMapping("/admin/login") - public void login(@RequestBody AdminLoginRequest loginRequest, HttpServletRequest request) { - adminService.validatePassword(loginRequest.password()); + public void login(@RequestBody AdminLoginRequest loginRequest, HttpServletRequest httpRequest) { + adminService.validatePassword(loginRequest); Admin admin = new Admin(loginRequest.nickname()); - HttpSession session = request.getSession(); + HttpSession session = httpRequest.getSession(); session.setAttribute(sessionKey, admin); log.info("어드민이 로그인 했습니다. nickname = {}, session = {}", loginRequest.nickname(), session.getId()); } diff --git a/backend/src/main/java/ddangkong/service/admin/AdminService.java b/backend/src/main/java/ddangkong/facade/admin/AdminService.java similarity index 68% rename from backend/src/main/java/ddangkong/service/admin/AdminService.java rename to backend/src/main/java/ddangkong/facade/admin/AdminService.java index aa369e315..34af65f8d 100644 --- a/backend/src/main/java/ddangkong/service/admin/AdminService.java +++ b/backend/src/main/java/ddangkong/facade/admin/AdminService.java @@ -1,6 +1,7 @@ -package ddangkong.service.admin; +package ddangkong.facade.admin; import ddangkong.exception.admin.NotMatchAdminPasswordException; +import ddangkong.facade.admin.dto.AdminLoginRequest; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -13,8 +14,8 @@ public AdminService(@Value("${admin.password}") String adminPassword) { this.adminPassword = adminPassword; } - public void validatePassword(String password) { - if (!adminPassword.equals(password)) { + public void validatePassword(AdminLoginRequest request) { + if (!adminPassword.equals(request.password())) { throw new NotMatchAdminPasswordException(); } } diff --git a/backend/src/main/java/ddangkong/controller/admin/dto/AdminLoginRequest.java b/backend/src/main/java/ddangkong/facade/admin/dto/AdminLoginRequest.java similarity index 63% rename from backend/src/main/java/ddangkong/controller/admin/dto/AdminLoginRequest.java rename to backend/src/main/java/ddangkong/facade/admin/dto/AdminLoginRequest.java index 56eebc416..eb5f2cc02 100644 --- a/backend/src/main/java/ddangkong/controller/admin/dto/AdminLoginRequest.java +++ b/backend/src/main/java/ddangkong/facade/admin/dto/AdminLoginRequest.java @@ -1,4 +1,4 @@ -package ddangkong.controller.admin.dto; +package ddangkong.facade.admin.dto; public record AdminLoginRequest(String nickname, String password) { } diff --git a/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java b/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java index 6a9f80a24..1b5cea601 100644 --- a/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java +++ b/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java @@ -1,6 +1,6 @@ package ddangkong.controller.admin; -import ddangkong.controller.admin.dto.AdminLoginRequest; +import ddangkong.facade.admin.dto.AdminLoginRequest; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.Nested; diff --git a/backend/src/test/java/ddangkong/controller/admin/BaseAdminControllerTest.java b/backend/src/test/java/ddangkong/controller/admin/BaseAdminControllerTest.java index 9dfbd1a2b..71fa53019 100644 --- a/backend/src/test/java/ddangkong/controller/admin/BaseAdminControllerTest.java +++ b/backend/src/test/java/ddangkong/controller/admin/BaseAdminControllerTest.java @@ -1,7 +1,7 @@ package ddangkong.controller.admin; import ddangkong.controller.BaseControllerTest; -import ddangkong.controller.admin.dto.AdminLoginRequest; +import ddangkong.facade.admin.dto.AdminLoginRequest; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeEach; diff --git a/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java b/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java index e4bb2a5a7..2c45d81df 100644 --- a/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java +++ b/backend/src/test/java/ddangkong/documentation/admin/AdminDocumentationTest.java @@ -9,8 +9,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import ddangkong.controller.admin.AdminController; -import ddangkong.controller.admin.dto.AdminLoginRequest; -import ddangkong.service.admin.AdminService; +import ddangkong.facade.admin.dto.AdminLoginRequest; +import ddangkong.facade.admin.AdminService; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -33,7 +33,7 @@ class 어드민_로그인 { // given AdminLoginRequest request = new AdminLoginRequest("이든", "password"); String content = objectMapper.writeValueAsString(request); - doNothing().when(adminService).validatePassword(request.password()); + doNothing().when(adminService).validatePassword(request); // when & then mockMvc.perform(post(ENDPOINT) diff --git a/backend/src/test/java/ddangkong/service/admin/AdminServiceTest.java b/backend/src/test/java/ddangkong/facade/admin/AdminServiceTest.java similarity index 75% rename from backend/src/test/java/ddangkong/service/admin/AdminServiceTest.java rename to backend/src/test/java/ddangkong/facade/admin/AdminServiceTest.java index 9791a2670..a6153a8a5 100644 --- a/backend/src/test/java/ddangkong/service/admin/AdminServiceTest.java +++ b/backend/src/test/java/ddangkong/facade/admin/AdminServiceTest.java @@ -1,10 +1,11 @@ -package ddangkong.service.admin; +package ddangkong.facade.admin; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import ddangkong.exception.admin.NotMatchAdminPasswordException; import ddangkong.facade.BaseServiceTest; +import ddangkong.facade.admin.dto.AdminLoginRequest; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -23,18 +24,21 @@ class 어드민_비밀번호_검증 { @Test void 어드민_비밀번호가_일치하면_아무_일도_일어나지_않는다() { + // given + AdminLoginRequest request = new AdminLoginRequest("admin", adminPassword); + // when & then - assertThatCode(() -> adminService.validatePassword(adminPassword)) + assertThatCode(() -> adminService.validatePassword(request)) .doesNotThrowAnyException(); } @Test void 어드민_비밀번호가_다르면_예외가_발생한다() { // given - String notMatchPassword = "no-password"; + AdminLoginRequest request = new AdminLoginRequest("admin", "no-password"); // when & then - assertThatThrownBy(() -> adminService.validatePassword(notMatchPassword)) + assertThatThrownBy(() -> adminService.validatePassword(request)) .isInstanceOf(NotMatchAdminPasswordException.class); } } From 9d4b3da7fbb7d858ac93bd8784b72048cdfccb73 Mon Sep 17 00:00:00 2001 From: leegwichan Date: Sun, 5 Jan 2025 21:03:52 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20session=20=EC=97=90=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20Serializable?= =?UTF-8?q?=20=EC=9D=84=20=EC=83=81=EC=86=8D=20#451?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/ddangkong/controller/admin/Admin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/ddangkong/controller/admin/Admin.java b/backend/src/main/java/ddangkong/controller/admin/Admin.java index b97f5fd39..fe9739a3f 100644 --- a/backend/src/main/java/ddangkong/controller/admin/Admin.java +++ b/backend/src/main/java/ddangkong/controller/admin/Admin.java @@ -1,9 +1,10 @@ package ddangkong.controller.admin; +import java.io.Serializable; import lombok.Getter; @Getter -public class Admin { +public class Admin implements Serializable { private final String nickname; From 0d9dbed55e2a1bd970bca4b4e6812ebb9f472664 Mon Sep 17 00:00:00 2001 From: leegwichan Date: Sun, 5 Jan 2025 21:53:26 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=EC=A0=84=EC=9A=A9=20API=20=EC=97=90=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20#451?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ddangkong/controller/admin/AdminAuth.java | 2 +- .../admin/AdminAuthorizationInterceptor.java | 5 ++--- .../controller/balance/AdminBalanceController.java | 8 +++++++- .../controller/admin/AdminControllerTest.java | 12 +++++++++++- .../balance/AdminBalanceControllerTest.java | 13 +++++++++---- .../balance/AdminBalanceDocumentationTest.java | 10 +++++++--- 6 files changed, 37 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java b/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java index c006a8231..d43912686 100644 --- a/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java +++ b/backend/src/main/java/ddangkong/controller/admin/AdminAuth.java @@ -5,7 +5,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Retention(RetentionPolicy.CLASS) +@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface AdminAuth { diff --git a/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java b/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java index 5e63eb19e..ec3d3ade5 100644 --- a/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java +++ b/backend/src/main/java/ddangkong/controller/admin/AdminAuthorizationInterceptor.java @@ -32,10 +32,9 @@ private boolean hasAdminAuthAnnotation(Object handler) { return false; } - Class authClass = AdminAuth.class; HandlerMethod handlerMethod = (HandlerMethod) handler; - return handlerMethod.getMethodAnnotation(authClass) != null || - handlerMethod.getBeanType().getAnnotation(authClass) != null; + return handlerMethod.getMethodAnnotation(AdminAuth.class) != null || + handlerMethod.getBeanType().getAnnotation(AdminAuth.class) != null; } private void authorizeAdmin(HttpServletRequest request) { diff --git a/backend/src/main/java/ddangkong/controller/balance/AdminBalanceController.java b/backend/src/main/java/ddangkong/controller/balance/AdminBalanceController.java index 568f4a13b..9473aba8d 100644 --- a/backend/src/main/java/ddangkong/controller/balance/AdminBalanceController.java +++ b/backend/src/main/java/ddangkong/controller/balance/AdminBalanceController.java @@ -1,9 +1,10 @@ package ddangkong.controller.balance; +import ddangkong.controller.admin.AdminAuth; import ddangkong.domain.balance.content.Category; import ddangkong.facade.balance.AdminBalanceContentFacade; -import ddangkong.facade.balance.dto.BalanceContentCreateResponse; import ddangkong.facade.balance.dto.BalanceContentCreateRequest; +import ddangkong.facade.balance.dto.BalanceContentCreateResponse; import ddangkong.facade.balance.dto.BalanceContentPatchRequest; import ddangkong.facade.balance.dto.BalanceContentPatchResponse; import ddangkong.facade.balance.dto.BalanceContentsAdminResponse; @@ -29,27 +30,32 @@ public class AdminBalanceController { private final AdminBalanceContentFacade adminBalanceContentFacade; + @AdminAuth @GetMapping("/admin/balances/contents") public BalanceContentsAdminResponse getContents(@RequestParam Category category) { return adminBalanceContentFacade.getContents(category); } + @AdminAuth @ResponseStatus(HttpStatus.CREATED) @PostMapping("/admin/balances/contents") public BalanceContentCreateResponse createContent(@RequestBody BalanceContentCreateRequest request) { return adminBalanceContentFacade.createContent(request); } + @AdminAuth @PatchMapping("/admin/balances/contents") public BalanceContentPatchResponse updateContent(@RequestBody BalanceContentPatchRequest request) { return adminBalanceContentFacade.updateContent(request); } + @AdminAuth @PatchMapping("/admin/balances/options") public BalanceOptionPatchResponse updateOption(@RequestBody BalanceOptionPatchRequest request) { return adminBalanceContentFacade.updateOption(request); } + @AdminAuth @ResponseStatus(HttpStatus.NO_CONTENT) @DeleteMapping("/admin/balances/contents/{contentId}") public void deleteContent(@PathVariable long contentId) { diff --git a/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java b/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java index 1b5cea601..1dbe5e78f 100644 --- a/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java +++ b/backend/src/test/java/ddangkong/controller/admin/AdminControllerTest.java @@ -34,10 +34,20 @@ class 어드민_로그아웃 { void 이미_로그인_한_유저는_로그아웃_할_수_있다() { // when & then RestAssured.given().log().all() - .cookie("JSESSIONID", sessionId) + .sessionId(sessionId) .when().post("/api/admin/logout") .then().log().all() .statusCode(200); } + + @Test + void 로그인_하지_않은_유저는_로그아웃_시도시_에러를_발생한다() { + // when & then + RestAssured.given().log().all() + .cookie("JSESSIONID", "NO-SESSION") + .when().post("/api/admin/logout") + .then().log().all() + .statusCode(401); + } } } diff --git a/backend/src/test/java/ddangkong/controller/balance/AdminBalanceControllerTest.java b/backend/src/test/java/ddangkong/controller/balance/AdminBalanceControllerTest.java index bf02492c5..0f640e141 100644 --- a/backend/src/test/java/ddangkong/controller/balance/AdminBalanceControllerTest.java +++ b/backend/src/test/java/ddangkong/controller/balance/AdminBalanceControllerTest.java @@ -3,7 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import ddangkong.controller.BaseControllerTest; +import ddangkong.controller.admin.BaseAdminControllerTest; import ddangkong.domain.balance.content.BalanceContent; import ddangkong.domain.balance.content.Category; import ddangkong.domain.balance.option.BalanceOption; @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -class AdminBalanceControllerTest extends BaseControllerTest { +class AdminBalanceControllerTest extends BaseAdminControllerTest { @Nested class 밸런스_게임_컨텐츠_조회 { @@ -41,6 +41,7 @@ class 밸런스_게임_컨텐츠_조회 { // when BalanceContentsAdminResponse actual = RestAssured.given() .queryParam("category", content1.getCategory()) + .sessionId(sessionId) .get("/api/admin/balances/contents") .then().contentType(ContentType.JSON).log().all() .statusCode(200) @@ -62,6 +63,7 @@ class 밸런스_게임_질문지_추가 { // when BalanceContentCreateResponse actual = RestAssured.given() + .sessionId(sessionId) .contentType(ContentType.JSON) .body(request) .post("/api/admin/balances/contents") @@ -75,8 +77,8 @@ class 밸런스_게임_질문지_추가 { () -> assertThat(actual.category()).isEqualTo(request.category()), () -> assertThat(actual.firstOption().name()).isEqualTo(request.firstOption()), () -> assertThat(actual.secondOption().name()).isEqualTo(request.secondOption()), - () -> assertThat(actual.firstOption().count()).isEqualTo(0), - () -> assertThat(actual.firstOption().percent()).isEqualTo(0) + () -> assertThat(actual.firstOption().count()).isZero(), + () -> assertThat(actual.firstOption().percent()).isZero() ); } } @@ -92,6 +94,7 @@ class 밸런스_게임_질문지_변경 { // when BalanceContentPatchResponse actual = RestAssured.given() + .sessionId(sessionId) .contentType(ContentType.JSON) .body(request) .patch("/api/admin/balances/contents") @@ -119,6 +122,7 @@ class 밸런스_게임_선택지_변경 { // when BalanceOptionPatchResponse actual = RestAssured.given() + .sessionId(sessionId) .contentType(ContentType.JSON) .body(request) .patch("/api/admin/balances/options") @@ -149,6 +153,7 @@ class 밸런스_게임_컨텐츠_삭제 { // when & then RestAssured.given() .pathParam("contentId", content.getId()) + .sessionId(sessionId) .delete("/api/admin/balances/contents/{contentId}") .then().log().all() .statusCode(204); diff --git a/backend/src/test/java/ddangkong/documentation/balance/AdminBalanceDocumentationTest.java b/backend/src/test/java/ddangkong/documentation/balance/AdminBalanceDocumentationTest.java index 704b35232..e8ce928a5 100644 --- a/backend/src/test/java/ddangkong/documentation/balance/AdminBalanceDocumentationTest.java +++ b/backend/src/test/java/ddangkong/documentation/balance/AdminBalanceDocumentationTest.java @@ -19,7 +19,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import ddangkong.controller.balance.AdminBalanceController; -import ddangkong.documentation.BaseDocumentationTest; +import ddangkong.documentation.admin.BaseAdminDocumentationTest; import ddangkong.domain.balance.content.Category; import ddangkong.facade.balance.AdminBalanceContentFacade; import ddangkong.facade.balance.dto.BalanceContentAdminResponse; @@ -39,7 +39,7 @@ import org.springframework.http.MediaType; @WebMvcTest(value = AdminBalanceController.class) -public class AdminBalanceDocumentationTest extends BaseDocumentationTest { +public class AdminBalanceDocumentationTest extends BaseAdminDocumentationTest { @MockBean private AdminBalanceContentFacade adminBalanceContentFacade; @@ -64,6 +64,7 @@ class 밸런스_게임_컨텐츠_조회 { // when & then mockMvc.perform(get(ENDPOINT) .queryParam("category", category.name()) + .session(session) ) .andExpect(status().isOk()) .andDo(document("admin/balance/get", @@ -112,6 +113,7 @@ class 밸런스_게임_질문지_추가 { // when & then mockMvc.perform(post(ENDPOINT) + .session(session) .content(content) .contentType(MediaType.APPLICATION_JSON) ) @@ -154,6 +156,7 @@ class 밸런스_게임_질문지_변경 { // when & then mockMvc.perform(patch(ENDPOINT) + .session(session) .content(content) .contentType(MediaType.APPLICATION_JSON) ) @@ -185,6 +188,7 @@ class 밸런스_게임_선택지_변경 { // when & then mockMvc.perform(patch(ENDPOINT) + .session(session) .content(content) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) @@ -211,7 +215,7 @@ class 밸런스_게임_컨텐츠_삭제 { doNothing().when(adminBalanceContentFacade).deleteContent(contentId); // when & then - mockMvc.perform(delete(ENDPOINT, contentId)) + mockMvc.perform(delete(ENDPOINT, contentId).session(session)) .andExpect(status().isNoContent()) .andDo(document("admin/balance/delete", pathParameters(