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

io.signpath.signpathclient.api.http.SignPathApiHttpClient Maven / Gradle / Ivy

package io.signpath.signpathclient.api.http;

import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import io.signpath.signpathclient.SignPathClient;
import io.signpath.signpathclient.SignPathClientException;
import io.signpath.signpathclient.api.model.SigningRequest;
import io.signpath.signpathclient.api.model.SigningRequestSubmitResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.apache.commons.lang.Validate;

/**
 *
 * @author rober
 */
public class SignPathApiHttpClient {

    public static final String BASE_URL = "https://app.signpath.io/API/";
    
    private static final String ARTIFACT_PARAM_NAME = "Artifact";
    private static final int NUMBER_OF_RETRIES = 4;

    private OkHttpClient okHttpClient = new OkHttpClient();
    private final String signPathApiBaseUrl;

    public SignPathApiHttpClient(String signPathApiBaseUrl) {
        this.signPathApiBaseUrl = signPathApiBaseUrl;
    }

    public SigningRequest getSigningRequestHttp(String ciUserToken,
            String tbsToken,
            String organizationId,
            String signingRequestId) {

        String url = "v1/{OrganizationId}/SigningRequests/{SigningRequestId}";
        url = url.replace("{OrganizationId}", organizationId);
        url = url.replace("{SigningRequestId}", signingRequestId);

        Request request = new Request.Builder()
                .url(this.signPathApiBaseUrl + url)
                .addHeader("Authorization", this.buildAuthorizationHeaderValue(ciUserToken, tbsToken))
                .build();

        okhttp3.Call call = okHttpClient.newCall(request);
        okhttp3.Response response = SignPathApiHttpClient.executeWithRetryOkHttp(call);
        SigningRequest signingRequest = new SigningRequest();
        if (response.isSuccessful() && response.body() != null) {
            try {
                Moshi moshi = new Moshi.Builder().build();
                JsonAdapter jsonAdapter = moshi.adapter(SigningRequest.class).lenient();

                signingRequest = jsonAdapter.fromJson(response.body().string());
            } catch (IOException ex) {
                Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return signingRequest;
    }

    public String submitSigningRequestHttp(
            String ciUserToken,
            String tbsToken,
            String organizationId,
            File artifact,
            String projectSlug,
            String signingPolicySlug,
            String artifactConfigurationSlug,
            String description,
            Map origin) {

        String url = "v1/{OrganizationId}/SigningRequests";
        url = url.replace("{OrganizationId}", organizationId);

        MultipartBody.Builder requestBuilder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart(ARTIFACT_PARAM_NAME, artifact.getName(),
                        RequestBody.create(MediaType.parse("application/octet-stream"), artifact))
                .addFormDataPart("ProjectSlug", projectSlug)
                .addFormDataPart("SigningPolicySlug", signingPolicySlug);

        if (artifactConfigurationSlug != null && !artifactConfigurationSlug.isEmpty()) {
            requestBuilder.addFormDataPart("ArtifactConfigurationSlug", artifactConfigurationSlug);
        }
        if (description != null && !description.isEmpty()) {
            requestBuilder.addFormDataPart("Description", description);
        }

        if (origin != null) {
            String prefix = "Origin.";
            for (String key : origin.keySet()) {
                String value = origin.get(key);
                RequestBody originRequestBody;
                if (value.startsWith("@")) {
                    String filePath = value.substring(1);
                    requestBuilder.addFormDataPart(prefix + key, ARTIFACT_PARAM_NAME,
                            RequestBody.create(MediaType.parse("application/octet-stream"), new File(filePath)));

                } else {
                    requestBuilder.addFormDataPart(prefix + key, value);
                }
            }
        }

        RequestBody requestBody = requestBuilder.build();

        Request request = new Request.Builder()
                .url(this.signPathApiBaseUrl + url)
                .addHeader("Authorization", this.buildAuthorizationHeaderValue(ciUserToken, tbsToken))
                .post(requestBody)
                .build();

        okhttp3.Call call = okHttpClient.newCall(request);
        okhttp3.Response response = SignPathApiHttpClient.executeWithRetryOkHttp(call);
        SigningRequestSubmitResponse signingRequestSubmitResponse = new SigningRequestSubmitResponse();
        if (response.isSuccessful() && response.body() != null) {
            try {
                Moshi moshi = new Moshi.Builder().build();
                JsonAdapter jsonAdapter = moshi.adapter(SigningRequestSubmitResponse.class).lenient();
                signingRequestSubmitResponse = jsonAdapter.fromJson(response.body().string());
            } catch (IOException ex) {
                Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return signingRequestSubmitResponse.getSigningRequestId();
    }

    public void downloadSignedArtifactHttp(
            String ciUserToken,
            String tbsToken,
            String organizationId,
            String signingRequestId,
            File artifactTargetFile) {

        String url = "v1/{OrganizationId}/SigningRequests/{SigningRequestId}/SignedArtifact";
        url = url.replace("{OrganizationId}", organizationId);
        url = url.replace("{SigningRequestId}", signingRequestId);

        Request request = new Request.Builder()
                .url(this.signPathApiBaseUrl + url)
                .addHeader("Authorization", this.buildAuthorizationHeaderValue(ciUserToken, tbsToken))
                .build();

        okhttp3.Call call = okHttpClient.newCall(request);
        okhttp3.Response response = SignPathApiHttpClient.executeWithRetryOkHttp(call);

        try ( InputStream stream = response.body().byteStream();  FileOutputStream outStream = new FileOutputStream(artifactTargetFile);) {
            copyStream(stream, outStream);
            outStream.close();
        } catch (IOException ex) {
            Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, null, ex);
            throw new SignPathClientException("Artifact download error", ex);
        }
    }

    private static okhttp3.Response executeWithRetryOkHttp(okhttp3.Call call) throws SignPathClientException {
        int numberOfTries = 0;
        int delayInSeconds = 1;
        okhttp3.Response lastResponse;
        Exception lastException = null;
        while (numberOfTries <= NUMBER_OF_RETRIES) {
            try {
                // make sure we don't call execute more than once per call object
                lastResponse = call.clone().execute();
                if (!IsTransientHttpError(lastResponse.code())) {
                    // the response is either successful or not transient
                    // that's why there is no sense in trying again
                    // we return whatever we have
                    if (!lastResponse.isSuccessful()) {
                        throw new SignPathClientException(
                                "The SignPath API call cannot be completed due to the following error: "
                                + "Response Code: " + lastResponse.code() + " - " + lastResponse.body().string());
                    }
                    return lastResponse;
                }
            } catch (IOException ex) {
                lastException = ex;
                Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, "encountered io exception -> retry triggered", ex);
            }
            try {
                Thread.sleep(delayInSeconds * 1000);
            } catch (InterruptedException ex) {
                Logger.getLogger(SignPathClient.class.getName()).log(Level.SEVERE, null, ex);
            }
            numberOfTries++;
            delayInSeconds *= 3; // each try increases the delay three times
        }
        throw new SignPathClientException("SignPath API communication error.", lastException);
    }

    private String buildAuthorizationHeaderValue(String ciUserToken, String tbsToken) {
        Validate.notEmpty(ciUserToken);
        //Validate.notEmpty(tbsToken);
        if (tbsToken == null || tbsToken.isEmpty()) {
            return String.format("Bearer %s", ciUserToken);
        }
        return String.format("Bearer %s:%s", ciUserToken, tbsToken);
    }

    private static boolean IsTransientHttpError(int httpResponseCode) {
        return httpResponseCode == 408 // Request Timeout
                || (httpResponseCode >= 500 && httpResponseCode < 600);
    }

    private void copyStream(InputStream input, OutputStream output) throws IOException {
        byte[] buf = new byte[8192];
        int length;
        while ((length = input.read(buf)) > 0) {
            output.write(buf, 0, length);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy