Skip to content

Commit 629dfcf

Browse files
bronabshaffer
authored andcommitted
Adds example for generating Cloud CDN Signed URLs (GoogleCloudPlatform#805)
1 parent ec3751f commit 629dfcf

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed

cdn/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Google Cloud CDN Sign URL
2+
3+
PHP implementation of [`gcloud compute sign-url`](https://cloud.google.com/sdk/gcloud/reference/compute/sign-url) based on [Google Cloud CDN Documentation](https://cloud.google.com/cdn/docs/using-signed-urls#signing_urls).
4+
The provided file includes implementation of base64url encode and decode functions based on [RFC4648 Section 5](https://tools.ietf.org/html/rfc4648#section-5).
5+
6+
## Usage
7+
8+
```
9+
10+
require_once 'signUrl.php';
11+
$base64url_key = 'wpLL7f4VB9RNe_WI0BBGmA=='; // head -c 16 /dev/urandom | base64 | tr +/ -_
12+
$signed_url = signUrl('https://example.com/foo', 'my-key', $base64url_key, time() + 1800);
13+
echo $signed_url;
14+
?>
15+
```

cdn/phpunit.xml.dist

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
xml version="1.0" encoding="UTF-8"?>
2+
17+
<phpunit>
18+
<testsuites>
19+
<testsuite name="Cloud CDN Sign URL Test">
20+
<directory>testdirectory>
21+
testsuite>
22+
testsuites>
23+
<logging>
24+
<log type="coverage-clover" target="build/logs/clover.xml"/>
25+
logging>
26+
<filter>
27+
<whitelist>
28+
<file>signUrl.phpfile>
29+
whitelist>
30+
filter>
31+
phpunit>

cdn/signUrl.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
/*
3+
* Copyright 2018 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
# [START signed_url]
19+
/**
20+
* Decodes base64url (RFC4648 Section 5) string
21+
*
22+
* @param string $input base64url encoded string
23+
*
24+
* @return string
25+
*/
26+
function base64url_decode($input)
27+
{
28+
$input .= str_repeat('=', (4 - strlen($input) % 4) % 4);
29+
return base64_decode(strtr($input, '-_', '+/'));
30+
}
31+
32+
/**
33+
* Encodes a string with base64url (RFC4648 Section 5)
34+
* Keeps the '=' padding by default.
35+
*
36+
* @param string $input String to be encoded
37+
* @param bool $padding Keep the '=' padding
38+
*
39+
* @return string
40+
*/
41+
function base64url_encode($input, $padding = true)
42+
{
43+
$output = strtr(base64_encode($input), '+/', '-_');
44+
return ($padding) ? $output : str_replace('=', '', $output);
45+
}
46+
47+
/**
48+
* Creates signed URL for Google Cloud CDN
49+
* Details about order of operations: https://cloud.google.com/cdn/docs/using-signed-urls#creating_signed_urls
50+
*
51+
* Example function invocation (In production store the key safely with other secrets):
52+
*
53+
*
54+
* $base64UrlKey = 'wpLL7f4VB9RNe_WI0BBGmA=='; // head -c 16 /dev/urandom | base64 | tr +/ -_
55+
* $signedUrl = sign_url('https://example.com/foo', 'my-key', $base64UrlKey, time() + 1800);
56+
* echo $signedUrl;
57+
* ?>
58+
*
59+
* @param string $url URL of the endpoint served by Cloud CDN
60+
* @param string $keyName Name of the signing key added to the Google Cloud Storage bucket or service
61+
* @param string $base64UrlKey Signing key as base64url (RFC4648 Section 5) encoded string
62+
* @param int $expirationTime Expiration time as a UNIX timestamp (GMT, e.g. time())
63+
*
64+
* @return string
65+
*/
66+
function sign_url($url, $keyName, $base64UrlKey, $expirationTime)
67+
{
68+
// Decode the key
69+
$decodedKey = base64url_decode($base64UrlKey, true);
70+
71+
// Determine which separator makes sense given a URL
72+
$separator = (strpos($url, '?') === false) ? '?' : '&';
73+
74+
// Concatenate url with expected query parameters Expires and KeyName
75+
$url = "{$url}{$separator}Expires={$expirationTime}&KeyName={$keyName}";
76+
77+
// Sign the url using the key and encode the signature using base64url
78+
$signature = hash_hmac('sha1', $url, $decodedKey, true);
79+
$encodedSignature = base64url_encode($signature);
80+
81+
// Concatenate the URL and encoded signature
82+
return "{$url}&Signature={$encodedSignature}";
83+
}
84+
// [END signed_url]

cdn/test/signUrlTest.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
/*
3+
* Copyright 2018 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
use PHPUnit\Framework\TestCase;
19+
20+
require_once __DIR__."/../signUrl.php";
21+
22+
class signUrlTest extends TestCase
23+
{
24+
public function testBase64UrlEncode()
25+
{
26+
$this->assertEquals(base64url_encode(hex2bin("9d9b51a2174d17d9b770a336e0870ae3")), "nZtRohdNF9m3cKM24IcK4w==");
27+
}
28+
29+
public function testBase64UrlEncodeWithoutPadding()
30+
{
31+
$this->assertEquals(base64url_encode(hex2bin("9d9b51a2174d17d9b770a336e0870ae3"), false), "nZtRohdNF9m3cKM24IcK4w");
32+
}
33+
34+
public function testBase64UrlDecode()
35+
{
36+
$this->assertEquals(hex2bin("9d9b51a2174d17d9b770a336e0870ae3"), base64url_decode("nZtRohdNF9m3cKM24IcK4w=="));
37+
}
38+
39+
public function testBase64UrlDecodeWithoutPadding()
40+
{
41+
$this->assertEquals(hex2bin("9d9b51a2174d17d9b770a336e0870ae3"), base64url_decode("nZtRohdNF9m3cKM24IcK4w"));
42+
}
43+
44+
public function testSignUrl()
45+
{
46+
$encoded_key="nZtRohdNF9m3cKM24IcK4w=="; // base64url encoded key
47+
48+
$cases = array(
49+
array("http://35.186.234.33/index.html", "my-key", 1558131350,
50+
"http://35.186.234.33/index.html?Expires=1558131350&KeyName=my-key&Signature=fm6JZSmKNsB5sys8VGr-JE4LiiE="),
51+
array("https://www.google.com/", "my-key", 1549751401,
52+
"https://www.google.com/?Expires=1549751401&KeyName=my-key&Signature=M_QO7BGHi2sGqrJO-MDr0uhDFuc="),
53+
array("https://www.example.com/some/path?some=query&another=param", "my-key", 1549751461,
54+
"https://www.example.com/some/path?some=query&another=param&Expires=1549751461&KeyName=my-key&Signature=sTqqGX5hUJmlRJ84koAIhWW_c3M="),
55+
);
56+
57+
foreach($cases as $c)
58+
{
59+
$this->assertEquals(sign_url($c[0], $c[1], $encoded_key, $c[2]), $c[3]);
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)