Fix unicode sandwich issue in cas_server.utils.update_url. Fix #82

This commit is contained in:
Valentin Samir 2022-10-16 20:53:33 +02:00
parent 6212ba5f2a
commit a82d348b20
3 changed files with 35 additions and 16 deletions

View file

@ -13,6 +13,10 @@ Added
----- -----
* Support for Django 4.0 and 4.1 * Support for Django 4.0 and 4.1
Fixes
-----
* Fix unicode sandwich issue in cas_server.utils.update_url
Removed Removed
------- -------
* Drop support for Django 1.11 (now deprecated for more than 2 years) * Drop support for Django 1.11 (now deprecated for more than 2 years)

View file

@ -262,7 +262,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
# check that the service pattern registered on the ticket is the on we use for tests # check that the service pattern registered on the ticket is the on we use for tests
self.assertEqual(ticket.service_pattern, self.service_pattern) self.assertEqual(ticket.service_pattern, self.service_pattern)
def assert_service_ticket(self, client, response): def assert_service_ticket(self, client, response, service="https://www.example.com"):
"""check that a ticket is well emited when requested on a allowed service""" """check that a ticket is well emited when requested on a allowed service"""
# On ticket emission, we should be redirected to the service url, setting the ticket # On ticket emission, we should be redirected to the service url, setting the ticket
# GET parameter # GET parameter
@ -270,7 +270,7 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
self.assertTrue(response.has_header('Location')) self.assertTrue(response.has_header('Location'))
self.assertTrue( self.assertTrue(
response['Location'].startswith( response['Location'].startswith(
"https://www.example.com?ticket=%s-" % settings.CAS_SERVICE_TICKET_PREFIX "%s?ticket=%s-" % (service, settings.CAS_SERVICE_TICKET_PREFIX)
) )
) )
# check that the value of the ticket GET parameter match the value of the ticket # check that the value of the ticket GET parameter match the value of the ticket
@ -337,14 +337,17 @@ class LoginTestCase(TestCase, BaseServicePattern, CanLogin):
self.assertFalse(b"Service https://www.example.net not allowed" in response.content) self.assertFalse(b"Service https://www.example.net not allowed" in response.content)
def test_view_login_get_auth_allowed_service(self): def test_view_login_get_auth_allowed_service(self):
"""Request a ticket for an allowed service by an authenticated client containing non ascii char in url""" """
Request a ticket for an allowed service by an authenticated client containing
non ascii char in url
"""
# get a client that is already authenticated # get a client that is already authenticated
client = get_auth_client() client = get_auth_client()
# ask for a ticket for https://www.example.com # ask for a ticket for https://www.example.com
response = client.get("/login?service=https://www.example.com/é") response = client.get("/login?service=https://www.example.com/é")
# as https://www.example.com/é is a valid service a ticket should be created and the # as https://www.example.com/é is a valid service a ticket should be created and the
# user redirected to the service url # user redirected to the service url
self.assert_service_ticket(client, response) self.assert_service_ticket(client, response, service="https://www.example.com/%C3%A9")
def test_view_login_get_auth_allowed_service_non_ascii(self): def test_view_login_get_auth_allowed_service_non_ascii(self):
"""Request a ticket for an allowed service by an authenticated client""" """Request a ticket for an allowed service by an authenticated client"""

View file

@ -249,15 +249,25 @@ def update_url(url, params):
:return: The URL with an updated querystring :return: The URL with an updated querystring
:rtype: unicode :rtype: unicode
""" """
if not isinstance(url, bytes): def to_unicode(data):
url = url.encode('utf-8') if isinstance(data, bytes):
for key, value in list(params.items()): return data.decode('utf-8')
if not isinstance(key, bytes): else:
del params[key] return data
key = key.encode('utf-8')
if not isinstance(value, bytes): def to_bytes(data):
value = value.encode('utf-8') if not isinstance(data, bytes):
params[key] = value return data.encode('utf-8')
else:
return data
if six.PY3:
url = to_unicode(url)
params = {to_unicode(key): to_unicode(value) for (key, value) in params.items()}
else:
url = to_bytes(url)
params = {to_bytes(key): to_bytes(value) for (key, value) in params.items()}
url_parts = list(urlparse(url)) url_parts = list(urlparse(url))
query = dict(parse_qsl(url_parts[4], keep_blank_values=True)) query = dict(parse_qsl(url_parts[4], keep_blank_values=True))
query.update(params) query.update(params)
@ -265,10 +275,12 @@ def update_url(url, params):
query = list(query.items()) query = list(query.items())
query.sort() query.sort()
url_query = urlencode(query) url_query = urlencode(query)
if not isinstance(url_query, bytes): # pragma: no cover in python3 urlencode return an unicode
url_query = url_query.encode("utf-8")
url_parts[4] = url_query url_parts[4] = url_query
return urlunparse(url_parts).decode('utf-8') url = urlunparse(url_parts)
if isinstance(url, bytes):
url = url.decode('utf-8')
return url
def unpack_nested_exception(error): def unpack_nested_exception(error):