mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-18 00:00:00 +00:00
[utils] Update is_safe_url
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
"""Copied from latest django/utils/http.py::is_safe_url
|
||||
"""
|
||||
import unicodedata
|
||||
import urlparse
|
||||
import json
|
||||
@@ -20,39 +18,6 @@ class BadRequestException(_HTTPException):
|
||||
class RequestForbbiddenException(_HTTPException):
|
||||
pass
|
||||
|
||||
def is_safe_url(url, host=None):
|
||||
"""
|
||||
Return ``True`` if the url is a safe redirection (i.e. it doesn't point to
|
||||
a different host and uses a safe scheme).
|
||||
|
||||
Always returns ``False`` on an empty url.
|
||||
"""
|
||||
if url is not None:
|
||||
url = url.strip()
|
||||
if not url:
|
||||
return False
|
||||
# Chrome treats \ completely as /
|
||||
url = url.replace('\\', '/')
|
||||
# Chrome considers any URL with more than two slashes to be absolute, but
|
||||
# urlparse is not so flexible. Treat any url with three slashes as unsafe.
|
||||
if url.startswith('///'):
|
||||
return False
|
||||
url_info = urlparse.urlparse(url)
|
||||
|
||||
# Forbid URLs like http:///example.com - with a scheme, but without a hostname.
|
||||
# In that URL, example.com is not the hostname but, a path component. However,
|
||||
# Chrome will still consider example.com to be the hostname, so we must not
|
||||
# allow this syntax.
|
||||
if not url_info.netloc and url_info.scheme:
|
||||
return False
|
||||
# Forbid URLs that start with control characters. Some browsers (like
|
||||
# Chrome) ignore quite a few control characters at the start of a
|
||||
# URL and might consider the URL as scheme relative.
|
||||
if unicodedata.category(url[0])[0] == 'C':
|
||||
return False
|
||||
return ((not url_info.netloc or url_info.netloc == host) and
|
||||
(not url_info.scheme or url_info.scheme in ['http', 'https']))
|
||||
|
||||
JSON_CONTENT_TYPE = 'application/json; charset=utf-8'
|
||||
def json_response(func):
|
||||
@wraps(func)
|
||||
@@ -78,3 +43,39 @@ def int_param(request, key):
|
||||
return int(v)
|
||||
except ValueError:
|
||||
raise BadRequestException()
|
||||
|
||||
def is_safe_url(url, host=None):
|
||||
"""
|
||||
https://github.com/django/django/blob/fc6d147a63f89795dbcdecb0559256470fff4380/django/utils/http.py
|
||||
|
||||
Return ``True`` if the url is a safe redirection (i.e. it doesn't point to
|
||||
a different host and uses a safe scheme).
|
||||
Always returns ``False`` on an empty url.
|
||||
"""
|
||||
if url is not None:
|
||||
url = url.strip()
|
||||
if not url:
|
||||
return False
|
||||
# Chrome treats \ completely as / in paths but it could be part of some
|
||||
# basic auth credentials so we need to check both URLs.
|
||||
return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host)
|
||||
|
||||
def _is_safe_url(url, host):
|
||||
# Chrome considers any URL with more than two slashes to be absolute, but
|
||||
# urlparse is not so flexible. Treat any url with three slashes as unsafe.
|
||||
if url.startswith('///'):
|
||||
return False
|
||||
url_info = urlparse.urlparse(url)
|
||||
# Forbid URLs like http:///example.com - with a scheme, but without a hostname.
|
||||
# In that URL, example.com is not the hostname but, a path component. However,
|
||||
# Chrome will still consider example.com to be the hostname, so we must not
|
||||
# allow this syntax.
|
||||
if not url_info.netloc and url_info.scheme:
|
||||
return False
|
||||
# Forbid URLs that start with control characters. Some browsers (like
|
||||
# Chrome) ignore quite a few control characters at the start of a
|
||||
# URL and might consider the URL as scheme relative.
|
||||
if unicodedata.category(url[0])[0] == 'C':
|
||||
return False
|
||||
return ((not url_info.netloc or url_info.netloc == host) and
|
||||
(not url_info.scheme or url_info.scheme in ['http', 'https']))
|
||||
|
47
tests/seahub/auth/views/test_login.py
Normal file
47
tests/seahub/auth/views/test_login.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from seahub.test_utils import BaseTestCase
|
||||
|
||||
|
||||
class LoginTest(BaseTestCase):
|
||||
def test_can_login(self):
|
||||
resp = self.client.post(
|
||||
reverse('auth_login'), {'login': self.user.username,
|
||||
'password': self.user_password}
|
||||
)
|
||||
|
||||
self.assertEqual(302, resp.status_code)
|
||||
self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL)
|
||||
|
||||
def test_redirect_to_after_success_login(self):
|
||||
resp = self.client.post(
|
||||
reverse('auth_login') + '?next=/foo/',
|
||||
{'login': self.user.username,
|
||||
'password': self.user_password}
|
||||
)
|
||||
|
||||
self.assertEqual(302, resp.status_code)
|
||||
self.assertRegexpMatches(resp['Location'], r'http://testserver/foo/')
|
||||
|
||||
def test_bad_redirect_to_after_success_login(self):
|
||||
from django.utils.http import urlquote
|
||||
resp = self.client.post(
|
||||
reverse('auth_login') + '?next=' + urlquote('http://testserver\@example.com'),
|
||||
{'login': self.user.username,
|
||||
'password': self.user_password}
|
||||
)
|
||||
|
||||
self.assertEqual(302, resp.status_code)
|
||||
self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL)
|
||||
|
||||
def test_redirect_to_other_host_after_success_login(self):
|
||||
from django.utils.http import urlquote
|
||||
resp = self.client.post(
|
||||
reverse('auth_login') + '?next=' + urlquote('http://foo.com'),
|
||||
{'login': self.user.username,
|
||||
'password': self.user_password}
|
||||
)
|
||||
|
||||
self.assertEqual(302, resp.status_code)
|
||||
self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL)
|
Reference in New Issue
Block a user