Skip to content

Commit c921e0b

Browse files
Manu ChaudharyManu Chaudhary
authored andcommitted
Fix for signature
1 parent 2b7e6d4 commit c921e0b

File tree

4 files changed

+52
-120
lines changed

4 files changed

+52
-120
lines changed

imagekitio/constants/errors.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ class ERRORS(enum.Enum):
1010
"message": "Invalid transformationPosition parameter",
1111
help: "",
1212
}
13+
MANDATORY_SRC_OR_PATH = {
14+
"message": "Pass one of the mandatory parameter path or src"
15+
}
1316
INVALID_URL_GENERATION_PARAMETER = {"message": "Invalid url parameter", help: ""}
1417
INVALID_TRANSFORMATION_OPTIONS = {
1518
"message": "Invalid transformation parameter options",

imagekitio/resource.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,7 @@ def get_auth_headers(self):
4242
4343
:return: dictionary of encoded private key
4444
"""
45-
# checking if ':' is appearing for basic authentication
46-
# otherwise password will be required with username
47-
# see basic authentication related articles about
48-
# being authenticated only with username
49-
if self.private_key[-1] != ":":
50-
self.private_key += ":"
51-
encoded_private_key = base64.b64encode(self.private_key.encode()).decode(
45+
encoded_private_key = base64.b64encode((self.private_key + ":").encode()).decode(
5246
"utf-8"
5347
)
5448
return {"Authorization": "Basic {}".format(encoded_private_key)}

imagekitio/url.py

Lines changed: 38 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import hashlib
22
import hmac
3+
import sys
34
from datetime import datetime as dt
45
from typing import Any, Dict, List
5-
from urllib.parse import ParseResult, urlparse, urlunparse
6+
from urllib.parse import ParseResult, urlparse, urlunparse, parse_qsl, urlencode
67

78
from imagekitio.constants.defaults import Default
89
from imagekitio.constants.supported_transform import SUPPORTED_TRANS
@@ -33,135 +34,69 @@ def __init__(self, request_obj):
3334

3435
def generate_url(self, options: Dict = None) -> str:
3536
options = camel_dict_to_snake_dict(options)
36-
if options.get("src"):
37-
options["transformation_position"] = DEFAULT_TRANSFORMATION_POSITION
3837
extended_options = self.request.extend_url_options(options)
3938
return self.build_url(extended_options)
4039

4140
def build_url(self, options: dict) -> str:
4241
"""
4342
builds url for from all options,
4443
"""
45-
path = options.get("path", "")
46-
src = options.get("src", "")
47-
url_endpoint = options.get("url_endpoint", "")
48-
transformation_position = options.get("transformation_position")
44+
45+
path = options.get("path", "").strip("/")
46+
src = options.get("src", "").strip("/")
47+
url_endpoint = options.get("url_endpoint", "").strip("/")
48+
transformation_str = self.transformation_to_str(options.get("transformation"))
49+
transformation_position = options.get("transformation_position") or DEFAULT_TRANSFORMATION_POSITION
50+
4951
if transformation_position not in Default.VALID_TRANSFORMATION_POSITION.value:
5052
raise ValueError(ERRORS.INVALID_TRANSFORMATION_POSITION.value)
5153

52-
if src or (
53-
options.get(
54-
"transformation_position") == QUERY_TRANSFORMATION_POSITION
55-
):
56-
src_param_used_for_url = True
57-
else:
58-
src_param_used_for_url = False
59-
if not (path or src):
54+
if (path is "" and src is ""):
6055
return ""
61-
result_url_dict = {"netloc": "", "path": "", "query": ""}
62-
path_args = ""
63-
if path:
64-
if len(path.split("?")) > 1:
65-
path_args = path.split("?")[1]
66-
parsed_url = urlparse(path)
67-
parsed_host = urlparse(url_endpoint)
68-
result_url_dict["scheme"] = parsed_host.scheme
69-
result_url_dict["netloc"] = (parsed_host.netloc + parsed_host.path).lstrip(
70-
"/"
71-
)
72-
result_url_dict["path"] = parsed_url.path.strip("/")
73-
result_url_dict["query"] = path_args
7456

57+
if src:
58+
temp_url = src.strip("/")
59+
transformation_position = QUERY_TRANSFORMATION_POSITION
7560
else:
76-
parsed_url = urlparse(src)
77-
if len(src.split("?")) > 1:
78-
path_args = src.split("?")[1]
79-
host = parsed_url.netloc
80-
if parsed_url.username:
81-
# creating host like username:[email protected] if username is there in parsed url
82-
host = "{}:{}@{}".format(
83-
parsed_url.username, parsed_url.password, parsed_url.netloc
84-
)
85-
result_url_dict["netloc"] = host
86-
result_url_dict["scheme"] = parsed_url.scheme
87-
result_url_dict["path"] = parsed_url.path
88-
src_param_used_for_url = True
89-
query_params = options.get("query_parameters", {})
90-
transformation_str = self.transformation_to_str(
91-
options.get("transformation"))
92-
if transformation_str:
93-
if (
94-
transformation_position == Default.QUERY_TRANSFORMATION_POSITION.value
95-
) or src_param_used_for_url:
96-
result_url_dict["query"] = "{}={}".format(
97-
TRANSFORMATION_PARAMETER, transformation_str
61+
if transformation_position == "path":
62+
temp_url = "{}/{}:{}/{}".format(
63+
url_endpoint.strip("/"),
64+
TRANSFORMATION_PARAMETER,
65+
transformation_str.strip("/"),
66+
path.strip("/")
9867
)
99-
10068
else:
101-
result_url_dict["path"] = "{}:{}/{}".format(
102-
TRANSFORMATION_PARAMETER,
103-
transformation_str,
104-
result_url_dict["path"],
69+
temp_url = "{}/{}".format(
70+
url_endpoint.strip("/"),
71+
path.strip("/")
10572
)
10673

107-
result_url_dict["scheme"] = result_url_dict["scheme"] or "https"
74+
url_object = urlparse(temp_url.strip("/"))
75+
76+
query_params = dict(parse_qsl(url_object.query))
77+
query_params.update(options.get("query_parameters", {}))
78+
if transformation_position == QUERY_TRANSFORMATION_POSITION:
79+
query_params.update({"tr": transformation_str})
80+
query_params.update({"ik-sdk-version": Default.SDK_VERSION.value})
81+
82+
# Update query params
83+
url_object = url_object._replace(query=urlencode(query_params))
10884

109-
# Signature String and Timestamp
110-
# We can do this only for URLs that are created using urlEndpoint and path parameter
111-
# because we need to know the endpoint to be able to remove it from the URL to create a signature
112-
# for the remaining. With the src parameter, we would not know the "pattern" in the URL
113-
if options.get("signed") and (not options.get("src")):
85+
if options.get("signed"):
11486
expire_seconds = options.get("expire_seconds")
11587
private_key = options.get("private_key")
11688
expiry_timestamp = self.get_signature_timestamp(expire_seconds)
117-
118-
# Temporary variable to generate the URL signature
119-
modified_result_url_dict = self.prepare_dict_for_unparse(
120-
result_url_dict)
121-
intermediate_url = urlunparse(
122-
modified_result_url_dict.get(f, "") for f in ParseResult._fields
123-
)
12489
url_signature = self.get_signature(
12590
private_key=private_key,
126-
url=intermediate_url,
91+
url=url_object.geturl(),
12792
url_endpoint=url_endpoint,
12893
expiry_timestamp=expiry_timestamp,
12994
)
130-
if expiry_timestamp and (expiry_timestamp != DEFAULT_TIMESTAMP):
131-
query_params[TIMESTAMP_PARAMETER] = expiry_timestamp
132-
query_params[SIGNATURE_PARAMETER] = url_signature
133-
query_params_str = "&".join(
134-
str(k) + "=" + str(v) for k, v in query_params.items()
135-
)
136-
result_url_dict["query"] = query_params_str
137-
138-
result_url_dict = self.prepare_dict_for_unparse(result_url_dict)
139-
if path_args:
140-
updated_query = path_args + "&" + result_url_dict["query"]
141-
result_url_dict["query"] = updated_query
142-
generated_url = urlunparse(
143-
result_url_dict.get(f, "") for f in ParseResult._fields
144-
)
145-
146-
if result_url_dict["query"]:
147-
generated_url = (
148-
generated_url + "&ik-sdk-version=" + Default.SDK_VERSION.value
149-
)
150-
else:
151-
generated_url = (
152-
generated_url + "?ik-sdk-version=" + Default.SDK_VERSION.value
153-
)
154-
155-
extra_query_parameters = options.get("query_parameters")
156-
if extra_query_parameters is not None:
157-
extra_query_parameters_str = "&".join(
158-
str(k) + "=" + str(v) for k, v in extra_query_parameters.items()
159-
)
160-
generated_url = (
161-
generated_url + "&" + extra_query_parameters_str
162-
)
95+
query_params.update({TIMESTAMP_PARAMETER: expiry_timestamp, SIGNATURE_PARAMETER: url_signature})
96+
# Update signature related query params
97+
url_object = url_object._replace(query=urlencode(query_params))
16398

164-
return generated_url
99+
return url_object.geturl()
165100

166101
@staticmethod
167102
def get_signature_timestamp(seconds: int = None) -> int:

tests/test_generate_url.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ def test_generate_url_query_parameters(self):
107107
url = self.client.url(options)
108108
self.assertEqual(
109109
url,
110-
"https://test-domain.com/test-endpoint/tr:h-300,w-400/default-image.jpg?ik-sdk-version={}".format(
110+
"https://test-domain.com/test-endpoint/tr:h-300,w-400/default-image.jpg?param1=value1&param2=value2&ik-sdk-version={}".format(
111111
Default.SDK_VERSION.value
112-
) + "&param1=value1&param2=value2",
112+
),
113113
)
114114

115115
def test_generate_url_with_src(self):
@@ -129,7 +129,7 @@ def test_generate_url_with_src(self):
129129
url = self.client.url(options)
130130
self.assertEqual(
131131
url,
132-
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?tr=h-300,w-400,f-jpg,pr-true,e-contrast-1:rt-90&ik-sdk-version={}".format(
132+
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?tr=h-300%2Cw-400%2Cf-jpg%2Cpr-true%2Ce-contrast-1%3Art-90&ik-sdk-version={}".format(
133133
Default.SDK_VERSION.value
134134
),
135135
)
@@ -155,9 +155,9 @@ def test_generate_url_with_src_with_query_params_double(self):
155155
# @TODO - adjust value of param1=value1 in test case but it should be there
156156
self.assertEqual(
157157
url,
158-
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?queryparam1=value1&tr=h-300,w-400,f-jpg,pr-true,e-contrast-1:rt-90&ik-sdk-version={}".format(
158+
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?queryparam1=value1&param1=value1&tr=h-300%2Cw-400%2Cf-jpg%2Cpr-true%2Ce-contrast-1%3Art-90&ik-sdk-version={}".format(
159159
Default.SDK_VERSION.value
160-
) + "&param1=value1",
160+
)
161161
)
162162

163163
def test_generate_url_with_path_and_signed(self):
@@ -206,7 +206,7 @@ def test_url_with_new_transformation_returns_as_it_is(self):
206206
self.assertIn("fake_xxxx", url)
207207
self.assertEqual(
208208
url,
209-
"https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300,fake_xxxx-400&ik-sdk-version={}".format(
209+
"https://ik.imagekit.io/your_imagekit_id/endpoint/default-image.jpg?tr=h-300%2Cfake_xxxx-400&ik-sdk-version={}".format(
210210
Default.SDK_VERSION.value
211211
),
212212
)
@@ -246,7 +246,7 @@ def test_generate_url_with_chained_transformations(self):
246246
url = self.client.url(options)
247247
self.assertEqual(
248248
url,
249-
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?tr=h-300,w-400,f-jpg,pr-true,e-contrast-1:rt-90&ik-sdk-version={}".format(
249+
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?tr=h-300%2Cw-400%2Cf-jpg%2Cpr-true%2Ce-contrast-1%3Art-90&ik-sdk-version={}".format(
250250
Default.SDK_VERSION.value
251251
),
252252
)
@@ -259,7 +259,7 @@ def test_url_check_query_param_are_added_correctly(self):
259259
}
260260
url = self.client.url(options)
261261
self.assertEqual(url,
262-
"https://test-domain.com/test-endpoint/default-image.jpg?client=123&user=5&tr=h-300,w-400&ik-sdk-version={}".format(
262+
"https://test-domain.com/test-endpoint/default-image.jpg?client=123&user=5&tr=h-300%2Cw-400&ik-sdk-version={}".format(
263263
Default.SDK_VERSION.value))
264264

265265
def test_generate_url_with_src_query_parameters_merge_correctly(self):
@@ -279,7 +279,7 @@ def test_generate_url_with_src_query_parameters_merge_correctly(self):
279279
url = self.client.url(options)
280280
self.assertEqual(
281281
url,
282-
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?client=123&ab=c&tr=h-300,w-400,f-jpg,pr-true,e-contrast-1:rt-90&ik-sdk-version={}".format(
282+
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?client=123&ab=c&tr=h-300%2Cw-400%2Cf-jpg%2Cpr-true%2Ce-contrast-1%3Art-90&ik-sdk-version={}".format(
283283
Default.SDK_VERSION.value
284284
),
285285
)
@@ -302,7 +302,7 @@ def test_generate_url_with_src_and_transformation_position_path(self):
302302
url = self.client.url(options)
303303
self.assertEqual(
304304
url,
305-
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?tr=h-300,w-400,f-jpg,pr-true,e-contrast-1:rt-90&ik-sdk-version={}".format(
305+
"https://ik.imagekit.io/ldt7znpgpjs/test_YhNhoRxWt.jpg?tr=h-300%2Cw-400%2Cf-jpg%2Cpr-true%2Ce-contrast-1%3Art-90&ik-sdk-version={}".format(
306306
Default.SDK_VERSION.value
307307
),
308308
)

0 commit comments

Comments
 (0)