Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: V4 POST policy #177

Merged
merged 40 commits into from Apr 30, 2020
Merged

feat: V4 POST policy #177

merged 40 commits into from Apr 30, 2020

Conversation

JesseLovelace
Copy link
Contributor

@JesseLovelace JesseLovelace commented Mar 12, 2020

Adds V4 post policy. Will add documentation once implementation is approved

Fixes: #280 #88

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Mar 12, 2020
@codecov
Copy link

codecov bot commented Mar 12, 2020

Codecov Report

Merging #177 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##             master     #177   +/-   ##
=========================================
  Coverage     62.43%   62.43%           
  Complexity      552      552           
=========================================
  Files            31       31           
  Lines          5007     5007           
  Branches        449      449           
=========================================
  Hits           3126     3126           
  Misses         1712     1712           
  Partials        169      169           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update da40d03...da40d03. Read the comment docs.

Copy link
Contributor

@elharo elharo left a comment

Choose a reason for hiding this comment

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

Can you add documentation in this PR? That would make the code here easier to understand and review.

/**
* Use a virtual hosted-style hostname, which adds the bucket into the host portion of the URI
* rather than the path, e.g. 'https://mybucket.storage.googleapis.com/...'. The bucket name
* will be obtained from the resource passed in.
Copy link
Contributor

Choose a reason for hiding this comment

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

will be --> is
per Google Tech Writing Guidelines
https://developers.google.com/style/tense

}

/**
* Provides a service account signer to sign the policy. If not provided an attempt will be made
Copy link
Contributor

Choose a reason for hiding this comment

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

will be --> is


/**
* Generate a path-style URL, which places the bucket name in the path portion of the URL
* instead of in the hostname, e.g 'https://storage.googleapis.com/mybucket/...'. Note that this
Copy link
Contributor

Choose a reason for hiding this comment

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

}

/**
* Generate a path-style URL, which places the bucket name in the path portion of the URL
Copy link
Contributor

Choose a reason for hiding this comment

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

/**
* 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
Copy link
Contributor

Choose a reason for hiding this comment

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

delete "Note that"


boolean usePathStyle = shouldUsePathStyleForSignedUrl(optionMap);

String url =
Copy link
Contributor

Choose a reason for hiding this comment

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

The ternary operator is hard to read when broken across multiple lines. Consider if else.

Copy link
Member

Choose a reason for hiding this comment

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

Elliotte's comment here makes sense. Probably want to use an if-else in this case.

@@ -0,0 +1,202 @@
/*
* Copyright 2019 Google LLC
Copy link
Contributor

Choose a reason for hiding this comment

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

2020

Copy link
Member

Choose a reason for hiding this comment

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

Should be 2020.


package com.google.cloud.storage;

import static com.google.cloud.storage.Storage.V4ConditionType.MATCHES;
Copy link
Contributor

Choose a reason for hiding this comment

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

Use non-static imports here as these are not well-known names.

private final ServiceAccountCredentials serviceAccountCredentials;

/**
* @param testData The serialized test data representing the test case.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: no cap and no period since it's not a complete sentence

}

/**
* Load all of the tests and return a {@code Collection<Object[]>} representing the set of tests.
Copy link
Contributor

Choose a reason for hiding this comment

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

Load --> Loads

Copy link
Member

@frankyn frankyn left a comment

Choose a reason for hiding this comment

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

I'm still reviewing your PR. One ask is to add examples of how your code is expected to be used into Surface Review Doc

}
}

class V4PostConditions {
Copy link
Member

Choose a reason for hiding this comment

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

+1 could you split this up into separate files for maintainability as well?

@@ -104,14 +105,14 @@ public void test() {

SignUrlOption style = SignUrlOption.withPathStyle();

if (testData.getUrlStyle().equals(SigningV4Test.UrlStyle.VIRTUAL_HOSTED_STYLE)) {
if (testData.getUrlStyle().equals(UrlStyle.VIRTUAL_HOSTED_STYLE)) {
Copy link
Member

Choose a reason for hiding this comment

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

can you split out this into a separte PR if possible?

Copy link
Member

Choose a reason for hiding this comment

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

For this specifically, I mean to split out conformance test changes into a separate PR.

Copy link
Member

@frankyn frankyn left a comment

Choose a reason for hiding this comment

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

Thanks again for your patience @JesseLovelace.

@@ -104,14 +105,14 @@ public void test() {

SignUrlOption style = SignUrlOption.withPathStyle();

if (testData.getUrlStyle().equals(SigningV4Test.UrlStyle.VIRTUAL_HOSTED_STYLE)) {
if (testData.getUrlStyle().equals(UrlStyle.VIRTUAL_HOSTED_STYLE)) {
Copy link
Member

Choose a reason for hiding this comment

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

For this specifically, I mean to split out conformance test changes into a separate PR.

@@ -1255,6 +1260,447 @@ public static SignUrlOption withQueryParams(Map<String, String> queryParams) {
}
}

class V4PostPolicyOption implements Serializable {
Copy link
Member

Choose a reason for hiding this comment

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

Can you split all V4PostPolicy helper classes into a separate file, except what's needed for generateV4PresignedPostPolicy ease of use such as V4PostPolicyOption?

return generateV4PresignedPostPolicy(
blobInfo, V4PostFields.newBuilder().build(), duration, options);
}

Copy link
Member

Choose a reason for hiding this comment

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

Put V4 at the end of the name please.

Copy link
Member

Choose a reason for hiding this comment

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

Please do this for other helpers as well that use V4 for POST Policy. From meeting.

Copy link
Member

@frankyn frankyn left a comment

Choose a reason for hiding this comment

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

@JesseLovelace adding name change request for top level method name.

@@ -2532,6 +2978,13 @@ Blob create(
*/
URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options);

V4PostPolicy generateV4PresignedPostPolicy(
Copy link
Member

Choose a reason for hiding this comment

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

Using consistent naming across languages, please use: generateSignedPostPolicyV4.

Copy link
Member

@frankyn frankyn left a comment

Choose a reason for hiding this comment

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

Some surface changes requested. Open to discussing them as well.

* @param duration how long until the form expires, in milliseconds
* @param options optional post policy options
*/
PostPolicyV4 generatePresignedPostPolicyV4(
Copy link
Member

Choose a reason for hiding this comment

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

use generateSignedPostPolicyV4

*/
PostPolicyV4 generatePresignedPostPolicyV4(
BlobInfo blobInfo,
PostFieldsV4 fields,
Copy link
Member

Choose a reason for hiding this comment

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

fields and conditions should be optional. I want to say these should be under options.

this.value = value;
}

public static ConditionV4 newV4Condition(ConditionV4Type type, String element, String value) {
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't translate correctly for ContentRangeLengths because element and value are start and end respectively.

}

public static ConditionV4 newV4Condition(ConditionV4Type type, String element, String value) {
return new ConditionV4(type, element, value);
Copy link
Member

Choose a reason for hiding this comment

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

Given PostConditionV4 requires users to use a related method. Should this class have a private static method helper as well? Also not sure how this is different from private ConditionV4()?

Copy link
Member

@frankyn frankyn left a comment

Choose a reason for hiding this comment

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

Small nits overall LGTM.

Please also add integration tests.

/**
* Presigned V4 post policy.
*
* @see <a href="https://cloud.google.com/storage/docs/xml-api/post-object" POST Object</a>
Copy link
Member

Choose a reason for hiding this comment

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

Missing >

/**
* Class representing which fields to specify in a V4 POST request.
*
* @see <a href=https://cloud.google.com/storage/docs/xml-api/post-object#form_fields> POST Object Form fields</a>
Copy link
Member

Choose a reason for hiding this comment

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

No double quote " around the URL.

return this;
}

public Builder addCustomCondition(ConditionV4Type type, String field, String value) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggest removing this until we get a feature request for this method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We actually use it in the generate method, but I can change it to package private

Copy link
Member

Choose a reason for hiding this comment

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

package private sgtm, thanks for clarifying!

* PostFieldsV4 fields = PostFieldsV4.newBuilder().setAcl("public-read").build();
* PostConditionsV4 conditions = PostConditionsV4.newBuilder().addContentTypeCondition(ConditionV4Type.MATCHES, "image/jpeg").build();
*
* PostPolicyV4 policy = storage.generatePresignedPostPolicyV4(
Copy link
Member

Choose a reason for hiding this comment

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

uses old, instead of generateSignedPostPolicyV4.

*
* PostPolicyV4 policy = storage.generatePresignedPostPolicyV4(
* BlobInfo.newBuilder("my-bucket", "my-object").build(),
* fields, conditions, 7, TimeUnit.DAYS);
Copy link
Member

Choose a reason for hiding this comment

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

Could you add an example of how to send a POST request with the policy result?


boolean usePathStyle = shouldUsePathStyleForSignedUrl(optionMap);

String url =
Copy link
Member

Choose a reason for hiding this comment

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

Elliotte's comment here makes sense. Probably want to use an if-else in this case.

@@ -0,0 +1,202 @@
/*
* Copyright 2019 Google LLC
Copy link
Member

Choose a reason for hiding this comment

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

Should be 2020.

PostPolicyV4 generateSignedPostPolicyV4(
BlobInfo blobInfo, long duration, TimeUnit unit, PostConditionsV4 conditions, PostPolicyV4Option... options);

PostPolicyV4 generateSignedPostPolicyV4(
Copy link
Member

Choose a reason for hiding this comment

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

Add integration tests.

public Builder setSuccessActionStatus(int successActionStatus) {
this.successActionStatus = "" + successActionStatus;
return this;
}
Copy link
Member

Choose a reason for hiding this comment

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

Custom field setter should be available here.

return this;
}

public Builder addCustomMetadataCondition(ConditionV4Type type, String field, String value) {
Copy link
Member

Choose a reason for hiding this comment

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

this should be in fields instead.

Copy link
Member

@frankyn frankyn left a comment

Choose a reason for hiding this comment

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

Thanks @JesseLovelace, added a few nit comments. Mainly the integration test should send the request to GCS and make sure it's successful.

* for(Map.Entry<String, String> entry : policy.getFields().entrySet()) {
* request.addHeader(entry.getKey(), entry.getValue());
* }
* client.execute(request);
Copy link
Member

Choose a reason for hiding this comment

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

This example is missing the file to upload*

@@ -67,6 +67,8 @@
import com.google.cloud.storage.CopyWriter;
import com.google.cloud.storage.HmacKey;
import com.google.cloud.storage.HttpMethod;
import com.google.cloud.storage.PostPolicyV4;
import com.google.cloud.storage.PostPolicyV4.*;
Copy link
Member

Choose a reason for hiding this comment

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

Explicitly import the needed classes and not use *.

@@ -3208,4 +3210,72 @@ public void testBucketLogging() throws ExecutionException, InterruptedException
RemoteStorageHelper.forceDelete(storage, loggingBucket, 5, TimeUnit.SECONDS);
}
}

@Test
public void testSignedPostPolicyV4() {
Copy link
Member

Choose a reason for hiding this comment

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

This is more of a unit test so I would say it's good to keep. I'd like to see a request similar to what you added into your documentation above:

PostFieldsV4 fields = PostFieldsV4.newBuilder().setAcl("public-read").build();
PostConditionsV4 conditions = PostConditionsV4.newBuilder().addContentTypeCondition(ConditionV4Type.MATCHES, "image/jpeg").build();

PostPolicyV4 policy = storage.generateSignedPostPolicyV4(
 BlobInfo.newBuilder("my-bucket", "my-object").build(),
 7, TimeUnit.DAYS, fields, conditions);

HttpClient client = HttpClientBuilder.create().build();
HttpPost request = new HttpPost(policy.getUrl());
for(Map.Entry<String, String> entry : policy.getFields().entrySet()) {
     request.addHeader(entry.getKey(), entry.getValue());
}
// Missing data to upload in request
client.execute(request);

@@ -2532,6 +2634,88 @@ Blob create(
*/
URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options);

/**
* Generates a URL and a map of fields that can be specified in a form to submit a POST request.
Copy link
Member

Choose a reason for hiding this comment

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

in an HTML form
You can link to relevant documentation here: https://cloud.google.com/storage/docs/xml-api/post-object#usage_and_examples

return this;
}

public Builder addBucketCondition(ConditionV4Type type, String bucket) {
Copy link
Member

Choose a reason for hiding this comment

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

Please remove this condition.

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
Copy link
Member

Choose a reason for hiding this comment

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

what's the scope for gson artifact?

@frankyn frankyn added kokoro:force-run Add this label to force Kokoro to re-run the tests. and removed kokoro:force-run Add this label to force Kokoro to re-run the tests. labels Apr 27, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 27, 2020
Copy link
Member

@frankyn frankyn left a comment

Choose a reason for hiding this comment

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

LGTM, let's get it merge after you fix the Binary Compat issue.

@@ -56,7 +57,6 @@
*
* @see <a href="https://cloud.google.com/storage/docs">Google Cloud Storage</a>
*/
@InternalExtensionOnly
public interface Storage extends Service<StorageOptions> {
Copy link
Contributor

Choose a reason for hiding this comment

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

@JesseLovelace , @frankyn

InternalExtensionOnly is necessary, otherwise this change will break API.

Copy link
Member

Choose a reason for hiding this comment

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

Definitely missed this during review, thank you @dmitry-fa!

* @return the updated blob
* @throws StorageException upon failure
* @see <a
* href="https://cloud.google.com/storage/docs/json_api/v1/objects/update">https://cloud.google.com/storage/docs/json_api/v1/objects/update</a>
*/
Blob update(BlobInfo blobInfo);
Copy link
Contributor

@dmitry-fa dmitry-fa Apr 30, 2020

Choose a reason for hiding this comment

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

These changes revert fixes made for #252.

cc: @frankyn

Copy link
Member

Choose a reason for hiding this comment

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

Addressed issues. Thanks @dmitry-fa

@frankyn frankyn added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 30, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 30, 2020
@frankyn frankyn requested a review from dmitry-fa April 30, 2020 15:42
Copy link
Contributor

@dmitry-fa dmitry-fa left a comment

Choose a reason for hiding this comment

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

Thanks for addressing my notes!

@JesseLovelace JesseLovelace merged commit 32d8ffa into master Apr 30, 2020
@JesseLovelace JesseLovelace deleted the updatev4 branch April 30, 2020 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Storage Update Conformance Tests
6 participants