Repository: jclouds Updated Branches: refs/heads/azure-signed-urls-expiration [created] 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/azure-signed-urls-expiration 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); - } - } }
