Browse Source

Closes #1460: Hostnames with no domain are now acceptable in custom URL fields

Jeremy Stretch 7 years ago
parent
commit
e0ee0b9254
2 changed files with 36 additions and 11 deletions
  1. 6 11
      netbox/utilities/forms.py
  2. 30 0
      netbox/utilities/validators.py

+ 6 - 11
netbox/utilities/forms.py

@@ -7,9 +7,10 @@ from mptt.forms import TreeNodeMultipleChoiceField
 
 
 from django import forms
 from django import forms
 from django.conf import settings
 from django.conf import settings
-from django.core.validators import URLValidator
 from django.urls import reverse_lazy
 from django.urls import reverse_lazy
 
 
+from .validators import EnhancedURLValidator
+
 
 
 COLOR_CHOICES = (
 COLOR_CHOICES = (
     ('aa1409', 'Dark red'),
     ('aa1409', 'Dark red'),
@@ -431,17 +432,11 @@ class FilterTreeNodeMultipleChoiceField(FilterChoiceFieldMixin, TreeNodeMultiple
 
 
 class LaxURLField(forms.URLField):
 class LaxURLField(forms.URLField):
     """
     """
-    Custom URLField which allows any valid URL scheme
+    Modifies Django's built-in URLField in two ways:
+      1) Allow any valid scheme per RFC 3986 section 3.1
+      2) Remove the requirement for fully-qualified domain names (e.g. http://myserver/ is valid)
     """
     """
-
-    class AnyURLScheme(object):
-        # A fake URL list which "contains" all scheme names abiding by the syntax defined in RFC 3986 section 3.1
-        def __contains__(self, item):
-            if not item or not re.match('^[a-z][0-9a-z+\-.]*$', item.lower()):
-                return False
-            return True
-
-    default_validators = [URLValidator(schemes=AnyURLScheme())]
+    default_validators = [EnhancedURLValidator()]
 
 
 
 
 #
 #

+ 30 - 0
netbox/utilities/validators.py

@@ -0,0 +1,30 @@
+from __future__ import unicode_literals
+import re
+
+from django.core.validators import _lazy_re_compile, URLValidator
+
+
+class EnhancedURLValidator(URLValidator):
+    """
+    Extends Django's built-in URLValidator to permit the use of hostnames with no domain extension.
+    """
+
+    class AnyURLScheme(object):
+        """
+        A fake URL list which "contains" all scheme names abiding by the syntax defined in RFC 3986 section 3.1
+        """
+        def __contains__(self, item):
+            if not item or not re.match('^[a-z][0-9a-z+\-.]*$', item.lower()):
+                return False
+            return True
+
+    fqdn_re = URLValidator.hostname_re + URLValidator.domain_re + URLValidator.tld_re
+    host_res = [URLValidator.ipv4_re, URLValidator.ipv6_re, fqdn_re, URLValidator.hostname_re]
+    regex = _lazy_re_compile(
+        r'^(?:[a-z0-9\.\-\+]*)://'          # Scheme (previously enforced by AnyURLScheme or schemes kwarg)
+        r'(?:\S+(?::\S*)?@)?'               # HTTP basic authentication
+        r'(?:' + '|'.join(host_res) + ')'   # IPv4, IPv6, FQDN, or hostname
+        r'(?::\d{2,5})?'                    # Port number
+        r'(?:[/?#][^\s]*)?'                 # Path
+        r'\Z', re.IGNORECASE)
+    schemes = AnyURLScheme()