Skip to content
Draft
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
1 change: 1 addition & 0 deletions .mvn/maven.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-T 1C
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
package org.opendevstack.apiservice.externalservice.aap.config;

import lombok.extern.slf4j.Slf4j;
import org.opendevstack.apiservice.externalservice.api.http.RestClientFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import org.springframework.web.client.RestClient;

/**
* Configuration class for external service components.
* Configuration class for the Ansible Automation Platform external service.
*
* <p>SSL wiring is delegated to {@link RestClientFactory} in {@code external-service-api};
* no SSL boilerplate lives here.
*/
@Configuration
@EnableAsync
Expand All @@ -33,85 +24,25 @@ public class ExternalServiceConfig {

private final SslProperties sslProperties;

@Value("${automation.platform.ansible.timeout:30000}")
private int timeoutMs;

public ExternalServiceConfig(@Qualifier("aapSslProperties") SslProperties sslProperties) {
this.sslProperties = sslProperties;
}

/**
* Creates a RestTemplate bean for HTTP client operations with configurable SSL settings.
* {@link RestClient} bean for the Ansible Automation Platform.
*
* <p>SSL and timeouts are configured via {@code automation.platform.ansible.ssl.*}
* and {@code automation.platform.ansible.timeout} properties respectively.
*
* @return RestTemplate instance with SSL configuration
* @param builder Spring prototype builder (injected fresh per bean definition)
* @return configured {@link RestClient}
*/
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
if (!sslProperties.isVerifyCertificates()) {
log.warn("SSL certificate verification is DISABLED - this should only be used in development environments");
return createInsecureRestTemplate();
} else {
log.info("SSL certificate verification is ENABLED");
return createSecureRestTemplate();
}
}

private RestTemplate createInsecureRestTemplate() {
try {
// Create a trust manager that accepts all certificates
// WARNING: This is insecure and should only be used in development environments
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0]; // Return empty array instead of null
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// Intentionally empty - accepts all client certificates (insecure)
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// Intentionally empty - accepts all server certificates (insecure)
}
}
};

// Install the all-trusting trust manager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

// Create hostname verifier that accepts all hostnames (insecure)
HostnameVerifier allHostsValid = (hostname, session) -> true;

// Create a custom request factory that uses our SSL configuration
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory() {
@Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
if (connection instanceof HttpsURLConnection httpsConnection) {
httpsConnection.setSSLSocketFactory(sslContext.getSocketFactory());
httpsConnection.setHostnameVerifier(allHostsValid);
}
super.prepareConnection(connection, httpMethod);
}
};

return new RestTemplate(requestFactory);

} catch (GeneralSecurityException e) {
log.warn("Failed to create insecure RestTemplate, falling back to default: {}", e.getMessage());
return new RestTemplate();
}
}

private RestTemplate createSecureRestTemplate() {
try {
// If custom trust store is provided, configure it
if (StringUtils.hasText(sslProperties.getTrustStorePath())) {
log.info("Custom trust store specified: {} (custom trust store support can be added in future versions)",
sslProperties.getTrustStorePath());
}

// Return default RestTemplate with system SSL settings
return new RestTemplate();

} catch (Exception e) {
log.warn("Failed to create secure RestTemplate with custom trust store, using default: {}", e.getMessage());
return new RestTemplate();
}
public RestClient aapRestClient(RestClient.Builder builder) {
log.info("Creating AAP RestClient (connect/read timeout={}ms)", timeoutMs);
return RestClientFactory.build(builder, sslProperties, timeoutMs, timeoutMs);
}
}
Original file line number Diff line number Diff line change
@@ -1,67 +1,16 @@
package org.opendevstack.apiservice.externalservice.aap.config;

import org.opendevstack.apiservice.externalservice.api.http.ExternalServiceSslProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
* Configuration properties for SSL settings in external service calls.
* SSL configuration properties for the Ansible Automation Platform external service.
* Binds to the {@code automation.platform.ansible.ssl} prefix.
*/
@Component("aapSslProperties")
@ConfigurationProperties(prefix = "automation.platform.ansible.ssl")
public class SslProperties {

/**
* Whether to verify SSL certificates when making external service calls.
* Default is true for security.
*/
private boolean verifyCertificates = true;

/**
* Path to the trust store file for SSL certificate validation.
* Optional - if not provided, uses system default trust store.
*/
private String trustStorePath;

/**
* Password for the trust store.
*/
private String trustStorePassword;

/**
* Type of the trust store (JKS, PKCS12, etc.).
* Default is JKS.
*/
private String trustStoreType = "JKS";

public boolean isVerifyCertificates() {
return verifyCertificates;
}

public void setVerifyCertificates(boolean verifyCertificates) {
this.verifyCertificates = verifyCertificates;
}

public String getTrustStorePath() {
return trustStorePath;
}

public void setTrustStorePath(String trustStorePath) {
this.trustStorePath = trustStorePath;
}

public String getTrustStorePassword() {
return trustStorePassword;
}

public void setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
}

public String getTrustStoreType() {
return trustStoreType;
}

public void setTrustStoreType(String trustStoreType) {
this.trustStoreType = trustStoreType;
}
}
public class SslProperties extends ExternalServiceSslProperties {
// All fields inherited from ExternalServiceSslProperties.
// Add AAP-specific SSL overrides here if ever needed.
}
Loading
Loading