fix(auth): align session validation and lockout semantics
This commit is contained in:
@@ -278,7 +278,7 @@ func (s *authService) resolveTokenTTL(authCfg config.AuthConfig) time.Duration {
|
|||||||
|
|
||||||
func (s *authService) validateSessionToken(token string) (*models.LoginSession, error) {
|
func (s *authService) validateSessionToken(token string) (*models.LoginSession, error) {
|
||||||
if strings.TrimSpace(token) == "" {
|
if strings.TrimSpace(token) == "" {
|
||||||
return nil, errorsx.InvalidToken("token 不能为空")
|
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||||
}
|
}
|
||||||
session := LoginSessionService.FindOne(sqls.NewCnd().Eq("token", token))
|
session := LoginSessionService.FindOne(sqls.NewCnd().Eq("token", token))
|
||||||
if session == nil {
|
if session == nil {
|
||||||
@@ -411,7 +411,7 @@ func (s *authService) createLoginCredentialLog(principal string, userID int64, s
|
|||||||
func (s *authService) isCredentialLocked(username string, authCfg config.AuthConfig) bool {
|
func (s *authService) isCredentialLocked(username string, authCfg config.AuthConfig) bool {
|
||||||
maxFailedAttempts := authCfg.MaxFailedAttempts
|
maxFailedAttempts := authCfg.MaxFailedAttempts
|
||||||
if maxFailedAttempts <= 0 {
|
if maxFailedAttempts <= 0 {
|
||||||
maxFailedAttempts = 5
|
return false
|
||||||
}
|
}
|
||||||
lockMinute := authCfg.CredentialLockMinute
|
lockMinute := authCfg.CredentialLockMinute
|
||||||
if lockMinute <= 0 {
|
if lockMinute <= 0 {
|
||||||
@@ -421,7 +421,6 @@ func (s *authService) isCredentialLocked(username string, authCfg config.AuthCon
|
|||||||
return LoginCredentialLogService.Count(sqls.NewCnd().
|
return LoginCredentialLogService.Count(sqls.NewCnd().
|
||||||
Eq("principal", username).
|
Eq("principal", username).
|
||||||
Eq("success", false).
|
Eq("success", false).
|
||||||
NotEq("reason", "credential locked").
|
|
||||||
Where("created_at >= ?", since)) >= int64(maxFailedAttempts)
|
Where("created_at >= ?", since)) >= int64(maxFailedAttempts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -155,11 +155,43 @@ func TestAuthServiceLoginCredentialLockout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAuthServiceCredentialLockoutDisabledWhenMaxAttemptsNonPositive(t *testing.T) {
|
||||||
|
db := setupAuthServiceTestDB(t)
|
||||||
|
user := createAuthTestUser(t, db, "admin", "secret")
|
||||||
|
now := time.Now()
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
if err := db.Create(&models.LoginCredentialLog{
|
||||||
|
Principal: "admin",
|
||||||
|
UserID: user.ID,
|
||||||
|
Success: false,
|
||||||
|
Reason: "credential locked",
|
||||||
|
CreatedAt: now.Add(-time.Duration(i+1) * time.Minute),
|
||||||
|
}).Error; err != nil {
|
||||||
|
t.Fatalf("seed credential log: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := newAuthService().Login(request.LoginRequest{Username: "admin", Password: "secret"}, config.AuthConfig{
|
||||||
|
TokenTTLHours: 2,
|
||||||
|
MaxFailedAttempts: 0,
|
||||||
|
CredentialLockMinute: 15,
|
||||||
|
}, "127.0.0.1", "go-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected lockout to be disabled, got %v", err)
|
||||||
|
}
|
||||||
|
if ret == nil || ret.AccessToken == "" {
|
||||||
|
t.Fatalf("expected login response with access token, got %+v", ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateSessionTokenStates(t *testing.T) {
|
func TestValidateSessionTokenStates(t *testing.T) {
|
||||||
db := setupAuthServiceTestDB(t)
|
db := setupAuthServiceTestDB(t)
|
||||||
svc := newAuthService()
|
svc := newAuthService()
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
|
if _, err := svc.validateSessionToken(" "); !hasCode(err, errorsx.CodeAuthUnauthorized) {
|
||||||
|
t.Fatalf("expected unauthorized for empty token, got %v", err)
|
||||||
|
}
|
||||||
if _, err := svc.validateSessionToken("missing"); !hasCode(err, errorsx.CodeAuthInvalidToken) {
|
if _, err := svc.validateSessionToken("missing"); !hasCode(err, errorsx.CodeAuthInvalidToken) {
|
||||||
t.Fatalf("expected invalid token for missing session, got %v", err)
|
t.Fatalf("expected invalid token for missing session, got %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user