All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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