diff --git a/models/group/group.go b/models/group/group.go index d2c9f19fe0..daacacbebc 100644 --- a/models/group/group.go +++ b/models/group/group.go @@ -97,7 +97,7 @@ func (g *Group) LoadSubgroups(ctx context.Context, recursive bool) error { } func (g *Group) LoadAccessibleSubgroups(ctx context.Context, recursive bool, doer *user_model.User) error { - return g.doLoadSubgroups(ctx, recursive, AccessibleGroupCondition(doer, unit.TypeInvalid, perm.AccessModeRead), 0) + return g.doLoadSubgroups(ctx, recursive, AccessibleGroupCondition(doer, g.OwnerID, unit.TypeInvalid, perm.AccessModeRead), 0) } func (g *Group) LoadAttributes(ctx context.Context) error { @@ -137,7 +137,7 @@ func (g *Group) CanAccess(ctx context.Context, user *user_model.User) (bool, err } func (g *Group) CanAccessAtLevel(ctx context.Context, user *user_model.User, level perm.AccessMode) (bool, error) { - orCond := builder.Or(AccessibleGroupCondition(user, unit.TypeInvalid, level)) + orCond := builder.Or(AccessibleGroupCondition(user, g.OwnerID, unit.TypeInvalid, level)) if level == perm.AccessModeRead { orCond = orCond.Or(builder.Eq{"`repo_group`.visibility": structs.VisibleTypePublic}) } diff --git a/models/group/group_list.go b/models/group/group_list.go index d5f839054a..e514009313 100644 --- a/models/group/group_list.go +++ b/models/group/group_list.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" "xorm.io/builder" @@ -28,67 +29,80 @@ func (groups RepoGroupList) LoadOwners(ctx context.Context) error { return nil } -func universalGroupPermBuilder(idStr string, userID int64) builder.Cond { +func universalGroupPermBuilder(idStr string, userID, orgID int64) builder.Cond { + adminSubquery := builder.Select("1"). + From("user"). + Where(builder.Eq{"`user`.is_admin": true, "`user`.id": userID}) + eqCond := builder.Eq{"`team_user`.uid": userID} teamUserSubquery := builder.Select("`team_user`.uid"). From("team_user"). Join("INNER", "team", "`team`.id = `team_user`.team_id"). - Join("INNER", "`user`", "`user`.`id` = `team`.org_id"). - Join("INNER", "`user` as iu", "iu.`id` = `team_user`.uid"). + Join("LEFT", "`user` as iu", "iu.`id` = `team_user`.uid"). Where( builder.And( - builder.Eq{"`team_user`.uid": userID}, + builder.Eq{"`team`.org_id": orgID}, + eqCond, builder.Or( builder.Gte{"`team`.authorize": perm.AccessModeOwner}, - builder.Eq{"iu.is_admin": true}, - ), - ), + )), ) + teamSubquery := builder.Select("`team`.id").From("`team`"). + Join("LEFT", "team_user", "team.id = team_user.team_id"). + Join("LEFT", "`user` as iu", "iu.`id` = `team_user`.uid"). + Where(builder.And( + builder.Eq{"`team`.org_id": orgID}, + builder.In("`team_user`.uid", teamUserSubquery), + )) sq := builder.Select("`repo_group`.id"). - From("`repo_group`, `team_user`"). - Join("LEFT", "repo_group_team", "`repo_group_team`.group_id = `repo_group`.id"). + From("`repo_group`, team_user"). + Join("LEFT", "team", "`team`.org_id = `repo_group`.owner_id"). Where( - builder.In("`team_user`.uid", teamUserSubquery)) - return builder.In(idStr, sq) + builder.And( + builder.Eq{"`repo_group`.owner_id": orgID}, + builder.And( + builder.In("`team_user`.team_id", teamSubquery), + ))) + return builder.Or(builder.In(idStr, sq), builder.Exists(adminSubquery)) } // userOrgTeamGroupBuilder returns group ids where user's teams can access. -func userOrgTeamGroupBuilder(userID int64) *builder.Builder { +func userOrgTeamGroupBuilder(userID, orgID int64) *builder.Builder { return builder.Select("`repo_group_team`.group_id"). From("repo_group_team"). Join("INNER", "team_user", "`team_user`.team_id = `repo_group_team`.team_id"). - Where(builder.Eq{"`team_user`.uid": userID}) + Where(builder.And(builder.Eq{"`team_user`.uid": userID}, builder.Eq{"`repo_group_team`.org_id": orgID})) } // UserOrgTeamPermCond returns a condition to select ids of groups that a user can access at the level described by `level` -func UserOrgTeamPermCond(idStr string, userID int64, level perm.AccessMode) builder.Cond { - selCond := userOrgTeamGroupBuilder(userID) +func UserOrgTeamPermCond(idStr string, userID, orgID int64, level perm.AccessMode) builder.Cond { + selCond := userOrgTeamGroupBuilder(userID, orgID) selCond = selCond.InnerJoin("team", "`team`.id = `repo_group_team`.team_id"). And(builder.Or(builder.Gte{"`team`.authorize": level}, builder.Gte{"`repo_group_team`.access_mode": level})) return builder.In(idStr, selCond) } // UserOrgTeamGroupCond returns a condition to select ids of groups that a user's team can access -func UserOrgTeamGroupCond(idStr string, userID int64) builder.Cond { - return builder.In(idStr, userOrgTeamGroupBuilder(userID)) +func UserOrgTeamGroupCond(idStr string, userID, orgID int64) builder.Cond { + return builder.In(idStr, userOrgTeamGroupBuilder(userID, orgID)) } // userOrgTeamUnitGroupCond returns a condition to select group ids where user's teams can access the special unit. -func userOrgTeamUnitGroupCond(idStr string, userID int64, unitType unit.Type) builder.Cond { +func userOrgTeamUnitGroupCond(idStr string, userID, orgID int64, unitType unit.Type) builder.Cond { return builder.Or(builder.In( - idStr, userOrgTeamUnitGroupBuilder(userID, unitType))) + idStr, userOrgTeamUnitGroupBuilder(userID, orgID, unitType))) } // userOrgTeamUnitGroupBuilder returns group ids where user's teams can access the special unit. -func userOrgTeamUnitGroupBuilder(userID int64, unitType unit.Type) *builder.Builder { - return userOrgTeamGroupBuilder(userID). - Join("INNER", "team_unit", "`team_unit`.team_id = `team_repo`.team_id"). +func userOrgTeamUnitGroupBuilder(userID, orgID int64, unitType unit.Type) *builder.Builder { + return userOrgTeamGroupBuilder(userID, orgID). + Join("INNER", "team_unit", "`team_unit`.team_id = `repo_group_team`.team_id"). Where(builder.Eq{"`team_unit`.`type`": unitType}). And(builder.Gt{"`team_unit`.`access_mode`": int(perm.AccessModeNone)}) } // AccessibleGroupCondition returns a condition that matches groups which a user can access via the specified unit -func AccessibleGroupCondition(user *user_model.User, unitType unit.Type, minMode perm.AccessMode) builder.Cond { +func AccessibleGroupCondition(user *user_model.User, orgID int64, unitType unit.Type, minMode perm.AccessMode) builder.Cond { cond := builder.NewCond() if user == nil || !user.IsRestricted || user.ID <= 0 { orgVisibilityLimit := []structs.VisibleType{structs.VisibleTypePrivate} @@ -105,15 +119,15 @@ func AccessibleGroupCondition(user *user_model.User, unitType unit.Type, minMode cond = cond.Or(condAnd) } if user != nil { - cond = cond.Or(universalGroupPermBuilder("`repo_group`.id", user.ID)) - cond = cond.Or(UserOrgTeamPermCond("`repo_group`.id", user.ID, minMode)) + cond = cond.Or(universalGroupPermBuilder("`repo_group`.id", user.ID, orgID)) + cond = cond.Or(UserOrgTeamPermCond("`repo_group`.id", user.ID, orgID, minMode)) if unitType == unit.TypeInvalid { cond = cond.Or( - UserOrgTeamGroupCond("`repo_group`.id", user.ID), + UserOrgTeamGroupCond("`repo_group`.id", user.ID, orgID), ) } else { cond = cond.Or( - userOrgTeamUnitGroupCond("`repo_group`.id", user.ID, unitType), + userOrgTeamUnitGroupCond("`repo_group`.id", user.ID, orgID, unitType), ) } } diff --git a/routers/api/v1/org/group.go b/routers/api/v1/org/group.go index 54caa5b43b..6f11ca8733 100644 --- a/routers/api/v1/org/group.go +++ b/routers/api/v1/org/group.go @@ -63,7 +63,7 @@ func GetOrgGroups(ctx *context.APIContext) { ActorID: doerID, OwnerID: org.ID, }, group_model. - AccessibleGroupCondition(ctx.Doer, unit.TypeInvalid, perm.AccessModeRead)) + AccessibleGroupCondition(ctx.Doer, org.ID, unit.TypeInvalid, perm.AccessModeRead)) if err != nil { ctx.APIErrorInternal(err) return diff --git a/services/group/search.go b/services/group/search.go index cc81b9f5c5..b57f176cca 100644 --- a/services/group/search.go +++ b/services/group/search.go @@ -119,7 +119,7 @@ func (w *WebSearchGroup) doLoadChildren(opts *WebSearchOptions) error { w.LatestCommitStatus = latestCommitStatuses[latestIdx] } w.Subgroups = make([]*WebSearchGroup, 0) - groups, err := group_model.FindGroupsByCond(opts.Ctx, opts.GroupOpts, group_model.AccessibleGroupCondition(opts.Actor, unit.TypeInvalid, perm.AccessModeRead)) + groups, err := group_model.FindGroupsByCond(opts.Ctx, opts.GroupOpts, group_model.AccessibleGroupCondition(opts.Actor, opts.OrgID, unit.TypeInvalid, perm.AccessModeRead)) if err != nil { return err }