fix: clean up orphaned user-keyed tables in deleteUser (#38511)

### Description
This PR addresses orphaned user-keyed tables during user deletion by
adding the missing models to the `db.DeleteBeans` call in
`services/user/delete.go`:
- `auth_model.TwoFactor` (TOTP secrets)
- `auth_model.WebAuthnCredential` (WebAuthn keys)
- `activities_model.Notification` (Notifications)
- `issues_model.IssueWatch` (Issue watches)

Additionally, it adds corresponding unit test coverage in
`services/user/user_test.go` to assert that these records are cleaned up
successfully.

### Related Issues
Fixes #38510

Signed-off-by: pranav718 <raypranav718@gmail.com>
This commit is contained in:
Knight
2026-07-18 09:38:36 +02:00
committed by GitHub
parent 2cc28ac2a9
commit c6791c3c58
2 changed files with 32 additions and 0 deletions
+4
View File
@@ -96,6 +96,10 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
&user_model.Blocking{BlockeeID: u.ID},
&actions_model.ActionRunnerToken{OwnerID: u.ID},
&actions_model.ActionScopedWorkflowSource{OwnerID: u.ID},
&auth_model.TwoFactor{UID: u.ID},
&auth_model.WebAuthnCredential{UserID: u.ID},
&activities_model.Notification{UserID: u.ID},
&issues_model.IssueWatch{UserID: u.ID},
); err != nil {
return fmt.Errorf("deleteBeans: %w", err)
}
+28
View File
@@ -9,8 +9,10 @@ import (
"testing"
"time"
activities_model "gitea.dev/models/activities"
"gitea.dev/models/auth"
"gitea.dev/models/db"
issues_model "gitea.dev/models/issues"
"gitea.dev/models/organization"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
@@ -61,6 +63,32 @@ func TestDeleteUser(t *testing.T) {
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
assert.Error(t, DeleteUser(t.Context(), org, false))
t.Run("CleanupOrphanedTables", func(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// assert they exist before deletion
unittest.AssertExistsAndLoadBean(t, &auth.TwoFactor{UID: 24})
unittest.AssertExistsAndLoadBean(t, &auth.WebAuthnCredential{UserID: 32})
unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{UserID: 2})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 2})
// delete users
user24 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 24})
assert.NoError(t, DeleteUser(t.Context(), user24, true))
user32 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 32})
assert.NoError(t, DeleteUser(t.Context(), user32, true))
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NoError(t, DeleteUser(t.Context(), user2, true))
// assert they do not exist after deletion
unittest.AssertNotExistsBean(t, &auth.TwoFactor{UID: 24})
unittest.AssertNotExistsBean(t, &auth.WebAuthnCredential{UserID: 32})
unittest.AssertNotExistsBean(t, &activities_model.Notification{UserID: 2})
unittest.AssertNotExistsBean(t, &issues_model.IssueWatch{UserID: 2})
})
}
func TestDeleteUserUnlinkedAttachments(t *testing.T) {