-
Notifications
You must be signed in to change notification settings - Fork 0
본인 소유 검증 누락으로 인한 멤버/디바이스 권한 문제 수정 #69
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
Conversation
- 멤버 디바이스 조회 기능 수정 - RequestParam email 제거 - 인증된 디바이스 식별자로 멤버 조회하도록 변경 - 디바이스 삭제 기능 수정 - 인증된 디바이스 식별자로 요청자 식별 - 삭제 대상 디바이스 소유권 검증 로직 추가 - Service Input DTO 이메일 필드 제거 - MemberFindInput, DeviceDeleteInput
- MemberServiceTest 예외 메시지 검증 구체화 - MemberControllerTest 불필요한 테스트 및 파라미터 제거
🧪 테스트 커버리지 리포트
|
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.
Pull request overview
GET /api/v1/members 및 DELETE /api/v1/device에서 email 기반 권한 판단을 제거하고, X-Device-Id 기반으로 본인 조회/본인 소유 디바이스만 삭제되도록 동작을 수정하는 PR입니다. Issue #67에서 지적된 “타 계정 조회/타인 디바이스 삭제 가능” 문제를 해소하는 방향의 변경입니다.
Changes:
GET /api/v1/members조회 기준을email→X-Device-Id(@AuthDevice)로 전환하고 입력 모델을 정리DELETE /api/v1/device에 요청자 디바이스 기반 소유권 검증 추가 및 비소유 시404정책 적용- 관련 테스트/REST Docs 및 에러 메시지 일부 수정
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/com/recyclestudy/member/controller/MemberController.java | 멤버 조회에서 email 쿼리 파라미터 제거, 헤더 기반 조회로 전환 |
| src/main/java/com/recyclestudy/member/controller/DeviceController.java | 디바이스 삭제 입력 생성 시 email 제거(legacy body 값 무시) |
| src/main/java/com/recyclestudy/member/service/MemberService.java | 멤버 조회를 device identifier 기반으로 변경, 디바이스 삭제 시 소유권 검증 추가 |
| src/main/java/com/recyclestudy/member/service/input/MemberFindInput.java | email 의존 제거(식별자만 받도록 변경) |
| src/main/java/com/recyclestudy/member/service/input/DeviceDeleteInput.java | email 의존 제거(요청 디바이스/대상 디바이스 식별자만 사용) |
| src/test/java/com/recyclestudy/member/service/MemberServiceTest.java | 서비스 로직 변경에 맞춘 테스트 수정 및 소유권/Unauthorized 케이스 추가 |
| src/test/java/com/recyclestudy/member/controller/MemberControllerTest.java | 멤버 조회 API 문서/테스트를 헤더 기반으로 일부 전환(일부 레거시 문서 잔존) |
| src/test/java/com/recyclestudy/member/controller/DeviceControllerTest.java | 디바이스 인증/삭제 문서 및 legacy email optional 처리, 에러 메시지 갱신 |
Comments suppressed due to low confidence (2)
src/test/java/com/recyclestudy/member/controller/MemberControllerTest.java:197
- GET /api/v1/members는 이제 @AuthDevice(X-Device-Id) 기반으로만 조회하도록 변경되어 email 쿼리 파라미터로 멤버를 찾지 않습니다(MemberController에서도 @RequestParam 제거). 그런데 이 테스트는 email 파라미터를 문서화/요청에 포함하고, 404(존재하지 않는 멤버) 시나리오를 유지하고 있어 변경된 API 동작과 문서가 어긋납니다. 404 시나리오를 실제 발생 가능한 케이스(예: 401/Unauthorized - 유효하지 않거나 비활성 디바이스)로 바꾸거나, email 파라미터 문서화를 일관되게 optional로 정리해 주세요.
@DisplayName("존재하지 않는 멤버 조회 시 404 응답을 반환한다")
void findAllMemberDevices_NotFoundMember() {
// given
final String email = "notfound@test.com";
final String headerIdentifier = "device-identifier";
given(memberService.findAllMemberDevices(any()))
.willThrow(new NotFoundException("존재하지 않는 멤버입니다"));
// when
// then
given(this.spec)
.filter(document(DEFAULT_REST_DOC_PATH,
builder()
.tag("Member")
.summary("멤버 디바이스 조회")
.description("존재하지 않는 멤버 조회 시 404 응답을 반환한다")
.requestHeaders(
headerWithName("X-Device-Id").description("디바이스 식별자")
)
.queryParameters(
parameterWithName("email").description("이메일 (다음 버전에서 제거 예정)")
)
.responseFields(
fieldWithPath("message").type(JsonFieldType.STRING).description("에러 메시지")
),
queryParameters(
parameterWithName("email").description("이메일 (다음 버전에서 제거 예정)")
)
))
.header("X-Device-Id", headerIdentifier)
.param("email", email)
.when()
.get("/api/v1/members")
.then()
.statusCode(HttpStatus.NOT_FOUND.value())
.body("message", equalTo("존재하지 않는 멤버입니다"));
}
src/test/java/com/recyclestudy/member/controller/MemberControllerTest.java:329
- "존재하지 않는 디바이스 식별자"로 조회 시나리오가 404로 문서화/검증되어 있는데, 실제로는 @AuthDevice 리졸버에서 디바이스 미존재/비활성은 401(UnauthorizedException)로 처리됩니다. 또한 queryParameters 블록 내 email 설명이 한 곳은 "이메일"로, 다른 곳은 "다음 버전에서 제거 예정"으로 섞여 있어 문서가 일관되지 않습니다. 이 케이스를 401로 정리하고, legacy email 파라미터를 유지한다면 optional로 동일하게 표기해 주세요.
@Test
@DisplayName("존재하지 않는 디바이스 식별자로 조회 시 404 응답을 반환한다")
void findAllMemberDevices_NotFoundDevice() {
// given
final String email = "test@test.com";
final String headerIdentifier = "not-found-id";
given(memberService.findAllMemberDevices(any()))
.willThrow(new NotFoundException("존재하지 않는 디바이스 아이디입니다"));
// when
// then
given(this.spec)
.filter(document(DEFAULT_REST_DOC_PATH,
builder()
.tag("Member")
.summary("멤버 디바이스 조회")
.description("존재하지 않는 디바이스 식별자로 조회 시 404 응답을 반환한다")
.requestHeaders(
headerWithName("X-Device-Id").description("디바이스 식별자")
)
.queryParameters(
parameterWithName("email").description("이메일")
)
.responseFields(
fieldWithPath("message").type(JsonFieldType.STRING).description("에러 메시지")
),
queryParameters(
parameterWithName("email").description("이메일 (다음 버전에서 제거 예정)")
)
))
.header("X-Device-Id", headerIdentifier)
.param("email", email)
.when()
.get("/api/v1/members")
.then()
.statusCode(HttpStatus.NOT_FOUND.value())
.body("message", equalTo("존재하지 않는 디바이스 아이디입니다"));
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| final List<Device> devices = deviceRepository.findAllByMemberEmail(input.email()); | ||
| return MemberFindOutput.of(input.email(), devices); | ||
| final Member member = memberRepository.findByIdentifier(input.deviceIdentifier()) | ||
| .orElseThrow(() -> new UnauthorizedException("인증되지 않은 디바이스입니다")); |
Copilot
AI
Feb 13, 2026
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.
memberRepository.findByIdentifier() 실패 시 findAllMemberDevices()만 "인증되지 않은 디바이스입니다"를 사용하고, 같은 패턴을 쓰는 다른 메서드들(findNotificationTime/updateNotificationTime/deleteDevice)은 "유효하지 않은 디바이스입니다"를 사용하고 있습니다. @AuthDevice 리졸버의 메시지/다른 서비스들과도 달라서 클라이언트 입장에서 에러 메시지가 일관되지 않습니다. 이 메서드도 동일한 메시지로 통일하거나(권장), 여기서 구분하려면 실제로 활성 여부를 판단하는 로직을 함께 두는 쪽이 맞습니다.
| .orElseThrow(() -> new UnauthorizedException("인증되지 않은 디바이스입니다")); | |
| .orElseThrow(() -> new UnauthorizedException("유효하지 않은 디바이스입니다")); |
| public void deleteDevice(final DeviceDeleteInput input) { | ||
| deviceRepository.deleteByIdentifier(input.targetDeviceIdentifier()); | ||
| final Member requestMember = memberRepository.findByIdentifier(input.deviceIdentifier()) | ||
| .orElseThrow(() -> new UnauthorizedException("유효하지 않은 디바이스입니다")); | ||
|
|
||
| final Device targetDevice = deviceRepository.findByIdentifier(input.targetDeviceIdentifier()) | ||
| .orElseThrow(() -> new NotFoundException("존재하지 않는 디바이스입니다: %s" | ||
| .formatted(input.targetDeviceIdentifier().getValue()))); | ||
|
|
||
| if (!targetDevice.getMember().hasEmail(requestMember.getEmail())) { | ||
| throw new NotFoundException("존재하지 않는 디바이스입니다: %s".formatted(input.targetDeviceIdentifier().getValue())); | ||
| } | ||
|
|
||
| deviceRepository.delete(targetDevice); |
Copilot
AI
Feb 13, 2026
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.
deleteDevice()에서 deviceRepository.findByIdentifier(...)는 member를 fetch join하지 않기 때문에, 이후 targetDevice.getMember().hasEmail(...) 호출 시 추가 쿼리가 발생합니다(지금은 1건이라도 항상 추가 round-trip). 소유권 검증/삭제를 한 번의 쿼리로 처리하도록 (1) findByIdentifier에 JOIN FETCH를 추가한 메서드를 만들거나, (2) identifier+member 조건으로 조회/삭제하는 전용 repository 메서드(예: findByIdentifierAndMemberEmail / deleteByIdentifierAndMemberEmail)를 고려해 주세요.
src/test/java/com/recyclestudy/member/controller/MemberControllerTest.java
Show resolved
Hide resolved
- 이메일 파라미터 누락 시 200 응답 반환에 맞춰 테스트 설명 수정
* refactor: 디바이스 인증 기능 마이그레이션 - 멤버 디바이스 조회 기능 수정 - RequestParam email 제거 - 인증된 디바이스 식별자로 멤버 조회하도록 변경 - 디바이스 삭제 기능 수정 - 인증된 디바이스 식별자로 요청자 식별 - 삭제 대상 디바이스 소유권 검증 로직 추가 - Service Input DTO 이메일 필드 제거 - MemberFindInput, DeviceDeleteInput * refactor: 리뷰 저장 시 커스텀 주기 소유권 검증 로직 수정 - MemberServiceTest 예외 메시지 검증 구체화 - MemberControllerTest 불필요한 테스트 및 파라미터 제거 * test: 멤버 디바이스 조회 테스트 설명 수정 - 이메일 파라미터 누락 시 200 응답 반환에 맞춰 테스트 설명 수정
🚀 작업 내용
GET /api/v1/members조회 기준을email에서X-Device-Id로 전환DELETE /api/v1/device에 요청자-디바이스 소유권 검증 추가404반환 정책 적용MemberFindInput,DeviceDeleteInput정리 (email의존 제거)📸 이슈 번호
✍ 궁금한 점