Skip to content

Commit 1e4f033

Browse files
committed
Merge remote-tracking branch 'origin/feature/http-proxy-auth'
2 parents cb2fdb5 + c8f61e2 commit 1e4f033

File tree

3 files changed

+158
-79
lines changed

3 files changed

+158
-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: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,26 @@
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;
3544

3645
import java.net.URI;
3746
import java.net.URISyntaxException;
47+
import java.nio.charset.StandardCharsets;
3848
import java.util.*;
3949

4050
abstract public class GenericDefectDojoService<T extends DefectDojoModel> {
@@ -64,17 +74,51 @@ public GenericDefectDojoService(DefectDojoConfig config) {
6474
private HttpHeaders getDefectDojoAuthorizationHeaders() {
6575
HttpHeaders headers = new HttpHeaders();
6676
headers.set("Authorization", "Token " + this.defectDojoConfig.getApiKey());
77+
78+
String username = System.getProperty("http.proxyUser", "");
79+
String password = System.getProperty("http.proxyPassword", "");
80+
81+
if (!username.isEmpty() || !password.isEmpty()) {
82+
System.out.println("Setting Proxy Auth Header...");
83+
headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString((username + ':' + password).getBytes(StandardCharsets.UTF_8)));
84+
}
85+
6786
return headers;
6887
}
6988

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+
70114
protected abstract String getUrlPath();
71115

72116
protected abstract Class<T> getModelClass();
73117

74118
protected abstract DefectDojoResponse<T> deserializeList(String response) throws JsonProcessingException;
75119

76120
public T get(long id) {
77-
RestTemplate restTemplate = new RestTemplate();
121+
var restTemplate = this.getRestTemplate();
78122
HttpEntity<String> payload = new HttpEntity<>(getDefectDojoAuthorizationHeaders());
79123

80124
ResponseEntity<T> response = restTemplate.exchange(
@@ -88,7 +132,7 @@ public T get(long id) {
88132
}
89133

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

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

126170
hasNext = response.getNext() != null;
127171
if (page > this.defectDojoConfig.getMaxPageCountForGets()) {
128-
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.");
129173
}
130174
} while (hasNext);
131175

@@ -156,22 +200,22 @@ public Optional<T> searchUnique(Map<String, Object> queryParams) throws URISynta
156200
}
157201

158202
public T create(T object) {
159-
RestTemplate restTemplate = new RestTemplate();
203+
var restTemplate = this.getRestTemplate();
160204
HttpEntity<T> payload = new HttpEntity<T>(object, getDefectDojoAuthorizationHeaders());
161205

162206
ResponseEntity<T> response = restTemplate.exchange(this.defectDojoConfig.getUrl() + "/api/v2/" + getUrlPath() + "/", HttpMethod.POST, payload, getModelClass());
163207
return response.getBody();
164208
}
165209

166210
public void delete(long id) {
167-
RestTemplate restTemplate = new RestTemplate();
211+
var restTemplate = this.getRestTemplate();
168212
HttpEntity<String> payload = new HttpEntity<>(getDefectDojoAuthorizationHeaders());
169213

170214
restTemplate.exchange(this.defectDojoConfig.getUrl() + "/api/v2/" + getUrlPath() + "/" + id + "/", HttpMethod.DELETE, payload, String.class);
171215
}
172216

173217
public T update(T object, long objectId) {
174-
RestTemplate restTemplate = new RestTemplate();
218+
var restTemplate = this.getRestTemplate();
175219
HttpEntity<T> payload = new HttpEntity<T>(object, getDefectDojoAuthorizationHeaders());
176220

177221
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)