mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-11 04:55:34 +02:00
refactor: update group model package
- add `CanAccessUnitAtLevel` method to check if a user has access to a unit in a group - update `CanAccessAtLevel` to wrap `CanAccessUnitAtLevel` - update `IsOwnedBy`, `IsAdminOf` and `CanCreateIn` methods to use existing cond builders - update `universalGroupPermBuilder` func to take a flag that dictates whether admin users should be taken into consideration - add `IsPrivateBecauseOfParentPermissions` method that checks if a group should be hidden because its parent is private/inaccessible - add `GetGroupByIDAndCond` method to find a group matching an ID and cond - make `GetGroupByRepoID` more efficient by replacing `where ... in` with join - ensure that `MoveGroup` updates each subgroup's sort order in the new parent - add `GetOwnerByGroupID` func that returns the owner of a group
This commit is contained in:
parent
a8e886822a
commit
dc376ee7e7
@ -137,40 +137,68 @@ 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, g.OwnerID, unit.TypeInvalid, level))
|
||||
return g.CanAccessUnitAtLevel(ctx, user, unit.TypeInvalid, level)
|
||||
}
|
||||
|
||||
func (g *Group) CanAccessUnitAtLevel(ctx context.Context, user *user_model.User, u unit.Type, level perm.AccessMode) (bool, error) {
|
||||
if user != nil {
|
||||
ownedBy, err := g.IsOwnedBy(ctx, user.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if ownedBy {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
orCond := builder.Or(AccessibleGroupCondition(user, g.OwnerID, u, level))
|
||||
if level == perm.AccessModeRead {
|
||||
orCond = orCond.Or(builder.Eq{"`repo_group`.visibility": structs.VisibleTypePublic})
|
||||
}
|
||||
return db.GetEngine(ctx).Table(g.TableName()).Where(orCond.And(builder.Eq{"`repo_group`.id": g.ID})).Exist()
|
||||
return db.GetEngine(ctx).Table(g.TableName()).Where(builder.And(builder.Eq{"`repo_group`.id": g.ID}, orCond)).Exist()
|
||||
}
|
||||
|
||||
func (g *Group) IsOwnedBy(ctx context.Context, userID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
Where("team_user.uid = ?", userID).
|
||||
Join("INNER", "team_user", "team_user.team_id = repo_group_team.team_id").
|
||||
And("repo_group_team.access_mode = ?", perm.AccessModeOwner).
|
||||
And("repo_group_team.group_id = ?", g.ID).
|
||||
Table("repo_group_team").
|
||||
Where(
|
||||
builder.Or(
|
||||
UserOrgTeamPermCond("`repo_group`.id", userID, g.OwnerID, perm.AccessModeOwner),
|
||||
universalGroupPermBuilder("`repo_group`.id", userID, g.OwnerID, false)).
|
||||
And(builder.Eq{"`repo_group`.id": g.ID})).
|
||||
Table(g.TableName()).
|
||||
Exist()
|
||||
}
|
||||
|
||||
func (g *Group) CanCreateIn(ctx context.Context, userID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
Where("team_user.uid = ?", userID).
|
||||
cond := builder.Eq{
|
||||
"team_user.uid": userID,
|
||||
"repo_group_team.group_id": g.ID,
|
||||
"repo_group_team.can_create_in": true,
|
||||
}
|
||||
|
||||
isAdmin, err := g.IsAdminOf(ctx, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
res, err := db.GetEngine(ctx).
|
||||
Join("INNER", "team_user", "team_user.team_id = repo_group_team.team_id").
|
||||
And("repo_group_team.group_id = ?", g.ID).
|
||||
And("repo_group_team.can_create_in = ?", true).
|
||||
Where(cond).
|
||||
Table("repo_group_team").
|
||||
Exist()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return isAdmin || res, nil
|
||||
}
|
||||
|
||||
func (g *Group) IsAdminOf(ctx context.Context, userID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
Where("team_user.uid = ?", userID).
|
||||
Join("INNER", "team_user", "team_user.team_id = repo_group_team.team_id").
|
||||
And("repo_group_team.group_id = ?", g.ID).
|
||||
And("repo_group_team.access_mode >= ?", perm.AccessModeAdmin).
|
||||
Table("repo_group_team").
|
||||
Where(
|
||||
builder.Or(
|
||||
UserOrgTeamPermCond("`repo_group`.id", userID, g.OwnerID, perm.AccessModeAdmin),
|
||||
universalGroupPermBuilder("`repo_group`.id", userID, g.OwnerID, false)).
|
||||
And(builder.Eq{"`repo_group`.id": g.ID})).
|
||||
Table(g.TableName()).
|
||||
Exist()
|
||||
}
|
||||
|
||||
@ -178,10 +206,20 @@ func (g *Group) ShortName(length int) string {
|
||||
return util.EllipsisDisplayString(g.Name, length)
|
||||
}
|
||||
|
||||
func GetGroupByID(ctx context.Context, id int64) (*Group, error) {
|
||||
func (g *Group) IsPrivateBecauseOfParentPermissions(ctx context.Context, user *user_model.User) (bool, error) {
|
||||
cond := AccessibleParentGroupCond(ctx, "`repo_group`.`id`", g.ID, user)
|
||||
has, err := db.GetEngine(ctx).Where(cond.And(builder.Eq{
|
||||
"`repo_group`.id": g.ID,
|
||||
})).Table(g.TableName()).Exist()
|
||||
return !has, err
|
||||
}
|
||||
|
||||
func GetGroupByIDAndCond(ctx context.Context, id int64, cond builder.Cond) (*Group, error) {
|
||||
group := new(Group)
|
||||
|
||||
has, err := db.GetEngine(ctx).ID(id).Get(group)
|
||||
|
||||
has, err := db.GetEngine(ctx).
|
||||
Where(cond.And(builder.Eq{"`repo_group`.id": id})).Get(group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
@ -190,13 +228,15 @@ func GetGroupByID(ctx context.Context, id int64) (*Group, error) {
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func GetGroupByID(ctx context.Context, id int64) (*Group, error) {
|
||||
return GetGroupByIDAndCond(ctx, id, builder.Expr("1 = 1"))
|
||||
}
|
||||
|
||||
func GetGroupByRepoID(ctx context.Context, repoID int64) (*Group, error) {
|
||||
group := new(Group)
|
||||
_, err := db.GetEngine(ctx).
|
||||
In("id", builder.
|
||||
Select("group_id").
|
||||
From("repo").
|
||||
Where(builder.Eq{"id": repoID})).
|
||||
Join("INNER", "repository", "repository.group_id = repo_group.id").
|
||||
Where(builder.Eq{"repository.`id`": repoID}).
|
||||
Get(group)
|
||||
return group, err
|
||||
}
|
||||
@ -324,6 +364,45 @@ func GetParentGroupIDChain(ctx context.Context, groupID int64) ([]int64, error)
|
||||
return ids, err
|
||||
}
|
||||
|
||||
func groupHierarchyCTEBuilder(cond builder.Cond) builder.Cond {
|
||||
firstPart := builder.Select(fmt.Sprintf("repo_group.*"), "1 as depth").
|
||||
From("repo_group").
|
||||
Where(builder.And(builder.Eq{
|
||||
"parent_group_id": 0,
|
||||
}, cond))
|
||||
secondPart := builder.Select("r.*", "h.depth + 1").
|
||||
From("repo_group", "r").
|
||||
Join("INNER", "group_hierarchy h", "r.parent_group_id = h.id")
|
||||
|
||||
firstSql, _ := firstPart.ToBoundSQL()
|
||||
secondSql, _ := secondPart.ToBoundSQL()
|
||||
return builder.Expr(firstSql + " UNION ALL " + secondSql)
|
||||
}
|
||||
|
||||
func AccessibleParentGroupCond(ctx context.Context, idStr string, groupID int64, user *user_model.User) builder.Cond {
|
||||
owner, err := GetOwnerByGroupID(ctx, groupID)
|
||||
if err != nil {
|
||||
return builder.Exists(builder.Select("1 as dummy").Where(builder.Eq{
|
||||
"dummy": 1,
|
||||
}))
|
||||
}
|
||||
accessibleCond := AccessibleGroupCondition(user, owner.ID, unit.TypeInvalid, perm.AccessModeRead)
|
||||
unionBldr := groupHierarchyCTEBuilder(accessibleCond)
|
||||
unionSql, err := builder.ToBoundSQL(unionBldr)
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
s := db.GetEngine(ctx)
|
||||
s.SQL("WITH RECURSIVE group_hierarchy AS ("+unionSql+") SELECT id from group_hierarchy", unionSql)
|
||||
var g []*Group
|
||||
err = s.Find(&g)
|
||||
if err != nil {
|
||||
log.Info("%s", err.Error())
|
||||
}
|
||||
return builder.In(idStr, builder.Expr("(WITH RECURSIVE group_hierarchy AS ("+unionSql+") SELECT id from group_hierarchy)"))
|
||||
//db.GetEngine(ctx).SQL()
|
||||
}
|
||||
|
||||
// ParentGroupCond returns a condition matching a group and its ancestors
|
||||
func ParentGroupCond(ctx context.Context, idStr string, groupID int64) builder.Cond {
|
||||
groupList, err := GetParentGroupIDChain(ctx, groupID)
|
||||
@ -347,20 +426,49 @@ func MoveGroup(ctx context.Context, group *Group, newParent int64, newSortOrder
|
||||
return err
|
||||
}
|
||||
|
||||
var siblings RepoGroupList
|
||||
var tmpSiblings RepoGroupList
|
||||
if ng != nil {
|
||||
if ng.OwnerID != group.OwnerID {
|
||||
return fmt.Errorf("group[%d]'s ownerID is not equal to new parent group[%d]'s owner ID", group.ID, ng.ID)
|
||||
}
|
||||
if err = ng.LoadSubgroups(ctx, false); err != nil {
|
||||
return err
|
||||
}
|
||||
siblings = append(append(ng.Subgroups[0:min(newSortOrder, len(ng.Subgroups))], group), ng.Subgroups[newSortOrder:]...)
|
||||
} else if newParent <= 0 {
|
||||
tmpSiblings, err = FindGroups(ctx, &FindGroupsOptions{
|
||||
OwnerID: group.OwnerID,
|
||||
ParentGroupID: 0,
|
||||
})
|
||||
tmpSiblings2 := make(RepoGroupList, newSortOrder)
|
||||
copy(tmpSiblings2, tmpSiblings[0:newSortOrder])
|
||||
tmpSiblings2 = append(tmpSiblings2, group)
|
||||
|
||||
siblings = append(tmpSiblings2, tmpSiblings[newSortOrder:]...)
|
||||
}
|
||||
parentGroupChain, err := GetParentGroupChain(ctx, newParent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(parentGroupChain) >= 20 {
|
||||
return ErrGroupTooDeep{
|
||||
ID: group.ID,
|
||||
}
|
||||
}
|
||||
|
||||
group.ParentGroupID = newParent
|
||||
group.SortOrder = newSortOrder
|
||||
if _, err = sess.Table(group.TableName()).
|
||||
ID(group.ID).
|
||||
AllCols().
|
||||
Update(group); err != nil {
|
||||
return err
|
||||
for i, gg := range siblings {
|
||||
gg.SortOrder = i
|
||||
if _, err = sess.Table(group.TableName()).
|
||||
ID(gg.ID).
|
||||
AllCols().
|
||||
Update(gg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if group.ParentGroup != nil && newParent != 0 {
|
||||
group.ParentGroup = nil
|
||||
if err = group.LoadParentGroup(ctx); err != nil {
|
||||
@ -369,3 +477,15 @@ func MoveGroup(ctx context.Context, group *Group, newParent int64, newSortOrder
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetOwnerByGroupID(ctx context.Context, groupID int64) (*user_model.User, error) {
|
||||
e := db.GetEngine(ctx)
|
||||
tableName := "repo_group"
|
||||
user := new(user_model.User)
|
||||
has, err := e.Join("INNER", tableName, fmt.Sprintf("`%s`.owner_id = `user`.`id`", tableName)).
|
||||
Where(builder.Eq{fmt.Sprintf("`%s`.id", tableName): groupID}).Get(user)
|
||||
if !has {
|
||||
return nil, user_model.ErrUserNotExist{}
|
||||
}
|
||||
return user, err
|
||||
}
|
||||
|
||||
@ -28,41 +28,35 @@ func (groups RepoGroupList) LoadOwners(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func universalGroupPermBuilder(idStr string, userID, orgID int64) builder.Cond {
|
||||
func universalGroupPermBuilder(idStr string, userID, orgID int64, includeAdmin bool) 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("LEFT", "`user` as iu", "iu.`id` = `team_user`.uid").
|
||||
Where(
|
||||
builder.And(
|
||||
builder.Eq{"`team`.org_id": orgID},
|
||||
eqCond,
|
||||
builder.Or(
|
||||
builder.Gte{"`team`.authorize": perm.AccessModeOwner},
|
||||
)),
|
||||
)
|
||||
|
||||
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),
|
||||
eqCond,
|
||||
builder.Gte{"`team`.authorize": perm.AccessModeOwner},
|
||||
))
|
||||
|
||||
sq := builder.Select("`repo_group`.id").
|
||||
From("`repo_group`, team_user").
|
||||
From("`repo_group`").
|
||||
Join("LEFT", "team", "`team`.org_id = `repo_group`.owner_id").
|
||||
Where(
|
||||
builder.And(
|
||||
builder.Eq{"`repo_group`.owner_id": orgID},
|
||||
builder.And(
|
||||
builder.In("`team_user`.team_id", teamSubquery),
|
||||
builder.In("`team`.id", teamSubquery),
|
||||
)))
|
||||
return builder.Or(builder.In(idStr, sq), builder.Exists(adminSubquery))
|
||||
cond := builder.In(idStr, sq)
|
||||
if includeAdmin {
|
||||
cond = cond.Or(builder.Exists(adminSubquery))
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
// userOrgTeamGroupBuilder returns group ids where user's teams can access.
|
||||
@ -104,9 +98,9 @@ func userOrgTeamUnitGroupBuilder(userID, orgID int64, unitType unit.Type) *build
|
||||
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}
|
||||
orgVisibilityLimit := []int{int(structs.VisibleTypePrivate)}
|
||||
if user == nil || user.ID <= 0 {
|
||||
orgVisibilityLimit = append(orgVisibilityLimit, structs.VisibleTypeLimited)
|
||||
orgVisibilityLimit = append(orgVisibilityLimit, int(structs.VisibleTypeLimited))
|
||||
}
|
||||
condAnd := builder.And(
|
||||
builder.NotIn("`repo_group`.owner_id", builder.Select("`user`.`id`").From("`user`").Where(
|
||||
@ -118,7 +112,7 @@ func AccessibleGroupCondition(user *user_model.User, orgID int64, unitType unit.
|
||||
cond = cond.Or(condAnd)
|
||||
}
|
||||
if user != nil {
|
||||
cond = cond.Or(universalGroupPermBuilder("`repo_group`.id", user.ID, orgID))
|
||||
cond = cond.Or(universalGroupPermBuilder("`repo_group`.id", user.ID, orgID, true))
|
||||
cond = cond.Or(UserOrgTeamPermCond("`repo_group`.id", user.ID, orgID, minMode))
|
||||
if unitType == unit.TypeInvalid {
|
||||
cond = cond.Or(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user