git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/validate/validator.go (about) 1 // Package govalidator is package of validators and sanitizers for strings, structs and collections. 2 package validate 3 4 import ( 5 "bytes" 6 "crypto/rsa" 7 "crypto/x509" 8 "encoding/base64" 9 "encoding/json" 10 "encoding/pem" 11 "fmt" 12 "io" 13 "net" 14 "net/url" 15 "reflect" 16 "regexp" 17 "sort" 18 "strconv" 19 "strings" 20 "time" 21 "unicode" 22 "unicode/utf8" 23 ) 24 25 var ( 26 fieldsRequiredByDefault bool 27 nilPtrAllowedByRequired = false 28 notNumberRegexp = regexp.MustCompile("[^0-9]+") 29 whiteSpacesAndMinus = regexp.MustCompile(`[\s-]+`) 30 paramsRegexp = regexp.MustCompile(`\(.*\)$`) 31 ) 32 33 const maxURLRuneCount = 2083 34 const minURLRuneCount = 3 35 const rfc3339WithoutZone = "2006-01-02T15:04:05" 36 37 // SetFieldsRequiredByDefault causes validation to fail when struct fields 38 // do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). 39 // This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): 40 // 41 // type exampleStruct struct { 42 // Name string `` 43 // Email string `valid:"email"` 44 // 45 // This, however, will only fail when Email is empty or an invalid email address: 46 // 47 // type exampleStruct2 struct { 48 // Name string `valid:"-"` 49 // Email string `valid:"email"` 50 // 51 // Lastly, this will only fail when Email is an invalid email address but not when it's empty: 52 // 53 // type exampleStruct2 struct { 54 // Name string `valid:"-"` 55 // Email string `valid:"email,optional"` 56 func SetFieldsRequiredByDefault(value bool) { 57 fieldsRequiredByDefault = value 58 } 59 60 // SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required. 61 // The validation will still reject ptr fields in their zero value state. Example with this enabled: 62 // 63 // type exampleStruct struct { 64 // Name *string `valid:"required"` 65 // 66 // With `Name` set to "", this will be considered invalid input and will cause a validation error. 67 // With `Name` set to nil, this will be considered valid by validation. 68 // By default this is disabled. 69 func SetNilPtrAllowedByRequired(value bool) { 70 nilPtrAllowedByRequired = value 71 } 72 73 // IsEmail checks if the string is an email. 74 func IsEmail(str string) bool { 75 // TODO uppercase letters are not supported 76 return rxEmail.MatchString(str) 77 } 78 79 // IsExistingEmail checks if the string is an email of existing domain 80 func IsExistingEmail(email string) bool { 81 82 if len(email) < 6 || len(email) > 254 { 83 return false 84 } 85 at := strings.LastIndex(email, "@") 86 if at <= 0 || at > len(email)-3 { 87 return false 88 } 89 user := email[:at] 90 host := email[at+1:] 91 if len(user) > 64 { 92 return false 93 } 94 switch host { 95 case "localhost", "example.com": 96 return true 97 } 98 if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) { 99 return false 100 } 101 if _, err := net.LookupMX(host); err != nil { 102 if _, err := net.LookupIP(host); err != nil { 103 return false 104 } 105 } 106 107 return true 108 } 109 110 // IsURL checks if the string is an URL. 111 func IsURL(str string) bool { 112 if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") { 113 return false 114 } 115 strTemp := str 116 if strings.Contains(str, ":") && !strings.Contains(str, "://") { 117 // support no indicated urlscheme but with colon for port number 118 // http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString 119 strTemp = "http://" + str 120 } 121 u, err := url.Parse(strTemp) 122 if err != nil { 123 return false 124 } 125 if strings.HasPrefix(u.Host, ".") { 126 return false 127 } 128 if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { 129 return false 130 } 131 return rxURL.MatchString(str) 132 } 133 134 // IsRequestURL checks if the string rawurl, assuming 135 // it was received in an HTTP request, is a valid 136 // URL confirm to RFC 3986 137 func IsRequestURL(rawurl string) bool { 138 url, err := url.ParseRequestURI(rawurl) 139 if err != nil { 140 return false //Couldn't even parse the rawurl 141 } 142 if len(url.Scheme) == 0 { 143 return false //No Scheme found 144 } 145 return true 146 } 147 148 // IsRequestURI checks if the string rawurl, assuming 149 // it was received in an HTTP request, is an 150 // absolute URI or an absolute path. 151 func IsRequestURI(rawurl string) bool { 152 _, err := url.ParseRequestURI(rawurl) 153 return err == nil 154 } 155 156 // IsAlpha checks if the string contains only letters (a-zA-Z). Empty string is valid. 157 func IsAlpha(str string) bool { 158 if IsNull(str) { 159 return true 160 } 161 return rxAlpha.MatchString(str) 162 } 163 164 // IsUTFLetter checks if the string contains only unicode letter characters. 165 // Similar to IsAlpha but for all languages. Empty string is valid. 166 func IsUTFLetter(str string) bool { 167 if IsNull(str) { 168 return true 169 } 170 171 for _, c := range str { 172 if !unicode.IsLetter(c) { 173 return false 174 } 175 } 176 return true 177 178 } 179 180 // IsAlphanumeric checks if the string contains only letters and numbers. Empty string is valid. 181 func IsAlphanumeric(str string) bool { 182 if IsNull(str) { 183 return true 184 } 185 return rxAlphanumeric.MatchString(str) 186 } 187 188 // IsUTFLetterNumeric checks if the string contains only unicode letters and numbers. Empty string is valid. 189 func IsUTFLetterNumeric(str string) bool { 190 if IsNull(str) { 191 return true 192 } 193 for _, c := range str { 194 if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok 195 return false 196 } 197 } 198 return true 199 200 } 201 202 // IsNumeric checks if the string contains only numbers. Empty string is valid. 203 func IsNumeric(str string) bool { 204 if IsNull(str) { 205 return true 206 } 207 return rxNumeric.MatchString(str) 208 } 209 210 // IsUTFNumeric checks if the string contains only unicode numbers of any kind. 211 // Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid. 212 func IsUTFNumeric(str string) bool { 213 if IsNull(str) { 214 return true 215 } 216 if strings.IndexAny(str, "+-") > 0 { 217 return false 218 } 219 if len(str) > 1 { 220 str = strings.TrimPrefix(str, "-") 221 str = strings.TrimPrefix(str, "+") 222 } 223 for _, c := range str { 224 if !unicode.IsNumber(c) { //numbers && minus sign are ok 225 return false 226 } 227 } 228 return true 229 230 } 231 232 // IsUTFDigit checks if the string contains only unicode radix-10 decimal digits. Empty string is valid. 233 func IsUTFDigit(str string) bool { 234 if IsNull(str) { 235 return true 236 } 237 if strings.IndexAny(str, "+-") > 0 { 238 return false 239 } 240 if len(str) > 1 { 241 str = strings.TrimPrefix(str, "-") 242 str = strings.TrimPrefix(str, "+") 243 } 244 for _, c := range str { 245 if !unicode.IsDigit(c) { //digits && minus sign are ok 246 return false 247 } 248 } 249 return true 250 251 } 252 253 // IsHexadecimal checks if the string is a hexadecimal number. 254 func IsHexadecimal(str string) bool { 255 return rxHexadecimal.MatchString(str) 256 } 257 258 // IsHexcolor checks if the string is a hexadecimal color. 259 func IsHexcolor(str string) bool { 260 return rxHexcolor.MatchString(str) 261 } 262 263 // IsRGBcolor checks if the string is a valid RGB color in form rgb(RRR, GGG, BBB). 264 func IsRGBcolor(str string) bool { 265 return rxRGBcolor.MatchString(str) 266 } 267 268 // IsLowerCase checks if the string is lowercase. Empty string is valid. 269 func IsLowerCase(str string) bool { 270 if IsNull(str) { 271 return true 272 } 273 return str == strings.ToLower(str) 274 } 275 276 // IsUpperCase checks if the string is uppercase. Empty string is valid. 277 func IsUpperCase(str string) bool { 278 if IsNull(str) { 279 return true 280 } 281 return str == strings.ToUpper(str) 282 } 283 284 // HasLowerCase checks if the string contains at least 1 lowercase. Empty string is valid. 285 func HasLowerCase(str string) bool { 286 if IsNull(str) { 287 return true 288 } 289 return rxHasLowerCase.MatchString(str) 290 } 291 292 // HasUpperCase checks if the string contains as least 1 uppercase. Empty string is valid. 293 func HasUpperCase(str string) bool { 294 if IsNull(str) { 295 return true 296 } 297 return rxHasUpperCase.MatchString(str) 298 } 299 300 // IsInt checks if the string is an integer. Empty string is valid. 301 func IsInt(str string) bool { 302 if IsNull(str) { 303 return true 304 } 305 return rxInt.MatchString(str) 306 } 307 308 // IsFloat checks if the string is a float. 309 func IsFloat(str string) bool { 310 return str != "" && rxFloat.MatchString(str) 311 } 312 313 // IsDivisibleBy checks if the string is a number that's divisible by another. 314 // If second argument is not valid integer or zero, it's return false. 315 // Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero). 316 func IsDivisibleBy(str, num string) bool { 317 f, _ := ToFloat(str) 318 p := int64(f) 319 q, _ := ToInt(num) 320 if q == 0 { 321 return false 322 } 323 return (p == 0) || (p%q == 0) 324 } 325 326 // IsNull checks if the string is null. 327 func IsNull(str string) bool { 328 return len(str) == 0 329 } 330 331 // IsNotNull checks if the string is not null. 332 func IsNotNull(str string) bool { 333 return !IsNull(str) 334 } 335 336 // HasWhitespaceOnly checks the string only contains whitespace 337 func HasWhitespaceOnly(str string) bool { 338 return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str) 339 } 340 341 // HasWhitespace checks if the string contains any whitespace 342 func HasWhitespace(str string) bool { 343 return len(str) > 0 && rxHasWhitespace.MatchString(str) 344 } 345 346 // IsByteLength checks if the string's length (in bytes) falls in a range. 347 func IsByteLength(str string, min, max int) bool { 348 return len(str) >= min && len(str) <= max 349 } 350 351 // IsUUIDv3 checks if the string is a UUID version 3. 352 func IsUUIDv3(str string) bool { 353 return rxUUID3.MatchString(str) 354 } 355 356 // IsUUIDv4 checks if the string is a UUID version 4. 357 func IsUUIDv4(str string) bool { 358 return rxUUID4.MatchString(str) 359 } 360 361 // IsUUIDv5 checks if the string is a UUID version 5. 362 func IsUUIDv5(str string) bool { 363 return rxUUID5.MatchString(str) 364 } 365 366 // IsUUID checks if the string is a UUID (version 3, 4 or 5). 367 func IsUUID(str string) bool { 368 return rxUUID.MatchString(str) 369 } 370 371 // Byte to index table for O(1) lookups when unmarshaling. 372 // We use 0xFF as sentinel value for invalid indexes. 373 var ulidDec = [...]byte{ 374 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 375 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 376 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 377 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 378 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 379 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 380 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 381 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, 382 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, 383 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 384 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 385 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 386 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 387 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 388 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 389 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 390 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 391 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 392 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 393 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 394 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 395 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 396 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 397 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 398 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 399 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 400 } 401 402 // EncodedSize is the length of a text encoded ULID. 403 const ulidEncodedSize = 26 404 405 // IsULID checks if the string is a ULID. 406 // 407 // Implementation got from: 408 // 409 // https://github.com/oklog/ulid (Apache-2.0 License) 410 func IsULID(str string) bool { 411 // Check if a base32 encoded ULID is the right length. 412 if len(str) != ulidEncodedSize { 413 return false 414 } 415 416 // Check if all the characters in a base32 encoded ULID are part of the 417 // expected base32 character set. 418 if ulidDec[str[0]] == 0xFF || 419 ulidDec[str[1]] == 0xFF || 420 ulidDec[str[2]] == 0xFF || 421 ulidDec[str[3]] == 0xFF || 422 ulidDec[str[4]] == 0xFF || 423 ulidDec[str[5]] == 0xFF || 424 ulidDec[str[6]] == 0xFF || 425 ulidDec[str[7]] == 0xFF || 426 ulidDec[str[8]] == 0xFF || 427 ulidDec[str[9]] == 0xFF || 428 ulidDec[str[10]] == 0xFF || 429 ulidDec[str[11]] == 0xFF || 430 ulidDec[str[12]] == 0xFF || 431 ulidDec[str[13]] == 0xFF || 432 ulidDec[str[14]] == 0xFF || 433 ulidDec[str[15]] == 0xFF || 434 ulidDec[str[16]] == 0xFF || 435 ulidDec[str[17]] == 0xFF || 436 ulidDec[str[18]] == 0xFF || 437 ulidDec[str[19]] == 0xFF || 438 ulidDec[str[20]] == 0xFF || 439 ulidDec[str[21]] == 0xFF || 440 ulidDec[str[22]] == 0xFF || 441 ulidDec[str[23]] == 0xFF || 442 ulidDec[str[24]] == 0xFF || 443 ulidDec[str[25]] == 0xFF { 444 return false 445 } 446 447 // Check if the first character in a base32 encoded ULID will overflow. This 448 // happens because the base32 representation encodes 130 bits, while the 449 // ULID is only 128 bits. 450 // 451 // See https://github.com/oklog/ulid/issues/9 for details. 452 if str[0] > '7' { 453 return false 454 } 455 return true 456 } 457 458 // IsCreditCard checks if the string is a credit card. 459 func IsCreditCard(str string) bool { 460 sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") 461 if !rxCreditCard.MatchString(sanitized) { 462 return false 463 } 464 var sum int64 465 var digit string 466 var tmpNum int64 467 var shouldDouble bool 468 for i := len(sanitized) - 1; i >= 0; i-- { 469 digit = sanitized[i:(i + 1)] 470 tmpNum, _ = ToInt(digit) 471 if shouldDouble { 472 tmpNum *= 2 473 if tmpNum >= 10 { 474 sum += (tmpNum % 10) + 1 475 } else { 476 sum += tmpNum 477 } 478 } else { 479 sum += tmpNum 480 } 481 shouldDouble = !shouldDouble 482 } 483 484 return sum%10 == 0 485 } 486 487 // IsISBN10 checks if the string is an ISBN version 10. 488 func IsISBN10(str string) bool { 489 return IsISBN(str, 10) 490 } 491 492 // IsISBN13 checks if the string is an ISBN version 13. 493 func IsISBN13(str string) bool { 494 return IsISBN(str, 13) 495 } 496 497 // IsISBN checks if the string is an ISBN (version 10 or 13). 498 // If version value is not equal to 10 or 13, it will be checks both variants. 499 func IsISBN(str string, version int) bool { 500 sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") 501 var checksum int32 502 var i int32 503 if version == 10 { 504 if !rxISBN10.MatchString(sanitized) { 505 return false 506 } 507 for i = 0; i < 9; i++ { 508 checksum += (i + 1) * int32(sanitized[i]-'0') 509 } 510 if sanitized[9] == 'X' { 511 checksum += 10 * 10 512 } else { 513 checksum += 10 * int32(sanitized[9]-'0') 514 } 515 if checksum%11 == 0 { 516 return true 517 } 518 return false 519 } else if version == 13 { 520 if !rxISBN13.MatchString(sanitized) { 521 return false 522 } 523 factor := []int32{1, 3} 524 for i = 0; i < 12; i++ { 525 checksum += factor[i%2] * int32(sanitized[i]-'0') 526 } 527 return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0 528 } 529 return IsISBN(str, 10) || IsISBN(str, 13) 530 } 531 532 // IsJSON checks if the string is valid JSON (note: uses json.Unmarshal). 533 func IsJSON(str string) bool { 534 var js json.RawMessage 535 return json.Unmarshal([]byte(str), &js) == nil 536 } 537 538 // IsMultibyte checks if the string contains one or more multibyte chars. Empty string is valid. 539 func IsMultibyte(str string) bool { 540 if IsNull(str) { 541 return true 542 } 543 return rxMultibyte.MatchString(str) 544 } 545 546 // IsASCII checks if the string contains ASCII chars only. Empty string is valid. 547 func IsASCII(str string) bool { 548 if IsNull(str) { 549 return true 550 } 551 return rxASCII.MatchString(str) 552 } 553 554 // IsPrintableASCII checks if the string contains printable ASCII chars only. Empty string is valid. 555 func IsPrintableASCII(str string) bool { 556 if IsNull(str) { 557 return true 558 } 559 return rxPrintableASCII.MatchString(str) 560 } 561 562 // IsFullWidth checks if the string contains any full-width chars. Empty string is valid. 563 func IsFullWidth(str string) bool { 564 if IsNull(str) { 565 return true 566 } 567 return rxFullWidth.MatchString(str) 568 } 569 570 // IsHalfWidth checks if the string contains any half-width chars. Empty string is valid. 571 func IsHalfWidth(str string) bool { 572 if IsNull(str) { 573 return true 574 } 575 return rxHalfWidth.MatchString(str) 576 } 577 578 // IsVariableWidth checks if the string contains a mixture of full and half-width chars. Empty string is valid. 579 func IsVariableWidth(str string) bool { 580 if IsNull(str) { 581 return true 582 } 583 return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str) 584 } 585 586 // IsBase64 checks if a string is base64 encoded. 587 func IsBase64(str string) bool { 588 return rxBase64.MatchString(str) 589 } 590 591 // IsFilePath checks is a string is Win or Unix file path and returns it's type. 592 func IsFilePath(str string) (bool, int) { 593 if rxWinPath.MatchString(str) { 594 //check windows path limit see: 595 // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath 596 if len(str[3:]) > 32767 { 597 return false, Win 598 } 599 return true, Win 600 } else if rxUnixPath.MatchString(str) { 601 return true, Unix 602 } 603 return false, Unknown 604 } 605 606 // IsWinFilePath checks both relative & absolute paths in Windows 607 func IsWinFilePath(str string) bool { 608 if rxARWinPath.MatchString(str) { 609 //check windows path limit see: 610 // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath 611 if len(str[3:]) > 32767 { 612 return false 613 } 614 return true 615 } 616 return false 617 } 618 619 // IsUnixFilePath checks both relative & absolute paths in Unix 620 func IsUnixFilePath(str string) bool { 621 if rxARUnixPath.MatchString(str) { 622 return true 623 } 624 return false 625 } 626 627 // IsDataURI checks if a string is base64 encoded data URI such as an image 628 func IsDataURI(str string) bool { 629 dataURI := strings.Split(str, ",") 630 if !rxDataURI.MatchString(dataURI[0]) { 631 return false 632 } 633 return IsBase64(dataURI[1]) 634 } 635 636 // IsMagnetURI checks if a string is valid magnet URI 637 func IsMagnetURI(str string) bool { 638 return rxMagnetURI.MatchString(str) 639 } 640 641 // IsISO3166Alpha2 checks if a string is valid two-letter country code 642 func IsISO3166Alpha2(str string) bool { 643 for _, entry := range ISO3166List { 644 if str == entry.Alpha2Code { 645 return true 646 } 647 } 648 return false 649 } 650 651 // IsISO3166Alpha3 checks if a string is valid three-letter country code 652 func IsISO3166Alpha3(str string) bool { 653 for _, entry := range ISO3166List { 654 if str == entry.Alpha3Code { 655 return true 656 } 657 } 658 return false 659 } 660 661 // IsISO693Alpha2 checks if a string is valid two-letter language code 662 func IsISO693Alpha2(str string) bool { 663 for _, entry := range ISO693List { 664 if str == entry.Alpha2Code { 665 return true 666 } 667 } 668 return false 669 } 670 671 // IsISO693Alpha3b checks if a string is valid three-letter language code 672 func IsISO693Alpha3b(str string) bool { 673 for _, entry := range ISO693List { 674 if str == entry.Alpha3bCode { 675 return true 676 } 677 } 678 return false 679 } 680 681 // IsDNSName will validate the given string as a DNS name 682 func IsDNSName(str string) bool { 683 if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 { 684 // constraints already violated 685 return false 686 } 687 return !IsIP(str) && rxDNSName.MatchString(str) 688 } 689 690 // IsHash checks if a string is a hash of type algorithm. 691 // Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b'] 692 func IsHash(str string, algorithm string) bool { 693 var len string 694 algo := strings.ToLower(algorithm) 695 696 if algo == "crc32" || algo == "crc32b" { 697 len = "8" 698 } else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" { 699 len = "32" 700 } else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" { 701 len = "40" 702 } else if algo == "tiger192" { 703 len = "48" 704 } else if algo == "sha3-224" { 705 len = "56" 706 } else if algo == "sha256" || algo == "sha3-256" { 707 len = "64" 708 } else if algo == "sha384" || algo == "sha3-384" { 709 len = "96" 710 } else if algo == "sha512" || algo == "sha3-512" { 711 len = "128" 712 } else { 713 return false 714 } 715 716 return Matches(str, "^[a-f0-9]{"+len+"}$") 717 } 718 719 // IsSHA3224 checks is a string is a SHA3-224 hash. Alias for `IsHash(str, "sha3-224")` 720 func IsSHA3224(str string) bool { 721 return IsHash(str, "sha3-224") 722 } 723 724 // IsSHA3256 checks is a string is a SHA3-256 hash. Alias for `IsHash(str, "sha3-256")` 725 func IsSHA3256(str string) bool { 726 return IsHash(str, "sha3-256") 727 } 728 729 // IsSHA3384 checks is a string is a SHA3-384 hash. Alias for `IsHash(str, "sha3-384")` 730 func IsSHA3384(str string) bool { 731 return IsHash(str, "sha3-384") 732 } 733 734 // IsSHA3512 checks is a string is a SHA3-512 hash. Alias for `IsHash(str, "sha3-512")` 735 func IsSHA3512(str string) bool { 736 return IsHash(str, "sha3-512") 737 } 738 739 // IsSHA512 checks is a string is a SHA512 hash. Alias for `IsHash(str, "sha512")` 740 func IsSHA512(str string) bool { 741 return IsHash(str, "sha512") 742 } 743 744 // IsSHA384 checks is a string is a SHA384 hash. Alias for `IsHash(str, "sha384")` 745 func IsSHA384(str string) bool { 746 return IsHash(str, "sha384") 747 } 748 749 // IsSHA256 checks is a string is a SHA256 hash. Alias for `IsHash(str, "sha256")` 750 func IsSHA256(str string) bool { 751 return IsHash(str, "sha256") 752 } 753 754 // IsTiger192 checks is a string is a Tiger192 hash. Alias for `IsHash(str, "tiger192")` 755 func IsTiger192(str string) bool { 756 return IsHash(str, "tiger192") 757 } 758 759 // IsTiger160 checks is a string is a Tiger160 hash. Alias for `IsHash(str, "tiger160")` 760 func IsTiger160(str string) bool { 761 return IsHash(str, "tiger160") 762 } 763 764 // IsRipeMD160 checks is a string is a RipeMD160 hash. Alias for `IsHash(str, "ripemd160")` 765 func IsRipeMD160(str string) bool { 766 return IsHash(str, "ripemd160") 767 } 768 769 // IsSHA1 checks is a string is a SHA-1 hash. Alias for `IsHash(str, "sha1")` 770 func IsSHA1(str string) bool { 771 return IsHash(str, "sha1") 772 } 773 774 // IsTiger128 checks is a string is a Tiger128 hash. Alias for `IsHash(str, "tiger128")` 775 func IsTiger128(str string) bool { 776 return IsHash(str, "tiger128") 777 } 778 779 // IsRipeMD128 checks is a string is a RipeMD128 hash. Alias for `IsHash(str, "ripemd128")` 780 func IsRipeMD128(str string) bool { 781 return IsHash(str, "ripemd128") 782 } 783 784 // IsCRC32 checks is a string is a CRC32 hash. Alias for `IsHash(str, "crc32")` 785 func IsCRC32(str string) bool { 786 return IsHash(str, "crc32") 787 } 788 789 // IsCRC32b checks is a string is a CRC32b hash. Alias for `IsHash(str, "crc32b")` 790 func IsCRC32b(str string) bool { 791 return IsHash(str, "crc32b") 792 } 793 794 // IsMD5 checks is a string is a MD5 hash. Alias for `IsHash(str, "md5")` 795 func IsMD5(str string) bool { 796 return IsHash(str, "md5") 797 } 798 799 // IsMD4 checks is a string is a MD4 hash. Alias for `IsHash(str, "md4")` 800 func IsMD4(str string) bool { 801 return IsHash(str, "md4") 802 } 803 804 // IsDialString validates the given string for usage with the various Dial() functions 805 func IsDialString(str string) bool { 806 if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) { 807 return true 808 } 809 810 return false 811 } 812 813 // IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP` 814 func IsIP(str string) bool { 815 return net.ParseIP(str) != nil 816 } 817 818 // IsPort checks if a string represents a valid port 819 func IsPort(str string) bool { 820 if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 { 821 return true 822 } 823 return false 824 } 825 826 // IsIPv4 checks if the string is an IP version 4. 827 func IsIPv4(str string) bool { 828 ip := net.ParseIP(str) 829 return ip != nil && strings.Contains(str, ".") 830 } 831 832 // IsIPv6 checks if the string is an IP version 6. 833 func IsIPv6(str string) bool { 834 ip := net.ParseIP(str) 835 return ip != nil && strings.Contains(str, ":") 836 } 837 838 // IsCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6) 839 func IsCIDR(str string) bool { 840 _, _, err := net.ParseCIDR(str) 841 return err == nil 842 } 843 844 // IsMAC checks if a string is valid MAC address. 845 // Possible MAC formats: 846 // 01:23:45:67:89:ab 847 // 01:23:45:67:89:ab:cd:ef 848 // 01-23-45-67-89-ab 849 // 01-23-45-67-89-ab-cd-ef 850 // 0123.4567.89ab 851 // 0123.4567.89ab.cdef 852 func IsMAC(str string) bool { 853 _, err := net.ParseMAC(str) 854 return err == nil 855 } 856 857 // IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name 858 func IsHost(str string) bool { 859 return IsIP(str) || IsDNSName(str) 860 } 861 862 // IsMongoID checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. 863 func IsMongoID(str string) bool { 864 return rxHexadecimal.MatchString(str) && (len(str) == 24) 865 } 866 867 // IsLatitude checks if a string is valid latitude. 868 func IsLatitude(str string) bool { 869 return rxLatitude.MatchString(str) 870 } 871 872 // IsLongitude checks if a string is valid longitude. 873 func IsLongitude(str string) bool { 874 return rxLongitude.MatchString(str) 875 } 876 877 // IsIMEI checks if a string is valid IMEI 878 func IsIMEI(str string) bool { 879 return rxIMEI.MatchString(str) 880 } 881 882 // IsIMSI checks if a string is valid IMSI 883 func IsIMSI(str string) bool { 884 if !rxIMSI.MatchString(str) { 885 return false 886 } 887 888 mcc, err := strconv.ParseInt(str[0:3], 10, 32) 889 if err != nil { 890 return false 891 } 892 893 switch mcc { 894 case 202, 204, 206, 208, 212, 213, 214, 216, 218, 219: 895 case 220, 221, 222, 226, 228, 230, 231, 232, 234, 235: 896 case 238, 240, 242, 244, 246, 247, 248, 250, 255, 257: 897 case 259, 260, 262, 266, 268, 270, 272, 274, 276, 278: 898 case 280, 282, 283, 284, 286, 288, 289, 290, 292, 293: 899 case 294, 295, 297, 302, 308, 310, 311, 312, 313, 314: 900 case 315, 316, 330, 332, 334, 338, 340, 342, 344, 346: 901 case 348, 350, 352, 354, 356, 358, 360, 362, 363, 364: 902 case 365, 366, 368, 370, 372, 374, 376, 400, 401, 402: 903 case 404, 405, 406, 410, 412, 413, 414, 415, 416, 417: 904 case 418, 419, 420, 421, 422, 424, 425, 426, 427, 428: 905 case 429, 430, 431, 432, 434, 436, 437, 438, 440, 441: 906 case 450, 452, 454, 455, 456, 457, 460, 461, 466, 467: 907 case 470, 472, 502, 505, 510, 514, 515, 520, 525, 528: 908 case 530, 536, 537, 539, 540, 541, 542, 543, 544, 545: 909 case 546, 547, 548, 549, 550, 551, 552, 553, 554, 555: 910 case 602, 603, 604, 605, 606, 607, 608, 609, 610, 611: 911 case 612, 613, 614, 615, 616, 617, 618, 619, 620, 621: 912 case 622, 623, 624, 625, 626, 627, 628, 629, 630, 631: 913 case 632, 633, 634, 635, 636, 637, 638, 639, 640, 641: 914 case 642, 643, 645, 646, 647, 648, 649, 650, 651, 652: 915 case 653, 654, 655, 657, 658, 659, 702, 704, 706, 708: 916 case 710, 712, 714, 716, 722, 724, 730, 732, 734, 736: 917 case 738, 740, 742, 744, 746, 748, 750, 995: 918 return true 919 default: 920 return false 921 } 922 return true 923 } 924 925 // IsRsaPublicKey checks if a string is valid public key with provided length 926 func IsRsaPublicKey(str string, keylen int) bool { 927 bb := bytes.NewBufferString(str) 928 pemBytes, err := io.ReadAll(bb) 929 if err != nil { 930 return false 931 } 932 block, _ := pem.Decode(pemBytes) 933 if block != nil && block.Type != "PUBLIC KEY" { 934 return false 935 } 936 var der []byte 937 938 if block != nil { 939 der = block.Bytes 940 } else { 941 der, err = base64.StdEncoding.DecodeString(str) 942 if err != nil { 943 return false 944 } 945 } 946 947 key, err := x509.ParsePKIXPublicKey(der) 948 if err != nil { 949 return false 950 } 951 pubkey, ok := key.(*rsa.PublicKey) 952 if !ok { 953 return false 954 } 955 bitlen := len(pubkey.N.Bytes()) * 8 956 return bitlen == int(keylen) 957 } 958 959 // IsRegex checks if a give string is a valid regex with RE2 syntax or not 960 func IsRegex(str string) bool { 961 if _, err := regexp.Compile(str); err == nil { 962 return true 963 } 964 return false 965 } 966 967 func toJSONName(tag string) string { 968 if tag == "" { 969 return "" 970 } 971 972 // JSON name always comes first. If there's no options then split[0] is 973 // JSON name, if JSON name is not set, then split[0] is an empty string. 974 split := strings.SplitN(tag, ",", 2) 975 976 name := split[0] 977 978 // However it is possible that the field is skipped when 979 // (de-)serializing from/to JSON, in which case assume that there is no 980 // tag name to use 981 if name == "-" { 982 return "" 983 } 984 return name 985 } 986 987 func prependPathToErrors(err error, path string) error { 988 switch err2 := err.(type) { 989 case Error: 990 err2.Path = append([]string{path}, err2.Path...) 991 return err2 992 case Errors: 993 errors := err2.Errors() 994 for i, err3 := range errors { 995 errors[i] = prependPathToErrors(err3, path) 996 } 997 return err2 998 } 999 return err 1000 } 1001 1002 // ValidateArray performs validation according to condition iterator that validates every element of the array 1003 func ValidateArray(array []interface{}, iterator ConditionIterator) bool { 1004 return Every(array, iterator) 1005 } 1006 1007 // ValidateMap use validation map for fields. 1008 // result will be equal to `false` if there are any errors. 1009 // s is the map containing the data to be validated. 1010 // m is the validation map in the form: 1011 // 1012 // map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}} 1013 func ValidateMap(s map[string]interface{}, m map[string]interface{}) (bool, error) { 1014 if s == nil { 1015 return true, nil 1016 } 1017 result := true 1018 var err error 1019 var errs Errors 1020 var index int 1021 val := reflect.ValueOf(s) 1022 for key, value := range s { 1023 presentResult := true 1024 validator, ok := m[key] 1025 if !ok { 1026 presentResult = false 1027 var err error 1028 err = fmt.Errorf("all map keys has to be present in the validation map; got %s", key) 1029 err = prependPathToErrors(err, key) 1030 errs = append(errs, err) 1031 } 1032 valueField := reflect.ValueOf(value) 1033 mapResult := true 1034 typeResult := true 1035 structResult := true 1036 resultField := true 1037 switch subValidator := validator.(type) { 1038 case map[string]interface{}: 1039 var err error 1040 if v, ok := value.(map[string]interface{}); !ok { 1041 mapResult = false 1042 err = fmt.Errorf("map validator has to be for the map type only; got %s", valueField.Type().String()) 1043 err = prependPathToErrors(err, key) 1044 errs = append(errs, err) 1045 } else { 1046 mapResult, err = ValidateMap(v, subValidator) 1047 if err != nil { 1048 mapResult = false 1049 err = prependPathToErrors(err, key) 1050 errs = append(errs, err) 1051 } 1052 } 1053 case string: 1054 if (valueField.Kind() == reflect.Struct || 1055 (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) && 1056 subValidator != "-" { 1057 var err error 1058 structResult, err = ValidateStruct(valueField.Interface()) 1059 if err != nil { 1060 err = prependPathToErrors(err, key) 1061 errs = append(errs, err) 1062 } 1063 } 1064 resultField, err = typeCheck(valueField, reflect.StructField{ 1065 Name: key, 1066 PkgPath: "", 1067 Type: val.Type(), 1068 Tag: reflect.StructTag(fmt.Sprintf("%s:%q", tagName, subValidator)), 1069 Offset: 0, 1070 Index: []int{index}, 1071 Anonymous: false, 1072 }, val, nil) 1073 if err != nil { 1074 errs = append(errs, err) 1075 } 1076 case nil: 1077 // already handlerd when checked before 1078 default: 1079 typeResult = false 1080 err = fmt.Errorf("map validator has to be either map[string]interface{} or string; got %s", valueField.Type().String()) 1081 err = prependPathToErrors(err, key) 1082 errs = append(errs, err) 1083 } 1084 result = result && presentResult && typeResult && resultField && structResult && mapResult 1085 index++ 1086 } 1087 // checks required keys 1088 requiredResult := true 1089 for key, value := range m { 1090 if schema, ok := value.(string); ok { 1091 tags := parseTagIntoMap(schema) 1092 if required, ok := tags["required"]; ok { 1093 if _, ok := s[key]; !ok { 1094 requiredResult = false 1095 if required.customErrorMessage != "" { 1096 err = Error{key, fmt.Errorf(required.customErrorMessage), true, "required", []string{}} 1097 } else { 1098 err = Error{key, fmt.Errorf("required field missing"), false, "required", []string{}} 1099 } 1100 errs = append(errs, err) 1101 } 1102 } 1103 } 1104 } 1105 1106 if len(errs) > 0 { 1107 err = errs 1108 } 1109 return result && requiredResult, err 1110 } 1111 1112 // ValidateStruct use tags for fields. 1113 // result will be equal to `false` if there are any errors. 1114 // todo currently there is no guarantee that errors will be returned in predictable order (tests may to fail) 1115 func ValidateStruct(s interface{}) (bool, error) { 1116 if s == nil { 1117 return true, nil 1118 } 1119 result := true 1120 var err error 1121 val := reflect.ValueOf(s) 1122 if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr { 1123 val = val.Elem() 1124 } 1125 // we only accept structs 1126 if val.Kind() != reflect.Struct { 1127 return false, fmt.Errorf("function only accepts structs; got %s", val.Kind()) 1128 } 1129 var errs Errors 1130 for i := 0; i < val.NumField(); i++ { 1131 valueField := val.Field(i) 1132 typeField := val.Type().Field(i) 1133 if typeField.PkgPath != "" { 1134 continue // Private field 1135 } 1136 structResult := true 1137 if valueField.Kind() == reflect.Interface { 1138 valueField = valueField.Elem() 1139 } 1140 if (valueField.Kind() == reflect.Struct || 1141 (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) && 1142 typeField.Tag.Get(tagName) != "-" { 1143 var err error 1144 structResult, err = ValidateStruct(valueField.Interface()) 1145 if err != nil { 1146 err = prependPathToErrors(err, typeField.Name) 1147 errs = append(errs, err) 1148 } 1149 } 1150 resultField, err2 := typeCheck(valueField, typeField, val, nil) 1151 if err2 != nil { 1152 1153 // Replace structure name with JSON name if there is a tag on the variable 1154 jsonTag := toJSONName(typeField.Tag.Get("json")) 1155 if jsonTag != "" { 1156 switch jsonError := err2.(type) { 1157 case Error: 1158 jsonError.Name = jsonTag 1159 err2 = jsonError 1160 case Errors: 1161 for i2, err3 := range jsonError { 1162 switch customErr := err3.(type) { 1163 case Error: 1164 customErr.Name = jsonTag 1165 jsonError[i2] = customErr 1166 } 1167 } 1168 1169 err2 = jsonError 1170 } 1171 } 1172 1173 errs = append(errs, err2) 1174 } 1175 result = result && resultField && structResult 1176 } 1177 if len(errs) > 0 { 1178 err = errs 1179 } 1180 return result, err 1181 } 1182 1183 // ValidateStructAsync performs async validation of the struct and returns results through the channels 1184 func ValidateStructAsync(s interface{}) (<-chan bool, <-chan error) { 1185 res := make(chan bool) 1186 errors := make(chan error) 1187 1188 go func() { 1189 defer close(res) 1190 defer close(errors) 1191 1192 isValid, isFailed := ValidateStruct(s) 1193 1194 res <- isValid 1195 errors <- isFailed 1196 }() 1197 1198 return res, errors 1199 } 1200 1201 // ValidateMapAsync performs async validation of the map and returns results through the channels 1202 func ValidateMapAsync(s map[string]interface{}, m map[string]interface{}) (<-chan bool, <-chan error) { 1203 res := make(chan bool) 1204 errors := make(chan error) 1205 1206 go func() { 1207 defer close(res) 1208 defer close(errors) 1209 1210 isValid, isFailed := ValidateMap(s, m) 1211 1212 res <- isValid 1213 errors <- isFailed 1214 }() 1215 1216 return res, errors 1217 } 1218 1219 // parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""} 1220 func parseTagIntoMap(tag string) tagOptionsMap { 1221 optionsMap := make(tagOptionsMap) 1222 options := strings.Split(tag, ",") 1223 1224 for i, option := range options { 1225 option = strings.TrimSpace(option) 1226 1227 validationOptions := strings.Split(option, "~") 1228 if !isValidTag(validationOptions[0]) { 1229 continue 1230 } 1231 if len(validationOptions) == 2 { 1232 optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i} 1233 } else { 1234 optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i} 1235 } 1236 } 1237 return optionsMap 1238 } 1239 1240 func isValidTag(s string) bool { 1241 if s == "" { 1242 return false 1243 } 1244 for _, c := range s { 1245 switch { 1246 case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c): 1247 // Backslash and quote chars are reserved, but 1248 // otherwise any punctuation chars are allowed 1249 // in a tag name. 1250 default: 1251 if !unicode.IsLetter(c) && !unicode.IsDigit(c) { 1252 return false 1253 } 1254 } 1255 } 1256 return true 1257 } 1258 1259 // IsSSN will validate the given string as a U.S. Social Security Number 1260 func IsSSN(str string) bool { 1261 if str == "" || len(str) != 11 { 1262 return false 1263 } 1264 return rxSSN.MatchString(str) 1265 } 1266 1267 // IsSemver checks if string is valid semantic version 1268 func IsSemver(str string) bool { 1269 return rxSemver.MatchString(str) 1270 } 1271 1272 // IsType checks if interface is of some type 1273 func IsType(v interface{}, params ...string) bool { 1274 if len(params) == 1 { 1275 typ := params[0] 1276 return strings.Replace(reflect.TypeOf(v).String(), " ", "", -1) == strings.Replace(typ, " ", "", -1) 1277 } 1278 return false 1279 } 1280 1281 // IsTime checks if string is valid according to given format 1282 func IsTime(str string, format string) bool { 1283 _, err := time.Parse(format, str) 1284 return err == nil 1285 } 1286 1287 // IsUnixTime checks if string is valid unix timestamp value 1288 func IsUnixTime(str string) bool { 1289 if _, err := strconv.Atoi(str); err == nil { 1290 return true 1291 } 1292 return false 1293 } 1294 1295 // IsRFC3339 checks if string is valid timestamp value according to RFC3339 1296 func IsRFC3339(str string) bool { 1297 return IsTime(str, time.RFC3339) 1298 } 1299 1300 // IsRFC3339WithoutZone checks if string is valid timestamp value according to RFC3339 which excludes the timezone. 1301 func IsRFC3339WithoutZone(str string) bool { 1302 return IsTime(str, rfc3339WithoutZone) 1303 } 1304 1305 // IsISO4217 checks if string is valid ISO currency code 1306 func IsISO4217(str string) bool { 1307 for _, currency := range ISO4217List { 1308 if str == currency { 1309 return true 1310 } 1311 } 1312 1313 return false 1314 } 1315 1316 // ByteLength checks string's length 1317 func ByteLength(str string, params ...string) bool { 1318 if len(params) == 2 { 1319 min, _ := ToInt(params[0]) 1320 max, _ := ToInt(params[1]) 1321 return len(str) >= int(min) && len(str) <= int(max) 1322 } 1323 1324 return false 1325 } 1326 1327 // RuneLength checks string's length 1328 // Alias for StringLength 1329 func RuneLength(str string, params ...string) bool { 1330 return StringLength(str, params...) 1331 } 1332 1333 // IsRsaPub checks whether string is valid RSA key 1334 // Alias for IsRsaPublicKey 1335 func IsRsaPub(str string, params ...string) bool { 1336 if len(params) == 1 { 1337 len, _ := ToInt(params[0]) 1338 return IsRsaPublicKey(str, int(len)) 1339 } 1340 1341 return false 1342 } 1343 1344 // StringMatches checks if a string matches a given pattern. 1345 func StringMatches(s string, params ...string) bool { 1346 if len(params) == 1 { 1347 pattern := params[0] 1348 return Matches(s, pattern) 1349 } 1350 return false 1351 } 1352 1353 // StringLength checks string's length (including multi byte strings) 1354 func StringLength(str string, params ...string) bool { 1355 1356 if len(params) == 2 { 1357 strLength := utf8.RuneCountInString(str) 1358 min, _ := ToInt(params[0]) 1359 max, _ := ToInt(params[1]) 1360 return strLength >= int(min) && strLength <= int(max) 1361 } 1362 1363 return false 1364 } 1365 1366 // MinStringLength checks string's minimum length (including multi byte strings) 1367 func MinStringLength(str string, params ...string) bool { 1368 1369 if len(params) == 1 { 1370 strLength := utf8.RuneCountInString(str) 1371 min, _ := ToInt(params[0]) 1372 return strLength >= int(min) 1373 } 1374 1375 return false 1376 } 1377 1378 // MaxStringLength checks string's maximum length (including multi byte strings) 1379 func MaxStringLength(str string, params ...string) bool { 1380 1381 if len(params) == 1 { 1382 strLength := utf8.RuneCountInString(str) 1383 max, _ := ToInt(params[0]) 1384 return strLength <= int(max) 1385 } 1386 1387 return false 1388 } 1389 1390 // Range checks string's length 1391 func Range(str string, params ...string) bool { 1392 if len(params) == 2 { 1393 value, _ := ToFloat(str) 1394 min, _ := ToFloat(params[0]) 1395 max, _ := ToFloat(params[1]) 1396 return InRange(value, min, max) 1397 } 1398 1399 return false 1400 } 1401 1402 // IsInRaw checks if string is in list of allowed values 1403 func IsInRaw(str string, params ...string) bool { 1404 if len(params) == 1 { 1405 rawParams := params[0] 1406 1407 parsedParams := strings.Split(rawParams, "|") 1408 1409 return IsIn(str, parsedParams...) 1410 } 1411 1412 return false 1413 } 1414 1415 // IsIn checks if string str is a member of the set of strings params 1416 func IsIn(str string, params ...string) bool { 1417 for _, param := range params { 1418 if str == param { 1419 return true 1420 } 1421 } 1422 1423 return false 1424 } 1425 1426 func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) { 1427 if nilPtrAllowedByRequired { 1428 k := v.Kind() 1429 if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() { 1430 return true, nil 1431 } 1432 } 1433 1434 if requiredOption, isRequired := options["required"]; isRequired { 1435 if len(requiredOption.customErrorMessage) > 0 { 1436 return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}} 1437 } 1438 return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}} 1439 } else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional { 1440 return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}} 1441 } 1442 // not required and empty is valid 1443 return true, nil 1444 } 1445 1446 func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) { 1447 if !v.IsValid() { 1448 return false, nil 1449 } 1450 1451 tag := t.Tag.Get(tagName) 1452 1453 // checks if the field should be ignored 1454 switch tag { 1455 case "": 1456 if v.Kind() != reflect.Slice && v.Kind() != reflect.Map { 1457 if !fieldsRequiredByDefault { 1458 return true, nil 1459 } 1460 return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}} 1461 } 1462 case "-": 1463 return true, nil 1464 } 1465 1466 isRootType := false 1467 if options == nil { 1468 isRootType = true 1469 options = parseTagIntoMap(tag) 1470 } 1471 1472 if isEmptyValue(v) { 1473 // an empty value is not validated, checks only required 1474 isValid, resultErr = checkRequired(v, t, options) 1475 for key := range options { 1476 delete(options, key) 1477 } 1478 return isValid, resultErr 1479 } 1480 1481 var customTypeErrors Errors 1482 optionsOrder := options.orderedKeys() 1483 for _, validatorName := range optionsOrder { 1484 validatorStruct := options[validatorName] 1485 if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok { 1486 delete(options, validatorName) 1487 1488 if result := validatefunc(v.Interface(), o.Interface()); !result { 1489 if len(validatorStruct.customErrorMessage) > 0 { 1490 customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)}) 1491 continue 1492 } 1493 customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)}) 1494 } 1495 } 1496 } 1497 1498 if len(customTypeErrors.Errors()) > 0 { 1499 return false, customTypeErrors 1500 } 1501 1502 if isRootType { 1503 // Ensure that we've checked the value by all specified validators before report that the value is valid 1504 defer func() { 1505 delete(options, "optional") 1506 delete(options, "required") 1507 1508 if isValid && resultErr == nil && len(options) != 0 { 1509 optionsOrder := options.orderedKeys() 1510 for _, validator := range optionsOrder { 1511 isValid = false 1512 resultErr = Error{t.Name, fmt.Errorf( 1513 "The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}} 1514 return 1515 } 1516 } 1517 }() 1518 } 1519 1520 for _, validatorSpec := range optionsOrder { 1521 validatorStruct := options[validatorSpec] 1522 var negate bool 1523 validator := validatorSpec 1524 customMsgExists := len(validatorStruct.customErrorMessage) > 0 1525 1526 // checks whether the tag looks like '!something' or 'something' 1527 if validator[0] == '!' { 1528 validator = validator[1:] 1529 negate = true 1530 } 1531 1532 // checks for interface param validators 1533 for key, value := range InterfaceParamTagRegexMap { 1534 ps := value.FindStringSubmatch(validator) 1535 if len(ps) == 0 { 1536 continue 1537 } 1538 1539 validatefunc, ok := InterfaceParamTagMap[key] 1540 if !ok { 1541 continue 1542 } 1543 1544 delete(options, validatorSpec) 1545 1546 field := fmt.Sprint(v) 1547 if result := validatefunc(v.Interface(), ps[1:]...); (!result && !negate) || (result && negate) { 1548 if customMsgExists { 1549 return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1550 } 1551 if negate { 1552 return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1553 } 1554 return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1555 } 1556 } 1557 } 1558 1559 switch v.Kind() { 1560 case reflect.Bool, 1561 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 1562 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, 1563 reflect.Float32, reflect.Float64, 1564 reflect.String: 1565 // for each tag option checks the map of validator functions 1566 for _, validatorSpec := range optionsOrder { 1567 validatorStruct := options[validatorSpec] 1568 var negate bool 1569 validator := validatorSpec 1570 customMsgExists := len(validatorStruct.customErrorMessage) > 0 1571 1572 // checks whether the tag looks like '!something' or 'something' 1573 if validator[0] == '!' { 1574 validator = validator[1:] 1575 negate = true 1576 } 1577 1578 // checks for param validators 1579 for key, value := range ParamTagRegexMap { 1580 ps := value.FindStringSubmatch(validator) 1581 if len(ps) == 0 { 1582 continue 1583 } 1584 1585 validatefunc, ok := ParamTagMap[key] 1586 if !ok { 1587 continue 1588 } 1589 1590 delete(options, validatorSpec) 1591 1592 switch v.Kind() { 1593 case reflect.String, 1594 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 1595 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 1596 reflect.Float32, reflect.Float64: 1597 1598 field := fmt.Sprint(v) // make value into string, then validate with regex 1599 if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) { 1600 if customMsgExists { 1601 return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1602 } 1603 if negate { 1604 return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1605 } 1606 return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1607 } 1608 default: 1609 // type not yet supported, fail 1610 return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}} 1611 } 1612 } 1613 1614 if validatefunc, ok := TagMap[validator]; ok { 1615 delete(options, validatorSpec) 1616 1617 switch v.Kind() { 1618 case reflect.String, 1619 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 1620 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 1621 reflect.Float32, reflect.Float64: 1622 field := fmt.Sprint(v) // make value into string, then validate with regex 1623 if result := validatefunc(field); !result && !negate || result && negate { 1624 if customMsgExists { 1625 return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1626 } 1627 if negate { 1628 return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1629 } 1630 return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} 1631 } 1632 default: 1633 //Not Yet Supported Types (Fail here!) 1634 err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v) 1635 return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}} 1636 } 1637 } 1638 } 1639 return true, nil 1640 case reflect.Map: 1641 if v.Type().Key().Kind() != reflect.String { 1642 return false, &UnsupportedTypeError{v.Type()} 1643 } 1644 var sv stringValues 1645 sv = v.MapKeys() 1646 sort.Sort(sv) 1647 result := true 1648 for i, k := range sv { 1649 var resultItem bool 1650 var err error 1651 if v.MapIndex(k).Kind() != reflect.Struct { 1652 resultItem, err = typeCheck(v.MapIndex(k), t, o, options) 1653 if err != nil { 1654 return false, err 1655 } 1656 } else { 1657 resultItem, err = ValidateStruct(v.MapIndex(k).Interface()) 1658 if err != nil { 1659 err = prependPathToErrors(err, t.Name+"."+sv[i].Interface().(string)) 1660 return false, err 1661 } 1662 } 1663 result = result && resultItem 1664 } 1665 return result, nil 1666 case reflect.Slice, reflect.Array: 1667 result := true 1668 for i := 0; i < v.Len(); i++ { 1669 var resultItem bool 1670 var err error 1671 if v.Index(i).Kind() != reflect.Struct { 1672 resultItem, err = typeCheck(v.Index(i), t, o, options) 1673 if err != nil { 1674 return false, err 1675 } 1676 } else { 1677 resultItem, err = ValidateStruct(v.Index(i).Interface()) 1678 if err != nil { 1679 err = prependPathToErrors(err, t.Name+"."+strconv.Itoa(i)) 1680 return false, err 1681 } 1682 } 1683 result = result && resultItem 1684 } 1685 return result, nil 1686 case reflect.Interface: 1687 // If the value is an interface then encode its element 1688 if v.IsNil() { 1689 return true, nil 1690 } 1691 return ValidateStruct(v.Interface()) 1692 case reflect.Ptr: 1693 // If the value is a pointer then checks its element 1694 if v.IsNil() { 1695 return true, nil 1696 } 1697 return typeCheck(v.Elem(), t, o, options) 1698 case reflect.Struct: 1699 return true, nil 1700 default: 1701 return false, &UnsupportedTypeError{v.Type()} 1702 } 1703 } 1704 1705 func stripParams(validatorString string) string { 1706 return paramsRegexp.ReplaceAllString(validatorString, "") 1707 } 1708 1709 // isEmptyValue checks whether value empty or not 1710 func isEmptyValue(v reflect.Value) bool { 1711 switch v.Kind() { 1712 case reflect.String, reflect.Array: 1713 return v.Len() == 0 1714 case reflect.Map, reflect.Slice: 1715 return v.Len() == 0 || v.IsNil() 1716 case reflect.Bool: 1717 return !v.Bool() 1718 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1719 return v.Int() == 0 1720 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1721 return v.Uint() == 0 1722 case reflect.Float32, reflect.Float64: 1723 return v.Float() == 0 1724 case reflect.Interface, reflect.Ptr: 1725 return v.IsNil() 1726 } 1727 1728 return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) 1729 } 1730 1731 // ErrorByField returns error for specified field of the struct 1732 // validated by ValidateStruct or empty string if there are no errors 1733 // or this field doesn't exists or doesn't have any errors. 1734 func ErrorByField(e error, field string) string { 1735 if e == nil { 1736 return "" 1737 } 1738 return ErrorsByField(e)[field] 1739 } 1740 1741 // ErrorsByField returns map of errors of the struct validated 1742 // by ValidateStruct or empty map if there are no errors. 1743 func ErrorsByField(e error) map[string]string { 1744 m := make(map[string]string) 1745 if e == nil { 1746 return m 1747 } 1748 // prototype for ValidateStruct 1749 1750 switch e := e.(type) { 1751 case Error: 1752 m[e.Name] = e.Err.Error() 1753 case Errors: 1754 for _, item := range e.Errors() { 1755 n := ErrorsByField(item) 1756 for k, v := range n { 1757 m[k] = v 1758 } 1759 } 1760 } 1761 1762 return m 1763 } 1764 1765 // Error returns string equivalent for reflect.Type 1766 func (e *UnsupportedTypeError) Error() string { 1767 return "validator: unsupported type: " + e.Type.String() 1768 } 1769 1770 func (sv stringValues) Len() int { return len(sv) } 1771 func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } 1772 func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } 1773 func (sv stringValues) get(i int) string { return sv[i].String() } 1774 1775 func IsE164(str string) bool { 1776 return rxE164.MatchString(str) 1777 }