Skip to content

Commit 4ef241f

Browse files
committed
feat: add cdn signed url sample
1 parent 0ef037a commit 4ef241f

File tree

5 files changed

+126
-0
lines changed

5 files changed

+126
-0
lines changed
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: cdn-signed-urls
16+
on:
17+
push:
18+
branches:
19+
- main
20+
paths:
21+
- 'cdn/signed-urls/**'
22+
- '.github/workflows/cdn-signed-urls.yaml'
23+
pull_request:
24+
types:
25+
- opened
26+
- reopened
27+
- synchronize
28+
- labeled
29+
paths:
30+
- 'cdn/signed-urls/**'
31+
- '.github/workflows/cdn-signed-urls.yaml'
32+
schedule:
33+
- cron: '0 0 * * 0'
34+
jobs:
35+
test:
36+
# Ref: https://github.com/google-github-actions/auth#usage
37+
permissions:
38+
contents: 'read'
39+
id-token: 'write'
40+
if: github.event.action != 'labeled' || github.event.label.name == 'actions:force-run'
41+
uses: ./.github/workflows/test.yaml
42+
with:
43+
name: 'cdn-signed-urls'
44+
path: 'cdn/signed-urls'
45+
flakybot:
46+
# Ref: https://github.com/google-github-actions/auth#usage
47+
permissions:
48+
contents: 'read'
49+
id-token: 'write'
50+
if: github.event_name == 'schedule' && always() # always() submits logs even if tests fail
51+
uses: ./.github/workflows/flakybot.yaml
52+
needs: [test]

.github/workflows/utils/workflows.json

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"asset/snippets",
1919
"auth",
2020
"batch",
21+
"cdn/signed-urls",
2122
"cloudbuild",
2223
"cloud-language",
2324
"cloud-tasks/snippets",

cdn/signed-urls/package.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "signed-urls-samples",
3+
"private": true,
4+
"license": "Apache-2.0",
5+
"author": "Google LLC",
6+
"engines": {
7+
"node": ">=16.0.0"
8+
},
9+
"files": [
10+
"*.js"
11+
],
12+
"scripts": {
13+
"test": "mocha -p -j 2 **/*.test.js"
14+
},
15+
"devDependencies": {
16+
"mocha": "^10.0.0"
17+
}
18+
}

cdn/signed-urls/signurl.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// [START nodejs_cdn_signed_urls]
2+
const crypto = require('crypto');
3+
4+
/**
5+
* Sign url to access Google Cloud CDN resource secured by key.
6+
* @param url the Cloud CDN endpoint to sign
7+
* @param keyName name of the signing key configured in the backend service/bucket
8+
* @param keyValue value of the signing key
9+
* @param expirationDate the date that the signed URL expires
10+
* @return signed CDN URL
11+
*/
12+
function signUrl(url, keyName, keyValue, expirationDate) {
13+
const urlObject = new URL(url);
14+
urlObject.searchParams.set(
15+
'Expires',
16+
Math.floor(expirationDate.valueOf() / 1000).toString()
17+
);
18+
urlObject.searchParams.set('KeyName', keyName);
19+
20+
const signature = crypto
21+
.createHmac('sha1', Buffer.from(keyValue, 'base64'))
22+
.update(urlObject.href)
23+
.digest('base64url');
24+
urlObject.searchParams.set('Signature', signature);
25+
26+
return urlObject.href;
27+
}
28+
// [END nodejs_cdn_signed_urls]
29+
30+
module.exports = {
31+
signUrl,
32+
};

cdn/signed-urls/test/signurl.test.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const {signUrl} = require('../signurl');
2+
const assert = require('node:assert/strict');
3+
4+
describe('signUrl', () => {
5+
it('should return signed url with corresponding parameters', () => {
6+
const url = new URL('https://cdn.example.com/test-path');
7+
const keyName = 'test-key-name';
8+
const keyValue = '0zY26LFZ2yAa3fERaKiKDQ==';
9+
const expirationDate = new Date();
10+
11+
const resultUrl = new URL(
12+
signUrl(url.href, keyName, keyValue, expirationDate)
13+
);
14+
assert.strictEqual(resultUrl.hostname, url.hostname);
15+
assert.strictEqual(resultUrl.pathname, url.pathname);
16+
assert.strictEqual(resultUrl.searchParams.get('KeyName'), keyName);
17+
assert.strictEqual(
18+
resultUrl.searchParams.get('Expires'),
19+
Math.floor(expirationDate.valueOf() / 1000).toString()
20+
);
21+
assert.ok(resultUrl.searchParams.get('Signature'));
22+
});
23+
});

0 commit comments

Comments
 (0)