From 36ca80c74409a2520e0217406b05c5a98e650443 Mon Sep 17 00:00:00 2001 From: "Terence D. Honles" Date: Mon, 16 Oct 2023 18:35:19 +0200 Subject: [PATCH] [s3] fix disabling cloudfront signing with cloudfront_signer=None This change fixes disabling cloudfront signing by specifying "None" (or another falsy value). This allows multiple storage configs that are all distributed via cloudfront, but only some of the configs need to be signed. --- docs/backends/amazon-S3.rst | 39 ++++++++++++++++++++++++++++++++++++- storages/backends/s3.py | 7 +++++-- tests/test_s3.py | 4 ++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/backends/amazon-S3.rst b/docs/backends/amazon-S3.rst index 969c1075..660c9144 100644 --- a/docs/backends/amazon-S3.rst +++ b/docs/backends/amazon-S3.rst @@ -186,12 +186,49 @@ Settings Set this to specify a custom domain for constructed URLs. .. note:: - You'll have to configure CloudFront to use the bucket as an origin for this to work. + You'll have to configure CloudFront to use the bucket as an origin for this to + work. + + If your CloudFront config restricts viewer access you will also need to provide + ``cloudfront_key`` / ``AWS_CLOUDFRONT_KEY`` and ``cloudfront_key_id`` / + ``AWS_CLOUDFRONT_KEY_ID``; See those settings and + :ref:`cloudfront-signed-url-header` for more info. + + If you have more than one storage with different viewer access permissions, you + can provide ``cloudfront_signer=None`` to disable signing on one or more + storages. .. warning:: Django’s STATIC_URL must end in a slash and this must not. It is best to set this variable independently of STATIC_URL. +``cloudfront_key`` or ``AWS_CLOUDFRONT_KEY`` + + Default: ``None`` + + A private PEM encoded key to use in a ``boto3`` ``CloudFrontSigner``; See + :ref:`cloudfront-signed-url-header` for more info. + +``cloudfront_key_id`` or ``AWS_CLOUDFRONT_KEY_ID`` + + Default: ``None`` + + The AWS key ID for the private key provided with ``cloudfront_key`` / + ``AWS_CLOUDFRONT_KEY``; See :ref:`cloudfront-signed-url-header` for more info. + +``cloudfront_signer`` + + Default: omitted + + By default the ``cloudfront_signer`` is generated based on the CloudFront key and ID + provided. If both are provided URLs will be signed and will work for distributions + with restricted viewer access, but if neither are provided then URLs will not be + signed and will work for distributions with unrestricted viewer access. + + If you require a custom CloudFront signer you may pass a ``boto3`` + ``CloudFrontSigner`` instance that can sign URLs, and to disable signing you may pass + ``None``. + ``signature_version`` or ``AWS_S3_SIGNATURE_VERSION`` Default: ``None`` diff --git a/storages/backends/s3.py b/storages/backends/s3.py index 204a6ee3..466489c8 100644 --- a/storages/backends/s3.py +++ b/storages/backends/s3.py @@ -299,7 +299,8 @@ class S3Storage(CompressStorageMixin, BaseStorage): config = None def __init__(self, **settings): - self.cloudfront_signer = settings.pop("cloudfront_signer", None) + omitted = object() + self.cloudfront_signer = settings.pop("cloudfront_signer", omitted) super().__init__(**settings) @@ -333,7 +334,7 @@ def __init__(self, **settings): if self.transfer_config is None: self.transfer_config = TransferConfig(use_threads=self.use_threads) - if not self.cloudfront_signer: + if self.cloudfront_signer is omitted: if self.cloudfront_key_id and self.cloudfront_key: self.cloudfront_signer = self.get_cloudfront_signer( self.cloudfront_key_id, self.cloudfront_key @@ -343,6 +344,8 @@ def __init__(self, **settings): "Both AWS_CLOUDFRONT_KEY_ID/cloudfront_key_id and " "AWS_CLOUDFRONT_KEY/cloudfront_key must be provided together." ) + else: + self.cloudfront_signer = None def get_cloudfront_signer(self, key_id, key): return _cloud_front_signer_from_pem(key_id, key) diff --git a/tests/test_s3.py b/tests/test_s3.py index e2340f70..36f75b32 100644 --- a/tests/test_s3.py +++ b/tests/test_s3.py @@ -890,6 +890,10 @@ def test_cloudfront_config(self): storage = s3.S3Storage() self.assertIsNotNone(storage.cloudfront_signer) + # allow disabling cloudfront signing + storage = s3.S3Storage(cloudfront_signer=None) + self.assertIsNone(storage.cloudfront_signer) + storage = s3.S3Storage(cloudfront_key_id=key_id, cloudfront_key=pem) self.assertIsNotNone(storage.cloudfront_signer)