Beispiel #1
0
    def process_exception(self, request, exception):
        '''Rewrite the URL based on settings.APPEND_SLASH and the urlconf of the current page.'''

        if isinstance(exception, Http404):
            if settings.APPEND_SLASH and not request.path_info.endswith('/'):
                page = request.pages.current

                if page:
                    script_name = page.get_absolute_url()[:-1]
                    path_info = request.path[len(script_name):]

                    urlconf = getattr(page.content,
                                      'urlconf', None) if hasattr(
                                          page, 'content') else None

                    # Check if the URL with a slash appended is resolved by the current page's urlconf
                    if (is_valid_path(path_info, urlconf)
                            or not is_valid_path(f'{path_info}/', urlconf)):
                        # Check if the URL with a slash appended resolves for something other than a page
                        match = resolve(f'{path_info}/',
                                        getattr(request, 'urlconf', None))
                        if getattr(match.func, 'view_class',
                                   None) is PageDispatcherView:
                            # Couldn't find any view that would be resolved for this URL
                            # No point redirecting to a URL that will 404
                            return None

                new_path = request.get_full_path(force_append_slash=True)
                # Prevent construction of scheme relative urls.
                new_path = escape_leading_slashes(new_path)

                return HttpResponsePermanentRedirect(new_path)
Beispiel #2
0
def page_view(request, path):
    m = find_route(path)
    if m is None:
        if settings.APPEND_SLASH and not path.endswith('/'):
            m = find_route(f'{path}/')
            if m is not None:
                new_path = request.get_full_path(force_append_slash=True)
                new_path = escape_leading_slashes(new_path)
                return HttpResponsePermanentRedirect(new_path)
        raise Http404

    data = None
    if m.route.endpoint:
        endpoint, params = m.build(allow_preview=settings.ALLOW_PREVIEW)
        r = requests.get(endpoint, params=params)
        if r.status_code == 404:
            raise Http404
        r.raise_for_status()
        data = r.json()

    context = {
        'data': data,
    }
    return TemplateResponse(
        request,
        m.route.template_name,
        context=context,
        content_type=m.route.content_type,
    )
Beispiel #3
0
 def test(self):
     tests = (
         ('//example.com', '/%2Fexample.com'),
         ('//', '/%2F'),
     )
     for url, expected in tests:
         self.assertEqual(http.escape_leading_slashes(url), expected)
Beispiel #4
0
 def test(self):
     tests = (
         ('//example.com', '/%2Fexample.com'),
         ('//', '/%2F'),
     )
     for url, expected in tests:
         with self.subTest(url=url):
             self.assertEqual(escape_leading_slashes(url), expected)
Beispiel #5
0
 def get_full_path_with_slash(self, request):
     """ Return the full path of the request with a trailing slash appended
         without Exception in Debug mode
     """
     new_path = request.get_full_path(force_append_slash=True)
     # Prevent construction of scheme relative urls.
     new_path = escape_leading_slashes(new_path)
     return new_path
Beispiel #6
0
 def test(self):
     tests = (
         ('//example.com', '/%2Fexample.com'),
         ('//', '/%2F'),
     )
     for url, expected in tests:
         with self.subTest(url=url):
             self.assertEqual(escape_leading_slashes(url), expected)
Beispiel #7
0
 def test(self):
     tests = (
         ("//example.com", "/%2Fexample.com"),
         ("//", "/%2F"),
     )
     for url, expected in tests:
         with self.subTest(url=url):
             self.assertEqual(escape_leading_slashes(url), expected)
Beispiel #8
0
 def __call__(self, request):
     response = self.get_response(request)
     # This is based on APPEND_SLASH handling in Django
     if response.status_code == 404 and self.should_redirect_with_slash(
             request):
         new_path = request.get_full_path(force_append_slash=True)
         # Prevent construction of scheme relative urls.
         new_path = escape_leading_slashes(new_path)
         return HttpResponsePermanentRedirect(new_path)
     return response
Beispiel #9
0
def reverse_route(route_name, args=None, kwargs=None):
    args = args or []
    kwargs = kwargs or {}
    prefix = get_script_prefix()

    try:
        route = Route.objects.filter(name=route_name).get()
    except Route.DoesNotExist:
        msg = ("Reverse for '%s' not found." % (route_name))
        raise NoReverseMatch(msg)

    converters = _route_to_regex(route.path)[1]

    for result, params in normalize(_route_to_regex(route.path)[0]):
        if args:
            if len(args) != len(params):
                continue
            candidate_subs = dict(zip(params, args))
        else:
            if set(kwargs).symmetric_difference(params):
                continue
            candidate_subs = kwargs

        text_candidate_subs = {}
        for k, v in candidate_subs.items():
            if k in converters:
                text_candidate_subs[k] = converters[k].to_url(v)
            else:
                text_candidate_subs[k] = str(v)

        candidate_pat = prefix.replace('%', '%%') + result
        url = urllib.parse.quote(candidate_pat % text_candidate_subs,
                                 safe=RFC3986_SUBDELIMS + '/~:@')
        return escape_leading_slashes(url)

    if args:
        arg_msg = "arguments '%s'" % (args, )
    elif kwargs:
        arg_msg = "keyword arguments '%s'" % (kwargs, )
    else:
        arg_msg = "no arguments"
    msg = ("Reverse for '%s' with %s not matched." % (route_name, arg_msg))
    raise NoReverseMatch(msg)
Beispiel #10
0
    def get_full_path_with_slash(self, request):
        """
        Return the full path of the request with a trailing slash appended.

        Raise a RuntimeError if settings.DEBUG is True and request.method is
        POST, PUT, or PATCH.
        """
        new_path = request.get_full_path(force_append_slash=True)
        # Prevent construction of scheme relative urls.
        new_path = escape_leading_slashes(new_path)
        if settings.DEBUG and request.method in ("POST", "PUT", "PATCH"):
            raise RuntimeError(
                "You called this URL via %(method)s, but the URL doesn't end "
                "in a slash and you have APPEND_SLASH set. Django can't "
                "redirect to the slash URL while maintaining %(method)s data. "
                "Change your form to point to %(url)s (note the trailing "
                "slash), or set APPEND_SLASH=False in your Django settings."
                % {"method": request.method, "url": request.get_host() + new_path}
            )
        return new_path
Beispiel #11
0
    def get_full_path_with_slash(self, request):
        """
        Return the full path of the request with a trailing slash appended.

        Raise a RuntimeError if settings.DEBUG is True and request.method is
        POST, PUT, or PATCH.
        """
        new_path = request.get_full_path(force_append_slash=True)
        # Prevent construction of scheme relative urls.
        new_path = escape_leading_slashes(new_path)
        if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'):
            raise RuntimeError(
                "You called this URL via %(method)s, but the URL doesn't end "
                "in a slash and you have APPEND_SLASH set. Django can't "
                "redirect to the slash URL while maintaining %(method)s data. "
                "Change your form to point to %(url)s (note the trailing "
                "slash), or set APPEND_SLASH=False in your Django settings." % {
                    'method': request.method,
                    'url': request.get_host() + new_path,
                }
            )
        return new_path
Beispiel #12
0
    def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs):
        if args and kwargs:
            raise ValueError(
                "Don't mix *args and **kwargs in call to reverse()!")

        if not self._populated:
            self._populate()

        possibilities = self.reverse_dict.getlist(lookup_view)

        for possibility, pattern, defaults, converters in possibilities:
            for result, params in possibility:
                if args:
                    if len(args) != len(params):
                        continue
                    candidate_subs = dict(zip(params, args))
                else:
                    if set(kwargs).symmetric_difference(params).difference(
                            defaults):
                        continue
                    if any(kwargs.get(k, v) != v for k, v in defaults.items()):
                        continue
                    candidate_subs = kwargs
                # Convert the candidate subs to text using Converter.to_url().
                text_candidate_subs = {}
                match = True
                for k, v in candidate_subs.items():
                    if k in converters:
                        try:
                            text_candidate_subs[k] = converters[k].to_url(v)
                        except ValueError:
                            match = False
                            break
                    else:
                        text_candidate_subs[k] = str(v)
                if not match:
                    continue
                # WSGI provides decoded URLs, without %xx escapes, and the URL
                # resolver operates on such URLs. First substitute arguments
                # without quoting to build a decoded URL and look for a match.
                # Then, if we have a match, redo the substitution with quoted
                # arguments in order to return a properly encoded URL.
                candidate_pat = _prefix.replace('%', '%%') + result
                if re.search('^%s%s' % (re.escape(_prefix), pattern),
                             candidate_pat % text_candidate_subs):
                    # safe characters from `pchar` definition of RFC 3986
                    url = quote(candidate_pat % text_candidate_subs,
                                safe=RFC3986_SUBDELIMS + '/~:@')
                    # Don't allow construction of scheme relative urls.
                    return escape_leading_slashes(url)
        # lookup_view can be URL name or callable, but callables are not
        # friendly in error messages.
        m = getattr(lookup_view, '__module__', None)
        n = getattr(lookup_view, '__name__', None)
        if m is not None and n is not None:
            lookup_view_s = "%s.%s" % (m, n)
        else:
            lookup_view_s = lookup_view

        patterns = [pattern for (_, pattern, _, _) in possibilities]
        if patterns:
            if args:
                arg_msg = "arguments '%s'" % (args, )
            elif kwargs:
                arg_msg = "keyword arguments '%s'" % kwargs
            else:
                arg_msg = "no arguments"
            msg = (
                "Reverse for '%s' with %s not found. %d pattern(s) tried: %s" %
                (lookup_view_s, arg_msg, len(patterns), patterns))
        else:
            msg = ("Reverse for '%(view)s' not found. '%(view)s' is not "
                   "a valid view function or pattern name." % {
                       'view': lookup_view_s
                   })
        raise NoReverseMatch(msg)
Beispiel #13
0
    def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs):
        if args and kwargs:
            raise ValueError("Don't mix *args and **kwargs in call to reverse()!")

        if not self._populated:
            self._populate()

        possibilities = self.reverse_dict.getlist(lookup_view)

        for possibility, pattern, defaults, converters in possibilities:
            for result, params in possibility:
                if args:
                    if len(args) != len(params):
                        continue
                    candidate_subs = dict(zip(params, args))
                else:
                    if set(kwargs).symmetric_difference(params).difference(defaults):
                        continue
                    if any(kwargs.get(k, v) != v for k, v in defaults.items()):
                        continue
                    candidate_subs = kwargs
                # Convert the candidate subs to text using Converter.to_url().
                text_candidate_subs = {}
                for k, v in candidate_subs.items():
                    if k in converters:
                        text_candidate_subs[k] = converters[k].to_url(v)
                    else:
                        text_candidate_subs[k] = str(v)
                # WSGI provides decoded URLs, without %xx escapes, and the URL
                # resolver operates on such URLs. First substitute arguments
                # without quoting to build a decoded URL and look for a match.
                # Then, if we have a match, redo the substitution with quoted
                # arguments in order to return a properly encoded URL.
                candidate_pat = _prefix.replace('%', '%%') + result
                if re.search('^%s%s' % (re.escape(_prefix), pattern), candidate_pat % text_candidate_subs):
                    # safe characters from `pchar` definition of RFC 3986
                    url = quote(candidate_pat % text_candidate_subs, safe=RFC3986_SUBDELIMS + '/~:@')
                    # Don't allow construction of scheme relative urls.
                    return escape_leading_slashes(url)
        # lookup_view can be URL name or callable, but callables are not
        # friendly in error messages.
        m = getattr(lookup_view, '__module__', None)
        n = getattr(lookup_view, '__name__', None)
        if m is not None and n is not None:
            lookup_view_s = "%s.%s" % (m, n)
        else:
            lookup_view_s = lookup_view

        patterns = [pattern for (_, pattern, _, _) in possibilities]
        if patterns:
            if args:
                arg_msg = "arguments '%s'" % (args,)
            elif kwargs:
                arg_msg = "keyword arguments '%s'" % (kwargs,)
            else:
                arg_msg = "no arguments"
            msg = (
                "Reverse for '%s' with %s not found. %d pattern(s) tried: %s" %
                (lookup_view_s, arg_msg, len(patterns), patterns)
            )
        else:
            msg = (
                "Reverse for '%(view)s' not found. '%(view)s' is not "
                "a valid view function or pattern name." % {'view': lookup_view_s}
            )
        raise NoReverseMatch(msg)
Beispiel #14
0
 def get_full_path_with_slash(self, request):
     new_path = request.get_full_path(force_append_slash=True)
     return escape_leading_slashes(new_path)
Beispiel #15
0
def test_escape_leading_slashes(inp):
    escape_leading_slashes(inp)