You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ga...@apache.org on 2013/07/25 00:23:20 UTC

git commit: AWS-S3 configurable temporary signed URL support

Updated Branches:
  refs/heads/master 48b499c63 -> c64c7423c


AWS-S3 configurable temporary signed URL support

Introduces AWSS3BlobRequestSigner, which reuses the
RequestAuthorizeSignature filter for most of the heavy lifting.
Other implementation details based on [1].

Tested with AWSS3BlobSignerLiveTest, in particular,
testSign(Get|Put)UrlWithTime.

Closes JCLOUDS-200

[1] http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html


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

Branch: refs/heads/master
Commit: c64c7423cdc2dac13d34196f7d526ba364c745c0
Parents: 48b499c
Author: Diwaker Gupta <di...@maginatics.com>
Authored: Mon Jul 22 09:30:22 2013 -0700
Committer: Andrew Gaul <ga...@apache.org>
Committed: Wed Jul 24 15:22:45 2013 -0700

----------------------------------------------------------------------
 .../s3/blobstore/S3BlobRequestSigner.java       | 12 +--
 .../s3/filters/RequestAuthorizeSignature.java   |  8 ++
 .../s3/blobstore/AWSS3BlobRequestSigner.java    | 98 ++++++++++++++++++++
 .../config/AWSS3BlobStoreContextModule.java     |  7 +-
 .../s3/blobstore/AWSS3BlobSignerExpectTest.java | 92 ++++++++++++++++++
 .../integration/AWSS3BlobSignerLiveTest.java    | 11 +++
 6 files changed, 217 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/c64c7423/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobRequestSigner.java
----------------------------------------------------------------------
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobRequestSigner.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobRequestSigner.java
index 9162f75..d6402af 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobRequestSigner.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobRequestSigner.java
@@ -44,13 +44,13 @@ import com.google.common.reflect.Invokable;
  */
 @Singleton
 public class S3BlobRequestSigner<T extends S3AsyncClient> implements BlobRequestSigner {
-   private final RestAnnotationProcessor processor;
-   private final BlobToObject blobToObject;
-   private final BlobToHttpGetOptions blob2HttpGetOptions;
+   protected final RestAnnotationProcessor processor;
+   protected final BlobToObject blobToObject;
+   protected final BlobToHttpGetOptions blob2HttpGetOptions;
 
-   private final Invokable<?, ?> getMethod;
-   private final Invokable<?, ?> deleteMethod;
-   private final Invokable<?, ?> createMethod;
+   protected final Invokable<?, ?> getMethod;
+   protected final Invokable<?, ?> deleteMethod;
+   protected final Invokable<?, ?> createMethod;
 
    @Inject
    public S3BlobRequestSigner(RestAnnotationProcessor processor, BlobToObject blobToObject,

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/c64c7423/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
----------------------------------------------------------------------
diff --git a/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
index 885b2ee..ca85401 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
@@ -50,6 +50,7 @@ import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpRequestFilter;
 import org.jclouds.http.HttpUtils;
 import org.jclouds.http.internal.SignatureWire;
+import org.jclouds.http.utils.Queries;
 import org.jclouds.logging.Logger;
 import org.jclouds.rest.RequestSigner;
 import org.jclouds.s3.util.S3Utils;
@@ -133,6 +134,13 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
    }
 
    HttpRequest replaceAuthorizationHeader(HttpRequest request, String signature) {
+      // Only add the Authorization header if the query string doesn't already contain
+      // the 'Signature' parameter, otherwise S3 will fail the request complaining about
+      // duplicate authentication methods. The 'Signature' parameter will be added for signed URLs
+      // with expiration.
+      if (Queries.queryParser().apply(request.getEndpoint().getQuery()).containsKey("Signature")) {
+         return request;
+      }
       request = request.toBuilder()
             .replaceHeader(HttpHeaders.AUTHORIZATION, authTag + " " + creds.get().identity + ":" + signature).build();
       return request;

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/c64c7423/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java
----------------------------------------------------------------------
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java
new file mode 100644
index 0000000..b0b9824
--- /dev/null
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java
@@ -0,0 +1,98 @@
+/*
+ * 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.aws.s3.blobstore;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.blobstore.util.BlobStoreUtils.cleanRequest;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.aws.s3.AWSS3AsyncClient;
+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.reflect.Invocation;
+import org.jclouds.rest.internal.RestAnnotationProcessor;
+import org.jclouds.s3.blobstore.S3BlobRequestSigner;
+import org.jclouds.s3.blobstore.functions.BlobToObject;
+import org.jclouds.s3.filters.RequestAuthorizeSignature;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.net.HttpHeaders;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+/**
+ * @author Diwaker Gupta
+ */
+public class AWSS3BlobRequestSigner extends S3BlobRequestSigner<AWSS3AsyncClient> {
+   private final RequestAuthorizeSignature authSigner;
+   private final String identity;
+   private final DateService dateService;
+   private final Provider<String> timeStampProvider;
+
+   @Inject
+   public AWSS3BlobRequestSigner(RestAnnotationProcessor processor, BlobToObject blobToObject,
+         BlobToHttpGetOptions blob2HttpGetOptions, Class<AWSS3AsyncClient> interfaceClass,
+         @org.jclouds.location.Provider Supplier<Credentials> credentials,
+         RequestAuthorizeSignature authSigner, @TimeStamp Provider<String> timeStampProvider,
+         DateService dateService) throws SecurityException, NoSuchMethodException {
+      super(processor, blobToObject, blob2HttpGetOptions, interfaceClass);
+      this.authSigner = authSigner;
+      this.identity = credentials.get().identity;
+      this.dateService = dateService;
+      this.timeStampProvider = timeStampProvider;
+   }
+
+   @Override
+   public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
+      checkNotNull(container, "container");
+      checkNotNull(name, "name");
+      HttpRequest request = processor.apply(Invocation.create(getMethod, ImmutableList.<Object> of(container, name)));
+      return cleanRequest(signForTemporaryAccess(request, timeInSeconds));
+   }
+
+   @Override
+   public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
+      checkNotNull(container, "container");
+      checkNotNull(blob, "blob");
+      HttpRequest request = processor.apply(Invocation.create(createMethod,
+         ImmutableList.<Object>of(container, blobToObject.apply(blob))));
+      return cleanRequest(signForTemporaryAccess(request, timeInSeconds));
+   }
+
+   private HttpRequest signForTemporaryAccess(HttpRequest request, long timeInSeconds) {
+      // Update the 'DATE' header
+      String dateString = request.getFirstHeaderOrNull(HttpHeaders.DATE);
+      if (dateString == null) {
+         dateString = timeStampProvider.get();
+      }
+      Date date = dateService.rfc1123DateParse(dateString);
+      String expiration = String.valueOf(TimeUnit.MILLISECONDS.toSeconds(date.getTime()) + timeInSeconds);
+      HttpRequest.Builder<?> builder = request.toBuilder().replaceHeader(HttpHeaders.DATE, expiration);
+      final String signature = authSigner.sign(authSigner.createStringToSign(builder.build()));
+      return builder.addQueryParam("Signature", signature)
+         .addQueryParam(HttpHeaders.EXPIRES, expiration)
+         .addQueryParam("AWSAccessKeyId", identity)
+         .build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/c64c7423/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/config/AWSS3BlobStoreContextModule.java
----------------------------------------------------------------------
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/config/AWSS3BlobStoreContextModule.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/config/AWSS3BlobStoreContextModule.java
index cb454bb..501645c 100644
--- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/config/AWSS3BlobStoreContextModule.java
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/config/AWSS3BlobStoreContextModule.java
@@ -16,8 +16,8 @@
  */
 package org.jclouds.aws.s3.blobstore.config;
 
-import org.jclouds.aws.s3.AWSS3AsyncClient;
 import org.jclouds.aws.s3.blobstore.AWSS3AsyncBlobStore;
+import org.jclouds.aws.s3.blobstore.AWSS3BlobRequestSigner;
 import org.jclouds.aws.s3.blobstore.AWSS3BlobStore;
 import org.jclouds.aws.s3.blobstore.strategy.AsyncMultipartUploadStrategy;
 import org.jclouds.aws.s3.blobstore.strategy.MultipartUploadStrategy;
@@ -25,12 +25,10 @@ import org.jclouds.aws.s3.blobstore.strategy.internal.ParallelMultipartUploadStr
 import org.jclouds.aws.s3.blobstore.strategy.internal.SequentialMultipartUploadStrategy;
 import org.jclouds.blobstore.BlobRequestSigner;
 import org.jclouds.s3.blobstore.S3AsyncBlobStore;
-import org.jclouds.s3.blobstore.S3BlobRequestSigner;
 import org.jclouds.s3.blobstore.S3BlobStore;
 import org.jclouds.s3.blobstore.config.S3BlobStoreContextModule;
 
 import com.google.inject.Scopes;
-import com.google.inject.TypeLiteral;
 
 /**
  * 
@@ -50,7 +48,6 @@ public class AWSS3BlobStoreContextModule extends S3BlobStoreContextModule {
 
    @Override
    protected void bindRequestSigner() {
-      bind(BlobRequestSigner.class).to(new TypeLiteral<S3BlobRequestSigner<AWSS3AsyncClient>>() {
-      });
+      bind(BlobRequestSigner.class).to(AWSS3BlobRequestSigner.class);
    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/c64c7423/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/AWSS3BlobSignerExpectTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/AWSS3BlobSignerExpectTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/AWSS3BlobSignerExpectTest.java
new file mode 100644
index 0000000..d041abd
--- /dev/null
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/AWSS3BlobSignerExpectTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.aws.s3.blobstore;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.aws.s3.config.AWSS3RestClientModule;
+import org.jclouds.blobstore.BlobStore;
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.s3.blobstore.S3BlobSignerExpectTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Module;
+
+/**
+ * @author Diwaker Gupta
+ */
+@Test(groups = "unit", testName = "AWSS3BlobSignerExpectTest")
+public class AWSS3BlobSignerExpectTest extends S3BlobSignerExpectTest {
+   public AWSS3BlobSignerExpectTest() {
+      provider = "aws-s3";
+   }
+
+   @Override
+   protected HttpRequest getBlobWithTime() {
+      return HttpRequest.builder().method("GET")
+         .endpoint("https://container.s3.amazonaws.com/name" +
+            "?Signature=Y4Ac4sZfBemGZmgfG78F7IX%20IFg%3D&Expires=1212683902&AWSAccessKeyId=identity")
+         .addHeader("Host", "container.s3.amazonaws.com")
+         .addHeader("Date", "Thu, 05 Jun 2008 16:38:19 GMT").build();
+   }
+
+   @Test
+   public void testSignGetBlobWithTime() {
+      BlobStore getBlobWithTime = requestsSendResponses(init());
+      HttpRequest compare = getBlobWithTime();
+      assertEquals(getBlobWithTime.getContext().getSigner().signGetBlob(container, name, 3l /* seconds */),
+         compare);
+   }
+
+   @Override
+   protected HttpRequest putBlobWithTime() {
+      return HttpRequest.builder().method("PUT")
+         .endpoint("https://container.s3.amazonaws.com/name" +
+            "?Signature=genkB2vLxe3AWV/bPvRTMqQts7E%3D&Expires=1212683902&AWSAccessKeyId=identity")
+         .addHeader("Expect", "100-continue")
+         .addHeader("Host", "container.s3.amazonaws.com")
+         .addHeader("Date", "Thu, 05 Jun 2008 16:38:19 GMT").build();
+   }
+
+   @Test
+   public void testSignPutBlobWithTime() throws Exception {
+      BlobStore signPutBloblWithTime = requestsSendResponses(init());
+      Blob blob = signPutBloblWithTime.blobBuilder(name).payload(text).contentType("text/plain").build();
+      HttpRequest compare = putBlobWithTime();
+      compare.setPayload(blob.getPayload());
+      assertEquals(signPutBloblWithTime.getContext().getSigner().signPutBlob(container, blob, 3l /* seconds */),
+         compare);
+   }
+
+   @Override
+   protected Module createModule() {
+      return new TestAWSS3RestClientModule();
+   }
+
+   @ConfiguresRestClient
+   private static final class TestAWSS3RestClientModule extends AWSS3RestClientModule {
+      @Override
+      @TimeStamp
+      protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
+         return "Thu, 05 Jun 2008 16:38:19 GMT";
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds/blob/c64c7423/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3BlobSignerLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3BlobSignerLiveTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3BlobSignerLiveTest.java
index d0f86f6..0a3ec91 100644
--- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3BlobSignerLiveTest.java
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/integration/AWSS3BlobSignerLiveTest.java
@@ -16,6 +16,9 @@
  */
 package org.jclouds.aws.s3.blobstore.integration;
 
+import java.util.Properties;
+
+import org.jclouds.Constants;
 import org.jclouds.s3.blobstore.integration.S3BlobSignerLiveTest;
 import org.testng.annotations.Test;
 
@@ -28,4 +31,12 @@ public class AWSS3BlobSignerLiveTest extends S3BlobSignerLiveTest {
    public AWSS3BlobSignerLiveTest() {
       provider = "aws-s3";
    }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      overrides.setProperty(Constants.PROPERTY_STRIP_EXPECT_HEADER, "true");
+      overrides.setProperty(Constants.PROPERTY_SESSION_INTERVAL, "1");
+      return overrides;
+   }
 }