fix: purge authentication data on hard user deletion (#6168)

* fix: purge authentication data on hard user deletion

* fix: fail closed when 2FA status lookup fails

* fix: reject stale Telegram login callbacks

* fix(twofa): prevent concurrent backup code and lockout bypasses

* fix(auth): harden user deletion and Telegram verification
This commit is contained in:
Seefs
2026-07-14 14:25:54 +08:00
committed by GitHub
parent 7c28993f6b
commit b6b97a66e3
8 changed files with 371 additions and 51 deletions
+34 -8
View File
@@ -423,12 +423,8 @@ func HardDeleteUserById(id int) error {
if id == 0 {
return errors.New("id 为空!")
}
return DB.Transaction(func(tx *gorm.DB) error {
if err := deleteUserOAuthBindingsByUserId(tx, id); err != nil {
return err
}
return tx.Unscoped().Delete(&User{}, "id = ?", id).Error
})
user := User{Id: id}
return user.HardDelete()
}
func inviteUser(inviterId int) (err error) {
@@ -754,12 +750,42 @@ func (user *User) HardDelete() error {
if user.Id == 0 {
return errors.New("id 为空!")
}
return DB.Transaction(func(tx *gorm.DB) error {
if err := deleteUserOAuthBindingsByUserId(tx, user.Id); err != nil {
var tokens []Token
err := DB.Transaction(func(tx *gorm.DB) error {
if common.RedisEnabled {
if err := tx.Unscoped().Select("id", commonKeyCol).Where("user_id = ?", user.Id).Find(&tokens).Error; err != nil {
return err
}
}
if err := deleteUserAuthenticationData(tx, user.Id); err != nil {
return err
}
return tx.Unscoped().Delete(user).Error
})
if err != nil {
return err
}
if err := invalidateTokensCache(tokens); err != nil {
common.SysError(fmt.Sprintf("failed to invalidate token cache after hard deleting user %d: %v", user.Id, err))
}
if err := invalidateUserCache(user.Id); err != nil {
common.SysError(fmt.Sprintf("failed to invalidate user cache after hard deleting user %d: %v", user.Id, err))
}
return nil
}
func deleteUserAuthenticationData(tx *gorm.DB, userId int) error {
for _, authenticationData := range []any{
&TwoFABackupCode{},
&TwoFA{},
&PasskeyCredential{},
&Token{},
} {
if err := tx.Unscoped().Where("user_id = ?", userId).Delete(authenticationData).Error; err != nil {
return err
}
}
return deleteUserOAuthBindingsByUserId(tx, userId)
}
// ValidateAndFill check password & user status