Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update PublicS3Storage for django-storages 1.14.4 #3836

Merged
merged 1 commit into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions infra/terraform/concrexit/modules/cdn/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,36 @@ resource "aws_cloudfront_response_headers_policy" "static_cors_headers" {
}
}

resource "aws_cloudfront_cache_policy" "public_storage" {
name = "${var.customer}-${var.stage}-concrexit-public-storage"
comment = "Cache policy that forwards the response-content-disposition header."
min_ttl = 1
default_ttl = 86400
max_ttl = 31536000

parameters_in_cache_key_and_forwarded_to_origin {
cookies_config {
cookie_behavior = "none"
}

headers_config {
header_behavior = "none"
}

query_strings_config {
# This is required to allow using the response-content-disposition query parameter
# to cause a public file (without a signed URL) to be received as an attachment.
query_string_behavior = "whitelist"
query_strings {
items = ["response-content-disposition"]
}
}

enable_accept_encoding_gzip = true
enable_accept_encoding_brotli = true
}
}

resource "aws_cloudfront_distribution" "this" {
aliases = [var.webdomain]

Expand All @@ -59,7 +89,6 @@ resource "aws_cloudfront_distribution" "this" {

trusted_key_groups = [aws_cloudfront_key_group.this.id]
cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id

}

ordered_cache_behavior {
Expand All @@ -70,7 +99,7 @@ resource "aws_cloudfront_distribution" "this" {
cached_methods = ["GET", "HEAD"]
compress = true

cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id
cache_policy_id = aws_cloudfront_cache_policy.public_storage.id
}

ordered_cache_behavior {
Expand Down
78 changes: 40 additions & 38 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ django-debug-toolbar = "4.4.6"
django-admin-autocomplete-filter = "0.7.1"
django-bootstrap5 = "^22.1"
django-tinymce = "4.1.0"
django-storages = "1.14.3"
django-storages = "1.14.4"
django-drf-filepond = "0.5.0"
django-filepond-widget = "0.9.0"
django-thumbnails = "0.7.0"
Expand Down
20 changes: 10 additions & 10 deletions website/thaliawebsite/storage/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,11 @@ class PublicS3Storage(S3RenameMixin, S3Boto3Storage):
PUBLIC_MEDIA_LOCATION publicly, despite the objects in the bucket having a "private"
ACL. Hence, we can still use the default "private" ACL to prevent people from using
the S3 bucket directly.

To support the "ResponseContentDisposition" parameter for the `attachment` argument
to `PublicS3Storage.url` we do still need to sign the url, but in other cases we can
strip the signing parameters from urls.
"""

location = settings.PUBLIC_MEDIA_LOCATION
file_overwrite = False
querystring_auth = False

# Cloudfront will have a behavior to serve files in PUBLIC_MEDIA_LOCATION publicly,
# despite the objects in the bucket having a private ACL. Hence, we can use the
Expand All @@ -39,17 +36,18 @@ class PublicS3Storage(S3RenameMixin, S3Boto3Storage):
def url(self, name, attachment=False, expire_seconds=None):
params = {}
if attachment:
# The S3 bucket will add the Content-Disposition header to the response
# if the signed URL contains the response-content-disposition query parameter.
# Cloudfront's cache policy is configured to pass this parameter through to
# the origin. Otherwise, it wouldn't end up at S3.
# The `ResponseContentDisposition` parameter as used in the private storage
# does not work if there's no signature present.
params[
"ResponseContentDisposition"
"response-content-disposition"
] = f'attachment; filename="{attachment}"'

url = super().url(name, params, expire=expire_seconds)

if not attachment:
# The signature is required even for public media in order
# to support the "ResponseContentDisposition" parameter.
return self._strip_signing_parameters(url)

return url


Expand All @@ -60,6 +58,8 @@ class PrivateS3Storage(S3RenameMixin, S3Boto3Storage):
def url(self, name, attachment=False, expire_seconds=None):
params = {}
if attachment:
# Cloudfront will add the Content-Disposition header to the response
# if the signed URL contains the ResponseContentDisposition query parameter.
params[
"ResponseContentDisposition"
] = f'attachment; filename="{attachment}"'
Expand Down