mirror of https://github.com/apache/jclouds.git
Add support for Swift TempURL Middleware
This commit is contained in:
parent
b558015515
commit
49bde19fa0
|
@ -67,11 +67,21 @@ public class AtmosBlobRequestSigner implements BlobRequestSigner {
|
|||
return cleanRequest(processor.createRequest(getMethod, getPath(container, name)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
return cleanRequest(processor.createRequest(createMethod, container, blobToObject.apply(blob)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signRemoveBlob(String container, String name) {
|
||||
return cleanRequest(processor.createRequest(deleteMethod, getPath(container, name)));
|
||||
|
|
|
@ -53,6 +53,11 @@
|
|||
<artifactId>swift</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jclouds.driver</groupId>
|
||||
<artifactId>jclouds-joda</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jclouds</groupId>
|
||||
<artifactId>jclouds-core</artifactId>
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.jclouds.blobstore.functions.ReturnNullOnContainerNotFound;
|
|||
import org.jclouds.cloudfiles.domain.ContainerCDNMetadata;
|
||||
import org.jclouds.cloudfiles.functions.ParseCdnUriFromHeaders;
|
||||
import org.jclouds.cloudfiles.functions.ParseContainerCDNMetadataFromHeaders;
|
||||
import org.jclouds.cloudfiles.functions.ParseTemporaryUrlKeyFromHeaders;
|
||||
import org.jclouds.cloudfiles.options.ListCdnContainerOptions;
|
||||
import org.jclouds.cloudfiles.reference.CloudFilesHeaders;
|
||||
import org.jclouds.openstack.filters.AuthenticateRequest;
|
||||
|
@ -57,7 +58,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
* All commands return a ListenableFuture of the result from Cloud Files. Any exceptions incurred
|
||||
* during processing will be backend in an {@link ExecutionException} as documented in
|
||||
* {@link ListenableFuture#get()}.
|
||||
*
|
||||
*
|
||||
* @see CloudFilesClient
|
||||
* @see <a href="http://www.rackspacecloud.com/cf-devguide-20090812.pdf" />
|
||||
* @author Adrian Cole
|
||||
|
@ -130,4 +131,19 @@ public interface CloudFilesAsyncClient extends CommonSwiftAsyncClient {
|
|||
@Endpoint(CDNManagement.class)
|
||||
ListenableFuture<Boolean> disableCDN(@PathParam("container") String container);
|
||||
|
||||
|
||||
/**
|
||||
* @see CloudFilesClient#getTemporaryUrlKey
|
||||
*/
|
||||
@HEAD
|
||||
@Path("/")
|
||||
@ResponseParser(ParseTemporaryUrlKeyFromHeaders.class)
|
||||
ListenableFuture<String> getTemporaryUrlKey();
|
||||
|
||||
/**
|
||||
* @see CloudFilesClient#setTemporaryUrlKey
|
||||
*/
|
||||
@POST
|
||||
@Path("/")
|
||||
ListenableFuture<Void> setTemporaryUrlKey(@HeaderParam(CloudFilesHeaders.ACCOUNT_TEMPORARY_URL_KEY) String key);
|
||||
}
|
||||
|
|
|
@ -34,9 +34,9 @@ import org.jclouds.openstack.swift.CommonSwiftClient;
|
|||
* <p/>
|
||||
* All commands return a Future of the result from Cloud Files. Any exceptions incurred during
|
||||
* processing will be backend in an {@link ExecutionException} as documented in {@link Future#get()}.
|
||||
*
|
||||
* @see <a href="http://www.rackspacecloud.com/cf-devguide-20090812.pdf" />
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @see <a href="http://www.rackspacecloud.com/cf-devguide-20090812.pdf" />
|
||||
*/
|
||||
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
|
||||
public interface CloudFilesClient extends CommonSwiftClient {
|
||||
|
@ -52,4 +52,24 @@ public interface CloudFilesClient extends CommonSwiftClient {
|
|||
|
||||
boolean disableCDN(String container);
|
||||
|
||||
/**
|
||||
* Retrieve the key used to generate Temporary object access URLs
|
||||
*
|
||||
* @see <a href="http://docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Account_Metadata-d1a4460.html" />
|
||||
* @return shared secret key
|
||||
*/
|
||||
String getTemporaryUrlKey();
|
||||
|
||||
/**
|
||||
* To create a Temporary URL you must first set a key as account metadata.
|
||||
*
|
||||
* Once the key is set, you should not change it while you still want others to be
|
||||
* able to access your temporary URL. If you change it, the TempURL becomes invalid
|
||||
* (within 60 seconds, which is the cache time for a key) and others will not be allowed
|
||||
* to access it.
|
||||
*
|
||||
* @see <a href="http://docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Account_Metadata-d1a4460.html" />
|
||||
* @param temporaryUrlKey
|
||||
*/
|
||||
void setTemporaryUrlKey(String temporaryUrlKey);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.cloudfiles;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Represents the key used for signing URLs that have temporary access to objects
|
||||
*
|
||||
* @see <a href="http://docs.openstack.org/developer/swift/misc.html#module-swift.common.middleware.tempurl" />
|
||||
* @author Andrei Savu
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
|
||||
@Qualifier
|
||||
public @interface TemporaryUrlKey {
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package org.jclouds.cloudfiles.blobstore;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.inject.Provider;
|
||||
import org.jclouds.blobstore.BlobRequestSigner;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
||||
import org.jclouds.cloudfiles.CloudFilesAsyncClient;
|
||||
import org.jclouds.cloudfiles.TemporaryUrlKey;
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.options.GetOptions;
|
||||
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
|
||||
import org.jclouds.openstack.swift.blobstore.functions.BlobToObject;
|
||||
import org.jclouds.openstack.swift.domain.SwiftObject;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.InvalidKeyException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Predicates.instanceOf;
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static org.jclouds.blobstore.util.BlobStoreUtils.cleanRequest;
|
||||
|
||||
public class CloudFilesBlobRequestSigner implements BlobRequestSigner {
|
||||
|
||||
private final RestAnnotationProcessor<CloudFilesAsyncClient> processor;
|
||||
private final Crypto crypto;
|
||||
|
||||
private final Provider<Long> unixEpochTimestampProvider;
|
||||
private final Provider<String> temporaryUrlKeyProvider;
|
||||
|
||||
private final BlobToObject blobToObject;
|
||||
private final BlobToHttpGetOptions blob2HttpGetOptions;
|
||||
|
||||
private final Method getMethod;
|
||||
private final Method deleteMethod;
|
||||
private final Method createMethod;
|
||||
|
||||
@Inject
|
||||
public CloudFilesBlobRequestSigner(RestAnnotationProcessor<CloudFilesAsyncClient> processor, BlobToObject blobToObject,
|
||||
BlobToHttpGetOptions blob2HttpGetOptions, Crypto crypto,
|
||||
@TimeStamp Provider<Long> unixEpochTimestampProvider,
|
||||
@TemporaryUrlKey Provider<String> temporaryUrlKeyProvider) throws SecurityException, NoSuchMethodException {
|
||||
this.processor = checkNotNull(processor, "processor");
|
||||
this.crypto = checkNotNull(crypto, "crypto");
|
||||
|
||||
this.unixEpochTimestampProvider = checkNotNull(unixEpochTimestampProvider, "unixEpochTimestampProvider");
|
||||
this.temporaryUrlKeyProvider = checkNotNull(temporaryUrlKeyProvider, "temporaryUrlKeyProvider");
|
||||
|
||||
this.blobToObject = checkNotNull(blobToObject, "blobToObject");
|
||||
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
|
||||
|
||||
this.getMethod = CloudFilesAsyncClient.class.getMethod("getObject", String.class, String.class, GetOptions[].class);
|
||||
this.deleteMethod = CloudFilesAsyncClient.class.getMethod("removeObject", String.class, String.class);
|
||||
this.createMethod = CloudFilesAsyncClient.class.getMethod("putObject", String.class, SwiftObject.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name) {
|
||||
return cleanRequest(processor.createRequest(getMethod, container, name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, org.jclouds.blobstore.options.GetOptions options) {
|
||||
return cleanRequest(processor.createRequest(getMethod, container, name, blob2HttpGetOptions.apply(options)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||
HttpRequest request = processor.createRequest(getMethod, container, name);
|
||||
return cleanRequest(signForTemporaryAccess(request, timeInSeconds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
return cleanRequest(processor.createRequest(createMethod, container, blobToObject.apply(blob)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||
HttpRequest request = processor.createRequest(createMethod, container, blobToObject.apply(blob));
|
||||
return cleanRequest(signForTemporaryAccess(request, timeInSeconds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signRemoveBlob(String container, String name) {
|
||||
return cleanRequest(processor.createRequest(deleteMethod, container, name));
|
||||
}
|
||||
|
||||
private HttpRequest signForTemporaryAccess(HttpRequest request, long timeInSeconds) {
|
||||
HttpRequest.Builder builder = request.toBuilder();
|
||||
builder.filters(filter(request.getFilters(), instanceOf(AuthenticateRequest.class)));
|
||||
|
||||
String key = temporaryUrlKeyProvider.get();
|
||||
long expiresInSeconds = unixEpochTimestampProvider.get() + timeInSeconds;
|
||||
|
||||
builder.addQueryParam("temp_url_sig", createSignature(key, createStringToSign(
|
||||
request.getMethod().toUpperCase(), request, expiresInSeconds)));
|
||||
builder.addQueryParam("temp_url_expires", "" + expiresInSeconds);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private String createStringToSign(String method, HttpRequest request, long expiresInSeconds) {
|
||||
checkArgument(method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("PUT"));
|
||||
return String.format("%s\n%d\n%s", method.toUpperCase(), expiresInSeconds,
|
||||
request.getEndpoint().getPath());
|
||||
}
|
||||
|
||||
private String createSignature(String key, String stringToSign) {
|
||||
try {
|
||||
return CryptoStreams.hex(crypto.hmacSHA1(key.getBytes()).doFinal(stringToSign.getBytes()));
|
||||
|
||||
} catch (InvalidKeyException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,11 +23,15 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.blobstore.BlobRequestSigner;
|
||||
import org.jclouds.cloudfiles.CloudFilesClient;
|
||||
import org.jclouds.cloudfiles.TemporaryUrlKey;
|
||||
import org.jclouds.cloudfiles.blobstore.CloudFilesAsyncBlobStore;
|
||||
import org.jclouds.cloudfiles.blobstore.CloudFilesBlobRequestSigner;
|
||||
import org.jclouds.cloudfiles.blobstore.CloudFilesBlobStore;
|
||||
import org.jclouds.cloudfiles.blobstore.functions.CloudFilesObjectToBlobMetadata;
|
||||
import org.jclouds.cloudfiles.domain.ContainerCDNMetadata;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.openstack.swift.blobstore.SwiftAsyncBlobStore;
|
||||
import org.jclouds.openstack.swift.blobstore.SwiftBlobStore;
|
||||
import org.jclouds.openstack.swift.blobstore.config.SwiftBlobStoreContextModule;
|
||||
|
@ -37,9 +41,11 @@ import com.google.common.cache.CacheBuilder;
|
|||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.inject.Provides;
|
||||
import org.jclouds.rest.HttpClient;
|
||||
import org.jclouds.rest.RequestSigner;
|
||||
import org.joda.time.Instant;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class CloudFilesBlobStoreContextModule extends SwiftBlobStoreContextModule {
|
||||
|
@ -61,6 +67,18 @@ public class CloudFilesBlobStoreContextModule extends SwiftBlobStoreContextModul
|
|||
});
|
||||
}
|
||||
|
||||
@Provides
|
||||
@TimeStamp
|
||||
protected Long unixEpochTimestampProvider() {
|
||||
return Instant.now().getMillis() / 1000; /* in seconds */
|
||||
}
|
||||
|
||||
@Provides
|
||||
@TemporaryUrlKey
|
||||
protected String temporaryUrlKeyProvider(CloudFilesClient client) {
|
||||
return client.getTemporaryUrlKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
|
@ -68,4 +86,9 @@ public class CloudFilesBlobStoreContextModule extends SwiftBlobStoreContextModul
|
|||
bind(SwiftAsyncBlobStore.class).to(CloudFilesAsyncBlobStore.class);
|
||||
bind(ObjectToBlobMetadata.class).to(CloudFilesObjectToBlobMetadata.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureRequestSigner() {
|
||||
bind(BlobRequestSigner.class).to(CloudFilesBlobRequestSigner.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.cloudfiles.functions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static org.jclouds.cloudfiles.reference.CloudFilesHeaders.ACCOUNT_TEMPORARY_URL_KEY;
|
||||
|
||||
/**
|
||||
* @author Andrei Savu
|
||||
*/
|
||||
public class ParseTemporaryUrlKeyFromHeaders implements Function<HttpResponse, String> {
|
||||
|
||||
@Override
|
||||
public String apply(HttpResponse httpResponse) {
|
||||
return getOnlyElement(httpResponse.getHeaders().get(ACCOUNT_TEMPORARY_URL_KEY));
|
||||
}
|
||||
}
|
|
@ -30,6 +30,8 @@ import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
|||
*/
|
||||
public interface CloudFilesHeaders extends SwiftHeaders {
|
||||
|
||||
public static final String ACCOUNT_TEMPORARY_URL_KEY = "X-Account-Meta-Temp-Url-Key";
|
||||
|
||||
public static final String CDN_ENABLED = "X-CDN-Enabled";
|
||||
public static final String CDN_REFERRER_ACL = "X-Referrer-ACL ";
|
||||
public static final String CDN_TTL = "X-TTL";
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.cloudfiles;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.cloudfiles.blobstore.config.CloudFilesBlobStoreContextModule;
|
||||
import org.jclouds.cloudfiles.config.CloudFilesRestClientModule;
|
||||
import org.jclouds.openstack.internal.TestOpenStackAuthenticationModule;
|
||||
import org.jclouds.openstack.swift.CommonSwiftClientTest;
|
||||
import org.jclouds.openstack.swift.SwiftApiMetadata;
|
||||
import org.jclouds.openstack.swift.SwiftAsyncClient;
|
||||
import org.jclouds.openstack.swift.config.SwiftRestClientModule;
|
||||
import org.jclouds.rest.HttpClient;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.jclouds.Constants.PROPERTY_API_VERSION;
|
||||
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
|
||||
import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
|
||||
|
||||
/**
|
||||
* @author Andrei Savu
|
||||
*/
|
||||
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
|
||||
@Test(groups = "unit", testName = "CloudFilesClientTest")
|
||||
public class CloudFilesClientTest extends CommonSwiftClientTest {
|
||||
|
||||
public static final long UNIX_EPOCH_TIMESTAMP = 123456789L;
|
||||
public static final String TEMPORARY_URL_KEY = "get-or-set-X-Account-Meta-Temp-Url-Key";
|
||||
|
||||
protected String provider = "cloudfiles";
|
||||
|
||||
public static class FixedCloudFilesBlobStoreContextModule extends CloudFilesBlobStoreContextModule {
|
||||
@Override
|
||||
protected Long unixEpochTimestampProvider() {
|
||||
return UNIX_EPOCH_TIMESTAMP;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String temporaryUrlKeyProvider(CloudFilesClient _) {
|
||||
return TEMPORARY_URL_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ApiMetadata createApiMetadata() {
|
||||
return new CloudFilesApiMetadata().toBuilder().defaultModules(
|
||||
ImmutableSet.<Class<? extends Module>>of(StorageEndpointModule.class,
|
||||
CloudFilesRestClientModule.class, FixedCloudFilesBlobStoreContextModule.class)).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds 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.cloudfiles.blobstore;
|
||||
|
||||
import org.jclouds.blobstore.BlobRequestSigner;
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.cloudfiles.CloudFilesClientTest;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
|
||||
/**
|
||||
* @author Andrei Savu
|
||||
*/
|
||||
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
|
||||
@Test(groups = "unit", testName = "CloudFilesBlobSignerTest")
|
||||
public class CloudFilesBlobSignerTest extends CloudFilesClientTest {
|
||||
|
||||
private BlobRequestSigner signer;
|
||||
private Blob.Factory blobFactory;
|
||||
|
||||
public void testSignGetBlobWithTime() {
|
||||
HttpRequest request = signer.signGetBlob("container", "name", 120);
|
||||
|
||||
assertRequestLineEquals(request, "GET http://storage/container/name?" +
|
||||
"temp_url_sig=4759d99d13c826bba0af2c9f0c526ca53c95abaf&temp_url_expires=123456909 HTTP/1.1");
|
||||
assertFalse(request.getHeaders().containsKey("X-Auth-Token"));
|
||||
assertPayloadEquals(request, null, null, false);
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
||||
public void testSignPutBlobWithTime() throws Exception {
|
||||
Blob blob = blobFactory.create(null);
|
||||
|
||||
blob.getMetadata().setName("name");
|
||||
blob.setPayload("");
|
||||
blob.getPayload().getContentMetadata().setContentLength(2l);
|
||||
blob.getPayload().getContentMetadata().setContentMD5(new byte[]{0, 2, 4, 8});
|
||||
blob.getPayload().getContentMetadata().setContentType("text/plain");
|
||||
blob.getPayload().getContentMetadata().setExpires(new Date(1000));
|
||||
|
||||
HttpRequest request = signer.signPutBlob("container", blob, 120 /* seconds */);
|
||||
|
||||
assertRequestLineEquals(request, "PUT http://storage/container/name?" +
|
||||
"temp_url_sig=490690286130adac9e7144d85b320a00b1bf9e2b&temp_url_expires=123456909 HTTP/1.1");
|
||||
|
||||
assertFalse(request.getHeaders().containsKey("X-Auth-Token"));
|
||||
assertContentHeadersEqual(request, "text/plain", null, null, null, (long) 2l, new byte[]{0, 2, 4, 8}, new Date(1000));
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
protected void setupFactory() throws IOException {
|
||||
super.setupFactory();
|
||||
this.blobFactory = injector.getInstance(Blob.Factory.class);
|
||||
this.signer = injector.getInstance(BlobRequestSigner.class);
|
||||
}
|
||||
}
|
|
@ -18,16 +18,37 @@
|
|||
*/
|
||||
package org.jclouds.cloudfiles.blobstore.integration;
|
||||
|
||||
import org.jclouds.cloudfiles.CloudFilesApiMetadata;
|
||||
import org.jclouds.cloudfiles.CloudFilesClient;
|
||||
import org.jclouds.openstack.swift.blobstore.integration.SwiftBlobLiveTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "live" })
|
||||
@Test(groups = {"live"})
|
||||
public class CloudFilesBlobLiveTest extends SwiftBlobLiveTest {
|
||||
public CloudFilesBlobLiveTest(){
|
||||
public CloudFilesBlobLiveTest() {
|
||||
provider = "cloudfiles";
|
||||
}
|
||||
|
||||
public void testGetAndSetTemporaryUrlKey() {
|
||||
CloudFilesClient client = view.unwrap(CloudFilesApiMetadata.CONTEXT_TOKEN).getApi();
|
||||
|
||||
String currentSecretKey = client.getTemporaryUrlKey();
|
||||
assertNotNull(currentSecretKey);
|
||||
try {
|
||||
String testKey = UUID.randomUUID().toString();
|
||||
client.setTemporaryUrlKey(testKey);
|
||||
assertEquals(client.getTemporaryUrlKey(), testKey);
|
||||
|
||||
} finally {
|
||||
client.setTemporaryUrlKey(currentSecretKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,78 @@
|
|||
*/
|
||||
package org.jclouds.cloudfiles.blobstore.integration;
|
||||
|
||||
import org.jclouds.blobstore.domain.Blob;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.openstack.swift.blobstore.integration.SwiftBlobSignerLiveTest;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "live" })
|
||||
@Test(groups = {"live"})
|
||||
public class CloudFilesBlobSignerLiveTest extends SwiftBlobSignerLiveTest {
|
||||
public CloudFilesBlobSignerLiveTest(){
|
||||
public CloudFilesBlobSignerLiveTest() {
|
||||
provider = "cloudfiles";
|
||||
}
|
||||
|
||||
public void testSignGetUrlWithTime() throws InterruptedException, IOException {
|
||||
String name = "hello";
|
||||
String text = "fooooooooooooooooooooooo";
|
||||
|
||||
Blob blob = view.getBlobStore().blobBuilder(name).payload(text).contentType("text/plain").build();
|
||||
String container = getContainerName();
|
||||
try {
|
||||
view.getBlobStore().putBlob(container, blob);
|
||||
assertConsistencyAwareContainerSize(container, 1);
|
||||
HttpRequest request = view.getSigner().signGetBlob(container, name, 3 /* seconds */);
|
||||
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
assertEquals(Strings2.toString(view.utils().http().invoke(request).getPayload()), text);
|
||||
|
||||
TimeUnit.SECONDS.sleep(4);
|
||||
try {
|
||||
Strings2.toString(view.utils().http().invoke(request).getPayload());
|
||||
fail("Temporary URL did not expire as expected");
|
||||
} catch (AuthorizationException expected) {
|
||||
}
|
||||
} finally {
|
||||
returnContainer(container);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignPutUrlWithTime() throws Exception {
|
||||
String name = "hello";
|
||||
String text = "fooooooooooooooooooooooo";
|
||||
|
||||
Blob blob = view.getBlobStore().blobBuilder(name).payload(text).contentType("text/plain").build();
|
||||
String container = getContainerName();
|
||||
try {
|
||||
HttpRequest request = view.getSigner().signPutBlob(container, blob, 3 /* seconds */);
|
||||
assertEquals(request.getFilters().size(), 0);
|
||||
|
||||
Strings2.toString(view.utils().http().invoke(request).getPayload());
|
||||
assertConsistencyAwareContainerSize(container, 1);
|
||||
|
||||
view.getBlobStore().removeBlob(container, name);
|
||||
assertConsistencyAwareContainerSize(container, 0);
|
||||
|
||||
TimeUnit.SECONDS.sleep(4);
|
||||
try {
|
||||
Strings2.toString(view.utils().http().invoke(request).getPayload());
|
||||
fail("Temporary URL did not expire as expected");
|
||||
} catch (AuthorizationException expected) {
|
||||
}
|
||||
} finally {
|
||||
returnContainer(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,11 +69,21 @@ public class S3BlobRequestSigner implements BlobRequestSigner {
|
|||
return cleanRequest(processor.createRequest(getMethod, container, name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
return cleanRequest(processor.createRequest(createMethod, container, blobToObject.apply(blob)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signRemoveBlob(String container, String name) {
|
||||
return cleanRequest(processor.createRequest(deleteMethod, container, name));
|
||||
|
|
|
@ -37,12 +37,14 @@ import org.jclouds.openstack.swift.domain.SwiftObject;
|
|||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
||||
|
||||
private final RestAnnotationProcessor<CommonSwiftAsyncClient> processor;
|
||||
|
||||
private final BlobToObject blobToObject;
|
||||
private final BlobToHttpGetOptions blob2HttpGetOptions;
|
||||
|
||||
|
@ -54,13 +56,14 @@ public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
|||
public SwiftBlobRequestSigner(RestAnnotationProcessor<CommonSwiftAsyncClient> processor, BlobToObject blobToObject,
|
||||
BlobToHttpGetOptions blob2HttpGetOptions) throws SecurityException, NoSuchMethodException {
|
||||
this.processor = checkNotNull(processor, "processor");
|
||||
|
||||
this.blobToObject = checkNotNull(blobToObject, "blobToObject");
|
||||
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
|
||||
|
||||
this.getMethod = CommonSwiftAsyncClient.class.getMethod("getObject", String.class, String.class,
|
||||
GetOptions[].class);
|
||||
this.deleteMethod = CommonSwiftAsyncClient.class.getMethod("removeObject", String.class, String.class);
|
||||
this.createMethod = CommonSwiftAsyncClient.class.getMethod("putObject", String.class, SwiftObject.class);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,11 +71,21 @@ public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
|||
return cleanRequest(processor.createRequest(getMethod, container, name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
return cleanRequest(processor.createRequest(createMethod, container, blobToObject.apply(blob)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signRemoveBlob(String container, String name) {
|
||||
return cleanRequest(processor.createRequest(deleteMethod, container, name));
|
||||
|
|
|
@ -33,7 +33,7 @@ import com.google.inject.Scopes;
|
|||
/**
|
||||
* Configures the {@link CloudFilesBlobStoreContext}; requires {@link SwiftAsyncBlobStore}
|
||||
* bound.
|
||||
*
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class SwiftBlobStoreContextModule extends AbstractModule {
|
||||
|
@ -44,8 +44,10 @@ public class SwiftBlobStoreContextModule extends AbstractModule {
|
|||
bind(ConsistencyModel.class).toInstance(ConsistencyModel.STRICT);
|
||||
bind(AsyncBlobStore.class).to(SwiftAsyncBlobStore.class).in(Scopes.SINGLETON);
|
||||
bind(BlobStore.class).to(SwiftBlobStore.class).in(Scopes.SINGLETON);
|
||||
bind(BlobRequestSigner.class).to(SwiftBlobRequestSigner.class);
|
||||
configureRequestSigner();
|
||||
}
|
||||
|
||||
|
||||
protected void configureRequestSigner() {
|
||||
bind(BlobRequestSigner.class).to(SwiftBlobRequestSigner.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ public abstract class CommonSwiftClientTest extends BaseAsyncClientTest<SwiftAsy
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ApiMetadata createApiMetadata() {
|
||||
return new SwiftApiMetadata().toBuilder().defaultModules(
|
||||
ImmutableSet.<Class<? extends Module>> of(StorageEndpointModule.class, SwiftRestClientModule.class,
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.jclouds.openstack.swift.blobstore;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
|
|
@ -25,18 +25,19 @@ import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "live" })
|
||||
public class SwiftBlobSignerLiveTest extends BaseBlobSignerLiveTest {
|
||||
|
||||
@Override
|
||||
protected Properties setupProperties() {
|
||||
Properties props = super.setupProperties();
|
||||
setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE);
|
||||
return props;
|
||||
}
|
||||
|
||||
|
||||
public SwiftBlobSignerLiveTest() {
|
||||
provider = System.getProperty("test.swift.provider", "swift");
|
||||
}
|
||||
|
|
|
@ -47,6 +47,16 @@ public interface BlobRequestSigner {
|
|||
*/
|
||||
HttpRequest signGetBlob(String container, String name);
|
||||
|
||||
/**
|
||||
* gets a signed request, including headers as necessary, to allow access to a blob
|
||||
* from an external client for a limited period of time
|
||||
*
|
||||
* @param timeInSeconds
|
||||
* validity time in seconds for the generated request
|
||||
* @see #signGetBlob(String, String)
|
||||
*/
|
||||
HttpRequest signGetBlob(String container, String name, long timeInSeconds);
|
||||
|
||||
/**
|
||||
* @param options
|
||||
* @see #signGetBlob(String, String)
|
||||
|
@ -84,4 +94,15 @@ public interface BlobRequestSigner {
|
|||
* @see BlobBuilder#forSigning
|
||||
*/
|
||||
HttpRequest signPutBlob(String container, Blob blob);
|
||||
|
||||
/**
|
||||
* gets a signed request, including headers as necessary, to upload a blob from an
|
||||
* external client for a limited period of time
|
||||
*
|
||||
* @param timeInSeconds
|
||||
* validity time in seconds for the generated request
|
||||
* @see BlobBuilder#forSigning
|
||||
* @see BlobRequestSigner#signPutBlob
|
||||
*/
|
||||
HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,11 @@ public class TransientBlobRequestSigner implements BlobRequestSigner {
|
|||
return basicAuth.filter(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
HttpRequest request = HttpRequest.builder().method("PUT").endpoint(
|
||||
|
@ -71,6 +76,11 @@ public class TransientBlobRequestSigner implements BlobRequestSigner {
|
|||
return basicAuth.filter(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signRemoveBlob(String container, String name) {
|
||||
HttpRequest request = HttpRequest.builder().method("DELETE").endpoint(String.format("%s/%s/%s", endpoint.get(), container,
|
||||
|
|
|
@ -38,7 +38,12 @@ public class RequestSigningUnsupported implements BlobRequestSigner {
|
|||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, GetOptions options) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -48,7 +53,12 @@ public class RequestSigningUnsupported implements BlobRequestSigner {
|
|||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, GetOptions options) {
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -66,11 +66,21 @@ public class AzureBlobRequestSigner implements BlobRequestSigner {
|
|||
return cleanRequest(processor.createRequest(getMethod, container, name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
return cleanRequest(processor.createRequest(createMethod, container, blobToBlob.apply(blob)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signRemoveBlob(String container, String name) {
|
||||
return cleanRequest(processor.createRequest(deleteMethod, container, name));
|
||||
|
|
|
@ -19,7 +19,6 @@ import org.jclouds.openstack.swift.domain.SwiftObject;
|
|||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
|
@ -42,7 +41,6 @@ public class HPCloudObjectStorageBlobRequestSigner implements BlobRequestSigner
|
|||
GetOptions[].class);
|
||||
this.deleteMethod = HPCloudObjectStorageAsyncClient.class.getMethod("removeObject", String.class, String.class);
|
||||
this.createMethod = HPCloudObjectStorageAsyncClient.class.getMethod("putObject", String.class, SwiftObject.class);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,11 +48,21 @@ public class HPCloudObjectStorageBlobRequestSigner implements BlobRequestSigner
|
|||
return cleanRequest(processor.createRequest(getMethod, container, name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob) {
|
||||
return cleanRequest(processor.createRequest(createMethod, container, blobToObject.apply(blob)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signRemoveBlob(String container, String name) {
|
||||
return cleanRequest(processor.createRequest(deleteMethod, container, name));
|
||||
|
|
Loading…
Reference in New Issue