fix(events): build event doers without re-fetching the user

GetUserOrLinkShareUser re-fetches the account and fails its status
check, which broke deleting a disabled user's projects (the deletion
runs with the disabled account as doer). Convert the authenticated
principal directly instead — it also matches what the events serialized
before the doer became concrete, and drops a query per event.
This commit is contained in:
kolaente 2026-06-12 09:36:07 +02:00 committed by kolaente
parent b3bcab1f72
commit f0eff52949
5 changed files with 19 additions and 25 deletions

View File

@ -1217,13 +1217,9 @@ func UpdateProject(s *xorm.Session, project *Project, auth web.Auth, updateProje
return err return err
} }
doer, err := GetUserOrLinkShareUser(s, auth)
if err != nil {
return err
}
events.DispatchOnCommit(s, &ProjectUpdatedEvent{ events.DispatchOnCommit(s, &ProjectUpdatedEvent{
Project: project, Project: project,
Doer: doer, Doer: doerFromAuth(auth),
}) })
l, err := GetProjectSimpleByID(s, project.ID) l, err := GetProjectSimpleByID(s, project.ID)
@ -1452,13 +1448,9 @@ func (p *Project) Delete(s *xorm.Session, a web.Auth) (err error) {
return return
} }
doer, err := GetUserOrLinkShareUser(s, a)
if err != nil {
return err
}
events.DispatchOnCommit(s, &ProjectDeletedEvent{ events.DispatchOnCommit(s, &ProjectDeletedEvent{
Project: fullProject, Project: fullProject,
Doer: doer, Doer: doerFromAuth(a),
}) })
childProjects := []*Project{} childProjects := []*Project{}

View File

@ -109,14 +109,10 @@ func (tl *TeamProject) Create(s *xorm.Session, a web.Auth) (err error) {
return err return err
} }
doer, err := GetUserOrLinkShareUser(s, a)
if err != nil {
return err
}
events.DispatchOnCommit(s, &ProjectSharedWithTeamEvent{ events.DispatchOnCommit(s, &ProjectSharedWithTeamEvent{
Project: l, Project: l,
Team: team, Team: team,
Doer: doer, Doer: doerFromAuth(a),
}) })
err = updateProjectLastUpdated(s, l) err = updateProjectLastUpdated(s, l)

View File

@ -115,14 +115,10 @@ func (lu *ProjectUser) Create(s *xorm.Session, a web.Auth) (err error) {
return err return err
} }
doer, err := GetUserOrLinkShareUser(s, a)
if err != nil {
return err
}
events.DispatchOnCommit(s, &ProjectSharedWithUserEvent{ events.DispatchOnCommit(s, &ProjectSharedWithUserEvent{
Project: l, Project: l,
User: u, User: u,
Doer: doer, Doer: doerFromAuth(a),
}) })
err = updateProjectLastUpdated(s, l) err = updateProjectLastUpdated(s, l)

View File

@ -360,13 +360,9 @@ func (t *Team) Delete(s *xorm.Session, a web.Auth) (err error) {
return return
} }
doer, err := GetUserOrLinkShareUser(s, a)
if err != nil {
return err
}
events.DispatchOnCommit(s, &TeamDeletedEvent{ events.DispatchOnCommit(s, &TeamDeletedEvent{
Team: t, Team: t,
Doer: doer, Doer: doerFromAuth(a),
}) })
return nil return nil
} }

View File

@ -22,6 +22,20 @@ import (
"xorm.io/xorm" "xorm.io/xorm"
) )
// doerFromAuth converts the authenticated principal into a user for event
// payloads without re-fetching it. A re-fetch would fail its status check in
// flows acting on behalf of disabled accounts (e.g. user deletion), and the
// event only needs the principal as it authenticated.
func doerFromAuth(a web.Auth) *user.User {
if u, is := a.(*user.User); is {
return u
}
if share, is := a.(*LinkSharing); is {
return share.toUser()
}
return &user.User{ID: a.GetID()}
}
// GetUserOrLinkShareUser returns either a user or a link share disguised as a user. // GetUserOrLinkShareUser returns either a user or a link share disguised as a user.
func GetUserOrLinkShareUser(s *xorm.Session, a web.Auth) (uu *user.User, err error) { func GetUserOrLinkShareUser(s *xorm.Session, a web.Auth) (uu *user.User, err error) {
if u, is := a.(*user.User); is { if u, is := a.(*user.User); is {