Skip to content

Commit 154dce9

Browse files
mpwarresTakashi Matsuo
and
Takashi Matsuo
authored
cdn: add sign_cookie example with tests (#3281)
* Add code snippet and tests for Signed Cookies. * Address code review comments, and add code example for Signed URL prefix. * Fix lint error. * Group signed URL examples in single region, and adjust region tag to match existing documentation. Co-authored-by: Takashi Matsuo <[email protected]>
1 parent 39dff48 commit 154dce9

File tree

3 files changed

+177
-1
lines changed

3 files changed

+177
-1
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
/bigquery_storage/ @shollyman @GoogleCloudPlatform/python-samples-owners
1616
/bigtable/ @billyjacobson @GoogleCloudPlatform/python-samples-owners
1717
/blog/ @GoogleCloudPlatform/python-samples-owners
18-
/cdn/ @GoogleCloudPlatform/python-samples-owners
18+
/cdn/ @mpwarres @elithrar @GoogleCloudPlatform/python-samples-owners
1919
/cloud-sql/ @kvg @GoogleCloudPlatform/python-samples-owners
2020
/codelabs/ @GoogleCloudPlatform/python-samples-owners
2121
/composer/ @leahecole @GoogleCloudPlatform/python-samples-owners

cdn/snippets.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,87 @@ def sign_url(url, key_name, base64_key, expiration_time):
6868
url=url_to_sign, signature=signature)
6969

7070
print(signed_url)
71+
72+
73+
def sign_url_prefix(url, url_prefix, key_name, base64_key, expiration_time):
74+
"""Gets the Signed URL string for the specified URL prefix and configuration.
75+
76+
Args:
77+
url: URL of request.
78+
url_prefix: URL prefix to sign as a string.
79+
key_name: name of the signing key as a string.
80+
base64_key: signing key as a base64 encoded string.
81+
expiration_time: expiration time as a UTC datetime object.
82+
83+
Returns:
84+
Returns the Signed URL appended with the query parameters based on the
85+
specified URL prefix and configuration.
86+
"""
87+
stripped_url = url.strip()
88+
parsed_url = urllib.parse.urlsplit(stripped_url)
89+
query_params = urllib.parse.parse_qs(
90+
parsed_url.query, keep_blank_values=True)
91+
encoded_url_prefix = base64.urlsafe_b64encode(
92+
url_prefix.strip().encode('utf-8')).decode('utf-8')
93+
epoch = datetime.datetime.utcfromtimestamp(0)
94+
expiration_timestamp = int((expiration_time - epoch).total_seconds())
95+
decoded_key = base64.urlsafe_b64decode(base64_key)
96+
97+
policy_pattern = u'URLPrefix={encoded_url_prefix}&Expires={expires}&KeyName={key_name}'
98+
policy = policy_pattern.format(
99+
encoded_url_prefix=encoded_url_prefix,
100+
expires=expiration_timestamp,
101+
key_name=key_name)
102+
103+
digest = hmac.new(
104+
decoded_key, policy.encode('utf-8'), hashlib.sha1).digest()
105+
signature = base64.urlsafe_b64encode(digest).decode('utf-8')
106+
107+
signed_url = u'{url}{separator}{policy}&Signature={signature}'.format(
108+
url=stripped_url,
109+
separator='&' if query_params else '?',
110+
policy=policy,
111+
signature=signature)
112+
113+
print(signed_url)
71114
# [END sign_url]
72115

73116

117+
# [START cdn_sign_cookie]
118+
def sign_cookie(url_prefix, key_name, base64_key, expiration_time):
119+
"""Gets the Signed cookie value for the specified URL prefix and configuration.
120+
121+
Args:
122+
url_prefix: URL prefix to sign as a string.
123+
key_name: name of the signing key as a string.
124+
base64_key: signing key as a base64 encoded string.
125+
expiration_time: expiration time as a UTC datetime object.
126+
127+
Returns:
128+
Returns the Cloud-CDN-Cookie value based on the specified configuration.
129+
"""
130+
encoded_url_prefix = base64.urlsafe_b64encode(
131+
url_prefix.strip().encode('utf-8')).decode('utf-8')
132+
epoch = datetime.datetime.utcfromtimestamp(0)
133+
expiration_timestamp = int((expiration_time - epoch).total_seconds())
134+
decoded_key = base64.urlsafe_b64decode(base64_key)
135+
136+
policy_pattern = u'URLPrefix={encoded_url_prefix}:Expires={expires}:KeyName={key_name}'
137+
policy = policy_pattern.format(
138+
encoded_url_prefix=encoded_url_prefix,
139+
expires=expiration_timestamp,
140+
key_name=key_name)
141+
142+
digest = hmac.new(
143+
decoded_key, policy.encode('utf-8'), hashlib.sha1).digest()
144+
signature = base64.urlsafe_b64encode(digest).decode('utf-8')
145+
146+
signed_policy = u'Cloud-CDN-Cookie={policy}:Signature={signature}'.format(
147+
policy=policy, signature=signature)
148+
print(signed_policy)
149+
# [END cdn_sign_cookie]
150+
151+
74152
if __name__ == '__main__':
75153
parser = argparse.ArgumentParser(
76154
description=__doc__,
@@ -94,8 +172,48 @@ def sign_url(url, key_name, base64_key, expiration_time):
94172
type=lambda d: datetime.datetime.utcfromtimestamp(float(d)),
95173
help='Expiration time expessed as seconds since the epoch.')
96174

175+
sign_url_prefix_parser = subparsers.add_parser(
176+
'sign-url-prefix',
177+
help="Sign a URL prefix to grant temporary authorized access.")
178+
sign_url_prefix_parser.add_argument(
179+
'url', help='The request URL.')
180+
sign_url_prefix_parser.add_argument(
181+
'url_prefix', help='The URL prefix to sign.')
182+
sign_url_prefix_parser.add_argument(
183+
'key_name',
184+
help='Key name for the signing key.')
185+
sign_url_prefix_parser.add_argument(
186+
'base64_key',
187+
help='The base64 encoded signing key.')
188+
sign_url_prefix_parser.add_argument(
189+
'expiration_time',
190+
type=lambda d: datetime.datetime.utcfromtimestamp(float(d)),
191+
help='Expiration time expessed as seconds since the epoch.')
192+
193+
sign_cookie_parser = subparsers.add_parser(
194+
'sign-cookie',
195+
help="Generate a signed cookie to grant temporary authorized access.")
196+
sign_cookie_parser.add_argument(
197+
'url_prefix', help='The URL prefix to sign.')
198+
sign_cookie_parser.add_argument(
199+
'key_name',
200+
help='Key name for the signing key.')
201+
sign_cookie_parser.add_argument(
202+
'base64_key',
203+
help='The base64 encoded signing key.')
204+
sign_cookie_parser.add_argument(
205+
'expiration_time',
206+
type=lambda d: datetime.datetime.utcfromtimestamp(float(d)),
207+
help='Expiration time expessed as seconds since the epoch.')
208+
97209
args = parser.parse_args()
98210

99211
if args.command == 'sign-url':
100212
sign_url(
101213
args.url, args.key_name, args.base64_key, args.expiration_time)
214+
elif args.command == 'sign-url-prefix':
215+
sign_url_prefix(
216+
args.url, args.url_prefix, args.key_name, args.base64_key, args.expiration_time)
217+
elif args.command == 'sign-cookie':
218+
sign_cookie(
219+
args.url_prefix, args.key_name, args.base64_key, args.expiration_time)

cdn/snippets_test.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,61 @@ def test_sign_url(capsys):
5050
assert results[2] == (
5151
'http://www.example.com/some/path?some=query&another=param&Expires='
5252
'1549751401&KeyName=my-key&Signature=9Q9TCxSju8-W5nUkk5CuTrun2_o=')
53+
54+
55+
def test_sign_url_prefix(capsys):
56+
snippets.sign_url_prefix(
57+
'http://35.186.234.33/index.html',
58+
'http://35.186.234.33/',
59+
'my-key',
60+
'nZtRohdNF9m3cKM24IcK4w==',
61+
datetime.datetime.utcfromtimestamp(1549751401))
62+
snippets.sign_url_prefix(
63+
'http://www.example.com/',
64+
'http://www.example.com/',
65+
'my-key',
66+
'nZtRohdNF9m3cKM24IcK4w==',
67+
datetime.datetime.utcfromtimestamp(1549751401))
68+
snippets.sign_url_prefix(
69+
'http://www.example.com/some/path?some=query&another=param',
70+
'http://www.example.com/some/',
71+
'my-key',
72+
'nZtRohdNF9m3cKM24IcK4w==',
73+
datetime.datetime.utcfromtimestamp(1549751401))
74+
75+
out, _ = capsys.readouterr()
76+
77+
results = out.splitlines()
78+
assert results[0] == (
79+
'http://35.186.234.33/index.html?URLPrefix=aHR0cDovLzM1LjE4Ni4yMzQuMzMv&'
80+
'Expires=1549751401&KeyName=my-key&Signature=j7HYgoQ8dIOVsW3Rw4cpkjWfRMA=')
81+
assert results[1] == (
82+
'http://www.example.com/?URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS8=&'
83+
'Expires=1549751401&KeyName=my-key&Signature=UdT5nVks6Hh8QFMJI9kmXuXYBk0=')
84+
assert results[2] == (
85+
'http://www.example.com/some/path?some=query&another=param&'
86+
'URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS9zb21lLw==&'
87+
'Expires=1549751401&KeyName=my-key&Signature=3th4ThmpS95I1TAKYyYSCSq3dnQ=')
88+
89+
90+
def test_sign_cookie(capsys):
91+
snippets.sign_cookie(
92+
'http://35.186.234.33/index.html',
93+
'my-key',
94+
'nZtRohdNF9m3cKM24IcK4w==',
95+
datetime.datetime.utcfromtimestamp(1549751401))
96+
snippets.sign_cookie(
97+
'http://www.example.com/foo/',
98+
'my-key',
99+
'nZtRohdNF9m3cKM24IcK4w==',
100+
datetime.datetime.utcfromtimestamp(1549751401))
101+
102+
out, _ = capsys.readouterr()
103+
104+
results = out.splitlines()
105+
assert results[0] == (
106+
'Cloud-CDN-Cookie=URLPrefix=aHR0cDovLzM1LjE4Ni4yMzQuMzMvaW5kZXguaHRtbA==:'
107+
'Expires=1549751401:KeyName=my-key:Signature=uImwlOBCPs91mlCyG9vyyZRrNWU=')
108+
assert results[1] == (
109+
'Cloud-CDN-Cookie=URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS9mb28v:'
110+
'Expires=1549751401:KeyName=my-key:Signature=Z9uYAu73YHioRScZDxnP-TnS274=')

0 commit comments

Comments
 (0)