def __init__(self, level="number", message=None, code=None): self.level = level if message is not None: self.message = message if code is not None: self.code = code if self.level == "number": self.password_regex = lazy_re_compile(r"^\d{6}$", flags=re.IGNORECASE) elif self.level == "normal": self.password_regex = lazy_re_compile( r"^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,18}$", flags=re.IGNORECASE) elif self.level == "high": re_str = r"^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[\(\)])+$)" \ r"([^(0-9a-zA-Z)]|[\(\)]|[a-z]|[A-Z]|[0-9]){6,18}$" self.password_regex = lazy_re_compile(re_str, flags=re.IGNORECASE)
def __init__(self, regex=None, message=None, code=None, inverse_match=None, flags=None): if regex is not None: self.regex = regex if message is not None: self.message = message if code is not None: self.code = code if inverse_match is not None: self.inverse_match = inverse_match if flags is not None: self.flags = flags if self.flags and not isinstance(self.regex, str): raise TypeError( "If the flags are set, regex must be a regular expression string." ) self.regex = lazy_re_compile(self.regex, self.flags)
class PhoneValidator(object): """ 手机号码检查 移动号段: 134 135 136 137 138 139 147 148 150 151 152 157 158 159 172 178 182 183 184 187 188 198 联通号段: 130 131 132 145 146 155 156 166 171 175 176 185 186 电信号段: 133 149 153 173 174 177 180 181 189 199 虚拟运营商: 170 2017-08-08:工信部新批号段:电信199/移动198/联通166 ,146联通,148移动 精准的匹配:^(13[0-9]|14[5-9]|15[0-9]|16[6]|17[0-8]|18[0-9]|19[8-9])\d{8}$ 粗准匹配:^1(3|4|5|7|8|9)[0-9]{9}$ """ message = _("Enter a valid phone number") code = 'invalid' phone_regex = lazy_re_compile( r"^(13[0-9]|14[5-9]|15[0-9]|16[6]|17[0-8]|18[0-9]|19[8-9])\d{8}$", flags=re.IGNORECASE) def __init__(self, message=None, code=None): if message is not None: self.message = message if code is not None: self.code = code def __call__(self, value): value = force_text(value) if not value or not value.isdigit(): raise ValidationError(self.message, code=self.code) if not self.phone_regex.match(value): raise ValidationError(self.message, code=self.code)
class URLValidator(RegexValidator): ul = '\u00a1-\uffff' # unicode letters range (must be a unicode string, not 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}(?<!-))*' tld_re = ( r'\.' # dot r'(?!-)' # can't start with a dash r'(?:[a-z' + ul + '-]{2,63}' # domain label r'|xn--[a-z0-9]{1,59})' # or punycode label r'(?<!-)' # can't end with a dash r'\.?' # may have a trailing dot ) host_re = '(' + hostname_re + domain_re + tld_re + '|localhost)' regex = lazy_re_compile( r'^(?:[a-z0-9\.\-\+]*)://' # scheme is validated separately r'(?:\S+(?::\S*)?@)?' # user:pass authentication r'(?:' + ipv4_re + '|' + ipv6_re + '|' + host_re + ')' r'(?::\d{2,5})?' # port r'(?:[/?#][^\s]*)?' # resource path r'\Z', re.IGNORECASE) message = _('Enter a valid URL') schemes = ['http', 'https', 'ftp', 'ftps'] def __init__(self, schemes=None, **kwargs): super(URLValidator, self).__init__(**kwargs) if schemes is not None: self.schemes = schemes def __call__(self, value): value = force_text(value) # Check first if the scheme is valid scheme = value.split('://')[0].lower() if scheme not in self.schemes: raise ValidationError(self.message, code=self.code) # Then check full URL try: super(URLValidator, self).__call__(value) except ValidationError as e: # Trivial case failed. Try for possible IDN domain if value: try: scheme, netloc, path, query, fragment = urlsplit(value) except ValueError: # for example, "Invalid IPv6 URL" raise ValidationError(self.message, code=self.code) try: netloc = netloc.encode('idna').decode( 'ascii') # IDN -> ACE except UnicodeError: # invalid domain part raise e url = urlunsplit((scheme, netloc, path, query, fragment)) super(URLValidator, self).__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] if not IPAddressValidator.check_ipv6(potential_ip): raise ValidationError(self.message, code=self.code) # url = value # 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)
class EmailValidator(object): message = _('Enter a valid email address') code = 'invalid' user_regex = lazy_re_compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string re.IGNORECASE) domain_regex = lazy_re_compile( # max length for domain name labels is 63 characters per RFC 1034 r'((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)(?:[A-Z0-9-]{2,63}(?<!-))\Z', re.IGNORECASE) literal_regex = lazy_re_compile( # literal form, ipv4 or ipv6 address (SMTP 4.1.3) r'\[([A-f0-9:\.]+)\]\Z', 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 or '@' not in value: raise ValidationError(self.message, code=self.code) user_part, domain_part = value.rsplit('@', 1) if not self.user_regex.match(user_part): raise ValidationError(self.message, code=self.code) if (domain_part not in self.domain_whitelist and not self.validate_domain_part(domain_part)): # Try for possible IDN domain-part try: domain_part = domain_part.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) return IPAddressValidator.check_ipv4( ip_address) or IPAddressValidator.check_ipv6(ip_address) return False def __eq__(self, other): return (isinstance(other, EmailValidator) and (self.domain_whitelist == other.domain_whitelist) and (self.message == other.message) and (self.code == other.code))
potential_ip = host_match.groups()[0] if not IPAddressValidator.check_ipv6(potential_ip): raise ValidationError(self.message, code=self.code) # url = value # 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) integer_validator = RegexValidator( lazy_re_compile(r'^-?\d+\Z'), message=_('Enter a valid integer'), code='invalid', ) def validate_integer(value): return integer_validator(value) class EmailValidator(object): message = _('Enter a valid email address') code = 'invalid' user_regex = lazy_re_compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string