Permalink
Browse files

Merge remote-tracking branch 'upstream/stable/1.3.x' into develop

Conflicts:
	.hgignore
	README
	django/__init__.py
	django/conf/__init__.py
	django/conf/global_settings.py
	django/conf/project_template/settings.py
	django/contrib/admin/filterspecs.py
	django/contrib/admin/options.py
	django/contrib/admin/sites.py
	django/contrib/admin/templates/registration/password_reset_email.html
	django/contrib/auth/forms.py
	django/contrib/auth/management/__init__.py
	django/contrib/auth/models.py
	django/contrib/auth/tests/auth_backends.py
	django/contrib/auth/tests/forms.py
	django/contrib/auth/tests/templates/registration/password_reset_email.html
	django/contrib/auth/tests/urls.py
	django/contrib/auth/tests/views.py
	django/contrib/auth/urls.py
	django/contrib/auth/views.py
	django/contrib/comments/views/comments.py
	django/contrib/comments/views/moderation.py
	django/contrib/comments/views/utils.py
	django/contrib/markup/templatetags/markup.py
	django/contrib/markup/tests.py
	django/contrib/sites/management.py
	django/contrib/sites/tests.py
	django/contrib/staticfiles/management/commands/collectstatic.py
	django/core/cache/backends/db.py
	django/core/files/images.py
	django/core/files/uploadhandler.py
	django/core/management/commands/shell.py
	django/core/serializers/python.py
	django/core/serializers/xml_serializer.py
	django/core/validators.py
	django/db/backends/__init__.py
	django/db/backends/mysql/base.py
	django/db/backends/sqlite3/base.py
	django/db/models/base.py
	django/db/models/deletion.py
	django/db/models/fields/__init__.py
	django/db/models/fields/related.py
	django/db/models/query.py
	django/db/models/sql/compiler.py
	django/db/models/sql/query.py
	django/forms/fields.py
	django/forms/formsets.py
	django/http/__init__.py
	django/http/multipartparser.py
	django/http/utils.py
	django/templatetags/future.py
	django/test/client.py
	django/test/utils.py
	django/utils/datastructures.py
	django/utils/encoding.py
	django/utils/http.py
	django/views/i18n.py
	docs/conf.py
	docs/howto/deployment/modpython.txt
	docs/howto/deployment/modwsgi.txt
	docs/howto/initial-data.txt
	docs/howto/static-files.txt
	docs/index.txt
	docs/internals/contributing.txt
	docs/internals/deprecation.txt
	docs/internals/release-process.txt
	docs/intro/overview.txt
	docs/intro/tutorial01.txt
	docs/intro/tutorial02.txt
	docs/intro/tutorial03.txt
	docs/ref/class-based-views.txt
	docs/ref/contrib/admin/index.txt
	docs/ref/contrib/gis/create_template_postgis-debian.sh
	docs/ref/contrib/gis/db-api.txt
	docs/ref/contrib/gis/install.txt
	docs/ref/contrib/markup.txt
	docs/ref/databases.txt
	docs/ref/django-admin.txt
	docs/ref/models/options.txt
	docs/ref/request-response.txt
	docs/ref/settings.txt
	docs/ref/templates/api.txt
	docs/releases/0.95.txt
	docs/releases/0.96.txt
	docs/releases/1.0.1.txt
	docs/releases/index.txt
	docs/topics/auth.txt
	docs/topics/class-based-views.txt
	docs/topics/db/models.txt
	docs/topics/db/optimization.txt
	docs/topics/forms/formsets.txt
	docs/topics/forms/modelforms.txt
	docs/topics/http/file-uploads.txt
	docs/topics/http/urls.txt
	docs/topics/i18n/index.txt
	docs/topics/i18n/internationalization.txt
	docs/topics/logging.txt
	docs/topics/templates.txt
	docs/topics/testing.txt
	setup.py
	tests/modeltests/validators/tests.py
	tests/regressiontests/admin_filterspecs/models.py
	tests/regressiontests/admin_filterspecs/tests.py
	tests/regressiontests/admin_views/tests.py
	tests/regressiontests/app_loading/tests.py
	tests/regressiontests/comment_tests/tests/comment_view_tests.py
	tests/regressiontests/comment_tests/tests/moderation_view_tests.py
	tests/regressiontests/file_uploads/tests.py
	tests/regressiontests/file_uploads/uploadhandler.py
	tests/regressiontests/file_uploads/urls.py
	tests/regressiontests/file_uploads/views.py
	tests/regressiontests/forms/tests/formsets.py
	tests/regressiontests/formwizard/templates/forms/wizard.html
	tests/regressiontests/generic_views/dates.py
	tests/regressiontests/httpwrappers/tests.py
	tests/regressiontests/requests/tests.py
	tests/regressiontests/serializers_regress/tests.py
	tests/regressiontests/settings_tests/tests.py
	tests/regressiontests/staticfiles_tests/tests.py
	tests/regressiontests/templates/tests.py
	tests/regressiontests/utils/datastructures.py
	tests/regressiontests/utils/http.py
	tests/regressiontests/views/tests/i18n.py
  • Loading branch information...
aburgel committed Feb 20, 2013
2 parents c68fd7e + a6927d8 commit 1aa654e345938de938ec61b12f29ab0fecde4c14
Showing with 1,452 additions and 365 deletions.
  1. +2 −9 .hgignore
  2. +1 −1 README
  3. +1 −1 django/__init__.py
  4. +3 −0 django/conf/__init__.py
  5. +4 −0 django/conf/global_settings.py
  6. +4 −0 django/conf/project_template/settings.py
  7. +6 −3 django/contrib/admin/filterspecs.py
  8. +13 −7 django/contrib/admin/options.py
  9. +1 −0 django/contrib/auth/tests/urls.py
  10. +39 −0 django/contrib/auth/tests/views.py
  11. +23 −29 django/contrib/auth/views.py
  12. +4 −7 django/contrib/comments/views/comments.py
  13. +4 −3 django/contrib/comments/views/moderation.py
  14. +6 −4 django/contrib/comments/views/utils.py
  15. +15 −2 django/contrib/markup/templatetags/markup.py
  16. +15 −1 django/contrib/markup/tests.py
  17. +16 −4 django/contrib/sites/management.py
  18. +6 −0 django/contrib/sites/tests.py
  19. +6 −4 django/contrib/staticfiles/management/commands/collectstatic.py
  20. +7 −1 django/core/cache/backends/db.py
  21. +6 −1 django/core/files/images.py
  22. +2 −3 django/core/management/commands/shell.py
  23. +93 −1 django/core/serializers/xml_serializer.py
  24. +5 −1 django/core/validators.py
  25. +1 −1 django/db/backends/mysql/base.py
  26. +1 −1 django/db/backends/sqlite3/base.py
  27. +1 −0 django/db/models/base.py
  28. +0 −1 django/db/models/fields/__init__.py
  29. +4 −14 django/forms/fields.py
  30. +10 −2 django/forms/formsets.py
  31. +67 −13 django/http/__init__.py
  32. +2 −0 django/http/multipartparser.py
  33. +2 −1 django/http/utils.py
  34. +1 −1 django/templatetags/future.py
  35. +6 −0 django/test/utils.py
  36. +9 −6 django/utils/datastructures.py
  37. +12 −0 django/utils/http.py
  38. +7 −5 django/views/i18n.py
  39. +2 −2 docs/conf.py
  40. +5 −2 docs/howto/deployment/modpython.txt
  41. +5 −2 docs/howto/deployment/modwsgi.txt
  42. +19 −6 docs/howto/initial-data.txt
  43. +2 −2 docs/howto/static-files.txt
  44. +1 −1 docs/index.txt
  45. +9 −12 docs/internals/contributing.txt
  46. +6 −0 docs/internals/deprecation.txt
  47. +10 −0 docs/internals/release-process.txt
  48. +1 −1 docs/intro/overview.txt
  49. +4 −5 docs/intro/tutorial01.txt
  50. +5 −3 docs/intro/tutorial02.txt
  51. +8 −18 docs/intro/tutorial03.txt
  52. +2 −2 docs/ref/class-based-views.txt
  53. +4 −5 docs/ref/contrib/admin/index.txt
  54. +11 −5 docs/ref/contrib/gis/create_template_postgis-debian.sh
  55. +1 −1 docs/ref/contrib/gis/db-api.txt
  56. +22 −11 docs/ref/contrib/gis/install.txt
  57. +17 −1 docs/ref/contrib/markup.txt
  58. +8 −6 docs/ref/databases.txt
  59. +5 −9 docs/ref/django-admin.txt
  60. +13 −0 docs/ref/models/options.txt
  61. +4 −0 docs/ref/request-response.txt
  62. +73 −19 docs/ref/settings.txt
  63. +1 −1 docs/ref/templates/api.txt
  64. +5 −6 docs/releases/0.95.txt
  65. +10 −18 docs/releases/0.96.txt
  66. +7 −8 docs/releases/1.0.1.txt
  67. +78 −0 docs/releases/1.3.6.txt
  68. +1 −0 docs/releases/index.txt
  69. +13 −5 docs/topics/auth.txt
  70. +4 −2 docs/topics/class-based-views.txt
  71. +2 −3 docs/topics/db/models.txt
  72. +1 −1 docs/topics/db/optimization.txt
  73. +4 −2 docs/topics/forms/formsets.txt
  74. +2 −2 docs/topics/forms/modelforms.txt
  75. +7 −0 docs/topics/http/urls.txt
  76. +1 −1 docs/topics/i18n/index.txt
  77. +6 −2 docs/topics/i18n/internationalization.txt
  78. +1 −1 docs/topics/logging.txt
  79. +2 −0 docs/topics/templates.txt
  80. +1 −1 docs/topics/testing.txt
  81. +1 −1 setup.py
  82. +3 −0 tests/modeltests/validators/tests.py
  83. +15 −0 tests/regressiontests/admin_filterspecs/models.py
  84. +66 −2 tests/regressiontests/admin_filterspecs/tests.py
  85. +40 −0 tests/regressiontests/admin_views/tests.py
  86. +3 −1 tests/regressiontests/app_loading/tests.py
  87. +7 −0 tests/regressiontests/comment_tests/tests/comment_view_tests.py
  88. +85 −4 tests/regressiontests/comment_tests/tests/moderation_view_tests.py
  89. +25 −0 tests/regressiontests/file_uploads/tests.py
  90. +9 −8 tests/regressiontests/file_uploads/urls.py
  91. +7 −0 tests/regressiontests/file_uploads/views.py
  92. +72 −13 tests/regressiontests/forms/tests/formsets.py
  93. +1 −1 tests/regressiontests/formwizard/templates/forms/wizard.html
  94. +2 −1 tests/regressiontests/generic_views/dates.py
  95. +23 −2 tests/regressiontests/httpwrappers/tests.py
  96. +127 −26 tests/regressiontests/requests/tests.py
  97. +15 −0 tests/regressiontests/serializers_regress/tests.py
  98. +7 −0 tests/regressiontests/settings_tests/tests.py
  99. +53 −0 tests/regressiontests/staticfiles_tests/tests.py
  100. +1 −0 tests/regressiontests/templates/templates/ssi include with spaces.html
  101. +32 −12 tests/regressiontests/templates/tests.py
  102. +6 −0 tests/regressiontests/utils/datastructures.py
  103. +48 −0 tests/regressiontests/utils/http.py
  104. +16 −1 tests/regressiontests/views/tests/i18n.py
View
@@ -1,13 +1,6 @@
-syntax: glob
-*~
-*.tmp
-*.swp
+syntax:glob
+
*.egg-info
*.pot
*.py[co]
docs/_build/
-*.orig
-desktop.ini
-nbproject
-tests/django
-build
View
2 README
@@ -28,7 +28,7 @@ http://code.djangoproject.com/newticket
To get more help:
* Join the #django channel on irc.freenode.net. Lots of helpful people
- hang out there. Read the archives at http://botland.oebfare.com/logger/django/.
+ hang out there. Read the archives at http://django-irc-logs.com/.
* Join the django-users mailing list, or read the archives, at
http://groups.google.com/group/django-users.
View
@@ -1,4 +1,4 @@
-VERSION = (1, 3, 1, 'final', 0)
+VERSION = (1, 3, 6, 'final', 0)
def get_version():
version = '%s.%s' % (VERSION[0], VERSION[1])
View
@@ -70,6 +70,9 @@ def __setattr__(self, name, value):
if name in ("MEDIA_URL", "STATIC_URL") and value and not value.endswith('/'):
warnings.warn('If set, %s must end with a slash' % name,
PendingDeprecationWarning)
+ elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, basestring):
+ raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set "
+ "to a tuple, not a string.")
object.__setattr__(self, name, value)
@@ -29,6 +29,10 @@
# * Receive x-headers
INTERNAL_IPS = ()
+# Hosts/domain names that are valid for this site.
+# "*" matches anything, ".example.com" matches example.com and all subdomains
+ALLOWED_HOSTS = ['*']
+
# Local time zone for this installation. All choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
# systems may support all possibilities).
@@ -20,6 +20,10 @@
}
}
+# Hosts/domain names that are valid for this site; required if DEBUG is False
+# See https://docs.djangoproject.com/en/1.3/ref/settings/#allowed-hosts
+ALLOWED_HOSTS = []
+
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
@@ -74,9 +74,12 @@ def __init__(self, f, request, params, model, model_admin,
self.lookup_title = other_model._meta.verbose_name
else:
self.lookup_title = f.verbose_name # use field name
- rel_name = other_model._meta.pk.name
+ if hasattr(f, 'rel'):
+ rel_name = f.rel.get_related_field().name
+ else:
+ rel_name = other_model._meta.pk.name
self.lookup_kwarg = '%s__%s__exact' % (self.field_path, rel_name)
- self.lookup_kwarg_isnull = '%s__isnull' % (self.field_path)
+ self.lookup_kwarg_isnull = '%s__isnull' % self.field_path
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
self.lookup_val_isnull = request.GET.get(
self.lookup_kwarg_isnull, None)
@@ -176,7 +179,7 @@ def choices(self, cl):
class DateFieldFilterSpec(FilterSpec):
def __init__(self, f, request, params, model, model_admin,
- field_path=None):
+ field_path=None):
super(DateFieldFilterSpec, self).__init__(f, request, params, model,
model_admin,
field_path=field_path)
@@ -225,18 +225,18 @@ def lookup_allowed(self, lookup, value):
# if foo has been specificially included in the lookup list; so
# drop __id if it is the last part. However, first we need to find
# the pk attribute name.
- pk_attr_name = None
+ rel_name = None
for part in parts[:-1]:
field, _, _, _ = model._meta.get_field_by_name(part)
if hasattr(field, 'rel'):
model = field.rel.to
- pk_attr_name = model._meta.pk.name
+ rel_name = field.rel.get_related_field().name
elif isinstance(field, RelatedObject):
model = field.model
- pk_attr_name = model._meta.pk.name
+ rel_name = model._meta.pk.name
else:
- pk_attr_name = None
- if pk_attr_name and len(parts) > 1 and parts[-1] == pk_attr_name:
+ rel_name = None
+ if rel_name and len(parts) > 1 and parts[-1] == rel_name:
parts.pop()
try:
@@ -1242,15 +1242,21 @@ def delete_view(self, request, object_id, extra_context=None):
def history_view(self, request, object_id, extra_context=None):
"The 'history' admin view for this model."
from django.contrib.admin.models import LogEntry
+ # First check if the user can see this history.
model = self.model
+ obj = get_object_or_404(model, pk=unquote(object_id))
+
+ if not self.has_change_permission(request, obj):
+ raise PermissionDenied
+
+ # Then get the history for this object.
opts = model._meta
app_label = opts.app_label
action_list = LogEntry.objects.filter(
object_id = object_id,
content_type__id__exact = ContentType.objects.get_for_model(model).id
).select_related().order_by('action_time')
- # If no history was found, see whether this object even exists.
- obj = get_object_or_404(model, pk=unquote(object_id))
+
context = {
'title': _('Change history: %s') % force_unicode(obj),
'action_list': action_list,
@@ -19,6 +19,7 @@ def remote_user_auth_view(request):
(r'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')),
(r'^remote_user/$', remote_user_auth_view),
(r'^password_reset_from_email/$', 'django.contrib.auth.views.password_reset', dict(from_email='staffmember@example.com')),
+ (r'^admin_password_reset/$', 'django.contrib.auth.views.password_reset', dict(is_admin_site=True)),
(r'^login_required/$', login_required(password_reset)),
(r'^login_required_login_url/$', login_required(password_reset, login_url='/somewhere/')),
)
@@ -9,6 +9,7 @@
from django.contrib.auth.models import User
from django.test import TestCase
from django.core import mail
+from django.core.exceptions import SuspiciousOperation
from django.core.urlresolvers import reverse
from django.http import QueryDict
@@ -69,6 +70,44 @@ def test_email_found_custom_from(self):
self.assertEqual(len(mail.outbox), 1)
self.assertEqual("staffmember@example.com", mail.outbox[0].from_email)
+ def test_admin_reset(self):
+ "If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override."
+ response = self.client.post('/admin_password_reset/',
+ {'email': 'staffmember@example.com'},
+ HTTP_HOST='adminsite.com'
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(len(mail.outbox), 1)
+ self.assertTrue("http://adminsite.com" in mail.outbox[0].body)
+ self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email)
+
+ def test_poisoned_http_host(self):
+ "Poisoned HTTP_HOST headers can't be used for reset emails"
+ # This attack is based on the way browsers handle URLs. The colon
+ # should be used to separate the port, but if the URL contains an @,
+ # the colon is interpreted as part of a username for login purposes,
+ # making 'evil.com' the request domain. Since HTTP_HOST is used to
+ # produce a meaningful reset URL, we need to be certain that the
+ # HTTP_HOST header isn't poisoned. This is done as a check when get_host()
+ # is invoked, but we check here as a practical consequence.
+ def test_host_poisoning():
+ self.client.post('/password_reset/',
+ {'email': 'staffmember@example.com'},
+ HTTP_HOST='www.example:dr.frankenstein@evil.tld'
+ )
+ self.assertRaises(SuspiciousOperation, test_host_poisoning)
+ self.assertEqual(len(mail.outbox), 0)
+
+ def test_poisoned_http_host_admin_site(self):
+ "Poisoned HTTP_HOST headers can't be used for reset emails on admin views"
+ def test_host_poisoning():
+ self.client.post('/admin_password_reset/',
+ {'email': 'staffmember@example.com'},
+ HTTP_HOST='www.example:dr.frankenstein@evil.tld'
+ )
+ self.assertRaises(SuspiciousOperation, test_host_poisoning)
+ self.assertEqual(len(mail.outbox), 0)
+
def _test_confirm_start(self):
# Start by creating the email
response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
@@ -5,7 +5,7 @@
from django.http import HttpResponseRedirect, QueryDict
from django.shortcuts import render_to_response
from django.template import RequestContext
-from django.utils.http import urlsafe_base64_decode
+from django.utils.http import urlsafe_base64_decode, is_safe_url
from django.utils.translation import ugettext as _
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
@@ -33,18 +33,11 @@ def login(request, template_name='registration/login.html',
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
- netloc = urlparse.urlparse(redirect_to)[1]
-
- # Use default setting if redirect_to is empty
- if not redirect_to:
- redirect_to = settings.LOGIN_REDIRECT_URL
-
- # Security check -- don't allow redirection to a different
- # host.
- elif netloc and netloc != request.get_host():
+ # Ensure the user-originating redirection url is safe.
+ if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = settings.LOGIN_REDIRECT_URL
- # Okay, security checks complete. Log the user in.
+ # Okay, security check complete. Log the user in.
auth_login(request, form.get_user())
if request.session.test_cookie_worked():
@@ -76,26 +69,27 @@ def logout(request, next_page=None,
Logs out the user and displays 'You are logged out' message.
"""
auth_logout(request)
- redirect_to = request.REQUEST.get(redirect_field_name, '')
- if redirect_to:
- netloc = urlparse.urlparse(redirect_to)[1]
+
+ if redirect_field_name in request.REQUEST:
+ next_page = request.REQUEST[redirect_field_name]
# Security check -- don't allow redirection to a different host.
- if not (netloc and netloc != request.get_host()):
- return HttpResponseRedirect(redirect_to)
+ if not is_safe_url(url=next_page, host=request.get_host()):
+ next_page = request.path
- if next_page is None:
- current_site = get_current_site(request)
- context = {
- 'site': current_site,
- 'site_name': current_site.name,
- 'title': _('Logged out')
- }
- context.update(extra_context or {})
- return render_to_response(template_name, context,
- context_instance=RequestContext(request, current_app=current_app))
- else:
+ if next_page:
# Redirect to this page until the session has been cleared.
- return HttpResponseRedirect(next_page or request.path)
+ return HttpResponseRedirect(next_page)
+
+ current_site = get_current_site(request)
+ context = {
+ 'site': current_site,
+ 'site_name': current_site.name,
+ 'title': _('Logged out')
+ }
+ if extra_context is not None:
+ context.update(extra_context)
+ return render_to_response(template_name, context,
+ context_instance=RequestContext(request, current_app=current_app))
def logout_then_login(request, login_url=None, current_app=None, extra_context=None):
"""
@@ -151,7 +145,7 @@ def password_reset(request, is_admin_site=False,
'request': request,
}
if is_admin_site:
- opts = dict(opts, domain_override=request.META['HTTP_HOST'])
+ opts = dict(opts, domain_override=request.get_host())
form.save(**opts)
return HttpResponseRedirect(post_reset_redirect)
else:
@@ -40,9 +40,6 @@ def post_comment(request, next=None, using=None):
if not data.get('email', ''):
data["email"] = request.user.email
- # Check to see if the POST data overrides the view's next argument.
- next = data.get("next", next)
-
# Look up the object we're trying to comment about
ctype = data.get("content_type")
object_pk = data.get("object_pk")
@@ -94,9 +91,9 @@ def post_comment(request, next=None, using=None):
]
return render_to_response(
template_list, {
- "comment" : form.data.get("comment", ""),
- "form" : form,
- "next": next,
+ "comment": form.data.get("comment", ""),
+ "form": form,
+ "next": data.get("next", next),
},
RequestContext(request, {})
)
@@ -127,7 +124,7 @@ def post_comment(request, next=None, using=None):
request = request
)
- return next_redirect(data, next, comment_done, c=comment._get_pk_val())
+ return next_redirect(request, next, comment_done, c=comment._get_pk_val())
comment_done = confirmation_view(
template = "comments/posted.html",
@@ -7,6 +7,7 @@
from django.contrib.comments import signals
from django.views.decorators.csrf import csrf_protect
+
@csrf_protect
@login_required
def flag(request, comment_id, next=None):
@@ -23,7 +24,7 @@ def flag(request, comment_id, next=None):
# Flag on POST
if request.method == 'POST':
perform_flag(request, comment)
- return next_redirect(request.POST.copy(), next, flag_done, c=comment.pk)
+ return next_redirect(request, next, flag_done, c=comment.pk)
# Render a form on GET
else:
@@ -50,7 +51,7 @@ def delete(request, comment_id, next=None):
if request.method == 'POST':
# Flag the comment as deleted instead of actually deleting it.
perform_delete(request, comment)
- return next_redirect(request.POST.copy(), next, delete_done, c=comment.pk)
+ return next_redirect(request, next, delete_done, c=comment.pk)
# Render a form on GET
else:
@@ -77,7 +78,7 @@ def approve(request, comment_id, next=None):
if request.method == 'POST':
# Flag the comment as approved.
perform_approve(request, comment)
- return next_redirect(request.POST.copy(), next, approve_done, c=comment.pk)
+ return next_redirect(request, next, approve_done, c=comment.pk)
# Render a form on GET
else:
@@ -4,14 +4,15 @@
import urllib
import textwrap
-from django.http import HttpResponseRedirect
from django.core import urlresolvers
+from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.core.exceptions import ObjectDoesNotExist
from django.contrib import comments
+from django.utils.http import is_safe_url
-def next_redirect(data, default, default_view, **get_kwargs):
+def next_redirect(request, default, default_view, **get_kwargs):
"""
Handle the "where should I go next?" part of comment views.
@@ -21,9 +22,10 @@ def next_redirect(data, default, default_view, **get_kwargs):
Returns an ``HttpResponseRedirect``.
"""
- next = data.get("next", default)
- if next is None:
+ next = request.POST.get('next', default)
+ if not is_safe_url(url=next, host=request.get_host()):
next = urlresolvers.reverse(default_view)
+
if get_kwargs:
if '#' in next:
tmp = next.rsplit('#', 1)
Oops, something went wrong.

0 comments on commit 1aa654e

Please sign in to comment.