|
|
|
|
package kr.re.etri.autoflow.service;
|
|
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
import jakarta.annotation.PostConstruct;
|
|
|
|
|
import kr.re.etri.autoflow.payload.request.EdgePkgInfoRequest;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
import org.springframework.core.io.ByteArrayResource;
|
|
|
|
|
import org.springframework.http.*;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
import org.springframework.util.LinkedMultiValueMap;
|
|
|
|
|
import org.springframework.util.MultiValueMap;
|
|
|
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
|
|
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
|
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
|
|
|
|
import javax.net.ssl.*;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.net.URI;
|
|
|
|
|
import java.net.URLEncoder;
|
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
|
import java.security.cert.X509Certificate;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
@Service
|
|
|
|
|
public class ExternalAuthService {
|
|
|
|
|
|
|
|
|
|
private RestTemplate restTemplate;
|
|
|
|
|
|
|
|
|
|
@Value("${external.auth.signin-url}")
|
|
|
|
|
private String signinUrl;
|
|
|
|
|
|
|
|
|
|
@Value("${external.auth.edge-search-url}")
|
|
|
|
|
private String edgeSearchUrl;
|
|
|
|
|
|
|
|
|
|
@Value("${external.edge.add-url:https://cuuva.com:24443/api/datamanager/edge-pkg/add}")
|
|
|
|
|
private String edgeAddUrl;
|
|
|
|
|
|
|
|
|
|
@PostConstruct
|
|
|
|
|
public void init() {
|
|
|
|
|
this.restTemplate = createUnsafeRestTemplate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** SSL 무시용 RestTemplate 생성 */
|
|
|
|
|
private RestTemplate createUnsafeRestTemplate() {
|
|
|
|
|
try {
|
|
|
|
|
TrustManager[] trustAllCerts = new TrustManager[]{
|
|
|
|
|
new X509TrustManager() {
|
|
|
|
|
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
|
|
|
|
|
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
|
|
|
|
|
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
|
|
|
|
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
|
|
|
|
|
|
|
|
|
|
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
|
|
|
|
|
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
|
|
|
|
|
|
|
|
|
|
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
|
|
|
|
return new RestTemplate(requestFactory);
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new RuntimeException("Failed to create unsafe RestTemplate", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Bearer 토큰 발급 */
|
|
|
|
|
public Map<String, Object> getUserInfo(String id, String password) {
|
|
|
|
|
// 요청 본문
|
|
|
|
|
Map<String, String> body = Map.of("id", id, "password", password);
|
|
|
|
|
|
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
|
|
|
|
|
|
|
HttpEntity<Map<String, String>> request = new HttpEntity<>(body, headers);
|
|
|
|
|
|
|
|
|
|
ResponseEntity<Map> response = restTemplate.exchange(
|
|
|
|
|
signinUrl, HttpMethod.POST, request, Map.class);
|
|
|
|
|
|
|
|
|
|
if (response.getStatusCode() == HttpStatus.OK) {
|
|
|
|
|
Map<String, Object> respBody = response.getBody();
|
|
|
|
|
if (respBody != null && respBody.get("data") instanceof Map dataMap) {
|
|
|
|
|
Map<String, Object> result = new HashMap<>();
|
|
|
|
|
result.put("id", dataMap.get("id"));
|
|
|
|
|
result.put("name", dataMap.get("name"));
|
|
|
|
|
result.put("token", dataMap.get("token"));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new RuntimeException("Failed to get user info from external server");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Edge 패키지 검색 API 호출
|
|
|
|
|
*/
|
|
|
|
|
public Map<String, Object> getEdgePackageList(String id, String token) {
|
|
|
|
|
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=",
|
|
|
|
|
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();
|
|
|
|
|
headers.setBearerAuth(token);
|
|
|
|
|
headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON));
|
|
|
|
|
HttpEntity<Void> request = new HttpEntity<>(headers);
|
|
|
|
|
|
|
|
|
|
// 5) 요청 수행
|
|
|
|
|
ResponseEntity<Map> response = restTemplate.exchange(uri, HttpMethod.GET, request, Map.class);
|
|
|
|
|
|
|
|
|
|
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
|
|
|
|
|
return response.getBody();
|
|
|
|
|
} else {
|
|
|
|
|
throw new RuntimeException("외부 Edge 패키지 조회 실패");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (IllegalArgumentException iae) {
|
|
|
|
|
// 입력 검증 실패는 호출자에게 명확히 알릴 수 있음
|
|
|
|
|
throw iae;
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 내부 상세 예외는 래핑하여 외부에 노출하지 않음
|
|
|
|
|
throw new RuntimeException("외부 Edge API 호출 중 오류가 발생했습니다.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Object uploadEdgePackage(String bearerToken, EdgePkgInfoRequest edgePkgInfoRequest, MultipartFile file) throws IOException {
|
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
|
|
|
|
headers.set("Authorization", bearerToken.startsWith("Bearer ") ? bearerToken : "Bearer " + bearerToken);
|
|
|
|
|
|
|
|
|
|
// Object를 JSON 문자열로 변환
|
|
|
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
|
String edgePkgInfoJson = objectMapper.writeValueAsString(edgePkgInfoRequest);
|
|
|
|
|
|
|
|
|
|
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
|
|
|
|
body.add("edgePkgInfoVO", edgePkgInfoJson);
|
|
|
|
|
|
|
|
|
|
if (file != null && !file.isEmpty()) {
|
|
|
|
|
body.add("file", new ByteArrayResource(file.getBytes()) {
|
|
|
|
|
@Override
|
|
|
|
|
public String getFilename() {
|
|
|
|
|
return file.getOriginalFilename();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(body, headers);
|
|
|
|
|
|
|
|
|
|
ResponseEntity<Object> response = restTemplate.exchange(
|
|
|
|
|
edgeAddUrl,
|
|
|
|
|
HttpMethod.POST,
|
|
|
|
|
request,
|
|
|
|
|
Object.class
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return response.getBody();
|
|
|
|
|
}
|
|
|
|
|
}
|