django-orchestra-test/orchestra/core/validators.py

180 lines
4.8 KiB
Python

import logging
import re
from ipaddress import ip_address
import phonenumbers
from django.core import validators
from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible
from django.utils.translation import gettext_lazy as _
from ..utils.python import import_class
logger = logging.getLogger(__name__)
def all_valid(*args):
""" helper function to merge multiple validators at once """
if len(args) == 1:
# Dict
errors = {}
kwargs = args[0]
for field, validator in kwargs.items():
try:
validator[0](*validator[1:])
except ValidationError as error:
errors[field] = error
else:
# List
errors = []
value, validators = args
for validator in validators:
try:
validator(value)
except ValidationError as error:
errors.append(error)
if errors:
raise ValidationError(errors)
@deconstructible
class OrValidator(object):
"""
Run validators with an OR logic
"""
def __init__(self, *validators):
self.validators = validators
def __call__(self, value):
msg = []
for validator in self.validators:
try:
validator(value)
except ValidationError as err:
msg.append(str(err))
else:
return
raise ValidationError(' OR '.join(msg))
def validate_ipv4_address(value):
msg = _("Not a valid IPv4 address")
try:
ip = ip_address(value)
except ValueError:
raise ValidationError(msg)
if ip.version != 4:
raise ValidationError(msg)
def validate_ipv6_address(value):
msg = _("Not a valid IPv6 address")
try:
ip = ip_address(value)
except ValueError:
raise ValidationError(msg)
if ip.version != 6:
raise ValidationError(msg)
def validate_ip_address(value):
msg = _("Not a valid IP address")
try:
ip_address(value)
except ValueError:
raise ValidationError(msg)
def validate_name(value):
"""
A single non-empty line of free-form text with no whitespace.
"""
validators.RegexValidator('^[\.\_\-0-9a-z]+$',
_("Enter a valid name (spaceless lowercase text including _.-)."), 'invalid')(value)
def validate_ascii(value):
try:
value.encode('ascii')
except UnicodeEncodeError:
raise ValidationError('This is not an ASCII string.')
def validate_hostname(hostname):
"""
Ensures that each segment
* contains at least one character and a maximum of 63 characters
* consists only of allowed characters
* doesn't begin or end with a hyphen.
http://stackoverflow.com/a/2532344
"""
if len(hostname) > 255:
raise ValidationError(_("Too long for a hostname."))
hostname = hostname.rstrip('.')
allowed = re.compile('(?!-)[A-Z\d-]{1,63}(?<!-)$', re.IGNORECASE)
for name in hostname.split('.'):
if not allowed.match(name):
raise ValidationError(_("Not a valid hostname (%s).") % name)
def validate_username(value):
validators.RegexValidator(r'^[\w.-]+$', _("Enter a valid username."))(value)
def validate_password(value):
try:
import crack
except:
try:
import cracklib as crack
except:
logger.error("Can not validate password. Cracklib bindings are not installed.")
return
try:
crack.VeryFascistCheck(value)
except ValueError as message:
raise ValidationError("Password %s." % str(message)[3:])
def validate_url_path(value):
if not re.match(r'^\/[/.a-zA-Z0-9-_]*$', value):
raise ValidationError(_('"%s" is not a valid URL-path.') % value)
def validate_vat(vat, country):
field = 'localflavor.{lower}.forms.{upper}IdentityCardNumberField'.format(
lower=country.lower(),
upper=country.upper()
)
try:
field = import_class(field)
except (ImportError, AttributeError, ValueError):
pass
else:
field().clean(vat)
def validate_zipcode(zipcode, country):
field = 'localflavor.{lower}.forms.{upper}PostalCodeField'.format(
lower=country.lower(),
upper=country.upper()
)
try:
field = import_class(field)
except (ImportError, AttributeError, ValueError):
pass
else:
field().clean(zipcode)
def validate_phone(value, country):
""" local phone number or international """
msg = _("Not a valid %s nor international phone number.") % country
try:
number = phonenumbers.parse(value, country)
except phonenumbers.phonenumberutil.NumberParseException:
raise ValidationError(msg)
if not phonenumbers.is_valid_number(number):
raise ValidationError(msg)