diff --git a/CHANGELOG.md b/CHANGELOG.md index f1ab207f2..83772c730 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ [1]: https://pypi.org/project/google-cloud-storage/#history +## [1.28.0](https://www.github.com/googleapis/python-storage/compare/v1.27.0...v1.28.0) (2020-04-22) + + +### Features + +* **storage:** add arguments for *GenerationMatch uploading options ([#111](https://www.github.com/googleapis/python-storage/issues/111)) ([b11aa5f](https://www.github.com/googleapis/python-storage/commit/b11aa5f00753b094580847bc62c154ae0e584dbc)) + + +### Bug Fixes + +* **storage:** fix incorrect mtime by UTC offset ([#42](https://www.github.com/googleapis/python-storage/issues/42)) ([76bd652](https://www.github.com/googleapis/python-storage/commit/76bd652a3078d94e03e566b6a387fc488ab26910)) +* **storage:** remove expiration strict conversion ([#106](https://www.github.com/googleapis/python-storage/issues/106)) ([9550dad](https://www.github.com/googleapis/python-storage/commit/9550dad6e63e249110fc9dcda245061b76dacdcf)), closes [#105](https://www.github.com/googleapis/python-storage/issues/105) + ## [1.27.0](https://www.github.com/googleapis/python-storage/compare/v1.26.0...v1.27.0) (2020-04-01) diff --git a/docs/conf.py b/docs/conf.py index 9a042ba1e..1bb947c41 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,6 +38,7 @@ "sphinx.ext.napoleon", "sphinx.ext.todo", "sphinx.ext.viewcode", + "recommonmark", ] # autodoc/autosummary flags @@ -49,10 +50,6 @@ # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] -# Allow markdown includes (so releases.md can include CHANGLEOG.md) -# http://www.sphinx-doc.org/en/master/markdown.html -source_parsers = {".md": "recommonmark.parser.CommonMarkParser"} - # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] diff --git a/google/cloud/storage/_helpers.py b/google/cloud/storage/_helpers.py index 1a1aca8ce..b649384f7 100644 --- a/google/cloud/storage/_helpers.py +++ b/google/cloud/storage/_helpers.py @@ -19,6 +19,7 @@ import base64 from hashlib import md5 +from datetime import datetime import os from google.cloud.storage.constants import _DEFAULT_TIMEOUT @@ -297,3 +298,17 @@ def _base64_md5hash(buffer_object): _write_buffer_to_hash(buffer_object, hash_obj) digest_bytes = hash_obj.digest() return base64.b64encode(digest_bytes) + + +def _convert_to_timestamp(value): + """Convert non-none datetime to timestamp. + + :type value: :class:`datetime.datetime` + :param value: The datetime to convert. + + :rtype: int + :returns: The timestamp. + """ + utc_naive = value.replace(tzinfo=None) - value.utcoffset() + mtime = (utc_naive - datetime(1970, 1, 1)).total_seconds() + return mtime diff --git a/google/cloud/storage/_signing.py b/google/cloud/storage/_signing.py index 38610b6ff..ae2248465 100644 --- a/google/cloud/storage/_signing.py +++ b/google/cloud/storage/_signing.py @@ -82,7 +82,7 @@ def get_signed_query_params_v2(credentials, expiration, string_to_sign): service_account_name = credentials.signer_email return { "GoogleAccessId": service_account_name, - "Expires": str(expiration), + "Expires": expiration, "Signature": signature, } @@ -384,7 +384,7 @@ def generate_signed_url_v2( signature = _sign_message(string_to_sign, access_token, service_account_email) signed_query_params = { "GoogleAccessId": service_account_email, - "Expires": str(expiration), + "Expires": expiration_stamp, "Signature": signature, } else: diff --git a/google/cloud/storage/blob.py b/google/cloud/storage/blob.py index fb329d08d..4200c9dd4 100644 --- a/google/cloud/storage/blob.py +++ b/google/cloud/storage/blob.py @@ -31,8 +31,8 @@ from io import BytesIO import mimetypes import os -import time import warnings +import six from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import quote @@ -57,6 +57,7 @@ from google.cloud.storage._helpers import _get_storage_host from google.cloud.storage._helpers import _PropertyMixin from google.cloud.storage._helpers import _scalar_property +from google.cloud.storage._helpers import _convert_to_timestamp from google.cloud.storage._signing import generate_signed_url_v2 from google.cloud.storage._signing import generate_signed_url_v4 from google.cloud.storage.acl import ACL @@ -174,16 +175,19 @@ def __init__( kms_key_name=None, generation=None, ): + """ + property :attr:`name` + Get the blob's name. + """ name = _bytes_to_unicode(name) super(Blob, self).__init__(name=name) self.chunk_size = chunk_size # Check that setter accepts value. self._bucket = bucket self._acl = ObjectACL(self) - if encryption_key is not None and kms_key_name is not None: - raise ValueError( - "Pass at most one of 'encryption_key' " "and 'kms_key_name'" - ) + _raise_for_more_than_one_none( + encryption_key=encryption_key, kms_key_name=kms_key_name, + ) self._encryption_key = encryption_key @@ -842,7 +846,10 @@ def download_to_filename( updated = self.updated if updated is not None: - mtime = time.mktime(updated.timetuple()) + if six.PY2: + mtime = _convert_to_timestamp(updated) + else: + mtime = updated.timestamp() os.utime(file_obj.name, (mtime, mtime)) def download_as_string(self, client=None, start=None, end=None, raw_download=False): @@ -966,7 +973,17 @@ def _get_upload_arguments(self, content_type): return headers, object_metadata, content_type def _do_multipart_upload( - self, client, stream, content_type, size, num_retries, predefined_acl + self, + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ): """Perform a multipart upload. @@ -999,6 +1016,27 @@ def _do_multipart_upload( :type predefined_acl: str :param predefined_acl: (Optional) Predefined access control list + :type if_generation_match: long + :param if_generation_match: (Optional) Make the operation conditional on whether + the blob's current generation matches the given value. + Setting to 0 makes the operation succeed only if there + are no live versions of the blob. + + :type if_generation_not_match: long + :param if_generation_not_match: (Optional) Make the operation conditional on whether + the blob's current generation does not match the given + value. If no live blob exists, the precondition fails. + Setting to 0 makes the operation succeed only if there + is a live version of the blob. + + :type if_metageneration_match: long + :param if_metageneration_match: (Optional) Make the operation conditional on whether the + blob's current metageneration matches the given value. + + :type if_metageneration_not_match: long + :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the + blob's current metageneration does not match the given value. + :rtype: :class:`~requests.Response` :returns: The "200 OK" response object returned after the multipart upload request. @@ -1029,6 +1067,20 @@ def _do_multipart_upload( if predefined_acl is not None: name_value_pairs.append(("predefinedAcl", predefined_acl)) + if if_generation_match is not None: + name_value_pairs.append(("ifGenerationMatch", if_generation_match)) + + if if_generation_not_match is not None: + name_value_pairs.append(("ifGenerationNotMatch", if_generation_not_match)) + + if if_metageneration_match is not None: + name_value_pairs.append(("ifMetagenerationMatch", if_metageneration_match)) + + if if_metageneration_not_match is not None: + name_value_pairs.append( + ("ifMetaGenerationNotMatch", if_metageneration_not_match) + ) + upload_url = _add_query_parameters(base_url, name_value_pairs) upload = MultipartUpload(upload_url, headers=headers) @@ -1051,6 +1103,10 @@ def _initiate_resumable_upload( predefined_acl=None, extra_headers=None, chunk_size=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, ): """Initiate a resumable upload. @@ -1094,6 +1150,27 @@ def _initiate_resumable_upload( If not passed, will fall back to the chunk size on the current blob. + :type if_generation_match: long + :param if_generation_match: (Optional) Make the operation conditional on whether + the blob's current generation matches the given value. + Setting to 0 makes the operation succeed only if there + are no live versions of the blob. + + :type if_generation_not_match: long + :param if_generation_not_match: (Optional) Make the operation conditional on whether + the blob's current generation does not match the given + value. If no live blob exists, the precondition fails. + Setting to 0 makes the operation succeed only if there + is a live version of the blob. + + :type if_metageneration_match: long + :param if_metageneration_match: (Optional) Make the operation conditional on whether the + blob's current metageneration matches the given value. + + :type if_metageneration_not_match: long + :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the + blob's current metageneration does not match the given value. + :rtype: tuple :returns: Pair of @@ -1125,6 +1202,20 @@ def _initiate_resumable_upload( if predefined_acl is not None: name_value_pairs.append(("predefinedAcl", predefined_acl)) + if if_generation_match is not None: + name_value_pairs.append(("ifGenerationMatch", if_generation_match)) + + if if_generation_not_match is not None: + name_value_pairs.append(("ifGenerationNotMatch", if_generation_not_match)) + + if if_metageneration_match is not None: + name_value_pairs.append(("ifMetagenerationMatch", if_metageneration_match)) + + if if_metageneration_not_match is not None: + name_value_pairs.append( + ("ifMetaGenerationNotMatch", if_metageneration_not_match) + ) + upload_url = _add_query_parameters(base_url, name_value_pairs) upload = ResumableUpload(upload_url, chunk_size, headers=headers) @@ -1145,7 +1236,17 @@ def _initiate_resumable_upload( return upload, transport def _do_resumable_upload( - self, client, stream, content_type, size, num_retries, predefined_acl + self, + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ): """Perform a resumable upload. @@ -1180,6 +1281,27 @@ def _do_resumable_upload( :type predefined_acl: str :param predefined_acl: (Optional) Predefined access control list + :type if_generation_match: long + :param if_generation_match: (Optional) Make the operation conditional on whether + the blob's current generation matches the given value. + Setting to 0 makes the operation succeed only if there + are no live versions of the blob. + + :type if_generation_not_match: long + :param if_generation_not_match: (Optional) Make the operation conditional on whether + the blob's current generation does not match the given + value. If no live blob exists, the precondition fails. + Setting to 0 makes the operation succeed only if there + is a live version of the blob. + + :type if_metageneration_match: long + :param if_metageneration_match: (Optional) Make the operation conditional on whether the + blob's current metageneration matches the given value. + + :type if_metageneration_not_match: long + :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the + blob's current metageneration does not match the given value. + :rtype: :class:`~requests.Response` :returns: The "200 OK" response object returned after the final chunk is uploaded. @@ -1191,6 +1313,10 @@ def _do_resumable_upload( size, num_retries, predefined_acl=predefined_acl, + if_generation_match=if_generation_match, + if_generation_not_match=if_generation_not_match, + if_metageneration_match=if_metageneration_match, + if_metageneration_not_match=if_metageneration_not_match, ) while not upload.finished: @@ -1199,7 +1325,17 @@ def _do_resumable_upload( return response def _do_upload( - self, client, stream, content_type, size, num_retries, predefined_acl + self, + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ): """Determine an upload strategy and then perform the upload. @@ -1236,6 +1372,27 @@ def _do_upload( :type predefined_acl: str :param predefined_acl: (Optional) Predefined access control list + :type if_generation_match: long + :param if_generation_match: (Optional) Make the operation conditional on whether + the blob's current generation matches the given value. + Setting to 0 makes the operation succeed only if there + are no live versions of the blob. + + :type if_generation_not_match: long + :param if_generation_not_match: (Optional) Make the operation conditional on whether + the blob's current generation does not match the given + value. If no live blob exists, the precondition fails. + Setting to 0 makes the operation succeed only if there + is a live version of the blob. + + :type if_metageneration_match: long + :param if_metageneration_match: (Optional) Make the operation conditional on whether the + blob's current metageneration matches the given value. + + :type if_metageneration_not_match: long + :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the + blob's current metageneration does not match the given value. + :rtype: dict :returns: The parsed JSON from the "200 OK" response. This will be the **only** response in the multipart case and it will be the @@ -1243,11 +1400,29 @@ def _do_upload( """ if size is not None and size <= _MAX_MULTIPART_SIZE: response = self._do_multipart_upload( - client, stream, content_type, size, num_retries, predefined_acl + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) else: response = self._do_resumable_upload( - client, stream, content_type, size, num_retries, predefined_acl + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) return response.json() @@ -1261,6 +1436,10 @@ def upload_from_file( num_retries=None, client=None, predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, ): """Upload the contents of this blob from a file-like object. @@ -1322,6 +1501,27 @@ def upload_from_file( :type predefined_acl: str :param predefined_acl: (Optional) Predefined access control list + :type if_generation_match: long + :param if_generation_match: (Optional) Make the operation conditional on whether + the blob's current generation matches the given value. + Setting to 0 makes the operation succeed only if there + are no live versions of the blob. + + :type if_generation_not_match: long + :param if_generation_not_match: (Optional) Make the operation conditional on whether + the blob's current generation does not match the given + value. If no live blob exists, the precondition fails. + Setting to 0 makes the operation succeed only if there + is a live version of the blob. + + :type if_metageneration_match: long + :param if_metageneration_match: (Optional) Make the operation conditional on whether the + blob's current metageneration matches the given value. + + :type if_metageneration_not_match: long + :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the + blob's current metageneration does not match the given value. + :raises: :class:`~google.cloud.exceptions.GoogleCloudError` if the upload response returns an error status. @@ -1332,19 +1532,46 @@ def upload_from_file( if num_retries is not None: warnings.warn(_NUM_RETRIES_MESSAGE, DeprecationWarning, stacklevel=2) + _raise_for_more_than_one_none( + if_generation_match=if_generation_match, + if_generation_not_match=if_generation_not_match, + ) + + _raise_for_more_than_one_none( + if_metageneration_match=if_metageneration_match, + if_metageneration_not_match=if_metageneration_not_match, + ) + _maybe_rewind(file_obj, rewind=rewind) predefined_acl = ACL.validate_predefined(predefined_acl) try: created_json = self._do_upload( - client, file_obj, content_type, size, num_retries, predefined_acl + client, + file_obj, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) self._set_properties(created_json) except resumable_media.InvalidResponse as exc: _raise_from_invalid_response(exc) def upload_from_filename( - self, filename, content_type=None, client=None, predefined_acl=None + self, + filename, + content_type=None, + client=None, + predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, ): """Upload this blob's contents from the content of a named file. @@ -1382,6 +1609,27 @@ def upload_from_filename( :type predefined_acl: str :param predefined_acl: (Optional) Predefined access control list + + :type if_generation_match: long + :param if_generation_match: (Optional) Make the operation conditional on whether + the blob's current generation matches the given value. + Setting to 0 makes the operation succeed only if there + are no live versions of the blob. + + :type if_generation_not_match: long + :param if_generation_not_match: (Optional) Make the operation conditional on whether + the blob's current generation does not match the given + value. If no live blob exists, the precondition fails. + Setting to 0 makes the operation succeed only if there + is a live version of the blob. + + :type if_metageneration_match: long + :param if_metageneration_match: (Optional) Make the operation conditional on whether the + blob's current metageneration matches the given value. + + :type if_metageneration_not_match: long + :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the + blob's current metageneration does not match the given value. """ content_type = self._get_content_type(content_type, filename=filename) @@ -1393,10 +1641,22 @@ def upload_from_filename( client=client, size=total_bytes, predefined_acl=predefined_acl, + if_generation_match=if_generation_match, + if_generation_not_match=if_generation_not_match, + if_metageneration_match=if_metageneration_match, + if_metageneration_not_match=if_metageneration_not_match, ) def upload_from_string( - self, data, content_type="text/plain", client=None, predefined_acl=None + self, + data, + content_type="text/plain", + client=None, + predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, ): """Upload contents of this blob from the provided string. @@ -1429,6 +1689,27 @@ def upload_from_string( :type predefined_acl: str :param predefined_acl: (Optional) Predefined access control list + + :type if_generation_match: long + :param if_generation_match: (Optional) Make the operation conditional on whether + the blob's current generation matches the given value. + Setting to 0 makes the operation succeed only if there + are no live versions of the blob. + + :type if_generation_not_match: long + :param if_generation_not_match: (Optional) Make the operation conditional on whether + the blob's current generation does not match the given + value. If no live blob exists, the precondition fails. + Setting to 0 makes the operation succeed only if there + is a live version of the blob. + + :type if_metageneration_match: long + :param if_metageneration_match: (Optional) Make the operation conditional on whether the + blob's current metageneration matches the given value. + + :type if_metageneration_not_match: long + :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the + blob's current metageneration does not match the given value. """ data = _to_bytes(data, encoding="utf-8") string_buffer = BytesIO(data) @@ -1438,6 +1719,10 @@ def upload_from_string( content_type=content_type, client=client, predefined_acl=predefined_acl, + if_generation_match=if_generation_match, + if_generation_not_match=if_generation_not_match, + if_metageneration_match=if_metageneration_match, + if_metageneration_not_match=if_metageneration_not_match, ) def create_resumable_upload_session( @@ -2346,3 +2631,24 @@ def _add_query_parameters(base_url, name_value_pairs): query = parse_qsl(query) query.extend(name_value_pairs) return urlunsplit((scheme, netloc, path, urlencode(query), frag)) + + +def _raise_for_more_than_one_none(**kwargs): + """Raise ``ValueError`` exception if more than one parameter was set. + + :type error: :exc:`ValueError` + :param error: Description of which fields were set + + :raises: :class:`~ValueError` containing the fields that were set + """ + if sum(arg is not None for arg in kwargs.values()) > 1: + escaped_keys = ["'%s'" % name for name in kwargs.keys()] + + keys_but_last = ", ".join(escaped_keys[:-1]) + last_key = escaped_keys[-1] + + msg = "Pass at most one of {keys_but_last} and {last_key}".format( + keys_but_last=keys_but_last, last_key=last_key + ) + + raise ValueError(msg) diff --git a/google/cloud/storage/bucket.py b/google/cloud/storage/bucket.py index c2a909357..69fa321bb 100644 --- a/google/cloud/storage/bucket.py +++ b/google/cloud/storage/bucket.py @@ -505,6 +505,10 @@ class Bucket(_PropertyMixin): """Allowed values for :attr:`location_type`.""" def __init__(self, client, name=None, user_project=None): + """ + property :attr:`name` + Get the bucket's name. + """ name = _validate_name(name) super(Bucket, self).__init__(name=name) self._client = client @@ -1268,7 +1272,8 @@ def copy_blob( to the ``client`` stored on the current bucket. :type preserve_acl: bool - :param preserve_acl: (Optional) Copies ACL from old blob to new blob. + :param preserve_acl: DEPRECATED. This argument is not functional! + (Optional) Copies ACL from old blob to new blob. Default: True. :type source_generation: long @@ -1284,6 +1289,20 @@ def copy_blob( :rtype: :class:`google.cloud.storage.blob.Blob` :returns: The new Blob. + + Example: + Copy a blob including ACL. + + >>> from google.cloud import storage + + >>> client = storage.Client(project="project") + + >>> bucket = client.bucket("bucket") + >>> dst_bucket = client.bucket("destination-bucket") + + >>> blob = bucket.blob("file.ext") + >>> new_blob = bucket.copy_blob(blob, dst_bucket) + >>> new_blob.acl.save(blob.acl) """ client = self._require_client(client) query_params = {} diff --git a/setup.py b/setup.py index c6bf342fe..3fb88d83e 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ name = "google-cloud-storage" description = "Google Cloud Storage API client library" -version = "1.27.0" +version = "1.28.0" # Should be one of: # 'Development Status :: 3 - Alpha' # 'Development Status :: 4 - Beta' diff --git a/tests/system/test_system.py b/tests/system/test_system.py index 2cb3dc0ab..675758794 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -33,6 +33,7 @@ from google.cloud.storage.bucket import LifecycleRuleDelete from google.cloud.storage.bucket import LifecycleRuleSetStorageClass from google.cloud import kms +import google.api_core import google.oauth2 from test_utils.retry import RetryErrors from test_utils.system import unique_resource_id @@ -710,6 +711,32 @@ def test_upload_gzip_encoded_download_raw(self): raw = blob.download_as_string(raw_download=True) self.assertEqual(raw, zipped) + def test_resumable_upload_with_generation_match(self): + blob = self.bucket.blob("LargeFile") + + # uploading the file + file_data = self.FILES["big"] + with open(file_data["path"], "rb") as file_obj: + blob.upload_from_file(file_obj) + self.case_blobs_to_delete.append(blob) + + # reuploading with correct generations numbers + with open(file_data["path"], "rb") as file_obj: + blob.upload_from_file( + file_obj, + if_generation_match=blob.generation, + if_metageneration_match=blob.metageneration, + ) + + # reuploading with generations numbers that doesn't match original + with self.assertRaises(google.api_core.exceptions.PreconditionFailed): + with open(file_data["path"], "rb") as file_obj: + blob.upload_from_file(file_obj, if_generation_match=3) + + with self.assertRaises(google.api_core.exceptions.PreconditionFailed): + with open(file_data["path"], "rb") as file_obj: + blob.upload_from_file(file_obj, if_metageneration_match=3) + class TestUnicode(unittest.TestCase): @vpcsc_config.skip_if_inside_vpcsc @@ -1702,11 +1729,18 @@ def test_rewrite_rotate_csek_to_cmek(self): class TestRetentionPolicy(unittest.TestCase): def setUp(self): self.case_buckets_to_delete = [] + self.case_blobs_to_delete = [] def tearDown(self): + # discard test blobs retention policy settings + for blob in self.case_blobs_to_delete: + blob.event_based_hold = False + blob.temporary_hold = False + blob.patch() + for bucket_name in self.case_buckets_to_delete: bucket = Config.CLIENT.bucket(bucket_name) - retry_429_harder(bucket.delete)() + retry_429_harder(bucket.delete)(force=True) def test_bucket_w_retention_period(self): import datetime @@ -1732,6 +1766,8 @@ def test_bucket_w_retention_period(self): blob = bucket.blob(blob_name) blob.upload_from_string(payload) + self.case_blobs_to_delete.append(blob) + other = bucket.get_blob(blob_name) self.assertFalse(other.event_based_hold) @@ -1756,6 +1792,7 @@ def test_bucket_w_retention_period(self): self.assertIsNone(other.retention_expiration_time) other.delete() + self.case_blobs_to_delete.pop() def test_bucket_w_default_event_based_hold(self): from google.api_core import exceptions @@ -1780,6 +1817,8 @@ def test_bucket_w_default_event_based_hold(self): blob = bucket.blob(blob_name) blob.upload_from_string(payload) + self.case_blobs_to_delete.append(blob) + other = bucket.get_blob(blob_name) self.assertTrue(other.event_based_hold) @@ -1791,7 +1830,6 @@ def test_bucket_w_default_event_based_hold(self): other.event_based_hold = False other.patch() - other.delete() bucket.default_event_based_hold = False @@ -1803,11 +1841,12 @@ def test_bucket_w_default_event_based_hold(self): self.assertFalse(bucket.retention_policy_locked) blob.upload_from_string(payload) - self.assertFalse(other.event_based_hold) - self.assertFalse(other.temporary_hold) - self.assertIsNone(other.retention_expiration_time) + self.assertFalse(blob.event_based_hold) + self.assertFalse(blob.temporary_hold) + self.assertIsNone(blob.retention_expiration_time) blob.delete() + self.case_blobs_to_delete.pop() def test_blob_w_temporary_hold(self): from google.api_core import exceptions @@ -1824,6 +1863,8 @@ def test_blob_w_temporary_hold(self): blob = bucket.blob(blob_name) blob.upload_from_string(payload) + self.case_blobs_to_delete.append(blob) + other = bucket.get_blob(blob_name) other.temporary_hold = True other.patch() @@ -1839,6 +1880,7 @@ def test_blob_w_temporary_hold(self): other.patch() other.delete() + self.case_blobs_to_delete.pop() def test_bucket_lock_retention_policy(self): import datetime diff --git a/tests/unit/test__signing.py b/tests/unit/test__signing.py index d1d2224e3..2907429a5 100644 --- a/tests/unit/test__signing.py +++ b/tests/unit/test__signing.py @@ -31,7 +31,7 @@ _SERVICE_ACCOUNT_JSON = _read_local_json("url_signer_v4_test_account.json") -_CONFORMANCE_TESTS = _read_local_json("url_signer_v4_test_data.json") +_CONFORMANCE_TESTS = _read_local_json("url_signer_v4_test_data.json")["signingV4Tests"] _CLIENT_TESTS = [test for test in _CONFORMANCE_TESTS if "bucket" not in test] _BUCKET_TESTS = [ test for test in _CONFORMANCE_TESTS if "bucket" in test and not test.get("object") @@ -252,7 +252,7 @@ def test_it(self): expected = { "GoogleAccessId": account_name, - "Expires": str(expiration), + "Expires": expiration, "Signature": base64.b64encode(sig_bytes), } self.assertEqual(result, expected) diff --git a/tests/unit/test_blob.py b/tests/unit/test_blob.py index f656e6441..4ae0e21b3 100644 --- a/tests/unit/test_blob.py +++ b/tests/unit/test_blob.py @@ -1060,7 +1060,7 @@ def test_download_to_file_w_chunks_w_raw(self): def _download_to_filename_helper(self, updated, raw_download): import os - import time + from google.cloud.storage._helpers import _convert_to_timestamp from google.cloud._testing import _NamedTemporaryFile blob_name = "blob-name" @@ -1080,7 +1080,10 @@ def _download_to_filename_helper(self, updated, raw_download): self.assertIsNone(blob.updated) else: mtime = os.path.getmtime(temp.name) - updated_time = time.mktime(blob.updated.timetuple()) + if six.PY2: + updated_time = _convert_to_timestamp(blob.updated) + else: + updated_time = blob.updated.timestamp() self.assertEqual(mtime, updated_time) headers = {"accept-encoding": "gzip"} @@ -1300,6 +1303,10 @@ def _do_multipart_success( num_retries=None, user_project=None, predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, kms_key_name=None, ): from six.moves.urllib.parse import urlencode @@ -1317,7 +1324,16 @@ def _do_multipart_success( stream = io.BytesIO(data) content_type = u"application/xml" response = blob._do_multipart_upload( - client, stream, content_type, size, num_retries, predefined_acl + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) # Check the mocks and the returned value. @@ -1346,6 +1362,18 @@ def _do_multipart_success( if kms_key_name is not None: qs_params.append(("kmsKeyName", kms_key_name)) + if if_generation_match is not None: + qs_params.append(("ifGenerationMatch", if_generation_match)) + + if if_generation_not_match is not None: + qs_params.append(("ifGenerationNotMatch", if_generation_not_match)) + + if if_metageneration_match is not None: + qs_params.append(("ifMetagenerationMatch", if_metageneration_match)) + + if if_metageneration_not_match is not None: + qs_params.append(("ifMetaGenerationNotMatch", if_metageneration_not_match)) + upload_url += "?" + urlencode(qs_params) payload = ( @@ -1389,6 +1417,18 @@ def test__do_multipart_upload_with_kms(self, mock_get_boundary): def test__do_multipart_upload_with_retry(self, mock_get_boundary): self._do_multipart_success(mock_get_boundary, num_retries=8) + @mock.patch(u"google.resumable_media._upload.get_boundary", return_value=b"==0==") + def test__do_multipart_upload_with_generation_match(self, mock_get_boundary): + self._do_multipart_success( + mock_get_boundary, if_generation_match=4, if_metageneration_match=4 + ) + + @mock.patch(u"google.resumable_media._upload.get_boundary", return_value=b"==0==") + def test__do_multipart_upload_with_generation_not_match(self, mock_get_boundary): + self._do_multipart_success( + mock_get_boundary, if_generation_not_match=4, if_metageneration_not_match=4 + ) + def test__do_multipart_upload_bad_size(self): blob = self._make_one(u"blob-name", bucket=None) @@ -1398,7 +1438,9 @@ def test__do_multipart_upload_bad_size(self): self.assertGreater(size, len(data)) with self.assertRaises(ValueError) as exc_info: - blob._do_multipart_upload(None, stream, None, size, None, None) + blob._do_multipart_upload( + None, stream, None, size, None, None, None, None, None, None + ) exc_contents = str(exc_info.exception) self.assertIn("was specified but the file-like object only had", exc_contents) @@ -1412,6 +1454,10 @@ def _initiate_resumable_helper( num_retries=None, user_project=None, predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, blob_chunk_size=786432, kms_key_name=None, ): @@ -1452,6 +1498,10 @@ def _initiate_resumable_helper( extra_headers=extra_headers, chunk_size=chunk_size, predefined_acl=predefined_acl, + if_generation_match=if_generation_match, + if_generation_not_match=if_generation_not_match, + if_metageneration_match=if_metageneration_match, + if_metageneration_not_match=if_metageneration_not_match, ) # Check the returned values. @@ -1471,6 +1521,18 @@ def _initiate_resumable_helper( if kms_key_name is not None: qs_params.append(("kmsKeyName", kms_key_name)) + if if_generation_match is not None: + qs_params.append(("ifGenerationMatch", if_generation_match)) + + if if_generation_not_match is not None: + qs_params.append(("ifGenerationNotMatch", if_generation_not_match)) + + if if_metageneration_match is not None: + qs_params.append(("ifMetagenerationMatch", if_metageneration_match)) + + if if_metageneration_not_match is not None: + qs_params.append(("ifMetaGenerationNotMatch", if_metageneration_not_match)) + upload_url += "?" + urlencode(qs_params) self.assertEqual(upload.upload_url, upload_url) @@ -1555,6 +1617,16 @@ def test__initiate_resumable_upload_with_extra_headers(self): def test__initiate_resumable_upload_with_retry(self): self._initiate_resumable_helper(num_retries=11) + def test__initiate_resumable_upload_with_generation_match(self): + self._initiate_resumable_helper( + if_generation_match=4, if_metageneration_match=4 + ) + + def test__initiate_resumable_upload_with_generation_not_match(self): + self._initiate_resumable_helper( + if_generation_not_match=4, if_metageneration_not_match=4 + ) + def test__initiate_resumable_upload_with_predefined_acl(self): self._initiate_resumable_helper(predefined_acl="private") @@ -1577,7 +1649,16 @@ def _make_resumable_transport(self, headers1, headers2, headers3, total_bytes): return fake_transport, responses @staticmethod - def _do_resumable_upload_call0(blob, content_type, size=None, predefined_acl=None): + def _do_resumable_upload_call0( + blob, + content_type, + size=None, + predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, + ): # First mock transport.request() does initiates upload. upload_url = ( "https://storage.googleapis.com/upload/storage/v1" @@ -1599,7 +1680,16 @@ def _do_resumable_upload_call0(blob, content_type, size=None, predefined_acl=Non @staticmethod def _do_resumable_upload_call1( - blob, content_type, data, resumable_url, size=None, predefined_acl=None + blob, + content_type, + data, + resumable_url, + size=None, + predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, ): # Second mock transport.request() does sends first chunk. if size is None: @@ -1622,7 +1712,16 @@ def _do_resumable_upload_call1( @staticmethod def _do_resumable_upload_call2( - blob, content_type, data, resumable_url, total_bytes, predefined_acl=None + blob, + content_type, + data, + resumable_url, + total_bytes, + predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, ): # Third mock transport.request() does sends last chunk. content_range = "bytes {:d}-{:d}/{:d}".format( @@ -1642,7 +1741,14 @@ def _do_resumable_upload_call2( ) def _do_resumable_helper( - self, use_size=False, num_retries=None, predefined_acl=None + self, + use_size=False, + num_retries=None, + predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, ): bucket = _Bucket(name="yesterday") blob = self._make_one(u"blob-name", bucket=bucket) @@ -1670,7 +1776,16 @@ def _do_resumable_helper( stream = io.BytesIO(data) content_type = u"text/html" response = blob._do_resumable_upload( - client, stream, content_type, size, num_retries, predefined_acl + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) # Check the returned values. @@ -1679,7 +1794,14 @@ def _do_resumable_helper( # Check the mocks. call0 = self._do_resumable_upload_call0( - blob, content_type, size=size, predefined_acl=predefined_acl + blob, + content_type, + size=size, + predefined_acl=predefined_acl, + if_generation_match=if_generation_match, + if_generation_not_match=if_generation_not_match, + if_metageneration_match=if_metageneration_match, + if_metageneration_not_match=if_metageneration_not_match, ) call1 = self._do_resumable_upload_call1( blob, @@ -1688,6 +1810,10 @@ def _do_resumable_helper( resumable_url, size=size, predefined_acl=predefined_acl, + if_generation_match=if_generation_match, + if_generation_not_match=if_generation_not_match, + if_metageneration_match=if_metageneration_match, + if_metageneration_not_match=if_metageneration_not_match, ) call2 = self._do_resumable_upload_call2( blob, @@ -1696,6 +1822,10 @@ def _do_resumable_helper( resumable_url, total_bytes, predefined_acl=predefined_acl, + if_generation_match=if_generation_match, + if_generation_not_match=if_generation_not_match, + if_metageneration_match=if_metageneration_match, + if_metageneration_not_match=if_metageneration_not_match, ) self.assertEqual(transport.request.mock_calls, [call0, call1, call2]) @@ -1712,7 +1842,15 @@ def test__do_resumable_upload_with_predefined_acl(self): self._do_resumable_helper(predefined_acl="private") def _do_upload_helper( - self, chunk_size=None, num_retries=None, predefined_acl=None, size=None + self, + chunk_size=None, + num_retries=None, + predefined_acl=None, + if_generation_match=None, + if_generation_not_match=None, + if_metageneration_match=None, + if_metageneration_not_match=None, + size=None, ): from google.cloud.storage.blob import _MAX_MULTIPART_SIZE @@ -1738,19 +1876,46 @@ def _do_upload_helper( size = 12345654321 # Make the request and check the mocks. created_json = blob._do_upload( - client, stream, content_type, size, num_retries, predefined_acl + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) self.assertIs(created_json, mock.sentinel.json) response.json.assert_called_once_with() if size is not None and size <= _MAX_MULTIPART_SIZE: blob._do_multipart_upload.assert_called_once_with( - client, stream, content_type, size, num_retries, predefined_acl + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) blob._do_resumable_upload.assert_not_called() else: blob._do_multipart_upload.assert_not_called() blob._do_resumable_upload.assert_called_once_with( - client, stream, content_type, size, num_retries, predefined_acl + client, + stream, + content_type, + size, + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) def test__do_upload_uses_multipart(self): @@ -1785,6 +1950,10 @@ def _upload_from_file_helper(self, side_effect=None, **kwargs): content_type = u"font/woff" client = mock.sentinel.client predefined_acl = kwargs.get("predefined_acl", None) + if_generation_match = kwargs.get("if_generation_match", None) + if_generation_not_match = kwargs.get("if_generation_not_match", None) + if_metageneration_match = kwargs.get("if_metageneration_match", None) + if_metageneration_not_match = kwargs.get("if_metageneration_not_match", None) ret_val = blob.upload_from_file( stream, size=len(data), content_type=content_type, client=client, **kwargs ) @@ -1797,7 +1966,16 @@ def _upload_from_file_helper(self, side_effect=None, **kwargs): # Check the mock. num_retries = kwargs.get("num_retries") blob._do_upload.assert_called_once_with( - client, stream, content_type, len(data), num_retries, predefined_acl + client, + stream, + content_type, + len(data), + num_retries, + predefined_acl, + if_generation_match, + if_generation_not_match, + if_metageneration_match, + if_metageneration_not_match, ) return stream @@ -1841,12 +2019,16 @@ def _do_upload_mock_call_helper(self, blob, client, content_type, size): mock_call = blob._do_upload.mock_calls[0] call_name, pos_args, kwargs = mock_call self.assertEqual(call_name, "") - self.assertEqual(len(pos_args), 6) + self.assertEqual(len(pos_args), 10) self.assertEqual(pos_args[0], client) self.assertEqual(pos_args[2], content_type) self.assertEqual(pos_args[3], size) self.assertIsNone(pos_args[4]) # num_retries self.assertIsNone(pos_args[5]) # predefined_acl + self.assertIsNone(pos_args[6]) # if_generation_match + self.assertIsNone(pos_args[7]) # if_generation_not_match + self.assertIsNone(pos_args[8]) # if_metageneration_match + self.assertIsNone(pos_args[9]) # if_metageneration_not_match self.assertEqual(kwargs, {}) return pos_args[1] diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 8673bcfd0..63eeb370c 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -26,7 +26,9 @@ from . import _read_local_json _SERVICE_ACCOUNT_JSON = _read_local_json("url_signer_v4_test_account.json") -_CONFORMANCE_TESTS = _read_local_json("url_signer_v4_test_data.json") +_CONFORMANCE_TESTS = _read_local_json("url_signer_v4_test_data.json")[ + "postPolicyV4Tests" +] _POST_POLICY_TESTS = [test for test in _CONFORMANCE_TESTS if "policyInput" in test] _DUMMY_CREDENTIALS = Credentials.from_service_account_info(_SERVICE_ACCOUNT_JSON) diff --git a/tests/unit/url_signer_v4_test_data.json b/tests/unit/url_signer_v4_test_data.json index 686459b1e..5bcca0994 100644 --- a/tests/unit/url_signer_v4_test_data.json +++ b/tests/unit/url_signer_v4_test_data.json @@ -1,453 +1,457 @@ -[ - { - "description": "Simple GET", - "bucket": "test-bucket", - "object": "test-object", - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=95e6a13d43a1d1962e667f17397f2b80ac9bdd1669210d5e08e0135df9dff4e56113485dbe429ca2266487b9d1796ebdee2d7cf682a6ef3bb9fbb4c351686fba90d7b621cf1c4eb1fdf126460dd25fa0837dfdde0a9fd98662ce60844c458448fb2b352c203d9969cb74efa4bdb742287744a4f2308afa4af0e0773f55e32e92973619249214b97283b2daa14195244444e33f938138d1e5f561088ce8011f4986dda33a556412594db7c12fc40e1ff3f1bedeb7a42f5bcda0b9567f17f65855f65071fabb88ea12371877f3f77f10e1466fff6ff6973b74a933322ff0949ce357e20abe96c3dd5cfab42c9c83e740a4d32b9e11e146f0eb3404d2e975896f74" - }, - - { - "description": "Simple PUT", - "bucket": "test-bucket", - "object": "test-object", - "method": "PUT", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=8adff1d4285739e31aa68e73767a46bc5511fde377497dbe08481bf5ceb34e29cc9a59921748d8ec3dd4085b7e9b7772a952afedfcdaecb3ae8352275b8b7c867f204e3db85076220a3127a8a9589302fc1181eae13b9b7fe41109ec8cdc93c1e8bac2d7a0cc32a109ca02d06957211326563ab3d3e678a0ba296e298b5fc5e14593c99d444c94724cc4be97015dbff1dca377b508fa0cb7169195de98d0e4ac96c42b918d28c8d92d33e1bd125ce0fb3cd7ad2c45dae65c22628378f6584971b8bf3945b26f2611eb651e9b6a8648970c1ecf386bb71327b082e7296c4e1ee2fc0bdd8983da80af375c817fb1ad491d0bc22c0f51dba0d66e2cffbc90803e47" - }, - - { - "description": "POST for resumable uploads", - "bucket": "test-bucket", - "object": "test-object", - "method": "POST", - "expiration": 10, - "headers": { - "x-goog-resumable": "start" +{ + "signingV4Tests": [ + { + "description": "Simple GET", + "bucket": "test-bucket", + "object": "test-object", + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=95e6a13d43a1d1962e667f17397f2b80ac9bdd1669210d5e08e0135df9dff4e56113485dbe429ca2266487b9d1796ebdee2d7cf682a6ef3bb9fbb4c351686fba90d7b621cf1c4eb1fdf126460dd25fa0837dfdde0a9fd98662ce60844c458448fb2b352c203d9969cb74efa4bdb742287744a4f2308afa4af0e0773f55e32e92973619249214b97283b2daa14195244444e33f938138d1e5f561088ce8011f4986dda33a556412594db7c12fc40e1ff3f1bedeb7a42f5bcda0b9567f17f65855f65071fabb88ea12371877f3f77f10e1466fff6ff6973b74a933322ff0949ce357e20abe96c3dd5cfab42c9c83e740a4d32b9e11e146f0eb3404d2e975896f74" }, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host%3Bx-goog-resumable&X-Goog-Signature=4a6d39b23343cedf4c30782aed4b384001828c79ffa3a080a481ea01a640dea0a0ceb58d67a12cef3b243c3f036bb3799c6ee88e8db3eaf7d0bdd4b70a228d0736e07eaa1ee076aff5c6ce09dff1f1f03a0d8ead0d2893408dd3604fdabff553aa6d7af2da67cdba6790006a70240f96717b98f1a6ccb24f00940749599be7ef72aaa5358db63ddd54b2de9e2d6d6a586eac4fe25f36d86fc6ab150418e9c6fa01b732cded226c6d62fc95b72473a4cc55a8257482583fe66d9ab6ede909eb41516a8690946c3e87b0f2052eb0e97e012a14b2f721c42e6e19b8a1cd5658ea36264f10b9b1ada66b8ed5bf7ed7d1708377ac6e5fe608ae361fb594d2e5b24c54" - }, - { - "description": "Vary expiration and timestamp", - "bucket": "test-bucket", - "object": "test-object", - "method": "GET", - "expiration": 20, - "timestamp": "20190301T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190301%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190301T090000Z&X-Goog-Expires=20&X-Goog-SignedHeaders=host&X-Goog-Signature=9669ed5b10664dc594c758296580662912cf4bcc5a4ba0b6bf055bcbf6f34eed7bdad664f534962174a924741a0c273a4f67bc1847cef20192a6beab44223bd9d4fbbd749c407b79997598c30f82ddc269ff47ec09fa3afe74e00616d438df0d96a7d8ad0adacfad1dc3286f864d924fe919fb0dce45d3d975c5afe8e13af2db9cc37ba77835f92f7669b61e94c6d562196c1274529e76cfff1564cc2cad7d5387dc8e12f7a5dfd925685fe92c30b43709eee29fa2f66067472cee5423d1a3a4182fe8cea75c9329d181dc6acad7c393cd04f8bf5bc0515127d8ebd65d80c08e19ad03316053ea60033fd1b1fd85a69c576415da3bf0a3718d9ea6d03e0d66f0" - }, + { + "description": "Simple PUT", + "bucket": "test-bucket", + "object": "test-object", + "method": "PUT", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=8adff1d4285739e31aa68e73767a46bc5511fde377497dbe08481bf5ceb34e29cc9a59921748d8ec3dd4085b7e9b7772a952afedfcdaecb3ae8352275b8b7c867f204e3db85076220a3127a8a9589302fc1181eae13b9b7fe41109ec8cdc93c1e8bac2d7a0cc32a109ca02d06957211326563ab3d3e678a0ba296e298b5fc5e14593c99d444c94724cc4be97015dbff1dca377b508fa0cb7169195de98d0e4ac96c42b918d28c8d92d33e1bd125ce0fb3cd7ad2c45dae65c22628378f6584971b8bf3945b26f2611eb651e9b6a8648970c1ecf386bb71327b082e7296c4e1ee2fc0bdd8983da80af375c817fb1ad491d0bc22c0f51dba0d66e2cffbc90803e47" + }, - { - "description": "Vary bucket and object", - "bucket": "test-bucket2", - "object": "test-object2", - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket2/test-object2?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=36e3d58dfd3ec1d2dd2f24b5ee372a71e811ffaa2162a2b871d26728d0354270bc116face87127532969c4a3967ed05b7309af741e19c7202f3167aa8c2ac420b61417d6451442bb91d7c822cd17be8783f01e05372769c88913561d27e6660dd8259f0081a71f831be6c50283626cbf04494ac10c394b29bb3bce74ab91548f58a37118a452693cf0483d77561fc9cac8f1765d2c724994cca46a83517a10157ee0347a233a2aaeae6e6ab5e204ff8fc5f54f90a3efdb8301d9fff5475d58cd05b181affd657f48203f4fb133c3a3d355b8eefbd10d5a0a5fd70d06e9515460ad74e22334b2cba4b29cae4f6f285cdb92d8f3126d7a1479ca3bdb69c207d860" - }, + { + "description": "POST for resumable uploads", + "bucket": "test-bucket", + "object": "test-object", + "method": "POST", + "expiration": 10, + "headers": { + "x-goog-resumable": "start" + }, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host%3Bx-goog-resumable&X-Goog-Signature=4a6d39b23343cedf4c30782aed4b384001828c79ffa3a080a481ea01a640dea0a0ceb58d67a12cef3b243c3f036bb3799c6ee88e8db3eaf7d0bdd4b70a228d0736e07eaa1ee076aff5c6ce09dff1f1f03a0d8ead0d2893408dd3604fdabff553aa6d7af2da67cdba6790006a70240f96717b98f1a6ccb24f00940749599be7ef72aaa5358db63ddd54b2de9e2d6d6a586eac4fe25f36d86fc6ab150418e9c6fa01b732cded226c6d62fc95b72473a4cc55a8257482583fe66d9ab6ede909eb41516a8690946c3e87b0f2052eb0e97e012a14b2f721c42e6e19b8a1cd5658ea36264f10b9b1ada66b8ed5bf7ed7d1708377ac6e5fe608ae361fb594d2e5b24c54" + }, - { - "description": "Simple headers", - "bucket": "test-bucket", - "object": "test-object", - "headers": { - "foo": "foo-value", - "BAR": "BAR-value" + { + "description": "Vary expiration and timestamp", + "bucket": "test-bucket", + "object": "test-object", + "method": "GET", + "expiration": 20, + "timestamp": "20190301T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190301%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190301T090000Z&X-Goog-Expires=20&X-Goog-SignedHeaders=host&X-Goog-Signature=9669ed5b10664dc594c758296580662912cf4bcc5a4ba0b6bf055bcbf6f34eed7bdad664f534962174a924741a0c273a4f67bc1847cef20192a6beab44223bd9d4fbbd749c407b79997598c30f82ddc269ff47ec09fa3afe74e00616d438df0d96a7d8ad0adacfad1dc3286f864d924fe919fb0dce45d3d975c5afe8e13af2db9cc37ba77835f92f7669b61e94c6d562196c1274529e76cfff1564cc2cad7d5387dc8e12f7a5dfd925685fe92c30b43709eee29fa2f66067472cee5423d1a3a4182fe8cea75c9329d181dc6acad7c393cd04f8bf5bc0515127d8ebd65d80c08e19ad03316053ea60033fd1b1fd85a69c576415da3bf0a3718d9ea6d03e0d66f0" }, - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=bar%3Bfoo%3Bhost&X-Goog-Signature=68ecd3b008328ed30d91e2fe37444ed7b9b03f28ed4424555b5161980531ef87db1c3a5bc0265aad5640af30f96014c94fb2dba7479c41bfe1c020eb90c0c6d387d4dd09d4a5df8b60ea50eb6b01cdd786a1e37020f5f95eb8f9b6cd3f65a1f8a8a65c9fcb61ea662959efd9cd73b683f8d8804ef4d6d9b2852419b013368842731359d7f9e6d1139032ceca75d5e67cee5fd0192ea2125e5f2955d38d3d50cf116f3a52e6a62de77f6207f5b95aaa1d7d0f8a46de89ea72e7ea30f21286318d7eba0142232b0deb3a1dc9e1e812a981c66b5ffda3c6b01a8a9d113155792309fd53a3acfd054ca7776e8eec28c26480cd1e3c812f67f91d14217f39a606669d" - }, - { - "description": "Headers should be trimmed", - "bucket": "test-bucket", - "object": "test-object", - "headers": { - "leading": " xyz", - "trailing": "abc ", - "collapsed": "abc def" + { + "description": "Vary bucket and object", + "bucket": "test-bucket2", + "object": "test-object2", + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket2/test-object2?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=36e3d58dfd3ec1d2dd2f24b5ee372a71e811ffaa2162a2b871d26728d0354270bc116face87127532969c4a3967ed05b7309af741e19c7202f3167aa8c2ac420b61417d6451442bb91d7c822cd17be8783f01e05372769c88913561d27e6660dd8259f0081a71f831be6c50283626cbf04494ac10c394b29bb3bce74ab91548f58a37118a452693cf0483d77561fc9cac8f1765d2c724994cca46a83517a10157ee0347a233a2aaeae6e6ab5e204ff8fc5f54f90a3efdb8301d9fff5475d58cd05b181affd657f48203f4fb133c3a3d355b8eefbd10d5a0a5fd70d06e9515460ad74e22334b2cba4b29cae4f6f285cdb92d8f3126d7a1479ca3bdb69c207d860" }, - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=collapsed%3Bhost%3Bleading%3Btrailing&X-Goog-Signature=1839511d6238d9ac2bbcbba8b23515b3757db35dfa7b8f9bc4b8b4aa270224df747c812526f1a3bcf294d67ed84cd14e074c36bc090e0a542782934a7c925af4a5ea68123e97533704ce8b08ccdf5fe6b412f89c9fc4de243e29abdb098382c5672188ee3f6fef7131413e252c78e7a35658825ad842a50609e9cc463731e17284ff7a14824c989f87cef22fb99dfec20cfeed69d8b3a08f00b43b8284eecd535e50e982b05cd74c5750cd5f986cfc21a2a05f7f3ab7fc31bd684ed1b823b64d29281e923fc6580c49005552ca19c253de087d9d2df881144e44eda40965cfdb4889bf3a35553c9809f4ed20b8355be481b92b9618952b6a04f3017b36053e15" - }, - { - "description": "Header value with multiple inline values", - "bucket": "test-bucket", - "object": "test-object", - "headers": { - "multiple": " xyz , abc, def , xyz " + { + "description": "Simple headers", + "bucket": "test-bucket", + "object": "test-object", + "headers": { + "foo": "foo-value", + "BAR": "BAR-value" + }, + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=bar%3Bfoo%3Bhost&X-Goog-Signature=68ecd3b008328ed30d91e2fe37444ed7b9b03f28ed4424555b5161980531ef87db1c3a5bc0265aad5640af30f96014c94fb2dba7479c41bfe1c020eb90c0c6d387d4dd09d4a5df8b60ea50eb6b01cdd786a1e37020f5f95eb8f9b6cd3f65a1f8a8a65c9fcb61ea662959efd9cd73b683f8d8804ef4d6d9b2852419b013368842731359d7f9e6d1139032ceca75d5e67cee5fd0192ea2125e5f2955d38d3d50cf116f3a52e6a62de77f6207f5b95aaa1d7d0f8a46de89ea72e7ea30f21286318d7eba0142232b0deb3a1dc9e1e812a981c66b5ffda3c6b01a8a9d113155792309fd53a3acfd054ca7776e8eec28c26480cd1e3c812f67f91d14217f39a606669d" }, - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host%3Bmultiple&X-Goog-Signature=5cc113735625341f59c7203f0c2c9febc95ba6af6b9c38814f8e523214712087dc0996e4960d273ae1889f248ac1e58d4d19cb3a69ad7670e9a8ca1b434e878f59339dc7006cf32dfd715337e9f593e0504371839174962a08294586e0c78160a7aa303397888c8350637c6af3b32ac310886cc4590bfda9ca561ee58fb5b8ec56bc606d2ada6e7df31f4276e9dcb96bcaea39dc2cd096f3fad774f9c4b30e317ad43736c05f76831437f44e8726c1e90d3f6c9827dc273f211f32fc85658dfc5d357eb606743a6b00a29e519eef1bebaf9db3e8f4b1f5f9afb648ad06e60bc42fa8b57025056697c874c9ea76f5a73201c9717ea43e54713ff3502ff3fc626b" - }, - { - "description": "Customer-supplied encryption key", - "bucket": "test-bucket", - "object": "test-object", - "headers": { - "X-Goog-Encryption-Key": "key", - "X-Goog-Encryption-Key-Sha256": "key-hash", - "X-Goog-Encryption-Algorithm": "AES256" + "description": "Headers should be trimmed", + "bucket": "test-bucket", + "object": "test-object", + "headers": { + "leading": " xyz", + "trailing": "abc ", + "collapsed": "abc def" + }, + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=collapsed%3Bhost%3Bleading%3Btrailing&X-Goog-Signature=1839511d6238d9ac2bbcbba8b23515b3757db35dfa7b8f9bc4b8b4aa270224df747c812526f1a3bcf294d67ed84cd14e074c36bc090e0a542782934a7c925af4a5ea68123e97533704ce8b08ccdf5fe6b412f89c9fc4de243e29abdb098382c5672188ee3f6fef7131413e252c78e7a35658825ad842a50609e9cc463731e17284ff7a14824c989f87cef22fb99dfec20cfeed69d8b3a08f00b43b8284eecd535e50e982b05cd74c5750cd5f986cfc21a2a05f7f3ab7fc31bd684ed1b823b64d29281e923fc6580c49005552ca19c253de087d9d2df881144e44eda40965cfdb4889bf3a35553c9809f4ed20b8355be481b92b9618952b6a04f3017b36053e15" }, - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host%3Bx-goog-encryption-algorithm%3Bx-goog-encryption-key%3Bx-goog-encryption-key-sha256&X-Goog-Signature=278a1c5a3bad248637054a047014760353942433955871031ed08f515b54588654ad033e91f046ab202b68673030e117d1b786c325e870238b035ba75b3feed560a17aff9bab6bddebd4a31a52cb68b214e27d3b0bd886502c6b36b164306fe88b5a07c6063592afe746b2a5d205dbe90dd5386b94f0a78f75d9f53ee884e18f476e8fc2eb1dd910ce0b4ae1f5d7b09876ef9bf983f539c028429e14bad3c75dbd4ed1ae37856f6d6f8a1805eaf8b52a0d6fc993902e4c1ee8de477661f7b67c3663000474cb00e178189789b2a3ed6bd21b4ade684fca8108ac4dd106acb17f5954d045775f7aa5a98ebda5d3075e11a8ea49c64c6ad1481e463e8c9f11f704" - }, - { - "description": "List Objects", - "bucket": "test-bucket", - "object": "", - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://storage.googleapis.com/test-bucket?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=6dbe94f8e52b2b8a9a476b1c857efa474e09944e2b52b925800316e094a7169d8dbe0df9c0ac08dabb22ac7e827470ceccd65f5a3eadba2a4fb9beebfe37f0d9bb1e552b851fa31a25045bdf019e507f5feb44f061551ef1aeb18dcec0e38ba2e2f77d560a46eaace9c56ed9aa642281301a9d848b0eb30749e34bc7f73a3d596240533466ff9b5f289cd0d4c845c7d96b82a35a5abd0c3aff83e4440ee6873e796087f43545544dc8c01afe1d79c726696b6f555371e491980e7ec145cca0803cf562c38f3fa1d724242f5dea25aac91d74ec9ddd739ff65523627763eaef25cd1f95ad985aaf0079b7c74eb5bcb2870a9b137a7b2c8e41fbe838c95872f75b" - }, + { + "description": "Header value with multiple inline values", + "bucket": "test-bucket", + "object": "test-object", + "headers": { + "multiple": " xyz , abc, def , xyz " + }, + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host%3Bmultiple&X-Goog-Signature=5cc113735625341f59c7203f0c2c9febc95ba6af6b9c38814f8e523214712087dc0996e4960d273ae1889f248ac1e58d4d19cb3a69ad7670e9a8ca1b434e878f59339dc7006cf32dfd715337e9f593e0504371839174962a08294586e0c78160a7aa303397888c8350637c6af3b32ac310886cc4590bfda9ca561ee58fb5b8ec56bc606d2ada6e7df31f4276e9dcb96bcaea39dc2cd096f3fad774f9c4b30e317ad43736c05f76831437f44e8726c1e90d3f6c9827dc273f211f32fc85658dfc5d357eb606743a6b00a29e519eef1bebaf9db3e8f4b1f5f9afb648ad06e60bc42fa8b57025056697c874c9ea76f5a73201c9717ea43e54713ff3502ff3fc626b" + }, - { - "description": "HTTP Bucket Bound Hostname Support", - "bucket": "test-bucket", - "object": "test-object", - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "http://mydomain.tld/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=7115a77f8c7ed1a8b74bca8b520904fca7f3bab90d69ea052687a94efd9b3a4e2a7fb7135d40e295e0a21958194c55da7e106227957c22ed6edc9d8b3d2a8133bc8af84fc9695dda8081d53f0db5ea9f28e5bfc225d78f873e9f571fd287bb7a95330e726aebd8eb4623cdb0b1a7ceb210b2ce1351b6be0191c2ad7b38f7ceb6c5ce2f98dbfb5a5a649050585e46e97f72f1f5407de657a56e34a3fdc80cdaa0598bd47f3e8af5ff22d0916b19b106890bff8c3f6587f1d3b076b16cd0ba0508607a672be33b9c75d537e15258450b43d22a21c4d528090acbb8e5bae7b31fc394e61394106ef1d6a8ed43074ab05bcec65674cd8113fb3de388da4d97e62f56", - "scheme": "http", - "urlStyle": "BUCKET_BOUND_HOSTNAME", - "bucketBoundHostname": "mydomain.tld" - }, + { + "description": "Customer-supplied encryption key", + "bucket": "test-bucket", + "object": "test-object", + "headers": + { + "X-Goog-Encryption-Key": "key", + "X-Goog-Encryption-Key-Sha256": "key-hash", + "X-Goog-Encryption-Algorithm": "AES256" + }, + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host%3Bx-goog-encryption-algorithm%3Bx-goog-encryption-key%3Bx-goog-encryption-key-sha256&X-Goog-Signature=278a1c5a3bad248637054a047014760353942433955871031ed08f515b54588654ad033e91f046ab202b68673030e117d1b786c325e870238b035ba75b3feed560a17aff9bab6bddebd4a31a52cb68b214e27d3b0bd886502c6b36b164306fe88b5a07c6063592afe746b2a5d205dbe90dd5386b94f0a78f75d9f53ee884e18f476e8fc2eb1dd910ce0b4ae1f5d7b09876ef9bf983f539c028429e14bad3c75dbd4ed1ae37856f6d6f8a1805eaf8b52a0d6fc993902e4c1ee8de477661f7b67c3663000474cb00e178189789b2a3ed6bd21b4ade684fca8108ac4dd106acb17f5954d045775f7aa5a98ebda5d3075e11a8ea49c64c6ad1481e463e8c9f11f704" + }, - { - "description": "HTTPS Bucket Bound Hostname Support", - "bucket": "test-bucket", - "object": "test-object", - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://mydomain.tld/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=7115a77f8c7ed1a8b74bca8b520904fca7f3bab90d69ea052687a94efd9b3a4e2a7fb7135d40e295e0a21958194c55da7e106227957c22ed6edc9d8b3d2a8133bc8af84fc9695dda8081d53f0db5ea9f28e5bfc225d78f873e9f571fd287bb7a95330e726aebd8eb4623cdb0b1a7ceb210b2ce1351b6be0191c2ad7b38f7ceb6c5ce2f98dbfb5a5a649050585e46e97f72f1f5407de657a56e34a3fdc80cdaa0598bd47f3e8af5ff22d0916b19b106890bff8c3f6587f1d3b076b16cd0ba0508607a672be33b9c75d537e15258450b43d22a21c4d528090acbb8e5bae7b31fc394e61394106ef1d6a8ed43074ab05bcec65674cd8113fb3de388da4d97e62f56", - "scheme": "https", - "urlStyle": "BUCKET_BOUND_HOSTNAME", - "bucketBoundHostname": "mydomain.tld" - }, + { + "description": "List Objects", + "bucket": "test-bucket", + "object": "", + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://storage.googleapis.com/test-bucket?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=6dbe94f8e52b2b8a9a476b1c857efa474e09944e2b52b925800316e094a7169d8dbe0df9c0ac08dabb22ac7e827470ceccd65f5a3eadba2a4fb9beebfe37f0d9bb1e552b851fa31a25045bdf019e507f5feb44f061551ef1aeb18dcec0e38ba2e2f77d560a46eaace9c56ed9aa642281301a9d848b0eb30749e34bc7f73a3d596240533466ff9b5f289cd0d4c845c7d96b82a35a5abd0c3aff83e4440ee6873e796087f43545544dc8c01afe1d79c726696b6f555371e491980e7ec145cca0803cf562c38f3fa1d724242f5dea25aac91d74ec9ddd739ff65523627763eaef25cd1f95ad985aaf0079b7c74eb5bcb2870a9b137a7b2c8e41fbe838c95872f75b" + }, - { + { "description": "HTTP Bucket Bound Hostname Support", "bucket": "test-bucket", + "object": "test-object", "method": "GET", "expiration": 10, "timestamp": "20190201T090000Z", - "expectedUrl": "http://mydomain.tld/?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=7a629a5632f16dba78961250b17c1f0d2ac0d2a28dbd7cbf79088fd6cd0b7f3ec66285cdeccca024f7b8134376f5cdcf0d60f399c6df1f19fcf5cf3be9d7f905d72cb6c0b5600f83dd6a7c8df607510c0e12e36216530a7b832eab87920363c5368a7e610d44005c73324f6ca4b435e8687672f46cc1342419ec4a5264549cb4b77bdc73f4f461edf39fbdd8fda99db440b077e906ef48d2c6b854c11ded58096f293d664650c123c6ec2a0379affd05bf5696ba11d3474623e039d5e05d3dc331b86ff4f7afb9262cf9750ff5944e661e70cc443b28f7e150796dde831d70e205c7e848c19b8281510f1d195e5819176e4868713266d0e0db7a3354857187cf", + "expectedUrl": "http://mydomain.tld/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=7115a77f8c7ed1a8b74bca8b520904fca7f3bab90d69ea052687a94efd9b3a4e2a7fb7135d40e295e0a21958194c55da7e106227957c22ed6edc9d8b3d2a8133bc8af84fc9695dda8081d53f0db5ea9f28e5bfc225d78f873e9f571fd287bb7a95330e726aebd8eb4623cdb0b1a7ceb210b2ce1351b6be0191c2ad7b38f7ceb6c5ce2f98dbfb5a5a649050585e46e97f72f1f5407de657a56e34a3fdc80cdaa0598bd47f3e8af5ff22d0916b19b106890bff8c3f6587f1d3b076b16cd0ba0508607a672be33b9c75d537e15258450b43d22a21c4d528090acbb8e5bae7b31fc394e61394106ef1d6a8ed43074ab05bcec65674cd8113fb3de388da4d97e62f56", "scheme": "http", "urlStyle": "BUCKET_BOUND_HOSTNAME", "bucketBoundHostname": "mydomain.tld" - }, - - { - "description": "HTTPS Bucket Bound Hostname Support", - "bucket": "test-bucket", - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://mydomain.tld/?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=7a629a5632f16dba78961250b17c1f0d2ac0d2a28dbd7cbf79088fd6cd0b7f3ec66285cdeccca024f7b8134376f5cdcf0d60f399c6df1f19fcf5cf3be9d7f905d72cb6c0b5600f83dd6a7c8df607510c0e12e36216530a7b832eab87920363c5368a7e610d44005c73324f6ca4b435e8687672f46cc1342419ec4a5264549cb4b77bdc73f4f461edf39fbdd8fda99db440b077e906ef48d2c6b854c11ded58096f293d664650c123c6ec2a0379affd05bf5696ba11d3474623e039d5e05d3dc331b86ff4f7afb9262cf9750ff5944e661e70cc443b28f7e150796dde831d70e205c7e848c19b8281510f1d195e5819176e4868713266d0e0db7a3354857187cf", - "scheme": "https", - "urlStyle": "BUCKET_BOUND_HOSTNAME", - "bucketBoundHostname": "mydomain.tld" - }, - - { - "description": "Virtual Hosted Style", - "bucket": "test-bucket", - "object": "test-object", - "method": "GET", - "expiration": 10, - "timestamp": "20190201T090000Z", - "expectedUrl": "https://test-bucket.storage.googleapis.com/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=06c633ea060b0eda81ee58dd3337b01b0d243a44f18cb03ec948861f533a129e579c7fd4c856d187f1c7b86e5715ea0abf6a1c6ba32b69274d22b1b0406df6847dc87f0d289fe8dc0682351574849b8b13e4b66922f39441af96becb73ea4c56cd5e3eeb30bc91fe84e8bd205adca8639253bdb65b2fcaf2598a230c6d8f6d8177c9e58a61b6e826767f594056b490184d676897c4bbedc15d6fbf08c3fa82a406c62e74db661e6c5d7d3ced29e0619ee719dce4b8136360345b8dce120b9f1debd511c8dac3e6d874ee05bfda8c8f1c4fedd0c07fc6d98f5f18a349bb204d8ff401402a025194e2792df8a09282141157e4ca51d26a8d0d142a01c805321911", - "scheme": "https", - "urlStyle": "VIRTUAL_HOSTED_STYLE", - "expectedCanonicalRequest": "GET\n/test-object\nX-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host\nhost:test-bucket.storage.googleapis.com\n\nhost\nUNSIGNED-PAYLOAD", - "expectedStringToSign": "GOOG4-RSA-SHA256\n20190201T090000Z\n20190201/auto/storage/goog4_request\n89eeae48258eccdcb1f592fb908008e3f5d36a949c002c1e614c94356dc18fc6" - }, - - { - "description": "POST Policy Simple", - "policyInput": { - "scheme": "https", - "bucket": "rsaposttest-1579902670-h3q7wvodjor6bc7y", - "object": "test-object", - "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z" }, - "policyOutput": { - "url": "https://storage.googleapis.com/rsaposttest-1579902670-h3q7wvodjor6bc7y/", - "fields": { - "key": "test-object", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "2821a23ca8886ae9e9d67e8c094c06626a733f373cdca76d9d8c325e60ed8237b500b2f39937a15a1ebbdef7ed156fe3f41bc3456d453bb68d6755872eb429c329ae4e0dfb5e0abef4ca976394430ad7b3d1f14e50f6040c2cf81a2880980c26f9aeb800a674fc834b0e21e414adc3d1a0835da64107cce6a14bd2b39b4d6627029f821009e15ff3a7538ddc27dcf8c189ed197d25d50f0bddf58f99e0265adf562fff7f6689faba60920d73e6a6704c3d2be34a424766398396c55d3680f8dfd19513e2861fa4efa7ae99001877f043567132d413dc3c69e04a7ef5d30a325c2f9bc64e19dd6f0576781e546383378e7a4f2904411ee32d5c5a763683333dd7", - "policy": "eyJjb25kaXRpb25zIjpbeyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" - }, - "expectedDecodedPolicy": "{\"conditions\":[{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" - } - }, - { - "description": "POST Policy Simple Virtual Hosted Style", - "policyInput": { - "scheme": "https", - "urlStyle": "VIRTUAL_HOSTED_STYLE", - "bucket": "rsaposttest-1579902670-h3q7wvodjor6bc7y", + { + "description": "HTTPS Bucket Bound Hostname Support", + "bucket": "test-bucket", "object": "test-object", + "method": "GET", "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z" - }, - "policyOutput": { - "url": "https://rsaposttest-1579902670-h3q7wvodjor6bc7y.storage.googleapis.com/", - "fields": { - "key": "test-object", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "2821a23ca8886ae9e9d67e8c094c06626a733f373cdca76d9d8c325e60ed8237b500b2f39937a15a1ebbdef7ed156fe3f41bc3456d453bb68d6755872eb429c329ae4e0dfb5e0abef4ca976394430ad7b3d1f14e50f6040c2cf81a2880980c26f9aeb800a674fc834b0e21e414adc3d1a0835da64107cce6a14bd2b39b4d6627029f821009e15ff3a7538ddc27dcf8c189ed197d25d50f0bddf58f99e0265adf562fff7f6689faba60920d73e6a6704c3d2be34a424766398396c55d3680f8dfd19513e2861fa4efa7ae99001877f043567132d413dc3c69e04a7ef5d30a325c2f9bc64e19dd6f0576781e546383378e7a4f2904411ee32d5c5a763683333dd7", - "policy": "eyJjb25kaXRpb25zIjpbeyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" - }, - "expectedDecodedPolicy": "{\"conditions\":[{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" - } - }, - - { - "description": "POST Policy Simple Bucket Bound Hostname", - "policyInput": { + "timestamp": "20190201T090000Z", + "expectedUrl": "https://mydomain.tld/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=7115a77f8c7ed1a8b74bca8b520904fca7f3bab90d69ea052687a94efd9b3a4e2a7fb7135d40e295e0a21958194c55da7e106227957c22ed6edc9d8b3d2a8133bc8af84fc9695dda8081d53f0db5ea9f28e5bfc225d78f873e9f571fd287bb7a95330e726aebd8eb4623cdb0b1a7ceb210b2ce1351b6be0191c2ad7b38f7ceb6c5ce2f98dbfb5a5a649050585e46e97f72f1f5407de657a56e34a3fdc80cdaa0598bd47f3e8af5ff22d0916b19b106890bff8c3f6587f1d3b076b16cd0ba0508607a672be33b9c75d537e15258450b43d22a21c4d528090acbb8e5bae7b31fc394e61394106ef1d6a8ed43074ab05bcec65674cd8113fb3de388da4d97e62f56", "scheme": "https", "urlStyle": "BUCKET_BOUND_HOSTNAME", - "bucketBoundHostname": "mydomain.tld", - "bucket": "rsaposttest-1579902670-h3q7wvodjor6bc7y", - "object": "test-object", - "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z" + "bucketBoundHostname": "mydomain.tld" }, - "policyOutput": { - "url": "https://mydomain.tld/", - "fields": { - "key": "test-object", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "2821a23ca8886ae9e9d67e8c094c06626a733f373cdca76d9d8c325e60ed8237b500b2f39937a15a1ebbdef7ed156fe3f41bc3456d453bb68d6755872eb429c329ae4e0dfb5e0abef4ca976394430ad7b3d1f14e50f6040c2cf81a2880980c26f9aeb800a674fc834b0e21e414adc3d1a0835da64107cce6a14bd2b39b4d6627029f821009e15ff3a7538ddc27dcf8c189ed197d25d50f0bddf58f99e0265adf562fff7f6689faba60920d73e6a6704c3d2be34a424766398396c55d3680f8dfd19513e2861fa4efa7ae99001877f043567132d413dc3c69e04a7ef5d30a325c2f9bc64e19dd6f0576781e546383378e7a4f2904411ee32d5c5a763683333dd7", - "policy": "eyJjb25kaXRpb25zIjpbeyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" - }, - "expectedDecodedPolicy": "{\"conditions\":[{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" - } - }, - { - "description": "POST Policy Simple Bucket Bound Hostname HTTP", - "policyInput": { + { + "description": "HTTP Bucket Bound Hostname Support", + "bucket": "test-bucket", + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "http://mydomain.tld/?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=7a629a5632f16dba78961250b17c1f0d2ac0d2a28dbd7cbf79088fd6cd0b7f3ec66285cdeccca024f7b8134376f5cdcf0d60f399c6df1f19fcf5cf3be9d7f905d72cb6c0b5600f83dd6a7c8df607510c0e12e36216530a7b832eab87920363c5368a7e610d44005c73324f6ca4b435e8687672f46cc1342419ec4a5264549cb4b77bdc73f4f461edf39fbdd8fda99db440b077e906ef48d2c6b854c11ded58096f293d664650c123c6ec2a0379affd05bf5696ba11d3474623e039d5e05d3dc331b86ff4f7afb9262cf9750ff5944e661e70cc443b28f7e150796dde831d70e205c7e848c19b8281510f1d195e5819176e4868713266d0e0db7a3354857187cf", "scheme": "http", "urlStyle": "BUCKET_BOUND_HOSTNAME", - "bucketBoundHostname": "mydomain.tld", - "bucket": "rsaposttest-1579902670-h3q7wvodjor6bc7y", - "object": "test-object", - "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z" + "bucketBoundHostname": "mydomain.tld" }, - "policyOutput": { - "url": "http://mydomain.tld/", - "fields": { - "key": "test-object", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "2821a23ca8886ae9e9d67e8c094c06626a733f373cdca76d9d8c325e60ed8237b500b2f39937a15a1ebbdef7ed156fe3f41bc3456d453bb68d6755872eb429c329ae4e0dfb5e0abef4ca976394430ad7b3d1f14e50f6040c2cf81a2880980c26f9aeb800a674fc834b0e21e414adc3d1a0835da64107cce6a14bd2b39b4d6627029f821009e15ff3a7538ddc27dcf8c189ed197d25d50f0bddf58f99e0265adf562fff7f6689faba60920d73e6a6704c3d2be34a424766398396c55d3680f8dfd19513e2861fa4efa7ae99001877f043567132d413dc3c69e04a7ef5d30a325c2f9bc64e19dd6f0576781e546383378e7a4f2904411ee32d5c5a763683333dd7", - "policy": "eyJjb25kaXRpb25zIjpbeyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" - }, - "expectedDecodedPolicy": "{\"conditions\":[{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" - } - }, - { - "description": "POST Policy ACL matching", - "policyInput": { - "scheme": "https", - "bucket": "rsaposttest-1579902662-x2kd7kjwh2w5izcw", - "object": "test-object", + { + "description": "HTTPS Bucket Bound Hostname Support", + "bucket": "test-bucket", + "method": "GET", "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z", - "conditions": { - "startsWith": [ - "$acl", - "public" - ] - } + "timestamp": "20190201T090000Z", + "expectedUrl": "https://mydomain.tld/?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=7a629a5632f16dba78961250b17c1f0d2ac0d2a28dbd7cbf79088fd6cd0b7f3ec66285cdeccca024f7b8134376f5cdcf0d60f399c6df1f19fcf5cf3be9d7f905d72cb6c0b5600f83dd6a7c8df607510c0e12e36216530a7b832eab87920363c5368a7e610d44005c73324f6ca4b435e8687672f46cc1342419ec4a5264549cb4b77bdc73f4f461edf39fbdd8fda99db440b077e906ef48d2c6b854c11ded58096f293d664650c123c6ec2a0379affd05bf5696ba11d3474623e039d5e05d3dc331b86ff4f7afb9262cf9750ff5944e661e70cc443b28f7e150796dde831d70e205c7e848c19b8281510f1d195e5819176e4868713266d0e0db7a3354857187cf", + "scheme": "https", + "urlStyle": "BUCKET_BOUND_HOSTNAME", + "bucketBoundHostname": "mydomain.tld" }, - "policyOutput": { - "url": "https://storage.googleapis.com/rsaposttest-1579902662-x2kd7kjwh2w5izcw/", - "fields": { - "key": "test-object", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "512593e9f739b044cf1b1f38e9ce492e43917a4864a027bc296fd595dd1ed53bb1e4da804251a812400bc93111181a13aad6f8e85b1442700cb13ac830309a8d2cfa5f21b85815644ad2942393a413f90ec6c34b4d3234630185c3e2bcee2bf560b2d1a07cd2d94bd28021b7ee60d95ba98486d574b6255663350805e88f14c8be3f095e1f40c5c9f97c7c8cbbb67538cfa3e23e7dbda6dbfd2a0f98daa6c40fadfb8f3e7799fe152a542a7784ddd2e6ba0b21fbdc13984dfdff1af886207fa6b0b50e774337d7d9eb6516ed1f81b338b9d4ddefda1b11d7bc3d0308b8d3e93f78709be4e1308562c5c0713f88267df40c51b887917d254718393a2c09fc4c55", - "policy": "eyJjb25kaXRpb25zIjpbWyJzdGFydHMtd2l0aCIsIiRhY2wiLCJwdWJsaWMiXSx7ImtleSI6InRlc3Qtb2JqZWN0In0seyJ4LWdvb2ctZGF0ZSI6IjIwMjAwMTIzVDA0MzUzMFoifSx7IngtZ29vZy1jcmVkZW50aWFsIjoidGVzdC1pYW0tY3JlZGVudGlhbHNAZHVtbXktcHJvamVjdC1pZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbS8yMDIwMDEyMy9hdXRvL3N0b3JhZ2UvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDIwLTAxLTIzVDA0OjM1OjQwWiJ9" - }, - "expectedDecodedPolicy": "{\"conditions\":[[\"starts-with\",\"$acl\",\"public\"],{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" + + { + "description": "Virtual Hosted Style", + "bucket": "test-bucket", + "object": "test-object", + "method": "GET", + "expiration": 10, + "timestamp": "20190201T090000Z", + "expectedUrl": "https://test-bucket.storage.googleapis.com/test-object?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host&X-Goog-Signature=06c633ea060b0eda81ee58dd3337b01b0d243a44f18cb03ec948861f533a129e579c7fd4c856d187f1c7b86e5715ea0abf6a1c6ba32b69274d22b1b0406df6847dc87f0d289fe8dc0682351574849b8b13e4b66922f39441af96becb73ea4c56cd5e3eeb30bc91fe84e8bd205adca8639253bdb65b2fcaf2598a230c6d8f6d8177c9e58a61b6e826767f594056b490184d676897c4bbedc15d6fbf08c3fa82a406c62e74db661e6c5d7d3ced29e0619ee719dce4b8136360345b8dce120b9f1debd511c8dac3e6d874ee05bfda8c8f1c4fedd0c07fc6d98f5f18a349bb204d8ff401402a025194e2792df8a09282141157e4ca51d26a8d0d142a01c805321911", + "scheme": "https", + "urlStyle": "VIRTUAL_HOSTED_STYLE", + "expectedCanonicalRequest": "GET\n/test-object\nX-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=test-iam-credentials%40dummy-project-id.iam.gserviceaccount.com%2F20190201%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190201T090000Z&X-Goog-Expires=10&X-Goog-SignedHeaders=host\nhost:test-bucket.storage.googleapis.com\n\nhost\nUNSIGNED-PAYLOAD", + "expectedStringToSign": "GOOG4-RSA-SHA256\n20190201T090000Z\n20190201/auto/storage/goog4_request\n89eeae48258eccdcb1f592fb908008e3f5d36a949c002c1e614c94356dc18fc6" } - }, + ], - { - "description": "POST Policy Within Content-Range", - "policyInput": { - "scheme": "https", - "bucket": "rsaposttest-1579902672-lpd47iogn6hx4sle", - "object": "test-object", - "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z", - "conditions": { - "contentLengthRange": [ - 246, - 266 - ] + "postPolicyV4Tests": [ + { + "description": "POST Policy Simple", + "policyInput": { + "scheme": "https", + "bucket": "rsaposttest-1579902670-h3q7wvodjor6bc7y", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z" + }, + "policyOutput": { + "url": "https://storage.googleapis.com/rsaposttest-1579902670-h3q7wvodjor6bc7y/", + "fields": { + "key": "test-object", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "2821a23ca8886ae9e9d67e8c094c06626a733f373cdca76d9d8c325e60ed8237b500b2f39937a15a1ebbdef7ed156fe3f41bc3456d453bb68d6755872eb429c329ae4e0dfb5e0abef4ca976394430ad7b3d1f14e50f6040c2cf81a2880980c26f9aeb800a674fc834b0e21e414adc3d1a0835da64107cce6a14bd2b39b4d6627029f821009e15ff3a7538ddc27dcf8c189ed197d25d50f0bddf58f99e0265adf562fff7f6689faba60920d73e6a6704c3d2be34a424766398396c55d3680f8dfd19513e2861fa4efa7ae99001877f043567132d413dc3c69e04a7ef5d30a325c2f9bc64e19dd6f0576781e546383378e7a4f2904411ee32d5c5a763683333dd7", + "policy": "eyJjb25kaXRpb25zIjpbeyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" + }, + "expectedDecodedPolicy": "{\"conditions\":[{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" } }, - "policyOutput": { - "url": "https://storage.googleapis.com/rsaposttest-1579902672-lpd47iogn6hx4sle/", - "fields": { - "key": "test-object", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "26a8a68997592a5bbd52c8e429ad1687db87aea9fa3ac6fcf4ff7d4526a46b5baf081e6baf5bb028bdddd260f1fcb04d12b0431587e5136815d377dbf62405ab19a6434ddf82b46cddf23e03c7e42613964fffbb70ec5a7d9878191c62fa3b3cdce2e3d3b55a47496c366161ddc24a80199678fe6c90e732fa0803024e9d9c4fada45a06608184420a4e6a0d97faff3cad30ce1956ea5416d9df41cf3b3911fec4a416248478a9557fa527a0ee732adf22d2b87de7f34d49dd9ecd4a4eca8aadd5eb7207c0fe71215fdfd2b4341c58146206e66c908f2c3922e45e4a84da4e0f9b2cfa102f498605b2ec71646c24355f524ae5e14ff4fcd36dd81a6c95c52e55", - "policy": "eyJjb25kaXRpb25zIjpbWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMjQ2LDI2Nl0seyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" + + { + "description": "POST Policy Simple Virtual Hosted Style", + "policyInput": { + "scheme": "https", + "urlStyle": "VIRTUAL_HOSTED_STYLE", + "bucket": "rsaposttest-1579902670-h3q7wvodjor6bc7y", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z" }, - "expectedDecodedPolicy": "{\"conditions\":[[\"content-length-range\",246,266],{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" - } - }, + "policyOutput": { + "url": "https://rsaposttest-1579902670-h3q7wvodjor6bc7y.storage.googleapis.com/", + "fields": { + "key": "test-object", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "2821a23ca8886ae9e9d67e8c094c06626a733f373cdca76d9d8c325e60ed8237b500b2f39937a15a1ebbdef7ed156fe3f41bc3456d453bb68d6755872eb429c329ae4e0dfb5e0abef4ca976394430ad7b3d1f14e50f6040c2cf81a2880980c26f9aeb800a674fc834b0e21e414adc3d1a0835da64107cce6a14bd2b39b4d6627029f821009e15ff3a7538ddc27dcf8c189ed197d25d50f0bddf58f99e0265adf562fff7f6689faba60920d73e6a6704c3d2be34a424766398396c55d3680f8dfd19513e2861fa4efa7ae99001877f043567132d413dc3c69e04a7ef5d30a325c2f9bc64e19dd6f0576781e546383378e7a4f2904411ee32d5c5a763683333dd7", + "policy": "eyJjb25kaXRpb25zIjpbeyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" + }, + "expectedDecodedPolicy": "{\"conditions\":[{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" + } + }, - { - "description": "POST Policy Cache-Control File Header", - "policyInput": { - "scheme": "https", - "bucket": "rsaposttest-1579902669-nwk5s7vvfjgdjs62", - "object": "test-object", - "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z", - "fields": { - "acl": "public-read", - "cache-control": "public,max-age=86400" + { + "description": "POST Policy Simple Bucket Bound Hostname", + "policyInput": { + "scheme": "https", + "urlStyle": "BUCKET_BOUND_HOSTNAME", + "bucketBoundHostname": "mydomain.tld", + "bucket": "rsaposttest-1579902670-h3q7wvodjor6bc7y", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z" + }, + "policyOutput": { + "url": "https://mydomain.tld/", + "fields": { + "key": "test-object", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "2821a23ca8886ae9e9d67e8c094c06626a733f373cdca76d9d8c325e60ed8237b500b2f39937a15a1ebbdef7ed156fe3f41bc3456d453bb68d6755872eb429c329ae4e0dfb5e0abef4ca976394430ad7b3d1f14e50f6040c2cf81a2880980c26f9aeb800a674fc834b0e21e414adc3d1a0835da64107cce6a14bd2b39b4d6627029f821009e15ff3a7538ddc27dcf8c189ed197d25d50f0bddf58f99e0265adf562fff7f6689faba60920d73e6a6704c3d2be34a424766398396c55d3680f8dfd19513e2861fa4efa7ae99001877f043567132d413dc3c69e04a7ef5d30a325c2f9bc64e19dd6f0576781e546383378e7a4f2904411ee32d5c5a763683333dd7", + "policy": "eyJjb25kaXRpb25zIjpbeyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" + }, + "expectedDecodedPolicy": "{\"conditions\":[{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" } }, - "policyOutput": { - "url": "https://storage.googleapis.com/rsaposttest-1579902669-nwk5s7vvfjgdjs62/", - "fields": { - "key": "test-object", - "acl": "public-read", - "cache-control": "public,max-age=86400", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "2fc42968e980af863af91c20f0a9158b76249b64fd066f3a9e974e3f95fc4a56fb3753845913556180d99f4e3678f326cbfef0b91d2181dcc76a2900d85070443fba87f73f139df02c7a391459006e4e22defd1746272bb42af180ec4bc6d440e831e8074413ca2078a8fac622f1d64a116bbbd61ac3ef7df4f67dc568df9d489ed1dcbb8dc1964b459d5e557caf1f5710be5863b7606adeca85befe8d2754f4ca368fb7e87f4e490218dcfcbdd64f1a65f79d45dc3634b7aac96a9520829357b2694902e8eb7e2a22ba7aa9150730eadc7b99de2929ff302efc6839c811fd6ad9a31c895421266784dd49dc76ee30140cbc577852237fd2f1715358e67d8c0f", - "policy": "eyJjb25kaXRpb25zIjpbeyJhY2wiOiJwdWJsaWMtcmVhZCJ9LHsiY2FjaGUtY29udHJvbCI6InB1YmxpYyxtYXgtYWdlPTg2NDAwIn0seyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" + + { + "description": "POST Policy Simple Bucket Bound Hostname HTTP", + "policyInput": { + "scheme": "http", + "urlStyle": "BUCKET_BOUND_HOSTNAME", + "bucketBoundHostname": "mydomain.tld", + "bucket": "rsaposttest-1579902670-h3q7wvodjor6bc7y", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z" }, - "expectedDecodedPolicy": "{\"conditions\":[{\"acl\":\"public-read\"},{\"cache-control\":\"public,max-age=86400\"},{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" - } - }, + "policyOutput": { + "url": "http://mydomain.tld/", + "fields": { + "key": "test-object", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "2821a23ca8886ae9e9d67e8c094c06626a733f373cdca76d9d8c325e60ed8237b500b2f39937a15a1ebbdef7ed156fe3f41bc3456d453bb68d6755872eb429c329ae4e0dfb5e0abef4ca976394430ad7b3d1f14e50f6040c2cf81a2880980c26f9aeb800a674fc834b0e21e414adc3d1a0835da64107cce6a14bd2b39b4d6627029f821009e15ff3a7538ddc27dcf8c189ed197d25d50f0bddf58f99e0265adf562fff7f6689faba60920d73e6a6704c3d2be34a424766398396c55d3680f8dfd19513e2861fa4efa7ae99001877f043567132d413dc3c69e04a7ef5d30a325c2f9bc64e19dd6f0576781e546383378e7a4f2904411ee32d5c5a763683333dd7", + "policy": "eyJjb25kaXRpb25zIjpbeyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" + }, + "expectedDecodedPolicy": "{\"conditions\":[{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" + } + }, - { - "description": "POST Policy Success With Status", - "policyInput": { - "scheme": "https", - "bucket": "rsaposttest-1579902678-pt5yms55j47r6qy4", - "object": "test-object", - "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z", - "fields": { - "success_action_status": "200" + { + "description": "POST Policy ACL matching", + "policyInput": { + "scheme": "https", + "bucket": "rsaposttest-1579902662-x2kd7kjwh2w5izcw", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z", + "conditions": { + "startsWith": [ + "$acl", + "public" + ] + } + }, + "policyOutput": { + "url": "https://storage.googleapis.com/rsaposttest-1579902662-x2kd7kjwh2w5izcw/", + "fields": { + "key": "test-object", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "512593e9f739b044cf1b1f38e9ce492e43917a4864a027bc296fd595dd1ed53bb1e4da804251a812400bc93111181a13aad6f8e85b1442700cb13ac830309a8d2cfa5f21b85815644ad2942393a413f90ec6c34b4d3234630185c3e2bcee2bf560b2d1a07cd2d94bd28021b7ee60d95ba98486d574b6255663350805e88f14c8be3f095e1f40c5c9f97c7c8cbbb67538cfa3e23e7dbda6dbfd2a0f98daa6c40fadfb8f3e7799fe152a542a7784ddd2e6ba0b21fbdc13984dfdff1af886207fa6b0b50e774337d7d9eb6516ed1f81b338b9d4ddefda1b11d7bc3d0308b8d3e93f78709be4e1308562c5c0713f88267df40c51b887917d254718393a2c09fc4c55", + "policy": "eyJjb25kaXRpb25zIjpbWyJzdGFydHMtd2l0aCIsIiRhY2wiLCJwdWJsaWMiXSx7ImtleSI6InRlc3Qtb2JqZWN0In0seyJ4LWdvb2ctZGF0ZSI6IjIwMjAwMTIzVDA0MzUzMFoifSx7IngtZ29vZy1jcmVkZW50aWFsIjoidGVzdC1pYW0tY3JlZGVudGlhbHNAZHVtbXktcHJvamVjdC1pZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbS8yMDIwMDEyMy9hdXRvL3N0b3JhZ2UvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDIwLTAxLTIzVDA0OjM1OjQwWiJ9" + }, + "expectedDecodedPolicy": "{\"conditions\":[[\"starts-with\",\"$acl\",\"public\"],{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" } }, - "policyOutput": { - "url": "https://storage.googleapis.com/rsaposttest-1579902678-pt5yms55j47r6qy4/", - "fields": { - "key": "test-object", - "success_action_status": "200", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "98b21b196e889787a779e98f0349cb69d46f75efe10278c1834eb29d6bc6635f0abfb3708108882a12071dd3aedaa30622c291b80e68b53ad959ef614c32576a8af5bfcbf87ecf3dbc4626360c093ed351f3517ff59793e43c26de3477a3ea2abb733ef2e847f0fa0f761ca1a18c82f196d16775522be6f0030bba03e9b0b0a533cf1eda07e4809f8b984ccf2652911b5c2eb01cf1a404bd8cfca628ce124f71d390d718d9f7f983c504b110f638874d1e5928ca607e5324dea0a8ec8fe8f8799b7d2b5801059dbffaa02fe6525e88ac393b86325ab5401ebc74661fa07932d385e1f2bb6703447b4b593ca13482426201464fe81d60a5c6ca47519137f009ea", - "policy": "eyJjb25kaXRpb25zIjpbeyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDAifSx7ImtleSI6InRlc3Qtb2JqZWN0In0seyJ4LWdvb2ctZGF0ZSI6IjIwMjAwMTIzVDA0MzUzMFoifSx7IngtZ29vZy1jcmVkZW50aWFsIjoidGVzdC1pYW0tY3JlZGVudGlhbHNAZHVtbXktcHJvamVjdC1pZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbS8yMDIwMDEyMy9hdXRvL3N0b3JhZ2UvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDIwLTAxLTIzVDA0OjM1OjQwWiJ9" + + { + "description": "POST Policy Within Content-Range", + "policyInput": { + "scheme": "https", + "bucket": "rsaposttest-1579902672-lpd47iogn6hx4sle", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z", + "conditions": { + "contentLengthRange": [ + 246, + 266 + ] + } }, - "expectedDecodedPolicy": "{\"conditions\":[{\"success_action_status\":\"200\"},{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" - } - }, + "policyOutput": { + "url": "https://storage.googleapis.com/rsaposttest-1579902672-lpd47iogn6hx4sle/", + "fields": { + "key": "test-object", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "26a8a68997592a5bbd52c8e429ad1687db87aea9fa3ac6fcf4ff7d4526a46b5baf081e6baf5bb028bdddd260f1fcb04d12b0431587e5136815d377dbf62405ab19a6434ddf82b46cddf23e03c7e42613964fffbb70ec5a7d9878191c62fa3b3cdce2e3d3b55a47496c366161ddc24a80199678fe6c90e732fa0803024e9d9c4fada45a06608184420a4e6a0d97faff3cad30ce1956ea5416d9df41cf3b3911fec4a416248478a9557fa527a0ee732adf22d2b87de7f34d49dd9ecd4a4eca8aadd5eb7207c0fe71215fdfd2b4341c58146206e66c908f2c3922e45e4a84da4e0f9b2cfa102f498605b2ec71646c24355f524ae5e14ff4fcd36dd81a6c95c52e55", + "policy": "eyJjb25kaXRpb25zIjpbWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMjQ2LDI2Nl0seyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" + }, + "expectedDecodedPolicy": "{\"conditions\":[[\"content-length-range\",246,266],{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" + } + }, - { - "description": "POST Policy Success With Redirect", - "policyInput": { - "scheme": "https", - "bucket": "rsaposttest-1579902671-6ldm6caw4se52vrx", - "object": "test-object", - "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z", - "fields": { - "success_action_redirect": "http://www.google.com/" + { + "description": "POST Policy Cache-Control File Header", + "policyInput": { + "scheme": "https", + "bucket": "rsaposttest-1579902669-nwk5s7vvfjgdjs62", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z", + "fields": { + "acl": "public-read", + "cache-control": "public,max-age=86400" + } + }, + "policyOutput": { + "url": "https://storage.googleapis.com/rsaposttest-1579902669-nwk5s7vvfjgdjs62/", + "fields": { + "key": "test-object", + "acl": "public-read", + "cache-control": "public,max-age=86400", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "2fc42968e980af863af91c20f0a9158b76249b64fd066f3a9e974e3f95fc4a56fb3753845913556180d99f4e3678f326cbfef0b91d2181dcc76a2900d85070443fba87f73f139df02c7a391459006e4e22defd1746272bb42af180ec4bc6d440e831e8074413ca2078a8fac622f1d64a116bbbd61ac3ef7df4f67dc568df9d489ed1dcbb8dc1964b459d5e557caf1f5710be5863b7606adeca85befe8d2754f4ca368fb7e87f4e490218dcfcbdd64f1a65f79d45dc3634b7aac96a9520829357b2694902e8eb7e2a22ba7aa9150730eadc7b99de2929ff302efc6839c811fd6ad9a31c895421266784dd49dc76ee30140cbc577852237fd2f1715358e67d8c0f", + "policy": "eyJjb25kaXRpb25zIjpbeyJhY2wiOiJwdWJsaWMtcmVhZCJ9LHsiY2FjaGUtY29udHJvbCI6InB1YmxpYyxtYXgtYWdlPTg2NDAwIn0seyJrZXkiOiJ0ZXN0LW9iamVjdCJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ==" + }, + "expectedDecodedPolicy": "{\"conditions\":[{\"acl\":\"public-read\"},{\"cache-control\":\"public,max-age=86400\"},{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" } }, - "policyOutput": { - "url": "https://storage.googleapis.com/rsaposttest-1579902671-6ldm6caw4se52vrx/", - "fields": { - "key": "test-object", - "success_action_redirect": "http://www.google.com/", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "39faed7256b042d0bd763ad1eb2439fcd6636aeaadc8a5501510f3baa6769f5f96bfe59321c6c76a82890a714afcb59580673a471f00d41c4ee0cdb1bbd51c05e2c32d9847bdfd96442996e81b783eac54c494c1ba8fdee50370c6b9b03c3812ccaa82bed390d27f82e9abf1e99b69c93a55e9dbcceac3a629e1e4f7178731fcfcda9fafee2fbbee20c135b67873ea298b5a29908215597a81adede5116b9e7b01b718c0c4b11b387a42a9281cad75b3b5f1dd5d62bec746ac1f723119b745d2ca4079282bfd82007a3e8db631736f9fa1ca1abe28975a0bbfa99eff065fa05328d2bcb69a9506b4bd4b694b357afc9ff0fb0b7457ab0b63a4f8f9ec46719d60", - "policy": "eyJjb25kaXRpb25zIjpbeyJzdWNjZXNzX2FjdGlvbl9yZWRpcmVjdCI6Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbS8ifSx7ImtleSI6InRlc3Qtb2JqZWN0In0seyJ4LWdvb2ctZGF0ZSI6IjIwMjAwMTIzVDA0MzUzMFoifSx7IngtZ29vZy1jcmVkZW50aWFsIjoidGVzdC1pYW0tY3JlZGVudGlhbHNAZHVtbXktcHJvamVjdC1pZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbS8yMDIwMDEyMy9hdXRvL3N0b3JhZ2UvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDIwLTAxLTIzVDA0OjM1OjQwWiJ9" + + { + "description": "POST Policy Success With Status", + "policyInput": { + "scheme": "https", + "bucket": "rsaposttest-1579902678-pt5yms55j47r6qy4", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z", + "fields": { + "success_action_status": "200" + } }, - "expectedDecodedPolicy": "{\"conditions\":[{\"success_action_redirect\":\"http://www.google.com/\"},{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" - } - }, + "policyOutput": { + "url": "https://storage.googleapis.com/rsaposttest-1579902678-pt5yms55j47r6qy4/", + "fields": { + "key": "test-object", + "success_action_status": "200", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "98b21b196e889787a779e98f0349cb69d46f75efe10278c1834eb29d6bc6635f0abfb3708108882a12071dd3aedaa30622c291b80e68b53ad959ef614c32576a8af5bfcbf87ecf3dbc4626360c093ed351f3517ff59793e43c26de3477a3ea2abb733ef2e847f0fa0f761ca1a18c82f196d16775522be6f0030bba03e9b0b0a533cf1eda07e4809f8b984ccf2652911b5c2eb01cf1a404bd8cfca628ce124f71d390d718d9f7f983c504b110f638874d1e5928ca607e5324dea0a8ec8fe8f8799b7d2b5801059dbffaa02fe6525e88ac393b86325ab5401ebc74661fa07932d385e1f2bb6703447b4b593ca13482426201464fe81d60a5c6ca47519137f009ea", + "policy": "eyJjb25kaXRpb25zIjpbeyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDAifSx7ImtleSI6InRlc3Qtb2JqZWN0In0seyJ4LWdvb2ctZGF0ZSI6IjIwMjAwMTIzVDA0MzUzMFoifSx7IngtZ29vZy1jcmVkZW50aWFsIjoidGVzdC1pYW0tY3JlZGVudGlhbHNAZHVtbXktcHJvamVjdC1pZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbS8yMDIwMDEyMy9hdXRvL3N0b3JhZ2UvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDIwLTAxLTIzVDA0OjM1OjQwWiJ9" + }, + "expectedDecodedPolicy": "{\"conditions\":[{\"success_action_status\":\"200\"},{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" + } + }, - { - "description": "POST Policy Character Escaping", - "policyInput": { - "scheme": "https", - "bucket": "rsaposttest-1579902671-6ldm6caw4se52vrx", - "object": "$test-object-é", - "expiration": 10, - "timestamp": "2020-01-23T04:35:30Z", - "fields": { - "success_action_redirect": "http://www.google.com/", - "x-goog-meta": "$test-object-é-metadata" + { + "description": "POST Policy Success With Redirect", + "policyInput": { + "scheme": "https", + "bucket": "rsaposttest-1579902671-6ldm6caw4se52vrx", + "object": "test-object", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z", + "fields": { + "success_action_redirect": "http://www.google.com/" + } + }, + "policyOutput": { + "url": "https://storage.googleapis.com/rsaposttest-1579902671-6ldm6caw4se52vrx/", + "fields": { + "key": "test-object", + "success_action_redirect": "http://www.google.com/", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "39faed7256b042d0bd763ad1eb2439fcd6636aeaadc8a5501510f3baa6769f5f96bfe59321c6c76a82890a714afcb59580673a471f00d41c4ee0cdb1bbd51c05e2c32d9847bdfd96442996e81b783eac54c494c1ba8fdee50370c6b9b03c3812ccaa82bed390d27f82e9abf1e99b69c93a55e9dbcceac3a629e1e4f7178731fcfcda9fafee2fbbee20c135b67873ea298b5a29908215597a81adede5116b9e7b01b718c0c4b11b387a42a9281cad75b3b5f1dd5d62bec746ac1f723119b745d2ca4079282bfd82007a3e8db631736f9fa1ca1abe28975a0bbfa99eff065fa05328d2bcb69a9506b4bd4b694b357afc9ff0fb0b7457ab0b63a4f8f9ec46719d60", + "policy": "eyJjb25kaXRpb25zIjpbeyJzdWNjZXNzX2FjdGlvbl9yZWRpcmVjdCI6Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbS8ifSx7ImtleSI6InRlc3Qtb2JqZWN0In0seyJ4LWdvb2ctZGF0ZSI6IjIwMjAwMTIzVDA0MzUzMFoifSx7IngtZ29vZy1jcmVkZW50aWFsIjoidGVzdC1pYW0tY3JlZGVudGlhbHNAZHVtbXktcHJvamVjdC1pZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbS8yMDIwMDEyMy9hdXRvL3N0b3JhZ2UvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDIwLTAxLTIzVDA0OjM1OjQwWiJ9" + }, + "expectedDecodedPolicy": "{\"conditions\":[{\"success_action_redirect\":\"http://www.google.com/\"},{\"key\":\"test-object\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" } }, - "policyOutput": { - "url": "https://storage.googleapis.com/rsaposttest-1579902671-6ldm6caw4se52vrx/", - "fields": { - "key": "$test-object-é", - "success_action_redirect": "http://www.google.com/", - "x-goog-meta": "$test-object-é-metadata", - "x-goog-algorithm": "GOOG4-RSA-SHA256", - "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", - "x-goog-date": "20200123T043530Z", - "x-goog-signature": "05eb19ea4ece513cbb2bc6a92c9bc82de6be46943fb4703df3f7b26e6033f90a194e2444e6c3166e9585ca468b5727702aa2696e5cca54677c047f7734119ea0d635404d6a5e577b737ffd5414059cd1b508aa99cfad592d9228f1bf47d7df3ffd73bcae6af6d8d83f7f50b4ccbf6e6c0798d2d9923a7e18c8888e2519fcf09d174b7015581a7de021964eeb9d27293213686d80d825332819c4e98d4ab2c5237f352840993e22a02a41d827ce6a4a294e84a33bf051519fdcbf982f2ad932f58714608c4b5a1f89d5e322d194f5e29fa4160fce771008320ac4e659adeead36aa07fe26a96e52e809436b7bd169256d6613c135148fdee6926caaef65817dc2", - "policy": "eyJjb25kaXRpb25zIjpbeyJ4LWdvb2ctbWV0YSI6IiR0ZXN0LW9iamVjdC1cdTAwZTktbWV0YWRhdGEifSx7InN1Y2Nlc3NfYWN0aW9uX3JlZGlyZWN0IjoiaHR0cDovL3d3dy5nb29nbGUuY29tLyJ9LHsia2V5IjoiJHRlc3Qtb2JqZWN0LVx1MDBlOSJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ" + + { + "description": "POST Policy Character Escaping", + "policyInput": { + "scheme": "https", + "bucket": "rsaposttest-1579902671-6ldm6caw4se52vrx", + "object": "$test-object-é", + "expiration": 10, + "timestamp": "2020-01-23T04:35:30Z", + "fields": { + "success_action_redirect": "http://www.google.com/", + "x-goog-meta": "$test-object-é-metadata" + } }, - "expectedDecodedPolicy": "{\"conditions\":[{\"success_action_redirect\":\"http://www.google.com/\"},{\"x-goog-meta\":\"$test-object-\u00e9-metadata\"},{\"key\":\"$test-object-\u00e9\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" + "policyOutput": { + "url": "https://storage.googleapis.com/rsaposttest-1579902671-6ldm6caw4se52vrx/", + "fields": { + "key": "$test-object-é", + "success_action_redirect": "http://www.google.com/", + "x-goog-meta": "$test-object-é-metadata", + "x-goog-algorithm": "GOOG4-RSA-SHA256", + "x-goog-credential": "test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request", + "x-goog-date": "20200123T043530Z", + "x-goog-signature": "05eb19ea4ece513cbb2bc6a92c9bc82de6be46943fb4703df3f7b26e6033f90a194e2444e6c3166e9585ca468b5727702aa2696e5cca54677c047f7734119ea0d635404d6a5e577b737ffd5414059cd1b508aa99cfad592d9228f1bf47d7df3ffd73bcae6af6d8d83f7f50b4ccbf6e6c0798d2d9923a7e18c8888e2519fcf09d174b7015581a7de021964eeb9d27293213686d80d825332819c4e98d4ab2c5237f352840993e22a02a41d827ce6a4a294e84a33bf051519fdcbf982f2ad932f58714608c4b5a1f89d5e322d194f5e29fa4160fce771008320ac4e659adeead36aa07fe26a96e52e809436b7bd169256d6613c135148fdee6926caaef65817dc2", + "policy": "eyJjb25kaXRpb25zIjpbeyJ4LWdvb2ctbWV0YSI6IiR0ZXN0LW9iamVjdC1cdTAwZTktbWV0YWRhdGEifSx7InN1Y2Nlc3NfYWN0aW9uX3JlZGlyZWN0IjoiaHR0cDovL3d3dy5nb29nbGUuY29tLyJ9LHsia2V5IjoiJHRlc3Qtb2JqZWN0LVx1MDBlOSJ9LHsieC1nb29nLWRhdGUiOiIyMDIwMDEyM1QwNDM1MzBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6InRlc3QtaWFtLWNyZWRlbnRpYWxzQGR1bW15LXByb2plY3QtaWQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20vMjAyMDAxMjMvYXV0by9zdG9yYWdlL2dvb2c0X3JlcXVlc3QifSx7IngtZ29vZy1hbGdvcml0aG0iOiJHT09HNC1SU0EtU0hBMjU2In1dLCJleHBpcmF0aW9uIjoiMjAyMC0wMS0yM1QwNDozNTo0MFoifQ" + }, + "expectedDecodedPolicy": "{\"conditions\":[{\"success_action_redirect\":\"http://www.google.com/\"},{\"x-goog-meta\":\"$test-object-\u00e9-metadata\"},{\"key\":\"$test-object-\u00e9\"},{\"x-goog-date\":\"20200123T043530Z\"},{\"x-goog-credential\":\"test-iam-credentials@dummy-project-id.iam.gserviceaccount.com/20200123/auto/storage/goog4_request\"},{\"x-goog-algorithm\":\"GOOG4-RSA-SHA256\"}],\"expiration\":\"2020-01-23T04:35:40Z\"}" + } } - } -] \ No newline at end of file + ] +} \ No newline at end of file