Skip to content

Commit faa7830

Browse files
author
arnets
committed
v3.0.0
1 parent 56483a8 commit faa7830

15 files changed

+193
-67
lines changed

src/main/java/com/emc/object/s3/S3Config.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ protected static int defaultPort(Protocol protocol) {
8181
protected int retryLimit = DEFAULT_RETRY_LIMIT;
8282
protected int retryBufferSize = DEFAULT_RETRY_BUFFER_SIZE;
8383
protected float faultInjectionRate = 0.0f;
84-
protected boolean signMetadataSearch = false; // TODO: make this true for 3.0
84+
protected boolean signMetadataSearch = true;
8585

8686
/**
8787
* Empty constructor for internal use only!

src/main/java/com/emc/object/s3/S3Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public final class S3Constants {
7474
public static final String PARAM_RESPONSE_HEADER_CONTENT_LANGUAGE = "response-content-language";
7575
public static final String PARAM_RESPONSE_HEADER_CONTENT_TYPE = "response-content-type";
7676
public static final String PARAM_RESPONSE_HEADER_EXPIRES = "response-expires";
77+
public static final String PARAM_SEARCH_METADATA = "searchmetadata";
7778
public static final String PARAM_SIGNATURE = "Signature";
7879
public static final String PARAM_SORTED = "sorted";
7980
public static final String PARAM_UPLOAD_ID = "uploadId";

src/main/java/com/emc/object/s3/S3AuthUtil.java renamed to src/main/java/com/emc/object/s3/S3SignerV2.java

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,15 @@
4444
import java.security.NoSuchAlgorithmException;
4545
import java.util.*;
4646

47-
public final class S3AuthUtil {
47+
public final class S3SignerV2 {
48+
private static final Logger log = LoggerFactory.getLogger(S3SignerV2.class);
4849

49-
private static final Logger log = LoggerFactory.getLogger(S3AuthUtil.class);
50+
private S3Config s3Config;
51+
private SortedSet<String> signedParameters;
5052

51-
public static SortedSet<String> SIGNED_PARAMETERS;
52-
53-
static {
54-
SIGNED_PARAMETERS = new TreeSet<String>(Arrays.asList(
53+
public S3SignerV2(S3Config s3Config) {
54+
this.s3Config = s3Config;
55+
signedParameters = new TreeSet<String>(Arrays.asList(
5556
"acl", "torrent", "logging", "location", "policy", "requestPayment", "versioning",
5657
"versions", "versionId", "notification", "uploadId", "uploads", "partNumber", "website",
5758
"delete", "lifecycle", "tagging", "cors", "restore",
@@ -63,16 +64,19 @@ public final class S3AuthUtil {
6364
S3Constants.PARAM_RESPONSE_HEADER_EXPIRES,
6465
S3Constants.PARAM_ENDPOINT,
6566
S3Constants.PARAM_IS_STALE_ALLOWED));
67+
if (s3Config.isSignMetadataSearch()) {
68+
signedParameters.add(S3Constants.PARAM_QUERY);
69+
signedParameters.add(S3Constants.PARAM_SEARCH_METADATA);
70+
}
6671
}
6772

68-
public static void sign(String method, String resource, Map<String, String> parameters, Map<String, List<Object>> headers,
69-
String accessKey, String secretKey, long clockSkew) {
70-
String stringToSign = getStringToSign(method, resource, parameters, headers, clockSkew);
71-
String signature = getSignature(stringToSign, secretKey);
72-
RestUtil.putSingle(headers, "Authorization", "AWS " + accessKey + ":" + signature);
73+
public void sign(String method, String resource, Map<String, String> parameters, Map<String, List<Object>> headers) {
74+
String stringToSign = getStringToSign(method, resource, parameters, headers);
75+
String signature = getSignature(stringToSign);
76+
RestUtil.putSingle(headers, "Authorization", "AWS " + s3Config.getIdentity() + ":" + signature);
7377
}
7478

75-
public static URL generatePresignedUrl(PresignedUrlRequest request, S3Config s3Config) {
79+
public URL generatePresignedUrl(PresignedUrlRequest request) {
7680
String namespace = request.getNamespace() != null ? request.getNamespace() : s3Config.getNamespace();
7781

7882
URI uri = s3Config.resolvePath(request.getPath(), null); // don't care about the query string yet
@@ -100,9 +104,8 @@ public static URL generatePresignedUrl(PresignedUrlRequest request, S3Config s3C
100104
queryParams.put(S3Constants.PARAM_ACCESS_KEY, s3Config.getIdentity());
101105

102106
// sign the request
103-
String stringToSign = S3AuthUtil.getStringToSign(request.getMethod().toString(), resource,
104-
queryParams, request.getHeaders(), s3Config.getServerClockSkew());
105-
String signature = S3AuthUtil.getSignature(stringToSign, s3Config.getSecretKey());
107+
String stringToSign = getStringToSign(request.getMethod().toString(), resource, queryParams, request.getHeaders());
108+
String signature = getSignature(stringToSign);
106109

107110
// add signature to query string
108111
queryParams.put(S3Constants.PARAM_SIGNATURE, signature);
@@ -118,8 +121,8 @@ public static URL generatePresignedUrl(PresignedUrlRequest request, S3Config s3C
118121
}
119122
}
120123

121-
public static String getStringToSign(String method, String resource, Map<String, String> parameters,
122-
Map<String, List<Object>> headers, long clockSkew) {
124+
public String getStringToSign(String method, String resource, Map<String, String> parameters,
125+
Map<String, List<Object>> headers) {
123126
StringBuilder stringToSign = new StringBuilder();
124127

125128
// method line
@@ -140,7 +143,7 @@ public static String getStringToSign(String method, String resource, Map<String,
140143
String date = RestUtil.getFirstAsString(headers, RestUtil.HEADER_DATE);
141144
if (date == null) {
142145
// must have a date in the headers
143-
date = RestUtil.getRequestDate(clockSkew);
146+
date = RestUtil.getRequestDate(s3Config.getServerClockSkew());
144147
RestUtil.putSingle(headers, RestUtil.HEADER_DATE, date);
145148
}
146149
// if x-amz-date is specified, date line should be blank
@@ -162,7 +165,7 @@ public static String getStringToSign(String method, String resource, Map<String,
162165
// resource path (includes signed parameters)
163166
stringToSign.append(resource);
164167
boolean firstParameter = true;
165-
for (String parameter : SIGNED_PARAMETERS) {
168+
for (String parameter : signedParameters) {
166169
if (parameters.containsKey(parameter)) {
167170
stringToSign.append(firstParameter ? "?" : "&").append(parameter);
168171
String value = parameters.get(parameter);
@@ -176,7 +179,7 @@ public static String getStringToSign(String method, String resource, Map<String,
176179
return stringToSignStr;
177180
}
178181

179-
public static SortedMap<String, String> getCanonicalizedHeaders(Map<String, List<Object>> headers, Map<String, String> parameters) {
182+
private SortedMap<String, String> getCanonicalizedHeaders(Map<String, List<Object>> headers, Map<String, String> parameters) {
180183
SortedMap<String, String> canonicalizedHeaders = new TreeMap<String, String>();
181184

182185
// add x-emc- and x-amz- headers
@@ -198,7 +201,7 @@ public static SortedMap<String, String> getCanonicalizedHeaders(Map<String, List
198201
return canonicalizedHeaders;
199202
}
200203

201-
public static String trimAndJoin(List<Object> values, String delimiter) {
204+
private String trimAndJoin(List<Object> values, String delimiter) {
202205
if (values == null || values.isEmpty()) return null;
203206
StringBuilder delimited = new StringBuilder();
204207
Iterator<Object> valuesI = values.iterator();
@@ -209,10 +212,10 @@ public static String trimAndJoin(List<Object> values, String delimiter) {
209212
return delimited.toString();
210213
}
211214

212-
public static String getSignature(String stringToSign, String secretKey) {
215+
public String getSignature(String stringToSign) {
213216
try {
214217
Mac mac = Mac.getInstance("HmacSHA1");
215-
mac.init(new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA1")); // AWS does not B64-decode the secret key!
218+
mac.init(new SecretKeySpec(s3Config.getSecretKey().getBytes("UTF-8"), "HmacSHA1")); // AWS does not B64-decode the secret key!
216219
String signature = new String(Base64.encodeBase64(mac.doFinal(stringToSign.getBytes("UTF-8"))));
217220
log.debug("signature:\n" + signature);
218221
return signature;
@@ -221,10 +224,7 @@ public static String getSignature(String stringToSign, String secretKey) {
221224
} catch (UnsupportedEncodingException e) {
222225
throw new RuntimeException("UTF-8 encoding is not supported on this platform", e);
223226
} catch (InvalidKeyException e) {
224-
throw new RuntimeException("The secret key \"" + secretKey + "\" is not valid", e);
227+
throw new RuntimeException("The secret key \"" + s3Config.getSecretKey() + "\" is not valid", e);
225228
}
226229
}
227-
228-
private S3AuthUtil() {
229-
}
230230
}

src/main/java/com/emc/object/s3/bean/LifecycleRule.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
*/
2727
package com.emc.object.s3.bean;
2828

29+
import com.emc.object.util.Iso8601DateAdapter;
30+
2931
import javax.xml.bind.annotation.*;
32+
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
3033
import java.util.Date;
3134

3235
@XmlType(propOrder = {"id", "prefix", "status", "expiration"})
@@ -150,6 +153,7 @@ protected static class Expiration {
150153
@XmlElement(name = "Days")
151154
public Integer days;
152155
@XmlElement(name = "Date")
156+
@XmlJavaTypeAdapter(value = Iso8601DateAdapter.class)
153157
public Date date;
154158
}
155159

src/main/java/com/emc/object/s3/bean/package-info.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@
2626
*/
2727
@XmlSchema(namespace = "http://s3.amazonaws.com/doc/2006-03-01/", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
2828
@XmlJavaTypeAdapters({
29-
@XmlJavaTypeAdapter(value = com.emc.object.util.Iso8601Adapter.class, type = java.util.Date.class),
29+
@XmlJavaTypeAdapter(value = Iso8601DateTimeAdapter.class, type = java.util.Date.class),
3030
@XmlJavaTypeAdapter(value = RegionAdapter.class, type = com.emc.object.s3.bean.Region.class)
3131
}) package com.emc.object.s3.bean;
3232

3333
import com.emc.object.s3.RegionAdapter;
34+
import com.emc.object.util.Iso8601DateTimeAdapter;
3435

3536
import javax.xml.bind.annotation.XmlSchema;
3637
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

src/main/java/com/emc/object/s3/jersey/AuthorizationFilter.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
*/
2727
package com.emc.object.s3.jersey;
2828

29-
import com.emc.object.s3.S3AuthUtil;
3029
import com.emc.object.s3.S3Config;
3130
import com.emc.object.s3.S3Constants;
31+
import com.emc.object.s3.S3SignerV2;
3232
import com.emc.object.util.RestUtil;
3333
import com.sun.jersey.api.client.ClientHandlerException;
3434
import com.sun.jersey.api.client.ClientRequest;
@@ -39,9 +39,11 @@
3939

4040
public class AuthorizationFilter extends ClientFilter {
4141
private S3Config s3Config;
42+
private S3SignerV2 signer;
4243

4344
public AuthorizationFilter(S3Config s3Config) {
4445
this.s3Config = s3Config;
46+
this.signer = new S3SignerV2(s3Config);
4547
}
4648

4749
@Override
@@ -68,13 +70,10 @@ public ClientResponse handle(ClientRequest request) throws ClientHandlerExceptio
6870
if (namespace != null) resource = "/" + namespace + resource;
6971
}
7072

71-
S3AuthUtil.sign(request.getMethod(),
73+
signer.sign(request.getMethod(),
7274
resource,
7375
parameters,
74-
request.getHeaders(),
75-
s3Config.getIdentity(),
76-
s3Config.getSecretKey(),
77-
s3Config.getServerClockSkew());
76+
request.getHeaders());
7877
}
7978

8079
return getNext().handle(request);

src/main/java/com/emc/object/s3/jersey/S3JerseyClient.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ public class S3JerseyClient extends AbstractJerseyClient implements S3Client {
128128
protected S3Config s3Config;
129129
protected Client client;
130130
protected LoadBalancer loadBalancer;
131+
protected S3SignerV2 signer;
131132

132133
public S3JerseyClient(S3Config s3Config) {
133134
this(s3Config, null);
@@ -142,6 +143,7 @@ public S3JerseyClient(S3Config s3Config) {
142143
public S3JerseyClient(S3Config config, ClientHandler clientHandler) {
143144
super(new S3Config(config)); // deep-copy config so that two clients don't share the same host lists (SDK-122)
144145
s3Config = (S3Config) super.getObjectConfig();
146+
this.signer = new S3SignerV2(s3Config);
145147

146148
SmartConfig smartConfig = s3Config.toSmartConfig();
147149
loadBalancer = smartConfig.getLoadBalancer();
@@ -424,12 +426,13 @@ public Map<String, List<Object>> getHeaders() {
424426

425427
@Override
426428
public MetadataSearchList listSystemMetadataSearchKeys() {
427-
return executeRequest(client, new ObjectRequest(Method.GET, "", "searchmetadata"), MetadataSearchList.class);
429+
ObjectRequest request = new ObjectRequest(Method.GET, "", S3Constants.PARAM_SEARCH_METADATA);
430+
return executeRequest(client, request, MetadataSearchList.class);
428431
}
429432

430433
@Override
431434
public MetadataSearchList listBucketMetadataSearchKeys(String bucketName) {
432-
ObjectRequest request = new GenericBucketRequest(Method.GET, bucketName, "searchmetadata");
435+
ObjectRequest request = new GenericBucketRequest(Method.GET, bucketName, S3Constants.PARAM_SEARCH_METADATA);
433436
return executeRequest(client, request, MetadataSearchList.class);
434437
}
435438

@@ -595,7 +598,7 @@ public URL getPresignedUrl(String bucketName, String key, Date expirationTime) {
595598

596599
@Override
597600
public URL getPresignedUrl(PresignedUrlRequest request) {
598-
return S3AuthUtil.generatePresignedUrl(request, s3Config);
601+
return signer.generatePresignedUrl(request);
599602
}
600603

601604
@Override
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2015-2016, EMC Corporation.
3+
* Redistribution and use in source and binary forms, with or without modification,
4+
* are permitted provided that the following conditions are met:
5+
*
6+
* + Redistributions of source code must retain the above copyright notice,
7+
* this list of conditions and the following disclaimer.
8+
* + Redistributions in binary form must reproduce the above copyright
9+
* notice, this list of conditions and the following disclaimer in the
10+
* documentation and/or other materials provided with the distribution.
11+
* + The name of EMC Corporation may not be used to endorse or promote
12+
* products derived from this software without specific prior written
13+
* permission.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
19+
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25+
* POSSIBILITY OF SUCH DAMAGE.
26+
*/
27+
package com.emc.object.util;
28+
29+
import javax.xml.bind.annotation.adapters.XmlAdapter;
30+
import java.text.DateFormat;
31+
import java.text.SimpleDateFormat;
32+
import java.util.Date;
33+
import java.util.TimeZone;
34+
35+
public class Iso8601DateAdapter extends XmlAdapter<String, Date> {
36+
private static final String ISO_8601_FORMAT = "yyyy-MM-dd";
37+
38+
// DateFormat is *not* thread-safe!
39+
private static final ThreadLocal<DateFormat> iso8601Format = new ThreadLocal<DateFormat>();
40+
41+
@Override
42+
public Date unmarshal( String s ) throws Exception {
43+
return getFormat().parse( s );
44+
}
45+
46+
@Override
47+
public String marshal( Date date ) throws Exception {
48+
return getFormat().format( date );
49+
}
50+
51+
public static DateFormat getFormat() {
52+
DateFormat format = iso8601Format.get();
53+
if ( format == null ) {
54+
format = new SimpleDateFormat( ISO_8601_FORMAT );
55+
format.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
56+
iso8601Format.set( format );
57+
}
58+
return format;
59+
}
60+
}

src/main/java/com/emc/object/util/Iso8601Adapter.java renamed to src/main/java/com/emc/object/util/Iso8601DateTimeAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
import java.util.regex.Matcher;
3636
import java.util.regex.Pattern;
3737

38-
public class Iso8601Adapter extends XmlAdapter<String, Date> {
38+
public class Iso8601DateTimeAdapter extends XmlAdapter<String, Date> {
3939
private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
4040

4141
// DateFormat is *not* thread-safe!

0 commit comments

Comments
 (0)