Skip to content

Commit

Permalink
Changed behavior of java api client to avoid 401 problems #2553
Browse files Browse the repository at this point in the history
- created unit test to reproduce problem (wire mock test)
- changed login behavior, instead of using a authenticator object
  we use now a request interceptor and set the basic auth header
  directly
- removed server url and trust all setters
- introduced new constructor inside abstract SecHub client to have
  fields inside mocked client as well
  • Loading branch information
de-jcup committed Sep 21, 2023
1 parent 71740ba commit 142f9cc
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ public abstract class AbstractSecHubClient implements SecHubClient {

private Set<SecHubClientListener> secHubClientListeners;

public AbstractSecHubClient() {
secHubClientListeners = new LinkedHashSet<>();
public AbstractSecHubClient(URI serverUri, String username, String apiToken, boolean trustAll) {
this.serverUri = serverUri;
this.trustAll = trustAll;

this.secHubClientListeners = new LinkedHashSet<>();

setUsername(username);
setApiToken(apiToken);
}

public void setUsername(String username) {
Expand All @@ -30,14 +36,6 @@ public void setApiToken(String apiToken) {
this.sealedApiToken = apiTokenAccess.seal(apiToken);
}

public void setServerUri(URI serverUri) {
this.serverUri = serverUri;
}

public void setTrustAll(boolean trustAll) {
this.trustAll = trustAll;
}

@Override
public boolean isTrustAll() {
return trustAll;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ public DefaultSecHubClient(URI serverUri, String username, String apiToken) {
}

public DefaultSecHubClient(URI serverUri, String username, String apiToken, boolean trustAll) {
setUsername(username);
setApiToken(apiToken);
setServerUri(serverUri);
setTrustAll(trustAll);
super(serverUri, username, apiToken, trustAll);

apiClient = new ApiClientBuilder().createApiClient(this, mapper);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mercedesbenz.sechub.api;

import java.net.URI;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -33,6 +34,7 @@ public class MockedSecHubClient extends AbstractSecHubClient {
private Set<String> userToProjectAssignments = new HashSet<>();

public MockedSecHubClient() {
super(URI.create("https://localhost/mocked-sechub"), "mockuser", "pseudo-token", true);
mockDataAccess = new MockDataAccess();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.api.internal;

import java.net.Socket;
import java.net.http.HttpClient;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mercedesbenz.sechub.api.SecHubClient;
Expand All @@ -18,36 +21,30 @@
public class ApiClientBuilder {

public ApiClient createApiClient(SecHubClient client, ObjectMapper mapper) {
HttpClient.Builder builder = HttpClient.newBuilder().authenticator(new SecHubClientAuthenticator(client));
HttpClient.Builder builder = HttpClient.newBuilder();
if (client.isTrustAll()) {
builder.sslContext(createTrustAllSSLContext());
}

ApiClient apiClient = new ApiClient(builder, mapper, client.getServerUri().toString());
apiClient.setRequestInterceptor((request) -> {
request.setHeader("Authorization", createBasicAuthenticationHeader(client));
});
return apiClient;

}

private static final String createBasicAuthenticationHeader(SecHubClient client) {
String valueToEncode = client.getUsername() + ":" + client.getSealedApiToken();
return "Basic " + Base64.getEncoder().encodeToString(valueToEncode.getBytes());
}

private SSLContext createTrustAllSSLContext() {
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");

TrustManager trustManager = new X509TrustManager() {

private X509Certificate[] emptyCertificatesArray = new X509Certificate[] {};

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
/* we do not check the client - we trust all */
}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
/* we do not check the server - we trust all */
}

public X509Certificate[] getAcceptedIssuers() {
return emptyCertificatesArray;
}
};
TrustManager trustManager = new TrustAllManager();

sslContext.init(null, new TrustManager[] { trustManager }, null);

Expand All @@ -57,4 +54,42 @@ public X509Certificate[] getAcceptedIssuers() {
}

}

private class TrustAllManager extends X509ExtendedTrustManager {

private X509Certificate[] emptyCertificatesArray = new X509Certificate[] {};

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
/* we do not check - we trust all */
}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
/* we do not check - we trust all */
}

public X509Certificate[] getAcceptedIssuers() {
return emptyCertificatesArray;
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
/* we do not check - we trust all */
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
/* we do not check - we trust all */
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
/* we do not check - we trust all */

}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
/* we do not check - we trust all */
}
};
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.mercedesbenz.sechub.api;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
import static org.junit.Assert.*;

import java.net.URI;

import org.junit.Rule;
import org.junit.Test;

import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.mercedesbenz.sechub.test.TestPortProvider;

import wiremock.org.apache.http.HttpStatus;

/**
* Junit 4 test because of missing official WireMock Junit5 extension - so we
* use WireMock Rule and Junit4.
*
* @author Albert Tregnaghi
*
*/
public class DefaultSechubClientWireMockTest {

private static final String EXAMPLE_TOKEN = "example-token";

private static final String EXAMPLE_USER = "example-user";
private static final String APPLICATION_JSON = "application/json";

private static final int HTTPS_PORT = TestPortProvider.DEFAULT_INSTANCE.getWireMockTestHTTPSPort();

private static final int HTTP_PORT = TestPortProvider.DEFAULT_INSTANCE.getWireMockTestHTTPPort();

@Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().port(HTTP_PORT).httpsPort(HTTPS_PORT));

@Test
public void fetch_sechub_status_with_basic_auth() throws Exception {

/* prepare */
String statusBody = """
[ {
"key" : "status.scheduler.enabled",
"value" : "true"
}, {
"key" : "status.scheduler.jobs.all",
"value" : "2"
} ]
""";
stubFor(get(urlEqualTo("/api/admin/status")).withBasicAuth(EXAMPLE_USER, EXAMPLE_TOKEN)
.willReturn(aResponse().withStatus(HttpStatus.SC_OK).withHeader("Content-Type", APPLICATION_JSON).withBody(statusBody)));

DefaultSecHubClient client = createTestClientWithExampleCredentials();

/* execute */
SecHubStatus status = client.fetchSecHubStatus();

/* test */
verify(getRequestedFor(urlEqualTo("/api/admin/status")));
assertNotNull(status);
assertEquals("true", status.getStatusInformationMap().get("status.scheduler.enabled"));
assertEquals("2", status.getStatusInformationMap().get("status.scheduler.jobs.all"));

}

@Test
public void credential_changes_on_client_after_creation_are_possible() throws Exception {

/* prepare */
String statusBody = """
[ {
"key" : "status.scheduler.enabled",
"value" : "false"
}, {
"key" : "status.scheduler.jobs.all",
"value" : "0"
} ]
""";
stubFor(get(urlEqualTo("/api/admin/status")).withBasicAuth("other-user", "other-token")
.willReturn(aResponse().withStatus(HttpStatus.SC_OK).withHeader("Content-Type", APPLICATION_JSON).withBody(statusBody)));

DefaultSecHubClient client = createTestClientWithExampleCredentials();

/* execute */
client.setApiToken("other-token");
client.setUsername("other-user");

/* test */
SecHubStatus status = client.fetchSecHubStatus();
verify(getRequestedFor(urlEqualTo("/api/admin/status")));

assertEquals("false", status.getStatusInformationMap().get("status.scheduler.enabled"));
assertEquals("0", status.getStatusInformationMap().get("status.scheduler.jobs.all"));
}

private DefaultSecHubClient createTestClientWithExampleCredentials() {
DefaultSecHubClient client = new DefaultSecHubClient(URI.create(wireMockRule.baseUrl()), EXAMPLE_USER, EXAMPLE_TOKEN, true);
return client;
}
}

0 comments on commit 142f9cc

Please sign in to comment.