Skip to content

Commit 694c7b9

Browse files
authored
Merge pull request #63 from imagekit-developer/SDK-74
diactric fix
2 parents ff2d30a + ae6dcb2 commit 694c7b9

File tree

4 files changed

+107
-4
lines changed

4 files changed

+107
-4
lines changed

imagekitio/constants/defaults.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ class Default(enum.Enum):
1616
TRANSFORM_KEY_VALUE_DELIMITER = "-"
1717
SIGNATURE_PARAMETER = "ik-s"
1818
TIMESTAMP_PARAMETER = "ik-t"
19+
IGNORE_CHARACTERS = '~@#$&()*!+=:;,?/\''

imagekitio/url.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44
from datetime import datetime as dt
55
from typing import Any, Dict, List
6-
from urllib.parse import ParseResult, urlparse, urlunparse, parse_qsl, urlencode
6+
from urllib.parse import ParseResult, urlparse, urlunparse, parse_qsl, urlencode, quote, unquote
77

88
from .constants.defaults import Default
99
from .constants.supported_transform import SUPPORTED_TRANS
@@ -142,7 +142,7 @@ def get_signature(private_key, url, url_endpoint, expiry_timestamp: int) -> str:
142142
expiry_timestamp = Default.DEFAULT_TIMESTAMP.value
143143

144144
replaced_url = url.replace(url_endpoint, "") + str(expiry_timestamp)
145-
145+
replaced_url = Url.encode_string_if_required(replaced_url)
146146
signature = hmac.new(
147147
key=private_key.encode(), msg=replaced_url.encode(), digestmod=hashlib.sha1
148148
)
@@ -211,3 +211,21 @@ def transformation_to_str(transformation):
211211
)
212212

213213
return Default.CHAIN_TRANSFORM_DELIMITER.value.join(parsed_transforms)
214+
215+
@staticmethod
216+
def encodeURI(url_str):
217+
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI
218+
if "?" in url_str:
219+
# here we are not encoding query parameters as it is allready encoded
220+
encoded_url = quote(url_str.split('?')[0], safe=Default.IGNORE_CHARACTERS.value)+"?"+url_str.split('?')[1]
221+
else:
222+
encoded_url = quote(url_str, safe=Default.IGNORE_CHARACTERS.value)
223+
return encoded_url
224+
225+
@staticmethod
226+
def has_more_than_ascii(s):
227+
return any(ord(char) > 127 for char in s)
228+
229+
@staticmethod
230+
def encode_string_if_required(s):
231+
return Url.encodeURI(s) if Url.has_more_than_ascii(s) else s

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
setuptools.setup(
1010
name="imagekitio",
11-
version="4.0.0",
11+
version="4.0.1",
1212
description="Python wrapper for the ImageKit API",
1313
long_description=long_description,
1414
long_description_content_type="text/markdown",

tests/test_generate_url.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from imagekitio.client import ImageKit
44
from imagekitio.constants.defaults import Default
5-
5+
from imagekitio.url import Url
66

77
class TestGenerateURL(unittest.TestCase):
88
def setUp(self) -> None:
@@ -326,6 +326,90 @@ def test_url_signed_with_expire_in_seconds(self):
326326
url = self.client.url(options)
327327
self.assertIn("ik-t", url)
328328

329+
def test_url_signed_with_diacritic_in_filename(self):
330+
url = "https://test-domain.com/test-endpoint/test_é_path_alt.jpg"
331+
encodedUrl = Url.encode_string_if_required(url)
332+
self.assertEqual(
333+
encodedUrl,
334+
"https://test-domain.com/test-endpoint/test_%C3%A9_path_alt.jpg",
335+
)
336+
signature = Url.get_signature("private_key_test", url, "https://test-domain.com/test-endpoint", 9999999999)
337+
options = {
338+
"path": "/test_é_path_alt.jpg",
339+
"signed": True,
340+
}
341+
url = self.client.url(options)
342+
self.assertEqual(
343+
url,
344+
"https://test-domain.com/test-endpoint/test_é_path_alt.jpg?ik-s="+signature,
345+
)
346+
347+
def test_url_signed_with_diacritic_in_filename_and_path(self):
348+
url = "https://test-domain.com/test-endpoint/aéb/test_é_path_alt.jpg"
349+
encodedUrl = Url.encode_string_if_required(url)
350+
self.assertEqual(
351+
encodedUrl,
352+
"https://test-domain.com/test-endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg",
353+
)
354+
signature = Url.get_signature("private_key_test", url, "https://test-domain.com/test-endpoint", 9999999999)
355+
options = {
356+
"path": "/aéb/test_é_path_alt.jpg",
357+
"signed": True,
358+
}
359+
url = self.client.url(options)
360+
self.assertEqual(
361+
url,
362+
"https://test-domain.com/test-endpoint/aéb/test_é_path_alt.jpg?ik-s="+signature,
363+
)
364+
365+
def test_url_signed_with_diacritic_in_filename_path_transforamtion_in_path(self):
366+
url = "https://test-domain.com/test-endpoint/tr:l-text,i-Imagekité,fs-50,l-end/aéb/test_é_path_alt.jpg"
367+
encodedUrl = Url.encode_string_if_required(url)
368+
self.assertEqual(
369+
encodedUrl,
370+
"https://test-domain.com/test-endpoint/tr:l-text,i-Imagekit%C3%A9,fs-50,l-end/a%C3%A9b/test_%C3%A9_path_alt.jpg",
371+
)
372+
signature = Url.get_signature("private_key_test", url, "https://test-domain.com/test-endpoint", 9999999999)
373+
options = {
374+
"path": "/aéb/test_é_path_alt.jpg",
375+
"transformation": [
376+
{
377+
"raw": "l-text,i-Imagekité,fs-50,l-end"
378+
},
379+
],
380+
"signed": True,
381+
"transformation_position": "path"
382+
}
383+
url = self.client.url(options)
384+
self.assertEqual(
385+
url,
386+
"https://test-domain.com/test-endpoint/tr:l-text,i-Imagekité,fs-50,l-end/aéb/test_é_path_alt.jpg?ik-s="+signature,
387+
)
388+
389+
def test_url_signed_with_diacritic_in_filename_path_transforamtion_in_query(self):
390+
url = "https://test-domain.com/test-endpoint/aéb/test_é_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end"
391+
encodedUrl = Url.encode_string_if_required(url)
392+
self.assertEqual(
393+
encodedUrl,
394+
"https://test-domain.com/test-endpoint/a%C3%A9b/test_%C3%A9_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end",
395+
)
396+
signature = Url.get_signature("private_key_test", url, "https://test-domain.com/test-endpoint", 9999999999)
397+
options = {
398+
"path": "/aéb/test_é_path_alt.jpg",
399+
"transformation": [
400+
{
401+
"raw": "l-text,i-Imagekité,fs-50,l-end"
402+
},
403+
],
404+
"signed": True,
405+
"transformation_position": "query"
406+
}
407+
url = self.client.url(options)
408+
self.assertEqual(
409+
url,
410+
"https://test-domain.com/test-endpoint/aéb/test_é_path_alt.jpg?tr=l-text%2Ci-Imagekit%C3%A9%2Cfs-50%2Cl-end&ik-s="+signature,
411+
)
412+
329413
def test_generate_url_with_path_and_src_uses_path(self):
330414
"""
331415
In case when both path and src fields are provided, the `path` should be preferred

0 commit comments

Comments
 (0)