[MODIFY] 외부 Edge 패키지 등록 API 보안 개선, Bearer 인증 설명 추가 및 입력 데이터 검증 로직 강화, WebClient 기본 인증 변경

main
bjkim 8 months ago
parent 16759d37ad
commit d04f68adc6

@ -1,7 +1,5 @@
package kr.re.etri.autoflow.controllers; package kr.re.etri.autoflow.controllers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@ -53,28 +51,38 @@ public class ExternalAuthController {
@Operation( @Operation(
summary = "외부 Edge 패키지 등록", summary = "외부 Edge 패키지 등록",
description = "외부 서버로 Edge 패키지 정보를 파일과 함께 전송하여 등록합니다.", description = "외부 서버로 Edge 패키지 정보를 파일과 함께 전송하여 등록합니다.",
security = @SecurityRequirement(name = "bearerAuth") security = @SecurityRequirement(name = "bearerAuth") // 이 컨트롤러만 Bearer 적용
) )
@PostMapping(value = "/add", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/add", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> addEdgePackage( public ResponseEntity<?> addEdgePackage(
@Parameter(description = "로그인 시 발급받은 Bearer 토큰")
@RequestHeader("Authorization") String bearerToken, @RequestHeader("Authorization") String bearerToken,
@RequestPart("edgePkgInfoVO") String edgePkgInfoJson,
@Parameter(description = "Edge 패키지 등록 요청 데이터(JSON 형식)")
@RequestPart(value = "edgePkgInfoVO", required = true) EdgePkgInfoRequest edgePkgInfoRequest,
@Parameter(description = "업로드할 패키지 파일")
@RequestPart(value = "file", required = false) MultipartFile file @RequestPart(value = "file", required = false) MultipartFile file
) { ) {
try { try {
ObjectMapper mapper = new ObjectMapper(); // 서비스 호출
EdgePkgInfoRequest edgePkgInfoRequest = mapper.readValue(edgePkgInfoJson, EdgePkgInfoRequest.class);
Object response = externalAuthService.uploadEdgePackage(bearerToken, edgePkgInfoRequest, file); Object response = externalAuthService.uploadEdgePackage(bearerToken, edgePkgInfoRequest, file);
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} catch (JsonProcessingException e) { } catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body("잘못된 JSON 형식: " + e.getMessage()); return ResponseEntity.badRequest().body(
Map.of("success", false, "message", "잘못된 요청: " + e.getMessage())
);
} catch (Exception e) { } catch (Exception e) {
return ResponseEntity.internalServerError().body("파일 업로드 실패: " + e.getMessage()); return ResponseEntity.internalServerError().body(
Map.of("success", false, "message", "파일 업로드 실패: " + e.getMessage())
);
} }
} }
// DTO: 요청 // DTO: 요청
public static record SigninRequest(String id, String password) { } public static record SigninRequest(String id, String password) { }

@ -23,7 +23,7 @@ public class MlflowController {
public MlflowController() { public MlflowController() {
this.webClient = WebClient.builder() this.webClient = WebClient.builder()
.baseUrl("http://192.168.10.135:30128/api/2.0/mlflow") .baseUrl("http://192.168.10.135:30128/api/2.0/mlflow")
.defaultHeaders(headers -> headers.setBasicAuth("user", "LImQa2Me37nu")) .defaultHeaders(headers -> headers.setBasicAuth("user", "aBZ1RzEDGutI"))
.build(); .build();
} }

@ -15,6 +15,9 @@ import org.springframework.web.multipart.MultipartFile;
import javax.net.ssl.*; import javax.net.ssl.*;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -94,23 +97,52 @@ public class ExternalAuthService {
* Edge API * Edge API
*/ */
public Map<String, Object> getEdgePackageList(String id, String token) { public Map<String, Object> getEdgePackageList(String id, String token) {
// URL 구성 try {
// 1) 입력 검증: id에 URL/공격 문자열 포함 금지
if (id == null || id.isBlank()) {
throw new IllegalArgumentException("잘못된 사용자 ID입니다.");
}
if (id.contains("http://") || id.contains("https://")) {
throw new IllegalArgumentException("ID에 URL을 포함할 수 없습니다.");
}
// 2) 안전하게 파라미터 인코딩 및 URL 구성 (base URL은 외부 입력에서 분리)
String safeId = URLEncoder.encode(id, StandardCharsets.UTF_8);
String url = String.format("%s?sw_group=-1&sw_type=-1&searchType=&searchText=&pageNum=1&pageSize=10&auth_id=%s&user_id=", String url = String.format("%s?sw_group=-1&sw_type=-1&searchType=&searchText=&pageNum=1&pageSize=10&auth_id=%s&user_id=",
edgeSearchUrl, id); edgeSearchUrl, safeId);
// 3) URI 생성 (edgeSearchUrl이 올바른 형식인지 확인)
URI uri = URI.create(url);
// 추가 안전권장(선택) : 실제 호출되는 호스트가 설정한 base URL의 호스트와 동일한지 확인
// (내부망 차단을 원치 않으셨으므로 private IP 검사 코드는 제외)
URI baseUri = URI.create(edgeSearchUrl);
if (!uri.getHost().equalsIgnoreCase(baseUri.getHost())) {
throw new SecurityException("요청 호스트가 허용된 서비스와 일치하지 않습니다.");
}
// 4) 요청 헤더 구성
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(token); headers.setBearerAuth(token);
headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON)); headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON));
HttpEntity<Void> request = new HttpEntity<>(headers); HttpEntity<Void> request = new HttpEntity<>(headers);
ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.GET, request, Map.class); // 5) 요청 수행
ResponseEntity<Map> response = restTemplate.exchange(uri, HttpMethod.GET, request, Map.class);
if (response.getStatusCode() == HttpStatus.OK) { if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
return response.getBody(); return response.getBody();
} else {
throw new RuntimeException("외부 Edge 패키지 조회 실패");
} }
throw new RuntimeException("Failed to fetch edge package list"); } catch (IllegalArgumentException iae) {
// 입력 검증 실패는 호출자에게 명확히 알릴 수 있음
throw iae;
} catch (Exception e) {
// 내부 상세 예외는 래핑하여 외부에 노출하지 않음
throw new RuntimeException("외부 Edge API 호출 중 오류가 발생했습니다.");
}
} }
public Object uploadEdgePackage(String bearerToken, EdgePkgInfoRequest edgePkgInfoRequest, MultipartFile file) throws IOException { public Object uploadEdgePackage(String bearerToken, EdgePkgInfoRequest edgePkgInfoRequest, MultipartFile file) throws IOException {

Loading…
Cancel
Save