com.cx.restclient.httpClient.CxHttpClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cx-client-common Show documentation
Show all versions of cx-client-common Show documentation
Web client for interaction with Checkmarx SAST, SCA and OSA products
The newest version!
package com.cx.restclient.httpClient;
import com.cx.restclient.common.ErrorMessage;
import com.cx.restclient.dto.LoginSettings;
import com.cx.restclient.dto.ProxyConfig;
import com.cx.restclient.dto.TokenLoginResponse;
import com.cx.restclient.exception.CxClientException;
import com.cx.restclient.exception.CxHTTPClientException;
import com.cx.restclient.exception.CxTokenExpiredException;
import com.cx.restclient.osa.dto.ClientType;
import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.params.AuthPolicy;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.NoConnectionReuseStrategy;
import org.apache.http.impl.auth.BasicSchemeFactory;
import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.win.WindowsCredentialsProvider;
import org.apache.http.impl.auth.win.WindowsNTLMSchemeFactory;
import org.apache.http.impl.auth.win.WindowsNegotiateSchemeFactory;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import javax.annotation.Nullable;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.Closeable;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.IDN;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import static com.cx.restclient.common.CxPARAM.*;
import static com.cx.restclient.httpClient.utils.ContentType.CONTENT_TYPE_APPLICATION_JSON;
import static com.cx.restclient.httpClient.utils.HttpClientHelper.*;
/**
* Created by Galn on 05/02/2018.
*/
public class CxHttpClient implements Closeable {
private static String HTTP_NO_HOST = System.getProperty("http.nonProxyHosts");
private static String HTTPS_NO_HOST = System.getProperty("https.nonProxyHosts");
private static final String HTTPS = "https";
private static final String LOGIN_FAILED_MSG = "Fail to login with windows authentication: ";
private static final String DEFAULT_GRANT_TYPE = "password";
private static final String LOCATION_HEADER = "Location";
private static final String AUTH_MESSAGE = "authenticate";
private static final String CLIENT_SECRET_PROP = "client_secret";
public static final String REFRESH_TOKEN_PROP = "refresh_token";
private static final String PASSWORD_PROP = "password";
public static final String CLIENT_ID_PROP = "client_id";
private static final String KEY_USER = "user";
private static final String KEY_DOMAIN = "domain";
private HttpClient apacheClient;
private Logger log;
private TokenLoginResponse token;
private String rootUri;
private final String refreshToken;
private String cxOrigin;
private String cxOriginUrl;
private Boolean useSSo;
private Boolean useNTLM;
private LoginSettings lastLoginSettings;
private String teamPath;
private CookieStore cookieStore = new BasicCookieStore();
private HttpClientBuilder cb = HttpClients.custom();
private final Map customHeaders = new HashMap<>();
public CxHttpClient(String rootUri, String origin, boolean disableSSLValidation, boolean isSSO, String refreshToken,
boolean isProxy, @Nullable ProxyConfig proxyConfig, Logger log, Boolean useNTLM) throws CxClientException {
this.log = log;
this.rootUri = rootUri;
this.refreshToken = refreshToken;
this.cxOrigin = origin;
this.useSSo = isSSO;
this.useNTLM = useNTLM;
//create httpclient
cb.setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build());
setSSLTls("TLSv1.2", log);
SSLContextBuilder builder = new SSLContextBuilder();
SSLConnectionSocketFactory sslConnectionSocketFactory = null;
Registry registry;
PoolingHttpClientConnectionManager cm = null;
if (disableSSLValidation) {
try {
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
registry = RegistryBuilder.create()
.register("http", new PlainConnectionSocketFactory())
.register(HTTPS, sslConnectionSocketFactory)
.build();
cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(100);
} catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
log.error(e.getMessage());
}
cb.setSSLSocketFactory(sslConnectionSocketFactory);
cb.setConnectionManager(cm);
} else {
String customTrustStore = System.getProperty("javax.net.ssl.trustStore");
if(!StringUtils.isEmpty(customTrustStore))
this.log.info("Custom truststore is configured. Ensure that trusted certificate for all CxSAST/CxSCA endpoints are imported. Custom store path: " + customTrustStore );
cb.setConnectionManager(getHttpConnectionManager(false));
}
cb.setConnectionManagerShared(true);
if (isProxy) {
if (!setCustomProxy(cb, proxyConfig, log)) {
cb.useSystemProperties();
}
}
if (Boolean.TRUE.equals(useSSo)) {
cb.setDefaultCredentialsProvider(new WindowsCredentialsProvider(new SystemDefaultCredentialsProvider()));
cb.setDefaultCookieStore(cookieStore);
} else {
cb.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
}
cb.setDefaultAuthSchemeRegistry(getAuthSchemeProviderRegistry());
if (useNTLM) {
setNTLMProxy(proxyConfig, cb, log);
} else apacheClient = cb.build();
}
public CxHttpClient(String rootUri, String origin, String originUrl, boolean disableSSLValidation, boolean isSSO, String refreshToken,
boolean isProxy, @Nullable ProxyConfig proxyConfig, Logger log, Boolean useNTLM) throws CxClientException {
this(rootUri, origin, disableSSLValidation, isSSO, refreshToken, isProxy, proxyConfig, log, useNTLM);
this.cxOriginUrl = originUrl;
}
public void setRootUri(String rootUri) {
this.rootUri = rootUri;
}
public String getRootUri() {
return rootUri;
}
private void setNTLMProxy(ProxyConfig proxyConfig, HttpClientBuilder cb, Logger log) {
if (proxyConfig == null ||
StringUtils.isEmpty(proxyConfig.getHost()) ||
proxyConfig.getPort() == 0) {
log.info("Proxy configuration not provided.");
apacheClient = cb.build();
return;
}
log.info("Setting NTLM proxy for Checkmarx http client");
HttpHost proxy = new HttpHost(proxyConfig.getHost(), proxyConfig.getPort(), "http");
HashMap userDomainMap = splitDomainAndTheUserName(proxyConfig.getUsername());
String user = userDomainMap.get(KEY_USER);
String domain = userDomainMap.get(KEY_DOMAIN);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
final NTCredentials credentials = new NTCredentials(user, proxyConfig.getPassword(), null, domain);
credsProvider.setCredentials(AuthScope.ANY, credentials);
apacheClient = HttpClientBuilder.create()
.setDefaultCredentialsProvider(credsProvider)
.setProxy(proxy)
.setProxyAuthenticationStrategy(ProxyAuthenticationStrategy.INSTANCE)
.setDefaultRequestConfig(RequestConfig.custom()
.setAuthenticationEnabled(true)
.setProxyPreferredAuthSchemes(Arrays.asList(AuthPolicy.NTLM))
.build()
)
.build();
}
private static HashMap splitDomainAndTheUserName(String userName) {
String domain = "";
String user = "";
// If the username has a backslash, then the domain is the first part and the username is the second part
if (userName.contains("\\")) {
String[] parts = userName.split("[\\\\]");
if (parts.length == 2) {
domain = parts[0];
user = parts[1];
}
} else if (userName.contains("/")) {
// If the username has a slash, then the domain is the first part and the username is the second part
String[] parts = userName.split("[/]");
if (parts.length == 2) {
domain = parts[0];
user = parts[1];
}
} else if (userName.contains("@")) {
// If the username has an asterisk, then the domain is the second part and the username is the first part
String[] parts = userName.split("[@]");
if (parts.length == 2) {
user = parts[0];
domain = parts[1];
}
}
HashMap userDomain = new HashMap();
userDomain.put(KEY_USER, user);
userDomain.put(KEY_DOMAIN, domain);
return userDomain;
}
private static boolean setCustomProxy(HttpClientBuilder cb, ProxyConfig proxyConfig, Logger logi) {
if (proxyConfig == null ||
StringUtils.isEmpty(proxyConfig.getHost()) ||
proxyConfig.getPort() == 0) {
return false;
}
String scheme = proxyConfig.isUseHttps() ? HTTPS : "http";
HttpHost proxy = new HttpHost(proxyConfig.getHost(), proxyConfig.getPort(), scheme);
if (StringUtils.isNotEmpty(proxyConfig.getUsername()) &&
StringUtils.isNotEmpty(proxyConfig.getPassword())) {
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(proxyConfig.getUsername(), proxyConfig.getPassword());
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(proxy), credentials);
cb.setDefaultCredentialsProvider(credsProvider);
}
logi.info("Setting proxy for Checkmarx http client");
cb.setProxy(proxy);
cb.setRoutePlanner(getRoutePlanner(proxyConfig, proxy, logi));
cb.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
return true;
}
private static DefaultProxyRoutePlanner getRoutePlanner(ProxyConfig proxyConfig, HttpHost proxyHost, Logger logi) {
return new DefaultProxyRoutePlanner(proxyHost) {
public HttpRoute determineRoute(
final HttpHost host,
final HttpRequest request,
final HttpContext context) throws HttpException {
String hostname = host.getHostName();
String noHost = proxyConfig.getNoproxyHosts(); // StringUtils.isNotEmpty(HTTP_NO_HOST) ? HTTP_NO_HOST : HTTPS_NO_HOST;
if (StringUtils.isNotEmpty(noHost)) {
String[] hosts = noHost.split("\\|");
for (String nonHost : hosts) {
try {
if (matchNonProxyHostWildCard(hostname, noHost)) {
logi.debug("Bypassing proxy as host " + hostname + " is found in the nonProxyHosts");
return new HttpRoute(host);
}
} catch (PatternSyntaxException e) {
logi.warn("Wrong nonProxyHost param: " + nonHost);
}
}
}
return super.determineRoute(host, request, context);
}
};
}
/*
* '*' is the only wildcard support in nonProxyHosts JVM argument.
* * in Java regex has different meaning than required here.
* Hence the custom logic
*/
private static boolean matchNonProxyHostWildCard(String sourceHost, String nonProxyHost) {
if (nonProxyHost.indexOf("*") > -1)
nonProxyHost = nonProxyHost.replaceAll("\\.", "\\\\.");
nonProxyHost = nonProxyHost.replaceAll("\\*", "\\.\\*");
Pattern p = Pattern.compile(nonProxyHost);//. represents single character
Matcher m = p.matcher(sourceHost);
return m.matches();
}
private static SSLConnectionSocketFactory getTrustAllSSLSocketFactory() {
TrustStrategy acceptingTrustStrategy = new TrustAllStrategy();
SSLContext sslContext;
try {
sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
} catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
throw new CxClientException("Fail to set trust all certificate, 'SSLConnectionSocketFactory'", e);
}
return new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
}
private static PoolingHttpClientConnectionManager getHttpConnectionManager(boolean disableSSLValidation) {
ConnectionSocketFactory factory;
if (disableSSLValidation) {
factory = getTrustAllSSLSocketFactory();
} else {
factory = new SSLConnectionSocketFactory(SSLContexts.createDefault(), NoopHostnameVerifier.INSTANCE);
}
Registry socketFactoryRegistry = RegistryBuilder.create()
.register(HTTPS, factory)
.register("http", new PlainConnectionSocketFactory())
.build();
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connManager.setMaxTotal(50);
connManager.setDefaultMaxPerRoute(5);
return connManager;
}
private static Registry getAuthSchemeProviderRegistry() {
return RegistryBuilder.create()
.register(AuthSchemes.DIGEST, new DigestSchemeFactory())
.register(AuthSchemes.BASIC, new BasicSchemeFactory())
.register(AuthSchemes.NTLM, new WindowsNTLMSchemeFactory(null))
.register(AuthSchemes.SPNEGO, new WindowsNegotiateSchemeFactory(null))
.build();
}
public void login(LoginSettings settings) throws IOException {
lastLoginSettings = settings;
if (!settings.getSessionCookies().isEmpty()) {
setSessionCookies(settings.getSessionCookies());
return;
}
if (settings.getRefreshToken() != null) {
token = getAccessTokenFromRefreshToken(settings);
} else if (Boolean.TRUE.equals(useSSo)) {
if (settings.getVersion().equals("lower than 9.0")) {
ssoLegacyLogin();
} else {
token = ssoLogin();
// Don't delete this print. VS Code plugin relies on CxCLI output to work properly.
// Also we don't want the token to appear in regular logs.
System.out.printf("Access Token: %s%n", token.getAccess_token()); // NOSONAR: we need standard output here.
}
} else {
token = generateToken(settings);
}
}
public ArrayList ssoLegacyLogin() {
HttpUriRequest request;
HttpResponse loginResponse = null;
try {
request = RequestBuilder.post()
.setUri(rootUri + "auth/ssologin")
.setConfig(RequestConfig.DEFAULT)
.setEntity(new StringEntity("", StandardCharsets.UTF_8))
.build();
loginResponse = apacheClient.execute(request);
} catch (IOException e) {
String message = LOGIN_FAILED_MSG + e.getMessage();
log.error(message);
throw new CxClientException(message);
} finally {
HttpClientUtils.closeQuietly(loginResponse);
}
setSessionCookies(cookieStore.getCookies());
//return cookies clone - for IDE's usage
return new ArrayList<>(cookieStore.getCookies());
}
private void setSessionCookies(List cookies) {
String cxCookie = null;
String csrfToken = null;
for (Cookie cookie : cookies) {
if (cookie.getName().equals(CSRF_TOKEN_HEADER)) {
csrfToken = cookie.getValue();
}
if (cookie.getName().equals("cxCookie")) {
cxCookie = cookie.getValue();
}
}
List headers = new ArrayList<>();
headers.add(new BasicHeader(CSRF_TOKEN_HEADER, csrfToken));
headers.add(new BasicHeader("cookie", String.format("CXCSRFToken=%s; cxCookie=%s", csrfToken, cxCookie)));
// Don't delete these prints, they are being used on VS Code plugin
System.out.println(CSRF_TOKEN_HEADER + ": " + csrfToken);
System.out.printf("cookie: CXCSRFToken=%s; cxCookie=%s%n", csrfToken, cxCookie);
apacheClient = cb.setDefaultHeaders(headers).build();
}
private TokenLoginResponse ssoLogin() {
HttpUriRequest request;
HttpResponse response;
final String BASE_URL = "/auth/identity/";
RequestConfig requestConfig = RequestConfig.custom()
.setRedirectsEnabled(false)
.setAuthenticationEnabled(true)
.setCookieSpec(CookieSpecs.STANDARD)
.build();
try {
//Request1
request = RequestBuilder.post()
.setUri(rootUri + SSO_AUTHENTICATION)
.setConfig(requestConfig)
.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString())
.setEntity(generateSSOEntity())
.build();
response = apacheClient.execute(request);
//Request2
String cookies = retrieveCookies();
String redirectURL = response.getHeaders(LOCATION_HEADER)[0].getValue();
request = RequestBuilder.get()
.setUri(rootUri + BASE_URL + redirectURL)
.setConfig(requestConfig)
.setHeader("Cookie", cookies)
.setHeader("Upgrade-Insecure-Requests", "1")
.build();
response = apacheClient.execute(request);
//Request3
cookies = retrieveCookies();
redirectURL = response.getHeaders(LOCATION_HEADER)[0].getValue();
redirectURL = rootUri + redirectURL.replace("/CxRestAPI/", "");
request = RequestBuilder.get()
.setUri(redirectURL)
.setConfig(requestConfig)
.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString())
.setHeader("Cookie", cookies)
.build();
response = apacheClient.execute(request);
return extractToken(response);
} catch (IOException e) {
throw new CxClientException(LOGIN_FAILED_MSG + e.getMessage());
}
}
private TokenLoginResponse extractToken(HttpResponse response) {
String redirectURL = response.getHeaders(LOCATION_HEADER)[0].getValue();
if (!redirectURL.contains("access_token")) {
throw new CxClientException("Failed retrieving access token from server");
}
return new Gson().fromJson(urlToJson(redirectURL), TokenLoginResponse.class);
}
private String urlToJson(String url) {
url = url.replace("=", "\":\"");
url = url.replace("&", "\",\"");
return "{\"" + url + "\"}";
}
private String retrieveCookies() {
List cookieList = cookieStore.getCookies();
final StringBuilder builder = new StringBuilder();
cookieList.forEach(cookie ->
builder.append(cookie.getName()).append("=").append(cookie.getValue()).append(";"));
return builder.toString();
}
public TokenLoginResponse generateToken(LoginSettings settings) throws IOException {
UrlEncodedFormEntity requestEntity = getAuthRequest(settings);
HttpPost post = new HttpPost(settings.getAccessControlBaseUrl());
try {
return request(post, ContentType.APPLICATION_FORM_URLENCODED.toString(), requestEntity,
TokenLoginResponse.class, HttpStatus.SC_OK, AUTH_MESSAGE, false, false);
} catch (CxClientException e) {
if (!e.getMessage().contains("invalid_scope")) {
throw new CxClientException(String.format("Failed to generate access token, failure error was: %s", e.getMessage()), e);
}
ClientType.RESOURCE_OWNER.setScopes("sast_rest_api");
settings.setClientTypeForPasswordAuth(ClientType.RESOURCE_OWNER);
UrlEncodedFormEntity requestEntityForSecondLoginRetry = getAuthRequest(settings);
HttpPost post_1 = new HttpPost(settings.getAccessControlBaseUrl());
return request(post_1, ContentType.APPLICATION_FORM_URLENCODED.toString(), requestEntityForSecondLoginRetry,
TokenLoginResponse.class, HttpStatus.SC_OK, AUTH_MESSAGE, false, false);
}
}
private TokenLoginResponse getAccessTokenFromRefreshToken(LoginSettings settings) throws IOException {
UrlEncodedFormEntity requestEntity = getTokenRefreshingRequest(settings);
HttpPost post = new HttpPost(settings.getAccessControlBaseUrl());
try {
return request(post, ContentType.APPLICATION_FORM_URLENCODED.toString(), requestEntity,
TokenLoginResponse.class, HttpStatus.SC_OK, AUTH_MESSAGE, false, false);
} catch (CxClientException e) {
throw new CxClientException(String.format("Failed to generate access token from refresh token. The error was: %s", e.getMessage()), e);
}
}
public void revokeToken(String token) throws IOException {
UrlEncodedFormEntity requestEntity = getRevocationRequest(ClientType.CLI, token);
HttpPost post = new HttpPost(rootUri + REVOCATION);
try {
request(post, ContentType.APPLICATION_FORM_URLENCODED.toString(), requestEntity,
String.class, HttpStatus.SC_OK, "revocation", false, false);
} catch (CxClientException e) {
throw new CxClientException(String.format("Token revocation failure error was: %s", e.getMessage()), e);
}
}
private static UrlEncodedFormEntity getRevocationRequest(ClientType clientType, String token) {
List parameters = new ArrayList<>();
parameters.add(new BasicNameValuePair("token_type_hint", REFRESH_TOKEN_PROP));
parameters.add(new BasicNameValuePair("token", token));
parameters.add(new BasicNameValuePair(CLIENT_ID_PROP, clientType.getClientId()));
parameters.add(new BasicNameValuePair(CLIENT_SECRET_PROP, clientType.getClientSecret()));
return new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8);
}
private static UrlEncodedFormEntity getAuthRequest(LoginSettings settings) {
ClientType clientType = settings.getClientTypeForPasswordAuth();
String grantType = StringUtils.defaultString(clientType.getGrantType(), DEFAULT_GRANT_TYPE);
List parameters = new ArrayList<>();
parameters.add(new BasicNameValuePair("username", settings.getUsername()));
parameters.add(new BasicNameValuePair(PASSWORD_PROP, settings.getPassword()));
parameters.add(new BasicNameValuePair("grant_type", grantType));
parameters.add(new BasicNameValuePair("scope", clientType.getScopes()));
parameters.add(new BasicNameValuePair(CLIENT_ID_PROP, clientType.getClientId()));
parameters.add(new BasicNameValuePair(CLIENT_SECRET_PROP, clientType.getClientSecret()));
if (!StringUtils.isEmpty(settings.getTenant())) {
String authContext = String.format("Tenant:%s", settings.getTenant());
parameters.add(new BasicNameValuePair("acr_values", authContext));
}
return new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8);
}
private static UrlEncodedFormEntity getTokenRefreshingRequest(LoginSettings settings) throws UnsupportedEncodingException {
ClientType clientType = settings.getClientTypeForRefreshToken();
List parameters = new ArrayList<>();
parameters.add(new BasicNameValuePair("grant_type", REFRESH_TOKEN_PROP));
parameters.add(new BasicNameValuePair(CLIENT_ID_PROP, clientType.getClientId()));
parameters.add(new BasicNameValuePair(CLIENT_SECRET_PROP, clientType.getClientSecret()));
parameters.add(new BasicNameValuePair(REFRESH_TOKEN_PROP, settings.getRefreshToken()));
return new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8.name());
}
//GET REQUEST
public T getRequest(String relPath, String contentType, Class responseType, int expectStatus, String failedMsg, boolean isCollection) throws IOException {
return getRequest(rootUri, relPath, CONTENT_TYPE_APPLICATION_JSON, contentType, responseType, expectStatus, failedMsg, isCollection);
}
public T getRequest(String rootURL, String relPath, String acceptHeader, String contentType, Class responseType, int expectStatus, String failedMsg, boolean isCollection) throws IOException {
HttpGet get = new HttpGet(rootURL + relPath);
get.addHeader(HttpHeaders.ACCEPT, acceptHeader);
return request(get, contentType, null, responseType, expectStatus, "get " + failedMsg, isCollection, true);
}
//POST REQUEST
public T postRequest(String relPath, String contentType, HttpEntity entity, Class responseType, int expectStatus, String failedMsg) throws IOException {
HttpPost post = new HttpPost(rootUri + relPath);
return request(post, contentType, entity, responseType, expectStatus, failedMsg, false, true);
}
// POST REQUEST
public T postRequest(String relPath, String contentType, String acceptHeader, HttpEntity entity,
Class responseType, int expectStatus, String failedMsg) throws IOException {
HttpPost post = new HttpPost(rootUri + relPath);
post.addHeader("Accept", acceptHeader);
return request(post, contentType, entity, responseType, expectStatus, failedMsg, false, true);
}
//PUT REQUEST
public T putRequest(String relPath, String contentType, HttpEntity entity, Class responseType, int expectStatus, String failedMsg) throws IOException {
HttpPut put = new HttpPut(rootUri + relPath);
return request(put, contentType, entity, responseType, expectStatus, failedMsg, false, true);
}
//PATCH REQUEST
public void patchRequest(String relPath, String contentType, HttpEntity entity, int expectStatus, String failedMsg) throws IOException {
HttpPatch patch = new HttpPatch(rootUri + relPath);
request(patch, contentType, entity, null, expectStatus, failedMsg, false, true);
}
public void setTeamPathHeader(String teamPath) {
this.teamPath = teamPath;
}
public void addCustomHeader(String name, String value) {
log.debug(String.format("Adding a custom header: %s: %s", name, value));
customHeaders.put(name, value);
}
private T request(HttpRequestBase httpMethod, String contentType, HttpEntity entity, Class responseType, int expectStatus, String failedMsg, boolean isCollection, boolean retry) throws IOException {
//Support unicode characters
if (httpMethod.getURI() != null && (StringUtils.isNotEmpty(httpMethod.getURI().getHost()) ||
StringUtils.isNotEmpty(httpMethod.getURI().getAuthority()))) {
URI tmpUri = httpMethod.getURI();
String host = StringUtils.isNotEmpty(tmpUri.getAuthority()) ? tmpUri.getAuthority() : tmpUri.getHost();
host = IDN.toASCII(host, IDN.ALLOW_UNASSIGNED);
String hostname = host;
String portNumber = "" + tmpUri.getPort();
String[] arr = host.split(":");
if(arr != null && arr.length>1) {
hostname = arr[0];
portNumber = arr[1];
}
try {
URIBuilder uriBuilder = new URIBuilder(tmpUri).setHost(hostname).setPort(Integer.parseInt(portNumber));
URI uri = uriBuilder.build();
httpMethod.setURI(uri);
} catch (URISyntaxException e) {
log.error("Fail to convert URI: " + httpMethod.getURI().toString());
}
}
if (contentType != null) {
httpMethod.addHeader("Content-type", contentType);
}
if (entity != null && httpMethod instanceof HttpEntityEnclosingRequestBase) { //Entity for Post methods
((HttpEntityEnclosingRequestBase) httpMethod).setEntity(entity);
}
HttpResponse response = null;
int statusCode = 0;
try {
httpMethod.addHeader(ORIGIN_HEADER, cxOrigin);
httpMethod.addHeader(ORIGIN_URL_HEADER, cxOriginUrl);
httpMethod.addHeader(TEAM_PATH, this.teamPath);
if (token != null) {
httpMethod.addHeader(HttpHeaders.AUTHORIZATION, token.getToken_type() + " " + token.getAccess_token());
}
for (Map.Entry entry : customHeaders.entrySet()) {
httpMethod.addHeader(entry.getKey(), entry.getValue());
}
response = apacheClient.execute(httpMethod);
statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_UNAUTHORIZED) { // Token has probably expired
throw new CxTokenExpiredException(extractResponseBody(response));
}
validateResponse(response, expectStatus, "Failed to " + failedMsg);
//extract response as object and return the link
return convertToObject(response, responseType, isCollection);
} catch (UnknownHostException e) {
log.error(e.getMessage());
throw new CxHTTPClientException(ErrorMessage.CHECKMARX_SERVER_CONNECTION_FAILED.getErrorMessage(), e);
} catch (CxTokenExpiredException ex) {
if (retry) {
logTokenError(httpMethod, statusCode, ex);
if (lastLoginSettings != null) {
login(lastLoginSettings);
removeHeaders(httpMethod);
return request(httpMethod, contentType, entity, responseType, expectStatus, failedMsg, isCollection, false);
}
}
throw ex;
} finally {
httpMethod.releaseConnection();
HttpClientUtils.closeQuietly(response);
}
}
private void removeHeaders(HttpRequestBase httpMethod) {
httpMethod.removeHeaders("Content-type");
httpMethod.removeHeaders(ORIGIN_HEADER);
httpMethod.removeHeaders(ORIGIN_URL_HEADER);
httpMethod.removeHeaders(TEAM_PATH);
httpMethod.removeHeaders(HttpHeaders.AUTHORIZATION);
}
public void close() {
HttpClientUtils.closeQuietly(apacheClient);
}
private void setSSLTls(String protocol, Logger log) {
try {
final SSLContext sslContext = SSLContext.getInstance(protocol);
sslContext.init(null, null, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
log.warn(String.format("Failed to set SSL TLS : %s", e.getMessage()));
}
}
//TODO handle missing scope issue with management_and_orchestration_api
private StringEntity generateSSOEntity() {
final String clientId = "cxsast_client";
final String redirectUri = "%2Fcxwebclient%2FauthCallback.html%3F";
final String responseType = "id_token%20token";
final String nonce = "9313f0902ba64e50bc564f5137f35a52";
final String isPrompt = "true";
final String scopes = "sast_api openid sast-permissions access-control-permissions access_control_api management_and_orchestration_api".replace(" ", "%20");
final String providerId = "2"; //windows provider id
String redirectUrl = MessageFormat.format("/CxRestAPI/auth/identity/connect/authorize/callback" +
"?client_id={0}" +
"&redirect_uri={1}" + redirectUri +
"&response_type={2}" +
"&scope={3}" +
"&nonce={4}" +
"&prompt={5}"
, clientId, rootUri, responseType, scopes, nonce, isPrompt);
try {
List urlParameters = new ArrayList<>();
urlParameters.add(new BasicNameValuePair("redirectUrl", redirectUrl));
urlParameters.add(new BasicNameValuePair("providerid", providerId));
return new UrlEncodedFormEntity(urlParameters, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
throw new CxClientException(e.getMessage());
}
}
public void setToken(TokenLoginResponse token) {
this.token = token;
}
private void logTokenError(HttpRequestBase httpMethod, int statusCode, CxTokenExpiredException ex) {
String message = String.format("Received status code %d for URL: %s with the message: %s",
statusCode,
httpMethod.getURI(),
ex.getMessage());
log.warn(message);
log.info("Possible reason: access token has expired. Trying to request a new token...");
}
/*
* This will return string from encoded access token
* which will use to identify which language is used in SAST
*
* */
public String getLanguageFromAccessToken() {
String languageForSAST = "en-US";
try {
String actToken = token.getAccess_token();
String[] split_string = actToken.split("\\.");
if (split_string != null && split_string.length > 0) {
String base64EncodedBody = split_string[1];
Base64 base64Url = new Base64(true);
String body = new String(base64Url.decode(base64EncodedBody));
String tokenToParse = body.replace("\"", "'");
JSONObject json = new JSONObject(tokenToParse);
languageForSAST = json.getString("locale");
log.info("Locale used in CxSAST is " + languageForSAST);
}
} catch (Exception ex) {
// In case the SAST used will not have token, set to default English language
languageForSAST = "en-US";
}
return languageForSAST;
}
}