From d7b218f5eba78a99ae202e10c5d4b4d186735f7d Mon Sep 17 00:00:00 2001 From: Kevin Kennell Date: Mon, 17 Feb 2020 17:10:52 +0100 Subject: [PATCH] decode base64 credentials as utf8; adjust tests (#7193) * decode base64 credentials as utf8; adjust tests * basicauth: add dedicated test for utf8 credentials * basicauth: add fallback to latin-1 encoding if utf-8 fails --- rest_framework/authentication.py | 6 +++++- tests/authentication/test_authentication.py | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 1e30728d3..a2ba53480 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -74,7 +74,11 @@ class BasicAuthentication(BaseAuthentication): raise exceptions.AuthenticationFailed(msg) try: - auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':') + try: + auth_decoded = base64.b64decode(auth[1]).decode('utf-8') + except UnicodeDecodeError: + auth_decoded = base64.b64decode(auth[1]).decode('latin-1') + auth_parts = auth_decoded.partition(':') except (TypeError, UnicodeDecodeError, binascii.Error): msg = _('Invalid basic header. Credentials not correctly base64 encoded.') raise exceptions.AuthenticationFailed(msg) diff --git a/tests/authentication/test_authentication.py b/tests/authentication/test_authentication.py index 37e265e17..4760ea319 100644 --- a/tests/authentication/test_authentication.py +++ b/tests/authentication/test_authentication.py @@ -159,6 +159,25 @@ class BasicAuthTests(TestCase): ) assert response.status_code == status.HTTP_401_UNAUTHORIZED + def test_decoding_of_utf8_credentials(self): + username = 'walterwhité' + email = 'walterwhite@example.com' + password = 'pässwörd' + User.objects.create_user( + username, email, password + ) + credentials = ('%s:%s' % (username, password)) + base64_credentials = base64.b64encode( + credentials.encode('utf-8') + ).decode(HTTP_HEADER_ENCODING) + auth = 'Basic %s' % base64_credentials + response = self.csrf_client.post( + '/basic/', + {'example': 'example'}, + HTTP_AUTHORIZATION=auth + ) + assert response.status_code == status.HTTP_200_OK + @override_settings(ROOT_URLCONF=__name__) class SessionAuthTests(TestCase):