diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bd31cd59fdd..a1137e16859 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,7 +15,7 @@ /bigquery_storage/ @shollyman @GoogleCloudPlatform/python-samples-owners /bigtable/ @billyjacobson @GoogleCloudPlatform/python-samples-owners /blog/ @GoogleCloudPlatform/python-samples-owners -/cdn/ @GoogleCloudPlatform/python-samples-owners +/cdn/ @mpwarres @elithrar @GoogleCloudPlatform/python-samples-owners /cloud-sql/ @kvg @GoogleCloudPlatform/python-samples-owners /codelabs/ @GoogleCloudPlatform/python-samples-owners /composer/ @leahecole @GoogleCloudPlatform/python-samples-owners diff --git a/cdn/snippets.py b/cdn/snippets.py index 027b5cb04f9..2873118f4ce 100644 --- a/cdn/snippets.py +++ b/cdn/snippets.py @@ -68,9 +68,87 @@ def sign_url(url, key_name, base64_key, expiration_time): url=url_to_sign, signature=signature) print(signed_url) + + +def sign_url_prefix(url, url_prefix, key_name, base64_key, expiration_time): + """Gets the Signed URL string for the specified URL prefix and configuration. + + Args: + url: URL of request. + url_prefix: URL prefix to sign as a string. + key_name: name of the signing key as a string. + base64_key: signing key as a base64 encoded string. + expiration_time: expiration time as a UTC datetime object. + + Returns: + Returns the Signed URL appended with the query parameters based on the + specified URL prefix and configuration. + """ + stripped_url = url.strip() + parsed_url = urllib.parse.urlsplit(stripped_url) + query_params = urllib.parse.parse_qs( + parsed_url.query, keep_blank_values=True) + encoded_url_prefix = base64.urlsafe_b64encode( + url_prefix.strip().encode('utf-8')).decode('utf-8') + epoch = datetime.datetime.utcfromtimestamp(0) + expiration_timestamp = int((expiration_time - epoch).total_seconds()) + decoded_key = base64.urlsafe_b64decode(base64_key) + + policy_pattern = u'URLPrefix={encoded_url_prefix}&Expires={expires}&KeyName={key_name}' + policy = policy_pattern.format( + encoded_url_prefix=encoded_url_prefix, + expires=expiration_timestamp, + key_name=key_name) + + digest = hmac.new( + decoded_key, policy.encode('utf-8'), hashlib.sha1).digest() + signature = base64.urlsafe_b64encode(digest).decode('utf-8') + + signed_url = u'{url}{separator}{policy}&Signature={signature}'.format( + url=stripped_url, + separator='&' if query_params else '?', + policy=policy, + signature=signature) + + print(signed_url) # [END sign_url] +# [START cdn_sign_cookie] +def sign_cookie(url_prefix, key_name, base64_key, expiration_time): + """Gets the Signed cookie value for the specified URL prefix and configuration. + + Args: + url_prefix: URL prefix to sign as a string. + key_name: name of the signing key as a string. + base64_key: signing key as a base64 encoded string. + expiration_time: expiration time as a UTC datetime object. + + Returns: + Returns the Cloud-CDN-Cookie value based on the specified configuration. + """ + encoded_url_prefix = base64.urlsafe_b64encode( + url_prefix.strip().encode('utf-8')).decode('utf-8') + epoch = datetime.datetime.utcfromtimestamp(0) + expiration_timestamp = int((expiration_time - epoch).total_seconds()) + decoded_key = base64.urlsafe_b64decode(base64_key) + + policy_pattern = u'URLPrefix={encoded_url_prefix}:Expires={expires}:KeyName={key_name}' + policy = policy_pattern.format( + encoded_url_prefix=encoded_url_prefix, + expires=expiration_timestamp, + key_name=key_name) + + digest = hmac.new( + decoded_key, policy.encode('utf-8'), hashlib.sha1).digest() + signature = base64.urlsafe_b64encode(digest).decode('utf-8') + + signed_policy = u'Cloud-CDN-Cookie={policy}:Signature={signature}'.format( + policy=policy, signature=signature) + print(signed_policy) +# [END cdn_sign_cookie] + + if __name__ == '__main__': parser = argparse.ArgumentParser( description=__doc__, @@ -94,8 +172,48 @@ def sign_url(url, key_name, base64_key, expiration_time): type=lambda d: datetime.datetime.utcfromtimestamp(float(d)), help='Expiration time expessed as seconds since the epoch.') + sign_url_prefix_parser = subparsers.add_parser( + 'sign-url-prefix', + help="Sign a URL prefix to grant temporary authorized access.") + sign_url_prefix_parser.add_argument( + 'url', help='The request URL.') + sign_url_prefix_parser.add_argument( + 'url_prefix', help='The URL prefix to sign.') + sign_url_prefix_parser.add_argument( + 'key_name', + help='Key name for the signing key.') + sign_url_prefix_parser.add_argument( + 'base64_key', + help='The base64 encoded signing key.') + sign_url_prefix_parser.add_argument( + 'expiration_time', + type=lambda d: datetime.datetime.utcfromtimestamp(float(d)), + help='Expiration time expessed as seconds since the epoch.') + + sign_cookie_parser = subparsers.add_parser( + 'sign-cookie', + help="Generate a signed cookie to grant temporary authorized access.") + sign_cookie_parser.add_argument( + 'url_prefix', help='The URL prefix to sign.') + sign_cookie_parser.add_argument( + 'key_name', + help='Key name for the signing key.') + sign_cookie_parser.add_argument( + 'base64_key', + help='The base64 encoded signing key.') + sign_cookie_parser.add_argument( + 'expiration_time', + type=lambda d: datetime.datetime.utcfromtimestamp(float(d)), + help='Expiration time expessed as seconds since the epoch.') + args = parser.parse_args() if args.command == 'sign-url': sign_url( args.url, args.key_name, args.base64_key, args.expiration_time) + elif args.command == 'sign-url-prefix': + sign_url_prefix( + args.url, args.url_prefix, args.key_name, args.base64_key, args.expiration_time) + elif args.command == 'sign-cookie': + sign_cookie( + args.url_prefix, args.key_name, args.base64_key, args.expiration_time) diff --git a/cdn/snippets_test.py b/cdn/snippets_test.py index 245daea13cd..c14dd5dc141 100644 --- a/cdn/snippets_test.py +++ b/cdn/snippets_test.py @@ -50,3 +50,61 @@ def test_sign_url(capsys): assert results[2] == ( 'http://www.example.com/some/path?some=query&another=param&Expires=' '1549751401&KeyName=my-key&Signature=9Q9TCxSju8-W5nUkk5CuTrun2_o=') + + +def test_sign_url_prefix(capsys): + snippets.sign_url_prefix( + 'http://35.186.234.33/index.html', + 'http://35.186.234.33/', + 'my-key', + 'nZtRohdNF9m3cKM24IcK4w==', + datetime.datetime.utcfromtimestamp(1549751401)) + snippets.sign_url_prefix( + 'http://www.example.com/', + 'http://www.example.com/', + 'my-key', + 'nZtRohdNF9m3cKM24IcK4w==', + datetime.datetime.utcfromtimestamp(1549751401)) + snippets.sign_url_prefix( + 'http://www.example.com/some/path?some=query&another=param', + 'http://www.example.com/some/', + 'my-key', + 'nZtRohdNF9m3cKM24IcK4w==', + datetime.datetime.utcfromtimestamp(1549751401)) + + out, _ = capsys.readouterr() + + results = out.splitlines() + assert results[0] == ( + 'http://35.186.234.33/index.html?URLPrefix=aHR0cDovLzM1LjE4Ni4yMzQuMzMv&' + 'Expires=1549751401&KeyName=my-key&Signature=j7HYgoQ8dIOVsW3Rw4cpkjWfRMA=') + assert results[1] == ( + 'http://www.example.com/?URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS8=&' + 'Expires=1549751401&KeyName=my-key&Signature=UdT5nVks6Hh8QFMJI9kmXuXYBk0=') + assert results[2] == ( + 'http://www.example.com/some/path?some=query&another=param&' + 'URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS9zb21lLw==&' + 'Expires=1549751401&KeyName=my-key&Signature=3th4ThmpS95I1TAKYyYSCSq3dnQ=') + + +def test_sign_cookie(capsys): + snippets.sign_cookie( + 'http://35.186.234.33/index.html', + 'my-key', + 'nZtRohdNF9m3cKM24IcK4w==', + datetime.datetime.utcfromtimestamp(1549751401)) + snippets.sign_cookie( + 'http://www.example.com/foo/', + 'my-key', + 'nZtRohdNF9m3cKM24IcK4w==', + datetime.datetime.utcfromtimestamp(1549751401)) + + out, _ = capsys.readouterr() + + results = out.splitlines() + assert results[0] == ( + 'Cloud-CDN-Cookie=URLPrefix=aHR0cDovLzM1LjE4Ni4yMzQuMzMvaW5kZXguaHRtbA==:' + 'Expires=1549751401:KeyName=my-key:Signature=uImwlOBCPs91mlCyG9vyyZRrNWU=') + assert results[1] == ( + 'Cloud-CDN-Cookie=URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS9mb28v:' + 'Expires=1549751401:KeyName=my-key:Signature=Z9uYAu73YHioRScZDxnP-TnS274=')