Skip to content
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

531 refactor dnd module api url edit and enhancement #535

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions src/main/java/peer/backend/controller/dnd/DnDController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package peer.backend.controller.dnd;

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import peer.backend.dto.dnd.RequestDnDDTO;
import peer.backend.mongo.entity.TeamDnD;
import peer.backend.service.dnd.DnDService;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/dnd-main")
public class DnDController {
private final DnDService dndService;

@PostMapping("/create")
public ResponseEntity<?> create(@RequestBody TeamDnD data) {

Check failure

Code scanning / SonarCloud

Persistent entities should not be used as arguments of "@RequestMapping" methods High

Replace this persistent entity with a simple POJO or DTO object. See more on SonarCloud
Copy link
Contributor Author

@Paul2021-R Paul2021-R Jan 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분의 보안문제는 한마디로 정의하면 다음과 같다.
DTO 형태로 재정의 되지 않은 객체 데이터 형태를 주고 받는 방식은 보안적인 문제를 야기 할 수 있으며, 그렇기에 제한적인 부분만 정재시킨 DTO 형태로의 수정을 요청한 것이다.

하지만 해당 기능의 기획 자체가 프론트에서 사용 가능하도록 만드는 것이었으므로, 그러한 방식으로 프론트-백엔드 간의 협의 방식으로 변경시 의도를 침해하게됨

따라서 향후 이 부분에 대해 다른 방식으로 보안 적용이 필요해 보인다.

TeamDnD ret;
try {
ret = this.dndService.createDnD(data);
} catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<TeamDnD>(ret,HttpStatus.CREATED);
}

@PostMapping("/read")
public ResponseEntity<?> read(@RequestBody peer.backend.dto.dnd.RequestDnDDTO data) {
TeamDnD ret;
try {
ret = this.dndService.getDnD(data);
if (ret == null)
return new ResponseEntity<>("There is no that DnD data", HttpStatus.NOT_FOUND);
} catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<TeamDnD>(ret,HttpStatus.OK);
}

@PostMapping("/update")
public ResponseEntity<?> update(@RequestBody TeamDnD data) {

Check failure

Code scanning / SonarCloud

Persistent entities should not be used as arguments of "@RequestMapping" methods High

Replace this persistent entity with a simple POJO or DTO object. See more on SonarCloud
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분의 보안문제는 한마디로 정의하면 다음과 같다.
DTO 형태로 재정의 되지 않은 객체 데이터 형태를 주고 받는 방식은 보안적인 문제를 야기 할 수 있으며, 그렇기에 제한적인 부분만 정재시킨 DTO 형태로의 수정을 요청한 것이다.

하지만 해당 기능의 기획 자체가 프론트에서 사용 가능하도록 만드는 것이었으므로, 그러한 방식으로 프론트-백엔드 간의 협의 방식으로 변경시 의도를 침해하게됨

따라서 향후 이 부분에 대해 다른 방식으로 보안 적용이 필요해 보인다.

TeamDnD ret;
try{
ret = this.dndService.updateDnD(data);
} catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<TeamDnD>(ret, HttpStatus.OK);
}

@DeleteMapping("/delete")
public ResponseEntity<?> delete(@RequestBody RequestDnDDTO data) {
try {
this.dndService.deleteDnD(data);
} catch (Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}
145 changes: 145 additions & 0 deletions src/main/java/peer/backend/controller/dnd/DnDSubController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package peer.backend.controller.dnd;

import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import peer.backend.dto.dndSub.CalendarEventDTO;
import peer.backend.dto.dndSub.DeleteTargetDTO;
import peer.backend.dto.dndSub.MemberDTO;
import peer.backend.entity.team.Team;
import peer.backend.entity.user.User;
import peer.backend.exception.BadRequestException;
import peer.backend.service.dnd.DnDSubService;


import java.util.List;
import java.util.NoSuchElementException;

@RestController
@RequiredArgsConstructor
@RequestMapping(DnDSubController.WIDGET_URL)
public class DnDSubController {
public static final String WIDGET_URL = "api/v1/dnd-sub";
public static final String CALENDAR_IDENTIFIER = "calendar";
private final DnDSubService dnDSubService;

@GetMapping("calendar/team-list")
@ApiOperation(value = "", notes = "달력을 위한 팀 멤버 리스트를 제공합니다.")
public ResponseEntity<Object> getTeamMemberList(Authentication auth, @RequestBody long teamId) {
Team target = this.dnDSubService.getTeamByTeamId(teamId);

// redis 에 자주 쓸 가능성이 있는 team 정보 저장
this.dnDSubService.saveTeamDataInRedis(Long.toString(teamId), CALENDAR_IDENTIFIER, target);

// 유효성 검사
if (this.dnDSubService.validCheckForTeam(target)
|| this.dnDSubService.validCheckUserWithTeam(target, User.authenticationToUser(auth))) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}

// 멤버 리스트 담기 & 에러 핸들링
List<MemberDTO> ret;
try {
ret = this.dnDSubService.getMemberList(User.authenticationToUser(auth), target);
}
catch (NoSuchElementException | BadRequestException e) {
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}
if(ret == null) {
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}
return new ResponseEntity<>(ret, HttpStatus.OK);
}

@PostMapping("calendar/set-alarm")
@ApiOperation(value = "", notes = "알람으로 기록된 이벤트를 설정합니다.")
public ResponseEntity<Object> setAlarmEvent(Authentication auth, @RequestBody CalendarEventDTO event) {
// redis 에서 team 정보 찾기
Team target = (Team)this.dnDSubService.getTeamDataInRedis(Long.toString(event.getTeamId()), CALENDAR_IDENTIFIER);
if(target == null) {
target = this.dnDSubService.getTeamByTeamId(event.getTeamId());
this.dnDSubService.saveTeamDataInRedis(Long.toString(event.getTeamId()), CALENDAR_IDENTIFIER, target);
}

// 유효성 검사
if (this.dnDSubService.validCheckForTeam(target)
|| this.dnDSubService.validCheckUserWithTeam(target, User.authenticationToUser(auth))) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}

Long eventId;

try {
eventId = this.dnDSubService.setEventToAlarm(event);
} catch (BadRequestException e) {
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}

return new ResponseEntity<>(eventId, HttpStatus.OK);
}

@DeleteMapping("calendar/delete-alarm")
@ApiOperation(value = "", notes = "알람으로 기록된 이벤트를 삭제합니다.")
public ResponseEntity<Object> deleteAlarmEvent(Authentication auth, @RequestBody DeleteTargetDTO event) {
Team target = (Team)this.dnDSubService.getTeamDataInRedis(Long.toString(event.getTeamId()), CALENDAR_IDENTIFIER);
if(target == null) {
target = this.dnDSubService.getTeamByTeamId(event.getTeamId());
this.dnDSubService.saveTeamDataInRedis(Long.toString(event.getTeamId()), CALENDAR_IDENTIFIER, target);
}

// 유효성 검사
if (this.dnDSubService.validCheckForTeam(target)
|| this.dnDSubService.validCheckUserWithTeam(target, User.authenticationToUser(auth))) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}

//대상 삭제
try {
this.dnDSubService.deleteEventFromAlarm(event);
}catch (NoSuchElementException e) {
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}

return new ResponseEntity<>(HttpStatus.OK);
}

@PostMapping("calendar/update-alarm")
@ApiOperation(value = "", notes = "알람으로 기록된 이벤트를 갱신합니다.")
public ResponseEntity<Object> updateAlarmEvent(Authentication auth, @RequestBody CalendarEventDTO event) {
// redis 에서 team 정보 찾기
Team target = (Team)this.dnDSubService.getTeamDataInRedis(Long.toString(event.getTeamId()), CALENDAR_IDENTIFIER);
if(target == null) {
target = this.dnDSubService.getTeamByTeamId(event.getTeamId());
this.dnDSubService.saveTeamDataInRedis(Long.toString(event.getTeamId()), CALENDAR_IDENTIFIER, target);
}

// 유효성 검사
if (this.dnDSubService.validCheckForTeam(target)
|| this.dnDSubService.validCheckUserWithTeam(target, User.authenticationToUser(auth))) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}

Long eventId;

try {
eventId = this.dnDSubService.updateEventToAlarm(event);
} catch (BadRequestException e) {
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}

if (eventId == -1L){
return new ResponseEntity<>(new NoSuchElementException("존재하지 않는 이벤트입니다."), HttpStatus.BAD_REQUEST);
}

return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

@GetMapping("calendar/test/total-alarm")
@ApiOperation(value="", notes = "기록된 모든 알람을 호출합니다. 테스트용 코드입니다.")
public ResponseEntity<Object> getTestTotalAlarmList(Authentication auth) {
return new ResponseEntity<>(this.dnDSubService.getAllEvents(), HttpStatus.OK);
}
}
13 changes: 13 additions & 0 deletions src/main/java/peer/backend/dto/dnd/RequestDnDDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package peer.backend.dto.dnd;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class RequestDnDDTO {
private Long teamId;
private String type;
}
39 changes: 39 additions & 0 deletions src/main/java/peer/backend/dto/dndSub/CalendarEventDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package peer.backend.dto.dndSub;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CalendarEventDTO {
private Long teamId;
private Long eventId;
private String title;
private LocalDateTime start;
private LocalDateTime end;
private List<MemberDTO> member;


@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CalendarEventDTO target = (CalendarEventDTO) o;
return Objects.equals(eventId, ((CalendarEventDTO) o).eventId) &&
Objects.equals(teamId, ((CalendarEventDTO) o).teamId);
}
// HashSet 을 제대로 동작시키기 위한 코드, 같은지 여부를 다각도로 점검한다.

@Override
public int hashCode() {
return Objects.hash(teamId, eventId);
}
}
9 changes: 9 additions & 0 deletions src/main/java/peer/backend/dto/dndSub/DeleteTargetDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package peer.backend.dto.dndSub;

import lombok.Getter;

@Getter
public class DeleteTargetDTO {
private Long teamId;
private Long eventId;
}
37 changes: 37 additions & 0 deletions src/main/java/peer/backend/dto/dndSub/MemberDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package peer.backend.dto.dndSub;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.util.Objects;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MemberDTO {
@NotBlank
@Min(value = 1, message = "정상적인 userId를 넣어 주십시오.")
private Long userId;
@NotBlank(message = "닉네임은 필수 요소 입니다.")
private String nickname;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MemberDTO memberDTO = (MemberDTO) o;
return Objects.equals(userId, memberDTO.userId) &&
Objects.equals(nickname, memberDTO.nickname);
}
// HashSet 을 제대로 동작시키기 위한 코드, 같은지 여부를 다각도로 점검한다.

@Override
public int hashCode() {
return Objects.hash(userId, nickname);
}
}
Loading