This repository has been archived by the owner on Aug 16, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
password.go
123 lines (104 loc) · 2.88 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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package user
import (
"bytes"
"errors"
"unicode"
"golang.org/x/crypto/bcrypt"
)
var (
DefaultPasswordEncoder PasswordEncoder = &BcryptPasswordEncoder{}
ErrEncodingPassword = errors.New("encoding password")
ErrInvalidPassword = errors.New("invalid password")
ErrPasswordLength = errors.New("password at least 8 characters")
ErrPasswordUpper = errors.New("password should have upper case letters")
ErrPasswordLower = errors.New("password should have lower case letters")
ErrPasswordNumber = errors.New("password should have numbers")
)
type PasswordEncoder interface {
Encode(string) ([]byte, error)
Verify(string, []byte) (bool, error)
}
type BcryptPasswordEncoder struct{}
func (BcryptPasswordEncoder) Encode(pass string) ([]byte, error) {
return bcrypt.GenerateFromPassword([]byte(pass), 14)
}
func (BcryptPasswordEncoder) Verify(s string, p []byte) (bool, error) {
err := bcrypt.CompareHashAndPassword(p, []byte(s))
if err != nil {
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
return false, nil
}
return false, err
}
return true, nil
}
type NoopPasswordEncoder struct{}
func (m NoopPasswordEncoder) Encode(pass string) ([]byte, error) {
return []byte(pass), nil
}
func (m NoopPasswordEncoder) Verify(s string, p []byte) (bool, error) {
return bytes.Equal([]byte(s), []byte(p)), nil
}
type MockPasswordEncoder struct{ Mock []byte }
func (m MockPasswordEncoder) Encode(pass string) ([]byte, error) {
return append(m.Mock[:0:0], m.Mock...), nil
}
func (m MockPasswordEncoder) Verify(s string, p []byte) (bool, error) {
return bytes.Equal(m.Mock, []byte(s)), nil
}
type EncodedPassword []byte
func NewEncodedPassword(pass string) (EncodedPassword, error) {
if err := ValidatePasswordFormat(pass); err != nil {
return nil, err
}
got, err := DefaultPasswordEncoder.Encode(pass)
if err != nil {
return nil, ErrEncodingPassword
}
return got, nil
}
func MustEncodedPassword(pass string) EncodedPassword {
p, err := NewEncodedPassword(pass)
if err != nil {
panic(err)
}
return p
}
func (p EncodedPassword) Clone() EncodedPassword {
if p == nil {
return nil
}
return append(p[:0:0], p...)
}
func (p EncodedPassword) Verify(toVerify string) (bool, error) {
if len(toVerify) == 0 || len(p) == 0 {
return false, nil
}
return DefaultPasswordEncoder.Verify(toVerify, p)
}
func ValidatePasswordFormat(pass string) error {
var hasNum, hasUpper, hasLower bool
for _, c := range pass {
switch {
case unicode.IsNumber(c):
hasNum = true
case unicode.IsUpper(c):
hasUpper = true
case unicode.IsLower(c) || c == ' ':
hasLower = true
}
}
if len(pass) < 8 {
return ErrPasswordLength
}
if !hasLower {
return ErrPasswordLower
}
if !hasUpper {
return ErrPasswordUpper
}
if !hasNum {
return ErrPasswordNumber
}
return nil
}