2018-10-09 19:59:52 -04:00
|
|
|
package data
|
2018-09-15 15:05:36 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type passtimer struct {
|
|
|
|
password string
|
|
|
|
timer *time.Timer
|
|
|
|
}
|
|
|
|
|
|
|
|
type userpass struct {
|
|
|
|
correct *passtimer
|
|
|
|
wrong []passtimer
|
|
|
|
}
|
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
type Storage struct {
|
|
|
|
passwords map[string]*userpass
|
|
|
|
lock sync.RWMutex
|
|
|
|
success time.Duration
|
|
|
|
wrong time.Duration
|
|
|
|
}
|
2018-09-15 15:05:36 -04:00
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
func NewStorage(success, wrong time.Duration) *Storage {
|
|
|
|
return &Storage{
|
|
|
|
passwords: map[string]*userpass{},
|
|
|
|
lock: sync.RWMutex{},
|
|
|
|
success: success,
|
|
|
|
wrong: wrong,
|
2018-09-15 15:05:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
func (p *Storage) Get(username, password string) (bool, bool) {
|
|
|
|
p.lock.RLock()
|
|
|
|
defer p.lock.RUnlock()
|
2018-09-15 15:05:36 -04:00
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
data, found := p.passwords[username]
|
2018-09-15 15:05:36 -04:00
|
|
|
if !found {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
|
|
|
|
if data.correct != nil && (*data.correct).password == password {
|
|
|
|
return true, true
|
|
|
|
}
|
|
|
|
|
2018-09-15 16:43:44 -04:00
|
|
|
_, found = containsWrongPassword(data, password)
|
2018-09-15 15:05:36 -04:00
|
|
|
|
|
|
|
return false, found
|
|
|
|
}
|
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
func (p *Storage) Put(username, password string, ok bool) {
|
|
|
|
p.lock.Lock()
|
|
|
|
defer p.lock.Unlock()
|
2018-09-15 15:05:36 -04:00
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
data, found := p.passwords[username]
|
2018-09-15 15:05:36 -04:00
|
|
|
if !found {
|
2018-09-15 16:43:44 -04:00
|
|
|
data = &userpass{}
|
2018-10-09 19:59:52 -04:00
|
|
|
p.passwords[username] = data
|
2018-09-15 15:05:36 -04:00
|
|
|
}
|
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
timeout := p.wrong
|
2018-09-15 16:43:44 -04:00
|
|
|
if ok {
|
2018-10-09 19:59:52 -04:00
|
|
|
timeout = p.success
|
2018-09-15 16:43:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pass := passtimer{
|
|
|
|
password: password,
|
|
|
|
timer: time.AfterFunc(timeout, func() {
|
2018-10-09 19:59:52 -04:00
|
|
|
p.remove(username, password, ok)
|
2018-09-15 16:43:44 -04:00
|
|
|
}),
|
|
|
|
}
|
|
|
|
|
2018-09-15 15:05:36 -04:00
|
|
|
if ok {
|
|
|
|
if data.correct != nil {
|
|
|
|
data.correct.timer.Stop()
|
|
|
|
}
|
2018-09-15 16:43:44 -04:00
|
|
|
data.correct = &pass
|
2018-09-15 15:05:36 -04:00
|
|
|
} else {
|
2018-09-15 16:43:44 -04:00
|
|
|
pos, found := containsWrongPassword(data, password)
|
2018-09-15 15:05:36 -04:00
|
|
|
if found {
|
|
|
|
data.wrong[pos].timer.Stop()
|
|
|
|
} else {
|
|
|
|
data.wrong = append(data.wrong, passtimer{})
|
|
|
|
copy(data.wrong[pos+1:], data.wrong[pos:])
|
|
|
|
}
|
|
|
|
data.wrong[pos] = pass
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
func (p *Storage) remove(username, password string, ok bool) {
|
|
|
|
p.lock.Lock()
|
|
|
|
defer p.lock.Unlock()
|
2018-09-15 15:05:36 -04:00
|
|
|
|
2018-10-09 19:59:52 -04:00
|
|
|
data, found := p.passwords[username]
|
2018-09-15 15:05:36 -04:00
|
|
|
if !found {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
if data.correct != nil {
|
|
|
|
data.correct.timer.Stop()
|
|
|
|
data.correct = nil
|
|
|
|
}
|
|
|
|
} else {
|
2018-09-15 16:43:44 -04:00
|
|
|
pos, found := containsWrongPassword(data, password)
|
2018-09-15 15:05:36 -04:00
|
|
|
if found {
|
|
|
|
data.wrong[pos].timer.Stop()
|
|
|
|
data.wrong = data.wrong[:pos+copy(data.wrong[pos:], data.wrong[pos+1:])]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if data.correct == nil && len(data.wrong) == 0 {
|
2018-10-09 19:59:52 -04:00
|
|
|
delete(p.passwords, username)
|
2018-09-15 15:05:36 -04:00
|
|
|
}
|
|
|
|
}
|
2018-10-09 19:59:52 -04:00
|
|
|
|
|
|
|
func containsWrongPassword(data *userpass, password string) (int, bool) {
|
|
|
|
size := len(data.wrong)
|
|
|
|
if size == 0 {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
|
|
|
|
pos := sort.Search(size, func(i int) bool {
|
|
|
|
return data.wrong[i].password >= password
|
|
|
|
})
|
|
|
|
|
|
|
|
return pos, pos < size &&
|
|
|
|
data.wrong[pos].password == password
|
|
|
|
}
|