0d84b07f68
CI / docker (push) Successful in 1m58s
Go + Vue 管理台:Agent 隧道、域名反代、IP 安全、访客验证与监控大盘。 Gitea Actions CI 多架构镜像;纯 Go SQLite、无 CGO Docker 构建。 Co-authored-by: Cursor <cursoragent@cursor.com>
60 lines
1.3 KiB
Go
60 lines
1.3 KiB
Go
package auth
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/wormhole/wormhole/internal/config"
|
|
)
|
|
|
|
func TestLoginLimiterBlocksAfterMaxAttempts(t *testing.T) {
|
|
l := NewLoginLimiter(&config.AuthConfig{
|
|
LoginMaxAttempts: 3,
|
|
LoginWindow: time.Minute,
|
|
LoginLockout: 10 * time.Minute,
|
|
})
|
|
|
|
ip := "192.168.1.1"
|
|
user := "admin"
|
|
|
|
for i := 0; i < 3; i++ {
|
|
if _, blocked := l.IsBlocked(ip, user); blocked {
|
|
t.Fatalf("unexpected block at attempt %d", i+1)
|
|
}
|
|
l.RecordFailure(ip, user)
|
|
}
|
|
|
|
retry, blocked := l.IsBlocked(ip, user)
|
|
if !blocked || retry <= 0 {
|
|
t.Fatalf("expected block after max attempts, retry=%v blocked=%v", retry, blocked)
|
|
}
|
|
}
|
|
|
|
func TestLoginLimiterResetClearsBlock(t *testing.T) {
|
|
l := NewLoginLimiter(&config.AuthConfig{
|
|
LoginMaxAttempts: 2,
|
|
LoginWindow: time.Minute,
|
|
LoginLockout: time.Minute,
|
|
})
|
|
|
|
ip := "10.0.0.1"
|
|
l.RecordFailure(ip, "u1")
|
|
l.RecordFailure(ip, "u1")
|
|
if _, blocked := l.IsBlocked(ip, "u1"); !blocked {
|
|
t.Fatal("expected blocked")
|
|
}
|
|
|
|
l.Reset(ip, "u1")
|
|
if _, blocked := l.IsBlocked(ip, "u1"); blocked {
|
|
t.Fatal("expected unblocked after reset")
|
|
}
|
|
}
|
|
|
|
func TestLoginBlockedError(t *testing.T) {
|
|
err := &LoginBlockedError{RetryAfter: 30 * time.Second}
|
|
if !errors.As(err, new(*LoginBlockedError)) {
|
|
t.Fatal("LoginBlockedError should be detectable via errors.As")
|
|
}
|
|
}
|