Skip to content

Commit f87b0b6

Browse files
author
Marius Kleidl
committed
Add method for generating signed Smart CDN URLs
1 parent 6f12620 commit f87b0b6

2 files changed

Lines changed: 75 additions & 0 deletions

File tree

tests/test_client.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import unittest
2+
from unittest import mock
23

34
import requests_mock
45
from six.moves import urllib
@@ -94,3 +95,24 @@ def test_get_bill(self, mock):
9495

9596
response = self.transloadit.get_bill(month, year)
9697
self.assertEqual(response.data["ok"], "BILL_FOUND")
98+
99+
def test_get_signed_smart_cdn_url(self):
100+
client = Transloadit("foo_key", "foo_secret")
101+
102+
# Freeze time to 2024-05-01T00:00:00.000Z for consistent signatures
103+
with mock.patch('time.time', return_value=1714521600):
104+
url = client.get_signed_smart_cdn_url(
105+
workspace="foo_workspace",
106+
template="foo_template",
107+
input="foo/input",
108+
url_params={
109+
"foo": "bar",
110+
"aaa": 42 # Should be sorted as first param
111+
}
112+
)
113+
114+
expected_url = (
115+
"https://foo_workspace.tlcdn.com/foo_template/foo%2Finput?aaa=42&auth_key=foo_key&exp=1714525200000&foo=bar&sig=sha256:995dd1aae135fb77fa98b0e6946bd9768e0443a6028eba0361c03807e8fb68a5"
116+
)
117+
118+
self.assertEqual(url, expected_url)

transloadit/client.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import typing
2+
import hmac
3+
import hashlib
4+
import time
5+
from urllib.parse import urlencode, quote_plus
26

37
from typing import Optional
48

@@ -168,3 +172,52 @@ def get_bill(self, month: int, year: int):
168172
Return an instance of <transloadit.response.Response>
169173
"""
170174
return self.request.get(f"/bill/{year}-{month:02d}")
175+
176+
def get_signed_smart_cdn_url(
177+
self,
178+
workspace: str,
179+
template: str,
180+
input: str,
181+
url_params: Optional[dict] = None,
182+
expires_in: Optional[int] = 60 * 60 * 1000 # 1 hour
183+
) -> str:
184+
"""
185+
Construct a signed Smart CDN URL.
186+
See https://transloadit.com/docs/topics/signature-authentication/#smart-cdn
187+
188+
:Args:
189+
- workspace (str): Workspace slug
190+
- template (str): Template slug or template ID
191+
- input (str): Input value that is provided as ${fields.input} in the template
192+
- url_params (Optional[dict]): Additional parameters for the URL query string
193+
- expires_in (Optional[int]): Expiration time of signature in milliseconds. Defaults to 1 hour.
194+
195+
:Returns:
196+
str: The signed Smart CDN URL
197+
"""
198+
workspace_slug = quote_plus(workspace)
199+
template_slug = quote_plus(template)
200+
input_field = quote_plus(input)
201+
202+
# Convert url_params values to strings
203+
params = {}
204+
if url_params:
205+
params.update({k: str(v) for k, v in url_params.items()})
206+
207+
params["auth_key"] = self.auth_key
208+
params["exp"] = str(int(time.time() * 1000) + expires_in)
209+
210+
# Sort params alphabetically
211+
sorted_params = dict(sorted(params.items()))
212+
query_string = urlencode(sorted_params)
213+
214+
string_to_sign = f"{workspace_slug}/{template_slug}/{input_field}?{query_string}"
215+
algorithm = "sha256"
216+
217+
signature = hmac.new(
218+
self.auth_secret.encode("utf-8"),
219+
string_to_sign.encode("utf-8"),
220+
hashlib.sha256
221+
).hexdigest()
222+
223+
return f"https://{workspace_slug}.tlcdn.com/{template_slug}/{input_field}?{query_string}&sig={algorithm}:{signature}"

0 commit comments

Comments
 (0)