com.denimgroup.threadfix.service.defects.BugzillaDefectTracker.java Source code

Java tutorial

Introduction

Here is the source code for com.denimgroup.threadfix.service.defects.BugzillaDefectTracker.java

Source

////////////////////////////////////////////////////////////////////////
//
//     Copyright (c) 2009-2013 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.service.defects;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.HttpClient;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.client.XmlRpcCommonsTransportFactory;

import com.denimgroup.threadfix.data.entities.Defect;
import com.denimgroup.threadfix.data.entities.Vulnerability;

/**
 * @author dcornell
 * @author mcollins
 */
public class BugzillaDefectTracker extends AbstractDefectTracker {

    private static Map<String, String> versionMap = new HashMap<String, String>();

    private List<String> statuses = new ArrayList<String>();
    private List<String> components = new ArrayList<String>();
    private List<String> severities = new ArrayList<String>();
    private List<String> versions = new ArrayList<String>();
    private List<String> priorities = new ArrayList<String>();

    @Override
    public String createDefect(List<Vulnerability> vulnerabilities, DefectMetadata metadata) {
        String bugzillaId = null;

        // TODO Better handle error cases
        try {
            XmlRpcClient client = initializeClient();
            if (client == null) {
                return null;
            }

            String loginStatus = login(client);
            if (loginStatus == null) {
                return null;
            }

            // TODO Pass this information back to the user
            if (loginStatus.equals(LOGIN_FAILURE) || loginStatus.equals(BAD_CONFIGURATION)) {
                log.warn("Login Failed, check credentials");
                return null;
            }

            if (!projectExists(projectName, client)) {
                log.warn("The project name wasn't found on the server.");
                return null;
            }

            String description = makeDescription(vulnerabilities, metadata);
            if (description.length() > 65500) {
                description = description.substring(0, 65499);
            }

            if (metadata.getDescription() == null || metadata.getComponent() == null
                    || metadata.getVersion() == null || metadata.getSeverity() == null
                    || metadata.getStatus() == null || metadata.getPriority() == null) {
                return null;
            }

            Map<String, String> bugMap = new HashMap<String, String>();
            bugMap.put("product", projectName);
            bugMap.put("component", metadata.getComponent());
            bugMap.put("summary", metadata.getDescription());
            bugMap.put("version", metadata.getVersion());
            bugMap.put("description", description);
            bugMap.put("op_sys", "All");
            bugMap.put("platform", "All");
            bugMap.put("priority", metadata.getPriority());
            bugMap.put("severity", metadata.getSeverity());
            bugMap.put("status", metadata.getStatus());

            Object[] bugArray = new Object[1];
            bugArray[0] = bugMap;

            Object createResult = client.execute("Bug.create", bugArray);
            log.debug("Create result: " + createResult);

            @SuppressWarnings("unchecked")
            Map<String, Integer> actualResult = (HashMap<String, Integer>) createResult;
            Integer returnId = actualResult.get("id");
            bugzillaId = returnId.toString();
        } catch (XmlRpcException e) {
            log.debug("Exception occured while creating Defect: " + e.getMessage());
            e.printStackTrace();
        } catch (IllegalArgumentException e2) {
            log.error("Got IllegalArgumentException.", e2);
        }

        return bugzillaId;
    }

    /**
     * Retrieve all the variable fields that Bugzilla installations have.
     * 
     * @param productId
     * @return
     */
    @Override
    public ProjectMetadata getProjectMetadata() {
        getPermissibleBugFieldValues();

        return new ProjectMetadata(components, versions, severities, statuses, priorities);
    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<Defect, Boolean> getMultipleDefectStatus(List<Defect> defectList) {

        Map<String, Defect> idDefectMap = new HashMap<String, Defect>();

        for (Defect defect : defectList) {
            if (defect != null && defect.getNativeId() != null) {
                idDefectMap.put(defect.getNativeId(), defect);
            }
        }

        Map<String, Object[]> queryMap = new HashMap<String, Object[]>();
        queryMap.put("ids", idDefectMap.keySet().toArray(new Object[idDefectMap.keySet().size()]));

        Object queryResult = executeMethod("Bug.get", new Object[] { queryMap });

        Map<Defect, Boolean> returnList = new HashMap<Defect, Boolean>();

        if (queryResult instanceof HashMap) {
            Map<String, Object[]> returnedData = (HashMap<String, Object[]>) queryResult;
            Object[] bugsArray = returnedData.get("bugs");
            for (int i = 0; i < bugsArray.length; i++) {
                Object currentBug = bugsArray[i];
                Map<String, Object> currentBugHash = (HashMap<String, Object>) currentBug;
                if (currentBugHash == null) {
                    continue;
                }

                Boolean isOpen = (Boolean) currentBugHash.get("is_open");
                Object result = currentBugHash.get("status");

                Integer id = (Integer) currentBugHash.get("id");

                if (idDefectMap.containsKey(id.toString()) && idDefectMap.get(id.toString()) != null) {
                    Defect defect = idDefectMap.get(id.toString());

                    if (result instanceof String) {
                        defect.setStatus((String) result);
                    }

                    returnList.put(defect, isOpen);
                }
            }
        } else {
            log.error("Expected a HashMap return value, but got something else instead.");
        }

        return returnList;
    }

    @Override
    public String getTrackerError() {
        XmlRpcClient client = initializeClient();
        if (client == null) {
            return null;
        }
        String loginStatus = login(client);
        if (loginStatus == null) {
            return null;
        }

        // TODO Pass this information back to the user
        if (loginStatus.equals(LOGIN_FAILURE) || loginStatus.equals(BAD_CONFIGURATION)) {
            return "Bugzilla login failed, check your credentials.";
        }

        if (!projectExists(projectName, client)) {
            return "The project specified does not exist - please specify a different" + " one or create "
                    + projectName + " in Bugzilla.";
        }

        return null;
    }

    private void getPermissibleBugFieldValues() {
        client = initializeClient();

        String loginResponse = login(client);
        if (loginResponse == null) {
            return;
        }
        if (loginResponse.equals(LOGIN_FAILURE) || loginResponse.equals(BAD_CONFIGURATION)) {
            log.warn("Login Failed, check credentials");
            return;
        }

        Map<String, String> bugMap = new HashMap<String, String>();
        bugMap.put("field", "bug_severity");
        Object[] bugArray = new Object[] { bugMap };
        Object createResult = executeMethod("Bug.legal_values", bugArray);
        severities.addAll(getValues(createResult));

        bugMap.put("field", "bug_status");
        bugArray = new Object[] { bugMap }; // maybe useless line
        createResult = executeMethod("Bug.legal_values", bugArray);
        statuses.addAll(getValues(createResult));

        if (statuses.contains("UNCONFIRMED")) {
            statuses.remove("UNCONFIRMED");
        }

        bugMap.put("field", "priority");
        bugArray = new Object[] { bugMap }; // maybe useless line
        createResult = executeMethod("Bug.legal_values", bugArray);
        priorities.addAll(getValues(createResult));

        projectId = getProjectIdByName();

        Map<String, String> queryMap = new HashMap<String, String>();
        queryMap.put("field", "version");
        queryMap.put("product_id", projectId);
        createResult = executeMethod("Bug.legal_values", new Object[] { queryMap });
        versions.addAll(getValues(createResult));

        queryMap = new HashMap<String, String>();
        queryMap.put("field", "component");
        queryMap.put("product_id", projectId);
        createResult = executeMethod("Bug.legal_values", new Object[] { queryMap });
        components.addAll(getValues(createResult));
    }

    /**
     * If the Object given is a map containing a mapping for "values" to
     * an object array containing string values, this method will return
     * a List containing those items.
     * @param rpcResponse
     * @return
     */
    private List<String> getValues(Object rpcResponse) {
        List<String> responseList = new ArrayList<String>();
        if (rpcResponse != null && rpcResponse instanceof HashMap) {
            Map<?, ?> returnedData = (HashMap<?, ?>) rpcResponse;
            Object componentsObject = returnedData.get("values");
            if (componentsObject != null && componentsObject instanceof Object[]) {
                Object[] componentsArray = (Object[]) componentsObject;
                for (int i = 0; i < componentsArray.length; i++) {
                    if (componentsArray[i] != null) {
                        responseList.add(componentsArray[i].toString());
                    }
                }
            }
        }
        return responseList;
    }

    /**
     * Set up the configuration
     * 
     * @return
     * @throws MalformedURLException
     */
    private XmlRpcClient initializeClient() {
        // Get the RPC client set up and ready to go
        // The alternate TransportFactory stuff is required so that cookies
        // work and the logins behave persistently
        XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
        try {
            config.setServerURL(new URL(this.getServerURLWithRpc()));
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        // config.setEnabledForExtensions(true);
        XmlRpcClient client = new XmlRpcClient();
        client.setConfig(config);
        XmlRpcCommonsTransportFactory factory = new XmlRpcCommonsTransportFactory(client);
        factory.setHttpClient(new HttpClient());
        client.setTransportFactory(factory);

        return client;
    }

    /**
     * @param client
     * @throws XmlRpcException
     */
    private String login(XmlRpcClient client) {

        Map<String, String> loginMap = new HashMap<String, String>();
        loginMap.put("login", this.username);
        loginMap.put("password", this.password);
        loginMap.put("rememberlogin", "Bugzilla_remember");

        Object[] loginArray = new Object[1];
        loginArray[0] = loginMap;

        Object loginResult = null;
        try {
            loginResult = client.execute("User.login", loginArray);
        } catch (XmlRpcException e) {
            if (e.getMessage().contains("The username or password you entered is not valid")) {
                return LOGIN_FAILURE;
            }
            e.printStackTrace();
        } catch (IllegalArgumentException e2) {
            if (e2.getMessage().contains("Host name may not be null")) {
                return BAD_CONFIGURATION;
            } else {
                e2.printStackTrace();
                return BAD_CONFIGURATION;
            }
        }

        if (loginResult == null) {
            return null;
        } else {
            return loginResult.toString();
        }
    }

    private XmlRpcClient client = null;

    private Object executeMethod(String method, Object[] params) {
        if (method == null || params == null)
            return null;

        if (client == null) {
            client = initializeClient();
            String loginResponse = login(client);
            if (loginResponse == null) {
                return null;
            }
            if (loginResponse.equals(LOGIN_FAILURE) || loginResponse.equals(BAD_CONFIGURATION)) {
                log.warn("Login Failed, check credentials");
                return null;
            }
        }

        if (client == null) {
            log.warn("There was an error initializing the Bugzilla client.");
            return null;
        }

        try {
            return client.execute(method, params);
        } catch (XmlRpcException e) {
            e.printStackTrace();
        }

        return null;
    }

    public String getVersion() {
        if (versionMap.get(url) != null) {
            return versionMap.get(url);
        }

        Object createResult = executeMethod("Bugzilla.version", new Object[] {});

        if (createResult instanceof Map<?, ?>) {
            Map<?, ?> item = (Map<?, ?>) createResult;
            if (item.get("version") != null) {
                log.info("Bugzilla instance is version " + item.get("version"));
                versionMap.put(url, item.get("version").toString());
            }
        }
        return versionMap.get(url);
    }

    /**
     * @param projectName
     * @param client
     * @return
     */
    public boolean projectExists(String projectName, XmlRpcClient client) {
        this.projectName = projectName;
        return getProjectIdByName() != null;
    }

    /**
     * @param client
     * @return
     */
    @SuppressWarnings("unchecked")
    private String getProducts(XmlRpcClient client) {
        String productList = "";

        try {
            Map<String, Object[]> productsMap = (HashMap<String, Object[]>) client
                    .execute("Product.get_accessible_products", new Object[] {});
            Object[] ids = productsMap.get("ids");

            StringBuffer buffer = new StringBuffer();

            Map<String, Object[]> params = new HashMap<String, Object[]>();
            params.put("ids", ids);

            Map<String, Object[]> productMap = (HashMap<String, Object[]>) client.execute("Product.get",
                    new Object[] { params });
            Object[] products = productMap.get("products");

            for (Object item : products) {
                Map<String, Object> product = (HashMap<String, Object>) item;
                String productName = (String) product.get("name");
                buffer.append(productName).append(',');
            }

            productList = buffer.toString();
            productList = productList.substring(0, productList.length() - 1);
        } catch (XmlRpcException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e2) {
            if (e2.getMessage().contains("Host name may not be null")) {
                return BAD_CONFIGURATION;
            } else {
                e2.printStackTrace();
                return BAD_CONFIGURATION;
            }
        }
        if (productList == "") {
            setLastError("There were problems communicating with the Bugzilla server.");
            return "Authentication failed";
        }

        return productList;
    }

    public String getProjectIdByName() {
        String version = getVersion();

        if (version == null) {
            log.info("Unable to get Bugzilla version. Exiting.");
        } else if (version.charAt(0) == '3') {
            return getProjectIdByNameVersion3();
        } else if (version.charAt(0) == '4') {
            return getProjectIdByNameVersion4();
        } else {
            log.warn("Bugzilla version was not 3 or 4, exiting.");
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private String getProjectIdByNameVersion3() {
        if (projectName == null)
            return null;

        XmlRpcClient client = initializeClient();
        String status = login(client);

        if (status == null || status.equals(LOGIN_FAILURE) || status.equals(BAD_CONFIGURATION)) {
            log.warn(status);
            return null;
        }

        try {
            Map<String, Object[]> productsMap = (HashMap<String, Object[]>) client
                    .execute("Product.get_enterable_products", new Object[] {});
            Object[] ids = productsMap.get("ids");

            Map<String, Object[]> params = new HashMap<String, Object[]>();
            params.put("ids", ids);

            Map<String, Object[]> productMap = (HashMap<String, Object[]>) client.execute("Product.get",
                    new Object[] { params });
            Object[] products = productMap.get("products");
            if (products == null)
                return null;
            for (int i = 0; i < products.length; i++) {
                Map<String, Object> product = (HashMap<String, Object>) products[i];
                String productName = (String) product.get("name");
                if (projectName.equals(productName)) {
                    Integer temp = (Integer) product.get("id");
                    projectId = Integer.toString(temp);
                    return projectId;
                }
            }
        } catch (XmlRpcException xre) {
            xre.printStackTrace();
        } catch (IllegalArgumentException e2) {
            return null;
        }
        return null;
    }

    private String getProjectIdByNameVersion4() {
        if (client == null) {
            client = initializeClient();
        }
        String status = login(client);

        if (status == null || status.equals(LOGIN_FAILURE) || status.equals(BAD_CONFIGURATION)) {
            log.warn(status);
            return null;
        }

        // get Product info
        Map<String, Object[]> bugMap = new HashMap<String, Object[]>();
        Object[] names = new Object[] { projectName };
        bugMap.put("names", names);
        Object[] bugArray = new Object[] { bugMap };

        Object createResult = executeMethod("Product.get", bugArray);
        if (createResult != null && createResult instanceof Map<?, ?>) {
            Map<?, ?> mapVersion = (Map<?, ?>) createResult;
            if (mapVersion.containsKey("products")) {
                Object result = mapVersion.get("products");
                if (result instanceof Object[]) {
                    Object[] stuff = (Object[]) result;
                    for (Object item : stuff) {
                        if (item instanceof Map<?, ?>) {
                            Map<?, ?> map = (Map<?, ?>) item;
                            if (map.get("id") != null) {
                                return map.get("id").toString();
                            }
                        }
                    }
                }
            }
        }

        return null;
    }

    @Override
    public String getProductNames() {
        XmlRpcClient client = initializeClient();
        String status = login(client);

        if (LOGIN_FAILURE.equals(status) || BAD_CONFIGURATION.equals(status)) {
            lastError = status;
            return null;
        } else {
            return getProducts(client);
        }
    }

    @Override
    public boolean hasValidCredentials() {
        XmlRpcClient client = initializeClient();
        String status = login(client);

        return !LOGIN_FAILURE.equals(status) && !BAD_CONFIGURATION.equals(status);
    }

    @Override
    public boolean hasValidProjectName() {
        if (projectName == null) {
            return false;
        }

        XmlRpcClient client = initializeClient();
        return projectExists(projectName, client);
    }

    @Override
    public String getBugURL(String endpointURL, String bugID) {
        if (endpointURL != null && bugID != null && endpointURL.endsWith("xmlrpc.cgi")) {
            return endpointURL.replace("xmlrpc.cgi", "show_bug.cgi?id=" + bugID);
        } else if (endpointURL != null) {
            if (endpointURL.endsWith("/")) {
                return endpointURL + "show_bug.cgi?id=" + bugID;
            } else {
                return endpointURL + "/show_bug.cgi?id=" + bugID;
            }
        } else {
            log.error("getBugURL() in BugzillaDefectTracker was given a null endpointURL.");
            return null;
        }
    }

    @Override
    public boolean hasValidUrl() {
        log.info("Checking Bugzilla URL.");
        XmlRpcClient client = initializeClient();

        Map<String, String> loginMap = new HashMap<String, String>();
        loginMap.put("login", " ");
        loginMap.put("password", " ");
        loginMap.put("rememberlogin", "Bugzilla_remember");

        Object[] loginArray = new Object[1];
        loginArray[0] = loginMap;

        try {
            client.execute("User.login", loginArray);
            log.warn("Shouldn't be here, we just logged into " + "Bugzilla with blank username / password.");
            return true;
        } catch (XmlRpcException e) {
            if (e.getMessage().contains("The username or password you entered is not valid")) {
                log.info("The URL was good, received an authentication warning.");
                return true;
            } else if (e.getMessage().contains("I/O error while communicating with HTTP server")) {
                log.warn("Unable to retrieve a RPC response from that URL. Returning false.");
                return false;
            } else {
                log.warn("Something went wrong. Check out the error. Returning false.", e);
                return false;
            }
        } catch (IllegalArgumentException e2) {
            log.warn("IllegalArgumentException was tripped. Returning false.");
            return false;
        }
    }

    public String getServerURLWithRpc() {
        if (url == null || url.trim().equals("")) {
            return null;
        }

        if (url.contains("xmlrpc.cgi")) {
            return url;
        }

        String tempUrl = url.trim();
        if (tempUrl.endsWith("/")) {
            tempUrl = tempUrl.concat("xmlrpc.cgi");
        } else {
            tempUrl = tempUrl.concat("/xmlrpc.cgi");
        }

        return tempUrl;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Defect> getDefectList() {

        XmlRpcClient client = initializeClient();
        if (client == null) {
            return null;
        }

        String loginStatus = login(client);
        if (loginStatus == null) {
            return null;
        }

        // TODO Pass this information back to the user
        if (loginStatus.equals(LOGIN_FAILURE) || loginStatus.equals(BAD_CONFIGURATION)) {
            log.warn("Login Failed, check credentials");
            return null;
        }

        List<Defect> returnList = new ArrayList<Defect>();
        Map<String, String> queryMap = new HashMap<String, String>();
        queryMap.put("product", projectName);

        Object queryResult = executeMethod("Bug.search", new Object[] { queryMap });
        if (queryResult instanceof HashMap) {
            Map<String, Object[]> returnedData = (HashMap<String, Object[]>) queryResult;
            Object[] bugsArray = returnedData.get("bugs");
            for (int i = 0; i < bugsArray.length; i++) {
                Object currentBug = bugsArray[i];
                Map<String, Object> currentBugHash = (HashMap<String, Object>) currentBug;
                if (currentBugHash == null) {
                    continue;
                }
                Integer id = (Integer) currentBugHash.get("id");
                Defect d = new Defect();
                d.setNativeId(String.valueOf(id));
                returnList.add(d);
            }
        } else {
            log.error("Expected a HashMap return value, but got something else instead.");
        }

        return returnList;
    }
}