Skip to content

Commit dcd1c83

Browse files
committed
Updating docs and adding additional settings checks
1 parent 93a3566 commit dcd1c83

File tree

2 files changed

+33
-28
lines changed

2 files changed

+33
-28
lines changed

README.rst

+9-12
Original file line numberDiff line numberDiff line change
@@ -216,21 +216,18 @@ Please note, however, that **custom URLs will not work with AWS_S3_PUBLIC_URL**
216216
URL doesn't accept extra parameters, and it will raise ``ValueError``.
217217

218218

219-
Presigned URL
220-
-----------
221-
Pre-signed URLs allow temporary access to S3 objects without AWS credentials.
222-
Generate a URL with permissions and time limit, then provide it to the user for downloading or uploading the object.
223-
API server does not need to handle the I/O of transferring the file, which can be resource-intensive and slow down the server's response time.
224-
Instead, the user can directly interact with S3, improving performance and reducing the load on your API server.
225-
It's secure and flexible for temporary access to S3 objects.
219+
Pre-signed URL uploads
220+
----------------------
226221

227-
For download a existed file:
228-
.. code:: python
229-
url = storage.url("foo/bar.pdf")
222+
Pre-signed URLs allow temporary access to S3 objects without AWS credentials. A pre-signed URL allows HTTP clients to
223+
upload files directly, improving performance and reducing the load on your server.
224+
225+
To generate a presigned URL allowing a file upload with HTTP ``PUT``:
230226

231-
For upload new file:
232227
.. code:: python
233-
url = storage.url("foo/bar.pdf", clientMethod="put_object")
228+
229+
url = storage.url("foo/bar.pdf", client_method="put_object")
230+
234231
235232
Management commands
236233
-------------------

django_s3_storage/storage.py

+24-16
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import shutil
77
from contextlib import closing
88
from datetime import timezone
9-
from functools import wraps
9+
from functools import wraps, partial
1010
from io import TextIOBase
1111
from tempfile import SpooledTemporaryFile
1212
from threading import local
@@ -18,6 +18,7 @@
1818
from botocore.exceptions import ClientError
1919
from django.conf import settings
2020
from django.contrib.staticfiles.storage import ManifestFilesMixin
21+
from django.core import checks
2122
from django.core.exceptions import ImproperlyConfigured
2223
from django.core.files.base import File
2324
from django.core.files.storage import Storage
@@ -183,14 +184,6 @@ def _setup(self):
183184
# Validate settings.
184185
if not self.settings.AWS_S3_BUCKET_NAME:
185186
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-
)
194187
# Create a thread-local connection manager.
195188
self._connections = _Local(self)
196189
# Set transfer config for S3 operations
@@ -217,9 +210,25 @@ def __init__(self, **kwargs):
217210
self._setup()
218211
# Re-initialize the storage if an AWS setting changes.
219212
setting_changed.connect(self._setting_changed_received)
213+
# Register system checks.
214+
checks.register(partial(self.__class__._system_checks, self), checks.Tags.security)
220215
# All done!
221216
super().__init__()
222217

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+
223232
def __reduce__(self):
224233
return unpickle_helper, (self.__class__, self._kwargs)
225234

@@ -436,25 +445,24 @@ def size(self, name):
436445
except KeyError:
437446
return meta["ContentLength"]
438447

439-
def url(self, name, extra_params=None, clientMethod="get_object"):
448+
def url(self, name, extra_params=None, client_method="get_object"):
440449
# Use a public URL, if specified.
441450
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")
447453
return urljoin(self.settings.AWS_S3_PUBLIC_URL, filepath_to_uri(name))
448454
# Otherwise, generate the URL.
449455
params = extra_params.copy() if extra_params else {}
450456
params.update(self._object_params(name))
451457
url = self.s3_connection.generate_presigned_url(
452-
ClientMethod=clientMethod,
458+
ClientMethod=client_method,
453459
Params=params,
454460
ExpiresIn=self.settings.AWS_S3_MAX_AGE_SECONDS,
455461
)
456462
# Strip off the query params if we're not interested in bucket auth.
457463
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")
458466
url = urlunsplit(urlsplit(url)[:3] + ("", "",))
459467
# All done!
460468
return url

0 commit comments

Comments
 (0)