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

com.ibm.cloud.objectstorage.services.s3.internal.S3Signer Maven / Gradle / Ivy

Go to download

The IBM COS Java SDK for Amazon S3 module holds the client classes that are used for communicating with IBM Cloud Object Storage Service

The newest version!
/*
 * Copyright 2010-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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.ibm.cloud.objectstorage.services.s3.internal;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ibm.cloud.objectstorage.Request;
import com.ibm.cloud.objectstorage.SignableRequest;
import com.ibm.cloud.objectstorage.auth.AWSCredentials;
import com.ibm.cloud.objectstorage.auth.AWSSessionCredentials;
import com.ibm.cloud.objectstorage.auth.AbstractAWSSigner;
import com.ibm.cloud.objectstorage.auth.Signer;
import com.ibm.cloud.objectstorage.auth.SigningAlgorithm;
import com.ibm.cloud.objectstorage.services.s3.Headers;
import com.ibm.cloud.objectstorage.util.SdkHttpUtils;

/**
 * Implementation of the {@linkplain Signer} interface specific to S3's signing
 * algorithm.
 */
public class S3Signer extends AbstractAWSSigner {

    /** Shared log for signing debug output */
    private static final Log log = LogFactory.getLog(S3Signer.class);

    /**
     * The HTTP verb (GET, PUT, HEAD, DELETE) the request to sign is using.
     *
     * TODO: We need to know the HTTP verb in order to create the authentication
     * signature, but we don't have easy access to it through the request
     * object.
     *
     * Maybe it'd be better for the S3 signer (or all signers?) to work directly
     * off of the HttpRequest instead of the Request object?
     */
    private final String httpVerb;

    /**
     * The canonical resource path portion of the S3 string to sign. Examples:
     * "/", "//", or "//"
     *
     * TODO: We don't want to hold the resource path as member data in the S3
     * signer, but we need access to it and can't get it through the request
     * yet.
     */
    private final String resourcePath;

    /**
     * The names of all the user-specified query parameters that should be
     * included in the canonical request, in addition to those default
     * parameters that are always signed.
     *
     * @see RestUtils#makeS3CanonicalString(String, String, Request, String)
     */
    private final Set additionalQueryParamsToSign;

    /**
     * Create a dummy instance of the S3Signer. This constructor will be invoked
     * by internal config via reflection.
     */
    public S3Signer() {
        /*
         * NOTE: don't delegate to the other ctors, otherwise an IAE will be
         * thrown since the resourcePath is lazily initialized to null.
         */
        this.httpVerb = null;
        this.resourcePath = null;
        this.additionalQueryParamsToSign = null;
    }

    /**
     * Constructs a new S3Signer to sign requests based on the Amazon Web Services credentials,
     * HTTP method and canonical S3 resource path.
     *
     * @param httpVerb
     *            The HTTP verb (GET, PUT, POST, HEAD, DELETE) the request is
     *            using.
     * @param resourcePath
     *            The canonical S3 resource path (ex: "/", "//", or
     *            "//".
     */
    public S3Signer(String httpVerb, String resourcePath) {
        this(httpVerb, resourcePath, null);
    }

    /**
     * Constructs a new S3Signer to sign requests based on the Amazon Web Services credentials,
     * HTTP method and canonical S3 resource path.
     *
     * @param httpVerb
     *            The HTTP verb (GET, PUT, POST, HEAD, DELETE) the request is
     *            using.
     * @param resourcePath
     *            The canonical S3 resource path (ex: "/", "//", or
     *            "//".
     * @param additionalQueryParamsToSign
     *            A collection of user-specified query parameters that should be
     *            included in the canonical request, in addition to those
     *            default parameters that are always signed.
     *
     * @see RestUtils#makeS3CanonicalString(String, String, Request, String)
     */
    public S3Signer(String httpVerb, String resourcePath,
            Collection additionalQueryParamsToSign) {
        if (resourcePath == null)
            throw new IllegalArgumentException(
                    "Parameter resourcePath is empty");

        this.httpVerb = httpVerb;
        this.resourcePath = resourcePath;
        this.additionalQueryParamsToSign = additionalQueryParamsToSign == null
                ? null
                : Collections.unmodifiableSet(new HashSet(
                        additionalQueryParamsToSign));
    }

    @Override
    public void sign(SignableRequest request, AWSCredentials credentials) {

        if (resourcePath == null) {
            throw new UnsupportedOperationException(
                    "Cannot sign a request using a dummy S3Signer instance with "
                            + "no resource path");
        }

        if (credentials == null || credentials.getAWSSecretKey() == null) {
            log.debug("Canonical string will not be signed, as no AWS Secret Key was provided");
            return;
        }

        AWSCredentials sanitizedCredentials = sanitizeCredentials(credentials);
        if (sanitizedCredentials instanceof AWSSessionCredentials) {
            addSessionCredentials(request,
                    (AWSSessionCredentials) sanitizedCredentials);
        }

        /*
         * In s3 sigv2, the way slash characters are encoded should be
         * consistent in both the request url and the encoded resource path.
         * Since we have to encode "//" to "/%2F" in the request url to make
         * httpclient works, we need to do the same encoding here for the
         * resource path.
         */
        String encodedResourcePath = SdkHttpUtils.appendUri(
                request.getEndpoint().getPath(), SdkHttpUtils.urlEncode(resourcePath, true),
                true);

        int timeOffset = request.getTimeOffset();
        Date date = getSignatureDate(timeOffset);
        request.addHeader(Headers.DATE, ServiceUtils.formatRfc822Date(date));
        String canonicalString = RestUtils.makeS3CanonicalString(httpVerb,
                encodedResourcePath, request, null, additionalQueryParamsToSign);
        log.debug("Calculated string to sign:\n\"" + canonicalString + "\"");

        String signature = super.signAndBase64Encode(canonicalString,
                sanitizedCredentials.getAWSSecretKey(),
                SigningAlgorithm.HmacSHA1);
        request.addHeader("Authorization",
                "AWS " + sanitizedCredentials.getAWSAccessKeyId() + ":"
                        + signature);
    }

    @Override
    protected void addSessionCredentials(SignableRequest request,
            AWSSessionCredentials credentials) {
        request.addHeader("x-amz-security-token", credentials.getSessionToken());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy