-
Notifications
You must be signed in to change notification settings - Fork 0
/
loginverifier.go
191 lines (166 loc) · 4.61 KB
/
loginverifier.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package acl
import (
"fmt"
"sync"
"time"
"github.com/kevinyjn/gocom/microsvc/builtinmodels"
)
// LoginUser interface of login user information
type LoginUser interface {
GetUserID() string
GetName() string
VerifyPassword(passwordOrHash string) bool
}
// LoginVerifier interface of login user validator
type LoginVerifier interface {
VerifyUser(userName string, passwordOrHash string, remoteIP string) (LoginUser, error)
PushBlocked(remoteIP string, expireTs int64)
}
// PushBuiltinUser for localhost login
func PushBuiltinUser(user LoginUser) {
builtinUsers.PushUser(user)
}
var (
UserVerifyManager LoginVerifier = &BuiltinUserVerifyManager{blacklist: NewBlacklistPool()}
builtinUsers = builtinUsersWrapper{
users: map[string]LoginUser{},
}
)
// builtinUserVerifyManager of LoginVerifier
type builtinUserVerifyManager struct {
users map[string]LoginUser
mu sync.RWMutex
}
// BuiltinUser
type BuiltinUser struct {
UserID string `json:"uid"`
Name string `json:"name"`
AppID string `json:"appId"`
PasswordOrHash string `json:"-"`
}
// GetUserID user id
func (bu *BuiltinUser) GetUserID() string {
return bu.UserID
}
// GetAppID app id
func (bu *BuiltinUser) GetAppID() string {
return bu.AppID
}
// GetName name of user
func (bu *BuiltinUser) GetName() string {
return bu.Name
}
// VerifyPassword if password matches
func (bu *BuiltinUser) VerifyPassword(passwordOrHash string) bool {
return passwordOrHash == bu.PasswordOrHash
}
type builtinUsersWrapper struct {
users map[string]LoginUser
mu sync.RWMutex
}
// VerifyUser if user exists or password matches
func (buw *builtinUsersWrapper) VerifyUser(userName string, passwordOrHash string, remoteIP string) (LoginUser, error) {
buw.mu.RLock()
defer buw.mu.RUnlock()
if nil == buw.users {
return nil, fmt.Errorf("User name does not exists")
}
u, _ := buw.users[userName]
if nil == u {
return nil, fmt.Errorf("User name does not exists")
}
if false == u.VerifyPassword(passwordOrHash) {
return nil, fmt.Errorf("User password were not correct")
}
return u, nil
}
// PushUser for localhost login
func (buw *builtinUsersWrapper) PushUser(user LoginUser) {
if nil == user {
return
}
buw.mu.Lock()
buw.users[user.GetName()] = user
buw.mu.Unlock()
}
// PushBlocked by remote IP
func (buw *builtinUserVerifyManager) PushBlocked(remoteIP string, expireTs int64) {
}
type BuiltinUserVerifyManager struct {
userModel builtinmodels.UserModel
blacklist BlacklistPool
failures map[string]failureUserElement
mu sync.RWMutex
}
type failureUserElement struct {
name string
count int
lastTimestamp int64
}
// VerifyUser if user exists or password matches
func (buv *BuiltinUserVerifyManager) VerifyUser(userName string, passwordOrHash string, remoteIP string) (LoginUser, error) {
if nil != buv.blacklist {
if buv.blacklist.IsBlocked(remoteIP) {
return nil, fmt.Errorf("Client were blocked")
}
}
u, err := buv.ensureUserModel().FindByName(userName)
if nil != err || nil == u {
if nil == err {
err = fmt.Errorf("User name does not exists")
}
buv.onInvalid(remoteIP, time.Now().Unix(), nil)
return nil, err
}
if false == u.VerifyPassword(passwordOrHash) {
now := time.Now().Unix()
buv.onInvalid(remoteIP, now, nil)
buv.onInvalid(userName, now, u)
return nil, fmt.Errorf("User password were not correct")
}
return u, nil
}
// PushUser for localhost login
func (buv *BuiltinUserVerifyManager) PushUser(user LoginUser) {
if nil == user {
return
}
}
// PushBlocked by remote IP
func (buv *BuiltinUserVerifyManager) PushBlocked(remoteIP string, expireTs int64) {
if nil == buv.blacklist {
buv.blacklist = NewBlacklistPool()
}
buv.blacklist.PushBlocked(remoteIP, expireTs)
}
func (buv *BuiltinUserVerifyManager) ensureUserModel() builtinmodels.UserModel {
if nil == buv.userModel {
buv.userModel = &builtinmodels.User{}
}
return buv.userModel
}
func (buv *BuiltinUserVerifyManager) onInvalid(name string, timestamp int64, user builtinmodels.UserModel) int {
buv.mu.Lock()
if nil == buv.failures {
buv.failures = map[string]failureUserElement{}
}
ele, ok := buv.failures[name]
if ok && ele.lastTimestamp+int64(ValidateFailureDurationSecondsThreshold) > timestamp {
ele.count = ele.count + 1
} else {
ele = failureUserElement{
name: name,
count: 1,
lastTimestamp: timestamp,
}
}
buv.failures[name] = ele
buv.mu.Unlock()
if ele.count > ValidateFailureCountThreshold && nil == user {
if nil == buv.blacklist {
buv.blacklist = NewBlacklistPool()
}
buv.blacklist.PushBlocked(name, timestamp+int64(BlockingIPDurationSeconds))
}
return ele.count
}