io.cloudslang.content.amazon.services.helpers.AwsSignatureHelper.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudslang.content.amazon.services.helpers.AwsSignatureHelper.java

Source

/*
 * (c) Copyright 2017 EntIT Software LLC, a Micro Focus company, L.P.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License v2.0 which accompany this distribution.
 *
 * The Apache License is available 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 io.cloudslang.content.amazon.services.helpers;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import static org.apache.commons.lang3.StringUtils.isNotBlank;

import static io.cloudslang.content.amazon.entities.constants.Constants.AwsParams.AWS_REQUEST_VERSION;
import static io.cloudslang.content.amazon.entities.constants.Constants.AwsParams.DEFAULT_AMAZON_REGION;
import static io.cloudslang.content.amazon.entities.constants.Constants.Miscellaneous.AMPERSAND;
import static io.cloudslang.content.amazon.entities.constants.Constants.Miscellaneous.COLON;
import static io.cloudslang.content.amazon.entities.constants.Constants.Miscellaneous.EQUAL;
import static io.cloudslang.content.amazon.entities.constants.Constants.Miscellaneous.EMPTY;
import static io.cloudslang.content.amazon.entities.constants.Constants.Miscellaneous.LINE_SEPARATOR;
import static io.cloudslang.content.amazon.entities.constants.Constants.Miscellaneous.SCOPE_SEPARATOR;

import static io.cloudslang.content.amazon.entities.constants.Constants.Values.ONE;
import static io.cloudslang.content.amazon.entities.constants.Constants.Values.START_INDEX;

/**
 * Created by Mihai Tusa.
 * 8/8/2016.
 */
public class AwsSignatureHelper {
    private static final String DATE_FORMAT = "yyyyMMdd'T'HHmmss'Z'";
    private static final String HYPHEN = "-";
    private static final String SEMICOLON = ";";
    private static final String TIME_ZONE = "UTC";

    private static final char DOT_CHAR = '.';

    /**
     * Canonicalized (standardized) query string is formed by first sorting all the query
     * parameters, then URI encoding both the key and value and then
     * joining them, in order, separating key value pairs with an '&'.
     *
     * @param queryParameters Query parameters to be canonicalized.
     * @return A canonicalized form for the specified query parameters.
     */
    public String canonicalizedQueryString(Map<String, String> queryParameters) {
        List<Map.Entry<String, String>> sortedList = getSortedMapEntries(queryParameters);

        StringBuilder queryString = new StringBuilder();
        for (Map.Entry<String, String> entry : sortedList) {
            queryString.append(entryToQuery(entry));
        }

        if (queryString.length() > START_INDEX) {
            queryString.deleteCharAt(queryString.length() - ONE); //removing last '&'
        }

        return queryString.toString();
    }

    /**
     * Canonicalized (standardized) headers string is formed by first sorting all the header
     * parameters, then converting all header names to lowercase and
     * trimming excess white space characters out of the header values
     *
     * @param headers Headers to be canonicalized.
     * @return A canonicalized form for the specified headers.
     */
    public String canonicalizedHeadersString(Map<String, String> headers) {
        List<Map.Entry<String, String>> sortedList = getSortedMapEntries(headers);

        String header;
        String headerValue;
        StringBuilder headerString = new StringBuilder();

        for (Map.Entry<String, String> ent : sortedList) {
            header = nullToEmpty(ent.getKey()).toLowerCase();
            headerValue = nullToEmpty(ent.getValue()).trim();
            headerString.append(header).append(COLON).append(headerValue).append(LINE_SEPARATOR);
        }

        return headerString.toString();
    }

    /**
     * Creates a comma separated list of headers.
     *
     * @param headers Headers to be signed.
     * @return Comma separated list of headers to be signed.
     */
    public String getSignedHeadersString(Map<String, String> headers) {
        List<String> sortedList = new ArrayList<>(headers.keySet());
        Collections.sort(sortedList, String.CASE_INSENSITIVE_ORDER);

        StringBuilder signedHeaderString = new StringBuilder();
        for (String header : sortedList) {
            if (signedHeaderString.length() > START_INDEX) {
                signedHeaderString.append(SEMICOLON);
            }
            signedHeaderString.append(nullToEmpty(header).toLowerCase());
        }
        return signedHeaderString.toString();
    }

    /**
     * Converts a java.util.Date to an AWS specific date format.
     * The AWS date should be in (java) format "yyyyMMdd'T'HHmmss'Z'" and in time zone UTC.
     *
     * @param date Date to be formatted.
     * @return A string representing the formatted date.
     */
    public String getAmazonDateString(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_FORMAT);
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone(TIME_ZONE));
        return simpleDateFormat.format(date);
    }

    /**
     * Crates the amazon AWS credential scope (is represented by a slash-separated string of dimensions).
     *
     * @param dateStamp  Request date stamp. (Format: "yyyyMMdd")
     * @param awsRegion  AWS region to which the request is sent.
     * @param awsService AWS service to which the request is sent.
     * @return A string representing the AWS credential scope.
     */
    public String getAmazonCredentialScope(String dateStamp, String awsRegion, String awsService) {
        return dateStamp + SCOPE_SEPARATOR + awsRegion + SCOPE_SEPARATOR + awsService + SCOPE_SEPARATOR
                + AWS_REQUEST_VERSION;
    }

    /**
     * Extracts the AWS region from the endpoint.
     * If the endpoint has no region the "us-east-1" region is returned b default.
     *
     * @param endpoint AWS request endpoint.
     * @return A (lowercase alphanumeric) string representing the AWS region.
     */
    public String getAmazonRegion(String endpoint) {
        if (isNotBlank(endpoint) && endpoint.contains(HYPHEN)) {
            endpoint = endpoint.substring(3);
            return endpoint.substring(START_INDEX, endpoint.indexOf(DOT_CHAR));
        }
        return DEFAULT_AMAZON_REGION;
    }

    private String entryToQuery(Map.Entry<String, String> entry) {
        String escapedKey = nullToEmpty(UriEncoder.escapeString(entry.getKey()));
        String escapedValue = nullToEmpty(UriEncoder.escapeString(entry.getValue()));

        return escapedKey + EQUAL + escapedValue + AMPERSAND;
    }

    private String nullToEmpty(String inputString) {
        if (inputString == null) {
            return EMPTY;
        }
        return inputString;
    }

    private List<Map.Entry<String, String>> getSortedMapEntries(Map<String, String> map) {
        List<Map.Entry<String, String>> sortedList = new ArrayList<>(map.entrySet());
        Collections.sort(sortedList, new Comparator<Map.Entry<String, String>>() {
            public int compare(Map.Entry<String, String> firstEntry, Map.Entry<String, String> secondEntry) {
                return (firstEntry.getKey().compareToIgnoreCase(secondEntry.getKey()));
            }
        });
        return sortedList;
    }
}