diff --git a/models/group/group_list.go b/models/group/group_list.go index c7710d950b..5715accb5c 100644 --- a/models/group/group_list.go +++ b/models/group/group_list.go @@ -1,17 +1,87 @@ package group 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/structs" "context" + "xorm.io/builder" ) type GroupList []*Group func (groups GroupList) LoadOwners(ctx context.Context) error { for _, g := range groups { - err := g.LoadOwner(ctx) - if err != nil { - return err + if g.Owner == nil { + err := g.LoadOwner(ctx) + if err != nil { + return err + } } } return nil } + +// userOrgTeamGroupBuilder returns group ids where user's teams can access. +func userOrgTeamGroupBuilder(userID int64) *builder.Builder { + return builder.Select("`group_team`.group_id"). + From("group_team"). + Join("INNER", "team_user", "`team_user`.team_id = `group_team`.team_id"). + Where(builder.Eq{"`team_user`.uid": userID}) +} + +// 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)) +} + +// 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 { + return builder.Or(builder.In( + idStr, userOrgTeamUnitGroupBuilder(userID, 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"). + 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) builder.Cond { + cond := builder.NewCond() + if user == nil || !user.IsRestricted || user.ID <= 0 { + orgVisibilityLimit := []structs.VisibleType{structs.VisibleTypePrivate} + if user == nil || user.ID <= 0 { + orgVisibilityLimit = append(orgVisibilityLimit, structs.VisibleTypeLimited) + } + // 1. Be able to see all non-private groups that either: + cond = cond.Or(builder.And( + builder.Eq{"`repo_group`.is_private": false}, + // 2. Aren't in an private organisation or limited organisation if we're not logged in + builder.NotIn("`repo_group`.owner_id", builder.Select("id").From("`user`").Where( + builder.And( + builder.Eq{"type": user_model.UserTypeOrganization}, + builder.In("visibility", orgVisibilityLimit)), + )))) + } + if user != nil { + // 2. Be able to see all repositories that we have unit independent access to + // 3. Be able to see all repositories through team membership(s) + if unitType == unit.TypeInvalid { + // Regardless of UnitType + cond = cond.Or( + UserOrgTeamGroupCond("`repo_group`.id", user.ID), + ) + } else { + // For a specific UnitType + cond = cond.Or( + userOrgTeamUnitGroupCond("`repo_group`.id", user.ID, unitType), + ) + } + } + return cond +}