Refs #27795 -- Removed force_bytes() usage from django/utils/http.py.

django.utils.http.urlsafe_base64_encode() now returns a string, not a
bytestring. Since URLs are represented as strings,
urlsafe_base64_encode() should return a string. All uses immediately
decoded the bytestring to a string anyway.

As the inverse operation, urlsafe_base64_decode() accepts a string.
This commit is contained in:
Jon Dufresne 2018-10-01 18:18:04 -05:00 committed by Tim Graham
parent f3d3338e06
commit c82893cb8c
6 changed files with 21 additions and 10 deletions

View File

@ -284,7 +284,7 @@ class PasswordResetForm(forms.Form):
'email': email, 'email': email,
'domain': domain, 'domain': domain,
'site_name': site_name, 'site_name': site_name,
'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(), 'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user, 'user': user,
'token': token_generator.make_token(user), 'token': token_generator.make_token(user),
'protocol': 'https' if use_https else 'http', 'protocol': 'https' if use_https else 'http',

View File

@ -15,7 +15,6 @@ from urllib.parse import (
from django.core.exceptions import TooManyFieldsSent from django.core.exceptions import TooManyFieldsSent
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from django.utils.deprecation import RemovedInDjango30Warning from django.utils.deprecation import RemovedInDjango30Warning
from django.utils.encoding import force_bytes
from django.utils.functional import keep_lazy_text from django.utils.functional import keep_lazy_text
# based on RFC 7232, Appendix C # based on RFC 7232, Appendix C
@ -220,10 +219,10 @@ def int_to_base36(i):
def urlsafe_base64_encode(s): def urlsafe_base64_encode(s):
""" """
Encode a bytestring in base64 for use in URLs. Strip any trailing equal Encode a bytestring to a base64 string for use in URLs. Strip any trailing
signs. equal signs.
""" """
return base64.urlsafe_b64encode(s).rstrip(b'\n=') return base64.urlsafe_b64encode(s).rstrip(b'\n=').decode('ascii')
def urlsafe_base64_decode(s): def urlsafe_base64_decode(s):
@ -231,7 +230,7 @@ def urlsafe_base64_decode(s):
Decode a base64 encoded string. Add back any trailing equal signs that Decode a base64 encoded string. Add back any trailing equal signs that
might have been stripped. might have been stripped.
""" """
s = force_bytes(s) s = s.encode()
try: try:
return base64.urlsafe_b64decode(s.ljust(len(s) + len(s) % 4, b'=')) return base64.urlsafe_b64decode(s.ljust(len(s) + len(s) % 4, b'='))
except (LookupError, BinasciiError) as e: except (LookupError, BinasciiError) as e:

View File

@ -726,14 +726,22 @@ escaping HTML.
.. function:: urlsafe_base64_encode(s) .. function:: urlsafe_base64_encode(s)
Encodes a bytestring in base64 for use in URLs, stripping any trailing Encodes a bytestring to a base64 string for use in URLs, stripping any
equal signs. trailing equal signs.
.. versionchanged:: 2.2
In older versions, it returns a bytestring instead of a string.
.. function:: urlsafe_base64_decode(s) .. function:: urlsafe_base64_decode(s)
Decodes a base64 encoded string, adding back any trailing equal signs that Decodes a base64 encoded string, adding back any trailing equal signs that
might have been stripped. might have been stripped.
.. versionchanged:: 2.2
In older versions, ``s`` may be a bytestring.
``django.utils.module_loading`` ``django.utils.module_loading``
=============================== ===============================

View File

@ -298,6 +298,10 @@ Miscellaneous
* Support for bytestring paths in the template filesystem loader is removed. * Support for bytestring paths in the template filesystem loader is removed.
* :func:`django.utils.http.urlsafe_base64_encode` now returns a string instead
of a bytestring, and :func:`django.utils.http.urlsafe_base64_decode` may no
longer be passed a bytestring.
.. _deprecated-features-2.2: .. _deprecated-features-2.2:
Features deprecated in 2.2 Features deprecated in 2.2

View File

@ -47,7 +47,7 @@ class AuthTemplateTests(TestCase):
client = PasswordResetConfirmClient() client = PasswordResetConfirmClient()
default_token_generator = PasswordResetTokenGenerator() default_token_generator = PasswordResetTokenGenerator()
token = default_token_generator.make_token(self.user) token = default_token_generator.make_token(self.user)
uidb64 = urlsafe_base64_encode(str(self.user.pk).encode()).decode() uidb64 = urlsafe_base64_encode(str(self.user.pk).encode())
url = reverse('password_reset_confirm', kwargs={'uidb64': uidb64, 'token': token}) url = reverse('password_reset_confirm', kwargs={'uidb64': uidb64, 'token': token})
response = client.get(url) response = client.get(url)
self.assertContains(response, '<title>Enter new password</title>') self.assertContains(response, '<title>Enter new password</title>')

View File

@ -424,7 +424,7 @@ class UUIDUserPasswordResetTest(CustomUserPasswordResetTest):
def test_confirm_invalid_uuid(self): def test_confirm_invalid_uuid(self):
"""A uidb64 that decodes to a non-UUID doesn't crash.""" """A uidb64 that decodes to a non-UUID doesn't crash."""
_, path = self._test_confirm_start() _, path = self._test_confirm_start()
invalid_uidb64 = urlsafe_base64_encode('INVALID_UUID'.encode()).decode() invalid_uidb64 = urlsafe_base64_encode('INVALID_UUID'.encode())
first, _uuidb64_, second = path.strip('/').split('/') first, _uuidb64_, second = path.strip('/').split('/')
response = self.client.get('/' + '/'.join((first, invalid_uidb64, second)) + '/') response = self.client.get('/' + '/'.join((first, invalid_uidb64, second)) + '/')
self.assertContains(response, 'The password reset link was invalid') self.assertContains(response, 'The password reset link was invalid')