diff --git a/cdn/README.rst b/cdn/README.rst new file mode 100644 index 00000000000..18b522dc360 --- /dev/null +++ b/cdn/README.rst @@ -0,0 +1,55 @@ +.. This file is automatically generated. Do not edit this file directly. + +Google Cloud CDN Python Samples +=============================================================================== + +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=cdn/README.rst + + +This directory contains samples for Google Cloud CDN. 'Google Cloud CDN'_ enables low-latency, low-cost content delivery using Google's global network + + + + +.. _Google Cloud CDN: https://cloud.google.com/cdn/docs + + +Samples +------------------------------------------------------------------------------- + +Snippets ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=cdn/snippets.py;cdn/README.rst + + + + +To run this sample: + +.. code-block:: bash + + $ python snippets.py + + usage: snippets.py [-h] {sign-url} ... + + This application demonstrates how to perform operations on data (content) + when using Google Cloud CDN (Content Delivery Network). + + For more information, see the README.md under /cdn and the documentation + at https://cloud.google.com/cdn/docs. + + positional arguments: + {sign-url} + sign-url Sign a URL to grant temporary authorized access. + + optional arguments: + -h, --help show this help message and exit + + + + + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/cdn/README.rst.in b/cdn/README.rst.in new file mode 100644 index 00000000000..f9c47171edc --- /dev/null +++ b/cdn/README.rst.in @@ -0,0 +1,16 @@ +# Used to generate README.rst + +product: + name: Google Cloud CDN + short_name: Cloud CDN + url: https://cloud.google.com/cdn/docs + description: > + 'Google Cloud CDN'_ enables low-latency, low-cost content delivery using + Google's global network + +samples: + - name: Snippets + file: snippets.py + show_help: true + +folder: cdn diff --git a/cdn/requirements.txt b/cdn/requirements.txt new file mode 100644 index 00000000000..ffe2fce4989 --- /dev/null +++ b/cdn/requirements.txt @@ -0,0 +1 @@ +six diff --git a/cdn/snippets.py b/cdn/snippets.py new file mode 100644 index 00000000000..753957d578e --- /dev/null +++ b/cdn/snippets.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +# +# Copyright 2017 Google, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This application demonstrates how to perform operations on data (content) +when using Google Cloud CDN (Content Delivery Network). + +For more information, see the README.md under /cdn and the documentation +at https://cloud.google.com/cdn/docs. +""" + +import argparse +import base64 +import datetime +import hashlib +import hmac + +from six.moves import urllib + + +# [BEGIN sign_url] +def sign_url(url, key_name, base64_key, expiration_time): + """Gets the Signed URL string for the specified URL and configuration. + + Args: + url: URL 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 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) + epoch = datetime.datetime.utcfromtimestamp(0) + expiration_timestamp = int((expiration_time - epoch).total_seconds()) + decoded_key = base64.urlsafe_b64decode(base64_key) + + url_pattern = u'{url}{separator}Expires={expires}&KeyName={key_name}' + + url_to_sign = url_pattern.format( + url=stripped_url, + separator='&' if query_params else '?', + expires=expiration_timestamp, + key_name=key_name) + + digest = hmac.new( + decoded_key, url_to_sign.encode('utf-8'), hashlib.sha1).digest() + signature = base64.urlsafe_b64encode(digest).decode('utf-8') + + signed_url = u'{url}&Signature={signature}'.format( + url=url_to_sign, signature=signature) + + print(signed_url) +# [END sign_url] + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + + subparsers = parser.add_subparsers(dest='command') + + sign_url_parser = subparsers.add_parser( + 'sign-url', + help="Sign a URL to grant temporary authorized access.") + sign_url_parser.add_argument( + 'url', help='The URL to sign.') + sign_url_parser.add_argument( + 'key_name', + help='Key name for the signing key.') + sign_url_parser.add_argument( + 'base64_key', + help='The base64 encoded signing key.') + sign_url_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) diff --git a/cdn/snippets_test.py b/cdn/snippets_test.py new file mode 100644 index 00000000000..245daea13cd --- /dev/null +++ b/cdn/snippets_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# +# Copyright 2017 Google, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for snippets.""" + +import datetime + +import snippets + + +def test_sign_url(capsys): + snippets.sign_url( + 'http://35.186.234.33/index.html', + 'my-key', + 'nZtRohdNF9m3cKM24IcK4w==', + datetime.datetime.utcfromtimestamp(1549751401)) + snippets.sign_url( + 'http://www.example.com/', + 'my-key', + 'nZtRohdNF9m3cKM24IcK4w==', + datetime.datetime.utcfromtimestamp(1549751401)) + snippets.sign_url( + 'http://www.example.com/some/path?some=query&another=param', + 'my-key', + 'nZtRohdNF9m3cKM24IcK4w==', + datetime.datetime.utcfromtimestamp(1549751401)) + + out, _ = capsys.readouterr() + + results = out.splitlines() + assert results[0] == ( + 'http://35.186.234.33/index.html?Expires=1549751401&KeyName=my-key&' + 'Signature=CRFqQnVfFyiUyR63OQf-HRUpIwc=') + assert results[1] == ( + 'http://www.example.com/?Expires=1549751401&KeyName=my-key&' + 'Signature=OqDUFfHpN5Vxga6r80bhsgxKves=') + assert results[2] == ( + 'http://www.example.com/some/path?some=query&another=param&Expires=' + '1549751401&KeyName=my-key&Signature=9Q9TCxSju8-W5nUkk5CuTrun2_o=')