Skip to content
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
183 changes: 0 additions & 183 deletions docs/api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -15975,189 +15975,6 @@
"summary" : "닉네임 변경",
"tags" : [ "사용자" ]
}
},
"/users/{userId}" : {
"delete" : {
"description" : "본인만 요청할 수 있습니다",
"operationId" : "deleteUser",
"parameters" : [ {
"description" : "사용자 ID",
"in" : "path",
"name" : "userId",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"description" : "API 버전. 생략하면 현재 버전(1). 지원하지 않는 값이면 400",
"in" : "header",
"name" : "X-API-Version",
"required" : false,
"schema" : {
"default" : "1",
"type" : "string"
}
} ],
"responses" : {
"200" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ApiSuccess"
}
}
},
"description" : "OK"
}
},
"security" : [ {
"Bearer Authentication" : [ ]
} ],
"summary" : "회원 탈퇴 (구 경로)",
"tags" : [ "사용자" ]
}
},
"/users/{userId}/deactivate" : {
"put" : {
"description" : "본인만 요청할 수 있습니다",
"operationId" : "deactivateUser",
"parameters" : [ {
"description" : "사용자 ID",
"in" : "path",
"name" : "userId",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"description" : "API 버전. 생략하면 현재 버전(1). 지원하지 않는 값이면 400",
"in" : "header",
"name" : "X-API-Version",
"required" : false,
"schema" : {
"default" : "1",
"type" : "string"
}
} ],
"responses" : {
"200" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/ApiSuccess"
}
}
},
"description" : "OK"
}
},
"security" : [ {
"Bearer Authentication" : [ ]
} ],
"summary" : "계정 비활성화 (구 경로)",
"tags" : [ "사용자" ]
}
},
"/users/{userId}/location" : {
"put" : {
"description" : "본인만 변경할 수 있습니다. 신규 클라이언트는 /users/me/location 을 사용하세요",
"operationId" : "updateUserLocation",
"parameters" : [ {
"description" : "사용자 ID",
"in" : "path",
"name" : "userId",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"description" : "위도",
"in" : "query",
"name" : "latitude",
"required" : true,
"schema" : {
"format" : "double",
"type" : "number"
}
}, {
"description" : "경도",
"in" : "query",
"name" : "longitude",
"required" : true,
"schema" : {
"format" : "double",
"type" : "number"
}
}, {
"description" : "API 버전. 생략하면 현재 버전(1). 지원하지 않는 값이면 400",
"in" : "header",
"name" : "X-API-Version",
"required" : false,
"schema" : {
"default" : "1",
"type" : "string"
}
} ],
"responses" : {
"200" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/UserDto"
}
}
},
"description" : "OK"
}
},
"security" : [ {
"Bearer Authentication" : [ ]
} ],
"summary" : "위치 갱신 (구 경로)",
"tags" : [ "사용자" ]
}
},
"/users/{userId}/profile-image" : {
"put" : {
"description" : "본인만 변경할 수 있습니다. 신규 클라이언트는 /users/me/profile-image 를 사용하세요",
"operationId" : "updateProfileImage",
"parameters" : [ {
"description" : "사용자 ID",
"in" : "path",
"name" : "userId",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"description" : "프로필 이미지 URL",
"in" : "query",
"name" : "profileImageUrl",
"required" : true,
"schema" : {
"type" : "string"
}
}, {
"description" : "API 버전. 생략하면 현재 버전(1). 지원하지 않는 값이면 400",
"in" : "header",
"name" : "X-API-Version",
"required" : false,
"schema" : {
"default" : "1",
"type" : "string"
}
} ],
"responses" : {
"200" : {
"description" : "OK"
}
},
"security" : [ {
"Bearer Authentication" : [ ]
} ],
"summary" : "프로필 이미지 변경 (구 경로)",
"tags" : [ "사용자" ]
}
}
},
"security" : [ {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ public ResponseEntity<ProfileImageResponse> uploadMyProfileImage(
return ResponseEntity.ok(userFacade.uploadProfileImage(selfDbId(), file));
}

@PutMapping("/{userId}/profile-image")
@LogExecutionTime
@Operation(summary = "프로필 이미지 변경 (구 경로)",
description = "본인만 변경할 수 있습니다. 신규 클라이언트는 /users/me/profile-image 를 사용하세요")
Expand All @@ -167,7 +166,6 @@ public ResponseEntity<UserDto> updateMyLocation(
return ResponseEntity.ok(userFacade.updateUserLocation(selfDbId(), latitude, longitude));
}

@PutMapping("/{userId}/location")
@LogExecutionTime
@Operation(summary = "위치 갱신 (구 경로)",
description = "본인만 변경할 수 있습니다. 신규 클라이언트는 /users/me/location 을 사용하세요")
Expand All @@ -191,7 +189,6 @@ public ResponseEntity<ApiSuccess> deactivateMe() {
return ResponseEntity.ok(ApiSuccess.of("회원 탈퇴가 완료되었습니다."));
}

@PutMapping("/{userId}/deactivate")
@LogExecutionTime
@Operation(summary = "계정 비활성화 (구 경로)", description = "본인만 요청할 수 있습니다")
@SecurityRequirement(name = "Bearer Authentication")
Expand All @@ -211,7 +208,6 @@ public ResponseEntity<ApiSuccess> deleteMe() {
return ResponseEntity.ok(ApiSuccess.of("회원 탈퇴가 완료되었습니다. 데이터는 보존되며 필요시 복구 가능합니다."));
}

@DeleteMapping("/{userId}")
@LogExecutionTime
@Operation(summary = "회원 탈퇴 (구 경로)", description = "본인만 요청할 수 있습니다")
@SecurityRequirement(name = "Bearer Authentication")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.springframework.test.web.servlet.MvcResult;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
Expand Down Expand Up @@ -181,7 +182,13 @@ void emailVerificationLinkIsPublic() throws Exception {
@ValueSource(strings = {
"/users/1/role",
"/users/1/activate",
"/users/1/reactivate"
"/users/1/reactivate",
// 아래 셋은 권한 상승이 아니라 "경로에 남의 ID 를 넣을 수 있는 것처럼 보이던"
// 구 경로다(본인 확인은 들어가 있었다). 대체 경로는 /users/me/* 이고
// 프런트는 그쪽만 쓴다. 이슈 #91
"/users/1/location",
"/users/1/profile-image",
"/users/1/deactivate"
})
@WithMockUser(username = "attacker@example.com", roles = "PARENT")
void privilegedMappingsRemovedFromUserApi(String path) throws Exception {
Expand All @@ -193,6 +200,21 @@ void privilegedMappingsRemovedFromUserApi(String path) throws Exception {
.isIn(404, 405);
}

/**
* 회원 탈퇴 구 경로도 사라졌다.
*
* <p>{@code DELETE /users/{userId}} 는 본인 확인이 들어가 있었지만, 경로에 남의 ID 를 넣을
* 수 있는 것처럼 보였다. 탈퇴는 {@code DELETE /users/me} 하나로만 받는다. 이슈 #91
*/
@Test
@DisplayName("DELETE /users/{userId} 구 경로는 사라졌다")
@WithMockUser(username = "member@example.com", roles = "PARENT")
void legacyAccountDeletionPathRemoved() throws Exception {
assertThat(mockMvc.perform(delete("/users/1")).andReturn().getResponse().getStatus())
.as("구 경로는 매핑이 없어야 한다 (404/405)")
.isIn(404, 405);
}

/**
* 공공데이터 동기화는 외부 API 한도를 태우고 DB 에 쓴다. 공개로 두면 누구나 실행할 수 있다.
*
Expand Down
Loading