diff --git a/pkg/models/task_attachment.go b/pkg/models/task_attachment.go index 23e3fb5c4..59d8d7594 100644 --- a/pkg/models/task_attachment.go +++ b/pkg/models/task_attachment.go @@ -134,9 +134,9 @@ func (ta *TaskAttachment) ReadOne(s *xorm.Session, _ web.Auth) (err error) { return } - // Get the creator (non-fatal if user doesn't exist) + // Swallow missing/disabled/locked so the user-delete cascade can complete. ta.CreatedBy, err = user.GetUserByID(s, ta.CreatedByID) - if err != nil && !user.IsErrUserDoesNotExist(err) { + if err != nil && !user.IsErrUserDoesNotExist(err) && !user.IsErrUserStatusError(err) { return err } diff --git a/pkg/models/user_delete_test.go b/pkg/models/user_delete_test.go index d705616b3..fa24cd928 100644 --- a/pkg/models/user_delete_test.go +++ b/pkg/models/user_delete_test.go @@ -86,6 +86,46 @@ func TestDeleteUser(t *testing.T) { require.NoError(t, s.Commit()) db.AssertMissing(t, "users", map[string]interface{}{"id": u.ID}) }) + t.Run("disabled user with task attachment they created", func(t *testing.T) { + // Regression test: the cascade calls TaskAttachment.Delete -> ReadOne, + // which loads the attachment's creator via user.GetUserByID. If the + // creator is the disabled user being deleted, that lookup must not + // surface ErrAccountDisabled out of the cascade. + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + notifications.Fake() + + project := &Project{ + Title: "disabled user project", + OwnerID: 17, + } + _, err := s.Insert(project) + require.NoError(t, err) + + task := &Task{ + Title: "disabled user task", + ProjectID: project.ID, + CreatedByID: 17, + Index: 1, + } + _, err = s.Insert(task) + require.NoError(t, err) + + _, err = s.Insert(&TaskAttachment{ + TaskID: task.ID, + FileID: 1, + CreatedByID: 17, + }) + require.NoError(t, err) + + err = DeleteUser(s, &user.User{ID: 17}) + require.NoError(t, err) + require.NoError(t, s.Commit()) + db.AssertMissing(t, "users", map[string]interface{}{"id": 17}) + db.AssertMissing(t, "projects", map[string]interface{}{"id": project.ID}) + db.AssertMissing(t, "tasks", map[string]interface{}{"id": task.ID}) + }) t.Run("cleans up task assignments and subscriptions", func(t *testing.T) { db.LoadAndAssertFixtures(t) s := db.NewSession()