新增活跃会话管理与通行密钥、钱包登录
CI / test (push) Successful in 55s

This commit is contained in:
2026-07-30 12:23:05 +08:00
parent f1914880ac
commit 109c345e5e
49 changed files with 6468 additions and 336 deletions
+16 -16
View File
@@ -21,7 +21,7 @@ func newTestAuth(t *testing.T) *AuthService {
if err != nil {
t.Fatalf("open in-memory sqlite: %v", err)
}
if err := db.AutoMigrate(&model.User{}); err != nil {
if err := db.AutoMigrate(&model.User{}, &model.UserSession{}); err != nil {
t.Fatalf("auto migrate: %v", err)
}
return NewAuthService(db, "test-jwt-secret")
@@ -32,7 +32,7 @@ func TestEnsureAdminCreatesUser(t *testing.T) {
if err := auth.EnsureAdmin("admin", "pass123"); err != nil {
t.Fatalf("EnsureAdmin: %v", err)
}
if _, _, err := auth.Login(context.Background(), "admin", "pass123", "127.0.0.1", ""); err != nil {
if _, _, err := auth.Login(context.Background(), "admin", "pass123", "", SessionMeta{ClientIP: "127.0.0.1"}); err != nil {
t.Errorf("Login after EnsureAdmin: %v", err)
}
}
@@ -45,10 +45,10 @@ func TestEnsureAdminDoesNotResetPassword(t *testing.T) {
if err := auth.EnsureAdmin("admin", "second"); err != nil {
t.Fatalf("EnsureAdmin twice: %v", err)
}
if _, _, err := auth.Login(context.Background(), "admin", "first", "127.0.0.1", ""); err != nil {
if _, _, err := auth.Login(context.Background(), "admin", "first", "", SessionMeta{ClientIP: "127.0.0.1"}); err != nil {
t.Errorf("Login with original password: %v", err)
}
if _, _, err := auth.Login(context.Background(), "admin", "second", "127.0.0.1", ""); !errors.Is(err, ErrInvalidCredentials) {
if _, _, err := auth.Login(context.Background(), "admin", "second", "", SessionMeta{ClientIP: "127.0.0.1"}); !errors.Is(err, ErrInvalidCredentials) {
t.Errorf("Login with new password: got %v, want ErrInvalidCredentials", err)
}
}
@@ -89,7 +89,7 @@ func TestLoginFailures(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, _, err := auth.Login(context.Background(), tt.username, tt.password, "127.0.0.1", "")
_, _, err := auth.Login(context.Background(), tt.username, tt.password, "", SessionMeta{ClientIP: "127.0.0.1"})
if !errors.Is(err, ErrInvalidCredentials) {
t.Errorf("Login(%q, %q) error = %v, want ErrInvalidCredentials", tt.username, tt.password, err)
}
@@ -102,7 +102,7 @@ func TestTokenRoundTrip(t *testing.T) {
if err := auth.EnsureAdmin("admin", "pass123"); err != nil {
t.Fatalf("EnsureAdmin: %v", err)
}
token, expires, err := auth.Login(context.Background(), "admin", "pass123", "127.0.0.1", "")
token, expires, err := auth.Login(context.Background(), "admin", "pass123", "", SessionMeta{ClientIP: "127.0.0.1"})
if err != nil {
t.Fatalf("Login: %v", err)
}
@@ -125,7 +125,7 @@ func TestParseTokenRejectsForged(t *testing.T) {
if err := other.EnsureAdmin("admin", "pass"); err != nil {
t.Fatalf("EnsureAdmin: %v", err)
}
forged, _, err := other.Login(context.Background(), "admin", "pass", "127.0.0.1", "")
forged, _, err := other.Login(context.Background(), "admin", "pass", "", SessionMeta{ClientIP: "127.0.0.1"})
if err != nil {
t.Fatalf("Login: %v", err)
}
@@ -143,21 +143,21 @@ func TestLogoutRevokesToken(t *testing.T) {
if err := auth.EnsureAdmin("admin", "pass123"); err != nil {
t.Fatalf("EnsureAdmin: %v", err)
}
token, _, err := auth.signToken("admin", 0)
token, _, _, err := auth.signToken("admin", 0)
if err != nil {
t.Fatalf("signToken: %v", err)
}
if _, err := auth.ParseToken(context.Background(), token); err != nil {
t.Fatalf("ParseToken before logout: %v", err)
}
auth.Logout(token)
auth.Logout(context.Background(), token)
if _, err := auth.ParseToken(context.Background(), token); err == nil {
t.Error("ParseToken after logout: got nil error, want revoked")
}
// 幂等:重复登出与无效令牌登出都不应 panic,也不影响其他令牌
auth.Logout(token)
auth.Logout("not.a.token")
fresh, _, err := auth.signToken("admin", 0)
auth.Logout(context.Background(), token)
auth.Logout(context.Background(), "not.a.token")
fresh, _, _, err := auth.signToken("admin", 0)
if err != nil {
t.Fatalf("signToken fresh: %v", err)
}
@@ -174,13 +174,13 @@ func TestTokenVersionInvalidatesOldToken(t *testing.T) {
t.Fatalf("EnsureAdmin: %v", err)
}
ctx := context.Background()
old, _, err := auth.Login(ctx, "admin", "pass123", "127.0.0.1", "")
old, _, err := auth.Login(ctx, "admin", "pass123", "", SessionMeta{ClientIP: "127.0.0.1"})
if err != nil {
t.Fatalf("Login: %v", err)
}
finalName, err := auth.UpdateCredentials(ctx, "admin", UpdateCredentialsInput{
NewPassword: "changed-456", CurrentPassword: "pass123",
})
}, proofOf(t, auth.db, "admin"))
if err != nil {
t.Fatalf("UpdateCredentials: %v", err)
}
@@ -203,11 +203,11 @@ func TestRevokeSessions(t *testing.T) {
t.Fatalf("EnsureAdmin: %v", err)
}
ctx := context.Background()
old, _, err := auth.Login(ctx, "admin", "pass123", "127.0.0.1", "")
old, _, err := auth.Login(ctx, "admin", "pass123", "", SessionMeta{ClientIP: "127.0.0.1"})
if err != nil {
t.Fatalf("Login: %v", err)
}
fresh, _, err := auth.RevokeSessions(ctx, "admin")
fresh, _, err := auth.RevokeSessions(ctx, "admin", "", SessionMeta{}, proofOf(t, auth.db, "admin"))
if err != nil {
t.Fatalf("RevokeSessions: %v", err)
}