Files

126 lines
2.8 KiB
Go

package admin
import (
"sync"
"time"
"git.asio.asia/nixevol/NixMsg/internal/auth"
)
// MemoryLoginLocks 是内存登录锁定(重启清零)。
// P3 正式实现合入前,A1 用本实现满足管理员 IP 锁定;参数对齐 DEVELOPMENT 第 5 节。
type MemoryLoginLocks struct {
mu sync.Mutex
entries map[string]*lockState
now func() time.Time
}
type lockState struct {
fails []time.Time
lockedUntil time.Time
}
// NewMemoryLoginLocks 创建内存锁定计数器。
func NewMemoryLoginLocks() *MemoryLoginLocks {
return &MemoryLoginLocks{
entries: make(map[string]*lockState),
now: time.Now,
}
}
func (l *MemoryLoginLocks) key(k auth.LockKey) string {
return string(k.Kind) + "|" + k.EndpointID + "|" + k.PeerID + "|" + k.IP
}
func (l *MemoryLoginLocks) params(kind auth.LockKind) (window time.Duration, threshold int, lockFor time.Duration) {
switch kind {
case auth.LockLoginEndpoint, auth.LockTalkTarget:
return time.Hour, 50, time.Hour
default:
// LockLoginEndpointIP / LockTalkPair / LockAdminIP / LockRegisterIP
return 5 * time.Minute, 10, 5 * time.Minute
}
}
// Check 实现 auth.LoginLocks。
func (l *MemoryLoginLocks) Check(key auth.LockKey) (bool, time.Duration) {
l.mu.Lock()
defer l.mu.Unlock()
now := l.now()
st := l.entries[l.key(key)]
if st == nil {
return false, 0
}
if now.Before(st.lockedUntil) {
return true, st.lockedUntil.Sub(now)
}
return false, 0
}
// Fail 实现 auth.LoginLocks。
func (l *MemoryLoginLocks) Fail(key auth.LockKey) (bool, time.Duration) {
l.mu.Lock()
defer l.mu.Unlock()
now := l.now()
k := l.key(key)
st := l.entries[k]
if st == nil {
st = &lockState{}
l.entries[k] = st
}
if now.Before(st.lockedUntil) {
return true, st.lockedUntil.Sub(now)
}
window, threshold, lockFor := l.params(key.Kind)
cutoff := now.Add(-window)
kept := st.fails[:0]
for _, t := range st.fails {
if t.After(cutoff) {
kept = append(kept, t)
}
}
kept = append(kept, now)
st.fails = kept
if len(st.fails) >= threshold {
st.lockedUntil = now.Add(lockFor)
st.fails = nil
return true, lockFor
}
return false, 0
}
// ClearEndpoint 实现 auth.LoginLocks。
func (l *MemoryLoginLocks) ClearEndpoint(endpointID string) {
l.mu.Lock()
defer l.mu.Unlock()
for k := range l.entries {
// kind|endpoint|peer|ip
parts := splitLockKey(k)
if len(parts) >= 2 && parts[1] == endpointID {
delete(l.entries, k)
}
}
}
// Clear 实现 auth.LoginLocks。
func (l *MemoryLoginLocks) Clear(key auth.LockKey) {
l.mu.Lock()
defer l.mu.Unlock()
delete(l.entries, l.key(key))
}
func splitLockKey(k string) []string {
out := make([]string, 0, 4)
start := 0
for i := 0; i < len(k); i++ {
if k[i] == '|' {
out = append(out, k[start:i])
start = i + 1
}
}
out = append(out, k[start:])
return out
}
var _ auth.LoginLocks = (*MemoryLoginLocks)(nil)