Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
a76845a
Update V4 signature to pass conformance tests
JesseLovelace Feb 4, 2020
9d6261a
Merge branch 'master' of github.com:googleapis/java-storage
JesseLovelace Feb 4, 2020
73a2da5
Merge branch 'master' of github.com:googleapis/java-storage
JesseLovelace Feb 4, 2020
25ce31d
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
aca896e
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
161fcb5
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
a485ac0
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
9cf145a
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
362ce2c
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
ee7c507
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
140c26c
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
821dddb
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
440a1ca
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
148dfc4
Merge branch 'updatev4' of github.com:googleapis/java-storage into up…
JesseLovelace Feb 5, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ private String constructV4CanonicalRequestHash() {
canonicalRequest
.append(serializer.serializeHeaderNames(canonicalizedExtensionHeaders))
.append(COMPONENT_SEPARATOR);
canonicalRequest.append("UNSIGNED-PAYLOAD");

String userProvidedHash = canonicalizedExtensionHeaders.get("X-Goog-Content-SHA256");
canonicalRequest.append(userProvidedHash == null ? "UNSIGNED-PAYLOAD" : userProvidedHash);

return Hashing.sha256()
.hashString(canonicalRequest.toString(), StandardCharsets.UTF_8)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,21 @@ public String getSelector() {
}
}

enum UriScheme {
HTTP("http"),
HTTPS("https");

private final String scheme;

UriScheme(String scheme) {
this.scheme = scheme;
}

public String getScheme() {
return scheme;
}
}

/** Class for specifying bucket target options. */
class BucketTargetOption extends Option {

Expand Down Expand Up @@ -1038,6 +1053,7 @@ enum Option {
HOST_NAME,
PATH_STYLE,
VIRTUAL_HOSTED_STYLE,
BUCKET_BOUND_HOST_NAME,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jesse, I want to confirm that HOST_NAME can't be in the same way as BUCKET_BOUND_HOST_NAME. Is this accurate?

QUERY_PARAMS
}

Expand Down Expand Up @@ -1160,6 +1176,44 @@ public static SignUrlOption withPathStyle() {
return new SignUrlOption(Option.PATH_STYLE, "");
}

/**
* Use a bucket-bound hostname, which replaces the storage.googleapis.com host with the name of
* a CNAME bucket, e.g. a bucket named 'gcs-subdomain.my.domain.tld', or a Google Cloud Load
* Balancer which routes to a bucket you own, e.g. 'my-load-balancer-domain.tld'. Note that this
* cannot be used alongside {@code withVirtualHostedStyle()} or {@code withPathStyle()}. This
* method signature uses HTTP for the URI scheme, and is equivalent to calling {@code
* withBucketBoundHostname("...", UriScheme.HTTP).}
*
* @see <a href="https://cloud.google.com/storage/docs/request-endpoints#cname">CNAME
* Redirects</a>
* @see <a
* href="https://cloud.google.com/load-balancing/docs/https/adding-backend-buckets-to-load-balancers">
* GCLB Redirects</a>
*/
public static SignUrlOption withBucketBoundHostname(String bucketBoundHostname) {
return withBucketBoundHostname(bucketBoundHostname, UriScheme.HTTP);
}

/**
* Use a bucket-bound hostname, which replaces the storage.googleapis.com host with the name of
* a CNAME bucket, e.g. a bucket named 'gcs-subdomain.my.domain.tld', or a Google Cloud Load
* Balancer which routes to a bucket you own, e.g. 'my-load-balancer-domain.tld'. Note that this
* cannot be used alongside {@code withVirtualHostedStyle()} or {@code withPathStyle()}. The
* bucket name itself should not include the URI scheme (http or https), so it is specified via
* a local enum.
*
* @see <a href="https://cloud.google.com/storage/docs/request-endpoints#cname">CNAME
* Redirects</a>
* @see <a
* href="https://cloud.google.com/load-balancing/docs/https/adding-backend-buckets-to-load-balancers">
* GCLB Redirects</a>
*/
public static SignUrlOption withBucketBoundHostname(
String bucketBoundHostname, UriScheme uriScheme) {
return new SignUrlOption(
Option.BUCKET_BOUND_HOST_NAME, uriScheme.getScheme() + "://" + bucketBoundHostname);
}

/**
* Use if the URL should contain additional query parameters.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,8 +660,10 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio

checkArgument(
!(optionMap.containsKey(SignUrlOption.Option.VIRTUAL_HOSTED_STYLE)
&& optionMap.containsKey(SignUrlOption.Option.PATH_STYLE)),
"Cannot specify both the VIRTUAL_HOSTED_STYLE and PATH_STYLE SignUrlOptions together.");
&& optionMap.containsKey(SignUrlOption.Option.PATH_STYLE)
&& optionMap.containsKey(SignUrlOption.Option.BUCKET_BOUND_HOST_NAME)),
"Only one of VIRTUAL_HOSTED_STYLE, PATH_STYLE, or BUCKET_BOUND_HOST_NAME SignUrlOptions can be"
+ " specified.");

String bucketName = slashlessBucketNameFromBlobInfo(blobInfo);
String escapedBlobName = "";
Expand All @@ -676,6 +678,10 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio
? STORAGE_XML_URI_SCHEME + "://" + getBaseStorageHostName(optionMap)
: STORAGE_XML_URI_SCHEME + "://" + bucketName + "." + getBaseStorageHostName(optionMap);

if (optionMap.containsKey(SignUrlOption.Option.BUCKET_BOUND_HOST_NAME)) {
storageXmlHostName = (String) optionMap.get(SignUrlOption.Option.BUCKET_BOUND_HOST_NAME);
}

String stPath =
usePathStyle
? constructResourceUriPath(bucketName, escapedBlobName, optionMap)
Expand Down Expand Up @@ -753,9 +759,7 @@ private String constructResourceUriPath(
}
return pathBuilder.toString();
}
if (!escapedBlobName.startsWith(PATH_DELIMITER)) {
pathBuilder.append(PATH_DELIMITER);
}
pathBuilder.append(PATH_DELIMITER);
pathBuilder.append(escapedBlobName);
return pathBuilder.toString();
}
Expand All @@ -776,7 +780,8 @@ private SignUrlOption.SignatureVersion getPreferredSignatureVersion(
private boolean shouldUsePathStyleForSignedUrl(EnumMap<SignUrlOption.Option, Object> optionMap) {
// TODO(#6362): If we decide to change the default style used to generate URLs, switch this
// logic to return false unless PATH_STYLE was explicitly specified.
if (optionMap.containsKey(SignUrlOption.Option.VIRTUAL_HOSTED_STYLE)) {
if (optionMap.containsKey(SignUrlOption.Option.VIRTUAL_HOSTED_STYLE)
|| optionMap.containsKey(SignUrlOption.Option.BUCKET_BOUND_HOST_NAME)) {
return false;
}
return true;
Expand Down Expand Up @@ -836,7 +841,8 @@ private SignatureInfo buildSignatureInfo(
extHeadersBuilder.put(
"host",
slashlessBucketNameFromBlobInfo(blobInfo) + "." + getBaseStorageHostName(optionMap));
} else if (optionMap.containsKey(SignUrlOption.Option.HOST_NAME)) {
} else if (optionMap.containsKey(SignUrlOption.Option.HOST_NAME)
|| optionMap.containsKey(SignUrlOption.Option.BUCKET_BOUND_HOST_NAME)) {
extHeadersBuilder.put("host", getBaseStorageHostName(optionMap));
}
}
Expand Down Expand Up @@ -868,9 +874,14 @@ private String slashlessBucketNameFromBlobInfo(BlobInfo blobInfo) {
/** Returns the hostname used to send requests to Cloud Storage, e.g. "storage.googleapis.com". */
private String getBaseStorageHostName(Map<SignUrlOption.Option, Object> optionMap) {
String specifiedBaseHostName = (String) optionMap.get(SignUrlOption.Option.HOST_NAME);
String bucketBoundHostName =
(String) optionMap.get(SignUrlOption.Option.BUCKET_BOUND_HOST_NAME);
if (!Strings.isNullOrEmpty(specifiedBaseHostName)) {
return specifiedBaseHostName.replaceFirst("http(s)?://", "");
}
if (!Strings.isNullOrEmpty(bucketBoundHostName)) {
return bucketBoundHostName.replaceFirst("http(s)?://", "");
}
return STORAGE_XML_URI_HOST_NAME;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1808,6 +1808,7 @@ public void testSignUrlLeadingSlash()
String expectedUrl =
new StringBuilder("https://storage.googleapis.com/")
.append(BUCKET_NAME1)
.append("/")
.append(expectedResourcePath)
.append("?GoogleAccessId=")
.append(ACCOUNT)
Expand All @@ -1825,6 +1826,7 @@ public void testSignUrlLeadingSlash()
.append(42L + 1209600)
.append("\n/")
.append(BUCKET_NAME1)
.append("/")
.append(expectedResourcePath);

Signature signer = Signature.getInstance("SHA256withRSA");
Expand Down Expand Up @@ -1857,6 +1859,7 @@ public void testSignUrlLeadingSlashWithHostName()
String expectedUrl =
new StringBuilder("https://example.com/")
.append(BUCKET_NAME1)
.append("/")
.append(escapedBlobName)
.append("?GoogleAccessId=")
.append(ACCOUNT)
Expand All @@ -1874,6 +1877,7 @@ public void testSignUrlLeadingSlashWithHostName()
.append(42L + 1209600)
.append("\n/")
.append(BUCKET_NAME1)
.append("/")
.append(escapedBlobName);

Signature signer = Signature.getInstance("SHA256withRSA");
Expand Down Expand Up @@ -2019,6 +2023,7 @@ public void testSignUrlForBlobWithSpecialChars()
String expectedUrl =
new StringBuilder("https://storage.googleapis.com/")
.append(BUCKET_NAME1)
.append("/")
.append(expectedBlobName)
.append("?GoogleAccessId=")
.append(ACCOUNT)
Expand All @@ -2036,6 +2041,7 @@ public void testSignUrlForBlobWithSpecialChars()
.append(42L + 1209600)
.append("\n/")
.append(BUCKET_NAME1)
.append("/")
.append(expectedBlobName);

Signature signer = Signature.getInstance("SHA256withRSA");
Expand Down Expand Up @@ -2075,6 +2081,7 @@ public void testSignUrlForBlobWithSpecialCharsAndHostName()
String expectedUrl =
new StringBuilder("https://example.com/")
.append(BUCKET_NAME1)
.append("/")
.append(expectedBlobName)
.append("?GoogleAccessId=")
.append(ACCOUNT)
Expand All @@ -2092,6 +2099,7 @@ public void testSignUrlForBlobWithSpecialCharsAndHostName()
.append(42L + 1209600)
.append("\n/")
.append(BUCKET_NAME1)
.append("/")
.append(expectedBlobName);

Signature signer = Signature.getInstance("SHA256withRSA");
Expand Down Expand Up @@ -2243,6 +2251,7 @@ public void testSignUrlForBlobWithSlashes()
String expectedUrl =
new StringBuilder("https://storage.googleapis.com/")
.append(BUCKET_NAME1)
.append("/")
.append(escapedBlobName)
.append("?GoogleAccessId=")
.append(ACCOUNT)
Expand All @@ -2260,6 +2269,7 @@ public void testSignUrlForBlobWithSlashes()
.append(42L + 1209600)
.append("\n/")
.append(BUCKET_NAME1)
.append("/")
.append(escapedBlobName);

Signature signer = Signature.getInstance("SHA256withRSA");
Expand Down Expand Up @@ -2293,6 +2303,7 @@ public void testSignUrlForBlobWithSlashesAndHostName()
String expectedUrl =
new StringBuilder("https://example.com/")
.append(BUCKET_NAME1)
.append("/")
.append(escapedBlobName)
.append("?GoogleAccessId=")
.append(ACCOUNT)
Expand All @@ -2310,6 +2321,7 @@ public void testSignUrlForBlobWithSlashesAndHostName()
.append(42L + 1209600)
.append("\n/")
.append(BUCKET_NAME1)
.append("/")
.append(escapedBlobName);

Signature signer = Signature.getInstance("SHA256withRSA");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@

package com.google.cloud.storage;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assume.assumeThat;

import com.google.api.core.ApiClock;
import com.google.auth.oauth2.ServiceAccountCredentials;
Expand Down Expand Up @@ -94,11 +91,6 @@ public V4SigningTest(

@Test
public void test() {
assumeThat(
"Test skipped until b/136171758 is resolved",
testName.getMethodName(),
is(not("test[Headers should be trimmed]")));

Storage storage =
RemoteStorageHelper.create()
.getOptions()
Expand All @@ -110,6 +102,19 @@ public void test() {

BlobInfo blob = BlobInfo.newBuilder(testData.getBucket(), testData.getObject()).build();

SignUrlOption style = SignUrlOption.withPathStyle();

if (testData.getUrlStyle().equals(SigningV4Test.UrlStyle.VIRTUAL_HOSTED_STYLE)) {
style = SignUrlOption.withVirtualHostedStyle();
} else if (testData.getUrlStyle().equals(SigningV4Test.UrlStyle.PATH_STYLE)) {
style = SignUrlOption.withPathStyle();
} else if (testData.getUrlStyle().equals(SigningV4Test.UrlStyle.BUCKET_BOUND_DOMAIN)) {
style =
SignUrlOption.withBucketBoundHostname(
testData.getBucketBoundDomain(),
Storage.UriScheme.valueOf(testData.getScheme().toUpperCase()));
}

final String signedUrl =
storage
.signUrl(
Expand All @@ -118,7 +123,9 @@ public void test() {
TimeUnit.SECONDS,
SignUrlOption.httpMethod(HttpMethod.valueOf(testData.getMethod())),
SignUrlOption.withExtHeaders(testData.getHeadersMap()),
SignUrlOption.withV4Signature())
SignUrlOption.withV4Signature(),
SignUrlOption.withQueryParams(testData.getQueryParametersMap()),
style)
.toString();
assertEquals(testData.getExpectedUrl(), signedUrl);
}
Expand Down
7 changes: 5 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-conformance-tests</artifactId>
<version>0.0.4</version>
<version>0.0.5</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand All @@ -225,7 +225,10 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<configuration>
<ignoredUnusedDeclaredDependencies>org.objenesis:objenesis</ignoredUnusedDeclaredDependencies>
<ignoredUnusedDeclaredDependencies>
<ignoredUnusedDeclaredDependency>org.hamcrest:hamcrest</ignoredUnusedDeclaredDependency>
<ignoredUnusedDeclaredDependency>org.objenesis:objenesis</ignoredUnusedDeclaredDependency>
</ignoredUnusedDeclaredDependencies>
</configuration>
</plugin>
</plugins>
Expand Down