-
Notifications
You must be signed in to change notification settings - Fork 1
[FEAT] 비회원용 테이블정보 조회 메서드 추가 #257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,11 +3,14 @@ | |
| import com.debatetimer.controller.auth.AuthMember; | ||
| import com.debatetimer.controller.tool.jwt.AuthManager; | ||
| import com.debatetimer.domain.member.Member; | ||
| import com.debatetimer.dto.customize.response.CustomizeTableResponse; | ||
| import com.debatetimer.dto.sharing.response.ChairmanTokenResponse; | ||
| import com.debatetimer.service.customize.CustomizeService; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.ResponseStatus; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
|
|
@@ -26,4 +29,12 @@ public ChairmanTokenResponse issueChairmanToken( | |
| String chairmanToken = authManager.issueChairmanToken(member, debateTime * 2); | ||
| return new ChairmanTokenResponse(chairmanToken); | ||
| } | ||
|
|
||
| @GetMapping("/api/live/table/customize/{tableId}") | ||
| @ResponseStatus(HttpStatus.OK) | ||
| public CustomizeTableResponse getTable( | ||
| @PathVariable long tableId | ||
| ) { | ||
| return customizeService.findTable(tableId); | ||
| } | ||
|
Comment on lines
+33
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 비회원 조회를 Line 33-39는 인증·공유 토큰·라이브 세션 검증 없이 🤖 Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,13 +54,27 @@ public CustomizeTable getByIdAndMember(long tableId, Member member) { | |
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public List<CustomizeTimeBox> getCustomizeTimeBoxes(long tableId, Member member) { | ||
| public CustomizeTable getById(long tableId) { | ||
| return tableRepository.getById(tableId) | ||
| .toDomain(); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public List<CustomizeTimeBox> getMemberCustomizeTimeBoxes(long tableId, Member member) { | ||
| CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member); | ||
| List<CustomizeTimeBoxEntity> timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity); | ||
| List<BellEntity> bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList); | ||
| return toCustomizeTimeBoxes(timeBoxEntityList, bellEntityList); | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| public List<CustomizeTimeBox> getCustomizeTimeBoxes(long tableId) { | ||
| CustomizeTableEntity tableEntity = tableRepository.getById(tableId); | ||
| List<CustomizeTimeBoxEntity> timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity); | ||
| List<BellEntity> bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList); | ||
| return toCustomizeTimeBoxes(timeBoxEntityList, bellEntityList); | ||
| } | ||
|
Comment on lines
+63
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 두 메서드( @Transactional(readOnly = true)
public List<CustomizeTimeBox> getMemberCustomizeTimeBoxes(long tableId, Member member) {
CustomizeTableEntity tableEntity = tableRepository.getByIdAndMember(tableId, member);
return getCustomizeTimeBoxes(tableEntity);
}
@Transactional(readOnly = true)
public List<CustomizeTimeBox> getCustomizeTimeBoxes(long tableId) {
CustomizeTableEntity tableEntity = tableRepository.getById(tableId);
return getCustomizeTimeBoxes(tableEntity);
}
private List<CustomizeTimeBox> getCustomizeTimeBoxes(CustomizeTableEntity tableEntity) {
List<CustomizeTimeBoxEntity> timeBoxEntityList = timeBoxRepository.findAllByCustomizeTable(tableEntity);
List<BellEntity> bellEntityList = bellRepository.findAllByCustomizeTimeBoxIn(timeBoxEntityList);
return toCustomizeTimeBoxes(timeBoxEntityList, bellEntityList);
} |
||
|
|
||
| @Transactional(readOnly = true) | ||
| public long getTotalTimeBoxTimes(long tableId) { | ||
| return timeBoxRepository.sumTimeByTableId(tableId); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package com.debatetimer.controller.sharing; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
| import static org.junit.jupiter.api.Assertions.assertAll; | ||
|
|
||
| import com.debatetimer.controller.BaseControllerTest; | ||
| import com.debatetimer.domain.customize.CustomizeBoxType; | ||
| import com.debatetimer.domain.member.Member; | ||
| import com.debatetimer.dto.customize.response.CustomizeTableResponse; | ||
| import com.debatetimer.entity.customize.CustomizeTableEntity; | ||
| import io.restassured.http.ContentType; | ||
| import org.junit.jupiter.api.Nested; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| class SharingRestControllerTest extends BaseControllerTest { | ||
|
|
||
| @Nested | ||
| class GetTable { | ||
|
|
||
| @Test | ||
| void 비회원이_사용자_지정_테이블을_조회한다() { | ||
| Member bito = memberGenerator.generate("default@gmail.com"); | ||
| CustomizeTableEntity bitoTable = customizeTableEntityGenerator.generate(bito); | ||
| customizeTimeBoxEntityGenerator.generate(bitoTable, CustomizeBoxType.NORMAL, 1); | ||
| customizeTimeBoxEntityGenerator.generateNotExistSpeaker(bitoTable, CustomizeBoxType.NORMAL, 2); | ||
|
|
||
| CustomizeTableResponse response = given() | ||
| .contentType(ContentType.JSON) | ||
| .pathParam("tableId", bitoTable.getId()) | ||
| .when().get("/api/live/table/customize/{tableId}") | ||
| .then().statusCode(200) | ||
| .extract().as(CustomizeTableResponse.class); | ||
|
|
||
| assertAll( | ||
| () -> assertThat(response.id()).isEqualTo(bitoTable.getId()), | ||
| () -> assertThat(response.table()).hasSize(2) | ||
| ); | ||
| } | ||
|
|
||
| @Test | ||
| void 존재하지_않는_테이블을_조회하면_예외가_발생한다() { | ||
| long notExistTableId = 999L; | ||
|
|
||
| given() | ||
| .contentType(ContentType.JSON) | ||
| .pathParam("tableId", notExistTableId) | ||
| .when().get("/api/live/table/customize/{tableId}") | ||
| .then().statusCode(404); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
비회원용 토론 테이블 정보를 조회하기 위해 인증 없이 접근 가능한
/api/live/table/customize/{tableId}엔드포인트를 추가하셨습니다.하지만
tableId가 순차적인long타입(Sequential ID)이기 때문에, 악의적인 사용자가 ID를 1씩 증가시키며 API를 호출(IDOR/BOLA 공격)할 경우 시스템에 등록된 모든 회원의 커스텀 토론 테이블 정보를 무단으로 조회(Scraping)할 수 있는 심각한 보안 취약점이 존재합니다.권장 해결 방안:
isShared또는status등)를 나타내는 필드를 추가하고, 비회원 조회 시 해당 테이블이 실제로 공유 가능한 상태인지 검증하는 로직을 추가합니다.