github.com/tommi2day/gomodules@v1.13.2-0.20240423190010-b7d55d252a27/pwlib/password_check.go (about) 1 package pwlib 2 3 import ( 4 "fmt" 5 "strings" 6 7 log "github.com/sirupsen/logrus" 8 ) 9 10 // SilentCheck skip log messages while checking 11 var SilentCheck = false 12 13 // SetSpecialChars change default charset for checks 14 func SetSpecialChars(specialChars string) { 15 all := UpperChar + LowerChar + Digits + specialChars 16 charset = PasswordCharset{UpperChar, LowerChar, Digits, specialChars, all} 17 } 18 19 // DoPasswordCheck Checks a password to given criteria 20 func DoPasswordCheck(password string, length int, upper int, lower int, numeric int, special int, firstCharCheck bool, allowedChars string) bool { 21 // var ls = true 22 var ucs = true 23 var lcs = true 24 var ncs = true 25 var sps = true 26 // var cs = true 27 var fcs = true 28 var err error 29 30 // allowed chars 31 possible := allowedChars 32 if allowedChars == "" { 33 possible = charset.AllChars 34 } 35 36 // do checks 37 ls, err := checkLength(password, length) 38 logError("length", err) 39 if upper > 0 { 40 ucs, err = checkClass(password, upper, charset.UpperChar) 41 logError("uppercase", err) 42 } 43 if lower > 0 { 44 lcs, err = checkClass(password, lower, charset.LowerChar) 45 logError("lowercase", err) 46 } 47 if numeric > 0 { 48 ncs, err = checkClass(password, numeric, charset.Digits) 49 logError("numeric", err) 50 } 51 if special > 0 { 52 sps, err = checkClass(password, special, charset.SpecialChar) 53 logError("special", err) 54 } 55 cs, err := checkChars(password, possible) 56 logError("allowed chars", err) 57 if firstCharCheck { 58 fcs, err = checkFirstChar(password, charset.UpperChar+charset.LowerChar) 59 logError("first character", err) 60 } 61 62 // final state 63 return ls && ucs && lcs && ncs && sps && cs && fcs 64 } 65 66 func logError(name string, err error) { 67 if SilentCheck { 68 return 69 } 70 if err != nil { 71 log.Errorf("%s check failed: %s", name, err.Error()) 72 } else { 73 log.Debugf("%s check passed", name) 74 } 75 } 76 77 func checkClass( 78 password string, 79 should int, 80 chars string, 81 ) (bool, error) { 82 if len(password) == 0 { 83 return false, fmt.Errorf("password empty") 84 } 85 cnt := 0 86 for _, char := range strings.Split(chars, "") { 87 cnt += strings.Count(password, char) 88 } 89 if cnt < should { 90 return false, fmt.Errorf("at least %d chars out of '%s' expected", should, chars) 91 } 92 return true, nil 93 } 94 95 func checkChars( 96 password string, 97 chars string, 98 ) (bool, error) { 99 if len(password) == 0 { 100 return false, fmt.Errorf("password empty") 101 } 102 data := []rune(password) 103 for i := 0; i < len(data); i++ { 104 r := data[i] 105 idx := strings.IndexRune(chars, r) 106 if idx == -1 { 107 return false, fmt.Errorf("only %s allowed", chars) 108 } 109 } 110 return true, nil 111 } 112 113 func checkLength(password string, minlen int) (bool, error) { 114 length := len(password) 115 if length < minlen { 116 return false, fmt.Errorf("at least %d chars expected, have %d", minlen, length) 117 } 118 return true, nil 119 } 120 121 func checkFirstChar( 122 password string, 123 allowed string, 124 ) (bool, error) { 125 if len(password) == 0 { 126 return false, fmt.Errorf("%s check failed, password empty", "first letter") 127 } 128 firstLetter := []rune(password)[0] 129 idx := strings.IndexRune(allowed, firstLetter) 130 if idx == -1 { 131 return false, fmt.Errorf("%s check failed, only %s allowed", "first letter", allowed) 132 } 133 return true, nil 134 }