|
2 | 2 | from urllib.parse import urlsplit, urlunsplit |
3 | 3 |
|
4 | 4 | from django.core.exceptions import ValidationError |
5 | | -from django.core.validators import RegexValidator |
| 5 | +from django.core.validators import URLValidator |
6 | 6 | from django.utils.encoding import force_text |
7 | 7 | from django.utils.translation import ugettext_lazy as _ |
8 | 8 |
|
9 | 9 | from .settings import oauth2_settings |
10 | 10 |
|
11 | 11 |
|
12 | | -class URIValidator(RegexValidator): |
13 | | - regex = re.compile( |
14 | | - r"^(?:[a-z][a-z0-9\.\-\+]*)://" # scheme... |
15 | | - r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain... |
16 | | - r"(?!-)[A-Z\d-]{1,63}(?<!-)|" # also cover non-dotted domain |
17 | | - r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|" # ...or ipv4 |
18 | | - r"\[?[A-F0-9]*:[A-F0-9:]+\]?)" # ...or ipv6 |
19 | | - r"(?::\d+)?" # optional port |
20 | | - r"(?:/?|[/?]\S+)$", re.IGNORECASE) |
21 | | - message = _("Enter a valid URL.") |
| 12 | +class URIValidator(URLValidator): |
| 13 | + scheme_re = r"^(?:[a-z][a-z0-9\.\-\+]*)://" |
22 | 14 |
|
23 | | - def __call__(self, value): |
24 | | - try: |
25 | | - super().__call__(value) |
26 | | - except ValidationError as e: |
27 | | - # Trivial case failed. Try for possible IDN domain |
28 | | - if value: |
29 | | - value = force_text(value) |
30 | | - try: |
31 | | - scheme, netloc, path, query, fragment = urlsplit(value) |
32 | | - except ValueError as e: |
33 | | - raise ValidationError("Cannot parse Redirect URI. Error: {}".format(e)) |
34 | | - try: |
35 | | - netloc = netloc.encode("idna").decode("ascii") # IDN -> ACE |
36 | | - except UnicodeError: # invalid domain part |
37 | | - raise e |
38 | | - url = urlunsplit((scheme, netloc, path, query, fragment)) |
39 | | - super().__call__(url) |
40 | | - else: |
41 | | - raise |
42 | | - else: |
43 | | - url = value |
| 15 | + dotless_domain_re = r"(?!-)[A-Z\d-]{1,63}(?<!-)" |
| 16 | + host_re = "|".join(( |
| 17 | + r"(?:"+ URLValidator.host_re, |
| 18 | + URLValidator.ipv4_re, |
| 19 | + URLValidator.ipv6_re, |
| 20 | + dotless_domain_re + ")" |
| 21 | + )) |
| 22 | + port_re = r"(?::\d{2,5})?" |
| 23 | + path_re = r"(?:[/?#][^\s]*)?" |
| 24 | + regex = re.compile(scheme_re + host_re + port_re + path_re, re.IGNORECASE) |
44 | 25 |
|
45 | 26 |
|
46 | 27 | class RedirectURIValidator(URIValidator): |
47 | 28 | def __init__(self, allowed_schemes): |
48 | | - self.allowed_schemes = allowed_schemes |
| 29 | + super().__init__(schemes=allowed_schemes) |
49 | 30 |
|
50 | 31 | def __call__(self, value): |
51 | 32 | super().__call__(value) |
52 | 33 | value = force_text(value) |
53 | 34 | if len(value.split("#")) > 1: |
54 | 35 | raise ValidationError("Redirect URIs must not contain fragments") |
55 | 36 | scheme, netloc, path, query, fragment = urlsplit(value) |
56 | | - if scheme.lower() not in self.allowed_schemes: |
| 37 | + if scheme.lower() not in self.schemes: |
57 | 38 | raise ValidationError("Redirect URI scheme is not allowed.") |
58 | 39 |
|
59 | 40 |
|
|
0 commit comments