org.artifactory.repo.remote.browse.S3RepositorySecuredHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.artifactory.repo.remote.browse.S3RepositorySecuredHelper.java

Source

/*
 * Artifactory is a binaries repository manager.
 * Copyright (C) 2013 JFrog Ltd.
 *
 * Artifactory is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Artifactory is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Artifactory.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.artifactory.repo.remote.browse;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.compress.utils.CharsetNames;
import org.artifactory.repo.HttpRepo;
import org.artifactory.security.crypto.CryptoHelper;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * @author mamo
 */
public class S3RepositorySecuredHelper {

    public static final String S3_ENDPOINT = "s3.amazonaws.com";
    private static final String HMAC_SHA1 = "HmacSHA1";

    /**
     * Builds a secured rest url request for Amazon S3
     *
     * @param url        The path to the item, for example https://s3.amazonaws.com/bucket/folder/file.ext
     * @param prefix     The prefix to the item, for example folder/file.ext
     * @param httpRepo   The repository assuming to contain the access key/secret key in username/password, respectively
     * @param expiration The time of expiration for the generated request in millis
     * @return A secured rest url request
     * @throws Exception
     */
    public static String buildSecuredS3RequestUrl(String url, String prefix, HttpRepo httpRepo, long expiration) {
        String awsAccessKey = httpRepo.getUsername();
        String awsSecretKey = CryptoHelper.decryptIfNeeded(httpRepo.getPassword());

        try {
            String endpoint = S3_ENDPOINT;

            String protocol = url.startsWith("https:") ? "https://" : "http://";
            String bucketName = getBucketName(url);
            String hostname = buildBucketHostname(bucketName, endpoint);
            long expires = expiration / 1000;

            String bucketPath = buildBucketPath(endpoint, hostname);
            String prefixPath = buildPrefixPath(hostname, bucketName, prefix);
            String signature = encodeUrl(signWithHmacSha1(awsSecretKey, "GET\n" + "\n\n" + //content-type,content-md5
                    String.valueOf(expires) + "\n" + "/" + bucketPath + prefixPath));

            String uriPath = prefixPath + "?AWSAccessKeyId=" + awsAccessKey + "&Expires=" + expires + "&Signature="
                    + signature;

            return protocol + hostname + "/" + uriPath;

        } catch (Exception e) {
            throw new RuntimeException("Could not get signed url for " + url + " and bucket " + getBucketName(url),
                    e);
        }
    }

    public static String getPrefix(String url) {
        if (!url.endsWith("/")) {
            url += "/";
        }
        String str = S3_ENDPOINT + "/";
        int i = url.indexOf(str);
        String substring = url.substring(i + str.length());
        int i1 = substring.indexOf("/");
        if (i1 != -1) {
            return substring.substring(i1 + 1);
        } else {
            return "";
        }
    }

    private static String getBucketName(String url) {
        String endpoint = S3_ENDPOINT;
        String substring = url.substring(url.indexOf(endpoint) + endpoint.length());
        if (substring.startsWith("/")) {
            substring = substring.substring(1);
        }
        int i = substring.indexOf("/");
        return i != -1 ? substring.substring(0, i) : "";
    }

    private static String buildPrefixPath(String hostname, String bucketName, String prefix) throws Exception {
        String uriPath = "";
        String encodedPrefix = encodePath(prefix, "/");
        if (!S3_ENDPOINT.equals(hostname)) {
            uriPath = prefix != null ? encodedPrefix : "";
        } else {
            uriPath = bucketName + (prefix != null ? "/" + encodedPrefix : "");
        }
        return uriPath;
    }

    private static String buildBucketPath(String endpoint, String hostname) {
        String path = "";
        if (!endpoint.equals(hostname)) {
            int i = hostname.lastIndexOf("." + endpoint);
            path = i > 0 ? hostname.substring(0, i) + "/" : hostname + "/";
        }
        return path;
    }

    private static String buildBucketHostname(String bucketName, String s3Endpoint) {
        return bucketName + "." + s3Endpoint;
    }

    private static String signWithHmacSha1(String awsSecretKey, String canonicalString) throws Exception {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(awsSecretKey.getBytes(CharsetNames.UTF_8), HMAC_SHA1);
            Mac mac = Mac.getInstance(HMAC_SHA1);
            mac.init(signingKey);
            byte[] b64 = Base64.encodeBase64(mac.doFinal(canonicalString.getBytes(CharsetNames.UTF_8)));
            return new String(b64, CharsetNames.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("Could not sign with " + HMAC_SHA1, e);
        }
    }

    private static String encodeUrl(String string) {
        try {
            return URLEncoder.encode(string, CharsetNames.UTF_8).replace("+", "%20").replace("%40", "@");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Could not encode string " + string, e);
        }
    }

    private static String encodePath(String path, String delimiter) throws Exception {
        StringBuilder sb = new StringBuilder();
        String split[] = path.split(delimiter);
        for (int i = 0; i < split.length; i++) {
            sb.append(encodeUrl(split[i]));
            if (i < split.length - 1) {
                sb.append(delimiter);
            }
        }
        return sb.toString();
    }
}