mirror of https://github.com/apache/jclouds.git
light work on ec2
git-svn-id: http://jclouds.googlecode.com/svn/trunk@1916 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
9f78e8d2fc
commit
6b0f7e289a
|
@ -0,0 +1,176 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
package org.jclouds.aws.ec2.commands.options;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.ACTION;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.AWS_ACCESS_KEY_ID;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.EXPIRES;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE_METHOD;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.SIGNATURE_VERSION;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.TIMESTAMP;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.VERSION;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
|
||||||
|
import org.jclouds.aws.ec2.reference.CommonEC2Parameters;
|
||||||
|
import org.jclouds.aws.reference.AWSConstants;
|
||||||
|
import org.jclouds.http.HttpException;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
|
import org.jclouds.http.HttpUtils;
|
||||||
|
import org.jclouds.util.DateService;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the base options needed for all EC2 QUERY API operations.<h2>
|
||||||
|
* Extend this class in the following way to avoid massive boilerplate code: Usage:
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public static class MyRequestOptions extends BaseEC2RequestOptions<MyRequestOptions> {
|
||||||
|
* static {
|
||||||
|
* realClass = MyRequestOptions.class;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public String getAction() {
|
||||||
|
* return "MyRequest";
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public String getId() {
|
||||||
|
* return queryParameters.get("id");
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public MyRequestOptions withId(String id) {
|
||||||
|
* encodeAndReplaceParameter("id", id);
|
||||||
|
* return this;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public static class Builder extends BaseEC2RequestOptions.Builder {
|
||||||
|
* public static MyRequestOptions withId(String id) {
|
||||||
|
* MyRequestOptions options = new MyRequestOptions();
|
||||||
|
* return options.withId(id);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see <a href=
|
||||||
|
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/Query-Common-Parameters.html"
|
||||||
|
* />
|
||||||
|
* @see CommonEC2Parameters
|
||||||
|
* @author Adrian Cole
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class EC2QuerySigner implements HttpRequestFilter {
|
||||||
|
|
||||||
|
public static String[] mandatoryParametersForSignature = new String[] { ACTION,
|
||||||
|
SIGNATURE_METHOD, SIGNATURE_VERSION, VERSION };
|
||||||
|
private final String accessKey;
|
||||||
|
private final String secretKey;
|
||||||
|
private final DateService dateService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public EC2QuerySigner(@Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String accessKey,
|
||||||
|
@Named(AWSConstants.PROPERTY_AWS_SECRETACCESSKEY) String secretKey,
|
||||||
|
DateService dateService) {
|
||||||
|
this.accessKey = accessKey;
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
this.dateService = dateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequest filter(HttpRequest request) throws HttpException {
|
||||||
|
validateRequest(request);
|
||||||
|
request = addSigningParamsToRequest(request);
|
||||||
|
String stringToSign = buildStringToSign(request);
|
||||||
|
String signature = sign(stringToSign);
|
||||||
|
return addSignatureToRequest(request, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateRequest(HttpRequest request) {
|
||||||
|
for (String parameter : mandatoryParametersForSignature) {
|
||||||
|
checkState(request.getEndpoint().getQuery().contains(parameter), "parameter " + parameter
|
||||||
|
+ " is required for signature");
|
||||||
|
}
|
||||||
|
checkState(request.getHeaders().get(HttpHeaders.HOST) != null,
|
||||||
|
"request is not ready to sign; host not present");
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpRequest addSignatureToRequest(HttpRequest request, String signature) {
|
||||||
|
UriBuilder builder = UriBuilder.fromUri(request.getEndpoint());
|
||||||
|
builder.queryParam(SIGNATURE, signature);
|
||||||
|
return new HttpRequest(request.getMethod(), builder.build(), request.getHeaders(), request
|
||||||
|
.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sign(String stringToSign) {
|
||||||
|
String signature;
|
||||||
|
try {
|
||||||
|
signature = HttpUtils.hmacSha256Base64(stringToSign, secretKey.getBytes());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new HttpException("error signing request", e);
|
||||||
|
}
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildStringToSign(HttpRequest request) {
|
||||||
|
// 1. Sort the UTF-8 query string components by parameter name with natural byte ordering.
|
||||||
|
// -- as queryParameters are a SortedSet, they are already sorted.
|
||||||
|
// 2. URL encode the parameter name and values according to the following rules...
|
||||||
|
// -- all queryParameters are URL encoded on the way in
|
||||||
|
// 3. Separate the encoded parameter names from their encoded values with the equals sign,
|
||||||
|
// even if the parameter value is empty.
|
||||||
|
// -- we do not allow null values.
|
||||||
|
// 4. Separate the name-value pairs with an ampersand.
|
||||||
|
// -- buildQueryString() does this.
|
||||||
|
StringBuilder toSign = new StringBuilder();
|
||||||
|
toSign.append(request.getMethod()).append("\n").append(
|
||||||
|
request.getEndpoint().getHost().toLowerCase()).append("\n").append("/").append("\n");
|
||||||
|
toSign.append(request.getEndpoint().getQuery());
|
||||||
|
String stringToSign = toSign.toString();
|
||||||
|
return stringToSign;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpRequest addSigningParamsToRequest(HttpRequest request) {
|
||||||
|
UriBuilder builder = UriBuilder.fromUri(request.getEndpoint());
|
||||||
|
builder.queryParam(SIGNATURE_METHOD, "HmacSHA256");
|
||||||
|
builder.queryParam(SIGNATURE_VERSION, "2");
|
||||||
|
builder.queryParam(VERSION, "2009-04-04");
|
||||||
|
|
||||||
|
// timestamp is incompatible with expires
|
||||||
|
if (request.getEndpoint().getQuery().contains(EXPIRES)) {
|
||||||
|
// TODO tune this if necessary
|
||||||
|
builder.queryParam(TIMESTAMP, dateService.iso8601DateFormat());
|
||||||
|
}
|
||||||
|
builder.queryParam(AWS_ACCESS_KEY_ID, accessKey);
|
||||||
|
return new HttpRequest(request.getMethod(), builder.build(), request.getHeaders(), request
|
||||||
|
.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
package org.jclouds.aws.ec2.commands.options;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.ACTION;
|
||||||
|
import static org.jclouds.aws.ec2.reference.CommonEC2Parameters.EXPIRES;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import javax.ws.rs.HttpMethod;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
|
||||||
|
import org.jclouds.aws.reference.AWSConstants;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.util.DateService;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import org.jclouds.util.Jsr330;
|
||||||
|
|
||||||
|
@Test(groups = "unit", testName = "s3.EC2QuerySignerTest")
|
||||||
|
public class EC2QuerySignerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExpires() {
|
||||||
|
UriBuilder builder = UriBuilder.fromUri(URI.create("https://ec2.amazonaws.com/"));
|
||||||
|
builder.queryParam(ACTION,"DescribeImages");
|
||||||
|
builder.queryParam(EXPIRES,"2008-02-10T12%3A00%3A00Z");
|
||||||
|
builder.queryParam("ImageId.1","ami-2bb65342");
|
||||||
|
HttpRequest request = new HttpRequest(HttpMethod.GET,builder.build());
|
||||||
|
createFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAclQueryString() {
|
||||||
|
URI host = URI.create("http://s3.amazonaws.com:80/?acl");
|
||||||
|
HttpRequest request = new HttpRequest(HttpMethod.GET, host);
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
createFilter().appendUriPath(request, builder);
|
||||||
|
assertEquals(builder.toString(), "/?acl");
|
||||||
|
}
|
||||||
|
|
||||||
|
// "?acl", "?location", "?logging", or "?torrent"
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAppendBucketNameHostHeaderService() {
|
||||||
|
URI host = URI.create("http://s3.amazonaws.com:80");
|
||||||
|
HttpRequest request = new HttpRequest(HttpMethod.GET, host);
|
||||||
|
request.getHeaders().put(HttpHeaders.HOST, "s3.amazonaws.com");
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
createFilter().appendBucketName(request, builder);
|
||||||
|
assertEquals(builder.toString(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAppendBucketNameURIHost() {
|
||||||
|
URI host = URI.create("http://adriancole.s3int5.s3-external-3.amazonaws.com:80");
|
||||||
|
HttpRequest request = new HttpRequest(HttpMethod.GET, host);
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
createFilter().appendBucketName(request, builder);
|
||||||
|
assertEquals(builder.toString(), "/adriancole.s3int5");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private EC2QuerySigner createFilter() {
|
||||||
|
return Guice.createInjector(new AbstractModule() {
|
||||||
|
|
||||||
|
protected void configure() {
|
||||||
|
bindConstant().annotatedWith(Jsr330.named(AWSConstants.PROPERTY_AWS_ACCESSKEYID)).to(
|
||||||
|
"foo");
|
||||||
|
bindConstant().annotatedWith(Jsr330.named(AWSConstants.PROPERTY_AWS_SECRETACCESSKEY)).to(
|
||||||
|
"bar");
|
||||||
|
bind(DateService.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
}).getInstance(EC2QuerySigner.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue