org.nuxeo.sheridan.S3TempSignedURLBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.sheridan.S3TempSignedURLBuilder.java

Source

/*
 * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library 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.
 *
 * Contributors:
 *     Thibaud Arguillere
 */
package org.nuxeo.sheridan;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.runtime.api.Framework;

import com.amazonaws.HttpMethod;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;

/**
 * This class builds a Temporary Signed Url to access a file in a S3 bucket.
 * <p>
 * The configuration <b>must</b> (typically, in nuxeo.conf) contain at least these 2 keys: <code>sheridan.s3.key</code>
 * and <code>sheridan.s3.secret</code>, which are the key ID and secret key used to get access to AWS.
 * <p>
 * Also, it is possible to set a bucket in the <code>sheridan.s3.bucket</code> configuration parameter. This bucket will
 * be used by default when no bucket is passed in some APIs.
 * <p>
 * Once these keys have been defined in the configuration, the class allows to build a temporary signed URL to an object
 * stored in AWS S3, in a specified bucket.
 * 
 * @since 7.10
 */
public class S3TempSignedURLBuilder {

    public static final String CONF_KEY_NAME_ACCESS_KEY = "sheridan.s3.key";

    public static final String CONF_KEY_NAME_SECRET_KEY = "sheridan.s3.secret";

    public static final String CONF_KEY_NAME_BUCKET = "sheridan.s3.bucket";

    public static final int DEFAULT_EXPIRE = 60 * 20; // 20mn

    private static AWSCredentialsProvider awsCredentialsProvider = null;

    protected AmazonS3 s3;

    protected static String awsAccessKeyId = null;

    protected static String awsSecretAccessKey = null;

    protected static String awsBucket = null;

    public S3TempSignedURLBuilder() {

        if (StringUtils.isBlank(awsBucket)) {
            awsBucket = Framework.getProperty(CONF_KEY_NAME_BUCKET);
            // Having no bucket name in the config is ok if a bucket is passed as argument to buld().
        }

        buildCredentiaProvider();
        if (awsCredentialsProvider == null) {
            throw new NuxeoException("AWS Access Key ID (" + CONF_KEY_NAME_ACCESS_KEY
                    + ") and/or Secret Access Key (" + CONF_KEY_NAME_SECRET_KEY
                    + ") are missing or invalid. Are they correctly set-up in the configuration?");

        }

        s3 = new AmazonS3Client(awsCredentialsProvider);
    }

    /**
     * Return an url as string. This url is a temporary signed url giving access to the object for
     * <code>expireInSeconds</expireInSeconds> seconds. After this time, the object cannot be accessed anymore with this URL.
     * <p>
     * Some default values apply:
     * <p>
     * <ul>
     * <li>If <code>bucket</code> is empty (null, "", " ", ....), the bucket defined in the configuration is used.</li>
     * <li>If <code>expireInSeconds</code> is less than 1, the default
     * <code>S3TempSignedURLBuilder.DEFAULT_EXPIRE</code> is used</li> <li><code>contentType</code> and
     * <code>contentDisposition</code> can be null or "", but it is recommended to set them to make sure the is no
     * ambiguity when the URL is used (a key without a file extension for example)</li> </ul>
     * <p>
     * 
     * @param bucket
     * @param objectKey
     * @param expireInSeconds
     * @param contentType
     * @param contentDisposition
     * @return the temporary signed Url
     * @throws IOException
     * @since 7.10
     */
    public String build(String bucket, String objectKey, int expireInSeconds, String contentType,
            String contentDisposition) throws IOException {

        if (StringUtils.isBlank(bucket)) {
            bucket = awsBucket;
        }
        if (StringUtils.isBlank(bucket)) {
            throw new NuxeoException(
                    "No bucket provided, and configuration key " + CONF_KEY_NAME_BUCKET + " is missing.");
        }

        Date expiration = new Date();
        if (expireInSeconds < 1) {
            expireInSeconds = DEFAULT_EXPIRE;
        }
        expiration.setTime(expiration.getTime() + (expireInSeconds * 1000));

        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, objectKey, HttpMethod.GET);

        // Do we need these?
        if (StringUtils.isNotBlank(contentType)) {
            request.addRequestParameter("response-content-type", contentType);
        }
        if (StringUtils.isNotBlank(contentDisposition)) {
            request.addRequestParameter("response-content-disposition", contentDisposition);
        }

        request.setExpiration(expiration);
        URL url = s3.generatePresignedUrl(request);

        try {
            URI uri = url.toURI();
            return uri.toString();
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }

    }

    /**
     * Return an url as string. This url is a temporary signed url giving access to the object for
     * <code>expireInSeconds</expireInSeconds> seconds. After this time, the object cannot be accessed anymore with this URL.
     * <p>
     * Some default values apply:
     * <p>
     * <ul>
     * <li>The bucket used is the one defined in the configuration.</li>
     * <li>If <code>expireInSeconds</code> is less than 1, the default
     * <code>S3TempSignedURLBuilder.DEFAULT_EXPIRE</code> is used</li> <li><code>contentType</code> and
     * <code>contentDisposition</code> can be null or "", but it is recommended to set them to make sure the is no
     * ambiguity when the URL is used (a key without a file extension for example)</li> </ul>
     * <p>
     * 
     * @param objectKey
     * @param expireInSeconds
     * @param contentType
     * @param contentDisposition
     * @return the temporary signed Url
     * @throws IOException
     * @since 7.10
     */
    public String build(String objectKey, int expireInSeconds, String contentType, String contentDisposition)
            throws IOException {

        return build(awsBucket, objectKey, expireInSeconds, contentType, contentDisposition);

    }

    protected void buildCredentiaProvider() {

        if (awsCredentialsProvider != null) {
            return;
        }

        awsAccessKeyId = Framework.getProperty(CONF_KEY_NAME_ACCESS_KEY);
        awsSecretAccessKey = Framework.getProperty(CONF_KEY_NAME_SECRET_KEY);

        if (StringUtils.isNotBlank(awsAccessKeyId) && StringUtils.isNotBlank(awsSecretAccessKey)) {
            awsCredentialsProvider = new SimpleAWSCredentialProvider(awsAccessKeyId, awsSecretAccessKey);
        }
    }

}