com.rapid7.Plugin.java Source code

Java tutorial

Introduction

Here is the source code for com.rapid7.Plugin.java

Source

/*
 * NOTE: Most of this code was adapted from https://github.com/cjnygard/rest-maven-plugin
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache 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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.rapid7;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status.Family;

import org.apache.commons.io.IOUtils;
import org.apache.maven.model.FileSet;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import org.codehaus.plexus.util.FileUtils;

/**
 * Codesigns file using the Platform Delivery cert-protector service.
 *
 */
@Mojo(name = "codesign")
public class Plugin extends AbstractMojo {

    /**
     * A URL path to the base of the REST request resource.
     *
     * This URL path is the base path, and can be used with multiple instances
     * (executions) in combination with the <code>resource</code> element to
     * specify different URL resources with a common base URL.
     *
     */
    @Parameter(defaultValue = "https://filesigner.osdc.lax.rapid7.com", property = "endpoint")
    private URI endpoint;

    /**
     * A resource path added to the endpoint URL to access the REST resource.
     *
     * The <code>resource</code> path will be concatenated onto the
     * <code>endpoint</code> URL to create the full resource path.
     *
     * Query parameters can be added to the URL <code>resource</code> but the
     * preference is to use the <code>queryParams</code> map to add parameters
     * to the URL.
     */
    @Parameter(defaultValue = "/sign/codesign", property = "resource")
    private String resource;

    /**
     * The username to use with the filesigner service via Basic HTTP auth.
     */
    @Parameter(property = "username")
    private String username;

    /**
     * The password to use with the filesigner service via Basic HTTP auth.
     */
    @Parameter(property = "password")
    private String password;

    /**
     * The method to use for the REST request.
     *
     * The REST request method can be configured via the <code>method</code>
     * tag.
     *
     * Defaults to <code>PUT</code>
     *
     */
    @Parameter(defaultValue = "PUT", property = "method")
    private String method;

    /**
     * A list of {@link org.apache.maven.model.FileSet} rules to select files
     * and directories.
     *
     * This list of <code>fileset</code> elements will be used to gather all the
     * files to be submitted in the REST request. One REST request will be made
     * per file.
     */
    @Parameter(property = "filesets")
    private List<FileSet> filesets = new ArrayList<>();

    /**
     * A {@link org.apache.maven.model.FileSet} rule to select files to send in
     * the REST request.
     *
     * The fileset will be used to gather all the files to be submitted in the
     * REST request. One REST request will be made per file.
     *
     * Internally, this element will be added to the list of
     * <code>filesets</code>, so it will be processed in addition to the list of
     * <code>filesets</code>
     */
    @Parameter(property = "fileset")
    private FileSet fileset;

    /**
     * Path where REST query result files are stored.
     *
     * Defaults to <code>${project.build.directory}/rest</code>
     *
     */
    @Parameter(defaultValue = "${project.build.directory}/signed", property = "outputDir", required = true)
    private File outputDir;

    @Override
    public void execute() throws MojoExecutionException {
        List<File> files = getFilesToProcess();
        if ((null == files) || (files.size() <= 0)) {
            getLog().info("No files to sign");
            return;
        }

        // Skip if username is set but password is blank.
        // Since we commonly pass the password via environment variables in Jenkins, this makes it easy
        // to skip code signing in development environments where the password variable isn't set.
        if (null != getUsername() && !getUsername().isEmpty()) {
            if (null == getPassword() || getPassword().isEmpty()) {
                getLog().warn(String.format("Skipping codesigning because no password is set for user: %s",
                        getUsername()));
                return;
            }
        }

        validateOutputDir();

        Client client = ClientBuilder.newClient();

        WebTarget baseTarget = client.target(getEndpoint());
        if (null != getResource()) {
            getLog().debug(String.format("Setting resource [%s]", getResource()));
            baseTarget = baseTarget.path(getResource());
        }

        Invocation.Builder builder = baseTarget.request().accept(getResponseType())
                .header("Content-Type", getRequestType()).header("Authorization", getBasicAuth());

        for (File f : files) {
            getLog().info(String.format("Uploading %s to %s%s", f.getName(), getEndpoint(), getResource()));
            processResponse(builder.method(getMethod(), Entity.entity(f, getRequestType())), f.getName());
        }
    }

    protected List<File> getFilesToProcess() throws MojoExecutionException {
        List<File> files = new ArrayList<>();
        if (null != getFileset()) {
            if (null == getFilesets()) {
                filesets = new ArrayList<>();
            }
            getFilesets().add(getFileset());

        }
        if (null != getFilesets()) {
            for (FileSet fs : getFilesets()) {
                if ((null != fs) && (null != fs.getDirectory())) {
                    FileSetTransformer fileMgr = new FileSetTransformer(fs);
                    files.addAll(fileMgr.toFileList());
                }
            }
        }
        return files;
    }

    protected void pipeToFile(InputStream stream, File outputFile) throws IOException {
        getLog().info(String.format("Writing signed file: %s", outputFile.getCanonicalPath()));
        OutputStream outStream = new FileOutputStream(outputFile);

        byte[] buffer = new byte[8 * 1024];
        int bytesRead;
        while ((bytesRead = stream.read(buffer)) != -1) {
            outStream.write(buffer, 0, bytesRead);
        }
        IOUtils.closeQuietly(stream);
        IOUtils.closeQuietly(outStream);
    }

    private void processResponse(Response response, String outputFilename) throws MojoExecutionException {
        if (response.getStatusInfo().getFamily() == Family.SUCCESSFUL) {
            getLog().debug(String.format("Status: [%d]", response.getStatus()));
            InputStream in = response.readEntity(InputStream.class);
            try {
                File of = new File(getOutputDir(), outputFilename);
                pipeToFile(in, of);
            } catch (IOException ex) {
                getLog().debug(String.format("IOException: [%s]", ex.toString()));
                throw new MojoExecutionException(String.format("IOException: [%s]", ex.getMessage()));
            }

        } else {
            getLog().warn(String.format("Error code: [%d]: %s", response.getStatus(), response.getStatusInfo()));
            getLog().debug(response.getEntity().toString());
            throw new MojoExecutionException(
                    String.format("Error code: [%d]: %s", response.getStatus(), response.getStatusInfo()));
        }
    }

    protected boolean validateOutputDir() throws MojoExecutionException {
        try {
            if (!outputDir.isDirectory()) {
                if (outputDir.isFile()) {
                    getLog().error(String.format("Error: [%s] is not a directory", outputDir.getCanonicalPath()));
                } else {
                    if (!outputDir.mkdirs()) {
                        getLog().error(
                                String.format("Error: Unable to create path[%s]", outputDir.getCanonicalPath()));

                    }
                }
            }
        }

        catch (IOException ex) {
            getLog().error(String.format("IOException: [%s]", ex.toString()));
            throw new MojoExecutionException(String.format("Unable to create destination dir [%s]: [%s]",
                    outputDir.toString(), ex.toString()));
        }
        return true;
    }

    public final class FileSetTransformer {

        private final FileSet fileSet;

        private FileSetTransformer(FileSet fileSet) {
            this.fileSet = fileSet;
        }

        public List<File> toFileList() throws MojoExecutionException {
            return toFileList(fileSet);
        }

        public List<File> toFileList(FileSet fs) throws MojoExecutionException {
            try {
                if (fs.getDirectory() != null) {
                    File directory = new File(fs.getDirectory());
                    String includes = toString(fs.getIncludes());
                    String excludes = toString(fs.getExcludes());
                    return FileUtils.getFiles(directory, includes, excludes);
                } else {
                    getLog().warn(String.format("Fileset [%s] directory empty", fs.toString()));
                    return new ArrayList<>();
                }
            } catch (IOException e) {
                throw new MojoExecutionException(
                        String.format("Unable to get paths to fileset [%s]", fs.toString()), e);
            }
        }

        private String toString(List<String> strings) {
            StringBuilder sb = new StringBuilder();
            for (String string : strings) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append(string);
            }
            return sb.toString();
        }
    }

    /**
     * @return the endpoint
     */
    private String getBasicAuth() {
        String token = getUsername() + ":" + getPassword();
        return "Basic " + Base64.getEncoder().encodeToString(token.getBytes());
    }

    /**
     * @return the endpoint
     */
    public URI getEndpoint() {
        return endpoint;
    }

    /**
     * @return the filesets
     */
    public List<FileSet> getFilesets() {
        return filesets;
    }

    /**
     * @return the fileset
     */
    public FileSet getFileset() {
        return fileset;
    }

    /**
     * @return the method
     */
    public String getMethod() {
        return method;
    }

    /**
     * @return the outputDir
     */
    public File getOutputDir() {
        return outputDir;
    }

    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }

    /**
     * @return the resource
     */
    public String getResource() {
        return resource;
    }

    /**
     * @return the requestType
     */
    public MediaType getRequestType() {
        return MediaType.APPLICATION_OCTET_STREAM_TYPE;
    }

    /**
     * @return the responseType
     */
    public MediaType getResponseType() {
        return MediaType.APPLICATION_OCTET_STREAM_TYPE;
    }

    /**
     * @return the username
     */
    public String getUsername() {
        return username;
    }
}