-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
password.go
90 lines (75 loc) · 2.32 KB
/
password.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package utils
import (
"errors"
"fmt"
"os"
"regexp"
"strings"
)
var (
ErrPasswordWhitespace = errors.New("leading/trailing whitespace detected in password")
ErrEmptyPasswordInFile = errors.New("detected empty password in password file")
)
// PasswordComplexityRequirements defines the complexity requirements message
// Note that adding an entropy requirement wouldn't add much, since a 16
// character password already has an entropy score of 75 even if it's all
// lowercase characters
const PasswordComplexityRequirements = `
Must have a length of 16-50 characters
Must not comprise:
Leading or trailing whitespace (note that a trailing newline in the password file, if present, will be ignored)
`
const MinRequiredLen = 16
var LeadingWhitespace = regexp.MustCompile(`^\s+`)
var TrailingWhitespace = regexp.MustCompile(`\s+$`)
var (
ErrMsgHeader = fmt.Sprintf(`
Expected password complexity:
Must be at least %d characters long
Must not comprise:
Leading or trailing whitespace
A user's API email
Faults:
`, MinRequiredLen)
ErrWhitespace = errors.New("password contains a leading or trailing whitespace")
)
func VerifyPasswordComplexity(password string, disallowedStrings ...string) (merr error) {
errMsg := ErrMsgHeader
var stringErrs []string
if LeadingWhitespace.MatchString(password) || TrailingWhitespace.MatchString(password) {
stringErrs = append(stringErrs, ErrWhitespace.Error())
}
if len(password) < MinRequiredLen {
stringErrs = append(stringErrs, fmt.Sprintf("password is less than %d characters long", MinRequiredLen))
}
for _, s := range disallowedStrings {
if strings.Contains(strings.ToLower(password), strings.ToLower(s)) {
stringErrs = append(stringErrs, fmt.Sprintf("password may not contain: %q", s))
}
}
if len(stringErrs) > 0 {
for _, stringErr := range stringErrs {
errMsg = fmt.Sprintf("%s %s\n", errMsg, stringErr)
}
merr = errors.New(errMsg)
}
return
}
func PasswordFromFile(pwdFile string) (string, error) {
if len(pwdFile) == 0 {
return "", nil
}
dat, err := os.ReadFile(pwdFile)
// handle POSIX case, when text files may have a trailing \n
pwd := strings.TrimSuffix(string(dat), "\n")
if err != nil {
return pwd, err
}
if len(pwd) == 0 {
return pwd, ErrEmptyPasswordInFile
}
if strings.TrimSpace(pwd) != pwd {
return pwd, ErrPasswordWhitespace
}
return pwd, err
}