Skip to content

Commit e8ff75f

Browse files
frantelloandreamlin
authored andcommitted
---
yaml --- r: 8663 b: refs/heads/master c: f40fffc h: refs/heads/master i: 8661: fa57ab2 8659: f1eb2e2 8655: 45edf0c
1 parent 2451aac commit e8ff75f

8 files changed

Lines changed: 529 additions & 35 deletions

File tree

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
refs/heads/master: 22331631bd3703d239cab66ded080c31f127bafa
2+
refs/heads/master: f40fffce3235d8093751bfb9254211bfbcccadb2
33
refs/heads/travis: 47e4fee4fd5af9b2a8ce46f23c72ec95f9b195b2
44
refs/heads/gh-pages: 6daca92127d91b7c2c99490080ecf8a13fa94cde
55
refs/tags/0.0.9: 22f1839238f66c39e67ed4dfdcd273b1ae2e8444
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2017 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.storage;
18+
19+
import java.util.ArrayList;
20+
import java.util.Collections;
21+
import java.util.HashMap;
22+
import java.util.List;
23+
import java.util.Map;
24+
25+
/**
26+
* Canonical extension header serializer.
27+
*
28+
* @see <a href=
29+
* "https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers">
30+
* Canonical Extension Headers</a>
31+
*/
32+
public class CanonicalExtensionHeadersSerializer {
33+
34+
private static final char HEADER_SEPARATOR = ':';
35+
36+
public StringBuilder serialize(Map<String, String> canonicalizedExtensionHeaders) {
37+
38+
StringBuilder serializedHeaders = new StringBuilder();
39+
40+
if (canonicalizedExtensionHeaders == null ||
41+
canonicalizedExtensionHeaders.isEmpty()) {
42+
43+
return serializedHeaders;
44+
}
45+
46+
// Make all custom header names lowercase.
47+
Map<String, String> lowercaseHeaders = new HashMap<>();
48+
for (String headerName : new ArrayList<>(canonicalizedExtensionHeaders.keySet())) {
49+
50+
String lowercaseHeaderName = headerName.toLowerCase();
51+
52+
// If present, remove the x-goog-encryption-key and x-goog-encryption-key-sha256 headers.
53+
if ("x-goog-encryption-key".equals(lowercaseHeaderName) ||
54+
"x-goog-encryption-key-sha256".equals(lowercaseHeaderName)) {
55+
56+
continue;
57+
}
58+
59+
lowercaseHeaders.put(lowercaseHeaderName, canonicalizedExtensionHeaders.get(headerName));
60+
}
61+
62+
// Sort all custom headers by header name using a lexicographical sort by code point value.
63+
List<String> sortedHeaderNames = new ArrayList<>(lowercaseHeaders.keySet());
64+
Collections.sort(sortedHeaderNames);
65+
66+
for (String headerName : sortedHeaderNames) {
67+
serializedHeaders
68+
.append(headerName).append(HEADER_SEPARATOR)
69+
.append(lowercaseHeaders.get(headerName)
70+
// Remove any whitespace around the colon that appears after the header name.
71+
.trim()
72+
// Replace any folding whitespace or newlines (CRLF or LF) with a single space.
73+
.replaceAll("[\\s]{2,}"," ")
74+
.replaceAll("(\\t|\\r?\\n)+", " "))
75+
// Append a newline (U+000A) to each custom header.
76+
.append(SignatureInfo.COMPONENT_SEPARATOR);
77+
}
78+
79+
// Concatenate all custom headers
80+
return serializedHeaders;
81+
}
82+
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright 2015 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.storage;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import java.net.URI;
21+
import java.util.Map;
22+
23+
/**
24+
* Signature Info holds payload components of the string that requires signing.
25+
*
26+
* @see <a href=
27+
* "https://cloud.google.com/storage/docs/access-control/signed-urls#string-components">
28+
* Components</a>
29+
*/
30+
public class SignatureInfo {
31+
32+
public static final char COMPONENT_SEPARATOR = '\n';
33+
34+
private final HttpMethod httpVerb;
35+
private final String contentMd5;
36+
private final String contentType;
37+
private final long expiration;
38+
private final Map<String, String> canonicalizedExtensionHeaders;
39+
private final URI canonicalizedResource;
40+
41+
private SignatureInfo(Builder builder) {
42+
this.httpVerb = builder.httpVerb;
43+
this.contentMd5 = builder.contentMd5;
44+
this.contentType = builder.contentType;
45+
this.expiration = builder.expiration;
46+
this.canonicalizedExtensionHeaders = builder.canonicalizedExtensionHeaders;
47+
this.canonicalizedResource = builder.canonicalizedResource;
48+
}
49+
50+
/**
51+
* Constructs payload to be signed.
52+
*
53+
* @return paylod to sign
54+
* @see <a href="https://cloud.google.com/storage/docs/access-control#Signed-URLs">Signed URLs</a>
55+
*/
56+
public String constructUnsignedPayload() {
57+
StringBuilder payload = new StringBuilder();
58+
59+
payload.append(httpVerb.name()).append(COMPONENT_SEPARATOR);
60+
if (contentMd5 != null) {
61+
payload.append(contentMd5);
62+
}
63+
payload.append(COMPONENT_SEPARATOR);
64+
65+
if (contentType != null) {
66+
payload.append(contentType);
67+
}
68+
payload.append(COMPONENT_SEPARATOR);
69+
70+
payload.append(expiration).append(COMPONENT_SEPARATOR);
71+
72+
if (canonicalizedExtensionHeaders != null) {
73+
payload.append(new CanonicalExtensionHeadersSerializer()
74+
.serialize(canonicalizedExtensionHeaders));
75+
}
76+
77+
payload.append(canonicalizedResource);
78+
79+
return payload.toString();
80+
}
81+
82+
public HttpMethod getHttpVerb() {
83+
return httpVerb;
84+
}
85+
86+
public String getContentMd5() {
87+
return contentMd5;
88+
}
89+
90+
public String getContentType() {
91+
return contentType;
92+
}
93+
94+
public long getExpiration() {
95+
return expiration;
96+
}
97+
98+
public Map<String, String> getCanonicalizedExtensionHeaders() {
99+
return canonicalizedExtensionHeaders;
100+
}
101+
102+
public URI getCanonicalizedResource() {
103+
return canonicalizedResource;
104+
}
105+
106+
public static final class Builder {
107+
108+
private final HttpMethod httpVerb;
109+
private String contentMd5;
110+
private String contentType;
111+
private final long expiration;
112+
private Map<String, String> canonicalizedExtensionHeaders;
113+
private final URI canonicalizedResource;
114+
115+
/**
116+
* Constructs builder.
117+
*
118+
* @param httpVerb the HTTP method
119+
* @param expiration the EPOX expiration date
120+
* @param canonicalizedResource the resource URI
121+
* @throws IllegalArgumentException if required field is not provided.
122+
*/
123+
public Builder(HttpMethod httpVerb, long expiration, URI canonicalizedResource) {
124+
this.httpVerb = httpVerb;
125+
this.expiration = expiration;
126+
this.canonicalizedResource = canonicalizedResource;
127+
}
128+
129+
public Builder(SignatureInfo signatureInfo) {
130+
this.httpVerb = signatureInfo.httpVerb;
131+
this.contentMd5 = signatureInfo.contentMd5;
132+
this.contentType = signatureInfo.contentType;
133+
this.expiration = signatureInfo.expiration;
134+
this.canonicalizedExtensionHeaders = signatureInfo.canonicalizedExtensionHeaders;
135+
this.canonicalizedResource = signatureInfo.canonicalizedResource;
136+
}
137+
138+
public Builder setContentMd5(String contentMd5) {
139+
this.contentMd5 = contentMd5;
140+
141+
return this;
142+
}
143+
144+
public Builder setContentType(String contentType) {
145+
this.contentType = contentType;
146+
147+
return this;
148+
}
149+
150+
public Builder setCanonicalizedExtensionHeaders(
151+
Map<String, String> canonicalizedExtensionHeaders) {
152+
this.canonicalizedExtensionHeaders = canonicalizedExtensionHeaders;
153+
154+
return this;
155+
}
156+
157+
/**
158+
* Creates an {@code SignatureInfo} object from this builder.
159+
*/
160+
public SignatureInfo build() {
161+
checkArgument(httpVerb != null, "Required HTTP method");
162+
checkArgument(canonicalizedResource != null, "Required canonicalized resource");
163+
checkArgument(expiration >= 0, "Expiration must be greater than or equal to zero");
164+
165+
return new SignatureInfo(this);
166+
}
167+
}
168+
}

trunk/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import java.util.LinkedHashSet;
4747
import java.util.LinkedList;
4848
import java.util.List;
49+
import java.util.Map;
4950
import java.util.Objects;
5051
import java.util.Set;
5152
import java.util.concurrent.TimeUnit;
@@ -876,7 +877,7 @@ class SignUrlOption implements Serializable {
876877
private final Object value;
877878

878879
enum Option {
879-
HTTP_METHOD, CONTENT_TYPE, MD5, SERVICE_ACCOUNT_CRED
880+
HTTP_METHOD, CONTENT_TYPE, MD5, EXT_HEADERS, SERVICE_ACCOUNT_CRED
880881
}
881882

882883
private SignUrlOption(Option option, Object value) {
@@ -897,7 +898,7 @@ Object getValue() {
897898
* If this method is not called, defaults to GET.
898899
*/
899900
public static SignUrlOption httpMethod(HttpMethod httpMethod) {
900-
return new SignUrlOption(Option.HTTP_METHOD, httpMethod.name());
901+
return new SignUrlOption(Option.HTTP_METHOD, httpMethod);
901902
}
902903

903904
/**
@@ -916,6 +917,16 @@ public static SignUrlOption withContentType() {
916917
public static SignUrlOption withMd5() {
917918
return new SignUrlOption(Option.MD5, true);
918919
}
920+
921+
/**
922+
* Use it if signature should include the blob's canonicalized extended headers.
923+
* When used, users of the signed URL should include the canonicalized extended headers with
924+
* their request.
925+
* @see <a href="https://cloud.google.com/storage/docs/xml-api/reference-headers"></a>
926+
*/
927+
public static SignUrlOption withExtHeaders(Map<String, String> extHeaders) {
928+
return new SignUrlOption(Option.EXT_HEADERS, extHeaders);
929+
}
919930

920931
/**
921932
* Provides a service account signer to sign the URL. If not provided an attempt will be made to

0 commit comments

Comments
 (0)