From 441b6ca6b0facde20d693227bae7202d2140b41d Mon Sep 17 00:00:00 2001 From: "Jens L." Date: Tue, 3 Sep 2024 21:54:12 +0200 Subject: [PATCH] root: backport s3 storage changes (#11181) re-add _strip_signing_parameters removed in https://github.com/jschneier/django-storages/pull/1402 could probably be re-factored to use the same approach that PR uses Signed-off-by: Jens Langhammer --- authentik/root/storages.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/authentik/root/storages.py b/authentik/root/storages.py index f1c6f516be..e76efb3374 100644 --- a/authentik/root/storages.py +++ b/authentik/root/storages.py @@ -1,6 +1,7 @@ """authentik storage backends""" import os +from urllib.parse import parse_qsl, urlsplit from django.conf import settings from django.core.exceptions import SuspiciousOperation @@ -110,3 +111,34 @@ class S3Storage(BaseS3Storage): if self.querystring_auth: return url return self._strip_signing_parameters(url) + + def _strip_signing_parameters(self, url): + # Boto3 does not currently support generating URLs that are unsigned. Instead + # we take the signed URLs and strip any querystring params related to signing + # and expiration. + # Note that this may end up with URLs that are still invalid, especially if + # params are passed in that only work with signed URLs, e.g. response header + # params. + # The code attempts to strip all query parameters that match names of known + # parameters from v2 and v4 signatures, regardless of the actual signature + # version used. + split_url = urlsplit(url) + qs = parse_qsl(split_url.query, keep_blank_values=True) + blacklist = { + "x-amz-algorithm", + "x-amz-credential", + "x-amz-date", + "x-amz-expires", + "x-amz-signedheaders", + "x-amz-signature", + "x-amz-security-token", + "awsaccesskeyid", + "expires", + "signature", + } + filtered_qs = ((key, val) for key, val in qs if key.lower() not in blacklist) + # Note: Parameters that did not have a value in the original query string will + # have an '=' sign appended to it, e.g ?foo&bar becomes ?foo=&bar= + joined_qs = ("=".join(keyval) for keyval in filtered_qs) + split_url = split_url._replace(query="&".join(joined_qs)) + return split_url.geturl()