69 lines
1.2 KiB
Go
69 lines
1.2 KiB
Go
package api
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
loginMaxFailures = 5
|
|
loginLockDuration = 15 * time.Minute
|
|
loginMinDelay = time.Second
|
|
)
|
|
|
|
type loginGuard struct {
|
|
mu sync.Mutex
|
|
byIP map[string]*loginGuardEntry
|
|
}
|
|
|
|
type loginGuardEntry struct {
|
|
failures int
|
|
lockedUntil time.Time
|
|
}
|
|
|
|
func newLoginGuard() *loginGuard {
|
|
return &loginGuard{byIP: make(map[string]*loginGuardEntry)}
|
|
}
|
|
|
|
func (g *loginGuard) check(ip string) (locked bool, retryAfter int) {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
|
|
e := g.byIP[ip]
|
|
if e == nil {
|
|
return false, 0
|
|
}
|
|
now := time.Now()
|
|
if now.Before(e.lockedUntil) {
|
|
return true, int(e.lockedUntil.Sub(now).Seconds()) + 1
|
|
}
|
|
if e.failures >= loginMaxFailures {
|
|
e.failures = 0
|
|
e.lockedUntil = time.Time{}
|
|
}
|
|
return false, 0
|
|
}
|
|
|
|
func (g *loginGuard) recordFailure(ip string) (locked bool, retryAfter int) {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
|
|
e := g.byIP[ip]
|
|
if e == nil {
|
|
e = &loginGuardEntry{}
|
|
g.byIP[ip] = e
|
|
}
|
|
e.failures++
|
|
if e.failures >= loginMaxFailures {
|
|
e.lockedUntil = time.Now().Add(loginLockDuration)
|
|
return true, int(loginLockDuration.Seconds())
|
|
}
|
|
return false, 0
|
|
}
|
|
|
|
func (g *loginGuard) reset(ip string) {
|
|
g.mu.Lock()
|
|
delete(g.byIP, ip)
|
|
g.mu.Unlock()
|
|
}
|