From 3711d573e68f653f5be0732747b4607754edf26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=98=99=E2=97=A6=20The=20Tablet=20=E2=9D=80=20GamerGirla?= =?UTF-8?q?ndCo=20=E2=97=A6=E2=9D=A7?= Date: Tue, 12 Aug 2025 23:55:46 -0400 Subject: [PATCH] fix deadlock caused by differing contexts when retrieving group ancestry with `ParentGroupCond`/`GetParentGroupChain` when using a sqlite db --- models/group/group.go | 8 ++--- models/organization/team_group.go | 4 +-- models/shared/group/org_group.go | 52 +++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 models/shared/group/org_group.go diff --git a/models/group/group.go b/models/group/group.go index 95219c6e77..38108f3495 100644 --- a/models/group/group.go +++ b/models/group/group.go @@ -195,7 +195,7 @@ func ParentGroupCondByRepoID(ctx context.Context, repoID int64, idStr string) bu if err != nil { return builder.In(idStr) } - return ParentGroupCond(idStr, g.ID) + return ParentGroupCond(ctx, idStr, g.ID) } type FindGroupsOptions struct { @@ -313,8 +313,8 @@ func GetParentGroupIDChain(ctx context.Context, groupID int64) (ids []int64, err } // ParentGroupCond returns a condition matching a group and its ancestors -func ParentGroupCond(idStr string, groupID int64) builder.Cond { - groupList, err := GetParentGroupIDChain(db.DefaultContext, groupID) +func ParentGroupCond(ctx context.Context, idStr string, groupID int64) builder.Cond { + groupList, err := GetParentGroupIDChain(ctx, groupID) if err != nil { log.Info("Error building group cond: %w", err) return builder.NotIn(idStr) @@ -331,7 +331,7 @@ func UpdateGroup(ctx context.Context, group *Group) error { func MoveGroup(ctx context.Context, group *Group, newParent int64, newSortOrder int) error { sess := db.GetEngine(ctx) ng, err := GetGroupByID(ctx, newParent) - if err != nil && !IsErrGroupNotExist(err) { + if !IsErrGroupNotExist(err) { return err } if ng != nil { diff --git a/models/organization/team_group.go b/models/organization/team_group.go index 0cdaa742e6..72a8c9790f 100644 --- a/models/organization/team_group.go +++ b/models/organization/team_group.go @@ -10,7 +10,7 @@ import ( func GetTeamsWithAccessToGroup(ctx context.Context, orgID, groupID int64, mode perm.AccessMode) ([]*Team, error) { teams := make([]*Team, 0) - inCond := group_model.ParentGroupCond("group_team.group_id", groupID) + inCond := group_model.ParentGroupCond(ctx, "group_team.group_id", groupID) return teams, db.GetEngine(ctx).Distinct("team.*").Where("group_team.access_mode >= ?", mode). Join("INNER", "group_team", "group_team.team_id = team.id and group_team.org_id = ?", orgID). And("group_team.org_id = ?", orgID). @@ -21,7 +21,7 @@ func GetTeamsWithAccessToGroup(ctx context.Context, orgID, groupID int64, mode p func GetTeamsWithAccessToGroupUnit(ctx context.Context, orgID, groupID int64, mode perm.AccessMode, unitType unit.Type) ([]*Team, error) { teams := make([]*Team, 0) - inCond := group_model.ParentGroupCond("group_team.group_id", groupID) + inCond := group_model.ParentGroupCond(ctx, "group_team.group_id", groupID) return teams, db.GetEngine(ctx).Where("group_team.access_mode >= ?", mode). Join("INNER", "group_team", "group_team.team_id = team.id"). Join("INNER", "group_unit", "group_unit.team_id = team.id"). diff --git a/models/shared/group/org_group.go b/models/shared/group/org_group.go new file mode 100644 index 0000000000..5ae4eeacda --- /dev/null +++ b/models/shared/group/org_group.go @@ -0,0 +1,52 @@ +package group + +import ( + "code.gitea.io/gitea/models/db" + group_model "code.gitea.io/gitea/models/group" + organization_model "code.gitea.io/gitea/models/organization" + user_model "code.gitea.io/gitea/models/user" + "context" + "xorm.io/builder" +) + +// FindGroupMembers finds all users who have access to a group via team membership +func FindGroupMembers(ctx context.Context, groupID int64, opts *organization_model.FindOrgMembersOpts) (user_model.UserList, error) { + cond := builder. + Select("`team_user`.uid"). + From("team_user"). + InnerJoin("org_user", "`org_user`.uid = team_user.uid"). + InnerJoin("group_team", "`group_team`.team_id = team_user.team_id"). + Where(builder.Eq{"`org_user`.org_id": opts.OrgID}). + And(group_model.ParentGroupCond(context.TODO(), "`group_team`.group_id", groupID)) + if opts.PublicOnly() { + cond = cond.And(builder.Eq{"`org_user`.is_public": true}) + } + sess := db.GetEngine(ctx).Where(builder.In("`user`.id", cond)) + if opts.ListOptions.PageSize > 0 { + sess = db.SetSessionPagination(sess, opts) + users := make([]*user_model.User, 0, opts.ListOptions.PageSize) + return users, sess.Find(&users) + } + + var users []*user_model.User + err := sess.Find(&users) + return users, err +} + +func GetGroupTeams(ctx context.Context, groupID int64) (teams []*organization_model.Team, err error) { + err = db.GetEngine(ctx). + Where("`group_team`.group_id = ?", groupID). + Join("INNER", "group_team", "`group_team`.team_id = `team`.id"). + Asc("`team`.name"). + Find(&teams) + return +} + +func IsGroupMember(ctx context.Context, groupID, userID int64) (bool, error) { + return db.GetEngine(ctx). + Where("`group_team`.group_id = ?", groupID). + Join("INNER", "group_team", "`group_team`.team_id = `team_user`.team_id"). + And("`team_user`.uid = ?", userID). + Table("team_user"). + Exist() +}