mirror of https://github.com/django/django.git
Fixed #9847 -- Added 403 response handler. Many thanks to kgrandis, adamnelson, vkryachko, fvox13 and Chris Beaven.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16606 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
958e049d4d
commit
1ca6e9b9e2
1
AUTHORS
1
AUTHORS
|
@ -457,6 +457,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Ben Slavin <benjamin.slavin@gmail.com>
|
Ben Slavin <benjamin.slavin@gmail.com>
|
||||||
sloonz <simon.lipp@insa-lyon.fr>
|
sloonz <simon.lipp@insa-lyon.fr>
|
||||||
Paul Smith <blinkylights23@gmail.com>
|
Paul Smith <blinkylights23@gmail.com>
|
||||||
|
Steven L. Smith (fvox13) <steven@stevenlsmith.com>
|
||||||
Warren Smith <warren@wandrsmith.net>
|
Warren Smith <warren@wandrsmith.net>
|
||||||
smurf@smurf.noris.de
|
smurf@smurf.noris.de
|
||||||
Vsevolod Solovyov
|
Vsevolod Solovyov
|
||||||
|
|
|
@ -6,6 +6,7 @@ from django.utils.importlib import import_module
|
||||||
|
|
||||||
__all__ = ['handler404', 'handler500', 'include', 'patterns', 'url']
|
__all__ = ['handler404', 'handler500', 'include', 'patterns', 'url']
|
||||||
|
|
||||||
|
handler403 = 'django.views.defaults.permission_denied'
|
||||||
handler404 = 'django.views.defaults.page_not_found'
|
handler404 = 'django.views.defaults.page_not_found'
|
||||||
handler500 = 'django.views.defaults.server_error'
|
handler500 = 'django.views.defaults.server_error'
|
||||||
|
|
||||||
|
|
|
@ -154,12 +154,22 @@ class BaseHandler(object):
|
||||||
finally:
|
finally:
|
||||||
receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
|
receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
|
||||||
except exceptions.PermissionDenied:
|
except exceptions.PermissionDenied:
|
||||||
logger.warning('Forbidden (Permission denied): %s' % request.path,
|
logger.warning(
|
||||||
extra={
|
'Forbidden (Permission denied): %s' % request.path,
|
||||||
'status_code': 403,
|
extra={
|
||||||
'request': request
|
'status_code': 403,
|
||||||
})
|
'request': request
|
||||||
response = http.HttpResponseForbidden('<h1>Permission denied</h1>')
|
})
|
||||||
|
try:
|
||||||
|
callback, param_dict = resolver.resolve403()
|
||||||
|
response = callback(request, **param_dict)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
response = self.handle_uncaught_exception(request,
|
||||||
|
resolver, sys.exc_info())
|
||||||
|
finally:
|
||||||
|
receivers = signals.got_request_exception.send(
|
||||||
|
sender=self.__class__, request=request)
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
|
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
|
||||||
raise
|
raise
|
||||||
|
|
|
@ -331,6 +331,9 @@ class RegexURLResolver(LocaleRegexProvider):
|
||||||
callback = getattr(defaults, 'handler%s' % view_type)
|
callback = getattr(defaults, 'handler%s' % view_type)
|
||||||
return get_callable(callback), {}
|
return get_callable(callback), {}
|
||||||
|
|
||||||
|
def resolve403(self):
|
||||||
|
return self._resolve_special('403')
|
||||||
|
|
||||||
def resolve404(self):
|
def resolve404(self):
|
||||||
return self._resolve_special('404')
|
return self._resolve_special('404')
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
from django import http
|
from django import http
|
||||||
|
from django.template import (Context, RequestContext,
|
||||||
|
loader, TemplateDoesNotExist)
|
||||||
from django.views.decorators.csrf import requires_csrf_token
|
from django.views.decorators.csrf import requires_csrf_token
|
||||||
from django.template import Context, RequestContext, loader
|
|
||||||
|
|
||||||
|
|
||||||
# This can be called when CsrfViewMiddleware.process_view has not run, therefore
|
# This can be called when CsrfViewMiddleware.process_view has not run,
|
||||||
# need @requires_csrf_token in case the template needs {% csrf_token %}.
|
# therefore need @requires_csrf_token in case the template needs
|
||||||
|
# {% csrf_token %}.
|
||||||
@requires_csrf_token
|
@requires_csrf_token
|
||||||
def page_not_found(request, template_name='404.html'):
|
def page_not_found(request, template_name='404.html'):
|
||||||
"""
|
"""
|
||||||
|
@ -31,6 +33,27 @@ def server_error(request, template_name='500.html'):
|
||||||
return http.HttpResponseServerError(t.render(Context({})))
|
return http.HttpResponseServerError(t.render(Context({})))
|
||||||
|
|
||||||
|
|
||||||
|
# This can be called when CsrfViewMiddleware.process_view has not run,
|
||||||
|
# therefore need @requires_csrf_token in case the template needs
|
||||||
|
# {% csrf_token %}.
|
||||||
|
@requires_csrf_token
|
||||||
|
def permission_denied(request, template_name='403.html'):
|
||||||
|
"""
|
||||||
|
Permission denied (403) handler.
|
||||||
|
|
||||||
|
Templates: `403.html`
|
||||||
|
Context: None
|
||||||
|
|
||||||
|
If the template does not exist, an Http403 response containing the text
|
||||||
|
"403 Forbidden" (as per RFC 2616) will be returned.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
template = loader.get_template(template_name)
|
||||||
|
except TemplateDoesNotExist:
|
||||||
|
return http.HttpResponseForbidden('<h1>403 Forbidden</h1>')
|
||||||
|
return http.HttpResponseForbidden(template.render(RequestContext(request)))
|
||||||
|
|
||||||
|
|
||||||
def shortcut(request, content_type_id, object_id):
|
def shortcut(request, content_type_id, object_id):
|
||||||
# TODO: Remove this in Django 2.0.
|
# TODO: Remove this in Django 2.0.
|
||||||
# This is a legacy view that depends on the contenttypes framework.
|
# This is a legacy view that depends on the contenttypes framework.
|
||||||
|
|
|
@ -261,6 +261,11 @@ Django 1.4 also includes several smaller improvements worth noting:
|
||||||
* It is now possible to load fixtures containing forward references when using
|
* It is now possible to load fixtures containing forward references when using
|
||||||
MySQL with the InnoDB database engine.
|
MySQL with the InnoDB database engine.
|
||||||
|
|
||||||
|
* A new 403 response handler has been added as
|
||||||
|
``'django.views.defaults.permission_denied'``. See the documentation
|
||||||
|
about :ref:`the 403 (HTTP Forbidden) view<http_forbidden_view>` for more
|
||||||
|
information.
|
||||||
|
|
||||||
.. _backwards-incompatible-changes-1.4:
|
.. _backwards-incompatible-changes-1.4:
|
||||||
|
|
||||||
Backwards incompatible changes in 1.4
|
Backwards incompatible changes in 1.4
|
||||||
|
|
|
@ -197,3 +197,24 @@ Two things to note about 500 views:
|
||||||
* If :setting:`DEBUG` is set to ``True`` (in your settings module), then
|
* If :setting:`DEBUG` is set to ``True`` (in your settings module), then
|
||||||
your 500 view will never be used, and the traceback will be displayed
|
your 500 view will never be used, and the traceback will be displayed
|
||||||
instead, with some debug information.
|
instead, with some debug information.
|
||||||
|
|
||||||
|
.. _http_forbidden_view:
|
||||||
|
|
||||||
|
The 403 (HTTP Forbidden) view
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 1.4
|
||||||
|
|
||||||
|
In the same vein as the 404 and 500 views, Django has a view to handle 403
|
||||||
|
Forbidden errors. If a view results in a 403 exception then Django will, by
|
||||||
|
default, call the view ``django.views.defaults.permission_denied``.
|
||||||
|
|
||||||
|
This view loads and renders the template ``403.html`` in your root template
|
||||||
|
directory, or if this file does not exist, instead serves the text
|
||||||
|
"403 Forbidden", as per RFC 2616 (the HTTP 1.1 Specification).
|
||||||
|
|
||||||
|
It is possible to override ``django.views.defaults.permission_denied`` in the
|
||||||
|
same way you can for the 404 and 500 views by specifying a ``handler403`` in
|
||||||
|
your URLconf::
|
||||||
|
|
||||||
|
handler403 = 'mysite.views.my_custom_permission_denied_view'
|
||||||
|
|
|
@ -6,6 +6,8 @@ import sys
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
from django.test import TestCase, RequestFactory
|
from django.test import TestCase, RequestFactory
|
||||||
|
from django.test.utils import (setup_test_template_loader,
|
||||||
|
restore_template_loaders)
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.template import TemplateSyntaxError
|
from django.template import TemplateSyntaxError
|
||||||
from django.views.debug import ExceptionReporter
|
from django.views.debug import ExceptionReporter
|
||||||
|
@ -40,6 +42,26 @@ class DebugViewTests(TestCase):
|
||||||
self.assertTrue('file_data.txt' in response.content)
|
self.assertTrue('file_data.txt' in response.content)
|
||||||
self.assertFalse('haha' in response.content)
|
self.assertFalse('haha' in response.content)
|
||||||
|
|
||||||
|
def test_403(self):
|
||||||
|
# Ensure no 403.html template exists to test the default case.
|
||||||
|
setup_test_template_loader({})
|
||||||
|
try:
|
||||||
|
response = self.client.get('/views/raises403/')
|
||||||
|
self.assertContains(response, '<h1>403 Forbidden</h1>', status_code=403)
|
||||||
|
finally:
|
||||||
|
restore_template_loaders()
|
||||||
|
|
||||||
|
def test_403_template(self):
|
||||||
|
# Set up a test 403.html template.
|
||||||
|
setup_test_template_loader(
|
||||||
|
{'403.html': 'This is a test template for a 403 Forbidden error.'}
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
response = self.client.get('/views/raises403/')
|
||||||
|
self.assertContains(response, 'test template', status_code=403)
|
||||||
|
finally:
|
||||||
|
restore_template_loaders()
|
||||||
|
|
||||||
def test_404(self):
|
def test_404(self):
|
||||||
response = self.client.get('/views/raises404/')
|
response = self.client.get('/views/raises404/')
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
|
@ -39,8 +39,9 @@ urlpatterns = patterns('',
|
||||||
(r'^server_error/', 'django.views.defaults.server_error'),
|
(r'^server_error/', 'django.views.defaults.server_error'),
|
||||||
|
|
||||||
# a view that raises an exception for the debug view
|
# a view that raises an exception for the debug view
|
||||||
(r'^raises/$', views.raises),
|
(r'raises/$', views.raises),
|
||||||
(r'^raises404/$', views.raises404),
|
(r'raises404/$', views.raises404),
|
||||||
|
(r'raises403/$', views.raises403),
|
||||||
|
|
||||||
# i18n views
|
# i18n views
|
||||||
(r'^i18n/', include('django.conf.urls.i18n')),
|
(r'^i18n/', include('django.conf.urls.i18n')),
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.core.urlresolvers import get_resolver
|
from django.core.urlresolvers import get_resolver
|
||||||
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import render_to_response, render
|
from django.shortcuts import render_to_response, render
|
||||||
from django.template import Context, RequestContext, TemplateDoesNotExist
|
from django.template import Context, RequestContext, TemplateDoesNotExist
|
||||||
from django.views.debug import technical_500_response, SafeExceptionReporterFilter
|
from django.views.debug import technical_500_response, SafeExceptionReporterFilter
|
||||||
|
@ -53,6 +54,9 @@ def raises404(request):
|
||||||
resolver = get_resolver(None)
|
resolver = get_resolver(None)
|
||||||
resolver.resolve('')
|
resolver.resolve('')
|
||||||
|
|
||||||
|
def raises403(request):
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
def redirect(request):
|
def redirect(request):
|
||||||
"""
|
"""
|
||||||
Forces an HTTP redirect.
|
Forces an HTTP redirect.
|
||||||
|
|
Loading…
Reference in New Issue