mirror of https://github.com/apache/jclouds.git
JCLOUDS-457: Created the skeleton of the Glacier API.
This commit is contained in:
commit
d4af465ca8
|
@ -0,0 +1,128 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.jclouds.labs</groupId>
|
||||
<artifactId>jclouds-labs-aws</artifactId>
|
||||
<version>1.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.apache.jclouds.labs</groupId>
|
||||
<artifactId>glacier</artifactId>
|
||||
<name>jclouds glacier api</name>
|
||||
<description>jclouds components to access an implementation of glacier</description>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<properties>
|
||||
<test.glacier.endpoint>https://glacier.eu-west-1.amazonaws.com</test.glacier.endpoint>
|
||||
<test.glacier.api-version>2012-06-01</test.glacier.api-version>
|
||||
<test.glacier.build-version />
|
||||
<test.glacier.identity>${test.aws.identity}</test.glacier.identity>
|
||||
<test.glacier.credential>${test.aws.credential}</test.glacier.credential>
|
||||
|
||||
<jclouds.osgi.export>org.jclouds.glacier*;version="${project.version}"</jclouds.osgi.export>
|
||||
<jclouds.osgi.import>
|
||||
org.jclouds.labs*;version="${project.version}",
|
||||
org.jclouds*;version="${project.version}",
|
||||
*
|
||||
</jclouds.osgi.import>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.jclouds.api</groupId>
|
||||
<artifactId>sts</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.jclouds</groupId>
|
||||
<artifactId>jclouds-blobstore</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.jclouds</groupId>
|
||||
<artifactId>jclouds-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.jclouds</groupId>
|
||||
<artifactId>jclouds-blobstore</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.jclouds.driver</groupId>
|
||||
<artifactId>jclouds-log4j</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.mockwebserver</groupId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>live</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
|
||||
<jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
|
||||
<test.glacier.endpoint>${test.glacier.endpoint}</test.glacier.endpoint>
|
||||
<test.glacier.api-version>${test.glacier.api-version}</test.glacier.api-version>
|
||||
<test.glacier.build-version>${test.glacier.build-version}</test.glacier.build-version>
|
||||
<test.glacier.identity>${test.glacier.identity}</test.glacier.identity>
|
||||
<test.glacier.credential>${test.glacier.credential}</test.glacier.credential>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier;
|
||||
|
||||
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
|
||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jclouds.blobstore.BlobStoreContext;
|
||||
import org.jclouds.glacier.config.GlacierRestClientModule;
|
||||
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||
import org.jclouds.rest.internal.BaseRestApiMetadata;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.Module;
|
||||
|
||||
/**
|
||||
* Implementation of ApiMetadata for Amazon Glacier API
|
||||
*
|
||||
* @author Roman Coedo
|
||||
*/
|
||||
public class GlacierApiMetadata extends BaseRestApiMetadata {
|
||||
|
||||
@Deprecated
|
||||
public static final TypeToken<org.jclouds.rest.RestContext<GlacierClient, GlacierAsyncClient>> CONTEXT_TOKEN = new TypeToken<org.jclouds.rest.RestContext<GlacierClient, GlacierAsyncClient>>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
|
||||
private static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder toBuilder() {
|
||||
return builder().fromApiMetadata(this);
|
||||
}
|
||||
|
||||
public GlacierApiMetadata() {
|
||||
this(builder());
|
||||
}
|
||||
|
||||
protected GlacierApiMetadata(Builder builder) {
|
||||
super(new Builder());
|
||||
}
|
||||
|
||||
public static Properties defaultProperties() {
|
||||
Properties properties = BaseRestApiMetadata.defaultProperties();
|
||||
properties.setProperty(PROPERTY_HEADER_TAG, GlacierHeaders.DEFAULT_AMAZON_HEADERTAG);
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static class Builder extends BaseRestApiMetadata.Builder<Builder> {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected Builder() {
|
||||
super(GlacierClient.class, GlacierAsyncClient.class);
|
||||
id("glacier")
|
||||
.name("Amazon Glacier API")
|
||||
.identityName("Access Key ID")
|
||||
.credentialName("Secret Access Key")
|
||||
.defaultEndpoint("https://glacier.us-east-1.amazonaws.com")
|
||||
.documentation(URI.create("http://docs.aws.amazon.com/amazonglacier/latest/dev/amazon-glacier-api.html"))
|
||||
.version("2012-06-01")
|
||||
.defaultProperties(GlacierApiMetadata.defaultProperties())
|
||||
.context(CONTEXT_TOKEN)
|
||||
.view(typeToken(BlobStoreContext.class))
|
||||
.defaultModules(ImmutableSet.<Class<? extends Module>> of(GlacierRestClientModule.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlacierApiMetadata build() {
|
||||
return new GlacierApiMetadata(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Builder self() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier;
|
||||
|
||||
import static org.jclouds.blobstore.attr.BlobScopes.CONTAINER;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
|
||||
import org.jclouds.blobstore.attr.BlobScope;
|
||||
import org.jclouds.glacier.filters.RequestAuthorizeSignature;
|
||||
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||
import org.jclouds.rest.annotations.Headers;
|
||||
import org.jclouds.rest.annotations.RequestFilters;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
@Headers(keys = GlacierHeaders.VERSION, values = "2012-06-01")
|
||||
@RequestFilters(RequestAuthorizeSignature.class)
|
||||
@BlobScope(CONTAINER)
|
||||
public interface GlacierAsyncClient extends Closeable {
|
||||
|
||||
@Named("CreateVault")
|
||||
@PUT
|
||||
@Path("/-/vaults/{vault}")
|
||||
ListenableFuture<URI> createVault(@PathParam("vault") String vaultName);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.net.URI;
|
||||
|
||||
public interface GlacierClient extends Closeable {
|
||||
|
||||
URI createVault(String vaultName);
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier.config;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.date.DateService;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.glacier.GlacierAsyncClient;
|
||||
import org.jclouds.glacier.GlacierClient;
|
||||
import org.jclouds.glacier.filters.RequestAuthorizeSignature;
|
||||
import org.jclouds.rest.ConfiguresRestClient;
|
||||
import org.jclouds.rest.config.RestClientModule;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Scopes;
|
||||
|
||||
@ConfiguresRestClient
|
||||
public class GlacierRestClientModule extends RestClientModule<GlacierClient, GlacierAsyncClient> {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
bind(RequestAuthorizeSignature.class).in(Scopes.SINGLETON);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@TimeStamp
|
||||
protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
|
||||
return cache.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@TimeStamp
|
||||
Supplier<String> provideTimeStampCache(@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
|
||||
final DateService dateService) {
|
||||
return Suppliers.memoizeWithExpiration(new Supplier<String>() {
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return dateService.iso8601SecondsDateFormat().replaceAll("[-:]", "");
|
||||
}
|
||||
}, seconds, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier.filters;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||
import org.jclouds.glacier.util.AWSRequestSignerV4;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpRequestFilter;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.logging.Logger;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
@Singleton
|
||||
public class RequestAuthorizeSignature implements HttpRequestFilter {
|
||||
|
||||
private final AWSRequestSignerV4 signer;
|
||||
|
||||
@Resource
|
||||
@Named(Constants.LOGGER_SIGNATURE)
|
||||
Logger signatureLog = Logger.NULL;
|
||||
|
||||
private final Provider<String> timeStampProvider;
|
||||
private final HttpUtils utils;
|
||||
|
||||
@Inject
|
||||
public RequestAuthorizeSignature(@TimeStamp Provider<String> timeStampProvider,
|
||||
@org.jclouds.location.Provider Supplier<Credentials> creds, Crypto crypto, HttpUtils utils) {
|
||||
checkNotNull(creds);
|
||||
this.signer = new AWSRequestSignerV4(creds.get().identity, creds.get().credential, checkNotNull(crypto));
|
||||
this.timeStampProvider = checkNotNull(timeStampProvider);
|
||||
this.utils = checkNotNull(utils);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest filter(HttpRequest request) throws HttpException {
|
||||
request = request.toBuilder().removeHeader(HttpHeaders.DATE)
|
||||
.replaceHeader(GlacierHeaders.ALTERNATE_DATE, timeStampProvider.get())
|
||||
.replaceHeader(HttpHeaders.HOST, request.getEndpoint().getHost()).build();
|
||||
utils.logRequest(signatureLog, request, ">>");
|
||||
request = this.signer.sign(request);
|
||||
utils.logRequest(signatureLog, request, "<<");
|
||||
return request;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier.reference;
|
||||
|
||||
public final class GlacierHeaders {
|
||||
|
||||
public static final String DEFAULT_AMAZON_HEADERTAG = "amz";
|
||||
public static final String HEADER_PREFIX = "x-" + DEFAULT_AMAZON_HEADERTAG + "-";
|
||||
public static final String VERSION = HEADER_PREFIX + "glacier-version";
|
||||
public static final String ALTERNATE_DATE = HEADER_PREFIX + "date";
|
||||
|
||||
private GlacierHeaders() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier.util;
|
||||
|
||||
import static com.google.common.base.Charsets.UTF_8;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.SortedSetMultimap;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
// TODO: Query parameters, not necessary for Glacier
|
||||
// TODO: Endpoint on buildCredentialScope is being read from the static string. Uncool.
|
||||
/**
|
||||
* Signs requests using the AWSv4 signing algorithm
|
||||
*
|
||||
* @see <a href="http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html" />
|
||||
* @author Roman Coedo
|
||||
*/
|
||||
public class AWSRequestSignerV4 {
|
||||
|
||||
public static final String AUTH_TAG = "AWS4";
|
||||
public static final String HEADER_TAG = "x-amz-";
|
||||
public static final String ALGORITHM = AUTH_TAG + "-HMAC-SHA256";
|
||||
public static final String TERMINATION_STRING = "aws4_request";
|
||||
public static final String REGION = "us-east-1";
|
||||
public static final String SERVICE = "glacier";
|
||||
|
||||
private final Crypto crypto;
|
||||
private final String identity;
|
||||
private final String credential;
|
||||
|
||||
public AWSRequestSignerV4(String identity, String credential, Crypto crypto) {
|
||||
this.crypto = checkNotNull(crypto);
|
||||
this.identity = checkNotNull(identity);
|
||||
this.credential = checkNotNull(credential);
|
||||
}
|
||||
|
||||
private static String buildHashedCanonicalRequest(String method, String endpoint, String hashedPayload,
|
||||
String canonicalizedHeadersString, String signedHeaders) {
|
||||
return sha256((method + "\n" + endpoint + "\n" + "" + "\n" + canonicalizedHeadersString + "\n" + signedHeaders
|
||||
+ "\n" + hashedPayload).getBytes());
|
||||
}
|
||||
|
||||
private static String createStringToSign(String date, String credentialScope, String hashedCanonicalRequest) {
|
||||
return ALGORITHM + "\n" + date + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
|
||||
}
|
||||
|
||||
private static String formatDateWithoutTimestamp(String date) {
|
||||
return date.substring(0, 8);
|
||||
}
|
||||
|
||||
private static String buildCredentialScope(String dateWithoutTimeStamp) {
|
||||
return dateWithoutTimeStamp + "/" + REGION + "/" + SERVICE + "/" + TERMINATION_STRING;
|
||||
}
|
||||
|
||||
private static Multimap<String, String> buildCanonicalizedHeadersMap(HttpRequest request) {
|
||||
Multimap<String, String> headers = request.getHeaders();
|
||||
SortedSetMultimap<String, String> canonicalizedHeaders = TreeMultimap.create();
|
||||
for (Entry<String, String> header : headers.entries()) {
|
||||
if (header.getKey() == null)
|
||||
continue;
|
||||
String key = header.getKey().toString().toLowerCase(Locale.getDefault());
|
||||
// Ignore any headers that are not particularly interesting.
|
||||
if (key.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE) || key.equalsIgnoreCase(HttpHeaders.CONTENT_MD5)
|
||||
|| key.equalsIgnoreCase(HttpHeaders.HOST) || key.startsWith(HEADER_TAG)) {
|
||||
canonicalizedHeaders.put(key, header.getValue());
|
||||
}
|
||||
}
|
||||
return canonicalizedHeaders;
|
||||
}
|
||||
|
||||
private static String buildCanonicalizedHeadersString(Multimap<String, String> canonicalizedHeadersMap) {
|
||||
StringBuilder canonicalizedHeadersBuffer = new StringBuilder();
|
||||
for (Entry<String, String> header : canonicalizedHeadersMap.entries()) {
|
||||
String key = header.getKey();
|
||||
canonicalizedHeadersBuffer.append(key.toLowerCase()).append(':').append(header.getValue()).append('\n');
|
||||
}
|
||||
return canonicalizedHeadersBuffer.toString();
|
||||
}
|
||||
|
||||
private static String buildSignedHeaders(Multimap<String, String> canonicalizedHeadersMap) {
|
||||
return Joiner.on(';').join(Iterables.transform(canonicalizedHeadersMap.keySet(), new Function<String, String>() {
|
||||
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
return input.toLowerCase();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static String sha256(byte[] unhashedBytes) {
|
||||
return Hashing.sha256().hashBytes(unhashedBytes).toString();
|
||||
}
|
||||
|
||||
private static String buildHashedPayload(HttpRequest request) {
|
||||
try {
|
||||
byte[] unhashedBytes = request.getPayload() == null ? "".getBytes() : ByteStreams.toByteArray(request
|
||||
.getPayload().getInput());
|
||||
return sha256(unhashedBytes);
|
||||
} catch (IOException e) {
|
||||
throw new HttpException("Error signing request", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildAuthHeader(String accessKey, String credentialScope, String signedHeaders,
|
||||
String signature) {
|
||||
return ALGORITHM + " " + "Credential=" + accessKey + "/" + credentialScope + "," + "SignedHeaders="
|
||||
+ signedHeaders + "," + "Signature=" + signature;
|
||||
}
|
||||
|
||||
private byte[] hmacSha256(byte[] key, String s) {
|
||||
try {
|
||||
Mac hmacSHA256 = crypto.hmacSHA256(key);
|
||||
return hmacSHA256.doFinal(s.getBytes());
|
||||
} catch (Exception e) {
|
||||
throw new HttpException("Error signing request", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildSignature(String dateWithoutTimestamp, String stringToSign) {
|
||||
byte[] kSecret = (AUTH_TAG + credential).getBytes(UTF_8);
|
||||
byte[] kDate = hmacSha256(kSecret, dateWithoutTimestamp);
|
||||
byte[] kRegion = hmacSha256(kDate, REGION);
|
||||
byte[] kService = hmacSha256(kRegion, SERVICE);
|
||||
byte[] kSigning = hmacSha256(kService, TERMINATION_STRING);
|
||||
return BaseEncoding.base16().encode(hmacSha256(kSigning, stringToSign)).toLowerCase();
|
||||
}
|
||||
|
||||
public HttpRequest sign(HttpRequest request) {
|
||||
// Grab the needed data to build the signature
|
||||
Multimap<String, String> canonicalizedHeadersMap = buildCanonicalizedHeadersMap(request);
|
||||
String canonicalizedHeadersString = buildCanonicalizedHeadersString(canonicalizedHeadersMap);
|
||||
String signedHeaders = buildSignedHeaders(canonicalizedHeadersMap);
|
||||
String date = request.getFirstHeaderOrNull(GlacierHeaders.ALTERNATE_DATE);
|
||||
String dateWithoutTimestamp = formatDateWithoutTimestamp(date);
|
||||
String method = request.getMethod();
|
||||
String endpoint = request.getEndpoint().getRawPath();
|
||||
String credentialScope = buildCredentialScope(dateWithoutTimestamp);
|
||||
String hashedPayload = buildHashedPayload(request);
|
||||
|
||||
// Task 1: Create a Canonical Request For Signature Version 4.
|
||||
String hashedCanonicalRequest = buildHashedCanonicalRequest(method, endpoint, hashedPayload,
|
||||
canonicalizedHeadersString, signedHeaders);
|
||||
|
||||
// Task 2: Create a String to Sign for Signature Version 4.
|
||||
String stringToSign = createStringToSign(date, credentialScope, hashedCanonicalRequest);
|
||||
|
||||
// Task 3: Calculate the AWS Signature Version 4.
|
||||
String signature = buildSignature(dateWithoutTimestamp, stringToSign);
|
||||
|
||||
// Sign the request
|
||||
String authHeader = buildAuthHeader(identity, credentialScope, signedHeaders, signature);
|
||||
request = request.toBuilder().replaceHeader(HttpHeaders.AUTHORIZATION, authHeader).build();
|
||||
return request;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.jclouds.glacier.GlacierApiMetadata
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier;
|
||||
|
||||
import org.jclouds.blobstore.internal.BaseBlobStoreApiMetadataTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Roman Coedo
|
||||
*/
|
||||
@Test(groups = "unit", testName = "GlacierApiMetadataTest")
|
||||
public class GlacierApiMetadataTest extends BaseBlobStoreApiMetadataTest {
|
||||
|
||||
public GlacierApiMetadataTest() {
|
||||
super(new GlacierApiMetadata());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier;
|
||||
|
||||
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
|
||||
import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.ContextBuilder;
|
||||
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Module;
|
||||
import com.google.mockwebserver.MockResponse;
|
||||
import com.google.mockwebserver.MockWebServer;
|
||||
import com.google.mockwebserver.RecordedRequest;
|
||||
|
||||
public class GlacierClientMockTest {
|
||||
|
||||
private static final String VAULT_NAME = "ConcreteVaultName";
|
||||
|
||||
private static final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor(),
|
||||
sameThreadExecutor()));
|
||||
|
||||
static GlacierClient getGlacierClient(URL server) {
|
||||
Properties overrides = new Properties();
|
||||
// prevent expect-100 bug http://code.google.com/p/mockwebserver/issues/detail?id=6
|
||||
overrides.setProperty(PROPERTY_SO_TIMEOUT, "0");
|
||||
overrides.setProperty(PROPERTY_MAX_RETRIES, "1");
|
||||
return ContextBuilder.newBuilder("glacier").credentials("accessKey", "secretKey").endpoint(server.toString())
|
||||
.modules(modules).overrides(overrides).buildApi(GlacierClient.class);
|
||||
}
|
||||
|
||||
public void testCreateVault() throws IOException, InterruptedException {
|
||||
// Prepare the response
|
||||
MockResponse mr = new MockResponse();
|
||||
mr.setResponseCode(201);
|
||||
mr.addHeader("x-amzn-RequestId", "AAABZpJrTyioDC_HsOmHae8EZp_uBSJr6cnGOLKp_XJCl-Q");
|
||||
mr.addHeader("Date", "Sun, 25 Mar 2012 12:02:00 GMT");
|
||||
mr.addHeader("Location", "/111122223333/vaults/" + VAULT_NAME);
|
||||
MockWebServer server = new MockWebServer();
|
||||
server.enqueue(mr);
|
||||
server.play();
|
||||
|
||||
// Send the request and check the response
|
||||
try {
|
||||
GlacierClient client = getGlacierClient(server.getUrl("/"));
|
||||
URI responseUri = client.createVault(VAULT_NAME);
|
||||
assertEquals(responseUri.toString(), server.getUrl("/") + "111122223333/vaults/" + VAULT_NAME);
|
||||
RecordedRequest request = server.takeRequest();
|
||||
assertEquals(request.getRequestLine(), "PUT /-/vaults/" + VAULT_NAME + " HTTP/1.1");
|
||||
} finally {
|
||||
server.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.glacier.util;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import org.jclouds.encryption.internal.JCECrypto;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
|
||||
@Test(groups = "unit", testName = "AWSRequestSignerV4Test")
|
||||
public class AWSRequestSignerV4Test {
|
||||
|
||||
@Test
|
||||
public void testSignatureCalculation() throws NoSuchAlgorithmException, CertificateException {
|
||||
String auth = "AWS4-HMAC-SHA256 " + "Credential=AKIAIOSFODNN7EXAMPLE/20120525/us-east-1/glacier/aws4_request,"
|
||||
+ "SignedHeaders=host;x-amz-date;x-amz-glacier-version,"
|
||||
+ "Signature=3ce5b2f2fffac9262b4da9256f8d086b4aaf42eba5f111c21681a65a127b7c2a";
|
||||
String identity = "AKIAIOSFODNN7EXAMPLE";
|
||||
String credential = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
|
||||
AWSRequestSignerV4 signer = new AWSRequestSignerV4(identity, credential, new JCECrypto());
|
||||
HttpRequest request = signer.sign(createRequest());
|
||||
assertEquals(request.getFirstHeaderOrNull("Authorization"), auth);
|
||||
}
|
||||
|
||||
private HttpRequest createRequest() {
|
||||
Multimap<String, String> headers = TreeMultimap.create();
|
||||
headers.put("Host", "glacier.us-east-1.amazonaws.com");
|
||||
headers.put("x-amz-date", "20120525T002453Z");
|
||||
headers.put("x-amz-glacier-version", "2012-06-01");
|
||||
HttpRequest request = HttpRequest.builder().method("PUT")
|
||||
.endpoint("https://glacier.us-east-1.amazonaws.com/-/vaults/examplevault").headers(headers).build();
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.jclouds.glacier.GlacierApiMetadata
|
Loading…
Reference in New Issue