Skip to content

Commit c8f61e2

Browse files
committed
Configure RestTemplates to properly use http proxy authentication
1 parent 397d8cf commit c8f61e2

File tree

3 files changed

+148
-79
lines changed

3 files changed

+148
-79
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ dependencies {
1717
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
1818

1919
implementation group: 'org.springframework', name: 'spring-web', version: '5.3.9'
20+
implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13'
2021

2122
implementation 'com.fasterxml.jackson.core:jackson-core:2.12.4'
2223
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.12.4'

src/main/java/io/securecodebox/persistence/defectdojo/service/GenericDefectDojoService.java

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,19 @@
2525
import io.securecodebox.persistence.defectdojo.exceptions.DefectDojoLoopException;
2626
import io.securecodebox.persistence.defectdojo.models.DefectDojoModel;
2727
import io.securecodebox.persistence.defectdojo.models.DefectDojoResponse;
28+
import org.apache.http.HttpHost;
29+
import org.apache.http.auth.AuthScope;
30+
import org.apache.http.auth.UsernamePasswordCredentials;
31+
import org.apache.http.client.CredentialsProvider;
32+
import org.apache.http.impl.client.BasicCredentialsProvider;
33+
import org.apache.http.impl.client.CloseableHttpClient;
34+
import org.apache.http.impl.client.HttpClientBuilder;
35+
import org.apache.http.impl.client.ProxyAuthenticationStrategy;
2836
import org.springframework.http.HttpEntity;
2937
import org.springframework.http.HttpHeaders;
3038
import org.springframework.http.HttpMethod;
3139
import org.springframework.http.ResponseEntity;
40+
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
3241
import org.springframework.util.LinkedMultiValueMap;
3342
import org.springframework.web.client.RestTemplate;
3443
import org.springframework.web.util.UriComponentsBuilder;
@@ -77,14 +86,39 @@ private HttpHeaders getDefectDojoAuthorizationHeaders() {
7786
return headers;
7887
}
7988

89+
protected RestTemplate getRestTemplate() {
90+
if (System.getProperty("http.proxyUser") != null && System.getProperty("http.proxyPassword") != null) {
91+
// Configuring Proxy Authentication explicitly as it isn't done by default for spring rest templates :(
92+
CredentialsProvider credsProvider = new BasicCredentialsProvider();
93+
credsProvider.setCredentials(
94+
new AuthScope(System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort"))),
95+
new UsernamePasswordCredentials(System.getProperty("http.proxyUser"), System.getProperty("http.proxyPassword"))
96+
);
97+
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
98+
99+
clientBuilder.useSystemProperties();
100+
clientBuilder.setProxy(new HttpHost(System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort"))));
101+
clientBuilder.setDefaultCredentialsProvider(credsProvider);
102+
clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
103+
104+
CloseableHttpClient client = clientBuilder.build();
105+
106+
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
107+
factory.setHttpClient(client);
108+
return new RestTemplate(factory);
109+
} else {
110+
return new RestTemplate();
111+
}
112+
}
113+
80114
protected abstract String getUrlPath();
81115

82116
protected abstract Class<T> getModelClass();
83117

84118
protected abstract DefectDojoResponse<T> deserializeList(String response) throws JsonProcessingException;
85119

86120
public T get(long id) {
87-
RestTemplate restTemplate = new RestTemplate();
121+
var restTemplate = this.getRestTemplate();
88122
HttpEntity<String> payload = new HttpEntity<>(getDefectDojoAuthorizationHeaders());
89123

90124
ResponseEntity<T> response = restTemplate.exchange(
@@ -98,7 +132,7 @@ public T get(long id) {
98132
}
99133

100134
protected DefectDojoResponse<T> internalSearch(Map<String, Object> queryParams, long limit, long offset) throws JsonProcessingException, URISyntaxException {
101-
RestTemplate restTemplate = new RestTemplate();
135+
var restTemplate = this.getRestTemplate();
102136
HttpEntity<String> payload = new HttpEntity<>(getDefectDojoAuthorizationHeaders());
103137

104138
var mutableQueryParams = new HashMap<String, Object>(queryParams);
@@ -135,7 +169,7 @@ public List<T> search(Map<String, Object> queryParams) throws URISyntaxException
135169

136170
hasNext = response.getNext() != null;
137171
if (page > this.defectDojoConfig.getMaxPageCountForGets()) {
138-
throw new DefectDojoLoopException("Found too many response object. Quitting after " + (page - 1) + " paginated API pages of " + DEFECT_DOJO_OBJET_LIMIT + " each.");
172+
throw new DefectDojoLoopException("Found too many response object. Quitting after " + (page - 1) + " paginated API pages of " + DEFECT_DOJO_OBJET_LIMIT + " each.");
139173
}
140174
} while (hasNext);
141175

@@ -166,22 +200,22 @@ public Optional<T> searchUnique(Map<String, Object> queryParams) throws URISynta
166200
}
167201

168202
public T create(T object) {
169-
RestTemplate restTemplate = new RestTemplate();
203+
var restTemplate = this.getRestTemplate();
170204
HttpEntity<T> payload = new HttpEntity<T>(object, getDefectDojoAuthorizationHeaders());
171205

172206
ResponseEntity<T> response = restTemplate.exchange(this.defectDojoConfig.getUrl() + "/api/v2/" + getUrlPath() + "/", HttpMethod.POST, payload, getModelClass());
173207
return response.getBody();
174208
}
175209

176210
public void delete(long id) {
177-
RestTemplate restTemplate = new RestTemplate();
211+
var restTemplate = this.getRestTemplate();
178212
HttpEntity<String> payload = new HttpEntity<>(getDefectDojoAuthorizationHeaders());
179213

180214
restTemplate.exchange(this.defectDojoConfig.getUrl() + "/api/v2/" + getUrlPath() + "/" + id + "/", HttpMethod.DELETE, payload, String.class);
181215
}
182216

183217
public T update(T object, long objectId) {
184-
RestTemplate restTemplate = new RestTemplate();
218+
var restTemplate = this.getRestTemplate();
185219
HttpEntity<T> payload = new HttpEntity<T>(object, getDefectDojoAuthorizationHeaders());
186220

187221
ResponseEntity<T> response = restTemplate.exchange(this.defectDojoConfig.getUrl() + "/api/v2/" + getUrlPath() + "/" + objectId + "/", HttpMethod.PUT, payload, getModelClass());

src/main/java/io/securecodebox/persistence/defectdojo/service/ImportScanService.java

Lines changed: 107 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,20 @@
2323
import io.securecodebox.persistence.defectdojo.exceptions.DefectDojoPersistenceException;
2424
import io.securecodebox.persistence.defectdojo.models.ScanFile;
2525
import lombok.Data;
26+
import org.apache.http.HttpHost;
27+
import org.apache.http.auth.AuthScope;
28+
import org.apache.http.auth.UsernamePasswordCredentials;
29+
import org.apache.http.client.CredentialsProvider;
30+
import org.apache.http.impl.client.BasicCredentialsProvider;
31+
import org.apache.http.impl.client.CloseableHttpClient;
32+
import org.apache.http.impl.client.HttpClientBuilder;
33+
import org.apache.http.impl.client.ProxyAuthenticationStrategy;
2634
import org.springframework.core.io.ByteArrayResource;
2735
import org.springframework.http.HttpEntity;
2836
import org.springframework.http.HttpHeaders;
2937
import org.springframework.http.HttpMethod;
3038
import org.springframework.http.MediaType;
39+
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
3140
import org.springframework.http.converter.FormHttpMessageConverter;
3241
import org.springframework.http.converter.ResourceHttpMessageConverter;
3342
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@@ -41,91 +50,116 @@
4150

4251
public class ImportScanService {
4352

44-
protected String defectDojoUrl;
45-
protected String defectDojoApiKey;
46-
47-
public ImportScanService(DefectDojoConfig config){
48-
this.defectDojoUrl = config.getUrl();
49-
this.defectDojoApiKey = config.getApiKey();
50-
}
51-
52-
/**
53-
* @return The DefectDojo Authentication Header
54-
*/
55-
private HttpHeaders getDefectDojoAuthorizationHeaders() {
56-
HttpHeaders headers = new HttpHeaders();
57-
headers.set("Authorization", "Token " + defectDojoApiKey);
58-
return headers;
59-
}
60-
61-
/**
62-
* Before version 1.5.4. testName (in DefectDojo _test_type_) must be defectDojoScanName, afterwards, you can have somethings else
63-
*/
64-
protected ImportScanResponse createFindings(ScanFile scanFile, String endpoint, long lead, String currentDate, ScanType scanType, long testType, MultiValueMap<String, Object> options) {
65-
RestTemplate restTemplate = new RestTemplate();
66-
HttpHeaders headers = getDefectDojoAuthorizationHeaders();
67-
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
68-
restTemplate.setMessageConverters(List.of(
69-
new FormHttpMessageConverter(),
70-
new ResourceHttpMessageConverter(),
71-
new MappingJackson2HttpMessageConverter())
72-
);
73-
74-
MultiValueMap<String, Object> mvn = new LinkedMultiValueMap<>();
75-
76-
mvn.add("lead", Long.toString(lead));
77-
mvn.add("scan_date", currentDate);
78-
mvn.add("scan_type", scanType.getTestType());
79-
mvn.add("close_old_findings", "true");
80-
mvn.add("skip_duplicates", "false");
81-
mvn.add("test_type", String.valueOf(testType));
82-
83-
for (String theKey : options.keySet()) {
84-
mvn.remove(theKey);
53+
protected String defectDojoUrl;
54+
protected String defectDojoApiKey;
55+
56+
public ImportScanService(DefectDojoConfig config) {
57+
this.defectDojoUrl = config.getUrl();
58+
this.defectDojoApiKey = config.getApiKey();
59+
}
60+
61+
/**
62+
* @return The DefectDojo Authentication Header
63+
*/
64+
private HttpHeaders getDefectDojoAuthorizationHeaders() {
65+
HttpHeaders headers = new HttpHeaders();
66+
headers.set("Authorization", "Token " + defectDojoApiKey);
67+
return headers;
68+
}
69+
70+
protected RestTemplate getRestTemplate() {
71+
if (System.getProperty("http.proxyUser") != null && System.getProperty("http.proxyPassword") != null) {
72+
// Configuring Proxy Authentication explicitly as it isn't done by default for spring rest templates :(
73+
CredentialsProvider credsProvider = new BasicCredentialsProvider();
74+
credsProvider.setCredentials(
75+
new AuthScope(System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort"))),
76+
new UsernamePasswordCredentials(System.getProperty("http.proxyUser"), System.getProperty("http.proxyPassword"))
77+
);
78+
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
79+
80+
clientBuilder.useSystemProperties();
81+
clientBuilder.setProxy(new HttpHost(System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort"))));
82+
clientBuilder.setDefaultCredentialsProvider(credsProvider);
83+
clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
84+
85+
CloseableHttpClient client = clientBuilder.build();
86+
87+
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
88+
factory.setHttpClient(client);
89+
return new RestTemplate(factory);
90+
} else {
91+
return new RestTemplate();
92+
}
8593
}
86-
mvn.addAll(options);
8794

88-
try {
89-
ByteArrayResource contentsAsResource = new ByteArrayResource(scanFile.getContent().getBytes(StandardCharsets.UTF_8)) {
90-
@Override
91-
public String getFilename() {
92-
return scanFile.getName();
95+
/**
96+
* Before version 1.5.4. testName (in DefectDojo _test_type_) must be defectDojoScanName, afterwards, you can have somethings else
97+
*/
98+
protected ImportScanResponse createFindings(ScanFile scanFile, String endpoint, long lead, String currentDate, ScanType scanType, long testType, MultiValueMap<String, Object> options) {
99+
var restTemplate = this.getRestTemplate();
100+
HttpHeaders headers = getDefectDojoAuthorizationHeaders();
101+
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
102+
restTemplate.setMessageConverters(List.of(
103+
new FormHttpMessageConverter(),
104+
new ResourceHttpMessageConverter(),
105+
new MappingJackson2HttpMessageConverter())
106+
);
107+
108+
MultiValueMap<String, Object> mvn = new LinkedMultiValueMap<>();
109+
110+
mvn.add("lead", Long.toString(lead));
111+
mvn.add("scan_date", currentDate);
112+
mvn.add("scan_type", scanType.getTestType());
113+
mvn.add("close_old_findings", "true");
114+
mvn.add("skip_duplicates", "false");
115+
mvn.add("test_type", String.valueOf(testType));
116+
117+
for (String theKey : options.keySet()) {
118+
mvn.remove(theKey);
93119
}
94-
};
120+
mvn.addAll(options);
95121

96-
mvn.add("file", contentsAsResource);
122+
try {
123+
ByteArrayResource contentsAsResource = new ByteArrayResource(scanFile.getContent().getBytes(StandardCharsets.UTF_8)) {
124+
@Override
125+
public String getFilename() {
126+
return scanFile.getName();
127+
}
128+
};
97129

98-
var payload = new HttpEntity<>(mvn, headers);
130+
mvn.add("file", contentsAsResource);
99131

100-
return restTemplate.exchange(defectDojoUrl + "/api/v2/" + endpoint + "/", HttpMethod.POST, payload, ImportScanResponse.class).getBody();
101-
} catch (HttpClientErrorException e) {
102-
throw new DefectDojoPersistenceException("Failed to attach findings to engagement.");
132+
var payload = new HttpEntity<>(mvn, headers);
133+
134+
return restTemplate.exchange(defectDojoUrl + "/api/v2/" + endpoint + "/", HttpMethod.POST, payload, ImportScanResponse.class).getBody();
135+
} catch (HttpClientErrorException e) {
136+
throw new DefectDojoPersistenceException("Failed to attach findings to engagement.");
137+
}
103138
}
104-
}
105139

106-
public ImportScanResponse importScan(ScanFile scanFile, long engagementId, long lead, String currentDate, ScanType scanType, long testType) {
107-
var additionalValues = new LinkedMultiValueMap<String, Object>();
108-
additionalValues.add("engagement", Long.toString(engagementId));
140+
public ImportScanResponse importScan(ScanFile scanFile, long engagementId, long lead, String currentDate, ScanType scanType, long testType) {
141+
var additionalValues = new LinkedMultiValueMap<String, Object>();
142+
additionalValues.add("engagement", Long.toString(engagementId));
109143

110-
return this.createFindings(scanFile, "import-scan", lead, currentDate, scanType, testType, additionalValues);
111-
}
144+
return this.createFindings(scanFile, "import-scan", lead, currentDate, scanType, testType, additionalValues);
145+
}
112146

113-
public ImportScanResponse reimportScan(ScanFile scanFile, long testId, long lead, String currentDate, ScanType scanType, long testType) {
114-
var additionalValues = new LinkedMultiValueMap<String, Object>();
115-
additionalValues.add("test", Long.toString(testId));
147+
public ImportScanResponse reimportScan(ScanFile scanFile, long testId, long lead, String currentDate, ScanType scanType, long testType) {
148+
var additionalValues = new LinkedMultiValueMap<String, Object>();
149+
additionalValues.add("test", Long.toString(testId));
116150

117-
return this.createFindings(scanFile, "reimport-scan", lead, currentDate, scanType, testType, additionalValues);
118-
}
151+
return this.createFindings(scanFile, "reimport-scan", lead, currentDate, scanType, testType, additionalValues);
152+
}
119153

120-
@Data
121-
public static class ImportScanResponse {
122-
@JsonProperty
123-
protected Boolean verified;
154+
@Data
155+
public static class ImportScanResponse {
156+
@JsonProperty
157+
protected Boolean verified;
124158

125-
@JsonProperty
126-
protected Boolean active;
159+
@JsonProperty
160+
protected Boolean active;
127161

128-
@JsonProperty("test")
129-
protected long testId;
130-
}
162+
@JsonProperty("test")
163+
protected long testId;
164+
}
131165
}

0 commit comments

Comments
 (0)