Uh oh!
There was an error while loading. Please reload this page.
This repository was archived by the owner on Mar 31, 2026. It is now read-only.
- Notifications
You must be signed in to change notification settings - Fork 173
feat(storage): improve v4 signature query parameters encoding#48
Merged
Uh oh!
There was an error while loading. Please reload this page.
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
7767686
feat(storage): improve v4 signature query parameters encoding
8cf8de4
add URL encoding test
cbd0412
add query_parameters arg into conformance tests
691deaa
Merge branch 'master' into v4_sign_query_params
frankyn aab5824
Merge branch 'master' into v4_sign_query_params
frankyn 9cca610
add tests for _quote_param() function
ae8ba7b
declare test file encoding
76f2377
Merge branch 'master' into v4_sign_query_params
frankyn 2b6ed38
Merge branch 'master' into v4_sign_query_params
frankyn bfeb050
fix the param type
9fcf70f
add test with bytes
d956a0f
Merge branch 'master' into v4_sign_query_params
frankyn 1d7a257
Merge branch 'master' into v4_sign_query_params
frankyn fd6c8bb
Merge branch 'master' into v4_sign_query_params
frankyn 8e94d8f
Update _signing.py
ab01f06
Merge branch 'master' into v4_sign_query_params
frankyn 4ee97ff
Merge branch 'master' into v4_sign_query_params
frankyn ad07cef
Merge branch 'master' into v4_sign_query_params
crwilcox File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -509,7 +509,7 @@ def generate_signed_url_v4( | ||
| :type query_parameters: dict | ||
| :param query_parameters: | ||
| (Optional) Additional query paramtersto be included as part of the | ||
| (Optional) Additional query parameters to be included as part of the | ||
| signed URLs. See: | ||
| https://cloud.google.com/storage/docs/xml-api/reference-headers#query | ||
| @@ -585,8 +585,7 @@ def generate_signed_url_v4( | ||
| if generation is not None: | ||
| query_parameters["generation"] = generation | ||
| ordered_query_parameters = sorted(query_parameters.items()) | ||
| canonical_query_string = six.moves.urllib.parse.urlencode(ordered_query_parameters) | ||
| canonical_query_string = _url_encode(query_parameters) | ||
| lowercased_headers = dict(ordered_headers) | ||
| @@ -672,3 +671,34 @@ def _sign_message(message, access_token, service_account_email): | ||
| data = json.loads(response.data.decode("utf-8")) | ||
| return data["signature"] | ||
| def _url_encode(query_params): | ||
| """Encode query params into URL. | ||
| :type query_params: dict | ||
| :param query_params: Query params to be encoded. | ||
| :rtype: str | ||
| :returns: URL encoded query params. | ||
| """ | ||
| params = [ | ||
| "{}={}".format(_quote_param(name), _quote_param(value)) | ||
| for name, value in query_params.items() | ||
| ] | ||
| return "&".join(sorted(params)) | ||
IlyaFaer marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| def _quote_param(param): | ||
| """Quote query param. | ||
| :type param: Any | ||
| :param param: Query param to be encoded. | ||
| :rtype: str | ||
| :returns: URL encoded query param. | ||
| """ | ||
| if not isinstance(param, bytes): | ||
| param = str(param) | ||
IlyaFaer marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| return six.moves.urllib.parse.quote(param, safe="~") | ||
IlyaFaer marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| # -*- coding: utf-8 -*- | ||
| # | ||
| # Copyright 2017 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| @@ -705,6 +707,61 @@ def test_sign_bytes_failure(self): | ||
| ) | ||
| class TestCustomURLEncoding(unittest.TestCase): | ||
| def test_url_encode(self): | ||
| from google.cloud.storage._signing import _url_encode | ||
| # param1 includes safe symbol ~ | ||
| # param# includes symbols, which must be encoded | ||
| query_params = {"param1": "value~1-2", "param#": "*value+value/"} | ||
| self.assertEqual( | ||
| _url_encode(query_params), "param%23=%2Avalue%2Bvalue%2F¶m1=value~1-2" | ||
| ) | ||
IlyaFaer marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| class TestQuoteParam(unittest.TestCase): | ||
| def test_ascii_symbols(self): | ||
| from google.cloud.storage._signing import _quote_param | ||
| encoded_param = _quote_param("param") | ||
| self.assertIsInstance(encoded_param, str) | ||
| self.assertEqual(encoded_param, "param") | ||
| def test_quoted_symbols(self): | ||
| from google.cloud.storage._signing import _quote_param | ||
| encoded_param = _quote_param("!#$%&'()*+,/:;=?@[]") | ||
| self.assertIsInstance(encoded_param, str) | ||
| self.assertEqual( | ||
| encoded_param, "%21%23%24%25%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D" | ||
| ) | ||
| def test_unquoted_symbols(self): | ||
| from google.cloud.storage._signing import _quote_param | ||
| import string | ||
| UNQUOTED = string.ascii_letters + string.digits + ".~_-" | ||
| encoded_param = _quote_param(UNQUOTED) | ||
| self.assertIsInstance(encoded_param, str) | ||
| self.assertEqual(encoded_param, UNQUOTED) | ||
| def test_unicode_symbols(self): | ||
| from google.cloud.storage._signing import _quote_param | ||
| encoded_param = _quote_param("ЁЙЦЯЩЯЩ") | ||
| self.assertIsInstance(encoded_param, str) | ||
| self.assertEqual(encoded_param, "%D0%81%D0%99%D0%A6%D0%AF%D0%A9%D0%AF%D0%A9") | ||
| def test_bytes(self): | ||
| from google.cloud.storage._signing import _quote_param | ||
| encoded_param = _quote_param(b"bytes") | ||
| self.assertIsInstance(encoded_param, str) | ||
| self.assertEqual(encoded_param, "bytes") | ||
| _DUMMY_SERVICE_ACCOUNT = None | ||
| @@ -731,6 +788,7 @@ def _run_conformance_test(resource, test_data): | ||
| method=test_data["method"], | ||
| _request_timestamp=test_data["timestamp"], | ||
| headers=test_data.get("headers"), | ||
| query_parameters=test_data.get("queryParameters"), | ||
| ) | ||
IlyaFaer marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| assert url == test_data["expectedUrl"] | ||
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.