Django

Code

Ticket #5515: 5515_make_403_errors_customizable.diff

File 5515_make_403_errors_customizable.diff, 8.8 kB (added by progprog, 7 months ago)

Patch to make 403 responses work like 404 and 500 responses (customizable via handler)

  • django/conf/urls/defaults.py

    old new  
    11from django.core.urlresolvers import RegexURLPattern, RegexURLResolver 
    22from django.core.exceptions import ImproperlyConfigured 
    33 
    4 __all__ = ['handler404', 'handler500', 'include', 'patterns', 'url'] 
     4__all__ = ['handler403', 'handler404', 'handler500', 'include', 'patterns', 'url'] 
    55 
     6handler403 = 'django.views.defaults.permission_denied' 
    67handler404 = 'django.views.defaults.page_not_found' 
    78handler500 = 'django.views.defaults.server_error' 
    89 
  • django/core/urlresolvers.py

    old new  
    268268        except (ImportError, AttributeError), e: 
    269269            raise ViewDoesNotExist, "Tried %s. Error was: %s" % (callback, str(e)) 
    270270 
     271    def resolve403(self): 
     272        return self._resolve_special('403') 
     273 
    271274    def resolve404(self): 
    272275        return self._resolve_special('404') 
    273276 
  • django/core/handlers/base.py

    old new  
    105105            else: 
    106106                callback, param_dict = resolver.resolve404() 
    107107                return callback(request, **param_dict) 
    108         except exceptions.PermissionDenied: 
    109             return http.HttpResponseForbidden('<h1>Permission denied</h1>') 
     108        except exceptions.PermissionDenied, e: 
     109            callback, param_dict = resolver.resolve403() 
     110            param_dict['reason'] = e.message 
     111            return callback(request, **param_dict) 
    110112        except SystemExit: 
    111113            pass # See http://code.djangoproject.com/ticket/1023 
    112114        except: # Handle everything else, including SuspiciousOperation, etc. 
  • django/core/exceptions.py

    old new  
    1010 
    1111class PermissionDenied(Exception): 
    1212    "The user did not have permission to do that" 
    13     pass 
     13    def __init__(self, message=None): 
     14        Exception.__init__(self) 
     15        self.message = message 
    1416 
    1517class ViewDoesNotExist(Exception): 
    1618    "The requested view does not exist" 
  • django/views/defaults.py

    old new  
    6666    else: 
    6767        return http.HttpResponseRedirect(absurl) 
    6868 
     69def permission_denied(request, template_name='403.html', reason=''): 
     70    """ 
     71    Default 403 handler. 
     72 
     73    Templates: `403.html` 
     74    Context: 
     75        request_path 
     76            The path of the requested URL (e.g., '/app/pages/form_page')     
     77    """ 
     78    context = { 
     79        'request_path': request.path, 
     80        'reason': reason, 
     81    } 
     82    t = loader.get_template(template_name) # You need to create a 403.html template. 
     83    return http.HttpResponseForbidden(t.render(RequestContext(request, context))) 
     84 
    6985def page_not_found(request, template_name='404.html'): 
    7086    """ 
    7187    Default 404 handler, which looks for the requested URL in the redirects 
  • django/contrib/csrf/middleware.py

    old new  
    66 
    77""" 
    88from django.conf import settings 
     9from django.core.exceptions import PermissionDenied 
    910from django.http import HttpResponseForbidden 
    1011from django.utils.safestring import mark_safe 
    1112import md5 
    1213import re 
    1314import itertools 
    1415 
    15 _ERROR_MSG = mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>') 
     16_ERROR_MSG = "Cross Site Request Forgery detected." 
    1617 
    1718_POST_FORM_RE = \ 
    1819    re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE) 
     
    5354            try: 
    5455                request_csrf_token = request.POST['csrfmiddlewaretoken'] 
    5556            except KeyError: 
    56                 return HttpResponseForbidden(_ERROR_MSG) 
     57                raise PermissionDenied(_ERROR_MSG) 
    5758             
    5859            if request_csrf_token != csrf_token: 
    59                 return HttpResponseForbidden(_ERROR_MSG) 
     60                raise PermissionDenied(_ERROR_MSG) 
    6061                 
    6162        return None 
    6263 
  • django/contrib/csrf/tests.py

    old new  
     1r""" 
     2>>> from django.conf import settings 
     3>>> from django import http 
     4>>> from django.contrib.csrf.middleware import CsrfMiddleware, _make_token 
     5>>> csrf = CsrfMiddleware() 
     6>>> request = http.HttpRequest() 
     7>>> request.method = 'POST' 
     8 
     9If no session exists, returns None (check not required) 
     10>>> csrf.process_request(request) 
     11 
     12If token doesn't exist, raise PermissionDenied 
     13>>> request.COOKIES[settings.SESSION_COOKIE_NAME] = 'my_session_id' 
     14>>> csrf.process_request(request) 
     15Traceback (most recent call last): 
     16    ... 
     17PermissionDenied 
     18 
     19If token exists and does not match session id, raise PermissionDenied 
     20>>> request.POST['csrfmiddlewaretoken'] = 'hackers_session_id' 
     21>>> csrf.process_request(request) 
     22Traceback (most recent call last): 
     23    ... 
     24PermissionDenied 
     25 
     26>>> request.POST['csrfmiddlewaretoken'] = _make_token('my_session_id') 
     27>>> csrf.process_request(request) 
     28 
     29""" 
     30 
     31if __name__ == '__main__': 
     32    import doctest 
     33    doctest.testmod() 
  • tests/regressiontests/views/tests/defaults.py

    old new  
    2525            response = self.client.get(short_url) 
    2626            self.assertEquals(response.status_code, 404) 
    2727 
     28    def test_permission_denied(self): 
     29        "A 403 status is returned by the permission_denied view" 
     30        response = self.client.get('/views/permission_denied_url/') 
     31        self.assertEquals(response.status_code, 403) 
     32 
     33    def test_permission_denied_with_reason(self): 
     34        "A 403 status can propagate the reason for denying to the permission_denied view" 
     35        response = self.client.get('/views/permission_denied_with_reason/') 
     36        self.assertContains(response, "Not allowed", status_code=403) 
     37 
    2838    def test_page_not_found(self): 
    2939        "A 404 status is returned by the page_not_found view" 
    3040        non_existing_urls = ['/views/non_existing_url/', # this is in urls.py 
     
    3444            self.assertEquals(response.status_code, 404) 
    3545 
    3646    def test_server_error(self): 
    37         "The server_error view raises a 500 status
     47        "A 500 status is returned by the server_error view
    3848        response = self.client.get('/views/server_error/') 
    3949        self.assertEquals(response.status_code, 500) 
  • tests/regressiontests/views/views.py

    old new  
     1from django.core.exceptions import PermissionDenied 
    12from django.http import HttpResponse 
    23from django.template import RequestContext 
    34 
     
    56    """Dummy index page""" 
    67    return HttpResponse('<html><body>Dummy page</body></html>') 
    78 
     9def generate_permission_denied_with_reason(request): 
     10    """Dummy page to test Permission Denied exception with reason""" 
     11    raise PermissionDenied("Not allowed") 
  • tests/regressiontests/views/urls.py

    old new  
    2525     
    2626    # Default views 
    2727    (r'^shortcut/(\d+)/(.*)/$', 'django.views.defaults.shortcut'), 
     28    (r'^permission_denied_url/', 'django.views.defaults.permission_denied'), 
     29    (r'^permission_denied_with_reason/', views.generate_permission_denied_with_reason), 
    2830    (r'^non_existing_url/', 'django.views.defaults.page_not_found'), 
    2931    (r'^server_error/', 'django.views.defaults.server_error'), 
    3032     
  • tests/templates/403.html

    old new  
     1Django Internal Tests: 403 Error 
     2{{ reason }}