2026-03-11 11:07:30 +01:00

650 lines
23 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cz.trask.migration;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import cz.trask.migration.config.ConfigManager;
import cz.trask.migration.model.APIInfo;
import cz.trask.migration.model.APIList;
import cz.trask.migration.model.ApplicationConfig;
import cz.trask.migration.model.ApplicationConfig.Wso2Settings;
import cz.trask.migration.model.HttpResponse;
import cz.trask.migration.model.RegisterResponse;
import cz.trask.migration.model.TokenResponse;
import cz.trask.migration.model.ZipEntryData;
import io.apicurio.registry.rest.client.RegistryClient;
import io.apicurio.registry.rest.client.RegistryClientFactory;
import io.apicurio.registry.rest.v2.beans.ArtifactMetaData;
import io.apicurio.registry.rest.v2.beans.ArtifactReference;
import io.apicurio.registry.rest.v2.beans.EditableMetaData;
import io.apicurio.registry.rest.v2.beans.Rule;
import io.apicurio.registry.types.RuleType;
public abstract class AbstractProcess {
private static Logger log = LogManager.getLogger(AbstractProcess.class);
protected static final String PARAM_SOURCE_APIM = "source_apim";
protected static final String VERSION_32 = "v32";
public static final String ARTIFACT_GROUP_SUBSCRIPTIONS = "SUBSCRIPTIONS";
public static final String ARTIFACT_NAME_SUBSCRIPTIONS = "subs.yaml";
public static final String ARTIFACT_GROUP_APPLICATIONS = "APPLICATIONS";
public static final String ARTIFACT_APPLICATION_DEFAULT_VERSION = "1.0.0";
public static final String DEFAULT_APPLICATION_NAME = "DefaultApplication";
public static final String ADMIN_USERNAME = "admin";
public static final String DEFAULT_DOC_FILE_NAME = "document.yaml";
public static ObjectMapper mapper;
public static ObjectMapper mapperYaml;
public static Proxy proxy;
public final RegistryClient client;
protected ApplicationConfig config;
protected AbstractProcess() {
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
YAMLFactory yamlFactory = new YAMLFactory();
yamlFactory.configure(YAMLGenerator.Feature.WRITE_DOC_START_MARKER, false);
yamlFactory.configure(YAMLGenerator.Feature.MINIMIZE_QUOTES, true);
yamlFactory.configure(YAMLGenerator.Feature.SPLIT_LINES, false);
mapperYaml = new ObjectMapper(yamlFactory);
mapperYaml.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
config = ConfigManager.getInstance().getConfig();
setTrustStoreCredentials();
if (config.getProxy() != null && config.getProxy().getHost() != null && !config.getProxy().getHost().isEmpty())
proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(config.getProxy().getHost(), config.getProxy().getPort()));
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() {
public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
return true;
}
});
Map<String, Object> clientConfigs = new HashMap<>();
try {
SSLContext sslContext = createSSLContext(
config.getTrustStore().getPath(),
config.getTrustStore().getPassword()
);
clientConfigs.put("io.apicurio.rest.client.jdk.sslContext", sslContext);
} catch (Exception e) {
log.error("Failed to initialize SSL context for Apicurio client", e);
}
this.client = RegistryClientFactory.create(config.getApicurio().getApiUrl(), clientConfigs);
client.listConfigProperties();
}
protected void setTrustStoreCredentials() {
File trustStoreFile = new File(config.getTrustStore().getPath());
if (!trustStoreFile.exists() || !trustStoreFile.isFile()) {
log.warn("Truststore file '{}' does not exist. Skipping truststore setup.",
config.getTrustStore().getPath());
return;
}
log.info("Setting truststore: " + trustStoreFile.getAbsolutePath());
System.setProperty("javax.net.ssl.trustStore", trustStoreFile.getAbsolutePath());
System.setProperty("javax.net.ssl.trustStorePassword", config.getTrustStore().getPassword());
}
private SSLContext createSSLContext(String trustStorePath, String trustStorePassword) throws Exception {
// Vytvoříme TrustStore
KeyStore trustStore = KeyStore.getInstance("JKS");
try (FileInputStream fis = new FileInputStream(trustStorePath)) {
trustStore.load(fis, trustStorePassword.toCharArray());
}
// Inicializujeme TrustManagerFactory
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(trustStore);
// Vytvoříme SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
return sslContext;
}
protected void configureHttpsConnection(HttpsURLConnection connection) throws Exception {
SSLContext sslContext = createSSLContext(System.getProperty("javax.net.ssl.trustStore"),
System.getProperty("javax.net.ssl.trustStorePassword"));
connection.setSSLSocketFactory(sslContext.getSocketFactory());
}
protected TokenResponse authenticateToWso2AndGetToken(Wso2Settings endpoints) throws Exception {
RegisterResponse register = register(endpoints.getRegistrationApiUrl(), endpoints.getWso2User());
String clientId = register.getClientId();
log.info("Registered with clientId: {}", clientId);
TokenResponse token = getToken(endpoints.getPublisherTokenUrl(), endpoints.getWso2User(), register,
"apim:api_view apim:api_create apim:api_manage apim:api_delete apim:api_publish apim:subscription_view apim:subscription_block "
+ "apim:subscription_manage apim:external_services_discover apim:threat_protection_policy_create apim:threat_protection_policy_manage "
+ "apim:document_create apim:document_manage apim:mediation_policy_view apim:mediation_policy_create apim:mediation_policy_manage "
+ "apim:client_certificates_view apim:client_certificates_add apim:client_certificates_update apim:ep_certificates_view apim:ep_certificates_add "
+ "apim:ep_certificates_update apim:publisher_settings apim:pub_alert_manage apim:shared_scope_manage apim:app_import_export apim:api_import_export "
+ "apim:api_product_import_export apim:api_generate_key apim:common_operation_policy_view apim:common_operation_policy_manage apim:comment_write "
+ "apim:comment_view apim:admin apim:subscribe apim:api_key apim:app_manage apim:sub_manage apim:store_settings apim:sub_alert_manage");
log.debug("Access token received {}", token.getAccess_token());
return token;
}
/**
* Retrieve access token based on clientId and clientSecret
*
* @param reg - client application object with clientId and clientSecret
* @param scope - requested OAuth2 scope
* @throws Exception
*/
protected TokenResponse getToken(String publisherurl, String wso2User, RegisterResponse reg, String scope)
throws Exception {
byte[] decoded = Base64.getDecoder().decode(wso2User);
String decodedstring = new String(decoded);
String[] decodedstringparts = decodedstring.split(":");
String username = decodedstringparts[0];
String password = decodedstringparts[1];
log.debug("Getting token with Username: '" + wso2User + "' URL: " + publisherurl);
Map<String, String> httpHeaders = new HashMap<>();
httpHeaders.put("Authorization", "Basic ".concat(Base64.getEncoder()
.encodeToString(reg.getClientId().concat(":").concat(reg.getClientSecret()).getBytes())));
httpHeaders.put("Content-Type", "application/x-www-form-urlencoded");
String data = "grant_type=password&username=".concat(username).concat("&password=")
.concat(URLEncoder.encode(password, "UTF-8")).concat("&scope=").concat(scope);
HttpResponse response = makeDataRequest(publisherurl, httpHeaders, data.getBytes());
log.debug("Token response: HTTP Code " + response.getResponseCode() + " Json: " + response.getResponse());
TokenResponse resp = mapper.readValue(response.getResponse(), TokenResponse.class);
return resp;
}
/**
* Register client application to get clientId and clientSecret
*
* @throws Exception
*/
protected RegisterResponse register(String publisherurl, String wso2User) throws Exception {
log.debug("Registering with Username: '" + wso2User + "' URL: " + publisherurl);
byte[] decodedUserBytes = Base64.getDecoder().decode(wso2User);
String decodeduserappkey = new String(decodedUserBytes);
String[] decodeduserparts = decodeduserappkey.split(":");
String decodeduser = decodeduserparts[0];
Map<String, String> httpHeaders = new HashMap<>();
httpHeaders.put("Authorization", "Basic ".concat(wso2User));
httpHeaders.put("Content-Type", "application/json");
String data = "{\"callbackUrl\": \"www.google.lk\",\"clientName\": \"rest_api_publisher" + decodeduser
+ "\",\"owner\": \"" + decodeduser + "\",\"grantType\": \"password refresh_token\",\"saasApp\": true}";
HttpResponse response = makeDataRequest(publisherurl, httpHeaders, data.getBytes());
log.debug(
"Register API response: HTTP Code " + response.getResponseCode() + " Json: " + response.getResponse());
RegisterResponse resp = mapper.readValue(response.getResponse(), RegisterResponse.class);
return resp;
}
/**
* Common function used for http request
*
* @param method - http method
* @param urlStr - url to dev poral
* @param httpHeaders
* @param params - currently is not used
* @throws Exception
*/
protected HttpResponse makeRequest(String method, String urlStr, Map<String, String> httpHeaders,
Map<String, String> params) throws Exception {
return makeRequest(method, urlStr, httpHeaders, params, false);
}
/**
* Common function used for http request
*
* @param method - http method
* @param urlStr - url to dev poral
* @param httpHeaders
* @param data - request data
* @param binary - binary or text mode
* @throws Exception
*/
protected HttpResponse makeRequest(String method, String urlStr, Map<String, String> httpHeaders,
Map<String, String> params, boolean binary) throws Exception {
log.info("Making {} request to URL: {}", method, urlStr);
String query = "";
if (params != null) {
for (String key : params.keySet()) {
query = query.concat(URLEncoder.encode(key, "UTF-8")).concat("=")
.concat(URLEncoder.encode(params.get(key), "UTF-8")).concat("&");
}
}
if (query.length() > 1 && "GET".equals(method)) {
urlStr = urlStr.concat("?").concat(query);
}
URL url = new URL(urlStr);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection(proxy!=null ? proxy : Proxy.NO_PROXY);
con.setRequestMethod(method);
con.setDoInput(true);
configureHttpsConnection(con);
for (String key : httpHeaders.keySet()) {
con.addRequestProperty(key, httpHeaders.get(key));
}
if (query.length() > 1 && "POST".equals(method)) {
con.setDoOutput(true);
OutputStream out = con.getOutputStream();
out.write(query.getBytes("UTF-8"));
}
InputStream in = con.getInputStream();
String res = "";
byte[] buf = new byte[4096];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read = in.read(buf);
while (read != -1) {
if (binary)
baos.write(buf, 0, read);
else
res = res.concat(new String(buf, 0, read));
read = in.read(buf);
}
baos.flush();
HttpResponse resp = new HttpResponse();
resp.setHeaders(con.getHeaderFields());
if (binary)
resp.setResponseBytes(baos.toByteArray());
else
resp.setResponse(res);
resp.setResponseCode(con.getResponseCode());
con.disconnect();
log.info("Response code: " + resp.getResponseCode());
// log.info("Response: " + resp.getResponse());
return resp;
}
/**
* Common function used for http request
*
* @param urlStr - url to dev poral
* @param httpHeaders
* @param data - request data
* @throws Exception
*/
protected HttpResponse makeDataRequest(String urlStr, Map<String, String> httpHeaders, byte[] data)
throws Exception {
URL url = new URL(urlStr);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection(proxy!=null ? proxy : Proxy.NO_PROXY);
con.setRequestMethod("POST");
con.setDoInput(true);
con.setDoOutput(true);
configureHttpsConnection(con);
for (String key : httpHeaders.keySet()) {
con.addRequestProperty(key, httpHeaders.get(key));
}
con.addRequestProperty("Content-Length", "" + data.length);
OutputStream out = con.getOutputStream();
out.write(data);
out.flush();
out.close();
InputStream in;
try {
in = con.getInputStream();
} catch (Exception e) {
in = con.getErrorStream();
}
if (in == null) {
HttpResponse resp = new HttpResponse();
resp.setHeaders(con.getHeaderFields());
resp.setResponseCode(con.getResponseCode());
return resp;
}
String res = "";
byte[] buf = new byte[4096];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read = in.read(buf);
while (read != -1) {
res = res.concat(new String(buf, 0, read));
read = in.read(buf);
}
baos.flush();
HttpResponse resp = new HttpResponse();
resp.setHeaders(con.getHeaderFields());
resp.setResponse(res);
resp.setResponseCode(con.getResponseCode());
return resp;
}
/**
* Retrieve the list of APIs by name.
*
* @param tokenResponse - WSO2 APIM access token
* @throws Exception
*/
protected APIList getList(String publisherurl, TokenResponse tokenResponse) throws Exception {
APIList listOfApis = null;
try {
//String url = publisherurl.concat(String.format("/apis?limit=10&offset=0&query=name:%s", "PTSPaymentHubCZSIT*"));
String url = publisherurl.concat("/apis?limit=9999&offset=0");
log.debug("Getting APIs with token: '" + tokenResponse.getAccess_token() + "' URL: " + url);
Map<String, String> httpHeaders = new HashMap<>();
Map<String, String> params = new HashMap<>();
httpHeaders.put("Authorization", "Bearer ".concat(tokenResponse.getAccess_token()));
HttpResponse response = makeRequest("GET", url, httpHeaders, params);
log.debug("Listing APIs: HTTP Code " + response.getResponseCode() + " Data: " + response.getResponse());
listOfApis = mapper.readValue(response.getResponse(), APIList.class);
if (response.getResponseCode() != 200)
log.error("Cannot list API. Something bad happened.");
} catch (Exception e) {
log.error("Cannot list API:" + e);
throw new Exception("Cannot list API:" + e.getMessage());
}
return listOfApis;
}
/**
* Common function used for upload API
*
* @param urlStr - url to dev poral
* @param httpHeaders
* @param params - currently is not used
* @param api - zip file to upload
* @throws Exception
*/
protected HttpResponse makeFileRequest(String method, String urlStr, Map<String, String> httpHeaders, byte[] buff,
String attachmentFileName) throws Exception {
if (buff == null) {
log.error("Cannot send NULL payload to rest service.");
}
String crlf = "\r\n";
String twoHyphens = "--";
String boundary = "----" + System.currentTimeMillis() + "----";
URL url = new URL(urlStr);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection(proxy!=null ? proxy : Proxy.NO_PROXY);
con.setUseCaches(false);
con.setDoOutput(true);
configureHttpsConnection(con);
con.setRequestMethod(method);
con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
con.setRequestProperty("User-Agent", "curl/7.55.1");
con.setRequestProperty("Accept", "*/*");
for (String key : httpHeaders.keySet()) {
con.addRequestProperty(key, httpHeaders.get(key));
}
DataOutputStream request = new DataOutputStream(con.getOutputStream());
request.writeBytes(crlf);
request.writeBytes(twoHyphens + boundary + crlf);
request.writeBytes(
"Content-Disposition: form-data; name=\"file\"; filename=\"" + attachmentFileName + "\"" + crlf);
request.writeBytes("Content-Type: application/octet-stream" + crlf);
request.writeBytes("Content-Transfer-Encoding: binary" + crlf);
request.writeBytes(crlf);
request.write(buff);
request.writeBytes(crlf);
request.writeBytes(twoHyphens + boundary + twoHyphens + crlf);
request.flush();
request.close();
String res = "";
byte[] buf = new byte[4096];
HttpResponse resp = new HttpResponse();
InputStream in;
int responseCode = con.getResponseCode();
if (responseCode >= 200 || responseCode <= 299) {
in = con.getInputStream();
} else {
in = con.getErrorStream();
}
while (in.available() > 0) {
int read = in.read(buf);
res = res.concat(new String(buf, 0, read));
}
resp.setHeaders(con.getHeaderFields());
resp.setResponse(res);
resp.setResponseCode(responseCode);
return resp;
}
protected int publishApiToWso2(String fileName, byte[] data, TokenResponse tokenResponse) {
int responseCode = -1;
try {
String url = config.getTarget().getPublisherApiUrl()
.concat(String.format("?preserveProvider=false&overwrite=true"));
log.info("API Import URL: " + url);
Map<String, String> httpHeaders = new HashMap<>();
httpHeaders.put("Authorization", "Bearer " + tokenResponse.getAccess_token());
HttpResponse response = makeFileRequest("POST", url, httpHeaders, data, fileName);
responseCode = response.getResponseCode();
if (response.getResponseCode() != 201 && response.getResponseCode() != 200) {
log.info("Cannot import API file: " + fileName + ", response code: " + response.getResponseCode());
}
} catch (Exception e) {
log.error("IO error while importing API file: " + fileName + ", error: " + e.getMessage(), e);
}
return responseCode;
}
protected int publishAppToWso2(String fileName, byte[] data, TokenResponse tokenResponse) {
int responseCode = -1;
try {
String url = config.getTarget().getDevPortalApiUrl().concat(String.format(
"/v3/applications/import?preserveOwner=true&skipSubscriptions=false&skipApplicationKeys=false&update=true"));
log.info("App Import URL: " + url);
Map<String, String> httpHeaders = new HashMap<>();
httpHeaders.put("Authorization", "Bearer " + tokenResponse.getAccess_token());
HttpResponse response = makeFileRequest("POST", url, httpHeaders, data, fileName);
responseCode = response.getResponseCode();
if (response.getResponseCode() < 200 || response.getResponseCode() > 299) {
log.info("Cannot import App file: " + fileName + ", response code: " + response.getResponseCode());
}
} catch (Exception e) {
log.error("IO error while importing App file: " + fileName + ", error: " + e.getMessage(), e);
}
return responseCode;
}
protected void setArtifactMetaData(ArtifactMetaData meta, String name, String description, Map<String, String> props) {
EditableMetaData metaData = new EditableMetaData();
metaData.setName(name);
metaData.setDescription(description);
if (props != null)
metaData.setProperties(props);
client.updateArtifactMetaData(meta.getGroupId(), meta.getId(), metaData);
}
protected void createRule(ArtifactMetaData meta, String config, RuleType type) {
Rule rule = new Rule();
rule.setConfig(config);
rule.setType(type);
client.createArtifactRule(meta.getGroupId(), meta.getId(), rule);
}
protected Map<String, String> createBearerAuthHeaders(TokenResponse tokenResponse) {
Map<String, String> httpHeaders = new HashMap<>();
httpHeaders.put("Authorization", "Bearer ".concat(tokenResponse.getAccess_token()));
return httpHeaders;
}
protected Map<String, String> createBasicAuthHeaders(String wso2User) throws Exception {
Map<String, String> httpHeaders = new HashMap<>();
byte[] decoded = Base64.getDecoder().decode(wso2User);
String decodedstring = new String(decoded);
String[] decodedstringparts = decodedstring.split(":");
String username = decodedstringparts[0];
String password = decodedstringparts[1];
httpHeaders.put("Authorization",
"Basic ".concat(Base64.getEncoder().encodeToString(username.concat(":").concat(password).getBytes())));
return httpHeaders;
}
protected void addEndpointsToProps(Map<String, String> props, Map<String, Object> apiMap) {
if (apiMap == null || !apiMap.containsKey("endpointURLs"))
return;
@SuppressWarnings("unchecked")
List<Map<String, Object>> envs = (List<Map<String, Object>>) apiMap.get("endpointURLs");
for (Map<String, Object> env : envs) {
@SuppressWarnings("unchecked")
Map<String, String> urls = (Map<String, String>) env.get("URLs");
if (urls == null)
continue;
urls.forEach((k, v) -> {
if (v != null)
props.put(k + " Endpoint", v);
});
}
}
protected void addTagsToProps(Map<String, String> props, List<String> tags) {
if (tags != null && !tags.isEmpty()) {
props.put("tags", String.join(", ", tags));
}
}
protected List<ArtifactReference> createReferencesFromZip(List<ZipEntryData> zipEntries, APIInfo api)
throws IOException {
List<ArtifactReference> references = new ArrayList<>();
for (ZipEntryData entry : zipEntries) {
String artifactId = convertToAscii(api.getName() + "/" + api.getVersion() + "/" + entry.getName());
log.debug("Creating artifact reference for entry: {} with artifactId: {}", entry.getName(), artifactId);
File tmpFile = new File("tmp/api/", entry.getName());
FileOutputStream fos = new FileOutputStream(tmpFile);
fos.write(entry.getContent());
fos.flush();
fos.close();
try (ByteArrayInputStream is = new ByteArrayInputStream(entry.getContent())) {
ArtifactMetaData meta = client.createArtifactWithVersion(entry.getType().toString(), artifactId, api.getVersion(), is);
Map<String, String> props = new LinkedHashMap<>();
props.put(PARAM_SOURCE_APIM, VERSION_32);
setArtifactMetaData(meta, entry.getName(), null, props);
}
ArtifactReference ref = new ArtifactReference();
ref.setName(entry.getName());
ref.setGroupId(entry.getType().toString());
ref.setArtifactId(artifactId);
ref.setVersion(api.getVersion());
references.add(ref);
}
return references;
}
private String convertToAscii(String input) {
if (input == null) {
return null;
}
String normalized = Normalizer.normalize(input, Normalizer.Form.NFD);
return normalized.replaceAll("[^\\x00-\\x7F]", "").replace(" ", "_");
}
}