Conversation
| ) | ||
| }) | ||
| @PostMapping("/login") | ||
| public ResponseEntity<User> login( |
There was a problem hiding this comment.
[P1] 비밀번호 해시 노출
ResponseEntity<User>를 반환하면 User 엔티티의 password 필드가 JSON 응답에 그대로 포함됩니다. OWASP A01:2021 Broken Access Control 해당.
| public ResponseEntity<User> login( | |
| public ResponseEntity<UserDto> login( |
UserDto 또는 UserLoginResponse DTO로 변환하여 반환하세요. 또는 User.password에 @JsonIgnore를 추가하세요.
|
|
||
| String name = Optional.ofNullable(request.newUsername()).orElse(user.getUsername()); | ||
| String email = Optional.ofNullable(request.newEmail()).orElse(user.getEmail()); | ||
| String password = Optional.ofNullable(request.newPassword()).orElse(user.getPassword()); |
There was a problem hiding this comment.
[P1] 비밀번호 업데이트 시 해싱 누락 (평문 저장)
create()에서는 passwordEncoder.encode()를 사용하지만, update()에서는 새 비밀번호를 해싱 없이 평문으로 저장합니다. 이후 passwordEncoder.matches()로 로그인 시 항상 실패하게 됩니다.
// Fix:
String password = Optional.ofNullable(request.newPassword())
.map(passwordEncoder::encode) // 해싱 추가
.orElse(user.getPassword());| examples = @ExampleObject(value = "User with email {email} already exists"))) | ||
| }) | ||
| @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | ||
| public ResponseEntity<User> createUser( |
There was a problem hiding this comment.
[P1] User Entity 직접 반환 — 비밀번호 노출
createUser가 ResponseEntity<User>를 반환하여 password가 응답에 포함됩니다. findAll/findById는 UserDto를 반환하므로 일관성도 없습니다.
UserDto로 통일하세요:
public ResponseEntity<UserDto> createUser(...) {
User user = userService.create(...);
return ResponseEntity.status(HttpStatus.CREATED).body(userService.toDto(user));
}| package com.sprint.mission.discodeit.user.dto; | ||
|
|
||
| import com.sprint.mission.discodeit.binarycontent.dto.BinaryContentResponse; | ||
| import jakarta.validation.constraints.Email; |
There was a problem hiding this comment.
[P1] 유효성 검증 어노테이션 제거됨
기존 @NotBlank, @Email 어노테이션이 모두 제거되었습니다. 빈 문자열, null, 잘못된 이메일이 그대로 저장됩니다.
// Fix:
public record UserCreateRequest(
@NotBlank String username,
@NotBlank @Email String email,
@NotBlank String password
) {}Controller에 @Valid도 추가 필요.
| http | ||
| .csrf(csrf -> csrf.disable()) | ||
| .authorizeHttpRequests(auth -> auth | ||
| .anyRequest().permitAll() |
There was a problem hiding this comment.
[P2] Spring Security 무력화 — 전체 엔드포인트 인증 없음
CSRF 비활성화 + anyRequest().permitAll()로 모든 요청이 허용됩니다. 로그인 엔드포인트가 존재하지만 실질적 인증이 없어 무의미합니다.
개발 단계라면 최소한 // TODO: SECURITY - enable auth before production 주석을 남기거나, 준비가 안 됐다면 spring-boot-starter-security 의존성 자체를 제거하세요.
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.HttpStatusCode; |
There was a problem hiding this comment.
[P5] 미사용 import — HttpStatusCode
사용되지 않는 import입니다. 제거하세요.
| private ChannelDto toDto(Channel channel) { | ||
| Instant lastMessageAt = messageRepository.findByChannelId(channel.getId()) | ||
| .stream() | ||
| .sorted(Comparator.comparing(Message::getCreatedAt).reversed()) |
There was a problem hiding this comment.
[P4] 비효율적인 정렬 패턴
최대값을 구하기 위해 전체 리스트를 정렬(O(n log n))하고 있습니다. Stream.max()를 사용하면 O(n)으로 개선됩니다.
// Fix:
Instant lastMessageAt = messageRepository.findByChannelId(channel.getId())
.stream()
.map(Message::getCreatedAt)
.max(Comparator.naturalOrder())
.orElse(Instant.MIN);| @PatchMapping( | ||
| value = "/{userId}", | ||
| consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | ||
| public ResponseEntity<User> updateUser( |
There was a problem hiding this comment.
[P3] updateUser도 Entity 직접 반환
createUser와 동일하게 ResponseEntity<User>를 반환하여 password가 노출됩니다. UserDto로 변환하세요.
| private final UserRepository userRepository; | ||
| private final PasswordEncoder passwordEncoder; | ||
|
|
||
| public User login(UserLoginRequest request) { |
There was a problem hiding this comment.
[P3] AuthService가 User Entity 직접 반환
login() 메서드가 User를 그대로 반환합니다. 서비스 계층에서 DTO로 변환하여 반환하는 것이 바람직합니다.
또한, UserStatusRepository를 import하지만 사용하지 않습니다 (dead import).
| @@ -0,0 +1,8 @@ | |||
| [build] | |||
There was a problem hiding this comment.
[P4] railway.toml 위치 오류
railway.toml이 src/main/java/com/sprint/mission/discodeit/ 내부에 있습니다. Railway는 프로젝트 루트에서 이 파일을 찾으므로, 현재 위치에서는 인식되지 않습니다.
프로젝트 루트 디렉토리로 이동하세요.
기본 요구사항
API 설계에 정답은 없지만, 이어지는 요구사항과 미션을 원활히 수행하기 위해 제공된 API 스펙에 맞추어 구현해주세요.
특히, 심화 요구사항에서 제공되는 프론트엔드 코드는 제공된 API 스펙을 준수해야 연동할 수 있습니다.
Postman API 테스트 결과를 export하여 PR에 첨부해주세요.
auth.postman_collection.json
binarycontent.postman_collection.json
channel.postman_collection.json
messsage.postman_collection.json
readstatus.postman_collection.json
user.postman_collection.json
심화 요구사항
fe_1.0.0.zip
화면 가이드
Railway.app은 애플리케이션을 쉽게 배포할 수 있도록 도와주는 PaaS입니다.
스크린샷
멘토에게