From 324be051514408570a091690550b5c21734baae1 Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Thu, 21 May 2020 21:27:36 +0000 Subject: [PATCH] added an XMPP URL validator - Legacy-Id: 17872 --- ietf/utils/validators.py | 80 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/ietf/utils/validators.py b/ietf/utils/validators.py index f4ca766de..f14d7ca32 100644 --- a/ietf/utils/validators.py +++ b/ietf/utils/validators.py @@ -5,14 +5,15 @@ import os import re from pyquery import PyQuery -from urllib.parse import urlparse +from urllib.parse import urlparse, urlsplit from django.conf import settings from django.core.exceptions import ValidationError -from django.core.validators import RegexValidator, URLValidator, EmailValidator +from django.core.validators import RegexValidator, URLValidator, EmailValidator, _lazy_re_compile from django.template.defaultfilters import filesizeformat from django.utils.deconstruct import deconstructible +from django.utils.translation import gettext_lazy as _ import debug # pyflakes:ignore @@ -92,6 +93,75 @@ validate_url = URLValidator() validate_http_url = URLValidator(schemes=['http','https']) validate_email = EmailValidator() +@deconstructible +class XMPPURLValidator(RegexValidator): + ul = '\u00a1-\uffff' # unicode letters range (must not be a raw string) + + # IP patterns + ipv4_re = r'(?:25[0-5]|2[0-4]\d|[0-1]?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}' + ipv6_re = r'\[[0-9a-f:\.]+\]' # (simple regex, validated later) + + # Host patterns + hostname_re = r'[a-z' + ul + r'0-9](?:[a-z' + ul + r'0-9-]{0,61}[a-z' + ul + r'0-9])?' + # Max length for domain name labels is 63 characters per RFC 1034 sec. 3.1 + domain_re = r'(?:\.(?!-)[a-z' + ul + r'0-9-]{1,63}(? ACE + except UnicodeError: # invalid domain part + raise e + url = urlunsplit((scheme, netloc, path, query, fragment)) + super().__call__(url) + else: + raise + else: + # Now verify IPv6 in the netloc part + host_match = re.search(r'^\[(.+)\](?::\d{2,5})?$', urlsplit(value).netloc) + if host_match: + potential_ip = host_match.groups()[0] + try: + validate_ipv6_address(potential_ip) + except ValidationError: + raise ValidationError(self.message, code=self.code) + + # The maximum length of a full host name is 253 characters per RFC 1034 + # section 3.1. It's defined to be 255 bytes or less, but this includes + # one byte for the length of the name and one byte for the trailing dot + # that's used to indicate absolute names in DNS. + if len(urlsplit(value).netloc) > 253: + raise ValidationError(self.message, code=self.code) + +validate_xmpp = XMPPURLValidator() + def validate_external_resource_value(name, value): """ validate a resource value using its name's properties """ @@ -102,11 +172,7 @@ def validate_external_resource_value(name, value): if urlparse(value).netloc.lower() != 'github.com': raise ValidationError('URL must be a github url') elif name.slug == 'jabber_room': - pass - # TODO - build a xmpp URL validator. See XEP-0032. - # It should be easy to build one by copyhacking URLValidator, - # but reading source says it would be better to wait to do that - # until after we make the Django 2 transition + validate_xmpp(value) else: validate_url(value)