com.denimgroup.threadfix.remote.HttpRestUtils Maven / Gradle / Ivy
////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009-2016 Denim Group, Ltd.
//
// The contents of this file are subject to the Mozilla Public License
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is ThreadFix.
//
// The Initial Developer of the Original Code is Denim Group, Ltd.
// Portions created by Denim Group, Ltd. are Copyright (C)
// Denim Group, Ltd. All Rights Reserved.
//
// Contributor(s): Denim Group, Ltd.
//
////////////////////////////////////////////////////////////////////////
package com.denimgroup.threadfix.remote;
import com.denimgroup.threadfix.logging.SanitizedLogger;
import com.denimgroup.threadfix.properties.PropertiesManager;
import com.denimgroup.threadfix.remote.response.ResponseParser;
import com.denimgroup.threadfix.remote.response.RestResponse;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.validator.routines.UrlValidator;
import javax.annotation.Nonnull;
import javax.net.ssl.SSLHandshakeException;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.List;
import static com.denimgroup.threadfix.CollectionUtils.list;
public class HttpRestUtils {
public static final String API_KEY_SEGMENT = "?apiKey=";
@Nonnull
final PropertiesManager propertiesManager;
private boolean unsafeFlag = false;
public static final String JAVA_KEY_STORE_FILE = getKeyStoreFile();
private static int count = 0;
private static final SanitizedLogger LOGGER = new SanitizedLogger(HttpRestUtils.class);
public HttpRestUtils(@Nonnull PropertiesManager manager) {
this.propertiesManager = manager;
System.setProperty("javax.net.ssl.trustStore", JAVA_KEY_STORE_FILE);
}
public RestResponse httpPostFile(@Nonnull String path,
@Nonnull String filePath,
@Nonnull String[] paramNames,
@Nonnull String[] paramVals,
@Nonnull Class targetClass){
if (isUnsafeFlag())
Protocol.registerProtocol("https", new Protocol("https", new AcceptAllTrustFactory(), 443));
String completeUrl = makePostUrl(path);
if (completeUrl == null) {
LOGGER.debug("The POST url could not be generated. Aborting request.");
return ResponseParser.getErrorResponse(
"The POST url could not be generated and the request was not attempted.",
0);
}
PostMethod filePost = new PostMethod(completeUrl);
filePost.setRequestHeader("Accept", "application/json");
RestResponse response = null;
int status = -1;
try {
File file = new File(filePath);
if(file.isDirectory()) {
return RestResponse.failure("File path resolves to a directory, not a file.");
}
List parts = list();
for(int j = 0; j < paramNames.length; j++){
parts.add(new StringPart(paramNames[j], paramVals[j]));
}
parts.add(new FilePart("file", file));
parts.add(new StringPart("apiKey", propertiesManager.getKey()));
parts.toArray(new Part[parts.size()]);
filePost.setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[parts.size()]),
filePost.getParams()));
filePost.setContentChunked(true);
HttpClient client = new HttpClient();
status = client.executeMethod(filePost);
if (status != 200) {
LOGGER.warn("Request for '" + completeUrl + "' status was " + status + ", not 200 as expected.");
}
if (status == 302) {
Header location = filePost.getResponseHeader("Location");
printRedirectInformation(location);
}
response = ResponseParser.getRestResponse(filePost.getResponseBodyAsStream(), status, targetClass);
} catch (SSLHandshakeException sslHandshakeException) {
importCert(sslHandshakeException);
}catch (IOException e) {
LOGGER.error("There was an error and the POST request was not finished.", e);
response = ResponseParser.getErrorResponse(
"There was an error and the POST request was not finished.",
status);
}
return response;
}
public RestResponse httpPostMultFile(@Nonnull String path,
@Nonnull List filePaths,
@Nonnull String[] paramNames,
@Nonnull String[] paramVals,
@Nonnull Class targetClass){
if (isUnsafeFlag())
Protocol.registerProtocol("https", new Protocol("https", new AcceptAllTrustFactory(), 443));
String completeUrl = makePostUrl(path);
if (completeUrl == null) {
LOGGER.debug("The POST url could not be generated. Aborting request.");
return ResponseParser.getErrorResponse(
"The POST url could not be generated and the request was not attempted.",
0);
}
PostMethod filePost = new PostMethod(completeUrl);
filePost.setRequestHeader("Accept", "application/json");
RestResponse response = null;
int status = -1;
try{
List files = list();
for (String filePath:filePaths) {
File file = new File(filePath);
if(!file.isDirectory()) {
files.add(file);
}else{
for(File f : file.listFiles()){
if(!f.isDirectory()) {
files.add(f);
}
}
}
}
List parts = list();
for(int j = 0; j RestResponse httpPut(@Nonnull String path,
@Nonnull String[] paramNames,
@Nonnull String[] paramVals,
@Nonnull Class targetClass) {
if (isUnsafeFlag())
Protocol.registerProtocol("https", new Protocol("https", new AcceptAllTrustFactory(), 443));
String urlString = makePutUrl(path);
if (urlString == null) {
LOGGER.debug("The PUT url could not be generated. Aborting request.");
return ResponseParser.getErrorResponse(
"The PUT url could not be generated and the request was not attempted.",
0);
}
PutMethod put = new PutMethod(urlString);
put.setRequestHeader("Accept", "application/json");
put.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
int responseCode = -1;
RestResponse response = null;
try {
put.setRequestBody(createRequestBody(paramNames, paramVals));
HttpClient client = new HttpClient();
responseCode = client.executeMethod(put);
if (responseCode != 200) {
LOGGER.warn("Request for '" + urlString + "' status was " + responseCode + ", not 200 as expected.");
}
if (responseCode == 302) {
Header location = put.getResponseHeader("Location");
printRedirectInformation(location);
}
response = ResponseParser.getRestResponse(put.getResponseBodyAsStream(), responseCode, targetClass);
} catch (SSLHandshakeException sslHandshakeException) {
importCert(sslHandshakeException);
} catch (IOException e1) {
LOGGER.error("Encountered IOException while trying to put to " + path, e1);
response = ResponseParser.getErrorResponse(
"There was an error and the POST request was not finished.",
responseCode);
}
return response;
}
public RestResponse httpPost(@Nonnull String path,
@Nonnull String[] paramNames,
@Nonnull String[] paramVals,
@Nonnull Class targetClass) {
if (isUnsafeFlag())
Protocol.registerProtocol("https", new Protocol("https", new AcceptAllTrustFactory(), 443));
String urlString = makePostUrl(path);
if (urlString == null) {
LOGGER.debug("The POST url could not be generated. Aborting request.");
return ResponseParser.getErrorResponse(
"The POST url could not be generated and the request was not attempted.",
0);
}
PostMethod post = new PostMethod(urlString);
post.setRequestHeader("Accept", "application/json");
int responseCode = -1;
RestResponse response = null;
try {
for (int i = 0; i < paramNames.length; i++) {
if (paramNames[i] != null && paramVals[i] != null) {
post.addParameter(paramNames[i], paramVals[i]);
}
}
addApiKey(post);
HttpClient client = new HttpClient();
responseCode = client.executeMethod(post);
if (responseCode != 200) {
LOGGER.warn("Request for '" + urlString + "' status was " + responseCode + ", not 200 as expected.");
}
if (responseCode == 302) {
Header location = post.getResponseHeader("Location");
printRedirectInformation(location);
}
response = ResponseParser.getRestResponse(post.getResponseBodyAsStream(), responseCode, targetClass);
} catch (SSLHandshakeException sslHandshakeException) {
importCert(sslHandshakeException);
} catch (IOException e1) {
LOGGER.error("Encountered IOException while trying to post to " + path, e1);
response = ResponseParser.getErrorResponse(
"There was an error and the POST request was not finished.",
responseCode);
}
return response;
}
@Nonnull
public RestResponse httpPostFile(@Nonnull String path,
@Nonnull File file,
@Nonnull String[] paramNames,
@Nonnull String[] paramVals,
@Nonnull Class targetClass) {
if (isUnsafeFlag())
Protocol.registerProtocol("https", new Protocol("https", new AcceptAllTrustFactory(), 443));
String completeUrl = makePostUrl(path);
if (completeUrl == null) {
LOGGER.debug("The POST url could not be generated. Aborting request.");
return ResponseParser.getErrorResponse(
"The POST url could not be generated and the request was not attempted.",
0);
}
PostMethod filePost = new PostMethod(completeUrl);
filePost.setRequestHeader("Accept", "application/json");
RestResponse response = null;
int status = -1;
try {
Part[] parts = new Part[paramNames.length + 2];
parts[paramNames.length] = new FilePart("file", file);
parts[paramNames.length + 1] = new StringPart("apiKey", propertiesManager.getKey());
for (int i = 0; i < paramNames.length; i++) {
parts[i] = new StringPart(paramNames[i], paramVals[i]);
}
filePost.setRequestEntity(new MultipartRequestEntity(parts,
filePost.getParams()));
filePost.setContentChunked(true);
HttpClient client = new HttpClient();
status = client.executeMethod(filePost);
if (status != 200) {
LOGGER.warn("Request for '" + completeUrl + "' status was " + status + ", not 200 as expected.");
}
if (status == 302) {
Header location = filePost.getResponseHeader("Location");
printRedirectInformation(location);
}
response = ResponseParser.getRestResponse(filePost.getResponseBodyAsStream(), status, targetClass);
} catch (SSLHandshakeException sslHandshakeException) {
importCert(sslHandshakeException);
} catch (IOException e1) {
LOGGER.error("There was an error and the POST request was not finished.", e1);
response = ResponseParser.getErrorResponse(
"There was an error and the POST request was not finished.",
status);
}
return response;
}
private void printRedirectInformation(Header location) {
LOGGER.warn("Location header for 302 response was: " + location);
if (location != null && location.getValue() != null) {
String target = location.getValue();
if (target.contains("login.jsp")) {
// this might be a ThreadFix server
target = target.substring(0, target.indexOf("login.jsp")) + "rest";
LOGGER.info("Based on the Location header, the correct URL should be: " + target);
LOGGER.info("Set it with -s url " + target);
}
}
}
@Nonnull
public RestResponse httpGet(@Nonnull String path, @Nonnull Class targetClass) {
return httpGet(path, "", targetClass);
}
@Nonnull
public RestResponse httpGet(@Nonnull String path, @Nonnull String params,
@Nonnull Class targetClass) {
return httpGet(path, params, targetClass, null);
}
@Nonnull
public RestResponse httpGet(@Nonnull String path, @Nonnull String params,
@Nonnull Class targetClass, String fileName) {
String urlString = makeGetUrl(path, params);
if (urlString == null) {
LOGGER.debug("The GET url could not be generated. Aborting request.");
return ResponseParser.getErrorResponse(
"The GET url could not be generated and the request was not attempted.",
0);
}
LOGGER.debug("Requesting " + urlString);
if (isUnsafeFlag())
Protocol.registerProtocol("https", new Protocol("https", new AcceptAllTrustFactory(), 443));
GetMethod get = new GetMethod(urlString);
get.setRequestHeader("Accept", "application/json");
HttpClient client = new HttpClient();
int status = -1;
RestResponse response = null;
try {
status = client.executeMethod(get);
if (status != 200) {
LOGGER.error("Status was not 200. It was " + status);
}
if (status == 302) {
Header location = get.getResponseHeader("Location");
printRedirectInformation(location);
}
if (fileName == null) {
response = ResponseParser.getRestResponse(get.getResponseBodyAsStream(), status, targetClass);
} else {
if (propertiesManager.getOutputFilePath() != null) {
response = ResponseParser.getFileResponse(get.getResponseBodyAsStream(), status,
new File(propertiesManager.getOutputFilePath()), fileName);
} else {
response = RestResponse.failure("No output file path set.");
}
}
} catch (SSLHandshakeException sslHandshakeException) {
importCert(sslHandshakeException);
} catch (IOException e) {
LOGGER.error("Encountered IOException while trying to post to " + path, e);
response = ResponseParser.getErrorResponse("There was an error and the GET request was not finished.", status);
}
return response;
}
// this is necessary because Spring double-decodes %s in the URL for some reason
public static String encodeDoublePercent(String input) {
if (input.contains("%")) {
input = input.replaceAll("%", "%25");
}
return encode(input);
}
public static String encode(String input) {
try {
return URLEncoder.encode(input, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
@Nonnull
private String makeGetUrl(@Nonnull String path, @Nonnull String params) {
String baseUrl = propertiesManager.getUrl();
String[] schemes = {"http","https"};
UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS);
if (!urlValidator.isValid(baseUrl)) {
LOGGER.debug("Base url " + baseUrl + " is not a valid url. Cannot build GET url with path "
+ path + ". Returning null.");
return null;
}
String apiKey = propertiesManager.getKey();
LOGGER.debug("Building GET url with path " + path + " and base url " + baseUrl);
if (baseUrl.endsWith("/rest") && path.charAt(0) != '/') {
baseUrl = baseUrl + "/";
}
String finishedUrl = baseUrl + path + API_KEY_SEGMENT + apiKey + "&" + params;
LOGGER.debug("Returning " + finishedUrl);
return finishedUrl;
}
@Nonnull
private String makePutUrl(@Nonnull String path) {
String baseUrl = propertiesManager.getUrl();
String[] schemes = {"http","https"};
UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS);
if (!urlValidator.isValid(baseUrl)) {
LOGGER.debug("Base url " + baseUrl + " is not a valid url. Cannot build POST url with path "
+ path + ". Returning null.");
return null;
}
String apiKey = propertiesManager.getKey();
LOGGER.debug("Building POST url with path " + path + " and base url " + baseUrl);
if (baseUrl.endsWith("/rest") && path.charAt(0) != '/') {
baseUrl = baseUrl + "/";
}
String finishedUrl = baseUrl + path + API_KEY_SEGMENT + apiKey;
LOGGER.debug("Returning " + finishedUrl);
return finishedUrl;
}
@Nonnull
private String makePostUrl(@Nonnull String path) {
String baseUrl = propertiesManager.getUrl();
String[] schemes = {"http","https"};
UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS);
if (!urlValidator.isValid(baseUrl)) {
LOGGER.debug("Base url " + baseUrl + " is not a valid url. Cannot build POST url with path "
+ path + ". Returning null.");
return null;
}
LOGGER.debug("Building POST url with path " + path + " and base url " + baseUrl);
if (baseUrl.endsWith("/rest") && path.charAt(0) != '/') {
baseUrl = baseUrl + "/";
}
LOGGER.debug("Returning " + baseUrl + path);
return baseUrl + path;
}
private void addApiKey(PostMethod post) {
if (propertiesManager.getKey() == null) {
throw new IllegalStateException("Please set your key before using this tool. Use the -s key option.");
} else {
post.addParameter("apiKey", propertiesManager.getKey());
}
}
public boolean isUnsafeFlag() {
return unsafeFlag;
}
public void setUnsafeFlag(boolean unsafeFlag) {
this.unsafeFlag = unsafeFlag;
}
private URI getURI() throws URISyntaxException {
String baseUrl = propertiesManager.getUrl();
URI uri = new URI(baseUrl);
return uri;
}
private void importCert(SSLHandshakeException sslHandshakeException){
if (count < 2) {
LOGGER.warn("Unsigned certificate found. Trying to import it to Java KeyStore.");
try {
URI uri = getURI();
String domain = uri.getHost();
domain = domain.startsWith("www.") ? domain.substring(4) : domain;
if (InstallCert.install(domain, uri.getPort())) {
count++;
LOGGER.info("Successfully imported certificate. Please run your command again.");
}
} catch (Exception e) {
LOGGER.error("Error when tried to import certificate. ", e);
}
} else {
LOGGER.error("Unsigned certificate found. We tried to import it but was not successful." +
"We recommend you import server certificate to the Java cacerts keystore, or add option -Dunsafe-ssl from command line to accept all unsigned certificates. " +
"Check out https://github.com/denimgroup/threadfix/wiki/Importing-Self-Signed-Certificates on how to import Self Signed Certificates.", sslHandshakeException);
}
}
private static String getKeyStoreFile() {
char SEP = File.separatorChar;
File dir = new File(System.getProperty("java.home") + SEP
+ "lib" + SEP + "security");
File file = new File(dir, "jssecacerts");
if (file.isFile() == false) {
file = new File(dir, "cacerts");
}
return file.getAbsolutePath();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy