|
3 | 3 | import re |
4 | 4 | import tempfile |
5 | 5 | import unittest |
| 6 | +import uuid |
| 7 | +import json |
| 8 | +import time |
6 | 9 | from collections import OrderedDict |
7 | 10 | from datetime import datetime, date |
8 | 11 | from fractions import Fraction |
|
13 | 16 |
|
14 | 17 | import cloudinary.utils |
15 | 18 | from cloudinary import CL_BLANK |
16 | | -from cloudinary.utils import build_list_of_dicts, json_encode, encode_unicode_url, base64url_encode, \ |
17 | | - patch_fetch_format, cloudinary_scaled_url, chain_transformations, generate_transformation_string, build_eager |
| 19 | +from cloudinary.utils import ( |
| 20 | + build_list_of_dicts, |
| 21 | + json_encode, |
| 22 | + encode_unicode_url, |
| 23 | + base64url_encode, |
| 24 | + patch_fetch_format, |
| 25 | + cloudinary_scaled_url, |
| 26 | + chain_transformations, |
| 27 | + generate_transformation_string, |
| 28 | + build_eager, |
| 29 | + compute_hex_hash, |
| 30 | + verify_notification_signature, |
| 31 | + verify_api_response_signature, |
| 32 | +) |
| 33 | +from cloudinary.compat import to_bytes |
18 | 34 | from test.helper_test import TEST_IMAGE, REMOTE_TEST_IMAGE |
19 | | -from test.test_api import API_TEST_TRANS_SCALE100, API_TEST_TRANS_SCALE100_STR, API_TEST_TRANS_SEPIA_STR, \ |
| 35 | +from test.test_api import ( |
| 36 | + API_TEST_TRANS_SCALE100, |
| 37 | + API_TEST_TRANS_SCALE100_STR, |
| 38 | + API_TEST_TRANS_SEPIA_STR, |
20 | 39 | API_TEST_TRANS_SEPIA |
| 40 | +) |
21 | 41 |
|
22 | 42 | DEFAULT_ROOT_PATH = 'http://res.cloudinary.com/test123/' |
23 | 43 |
|
|
33 | 53 | DEFAULT_VERSION_STR = 'v1' |
34 | 54 | TEST_FOLDER = 'folder/test' |
35 | 55 |
|
| 56 | +MOCKED_NOW = 1549533574 |
| 57 | +API_SECRET = 'X7qLTrsES31MzxxkxPPA-pAGGfU' |
36 | 58 |
|
37 | 59 | class TestUtils(unittest.TestCase): |
38 | 60 | crop_transformation = {'crop': 'crop', 'width': 100} |
@@ -1198,6 +1220,75 @@ def test_build_eager(self): |
1198 | 1220 | for message, value, expected in test_data: |
1199 | 1221 | self.assertEqual(expected, build_eager(value), message) |
1200 | 1222 |
|
| 1223 | + def test_compute_hash(self): |
| 1224 | + self.assertEqual("4de279c82056603e91aab3930a593b8b887d9e48", |
| 1225 | + compute_hex_hash("https://cloudinary.com/images/old_logo.png")) |
| 1226 | + |
| 1227 | + original_value = str(uuid.uuid4()) |
| 1228 | + |
| 1229 | + self.assertEqual(compute_hex_hash(original_value), compute_hex_hash(original_value), |
| 1230 | + "Equal STR inputs should be hashed the same way") |
| 1231 | + |
| 1232 | + self.assertNotEqual(compute_hex_hash(original_value), compute_hex_hash("some string"), |
| 1233 | + "Unequal inputs hashes should not match") |
| 1234 | + |
| 1235 | + def test_verify_api_response_signature(self): |
| 1236 | + public_id = 'tests/logo.png' |
| 1237 | + test_version = 1 |
| 1238 | + api_response_signature = '08d3107a5b2ad82e7d82c0b972218fbf20b5b1e0' |
| 1239 | + |
| 1240 | + with patch('cloudinary.config', return_value=cloudinary.config(api_secret=API_SECRET)): |
| 1241 | + self.assertTrue(verify_api_response_signature(public_id, test_version, api_response_signature), |
| 1242 | + "The response signature is valid for the same parameters") |
| 1243 | + |
| 1244 | + self.assertFalse(verify_api_response_signature(public_id, test_version + 1, api_response_signature), |
| 1245 | + "The response signature is invalid for the wrong version") |
| 1246 | + |
| 1247 | + with patch('cloudinary.config', return_value=cloudinary.config(api_secret=None)): |
| 1248 | + with self.assertRaises(Exception) as e: |
| 1249 | + verify_api_response_signature(public_id, test_version, api_response_signature) |
| 1250 | + self.assertEqual(str(e.exception), 'Api secret key is empty') |
| 1251 | + |
| 1252 | + def test_verify_notification_signature(self): |
| 1253 | + valid_for = 60 |
| 1254 | + signature = 'dfe82de1d9083fe0b7ea68070649f9a15b8874da' |
| 1255 | + body = '{"notification_type":"eager","eager":[{"transformation":"sp_full_hd/mp4","bytes":1055,' \ |
| 1256 | + '"url":"http://res.cloudinary.com/demo/video/upload/sp_full_hd/v1533125278/dog.mp4",' \ |
| 1257 | + '"secure_url":"https://res.cloudinary.com/demo/video/upload/sp_full_hd/v1533125278/dog.mp4"}],' \ |
| 1258 | + '"public_id":"dog","batch_id":"9b11fa058c61fa577f4ec516bf6ee756ac2aefef095af99aef1302142cc1694a"}' |
| 1259 | + |
| 1260 | + with patch('time.time', return_value=MOCKED_NOW): |
| 1261 | + valid_response_timestamp = time.time() - valid_for |
| 1262 | + test_message_part = "The notification signature is" |
| 1263 | + |
| 1264 | + with patch('cloudinary.config', return_value=cloudinary.config(api_secret=API_SECRET)): |
| 1265 | + self.assertTrue(verify_notification_signature(body, valid_response_timestamp, |
| 1266 | + signature, |
| 1267 | + valid_for), |
| 1268 | + "{} valid for matching and not expired signature".format(test_message_part)) |
| 1269 | + |
| 1270 | + self.assertFalse(verify_notification_signature(body, valid_response_timestamp, |
| 1271 | + signature, |
| 1272 | + valid_for - 1), |
| 1273 | + "{} invalid for matching but expired signature".format(test_message_part)) |
| 1274 | + |
| 1275 | + self.assertFalse(verify_notification_signature(body, valid_response_timestamp, |
| 1276 | + signature + 'chars'), |
| 1277 | + "{} invalid for non matching and not expired signature".format(test_message_part)) |
| 1278 | + |
| 1279 | + self.assertFalse(verify_notification_signature(body, valid_response_timestamp, |
| 1280 | + signature + 'chars', |
| 1281 | + valid_for - 1), |
| 1282 | + "{} invalid for non matching and expired signature".format(test_message_part)) |
| 1283 | + with self.assertRaises(Exception) as e: |
| 1284 | + verify_notification_signature(1, valid_response_timestamp, signature, valid_for) |
| 1285 | + self.assertEqual(str(e.exception), 'Body should be type of string') |
| 1286 | + |
| 1287 | + with patch('cloudinary.config', return_value=cloudinary.config(api_secret=None)): |
| 1288 | + with self.assertRaises(Exception) as e: |
| 1289 | + verify_notification_signature(body, valid_response_timestamp, signature, valid_for) |
| 1290 | + self.assertEqual(str(e.exception), 'Api secret key is empty') |
| 1291 | + |
1201 | 1292 |
|
1202 | 1293 | if __name__ == '__main__': |
1203 | 1294 | unittest.main() |
0 commit comments