EMMA Coverage Report (generated Mon Oct 17 05:41:20 EDT 2011)
[all classes][org.jclouds.aws.filters]

COVERAGE SUMMARY FOR SOURCE FILE [FormSigner.java]

nameclass, %method, %block, %line, %
FormSigner.java50%  (2/4)28%  (5/18)20%  (73/369)22%  (15/67)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FormSigner$10%   (0/1)0%   (0/2)0%   (0/16)0%   (0/4)
FormSigner$1 (FormSigner): void 0%   (0/1)0%   (0/6)0%   (0/1)
compare (String, String): int 0%   (0/1)0%   (0/10)0%   (0/3)
     
class FormSigner$20%   (0/1)0%   (0/2)0%   (0/42)0%   (0/6)
FormSigner$2 (FormSigner): void 0%   (0/1)0%   (0/6)0%   (0/1)
compare (Map$Entry, Map$Entry): int 0%   (0/1)0%   (0/36)0%   (0/5)
     
class FormSigner100% (1/1)25%  (3/12)18%  (54/292)21%  (12/56)
addSignature (Multimap, String): void 0%   (0/1)0%   (0/7)0%   (0/2)
addSigningParams (Multimap): void 0%   (0/1)0%   (0/32)0%   (0/6)
createStringToSign (HttpRequest): String 0%   (0/1)0%   (0/9)0%   (0/1)
createStringToSign (HttpRequest, Multimap): String 0%   (0/1)0%   (0/54)0%   (0/9)
filter (HttpRequest): HttpRequest 0%   (0/1)0%   (0/45)0%   (0/10)
setPayload (HttpRequest, Multimap): HttpRequest 0%   (0/1)0%   (0/17)0%   (0/3)
sign (String): String 0%   (0/1)0%   (0/31)0%   (0/7)
sortForSigning (String): String [] 0%   (0/1)0%   (0/12)0%   (0/3)
validateParams (Multimap): void 0%   (0/1)0%   (0/31)0%   (0/3)
<static initializer> 100% (1/1)100% (24/24)100% (2/2)
FormSigner (SignatureWire, String, String, Provider, Crypto, HttpUtils): void 100% (1/1)100% (24/24)100% (9/9)
buildCanonicalizedString (Multimap): String 100% (1/1)100% (6/6)100% (1/1)
     
class FormSigner$3100% (1/1)100% (2/2)100% (19/19)100% (4/4)
FormSigner$3 (): void 100% (1/1)100% (3/3)100% (1/1)
compare (Map$Entry, Map$Entry): int 100% (1/1)100% (16/16)100% (3/3)

1/**
2 * Licensed to jclouds, Inc. (jclouds) under one or more
3 * contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  jclouds licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.jclouds.aws.filters;
20 
21import static com.google.common.base.Preconditions.checkNotNull;
22import static com.google.common.base.Preconditions.checkState;
23import static org.jclouds.aws.reference.FormParameters.ACTION;
24import static org.jclouds.aws.reference.FormParameters.AWS_ACCESS_KEY_ID;
25import static org.jclouds.aws.reference.FormParameters.SIGNATURE;
26import static org.jclouds.aws.reference.FormParameters.SIGNATURE_METHOD;
27import static org.jclouds.aws.reference.FormParameters.SIGNATURE_VERSION;
28import static org.jclouds.aws.reference.FormParameters.TIMESTAMP;
29import static org.jclouds.aws.reference.FormParameters.VERSION;
30 
31import java.util.Arrays;
32import java.util.Comparator;
33import java.util.Map;
34import java.util.Map.Entry;
35 
36import javax.annotation.Resource;
37import javax.inject.Inject;
38import javax.inject.Named;
39import javax.inject.Provider;
40import javax.inject.Singleton;
41import javax.ws.rs.core.HttpHeaders;
42 
43import org.jclouds.Constants;
44import org.jclouds.crypto.Crypto;
45import org.jclouds.crypto.CryptoStreams;
46import org.jclouds.date.TimeStamp;
47import org.jclouds.http.HttpException;
48import org.jclouds.http.HttpRequest;
49import org.jclouds.http.HttpRequestFilter;
50import org.jclouds.http.HttpUtils;
51import org.jclouds.http.internal.SignatureWire;
52import org.jclouds.http.utils.ModifyRequest;
53import org.jclouds.io.InputSuppliers;
54import org.jclouds.logging.Logger;
55import org.jclouds.rest.RequestSigner;
56import org.jclouds.util.Strings2;
57 
58import com.google.common.annotations.VisibleForTesting;
59import com.google.common.collect.ImmutableList;
60import com.google.common.collect.Multimap;
61 
62/**
63 * 
64 * @see <a href=
65 *      "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/Form-Common-Parameters.html"
66 *      />
67 * @author Adrian Cole
68 * 
69 */
70@Singleton
71public class FormSigner implements HttpRequestFilter, RequestSigner {
72 
73   public static String[] mandatoryParametersForSignature = new String[] { ACTION, SIGNATURE_METHOD, SIGNATURE_VERSION,
74            VERSION };
75   private final SignatureWire signatureWire;
76   private final String accessKey;
77   private final String secretKey;
78   private final Provider<String> dateService;
79   private final Crypto crypto;
80   private final HttpUtils utils;
81 
82   @Resource
83   @Named(Constants.LOGGER_SIGNATURE)
84   private Logger signatureLog = Logger.NULL;
85 
86   @Inject
87   public FormSigner(SignatureWire signatureWire, @Named(Constants.PROPERTY_IDENTITY) String accessKey,
88            @Named(Constants.PROPERTY_CREDENTIAL) String secretKey, @TimeStamp Provider<String> dateService,
89            Crypto crypto, HttpUtils utils) {
90      this.signatureWire = signatureWire;
91      this.accessKey = accessKey;
92      this.secretKey = secretKey;
93      this.dateService = dateService;
94      this.crypto = crypto;
95      this.utils = utils;
96   }
97 
98   public HttpRequest filter(HttpRequest request) throws HttpException {
99      checkNotNull(request.getFirstHeaderOrNull(HttpHeaders.HOST), "request is not ready to sign; host not present");
100      Multimap<String, String> decodedParams = ModifyRequest.parseQueryToMap(request.getPayload().getRawContent()
101               .toString());
102      addSigningParams(decodedParams);
103      validateParams(decodedParams);
104      String stringToSign = createStringToSign(request, decodedParams);
105      String signature = sign(stringToSign);
106      addSignature(decodedParams, signature);
107      request = setPayload(request, decodedParams);
108      utils.logRequest(signatureLog, request, "<<");
109      return request;
110   }
111 
112   String[] sortForSigning(String queryLine) {
113      String[] parts = queryLine.split("&");
114      // 1. Sort the UTF-8 query string components by parameter name with natural byte ordering.
115      Arrays.sort(parts, new Comparator<String>() {
116 
117         public int compare(String o1, String o2) {
118            if (o1.startsWith("AWSAccessKeyId"))
119               return -1;
120            return o1.compareTo(o2);
121         }
122 
123      });
124      return parts;
125   }
126 
127   HttpRequest setPayload(HttpRequest request, Multimap<String, String> decodedParams) {
128      request.setPayload(ModifyRequest.makeQueryLine(decodedParams, new Comparator<Map.Entry<String, String>>() {
129         public int compare(Entry<String, String> o1, Entry<String, String> o2) {
130            if (o1.getKey().startsWith("Action") || o2.getKey().startsWith("AWSAccessKeyId"))
131               return -1;
132            if (o1.getKey().startsWith("AWSAccessKeyId") || o2.getKey().startsWith("Action"))
133               return 1;
134            return o1.getKey().compareTo(o2.getKey());
135         }
136      }));
137      request.getPayload().getContentMetadata().setContentType("application/x-www-form-urlencoded");
138      return request;
139   }
140 
141   @VisibleForTesting
142   void validateParams(Multimap<String, String> params) {
143      for (String parameter : mandatoryParametersForSignature) {
144         checkState(params.containsKey(parameter), "parameter " + parameter + " is required for signature");
145      }
146   }
147 
148   @VisibleForTesting
149   void addSignature(Multimap<String, String> params, String signature) {
150      params.replaceValues(SIGNATURE, ImmutableList.of(signature));
151   }
152 
153   @VisibleForTesting
154   public String sign(String stringToSign) {
155      String signature;
156      try {
157         signature = CryptoStreams.base64(CryptoStreams.mac(InputSuppliers.of(stringToSign), crypto
158                  .hmacSHA256(secretKey.getBytes())));
159         if (signatureWire.enabled())
160            signatureWire.input(Strings2.toInputStream(signature));
161      } catch (Exception e) {
162         throw new HttpException("error signing request", e);
163      }
164      return signature;
165   }
166 
167   @VisibleForTesting
168   public String createStringToSign(HttpRequest request, Multimap<String, String> decodedParams) {
169      utils.logRequest(signatureLog, request, ">>");
170      StringBuilder stringToSign = new StringBuilder();
171      // StringToSign = HTTPVerb + "\n" +
172      stringToSign.append(request.getMethod()).append("\n");
173      // ValueOfHostHeaderInLowercase + "\n" +
174      stringToSign.append(request.getFirstHeaderOrNull(HttpHeaders.HOST).toLowerCase()).append("\n");
175      // HTTPRequestURI + "\n" +
176      stringToSign.append(request.getEndpoint().getPath()).append("\n");
177      // CanonicalizedFormString <from the preceding step>
178      stringToSign.append(buildCanonicalizedString(decodedParams));
179      if (signatureWire.enabled())
180         signatureWire.output(stringToSign.toString());
181      return stringToSign.toString();
182   }
183 
184   @VisibleForTesting
185   String buildCanonicalizedString(Multimap<String, String> decodedParams) {
186      return ModifyRequest.makeQueryLine(decodedParams, sortAWSFirst);
187   }
188 
189   public static final Comparator<Map.Entry<String, String>> sortAWSFirst = new Comparator<Map.Entry<String, String>>() {
190      public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
191         if (o1.getKey().startsWith("AWSAccessKeyId"))
192            return -1;
193         return o1.getKey().compareTo(o2.getKey());
194      }
195   };
196 
197   @VisibleForTesting
198   void addSigningParams(Multimap<String, String> params) {
199      params.replaceValues(SIGNATURE_METHOD, ImmutableList.of("HmacSHA256"));
200      params.replaceValues(SIGNATURE_VERSION, ImmutableList.of("2"));
201      params.replaceValues(TIMESTAMP, ImmutableList.of(dateService.get()));
202      params.replaceValues(AWS_ACCESS_KEY_ID, ImmutableList.of(accessKey));
203      params.removeAll(SIGNATURE);
204   }
205 
206   public String createStringToSign(HttpRequest input) {
207      return createStringToSign(input, ModifyRequest.parseQueryToMap(input.getPayload().getRawContent().toString()));
208   }
209 
210}

[all classes][org.jclouds.aws.filters]
EMMA 2.0.5312 (C) Vladimir Roubtsov