Repository: jclouds
Updated Branches:
  refs/heads/master 13cf2cbab -> 1e04eafba


JCLOUDS-255: JCLOUDS-1109: Azure signed URLs expiration

This commit also changes signed URLs from header-based to query
parameter-based.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/1e04eafb
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/1e04eafb
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/1e04eafb

Branch: refs/heads/master
Commit: 1e04eafbabbeeb0a43bea6657738022b16cab4f8
Parents: 13cf2cb
Author: Andrew Gaul <[email protected]>
Authored: Thu Jan 21 20:12:02 2016 -0800
Committer: Andrew Gaul <[email protected]>
Committed: Sat Apr 22 10:59:15 2017 -0700

----------------------------------------------------------------------
 .../filters/SharedKeyLiteAuthentication.java    |   2 +-
 .../azure/storage/util/AzureStorageUtils.java   |   1 +
 .../blobstore/AzureBlobRequestSigner.java       | 134 ++++++++++++++-----
 .../integration/AzureBlobSignerLiveTest.java    |  45 -------
 4 files changed, 102 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/1e04eafb/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
----------------------------------------------------------------------
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
index 8e56390..aebcf95 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
@@ -129,7 +129,7 @@ public class SharedKeyLiteAuthentication implements 
HttpRequestFilter {
                   .getContentType())).append("\n");
    }
 
-   private String calculateSignature(String toSign) throws HttpException {
+   public String calculateSignature(String toSign) throws HttpException {
       String signature = signString(toSign);
       if (signatureWire.enabled())
          signatureWire.input(Strings2.toInputStream(signature));

http://git-wip-us.apache.org/repos/asf/jclouds/blob/1e04eafb/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/AzureStorageUtils.java
----------------------------------------------------------------------
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/AzureStorageUtils.java
 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/AzureStorageUtils.java
index bf62ff82a..b6a751d 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/AzureStorageUtils.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/AzureStorageUtils.java
@@ -50,6 +50,7 @@ public class AzureStorageUtils {
       AzureStorageError error = 
factory.create(errorHandlerProvider.get()).parse(content);
       
error.setRequestId(response.getFirstHeaderOrNull(AzureStorageHeaders.REQUEST_ID));
       if ("AuthenticationFailed".equals(error.getCode())) {
+         // this signature is incorrect for URLs from AzureBlobRequestSigner
          
error.setStringSigned(signer.createStringToSign(command.getCurrentRequest()));
          error.setSignature(signer.signString(error.getStringSigned()));
       }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/1e04eafb/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
----------------------------------------------------------------------
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
index 38fe95b..f5f6564 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
@@ -17,70 +17,77 @@
 package org.jclouds.azureblob.blobstore;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.blobstore.util.BlobStoreUtils.cleanRequest;
-import static org.jclouds.reflect.Reflection2.method;
 
+import java.net.URI;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import org.jclouds.azureblob.AzureBlobClient;
-import org.jclouds.azureblob.blobstore.functions.BlobToAzureBlob;
-import org.jclouds.azureblob.domain.AzureBlob;
+import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
 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.reflect.Invocation;
 
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.reflect.Invokable;
+import com.google.common.base.Supplier;
+import com.google.common.net.HttpHeaders;
+import com.google.inject.Provider;
 
 @Singleton
 public class AzureBlobRequestSigner implements BlobRequestSigner {
-   private final Function<Invocation, HttpRequest> processor;
-   private final BlobToAzureBlob blobToBlob;
-   private final BlobToHttpGetOptions blob2HttpGetOptions;
+   private static final int DEFAULT_EXPIRY_SECONDS = 15 * 60;
+   private static final String API_VERSION = "2016-05-31";
 
-   private final Invokable<?, ?> getMethod;
-   private final Invokable<?, ?> deleteMethod;
-   private final Invokable<?, ?> createMethod;
+   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;
 
    @Inject
-   public AzureBlobRequestSigner(Function<Invocation, HttpRequest> processor, 
BlobToAzureBlob blobToBlob,
-         BlobToHttpGetOptions blob2HttpGetOptions) throws SecurityException, 
NoSuchMethodException {
-      this.processor = checkNotNull(processor, "processor");
-      this.blobToBlob = checkNotNull(blobToBlob, "blobToBlob");
+   public AzureBlobRequestSigner(
+         BlobToHttpGetOptions blob2HttpGetOptions, @TimeStamp Provider<String> 
timeStampProvider,
+         DateService dateService, SharedKeyLiteAuthentication auth,
+         @org.jclouds.location.Provider Supplier<Credentials> creds)
+         throws SecurityException, NoSuchMethodException {
+      this.identity = creds.get().identity;
+      this.storageUrl = URI.create("https://"; + creds.get().identity + 
".blob.core.windows.net/");
       this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, 
"blob2HttpGetOptions");
-      this.getMethod = method(AzureBlobClient.class, "getBlob", String.class, 
String.class, GetOptions[].class);
-      this.deleteMethod = method(AzureBlobClient.class, "deleteBlob", 
String.class, String.class);
-      this.createMethod = method(AzureBlobClient.class, "putBlob", 
String.class, AzureBlob.class);
+      this.timeStampProvider = checkNotNull(timeStampProvider, 
"timeStampProvider");
+      this.dateService = checkNotNull(dateService, "dateService");
+      this.auth = auth;
    }
 
    @Override
    public HttpRequest signGetBlob(String container, String name) {
-      checkNotNull(container, "container");
-      checkNotNull(name, "name");
-      return cleanRequest(processor.apply(Invocation.create(getMethod, 
ImmutableList.<Object> of(container, name))));
+      return signGetBlob(container, name, DEFAULT_EXPIRY_SECONDS);
    }
 
    @Override
    public HttpRequest signGetBlob(String container, String name, long 
timeInSeconds) {
-      throw new UnsupportedOperationException();
+      checkNotNull(container, "container");
+      checkNotNull(name, "name");
+      return sign("GET", container, name, null, timeInSeconds, null);
    }
 
    @Override
    public HttpRequest signPutBlob(String container, Blob blob) {
-      checkNotNull(container, "container");
-      checkNotNull(blob, "blob");
-      return cleanRequest(processor.apply(Invocation.create(createMethod,
-            ImmutableList.<Object> of(container, blobToBlob.apply(blob)))));
+      return signPutBlob(container, blob, DEFAULT_EXPIRY_SECONDS);
    }
 
    @Override
    public HttpRequest signPutBlob(String container, Blob blob, long 
timeInSeconds) {
-      throw new UnsupportedOperationException();
+      checkNotNull(container, "container");
+      checkNotNull(blob, "blob");
+      return sign("PUT", container, blob.getMetadata().getName(), null, 
timeInSeconds,
+            blob.getMetadata().getContentMetadata().getContentLength());
    }
 
    @Deprecated
@@ -88,14 +95,73 @@ public class AzureBlobRequestSigner implements 
BlobRequestSigner {
    public HttpRequest signRemoveBlob(String container, String name) {
       checkNotNull(container, "container");
       checkNotNull(name, "name");
-      return cleanRequest(processor.apply(Invocation.create(deleteMethod, 
ImmutableList.<Object> of(container, name))));
+      return sign("DELETE", container, name, null, DEFAULT_EXPIRY_SECONDS, 
null);
    }
 
    @Override
    public HttpRequest signGetBlob(String container, String name, 
org.jclouds.blobstore.options.GetOptions options) {
       checkNotNull(container, "container");
       checkNotNull(name, "name");
-      return cleanRequest(processor.apply(Invocation.create(getMethod,
-            ImmutableList.of(container, name, 
blob2HttpGetOptions.apply(checkNotNull(options, "options"))))));
+      return sign("GET", container, name, 
blob2HttpGetOptions.apply(checkNotNull(options, "options")),
+            DEFAULT_EXPIRY_SECONDS, null);
+   }
+
+   private HttpRequest sign(String method, String container, String name, 
GetOptions options, long expires, Long contentLength) {
+      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
+
+      if (contentLength != null) {
+         request.replaceHeader(HttpHeaders.CONTENT_LENGTH, 
contentLength.toString());
+      }
+
+      if (options != null) {
+         request.headers(options.buildRequestHeaders());
+      }
+
+      if (method.equals("PUT")) {
+         request.replaceHeader("x-ms-blob-type", "BlockBlob");
+      }
+
+      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();
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/1e04eafb/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobSignerLiveTest.java
----------------------------------------------------------------------
diff --git 
a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobSignerLiveTest.java
 
b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobSignerLiveTest.java
index b08c88f..5083e3e 100644
--- 
a/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobSignerLiveTest.java
+++ 
b/providers/azureblob/src/test/java/org/jclouds/azureblob/blobstore/integration/AzureBlobSignerLiveTest.java
@@ -16,12 +16,7 @@
  */
 package org.jclouds.azureblob.blobstore.integration;
 
-import static org.testng.Assert.fail;
-
-import java.io.IOException;
-
 import org.jclouds.blobstore.integration.internal.BaseBlobSignerLiveTest;
-import org.testng.SkipException;
 import org.testng.annotations.Test;
 
 @Test(groups = { "live" })
@@ -29,44 +24,4 @@ public class AzureBlobSignerLiveTest extends 
BaseBlobSignerLiveTest {
    public AzureBlobSignerLiveTest() {
       provider = "azureblob";
    }
-
-   @Test
-   public void testSignGetUrlWithTime() throws InterruptedException, 
IOException {
-      try {
-         super.testSignGetUrlWithTime();
-         fail();
-      } catch (UnsupportedOperationException uoe) {
-         throw new SkipException("not supported in Azure", uoe);
-      }
-   }
-
-   @Test
-   public void testSignGetUrlWithTimeExpired() throws InterruptedException, 
IOException {
-      try {
-         super.testSignGetUrlWithTimeExpired();
-         fail();
-      } catch (UnsupportedOperationException uoe) {
-         throw new SkipException("not supported in Azure", uoe);
-      }
-   }
-
-   @Test
-   public void testSignPutUrlWithTime() throws Exception {
-      try {
-         super.testSignPutUrlWithTime();
-         fail();
-      } catch (UnsupportedOperationException uoe) {
-         throw new SkipException("not supported in Azure", uoe);
-      }
-   }
-
-   @Test
-   public void testSignPutUrlWithTimeExpired() throws Exception {
-      try {
-         super.testSignPutUrlWithTimeExpired();
-         fail();
-      } catch (UnsupportedOperationException uoe) {
-         throw new SkipException("not supported in Azure", uoe);
-      }
-   }
 }

Reply via email to