| /* |
| * 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); |
| } |
| } |