-
Notifications
You must be signed in to change notification settings - Fork 116
/
Copy pathpassword.go
82 lines (68 loc) · 2.19 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
package password
import (
"fmt"
"github.com/muesli/crunchy"
)
// Validator is a wrapper type for our password validation
type Validator struct {
opts crunchy.Options
cr *crunchy.Validator
}
// EmptyError is returned if the validated password is empty or all whitespace.
type EmptyError struct{}
func (*EmptyError) Error() string {
return "a password is required and must contain at least one non-whitespace character"
}
// TooShortError is returned if the password is shorter than the required number
// of chars.
type TooShortError struct {
min int
}
func (e *TooShortError) Error() string {
return fmt.Sprintf("password is too short (must be at least %d characters)", e.min)
}
// TooFewCharsError is returned if the password has less than the required number
// of distinct characters.
type TooFewCharsError struct {
min int
}
func (e *TooFewCharsError) Error() string {
return fmt.Sprintf("password does not contain enough distinct characters (minimum %d)", e.min)
}
// NewValidator instantiates a password.Validator struct
func NewValidator() (*Validator, error) {
opts := crunchy.Options{
MinDiff: 3, // distinct characters
MinLength: 8,
DictionaryPath: "/dev/null",
}
return &Validator{opts: opts, cr: crunchy.NewValidatorWithOpts(opts)}, nil
}
// Validate returns a descriptive error if the passed password fails to meet our
// password policy.
func (v *Validator) Validate(pass string) error {
return v.translateError(v.cr.Check(pass))
}
// Note 2017/12/21 (sr): crunchy.ErrHashedDictionary cannot happen in our case,
// since we don't set up hash functions to check.
func (v *Validator) translateError(err error) error {
// unwrap dictionary errors (ignore word and actual distance)
if underlyingErr, ok := err.(*crunchy.DictionaryError); ok {
err = underlyingErr.Err
}
switch err {
// These are ignored:
case crunchy.ErrTooSystematic,
crunchy.ErrDictionary,
crunchy.ErrMangledDictionary:
return nil
case crunchy.ErrEmpty:
return &EmptyError{}
case crunchy.ErrTooShort:
return &TooShortError{min: v.opts.MinLength}
case crunchy.ErrTooFewChars:
return &TooFewCharsError{min: v.opts.MinDiff}
default: // includes err == nil
return err
}
}