6
6
import shutil
7
7
from contextlib import closing
8
8
from datetime import timezone
9
- from functools import wraps
9
+ from functools import wraps , partial
10
10
from io import TextIOBase
11
11
from tempfile import SpooledTemporaryFile
12
12
from threading import local
18
18
from botocore .exceptions import ClientError
19
19
from django .conf import settings
20
20
from django .contrib .staticfiles .storage import ManifestFilesMixin
21
+ from django .core import checks
21
22
from django .core .exceptions import ImproperlyConfigured
22
23
from django .core .files .base import File
23
24
from django .core .files .storage import Storage
@@ -183,14 +184,6 @@ def _setup(self):
183
184
# Validate settings.
184
185
if not self .settings .AWS_S3_BUCKET_NAME :
185
186
raise ImproperlyConfigured (f"Setting AWS_S3_BUCKET_NAME{ self .s3_settings_suffix } is required." )
186
- if self .settings .AWS_S3_PUBLIC_URL and self .settings .AWS_S3_BUCKET_AUTH :
187
- log .warning (
188
- "Using AWS_S3_BUCKET_AUTH%s with AWS_S3_PUBLIC_URL%s. "
189
- "Private files on S3 may be inaccessible via the public URL. "
190
- "See https://github.com/etianen/django-s3-storage/issues/114 " ,
191
- self .s3_settings_suffix ,
192
- self .s3_settings_suffix ,
193
- )
194
187
# Create a thread-local connection manager.
195
188
self ._connections = _Local (self )
196
189
# Set transfer config for S3 operations
@@ -217,9 +210,25 @@ def __init__(self, **kwargs):
217
210
self ._setup ()
218
211
# Re-initialize the storage if an AWS setting changes.
219
212
setting_changed .connect (self ._setting_changed_received )
213
+ # Register system checks.
214
+ checks .register (partial (self .__class__ ._system_checks , self ), checks .Tags .security )
220
215
# All done!
221
216
super ().__init__ ()
222
217
218
+ def _system_checks (self , app_configs , ** kwargs ):
219
+ errors = []
220
+ if self .settings .AWS_S3_PUBLIC_URL and self .settings .AWS_S3_BUCKET_AUTH :
221
+ errors .append (
222
+ checks .Warning (
223
+ f"Using AWS_S3_BUCKET_AUTH{ self .s3_settings_suffix } "
224
+ f"with AWS_S3_PUBLIC_URL{ self .s3_settings_suffix } . "
225
+ "Private files on S3 may be inaccessible via the public URL. "
226
+ "See https://github.com/etianen/django-s3-storage/issues/114 " ,
227
+ id = "django_s3_storage.W001" ,
228
+ )
229
+ )
230
+ return errors
231
+
223
232
def __reduce__ (self ):
224
233
return unpickle_helper , (self .__class__ , self ._kwargs )
225
234
@@ -436,25 +445,24 @@ def size(self, name):
436
445
except KeyError :
437
446
return meta ["ContentLength" ]
438
447
439
- def url (self , name , extra_params = None , clientMethod = "get_object" ):
448
+ def url (self , name , extra_params = None , client_method = "get_object" ):
440
449
# Use a public URL, if specified.
441
450
if self .settings .AWS_S3_PUBLIC_URL :
442
- if extra_params :
443
- raise ValueError (
444
- "Use of extra_params to generate custom URLs is not allowed "
445
- "with AWS_S3_PUBLIC_URL"
446
- )
451
+ if extra_params or client_method != "get_object" :
452
+ raise ValueError ("Use of extra_params or client_method is not allowed with AWS_S3_PUBLIC_URL" )
447
453
return urljoin (self .settings .AWS_S3_PUBLIC_URL , filepath_to_uri (name ))
448
454
# Otherwise, generate the URL.
449
455
params = extra_params .copy () if extra_params else {}
450
456
params .update (self ._object_params (name ))
451
457
url = self .s3_connection .generate_presigned_url (
452
- ClientMethod = clientMethod ,
458
+ ClientMethod = client_method ,
453
459
Params = params ,
454
460
ExpiresIn = self .settings .AWS_S3_MAX_AGE_SECONDS ,
455
461
)
456
462
# Strip off the query params if we're not interested in bucket auth.
457
463
if not self .settings .AWS_S3_BUCKET_AUTH :
464
+ if extra_params or client_method != "get_object" :
465
+ raise ValueError ("Use of extra_params or client_method is not allowed with AWS_S3_BUCKET_AUTH" )
458
466
url = urlunsplit (urlsplit (url )[:3 ] + ("" , "" ,))
459
467
# All done!
460
468
return url
0 commit comments