fix: 统一对话密码锁键为发送方加对方不含 IP
This commit is contained in:
@@ -64,7 +64,7 @@ const (
|
||||
LockLoginEndpointIP LockKind = "login_endpoint_ip"
|
||||
// LockLoginEndpoint:编号总数,1 小时内 50 次错 → 暂停该编号密码登录 1 小时。
|
||||
LockLoginEndpoint LockKind = "login_endpoint"
|
||||
// LockTalkPair:发送方 + 对方对话密码。
|
||||
// LockTalkPair:发送方 + 对方对话密码(不含 IP)。
|
||||
LockTalkPair LockKind = "talk_pair"
|
||||
// LockTalkTarget:对方对话密码总数。
|
||||
LockTalkTarget LockKind = "talk_target"
|
||||
@@ -90,6 +90,8 @@ type LoginLocks interface {
|
||||
Fail(key LockKey) (locked bool, retryAfter time.Duration)
|
||||
// ClearEndpoint 清除某端编号相关的登录锁定(两种都清),对应管理 unlock。
|
||||
ClearEndpoint(endpointID string)
|
||||
// ClearAllForEndpoint 清除该编号作为 EndpointID 或 PeerID 出现的全部锁定(删除端后防编号复用继承)。
|
||||
ClearAllForEndpoint(endpointID string)
|
||||
// Clear 清除精确键。
|
||||
Clear(key LockKey)
|
||||
}
|
||||
|
||||
@@ -138,3 +138,54 @@ func TestLoginLocksClearEndpoint(t *testing.T) {
|
||||
t.Fatal("e1 total lock should clear")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginLocksClearEndpointKeepsTalk(t *testing.T) {
|
||||
locks := NewLoginLocks()
|
||||
talk := LockKey{Kind: LockTalkPair, EndpointID: "e1", PeerID: "e2"}
|
||||
target := LockKey{Kind: LockTalkTarget, EndpointID: "e1"}
|
||||
login := LockKey{Kind: LockLoginEndpoint, EndpointID: "e1"}
|
||||
for i := 0; i < 10; i++ {
|
||||
locks.Fail(talk)
|
||||
locks.Fail(login)
|
||||
}
|
||||
for i := 0; i < 50; i++ {
|
||||
locks.Fail(target)
|
||||
}
|
||||
locks.ClearEndpoint("e1")
|
||||
if locked, _ := locks.Check(login); locked {
|
||||
t.Fatal("login lock should clear")
|
||||
}
|
||||
if locked, _ := locks.Check(talk); !locked {
|
||||
t.Fatal("talk pair lock must survive ClearEndpoint")
|
||||
}
|
||||
if locked, _ := locks.Check(target); !locked {
|
||||
t.Fatal("talk target lock must survive ClearEndpoint")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginLocksClearAllForEndpoint(t *testing.T) {
|
||||
locks := NewLoginLocks()
|
||||
asSender := LockKey{Kind: LockTalkPair, EndpointID: "gone", PeerID: "peer"}
|
||||
asPeer := LockKey{Kind: LockTalkPair, EndpointID: "other", PeerID: "gone"}
|
||||
target := LockKey{Kind: LockTalkTarget, EndpointID: "gone"}
|
||||
login := LockKey{Kind: LockLoginEndpointIP, EndpointID: "gone", IP: "1.1.1.1"}
|
||||
keep := LockKey{Kind: LockTalkPair, EndpointID: "keep", PeerID: "peer"}
|
||||
for i := 0; i < 10; i++ {
|
||||
locks.Fail(asSender)
|
||||
locks.Fail(asPeer)
|
||||
locks.Fail(login)
|
||||
locks.Fail(keep)
|
||||
}
|
||||
for i := 0; i < 50; i++ {
|
||||
locks.Fail(target)
|
||||
}
|
||||
locks.ClearAllForEndpoint("gone")
|
||||
for _, k := range []LockKey{asSender, asPeer, target, login} {
|
||||
if locked, _ := locks.Check(k); locked {
|
||||
t.Fatalf("expected %s cleared", lockMapKey(k))
|
||||
}
|
||||
}
|
||||
if locked, _ := locks.Check(keep); !locked {
|
||||
t.Fatal("unrelated pair should remain")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +245,24 @@ func (l *MemoryLocks) ClearEndpoint(endpointID string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ClearAllForEndpoint 清除该编号作为 EndpointID 或 PeerID 出现的全部锁定。
|
||||
func (l *MemoryLocks) ClearAllForEndpoint(endpointID string) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if endpointID == "" {
|
||||
return
|
||||
}
|
||||
for k := range l.entries {
|
||||
parts := splitLockKey(k)
|
||||
if len(parts) != 4 {
|
||||
continue
|
||||
}
|
||||
if parts[1] == endpointID || parts[2] == endpointID {
|
||||
delete(l.entries, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear 清除精确键。
|
||||
func (l *MemoryLocks) Clear(key LockKey) {
|
||||
l.mu.Lock()
|
||||
|
||||
@@ -94,6 +94,12 @@ func (l *StubLoginLocks) ClearEndpoint(endpointID string) {
|
||||
l.Cleared = append(l.Cleared, endpointID)
|
||||
}
|
||||
|
||||
func (l *StubLoginLocks) ClearAllForEndpoint(endpointID string) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.Cleared = append(l.Cleared, endpointID)
|
||||
}
|
||||
|
||||
func (l *StubLoginLocks) Clear(LockKey) {}
|
||||
|
||||
// 编译期检查:假实现满足接口。
|
||||
|
||||
Reference in New Issue
Block a user