blob: 992389879290d5b4abc1c898b517b87c4da80226 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License 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 org.jclouds.azureblob.blobstore;
import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
import org.jclouds.azure.storage.util.storageurl.StorageUrlSupplier;
import org.jclouds.blobstore.BlobRequestSigner;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
import org.jclouds.date.DateService;
import org.jclouds.date.TimeStamp;
import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.Uris;
import org.jclouds.http.options.GetOptions;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Supplier;
import com.google.common.net.HttpHeaders;
import com.google.inject.Provider;
@Singleton
public class AzureBlobRequestSigner implements BlobRequestSigner {
private static final int DEFAULT_EXPIRY_SECONDS = 15 * 60;
private static final String API_VERSION = "2017-11-09";
private final String identity;
private final URI storageUrl;
private final BlobToHttpGetOptions blob2HttpGetOptions;
private final Provider<String> timeStampProvider;
private final DateService dateService;
private final SharedKeyLiteAuthentication auth;
private final String credential;
private final boolean isSAS;
@Inject
public AzureBlobRequestSigner(
BlobToHttpGetOptions blob2HttpGetOptions, @TimeStamp Provider<String> timeStampProvider,
DateService dateService, SharedKeyLiteAuthentication auth,
@org.jclouds.location.Provider Supplier<Credentials> creds, @Named("sasAuth") boolean sasAuthentication,
StorageUrlSupplier storageUriSupplier)
throws SecurityException, NoSuchMethodException {
this.identity = creds.get().identity;
this.credential = creds.get().credential;
this.storageUrl = storageUriSupplier.get();
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
this.timeStampProvider = checkNotNull(timeStampProvider, "timeStampProvider");
this.dateService = checkNotNull(dateService, "dateService");
this.auth = auth;
this.isSAS = sasAuthentication;
}
@Override
public HttpRequest signGetBlob(String container, String name) {
return signGetBlob(container, name, DEFAULT_EXPIRY_SECONDS);
}
@Override
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
checkNotNull(container, "container");
checkNotNull(name, "name");
return sign("GET", container, name, null, timeInSeconds, null, null);
}
@Override
public HttpRequest signPutBlob(String container, Blob blob) {
return signPutBlob(container, blob, DEFAULT_EXPIRY_SECONDS);
}
@Override
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
checkNotNull(container, "container");
checkNotNull(blob, "blob");
return sign("PUT", container, blob.getMetadata().getName(), null, timeInSeconds,
blob.getMetadata().getContentMetadata().getContentLength(),
blob.getMetadata().getContentMetadata().getContentType());
}
public HttpRequest signRemoveBlob(String container, String name) {
checkNotNull(container, "container");
checkNotNull(name, "name");
return sign("DELETE", container, name, null, DEFAULT_EXPIRY_SECONDS, null, null);
}
@Override
public HttpRequest signGetBlob(String container, String name, org.jclouds.blobstore.options.GetOptions options) {
checkNotNull(container, "container");
checkNotNull(name, "name");
return sign("GET", container, name, blob2HttpGetOptions.apply(checkNotNull(options, "options")),
DEFAULT_EXPIRY_SECONDS, null, null);
}
/**
* method to sign HttpRequest when SharedKey Authentication is used
*/
private HttpRequest signKey(String method, String container, String name, @Nullable GetOptions options, long expires, @Nullable Long contentLength, @Nullable String contentType) {
checkNotNull(method, "method");
checkNotNull(container, "container");
checkNotNull(name, "name");
String nowString = timeStampProvider.get();
Date now = dateService.rfc1123DateParse(nowString);
Date expiration = new Date(now.getTime() + TimeUnit.SECONDS.toMillis(expires));
String iso8601 = dateService.iso8601SecondsDateFormat(expiration);
String signedPermission;
if (method.equals("PUT")) {
signedPermission = "w";
} else if (method.equals("DELETE")) {
signedPermission = "d";
} else {
signedPermission = "r";
}
HttpRequest.Builder request = HttpRequest.builder()
.method(method)
.endpoint(Uris.uriBuilder(storageUrl).appendPath(container).appendPath(name).build())
.replaceHeader(HttpHeaders.DATE, nowString)
.addQueryParam("sv", API_VERSION)
.addQueryParam("se", iso8601)
.addQueryParam("sr", "b") // blob resource
.addQueryParam("sp", signedPermission); // permission
request = setHeaders(request, method, options, contentLength, contentType);
String stringToSign =
signedPermission + "\n" + // signedpermission
"\n" + // signedstart
iso8601 + "\n" + // signedexpiry
"/blob/" + identity + "/" + container + "/" + name + "\n" + // canonicalizedresource
"\n" + // signedidentifier
"\n" + // signedIP
"\n" + // signedProtocol
API_VERSION + "\n" + // signedversion
"\n" + // rscc
"\n" + // rscd
"\n" + // rsce
"\n" + // rscl
""; // rsct
String signature = auth.calculateSignature(stringToSign);
request.addQueryParam("sig", signature);
return request.build();
}
private HttpRequest.Builder setHeaders(HttpRequest.Builder request, String method, @Nullable GetOptions options, @Nullable Long contentLength, @Nullable String contentType){
if (contentLength != null) {
request.replaceHeader(HttpHeaders.CONTENT_LENGTH, contentLength.toString());
}
if (contentType != null) {
request.replaceHeader("x-ms-blob-content-type", contentType);
}
if (options != null) {
request.headers(options.buildRequestHeaders());
}
if (method.equals("PUT")) {
request.replaceHeader("x-ms-blob-type", "BlockBlob");
}
return request;
}
/**
* method, compatible with SAS Authentication
*/
private HttpRequest signSAS(String method, String container, String name, @Nullable GetOptions options, long expires, @Nullable Long contentLength, @Nullable String contentType) {
checkNotNull(method, "method");
checkNotNull(container, "container");
checkNotNull(name, "name");
String nowString = timeStampProvider.get();
HttpRequest.Builder request = HttpRequest.builder()
.method(method)
.endpoint(Uris.uriBuilder(storageUrl).appendPath(container).appendPath(name).query(this.credential).build())
.replaceHeader(HttpHeaders.DATE, nowString);
request = setHeaders(request, method, options, contentLength, contentType);
return request.build();
}
/**
* modified sign() method, which acts depending on the Auth input.
*/
public HttpRequest sign(String method, String container, String name, @Nullable GetOptions options, long expires, @Nullable Long contentLength, @Nullable String contentType) {
if (isSAS) {
return signSAS(method, container, name, options, expires, contentLength, contentType);
}
return signKey(method, container, name, options, expires, contentLength, contentType);
}
}