Skip to content

Commit 8630ba8

Browse files
committed
Nicely handle missing user in collaborations
It is possible to have a collaboration in a repository which refers to a no-longer existing user. This causes the repository transfer to fail with an unusual error. This PR makes `repo.getCollaborators()` nicely handle the missing user by ghosting the collaboration but also adds consistency check. It also adds an Access consistency check. Fix go-gitea#17044 Signed-off-by: Andrew Thornton <[email protected]>
1 parent 87505a9 commit 8630ba8

File tree

4 files changed

+97
-4
lines changed

4 files changed

+97
-4
lines changed

models/access.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int6
225225
return fmt.Errorf("getCollaborations: %v", err)
226226
}
227227
for _, c := range collaborators {
228+
if c.User.IsGhost() {
229+
continue
230+
}
228231
updateUserAccess(accessMap, c.User, c.Collaboration.Mode)
229232
}
230233
return nil

models/repo_collaboration.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package models
88
import (
99
"fmt"
1010

11+
"code.gitea.io/gitea/modules/log"
1112
"code.gitea.io/gitea/modules/timeutil"
1213

1314
"xorm.io/builder"
@@ -83,16 +84,24 @@ func (repo *Repository) getCollaborators(e Engine, listOptions ListOptions) ([]*
8384
return nil, fmt.Errorf("getCollaborations: %v", err)
8485
}
8586

86-
collaborators := make([]*Collaborator, len(collaborations))
87-
for i, c := range collaborations {
87+
collaborators := make([]*Collaborator, 0, len(collaborations))
88+
for _, c := range collaborations {
8889
user, err := getUserByID(e, c.UserID)
8990
if err != nil {
91+
if IsErrUserNotExist(err) {
92+
log.Warn("Inconsistent DB: User: %d is listed as collaborator of %-v but does not exist", c.UserID, repo)
93+
collaborators = append(collaborators, &Collaborator{
94+
User: NewGhostUser(),
95+
Collaboration: c,
96+
})
97+
continue
98+
}
9099
return nil, err
91100
}
92-
collaborators[i] = &Collaborator{
101+
collaborators = append(collaborators, &Collaborator{
93102
User: user,
94103
Collaboration: c,
95-
}
104+
})
96105
}
97106
return collaborators, nil
98107
}

models/repo_transfer.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,14 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
269269
// Dummy object.
270270
collaboration := &Collaboration{RepoID: repo.ID}
271271
for _, c := range collaborators {
272+
if c.IsGhost() {
273+
collaboration.ID = c.Collaboration.ID
274+
if _, err := sess.Delete(collaboration); err != nil {
275+
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
276+
}
277+
collaboration.ID = 0
278+
}
279+
272280
if c.ID != newOwner.ID {
273281
isMember, err := isOrganizationMember(sess, newOwner.ID, c.ID)
274282
if err != nil {
@@ -281,6 +289,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
281289
if _, err := sess.Delete(collaboration); err != nil {
282290
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
283291
}
292+
collaboration.UserID = 0
284293
}
285294

286295
// Remove old team-repository relations.

modules/doctor/dbconsistency.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,78 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
262262
}
263263
}
264264

265+
// find collaborations without users
266+
count, err = models.CountOrphanedObjects("collaboration", "user", "collaboration.user_id=user.id")
267+
if err != nil {
268+
logger.Critical("Error: %v whilst counting orphaned collaborations", err)
269+
return err
270+
}
271+
if count > 0 {
272+
if autofix {
273+
if err = models.DeleteOrphanedObjects("collaboration", "user", "collaboration.user_id=user.id"); err != nil {
274+
logger.Critical("Error: %v whilst deleting orphaned collaborations", err)
275+
return err
276+
}
277+
logger.Info("%d collaborations without existing users deleted", count)
278+
} else {
279+
logger.Warn("%d collaborations without existing users", count)
280+
}
281+
}
282+
283+
// find collaborations without repository
284+
count, err = models.CountOrphanedObjects("collaboration", "repository", "collaboration.repo_id=repository.id")
285+
if err != nil {
286+
logger.Critical("Error: %v whilst counting orphaned collaborations", err)
287+
return err
288+
}
289+
if count > 0 {
290+
if autofix {
291+
if err = models.DeleteOrphanedObjects("collaboration", "repository", "collaboration.repo_id=repository.id"); err != nil {
292+
logger.Critical("Error: %v whilst deleting orphaned collaborations", err)
293+
return err
294+
}
295+
logger.Info("%d collaborations without existing repository deleted", count)
296+
} else {
297+
logger.Warn("%d collaborations without existing repository", count)
298+
}
299+
}
300+
301+
// find access without users
302+
count, err = models.CountOrphanedObjects("access", "user", "access.user_id=user.id")
303+
if err != nil {
304+
logger.Critical("Error: %v whilst counting orphaned access", err)
305+
return err
306+
}
307+
if count > 0 {
308+
if autofix {
309+
if err = models.DeleteOrphanedObjects("access", "user", "access.user_id=user.id"); err != nil {
310+
logger.Critical("Error: %v whilst deleting orphaned access", err)
311+
return err
312+
}
313+
logger.Info("%d access without existing users deleted", count)
314+
} else {
315+
logger.Warn("%d access without existing users", count)
316+
}
317+
}
318+
319+
// find access without repository
320+
count, err = models.CountOrphanedObjects("access", "repository", "access.repo_id=repository.id")
321+
if err != nil {
322+
logger.Critical("Error: %v whilst counting orphaned access", err)
323+
return err
324+
}
325+
if count > 0 {
326+
if autofix {
327+
if err = models.DeleteOrphanedObjects("access", "repository", "access.repo_id=repository.id"); err != nil {
328+
logger.Critical("Error: %v whilst deleting orphaned access", err)
329+
return err
330+
}
331+
logger.Info("%d access without existing repository deleted", count)
332+
} else {
333+
logger.Warn("%d access without existing repository", count)
334+
}
335+
}
336+
265337
return nil
266338
}
267339

0 commit comments

Comments
 (0)