Skip to content

Instantly share code, notes, and snippets.

@d3cline
Created May 18, 2019 02:51
Show Gist options
  • Save d3cline/ca406755a08ae3caaf4166c435c02071 to your computer and use it in GitHub Desktop.
Save d3cline/ca406755a08ae3caaf4166c435c02071 to your computer and use it in GitHub Desktop.
Django DomainNameValidator adapted from EmailValidator
from django.core.exceptions import ValidationError
from django.core.validators import validate_ipv46_address
from django.utils.deconstruct import deconstructible
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
import re
@deconstructible
class DomainNameValidator(object):
"""
Domain name validator adapted from Django's EmailValidator.
"""
message = _('Enter a valid domain name.')
code = 'invalid'
domain_regex = re.compile(
# max length of the domain is 249: 254 (max email length) minus one
# period, two characters for the TLD, @ sign, & one character before @.
r'(?:[A-Z0-9](?:[A-Z0-9-]{0,247}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))$',
re.IGNORECASE)
literal_regex = re.compile(
# literal form, ipv4 or ipv6 address (SMTP 4.1.3)
r'\[([A-f0-9:\.]+)\]$',
re.IGNORECASE)
domain_whitelist = ['localhost']
def __init__(self, message=None, code=None, whitelist=None):
if message is not None:
self.message = message
if code is not None:
self.code = code
if whitelist is not None:
self.domain_whitelist = whitelist
def __call__(self, value):
value = force_text(value)
if not value:
raise ValidationError(self.message, code=self.code)
if value not in self.domain_whitelist and not self.validate_domain_part(value):
# Try for possible IDN domain-part
try:
domain_part = value.encode('idna').decode('ascii')
if self.validate_domain_part(domain_part):
return
except UnicodeError:
pass
raise ValidationError(self.message, code=self.code)
def validate_domain_part(self, domain_part):
if self.domain_regex.match(domain_part):
return True
literal_match = self.literal_regex.match(domain_part)
if literal_match:
ip_address = literal_match.group(1)
try:
validate_ipv46_address(ip_address)
return True
except ValidationError:
pass
return False
def __eq__(self, other):
return isinstance(other, DomainNameValidator) and (self.domain_whitelist == other.domain_whitelist) and (self.message == other.message) and (self.code == other.code) # noqa
validate_domain_name = DomainNameValidator()
def is_valid_domain_name(value):
try:
validate_domain_name(value)
return True
except ValidationError:
pass
return False
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment