Skip to content

Commit ddba6c3

Browse files
Fix CloudinaryField options consumption
* Change whitelisting of Django parameters by whitelisting of Cloudinary options which we control.
1 parent 37570d7 commit ddba6c3

File tree

3 files changed

+79
-62
lines changed

3 files changed

+79
-62
lines changed

cloudinary/models.py

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from cloudinary import CloudinaryResource, forms, uploader
44
from django.core.files.uploadedfile import UploadedFile
55
from django.db import models
6+
from cloudinary.uploader import upload_options
7+
from cloudinary.utils import upload_params
68

79
# Add introspection rules for South, if it's installed.
810
try:
@@ -16,27 +18,6 @@
1618
r'(?:v(?P<version>\d+)/)?' \
1719
r'(?P<public_id>.*?)' \
1820
r'(\.(?P<format>[^.]+))?$'
19-
DJANGO_FIELD_PARAMETERS = [
20-
'verbose_name',
21-
'name',
22-
'primary_key',
23-
'max_length',
24-
'unique',
25-
'null',
26-
'db_index',
27-
'rel',
28-
'default',
29-
'editable',
30-
'serialize',
31-
'unique_for_date',
32-
'unique_for_month',
33-
'unique_for_year',
34-
'choices',
35-
'help_text',
36-
'db_column',
37-
'db_tablespace',
38-
'auto_created'
39-
]
4021

4122

4223
def with_metaclass(meta, *bases):
@@ -59,15 +40,16 @@ class CloudinaryField(models.Field):
5940
description = "A resource stored in Cloudinary"
6041

6142
def __init__(self, *args, **kwargs):
62-
field_options = {key: kwargs.pop(key) for key in set(kwargs.keys()) if key in DJANGO_FIELD_PARAMETERS}
63-
field_options['max_length'] = 255
6443
self.default_form_class = kwargs.pop("default_form_class", forms.CloudinaryFileField)
6544
self.type = kwargs.pop("type", "upload")
6645
self.resource_type = kwargs.pop("resource_type", "image")
6746
self.width_field = kwargs.pop("width_field", None)
6847
self.height_field = kwargs.pop("height_field", None)
69-
self.options = {key: value for key, value in kwargs.items()}
48+
# Collect all options related to Cloudinary upload
49+
self.options = {key: kwargs.pop(key) for key in set(kwargs.keys()) if key in upload_params + upload_options}
7050

51+
field_options = kwargs
52+
field_options['max_length'] = 255
7153
super(CloudinaryField, self).__init__(*args, **field_options)
7254

7355
def get_internal_type(self):

cloudinary/uploader.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ def is_appengine_sandbox():
3434
ca_certs=certifi.where()
3535
)
3636

37+
upload_options = [
38+
"filename",
39+
"timeout",
40+
"chunk_size",
41+
"use_cache"
42+
]
43+
3744
UPLOAD_LARGE_CHUNK_SIZE = 20000000
3845

3946

cloudinary/utils.py

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
from collections import OrderedDict
1515
from datetime import datetime, date
1616
from fractions import Fraction
17+
from numbers import Number
1718

1819
import six.moves.urllib.parse
19-
from numbers import Number
2020
from six import iteritems
2121

2222
import cloudinary
@@ -65,6 +65,59 @@
6565
'version'
6666
]
6767

68+
__SIMPLE_UPLOAD_PARAMS = [
69+
"public_id",
70+
"callback",
71+
"format",
72+
"type",
73+
"backup",
74+
"faces",
75+
"image_metadata",
76+
"exif",
77+
"colors",
78+
"use_filename",
79+
"unique_filename",
80+
"discard_original_filename",
81+
"invalidate",
82+
"notification_url",
83+
"eager_notification_url",
84+
"eager_async",
85+
"proxy",
86+
"folder",
87+
"overwrite",
88+
"moderation",
89+
"raw_convert",
90+
"quality_override",
91+
"quality_analysis",
92+
"ocr",
93+
"categorization",
94+
"detection",
95+
"similarity_search",
96+
"background_removal",
97+
"upload_preset",
98+
"phash",
99+
"return_delete_token",
100+
"auto_tagging",
101+
"async",
102+
]
103+
104+
__SERIALIZED_UPLOAD_PARAMS = [
105+
"timestamp",
106+
"transformation",
107+
"headers",
108+
"eager",
109+
"tags",
110+
"allowed_formats",
111+
"face_coordinates",
112+
"custom_coordinates",
113+
"context",
114+
"auto_tagging",
115+
"responsive_breakpoints",
116+
"access_control"
117+
]
118+
119+
upload_params = __SIMPLE_UPLOAD_PARAMS + __SERIALIZED_UPLOAD_PARAMS
120+
68121

69122
def build_array(arg):
70123
if isinstance(arg, list):
@@ -844,55 +897,29 @@ def build_custom_headers(headers):
844897

845898

846899
def build_upload_params(**options):
847-
params = {
900+
params = {param_name: options.get(param_name) for param_name in __SIMPLE_UPLOAD_PARAMS}
901+
902+
serialized_params = {
848903
"timestamp": now(),
849904
"transformation": generate_transformation_string(**options)[0],
850-
"public_id": options.get("public_id"),
851-
"callback": options.get("callback"),
852-
"format": options.get("format"),
853-
"type": options.get("type"),
854-
"backup": options.get("backup"),
855-
"faces": options.get("faces"),
856-
"image_metadata": options.get("image_metadata"),
857-
"exif": options.get("exif"),
858-
"colors": options.get("colors"),
859905
"headers": build_custom_headers(options.get("headers")),
860906
"eager": build_eager(options.get("eager")),
861-
"use_filename": options.get("use_filename"),
862-
"unique_filename": options.get("unique_filename"),
863-
"discard_original_filename": options.get("discard_original_filename"),
864-
"invalidate": options.get("invalidate"),
865-
"notification_url": options.get("notification_url"),
866-
"eager_notification_url": options.get("eager_notification_url"),
867-
"eager_async": options.get("eager_async"),
868-
"proxy": options.get("proxy"),
869-
"folder": options.get("folder"),
870-
"overwrite": options.get("overwrite"),
871907
"tags": options.get("tags") and ",".join(build_array(options["tags"])),
872-
"allowed_formats": options.get("allowed_formats") and ",".join(
873-
build_array(options["allowed_formats"])),
908+
"allowed_formats": options.get("allowed_formats") and ",".join(build_array(options["allowed_formats"])),
874909
"face_coordinates": encode_double_array(options.get("face_coordinates")),
875910
"custom_coordinates": encode_double_array(options.get("custom_coordinates")),
876911
"context": encode_context(options.get("context")),
877-
"moderation": options.get("moderation"),
878-
"raw_convert": options.get("raw_convert"),
879-
"quality_override": options.get("quality_override"),
880-
"quality_analysis": options.get("quality_analysis"),
881-
"ocr": options.get("ocr"),
882-
"categorization": options.get("categorization"),
883-
"detection": options.get("detection"),
884-
"similarity_search": options.get("similarity_search"),
885-
"background_removal": options.get("background_removal"),
886-
"upload_preset": options.get("upload_preset"),
887-
"phash": options.get("phash"),
888-
"return_delete_token": options.get("return_delete_token"),
889912
"auto_tagging": options.get("auto_tagging") and str(options.get("auto_tagging")),
890-
"responsive_breakpoints": generate_responsive_breakpoints_string(
891-
options.get("responsive_breakpoints")),
892-
"async": options.get("async"),
913+
"responsive_breakpoints": generate_responsive_breakpoints_string(options.get("responsive_breakpoints")),
893914
"access_control": options.get("access_control") and json_encode(
894915
build_list_of_dicts(options.get("access_control")))
895916
}
917+
918+
# make sure that we are in-sync with __SERIALIZED_UPLOAD_PARAMS which are in use by other methods
919+
serialized_params = {param_name: serialized_params[param_name] for param_name in __SERIALIZED_UPLOAD_PARAMS}
920+
921+
params.update(serialized_params)
922+
896923
return params
897924

898925

@@ -1215,6 +1242,7 @@ def file_io_size(file_io):
12151242

12161243
return size
12171244

1245+
12181246
def check_property_enabled(f):
12191247
"""
12201248
Used as a class method decorator to check whether class is enabled(self.enabled is True)

0 commit comments

Comments
 (0)