mirror of https://github.com/apache/jclouds.git
Merge pull request #875 from andrewgaul/hpcloud-temporary-signing
Support HP Cloud Object Storage signed TempURL
This commit is contained in:
commit
f94504c9aa
|
@ -25,11 +25,11 @@ import java.util.Properties;
|
|||
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.blobstore.BlobRequestSigner;
|
||||
import org.jclouds.hpcloud.objectstorage.blobstore.HPCloudObjectStorageBlobRequestSigner;
|
||||
import org.jclouds.hpcloud.objectstorage.blobstore.config.HPCloudObjectStorageBlobStoreContextModule;
|
||||
import org.jclouds.hpcloud.objectstorage.config.HPCloudObjectStorageRestClientModule;
|
||||
import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
|
||||
import org.jclouds.openstack.swift.SwiftKeystoneApiMetadata;
|
||||
import org.jclouds.openstack.swift.blobstore.SwiftBlobSigner;
|
||||
import org.jclouds.openstack.swift.blobstore.config.TemporaryUrlExtensionModule;
|
||||
import org.jclouds.openstack.swift.config.SwiftRestClientModule.KeystoneStorageEndpointModule;
|
||||
import org.jclouds.openstack.swift.extensions.KeystoneTemporaryUrlKeyAsyncApi;
|
||||
|
@ -39,7 +39,6 @@ import org.jclouds.rest.RestContext;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
/**
|
||||
* Implementation of {@link org.jclouds.providers.ProviderMetadata} for HP Cloud Services Object Storage
|
||||
*
|
||||
|
@ -115,8 +114,7 @@ public class HPCloudObjectStorageApiMetadata extends SwiftKeystoneApiMetadata {
|
|||
|
||||
@Override
|
||||
protected void bindRequestSigner() {
|
||||
bind(BlobRequestSigner.class).to(new TypeLiteral<SwiftBlobSigner<HPCloudObjectStorageAsyncApi>>() {
|
||||
});
|
||||
bind(BlobRequestSigner.class).to(HPCloudObjectStorageBlobRequestSigner.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/**
|
||||
* 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.hpcloud.objectstorage.blobstore;
|
||||
|
||||
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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.InvalidKeyException;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
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.domain.Blob;
|
||||
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.hpcloud.objectstorage.HPCloudObjectStorageAsyncApi;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.options.GetOptions;
|
||||
import org.jclouds.openstack.keystone.v2_0.domain.Access;
|
||||
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
|
||||
import org.jclouds.openstack.swift.TemporaryUrlKey;
|
||||
import org.jclouds.openstack.swift.blobstore.functions.BlobToObject;
|
||||
import org.jclouds.openstack.swift.domain.SwiftObject;
|
||||
import org.jclouds.rest.annotations.Credential;
|
||||
import org.jclouds.rest.annotations.Identity;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
|
||||
/**
|
||||
* Signer for HP's variant of temporary signed URLs. They prefix the signature
|
||||
* with the tenant id.
|
||||
*
|
||||
* @author Andrew Gaul
|
||||
*/
|
||||
@Singleton
|
||||
public class HPCloudObjectStorageBlobRequestSigner implements BlobRequestSigner {
|
||||
|
||||
private final RestAnnotationProcessor<HPCloudObjectStorageAsyncApi> processor;
|
||||
private final Crypto crypto;
|
||||
|
||||
private final Provider<Long> unixEpochTimestampProvider;
|
||||
private final Supplier<Access> access;
|
||||
private String tenantId;
|
||||
private final String accessKeyId;
|
||||
private final String secretKey;
|
||||
|
||||
private final BlobToObject blobToObject;
|
||||
private final BlobToHttpGetOptions blob2HttpGetOptions;
|
||||
|
||||
private final Method getMethod;
|
||||
private final Method deleteMethod;
|
||||
private final Method createMethod;
|
||||
|
||||
@Inject
|
||||
public HPCloudObjectStorageBlobRequestSigner(RestAnnotationProcessor<HPCloudObjectStorageAsyncApi> processor, BlobToObject blobToObject,
|
||||
BlobToHttpGetOptions blob2HttpGetOptions,
|
||||
Crypto crypto, @TimeStamp Provider<Long> unixEpochTimestampProvider,
|
||||
Supplier<Access> access,
|
||||
@Identity String accessKey, @Credential String secretKey)
|
||||
throws SecurityException, NoSuchMethodException {
|
||||
this.processor = checkNotNull(processor, "processor");
|
||||
this.crypto = checkNotNull(crypto, "crypto");
|
||||
|
||||
this.unixEpochTimestampProvider = checkNotNull(unixEpochTimestampProvider, "unixEpochTimestampProvider");
|
||||
this.access = checkNotNull(access, "access");
|
||||
// accessKey is of the form tenantName:accessKeyId (not tenantId)
|
||||
this.accessKeyId = accessKey.substring(accessKey.indexOf(':') + 1);
|
||||
this.secretKey = secretKey;
|
||||
|
||||
this.blobToObject = checkNotNull(blobToObject, "blobToObject");
|
||||
this.blob2HttpGetOptions = checkNotNull(blob2HttpGetOptions, "blob2HttpGetOptions");
|
||||
|
||||
this.getMethod = HPCloudObjectStorageAsyncApi.class.getMethod("getObject", String.class, String.class,
|
||||
GetOptions[].class);
|
||||
this.deleteMethod = HPCloudObjectStorageAsyncApi.class.getMethod("removeObject", String.class, String.class);
|
||||
this.createMethod = HPCloudObjectStorageAsyncApi.class.getMethod("putObject", String.class, SwiftObject.class);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void populateTenantId() {
|
||||
// Defer call from constructor since access.get issues an RPC.
|
||||
this.tenantId = access.get().getToken().getTenant().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest signGetBlob(String container, String name) {
|
||||
return cleanRequest(processor.createRequest(getMethod, container, name));
|
||||
}
|
||||
|
||||
@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 signGetBlob(String container, String name, org.jclouds.blobstore.options.GetOptions options) {
|
||||
return cleanRequest(processor.createRequest(getMethod, container, name, blob2HttpGetOptions.apply(options)));
|
||||
}
|
||||
|
||||
@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)));
|
||||
|
||||
long expiresInSeconds = unixEpochTimestampProvider.get() + timeInSeconds;
|
||||
String signature = createSignature(secretKey, createStringToSign(
|
||||
request.getMethod().toUpperCase(), request, expiresInSeconds));
|
||||
|
||||
builder.addQueryParam("temp_url_sig",
|
||||
String.format("%s:%s:%s", tenantId, accessKeyId, signature));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,7 +60,8 @@ public class HPCloudObjectStorageBlobSignerExpectTest extends BaseBlobSignerExpe
|
|||
@Override
|
||||
protected HttpRequest getBlobWithTime() {
|
||||
return HttpRequest.builder().method("GET")
|
||||
.endpoint("https://objects.jclouds.org/v1.0/40806637803162/container/name?temp_url_sig=fd9b09acbc3ce71182240503c803dda4902098a9&temp_url_expires=123456792").build();
|
||||
.endpoint("https://objects.jclouds.org/v1.0/40806637803162/container/name?temp_url_sig=40806637803162%3Aidentity%3Ada88bc31122f0d0806b1c7bf71cd3af5c5d5b94c&temp_url_expires=123456792")
|
||||
.addHeader("X-Auth-Token", "Auth_4f173437e4b013bee56d1007").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,7 +81,8 @@ public class HPCloudObjectStorageBlobSignerExpectTest extends BaseBlobSignerExpe
|
|||
@Override
|
||||
protected HttpRequest putBlobWithTime() {
|
||||
return HttpRequest.builder().method("PUT")
|
||||
.endpoint("https://objects.jclouds.org/v1.0/40806637803162/container/name?temp_url_sig=72e5f6ebafab2b3da0586198797e58fb7478211e&temp_url_expires=123456792").build();
|
||||
.endpoint("https://objects.jclouds.org/v1.0/40806637803162/container/name?temp_url_sig=40806637803162%3Aidentity%3Ac90269245ab0a316d5ea5e654d4c2a975fb4bf77&temp_url_expires=123456792")
|
||||
.addHeader("X-Auth-Token", "Auth_4f173437e4b013bee56d1007").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue