mirror of https://github.com/apache/jclouds.git
Merge pull request #837 from andreisavu/generic-tempurl
Generic support for Swift TempUrl middleware
This commit is contained in:
commit
fdf5ff403f
|
@ -39,7 +39,6 @@ import org.jclouds.cloudfiles.binders.BindIterableToHeadersWithPurgeCDNObjectEma
|
||||||
import org.jclouds.cloudfiles.domain.ContainerCDNMetadata;
|
import org.jclouds.cloudfiles.domain.ContainerCDNMetadata;
|
||||||
import org.jclouds.cloudfiles.functions.ParseCdnUriFromHeaders;
|
import org.jclouds.cloudfiles.functions.ParseCdnUriFromHeaders;
|
||||||
import org.jclouds.cloudfiles.functions.ParseContainerCDNMetadataFromHeaders;
|
import org.jclouds.cloudfiles.functions.ParseContainerCDNMetadataFromHeaders;
|
||||||
import org.jclouds.cloudfiles.functions.ParseTemporaryUrlKeyFromHeaders;
|
|
||||||
import org.jclouds.cloudfiles.options.ListCdnContainerOptions;
|
import org.jclouds.cloudfiles.options.ListCdnContainerOptions;
|
||||||
import org.jclouds.cloudfiles.reference.CloudFilesHeaders;
|
import org.jclouds.cloudfiles.reference.CloudFilesHeaders;
|
||||||
import org.jclouds.openstack.filters.AuthenticateRequest;
|
import org.jclouds.openstack.filters.AuthenticateRequest;
|
||||||
|
@ -202,19 +201,4 @@ public interface CloudFilesAsyncClient extends CommonSwiftAsyncClient {
|
||||||
@Headers(keys = CloudFilesHeaders.CDN_WEBSITE_ERROR, values = "{error}")
|
@Headers(keys = CloudFilesHeaders.CDN_WEBSITE_ERROR, values = "{error}")
|
||||||
ListenableFuture<Boolean> setCDNStaticWebsiteError(@PathParam("container") String container,
|
ListenableFuture<Boolean> setCDNStaticWebsiteError(@PathParam("container") String container,
|
||||||
@PathParam("error") String error);
|
@PathParam("error") String error);
|
||||||
|
|
||||||
/**
|
|
||||||
* @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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,25 +105,4 @@ public interface CloudFilesClient extends CommonSwiftClient {
|
||||||
* http://docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Error_Pages_for_Static_Website-dle4005.html
|
* http://docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Error_Pages_for_Static_Website-dle4005.html
|
||||||
*/
|
*/
|
||||||
boolean setCDNStaticWebsiteError(String container, String error);
|
boolean setCDNStaticWebsiteError(String container, String error);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,124 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,14 +25,14 @@ import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobRequestSigner;
|
import org.jclouds.blobstore.BlobRequestSigner;
|
||||||
import org.jclouds.cloudfiles.CloudFilesClient;
|
import org.jclouds.cloudfiles.CloudFilesClient;
|
||||||
import org.jclouds.cloudfiles.TemporaryUrlKey;
|
import org.jclouds.openstack.swift.TemporaryUrlKey;
|
||||||
import org.jclouds.cloudfiles.blobstore.CloudFilesAsyncBlobStore;
|
import org.jclouds.cloudfiles.blobstore.CloudFilesAsyncBlobStore;
|
||||||
import org.jclouds.cloudfiles.blobstore.CloudFilesBlobRequestSigner;
|
|
||||||
import org.jclouds.cloudfiles.blobstore.CloudFilesBlobStore;
|
import org.jclouds.cloudfiles.blobstore.CloudFilesBlobStore;
|
||||||
import org.jclouds.cloudfiles.blobstore.functions.CloudFilesObjectToBlobMetadata;
|
import org.jclouds.cloudfiles.blobstore.functions.CloudFilesObjectToBlobMetadata;
|
||||||
import org.jclouds.cloudfiles.domain.ContainerCDNMetadata;
|
import org.jclouds.cloudfiles.domain.ContainerCDNMetadata;
|
||||||
import org.jclouds.date.TimeStamp;
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.openstack.swift.blobstore.SwiftAsyncBlobStore;
|
import org.jclouds.openstack.swift.blobstore.SwiftAsyncBlobStore;
|
||||||
|
import org.jclouds.openstack.swift.blobstore.SwiftBlobRequestSigner;
|
||||||
import org.jclouds.openstack.swift.blobstore.SwiftBlobStore;
|
import org.jclouds.openstack.swift.blobstore.SwiftBlobStore;
|
||||||
import org.jclouds.openstack.swift.blobstore.config.SwiftBlobStoreContextModule;
|
import org.jclouds.openstack.swift.blobstore.config.SwiftBlobStoreContextModule;
|
||||||
import org.jclouds.openstack.swift.blobstore.functions.ObjectToBlobMetadata;
|
import org.jclouds.openstack.swift.blobstore.functions.ObjectToBlobMetadata;
|
||||||
|
@ -64,28 +64,12 @@ public class CloudFilesBlobStoreContextModule extends SwiftBlobStoreContextModul
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@TimeStamp
|
|
||||||
protected Long unixEpochTimestampProvider() {
|
|
||||||
return System.currentTimeMillis() / 1000; /* convert to seconds */
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@TemporaryUrlKey
|
|
||||||
protected String temporaryUrlKeyProvider(CloudFilesClient client) {
|
|
||||||
return client.getTemporaryUrlKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
super.configure();
|
super.configure();
|
||||||
bind(SwiftBlobStore.class).to(CloudFilesBlobStore.class);
|
bind(SwiftBlobStore.class).to(CloudFilesBlobStore.class);
|
||||||
bind(SwiftAsyncBlobStore.class).to(CloudFilesAsyncBlobStore.class);
|
bind(SwiftAsyncBlobStore.class).to(CloudFilesAsyncBlobStore.class);
|
||||||
bind(ObjectToBlobMetadata.class).to(CloudFilesObjectToBlobMetadata.class);
|
bind(ObjectToBlobMetadata.class).to(CloudFilesObjectToBlobMetadata.class);
|
||||||
}
|
bind(BlobRequestSigner.class).to(SwiftBlobRequestSigner.class);
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configureRequestSigner() {
|
|
||||||
bind(BlobRequestSigner.class).to(CloudFilesBlobRequestSigner.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,6 @@ import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
||||||
*/
|
*/
|
||||||
public interface CloudFilesHeaders extends 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_ENABLED = "X-CDN-Enabled";
|
||||||
public static final String CDN_LOG_RETENTION = "X-Log-Retention";
|
public static final String CDN_LOG_RETENTION = "X-Log-Retention";
|
||||||
public static final String CDN_TTL = "X-TTL";
|
public static final String CDN_TTL = "X-TTL";
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
/**
|
|
||||||
* 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,9 +18,8 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.cloudfiles.blobstore.integration;
|
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.jclouds.openstack.swift.blobstore.integration.SwiftBlobLiveTest;
|
||||||
|
import org.jclouds.openstack.swift.extensions.TemporaryUrlKeyApi;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -33,12 +32,13 @@ import static org.testng.Assert.assertNotNull;
|
||||||
*/
|
*/
|
||||||
@Test(groups = {"live"})
|
@Test(groups = {"live"})
|
||||||
public class CloudFilesBlobLiveTest extends SwiftBlobLiveTest {
|
public class CloudFilesBlobLiveTest extends SwiftBlobLiveTest {
|
||||||
|
|
||||||
public CloudFilesBlobLiveTest() {
|
public CloudFilesBlobLiveTest() {
|
||||||
provider = "cloudfiles";
|
provider = "cloudfiles";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetAndSetTemporaryUrlKey() {
|
public void testGetAndSetTemporaryUrlKey() {
|
||||||
CloudFilesClient client = view.unwrap(CloudFilesApiMetadata.CONTEXT_TOKEN).getApi();
|
TemporaryUrlKeyApi client = view.utils().injector().getInstance(TemporaryUrlKeyApi.class);
|
||||||
|
|
||||||
String currentSecretKey = client.getTemporaryUrlKey();
|
String currentSecretKey = client.getTemporaryUrlKey();
|
||||||
assertNotNull(currentSecretKey);
|
assertNotNull(currentSecretKey);
|
||||||
|
|
|
@ -18,19 +18,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.cloudfiles.blobstore.integration;
|
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.openstack.swift.blobstore.integration.SwiftBlobSignerLiveTest;
|
||||||
import org.jclouds.rest.AuthorizationException;
|
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
import org.testng.annotations.Test;
|
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
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
|
@ -39,58 +29,4 @@ public class CloudFilesBlobSignerLiveTest extends SwiftBlobSignerLiveTest {
|
||||||
public CloudFilesBlobSignerLiveTest() {
|
public CloudFilesBlobSignerLiveTest() {
|
||||||
provider = "cloudfiles";
|
provider = "cloudfiles";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,5 +248,4 @@ public interface CommonSwiftAsyncClient {
|
||||||
@Headers(keys = "X-Object-Manifest", values="{container}/{name}")
|
@Headers(keys = "X-Object-Manifest", values="{container}/{name}")
|
||||||
ListenableFuture<String> putObjectManifest(@PathParam("container") String container,
|
ListenableFuture<String> putObjectManifest(@PathParam("container") String container,
|
||||||
@PathParam("name") String name);
|
@PathParam("name") String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.jclouds.cloudfiles;
|
package org.jclouds.openstack.swift;
|
||||||
|
|
||||||
import javax.inject.Qualifier;
|
import javax.inject.Qualifier;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
|
@ -18,32 +18,47 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.openstack.swift.blobstore;
|
package org.jclouds.openstack.swift.blobstore;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
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;
|
import static org.jclouds.blobstore.util.BlobStoreUtils.cleanRequest;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.inject.Provider;
|
||||||
import org.jclouds.blobstore.BlobRequestSigner;
|
import org.jclouds.blobstore.BlobRequestSigner;
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
|
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
|
||||||
import org.jclouds.openstack.swift.CommonSwiftAsyncClient;
|
import org.jclouds.openstack.swift.CommonSwiftAsyncClient;
|
||||||
|
import org.jclouds.openstack.swift.TemporaryUrlKey;
|
||||||
import org.jclouds.openstack.swift.blobstore.functions.BlobToObject;
|
import org.jclouds.openstack.swift.blobstore.functions.BlobToObject;
|
||||||
import org.jclouds.openstack.swift.domain.SwiftObject;
|
import org.jclouds.openstack.swift.domain.SwiftObject;
|
||||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
||||||
|
|
||||||
private final RestAnnotationProcessor<CommonSwiftAsyncClient> processor;
|
private final RestAnnotationProcessor<CommonSwiftAsyncClient> processor;
|
||||||
|
private final Crypto crypto;
|
||||||
|
|
||||||
|
private final Provider<Long> unixEpochTimestampProvider;
|
||||||
|
private final Supplier<String> temporaryUrlKeySupplier;
|
||||||
|
|
||||||
private final BlobToObject blobToObject;
|
private final BlobToObject blobToObject;
|
||||||
private final BlobToHttpGetOptions blob2HttpGetOptions;
|
private final BlobToHttpGetOptions blob2HttpGetOptions;
|
||||||
|
@ -54,14 +69,19 @@ public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SwiftBlobRequestSigner(RestAnnotationProcessor<CommonSwiftAsyncClient> processor, BlobToObject blobToObject,
|
public SwiftBlobRequestSigner(RestAnnotationProcessor<CommonSwiftAsyncClient> processor, BlobToObject blobToObject,
|
||||||
BlobToHttpGetOptions blob2HttpGetOptions) throws SecurityException, NoSuchMethodException {
|
BlobToHttpGetOptions blob2HttpGetOptions, Crypto crypto, @TimeStamp Provider<Long> unixEpochTimestampProvider,
|
||||||
|
@TemporaryUrlKey Supplier<String> temporaryUrlKeySupplier) throws SecurityException, NoSuchMethodException {
|
||||||
this.processor = checkNotNull(processor, "processor");
|
this.processor = checkNotNull(processor, "processor");
|
||||||
|
this.crypto = checkNotNull(crypto, "crypto");
|
||||||
|
|
||||||
|
this.unixEpochTimestampProvider = checkNotNull(unixEpochTimestampProvider, "unixEpochTimestampProvider");
|
||||||
|
this.temporaryUrlKeySupplier = checkNotNull(temporaryUrlKeySupplier, "temporaryUrlKeyProvider");
|
||||||
|
|
||||||
this.blobToObject = checkNotNull(blobToObject, "blobToObject");
|
this.blobToObject = checkNotNull(blobToObject, "blobToObject");
|
||||||
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
|
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
|
||||||
|
|
||||||
this.getMethod = CommonSwiftAsyncClient.class.getMethod("getObject", String.class, String.class,
|
this.getMethod = CommonSwiftAsyncClient.class.getMethod("getObject", String.class, String.class,
|
||||||
GetOptions[].class);
|
GetOptions[].class);
|
||||||
this.deleteMethod = CommonSwiftAsyncClient.class.getMethod("removeObject", String.class, String.class);
|
this.deleteMethod = CommonSwiftAsyncClient.class.getMethod("removeObject", String.class, String.class);
|
||||||
this.createMethod = CommonSwiftAsyncClient.class.getMethod("putObject", String.class, SwiftObject.class);
|
this.createMethod = CommonSwiftAsyncClient.class.getMethod("putObject", String.class, SwiftObject.class);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +93,13 @@ public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
|
||||||
throw new UnsupportedOperationException();
|
HttpRequest request = processor.createRequest(getMethod, container, name);
|
||||||
|
return cleanRequest(signForTemporaryAccess(request, timeInSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
@Override
|
||||||
|
@ -83,7 +109,8 @@ public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
|
||||||
throw new UnsupportedOperationException();
|
HttpRequest request = processor.createRequest(createMethod, container, blobToObject.apply(blob));
|
||||||
|
return cleanRequest(signForTemporaryAccess(request, timeInSeconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,8 +118,35 @@ public class SwiftBlobRequestSigner implements BlobRequestSigner {
|
||||||
return cleanRequest(processor.createRequest(deleteMethod, container, name));
|
return cleanRequest(processor.createRequest(deleteMethod, container, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private HttpRequest signForTemporaryAccess(HttpRequest request, long timeInSeconds) {
|
||||||
public HttpRequest signGetBlob(String container, String name, org.jclouds.blobstore.options.GetOptions options) {
|
HttpRequest.Builder builder = request.toBuilder();
|
||||||
return cleanRequest(processor.createRequest(getMethod, container, name, blob2HttpGetOptions.apply(options)));
|
builder.filters(filter(request.getFilters(), instanceOf(AuthenticateRequest.class)));
|
||||||
|
|
||||||
|
String key = temporaryUrlKeySupplier.get();
|
||||||
|
if (key == null) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,29 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.openstack.swift.blobstore.config;
|
package org.jclouds.openstack.swift.blobstore.config;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
import org.jclouds.blobstore.AsyncBlobStore;
|
import org.jclouds.blobstore.AsyncBlobStore;
|
||||||
import org.jclouds.blobstore.BlobRequestSigner;
|
import org.jclouds.blobstore.BlobRequestSigner;
|
||||||
import org.jclouds.blobstore.BlobStore;
|
import org.jclouds.blobstore.BlobStore;
|
||||||
import org.jclouds.blobstore.attr.ConsistencyModel;
|
import org.jclouds.blobstore.attr.ConsistencyModel;
|
||||||
import org.jclouds.blobstore.config.BlobStoreMapModule;
|
import org.jclouds.blobstore.config.BlobStoreMapModule;
|
||||||
|
import org.jclouds.date.TimeStamp;
|
||||||
|
import org.jclouds.openstack.swift.TemporaryUrlKey;
|
||||||
import org.jclouds.openstack.swift.blobstore.SwiftAsyncBlobStore;
|
import org.jclouds.openstack.swift.blobstore.SwiftAsyncBlobStore;
|
||||||
import org.jclouds.openstack.swift.blobstore.SwiftBlobRequestSigner;
|
import org.jclouds.openstack.swift.blobstore.SwiftBlobRequestSigner;
|
||||||
import org.jclouds.openstack.swift.blobstore.SwiftBlobStore;
|
import org.jclouds.openstack.swift.blobstore.SwiftBlobStore;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Scopes;
|
import com.google.inject.Scopes;
|
||||||
|
import org.jclouds.openstack.swift.extensions.TemporaryUrlKeyApi;
|
||||||
|
import org.jclouds.openstack.swift.extensions.TemporaryUrlKeyAsyncApi;
|
||||||
|
import org.jclouds.openstack.swift.suppliers.ReturnOrFetchTemporaryUrlKey;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link CloudFilesBlobStoreContext}; requires {@link SwiftAsyncBlobStore}
|
* Configures the {@link CloudFilesBlobStoreContext}; requires {@link SwiftAsyncBlobStore}
|
||||||
|
@ -38,16 +50,25 @@ import com.google.inject.Scopes;
|
||||||
*/
|
*/
|
||||||
public class SwiftBlobStoreContextModule extends AbstractModule {
|
public class SwiftBlobStoreContextModule extends AbstractModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@TimeStamp
|
||||||
|
protected Long unixEpochTimestampProvider() {
|
||||||
|
return System.currentTimeMillis() / 1000; /* convert to seconds */
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
install(new BlobStoreMapModule());
|
install(new BlobStoreMapModule());
|
||||||
bind(ConsistencyModel.class).toInstance(ConsistencyModel.STRICT);
|
bind(ConsistencyModel.class).toInstance(ConsistencyModel.STRICT);
|
||||||
bind(AsyncBlobStore.class).to(SwiftAsyncBlobStore.class).in(Scopes.SINGLETON);
|
bind(AsyncBlobStore.class).to(SwiftAsyncBlobStore.class).in(Scopes.SINGLETON);
|
||||||
bind(BlobStore.class).to(SwiftBlobStore.class).in(Scopes.SINGLETON);
|
bind(BlobStore.class).to(SwiftBlobStore.class).in(Scopes.SINGLETON);
|
||||||
configureRequestSigner();
|
bind(BlobRequestSigner.class).to(SwiftBlobRequestSigner.class);
|
||||||
|
configureTemporaryUrlExtension();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void configureRequestSigner() {
|
protected void configureTemporaryUrlExtension() {
|
||||||
bind(BlobRequestSigner.class).to(SwiftBlobRequestSigner.class);
|
bindClientAndAsyncClient(binder(), TemporaryUrlKeyApi.class, TemporaryUrlKeyAsyncApi.class);
|
||||||
|
bind(new TypeLiteral<Supplier<String>>() {
|
||||||
|
}).annotatedWith(TemporaryUrlKey.class).to(ReturnOrFetchTemporaryUrlKey.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* 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.openstack.swift.extensions;
|
||||||
|
|
||||||
|
import org.jclouds.concurrent.Timeout;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrei Savu
|
||||||
|
* @see <a href="http://docs.rackspace.com/files/api/v1/cf-devguide/content/Public_Access_to_Account-d1a4440.html" />
|
||||||
|
*/
|
||||||
|
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
|
||||||
|
public interface TemporaryUrlKeyApi {
|
||||||
|
/**
|
||||||
|
* Retrieve the key used to generate Temporary object access URLs
|
||||||
|
*
|
||||||
|
* @return shared secret key or null
|
||||||
|
* @see <a href="http://docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Account_Metadata-d1a4460.html" />
|
||||||
|
*/
|
||||||
|
String getTemporaryUrlKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To create a Temporary URL you must first set a key as account metadata.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param temporaryUrlKey
|
||||||
|
* @see <a href="http://docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Account_Metadata-d1a4460.html" />
|
||||||
|
*/
|
||||||
|
void setTemporaryUrlKey(String temporaryUrlKey);
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* 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.openstack.swift.extensions;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import org.jclouds.openstack.filters.AuthenticateRequest;
|
||||||
|
import org.jclouds.openstack.swift.Storage;
|
||||||
|
import org.jclouds.openstack.swift.functions.ParseTemporaryUrlKeyFromHeaders;
|
||||||
|
import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
||||||
|
import org.jclouds.rest.annotations.Endpoint;
|
||||||
|
import org.jclouds.rest.annotations.RequestFilters;
|
||||||
|
import org.jclouds.rest.annotations.ResponseParser;
|
||||||
|
import org.jclouds.rest.annotations.SkipEncoding;
|
||||||
|
|
||||||
|
import javax.ws.rs.HEAD;
|
||||||
|
import javax.ws.rs.HeaderParam;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrei Savu
|
||||||
|
* @see TemporaryUrlKeyApi
|
||||||
|
*/
|
||||||
|
@SkipEncoding('/')
|
||||||
|
@RequestFilters(AuthenticateRequest.class)
|
||||||
|
@Endpoint(Storage.class)
|
||||||
|
public interface TemporaryUrlKeyAsyncApi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see TemporaryUrlKeyApi#getTemporaryUrlKey
|
||||||
|
*/
|
||||||
|
@HEAD
|
||||||
|
@Path("/")
|
||||||
|
@ResponseParser(ParseTemporaryUrlKeyFromHeaders.class)
|
||||||
|
ListenableFuture<String> getTemporaryUrlKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see TemporaryUrlKeyApi#setTemporaryUrlKey
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/")
|
||||||
|
ListenableFuture<Void> setTemporaryUrlKey(@HeaderParam(SwiftHeaders.ACCOUNT_TEMPORARY_URL_KEY) String key);
|
||||||
|
|
||||||
|
}
|
|
@ -16,13 +16,15 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.jclouds.cloudfiles.functions;
|
package org.jclouds.openstack.swift.functions;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
||||||
|
|
||||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||||
import static org.jclouds.cloudfiles.reference.CloudFilesHeaders.ACCOUNT_TEMPORARY_URL_KEY;
|
import static org.jclouds.openstack.swift.reference.SwiftHeaders.ACCOUNT_TEMPORARY_URL_KEY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andrei Savu
|
* @author Andrei Savu
|
||||||
|
@ -31,6 +33,11 @@ public class ParseTemporaryUrlKeyFromHeaders implements Function<HttpResponse, S
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String apply(HttpResponse httpResponse) {
|
public String apply(HttpResponse httpResponse) {
|
||||||
return getOnlyElement(httpResponse.getHeaders().get(ACCOUNT_TEMPORARY_URL_KEY));
|
Multimap<String, String> headers = httpResponse.getHeaders();
|
||||||
|
if (headers.containsKey(ACCOUNT_TEMPORARY_URL_KEY)) {
|
||||||
|
return getOnlyElement(headers.get(ACCOUNT_TEMPORARY_URL_KEY));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,12 +24,15 @@ package org.jclouds.openstack.swift.reference;
|
||||||
*/
|
*/
|
||||||
public interface SwiftHeaders {
|
public interface SwiftHeaders {
|
||||||
|
|
||||||
|
public static final String ACCOUNT_TEMPORARY_URL_KEY = "X-Account-Meta-Temp-Url-Key";
|
||||||
public static final String ACCOUNT_BYTES_USED = "X-Account-Bytes-Used";
|
public static final String ACCOUNT_BYTES_USED = "X-Account-Bytes-Used";
|
||||||
public static final String ACCOUNT_CONTAINER_COUNT = "X-Account-Container-Count";
|
public static final String ACCOUNT_CONTAINER_COUNT = "X-Account-Container-Count";
|
||||||
|
|
||||||
public static final String CONTAINER_BYTES_USED = "X-Container-Bytes-Used";
|
public static final String CONTAINER_BYTES_USED = "X-Container-Bytes-Used";
|
||||||
public static final String CONTAINER_OBJECT_COUNT = "X-Container-Object-Count";
|
public static final String CONTAINER_OBJECT_COUNT = "X-Container-Object-Count";
|
||||||
public static final String CONTAINER_METADATA_PREFIX = "X-Container-Meta-";
|
public static final String CONTAINER_METADATA_PREFIX = "X-Container-Meta-";
|
||||||
public static final String CONTAINER_DELETE_METADATA_PREFIX = "X-Remove-Container-Meta-";
|
public static final String CONTAINER_DELETE_METADATA_PREFIX = "X-Remove-Container-Meta-";
|
||||||
|
|
||||||
public static final String USER_METADATA_PREFIX = "X-Object-Meta-";
|
public static final String USER_METADATA_PREFIX = "X-Object-Meta-";
|
||||||
public static final String OBJECT_COPY_FROM = "X-Copy-From";
|
public static final String OBJECT_COPY_FROM = "X-Copy-From";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* 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.openstack.swift.suppliers;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
import org.jclouds.openstack.swift.extensions.TemporaryUrlKeyApi;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class ReturnOrFetchTemporaryUrlKey implements Supplier<String> {
|
||||||
|
|
||||||
|
private TemporaryUrlKeyApi client;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ReturnOrFetchTemporaryUrlKey(TemporaryUrlKeyApi client) {
|
||||||
|
this.client = checkNotNull(client, "client");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
String key = client.getTemporaryUrlKey();
|
||||||
|
if (key == null) {
|
||||||
|
client.setTemporaryUrlKey(UUID.randomUUID().toString());
|
||||||
|
return client.getTemporaryUrlKey();
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,38 +21,47 @@ package org.jclouds.openstack.swift;
|
||||||
import static org.jclouds.Constants.PROPERTY_API_VERSION;
|
import static org.jclouds.Constants.PROPERTY_API_VERSION;
|
||||||
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
|
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
|
||||||
import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
|
import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
|
||||||
|
import static org.jclouds.rest.config.BinderUtils.bindClientAndAsyncClient;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.google.inject.*;
|
||||||
|
import com.google.inject.util.Modules;
|
||||||
import org.jclouds.apis.ApiMetadata;
|
import org.jclouds.apis.ApiMetadata;
|
||||||
|
import org.jclouds.date.TimeStamp;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.openstack.functions.URIFromAuthenticationResponseForService;
|
import org.jclouds.openstack.functions.URIFromAuthenticationResponseForService;
|
||||||
import org.jclouds.openstack.internal.TestOpenStackAuthenticationModule;
|
import org.jclouds.openstack.internal.TestOpenStackAuthenticationModule;
|
||||||
import org.jclouds.openstack.reference.AuthHeaders;
|
import org.jclouds.openstack.reference.AuthHeaders;
|
||||||
import org.jclouds.openstack.swift.blobstore.config.SwiftBlobStoreContextModule;
|
import org.jclouds.openstack.swift.blobstore.config.SwiftBlobStoreContextModule;
|
||||||
import org.jclouds.openstack.swift.config.SwiftRestClientModule;
|
import org.jclouds.openstack.swift.config.SwiftRestClientModule;
|
||||||
|
import org.jclouds.openstack.swift.extensions.TemporaryUrlKeyApi;
|
||||||
|
import org.jclouds.openstack.swift.extensions.TemporaryUrlKeyAsyncApi;
|
||||||
|
import org.jclouds.openstack.swift.suppliers.ReturnOrFetchTemporaryUrlKey;
|
||||||
import org.jclouds.rest.internal.BaseAsyncClientTest;
|
import org.jclouds.rest.internal.BaseAsyncClientTest;
|
||||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.Module;
|
|
||||||
import com.google.inject.Provides;
|
|
||||||
import com.google.inject.TypeLiteral;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests behavior of {@code BindSwiftObjectMetadataToRequest}
|
* Tests behavior of {@code BindSwiftObjectMetadataToRequest}
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
|
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
|
||||||
@Test(groups = "unit", testName = "SwiftClientTest")
|
@Test(groups = "unit", testName = "CommonSwiftClientTest")
|
||||||
public abstract class CommonSwiftClientTest extends BaseAsyncClientTest<SwiftAsyncClient> {
|
public abstract class CommonSwiftClientTest extends BaseAsyncClientTest<SwiftAsyncClient> {
|
||||||
|
|
||||||
|
public static final long UNIX_EPOCH_TIMESTAMP = 123456789L;
|
||||||
|
|
||||||
|
public static final String TEMPORARY_URL_KEY = "get-or-set-X-Account-Meta-Temp-Url-Key";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TypeLiteral<RestAnnotationProcessor<SwiftAsyncClient>> createTypeLiteral() {
|
protected TypeLiteral<RestAnnotationProcessor<SwiftAsyncClient>> createTypeLiteral() {
|
||||||
return new TypeLiteral<RestAnnotationProcessor<SwiftAsyncClient>>() {
|
return new TypeLiteral<RestAnnotationProcessor<SwiftAsyncClient>>() {
|
||||||
|
@ -74,11 +83,25 @@ public abstract class CommonSwiftClientTest extends BaseAsyncClientTest<SwiftAsy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class StaticTimeAndTemporaryUrlKeyModule extends SwiftBlobStoreContextModule {
|
||||||
|
@Override
|
||||||
|
protected Long unixEpochTimestampProvider() {
|
||||||
|
return UNIX_EPOCH_TIMESTAMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureTemporaryUrlExtension() {
|
||||||
|
bindClientAndAsyncClient(binder(), TemporaryUrlKeyApi.class, TemporaryUrlKeyAsyncApi.class);
|
||||||
|
bind(new TypeLiteral<Supplier<String>>() {
|
||||||
|
}).annotatedWith(TemporaryUrlKey.class).toInstance(Suppliers.ofInstance(TEMPORARY_URL_KEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ApiMetadata createApiMetadata() {
|
protected ApiMetadata createApiMetadata() {
|
||||||
return new SwiftApiMetadata().toBuilder().defaultModules(
|
return new SwiftApiMetadata().toBuilder().defaultModules(
|
||||||
ImmutableSet.<Class<? extends Module>> of(StorageEndpointModule.class, SwiftRestClientModule.class,
|
ImmutableSet.<Class<? extends Module>>of(StorageEndpointModule.class, SwiftRestClientModule.class,
|
||||||
SwiftBlobStoreContextModule.class)).build();
|
StaticTimeAndTemporaryUrlKeyModule.class)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.jclouds.openstack.swift.blobstore;
|
package org.jclouds.openstack.swift.blobstore;
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.assertFalse;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -43,8 +44,7 @@ public class SwiftBlobRequestSignerTest extends CommonSwiftClientTest {
|
||||||
private BlobRequestSigner signer;
|
private BlobRequestSigner signer;
|
||||||
private Factory blobFactory;
|
private Factory blobFactory;
|
||||||
|
|
||||||
public void testSignGetBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
public void testSignGetBlob() throws Exception {
|
||||||
NoSuchMethodException, IOException {
|
|
||||||
HttpRequest request = signer.signGetBlob("container", "name");
|
HttpRequest request = signer.signGetBlob("container", "name");
|
||||||
|
|
||||||
assertRequestLineEquals(request, "GET http://storage/container/name HTTP/1.1");
|
assertRequestLineEquals(request, "GET http://storage/container/name HTTP/1.1");
|
||||||
|
@ -54,19 +54,18 @@ public class SwiftBlobRequestSignerTest extends CommonSwiftClientTest {
|
||||||
assertEquals(request.getFilters().size(), 0);
|
assertEquals(request.getFilters().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSignRemoveBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
public void testSignGetBlobWithTime() {
|
||||||
NoSuchMethodException, IOException {
|
HttpRequest request = signer.signGetBlob("container", "name", 120);
|
||||||
HttpRequest request = signer.signRemoveBlob("container", "name");
|
|
||||||
|
|
||||||
assertRequestLineEquals(request, "DELETE http://storage/container/name HTTP/1.1");
|
assertRequestLineEquals(request, "GET http://storage/container/name?" +
|
||||||
assertNonPayloadHeadersEqual(request, "X-Auth-Token: testtoken\n");
|
"temp_url_sig=4759d99d13c826bba0af2c9f0c526ca53c95abaf&temp_url_expires=123456909 HTTP/1.1");
|
||||||
|
assertFalse(request.getHeaders().containsKey("X-Auth-Token"));
|
||||||
assertPayloadEquals(request, null, null, false);
|
assertPayloadEquals(request, null, null, false);
|
||||||
|
|
||||||
assertEquals(request.getFilters().size(), 0);
|
assertEquals(request.getFilters().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSignPutBlob() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException,
|
public void testSignPutBlob() throws Exception {
|
||||||
NoSuchMethodException, IOException {
|
|
||||||
Blob blob = blobFactory.create(null);
|
Blob blob = blobFactory.create(null);
|
||||||
blob.getMetadata().setName("name");
|
blob.getMetadata().setName("name");
|
||||||
blob.setPayload("");
|
blob.setPayload("");
|
||||||
|
@ -84,6 +83,37 @@ public class SwiftBlobRequestSignerTest extends CommonSwiftClientTest {
|
||||||
assertEquals(request.getFilters().size(), 0);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSignRemoveBlob() throws Exception {
|
||||||
|
HttpRequest request = signer.signRemoveBlob("container", "name");
|
||||||
|
|
||||||
|
assertRequestLineEquals(request, "DELETE http://storage/container/name HTTP/1.1");
|
||||||
|
assertNonPayloadHeadersEqual(request, "X-Auth-Token: testtoken\n");
|
||||||
|
assertPayloadEquals(request, null, null, false);
|
||||||
|
|
||||||
|
assertEquals(request.getFilters().size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
protected void setupFactory() throws IOException {
|
protected void setupFactory() throws IOException {
|
||||||
super.setupFactory();
|
super.setupFactory();
|
||||||
|
|
|
@ -19,11 +19,16 @@
|
||||||
package org.jclouds.openstack.swift.blobstore.integration;
|
package org.jclouds.openstack.swift.blobstore.integration;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest;
|
import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest;
|
||||||
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
|
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
|
||||||
|
import org.jclouds.openstack.swift.extensions.TemporaryUrlKeyApi;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.assertNotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author James Murty
|
* @author James Murty
|
||||||
|
|
|
@ -18,17 +18,16 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.openstack.swift.blobstore.integration;
|
package org.jclouds.openstack.swift.blobstore.integration;
|
||||||
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.integration.internal.BaseBlobSignerLiveTest;
|
import org.jclouds.blobstore.integration.internal.BaseBlobSignerLiveTest;
|
||||||
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
|
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Test(groups = { "live" })
|
@Test(groups = {"live"})
|
||||||
public class SwiftBlobSignerLiveTest extends BaseBlobSignerLiveTest {
|
public class SwiftBlobSignerLiveTest extends BaseBlobSignerLiveTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,6 +31,7 @@ import java.util.concurrent.ExecutorService;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javax.ws.rs.HeaderParam;
|
||||||
|
|
||||||
import org.jclouds.Constants;
|
import org.jclouds.Constants;
|
||||||
import org.jclouds.blobstore.LocalAsyncBlobStore;
|
import org.jclouds.blobstore.LocalAsyncBlobStore;
|
||||||
|
@ -60,6 +61,7 @@ import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import org.jclouds.openstack.swift.reference.SwiftHeaders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link SwiftAsyncClient} which keeps all data in a local Map object.
|
* Implementation of {@link SwiftAsyncClient} which keeps all data in a local Map object.
|
||||||
|
@ -201,7 +203,7 @@ public class StubSwiftAsyncClient implements CommonSwiftAsyncClient {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<Boolean> setObjectInfo(String container, String key, Map<String, String> userMetadata) {
|
public ListenableFuture<Boolean> setObjectInfo(String container, String key, Map<String, String> userMetadata) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,41 +20,28 @@ package org.jclouds.blobstore.integration.internal;
|
||||||
|
|
||||||
import static org.jclouds.blobstore.options.GetOptions.Builder.range;
|
import static org.jclouds.blobstore.options.GetOptions.Builder.range;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.fail;
|
||||||
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.rest.AuthorizationException;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
|
import org.testng.SkipException;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests integrated functionality of all signature commands.
|
* Tests integrated functionality of all signature commands.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Each test uses a different container name, so it should be perfectly fine to run in parallel.
|
* Each test uses a different container name, so it should be perfectly fine to run in parallel.
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Test(groups = { "live" })
|
@Test(groups = {"live"})
|
||||||
public class BaseBlobSignerLiveTest extends BaseBlobStoreIntegrationTest {
|
public class BaseBlobSignerLiveTest extends BaseBlobStoreIntegrationTest {
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSignRemoveUrl() throws Exception {
|
|
||||||
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().signRemoveBlob(container, name);
|
|
||||||
assertEquals(request.getFilters().size(), 0);
|
|
||||||
view.utils().http().invoke(request);
|
|
||||||
assert !view.getBlobStore().blobExists(container, name);
|
|
||||||
} finally {
|
|
||||||
returnContainer(container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignGetUrl() throws Exception {
|
public void testSignGetUrl() throws Exception {
|
||||||
String name = "hello";
|
String name = "hello";
|
||||||
|
@ -91,6 +78,34 @@ public class BaseBlobSignerLiveTest extends BaseBlobStoreIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
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) {
|
||||||
|
}
|
||||||
|
} catch (UnsupportedOperationException ignore) {
|
||||||
|
throw new SkipException("signGetUrl with a time limit is not supported on " + provider);
|
||||||
|
} finally {
|
||||||
|
returnContainer(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignPutUrl() throws Exception {
|
public void testSignPutUrl() throws Exception {
|
||||||
String name = "hello";
|
String name = "hello";
|
||||||
|
@ -108,4 +123,52 @@ public class BaseBlobSignerLiveTest extends BaseBlobStoreIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
}
|
||||||
|
} catch (UnsupportedOperationException ignore) {
|
||||||
|
throw new SkipException("signPutUrl with a time limit is not supported on " + provider);
|
||||||
|
} finally {
|
||||||
|
returnContainer(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignRemoveUrl() throws Exception {
|
||||||
|
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().signRemoveBlob(container, name);
|
||||||
|
assertEquals(request.getFilters().size(), 0);
|
||||||
|
view.utils().http().invoke(request);
|
||||||
|
assert !view.getBlobStore().blobExists(container, name);
|
||||||
|
} finally {
|
||||||
|
returnContainer(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue