mirror of
https://github.com/go-gitea/gitea
synced 2024-12-22 16:44:26 +00:00
Move team related functions to service layer (#32537)
There are still some functions under `models` after last big refactor about `models`. This change will move all team related functions to service layer with no code change.
This commit is contained in:
parent
5a50b271e7
commit
f62f68cbdd
@ -197,3 +197,8 @@ func TestUsersInTeamsCount(t *testing.T) {
|
|||||||
test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2) // userid 2,4
|
test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2) // userid 2,4
|
||||||
test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
|
test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsUsableTeamName(t *testing.T) {
|
||||||
|
assert.NoError(t, organization.IsUsableTeamName("usable"))
|
||||||
|
assert.True(t, db.IsErrNameReserved(organization.IsUsableTeamName("new")))
|
||||||
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
@ -15,6 +14,7 @@ import (
|
|||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
)
|
)
|
||||||
|
|
||||||
// listMembers list an organization's members
|
// listMembers list an organization's members
|
||||||
@ -322,7 +322,7 @@ func DeleteMember(ctx *context.APIContext) {
|
|||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := models.RemoveOrgUser(ctx, ctx.Org.Organization, member); err != nil {
|
if err := org_service.RemoveOrgUser(ctx, ctx.Org.Organization, member); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "RemoveOrgUser", err)
|
ctx.Error(http.StatusInternalServerError, "RemoveOrgUser", err)
|
||||||
}
|
}
|
||||||
ctx.Status(http.StatusNoContent)
|
ctx.Status(http.StatusNoContent)
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
activities_model "code.gitea.io/gitea/models/activities"
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
@ -240,7 +239,7 @@ func CreateTeam(ctx *context.APIContext) {
|
|||||||
attachAdminTeamUnits(team)
|
attachAdminTeamUnits(team)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.NewTeam(ctx, team); err != nil {
|
if err := org_service.NewTeam(ctx, team); err != nil {
|
||||||
if organization.IsErrTeamAlreadyExist(err) {
|
if organization.IsErrTeamAlreadyExist(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
} else {
|
} else {
|
||||||
@ -331,7 +330,7 @@ func EditTeam(ctx *context.APIContext) {
|
|||||||
attachAdminTeamUnits(team)
|
attachAdminTeamUnits(team)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil {
|
if err := org_service.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "EditTeam", err)
|
ctx.Error(http.StatusInternalServerError, "EditTeam", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -362,7 +361,7 @@ func DeleteTeam(ctx *context.APIContext) {
|
|||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
if err := models.DeleteTeam(ctx, ctx.Org.Team); err != nil {
|
if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "DeleteTeam", err)
|
ctx.Error(http.StatusInternalServerError, "DeleteTeam", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -496,7 +495,7 @@ func AddTeamMember(ctx *context.APIContext) {
|
|||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := models.AddTeamMember(ctx, ctx.Org.Team, u); err != nil {
|
if err := org_service.AddTeamMember(ctx, ctx.Org.Team, u); err != nil {
|
||||||
if errors.Is(err, user_model.ErrBlockedUser) {
|
if errors.Is(err, user_model.ErrBlockedUser) {
|
||||||
ctx.Error(http.StatusForbidden, "AddTeamMember", err)
|
ctx.Error(http.StatusForbidden, "AddTeamMember", err)
|
||||||
} else {
|
} else {
|
||||||
@ -537,7 +536,7 @@ func RemoveTeamMember(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.RemoveTeamMember(ctx, ctx.Org.Team, u); err != nil {
|
if err := org_service.RemoveTeamMember(ctx, ctx.Org.Team, u); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "RemoveTeamMember", err)
|
ctx.Error(http.StatusInternalServerError, "RemoveTeamMember", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -700,7 +699,7 @@ func AddTeamRepository(ctx *context.APIContext) {
|
|||||||
ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
|
ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := org_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil {
|
if err := repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "TeamAddRepository", err)
|
ctx.Error(http.StatusInternalServerError, "TeamAddRepository", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
org_service "code.gitea.io/gitea/services/org"
|
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -205,7 +204,7 @@ func changeRepoTeam(ctx *context.APIContext, add bool) {
|
|||||||
ctx.Error(http.StatusUnprocessableEntity, "alreadyAdded", fmt.Errorf("team '%s' is already added to repo", team.Name))
|
ctx.Error(http.StatusUnprocessableEntity, "alreadyAdded", fmt.Errorf("team '%s' is already added to repo", team.Name))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = org_service.TeamAddRepository(ctx, team, ctx.Repo.Repository)
|
err = repo_service.TeamAddRepository(ctx, team, ctx.Repo.Repository)
|
||||||
} else {
|
} else {
|
||||||
if !repoHasTeam {
|
if !repoHasTeam {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "notAdded", fmt.Errorf("team '%s' was not added to repo", team.Name))
|
ctx.Error(http.StatusUnprocessableEntity, "notAdded", fmt.Errorf("team '%s' was not added to repo", team.Name))
|
||||||
|
@ -7,7 +7,6 @@ package org
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
@ -15,6 +14,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
shared_user "code.gitea.io/gitea/routers/web/shared/user"
|
shared_user "code.gitea.io/gitea/routers/web/shared/user"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -108,14 +108,14 @@ func MembersAction(ctx *context.Context) {
|
|||||||
ctx.Error(http.StatusNotFound)
|
ctx.Error(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = models.RemoveOrgUser(ctx, org, member)
|
err = org_service.RemoveOrgUser(ctx, org, member)
|
||||||
if organization.IsErrLastOrgOwner(err) {
|
if organization.IsErrLastOrgOwner(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
|
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
|
||||||
ctx.JSONRedirect(ctx.Org.OrgLink + "/members")
|
ctx.JSONRedirect(ctx.Org.OrgLink + "/members")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "leave":
|
case "leave":
|
||||||
err = models.RemoveOrgUser(ctx, org, ctx.Doer)
|
err = org_service.RemoveOrgUser(ctx, org, ctx.Doer)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ctx.Flash.Success(ctx.Tr("form.organization_leave_success", org.DisplayName()))
|
ctx.Flash.Success(ctx.Tr("form.organization_leave_success", org.DisplayName()))
|
||||||
ctx.JSON(http.StatusOK, map[string]any{
|
ctx.JSON(http.StatusOK, map[string]any{
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
@ -78,9 +77,9 @@ func TeamsAction(ctx *context.Context) {
|
|||||||
ctx.Error(http.StatusNotFound)
|
ctx.Error(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = models.AddTeamMember(ctx, ctx.Org.Team, ctx.Doer)
|
err = org_service.AddTeamMember(ctx, ctx.Org.Team, ctx.Doer)
|
||||||
case "leave":
|
case "leave":
|
||||||
err = models.RemoveTeamMember(ctx, ctx.Org.Team, ctx.Doer)
|
err = org_service.RemoveTeamMember(ctx, ctx.Org.Team, ctx.Doer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if org_model.IsErrLastOrgOwner(err) {
|
if org_model.IsErrLastOrgOwner(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
|
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
|
||||||
@ -107,7 +106,7 @@ func TeamsAction(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = models.RemoveTeamMember(ctx, ctx.Org.Team, user)
|
err = org_service.RemoveTeamMember(ctx, ctx.Org.Team, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if org_model.IsErrLastOrgOwner(err) {
|
if org_model.IsErrLastOrgOwner(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
|
ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
|
||||||
@ -162,7 +161,7 @@ func TeamsAction(ctx *context.Context) {
|
|||||||
if ctx.Org.Team.IsMember(ctx, u.ID) {
|
if ctx.Org.Team.IsMember(ctx, u.ID) {
|
||||||
ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
|
ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
|
||||||
} else {
|
} else {
|
||||||
err = models.AddTeamMember(ctx, ctx.Org.Team, u)
|
err = org_service.AddTeamMember(ctx, ctx.Org.Team, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
page = "team"
|
page = "team"
|
||||||
@ -249,13 +248,13 @@ func TeamsRepoAction(ctx *context.Context) {
|
|||||||
ctx.ServerError("GetRepositoryByName", err)
|
ctx.ServerError("GetRepositoryByName", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = org_service.TeamAddRepository(ctx, ctx.Org.Team, repo)
|
err = repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo)
|
||||||
case "remove":
|
case "remove":
|
||||||
err = repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, ctx.FormInt64("repoid"))
|
err = repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, ctx.FormInt64("repoid"))
|
||||||
case "addall":
|
case "addall":
|
||||||
err = models.AddAllRepositories(ctx, ctx.Org.Team)
|
err = repo_service.AddAllRepositoriesToTeam(ctx, ctx.Org.Team)
|
||||||
case "removeall":
|
case "removeall":
|
||||||
err = models.RemoveAllRepositories(ctx, ctx.Org.Team)
|
err = repo_service.RemoveAllRepositoriesFromTeam(ctx, ctx.Org.Team)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -358,7 +357,7 @@ func NewTeamPost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.NewTeam(ctx, t); err != nil {
|
if err := org_service.NewTeam(ctx, t); err != nil {
|
||||||
ctx.Data["Err_TeamName"] = true
|
ctx.Data["Err_TeamName"] = true
|
||||||
switch {
|
switch {
|
||||||
case org_model.IsErrTeamAlreadyExist(err):
|
case org_model.IsErrTeamAlreadyExist(err):
|
||||||
@ -536,7 +535,7 @@ func EditTeamPost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.UpdateTeam(ctx, t, isAuthChanged, isIncludeAllChanged); err != nil {
|
if err := org_service.UpdateTeam(ctx, t, isAuthChanged, isIncludeAllChanged); err != nil {
|
||||||
ctx.Data["Err_TeamName"] = true
|
ctx.Data["Err_TeamName"] = true
|
||||||
switch {
|
switch {
|
||||||
case org_model.IsErrTeamAlreadyExist(err):
|
case org_model.IsErrTeamAlreadyExist(err):
|
||||||
@ -551,7 +550,7 @@ func EditTeamPost(ctx *context.Context) {
|
|||||||
|
|
||||||
// DeleteTeam response for the delete team request
|
// DeleteTeam response for the delete team request
|
||||||
func DeleteTeam(ctx *context.Context) {
|
func DeleteTeam(ctx *context.Context) {
|
||||||
if err := models.DeleteTeam(ctx, ctx.Org.Team); err != nil {
|
if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil {
|
||||||
ctx.Flash.Error("DeleteTeam: " + err.Error())
|
ctx.Flash.Error("DeleteTeam: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Success(ctx.Tr("org.teams.delete_team_success"))
|
ctx.Flash.Success(ctx.Tr("org.teams.delete_team_success"))
|
||||||
@ -593,7 +592,7 @@ func TeamInvitePost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.AddTeamMember(ctx, team, ctx.Doer); err != nil {
|
if err := org_service.AddTeamMember(ctx, team, ctx.Doer); err != nil {
|
||||||
ctx.ServerError("AddTeamMember", err)
|
ctx.ServerError("AddTeamMember", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/mailer"
|
"code.gitea.io/gitea/services/mailer"
|
||||||
org_service "code.gitea.io/gitea/services/org"
|
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -185,7 +184,7 @@ func AddTeamPost(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = org_service.TeamAddRepository(ctx, team, ctx.Repo.Repository); err != nil {
|
if err = repo_service.TeamAddRepository(ctx, team, ctx.Repo.Repository); err != nil {
|
||||||
ctx.ServerError("TeamAddRepository", err)
|
ctx.ServerError("TeamAddRepository", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
)
|
)
|
||||||
|
|
||||||
type syncType int
|
type syncType int
|
||||||
@ -100,12 +100,12 @@ func syncGroupsToTeamsCached(ctx context.Context, user *user_model.User, orgTeam
|
|||||||
}
|
}
|
||||||
|
|
||||||
if action == syncAdd && !isMember {
|
if action == syncAdd && !isMember {
|
||||||
if err := models.AddTeamMember(ctx, team, user); err != nil {
|
if err := org_service.AddTeamMember(ctx, team, user); err != nil {
|
||||||
log.Error("group sync: Could not add user to team: %v", err)
|
log.Error("group sync: Could not add user to team: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if action == syncRemove && isMember {
|
} else if action == syncRemove && isMember {
|
||||||
if err := models.RemoveTeamMember(ctx, team, user); err != nil {
|
if err := org_service.RemoveTeamMember(ctx, team, user); err != nil {
|
||||||
log.Error("group sync: Could not remove user from team: %v", err)
|
log.Error("group sync: Could not remove user from team: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@ package doctor
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
@ -29,7 +29,7 @@ func fixOwnerTeamCreateOrgRepo(ctx context.Context, logger log.Logger, autofix b
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return models.UpdateTeam(ctx, team, false, false)
|
return org_service.UpdateTeam(ctx, team, false, false)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package org
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/models/organization"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TeamAddRepository adds new repository to team of organization.
|
|
||||||
func TeamAddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
|
|
||||||
if repo.OwnerID != t.OrgID {
|
|
||||||
return errors.New("repository does not belong to organization")
|
|
||||||
} else if organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
|
||||||
return models.AddRepository(ctx, t, repo)
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,8 +1,7 @@
|
|||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package models
|
package org
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -19,138 +18,11 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
|
|
||||||
if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil {
|
|
||||||
return fmt.Errorf("update team: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.NumRepos++
|
|
||||||
|
|
||||||
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
|
|
||||||
return fmt.Errorf("recalculateAccesses: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make all team members watch this repo if enabled in global settings
|
|
||||||
if setting.Service.AutoWatchNewRepos {
|
|
||||||
if err = t.LoadMembers(ctx); err != nil {
|
|
||||||
return fmt.Errorf("getMembers: %w", err)
|
|
||||||
}
|
|
||||||
for _, u := range t.Members {
|
|
||||||
if err = repo_model.WatchRepo(ctx, u, repo, true); err != nil {
|
|
||||||
return fmt.Errorf("watchRepo: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// addAllRepositories adds all repositories to the team.
|
|
||||||
// If the team already has some repositories they will be left unchanged.
|
|
||||||
func addAllRepositories(ctx context.Context, t *organization.Team) error {
|
|
||||||
orgRepos, err := organization.GetOrgRepositories(ctx, t.OrgID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("get org repos: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, repo := range orgRepos {
|
|
||||||
if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
|
|
||||||
if err := AddRepository(ctx, t, repo); err != nil {
|
|
||||||
return fmt.Errorf("AddRepository: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddAllRepositories adds all repositories to the team
|
|
||||||
func AddAllRepositories(ctx context.Context, t *organization.Team) (err error) {
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = addAllRepositories(ctx, t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveAllRepositories removes all repositories from team and recalculates access
|
|
||||||
func RemoveAllRepositories(ctx context.Context, t *organization.Team) (err error) {
|
|
||||||
if t.IncludesAllRepositories {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = removeAllRepositories(ctx, t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeAllRepositories removes all repositories from team and recalculates access
|
|
||||||
// Note: Shall not be called if team includes all repositories
|
|
||||||
func removeAllRepositories(ctx context.Context, t *organization.Team) (err error) {
|
|
||||||
e := db.GetEngine(ctx)
|
|
||||||
// Delete all accesses.
|
|
||||||
for _, repo := range t.Repos {
|
|
||||||
if err := access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove watches from all users and now unaccessible repos
|
|
||||||
for _, user := range t.Members {
|
|
||||||
has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all IssueWatches a user has subscribed to in the repositories
|
|
||||||
if err = issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete team-repo
|
|
||||||
if _, err := e.
|
|
||||||
Where("team_id=?", t.ID).
|
|
||||||
Delete(new(organization.TeamRepo)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
t.NumRepos = 0
|
|
||||||
if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTeam creates a record of new team.
|
// NewTeam creates a record of new team.
|
||||||
// It's caller's responsibility to assign organization ID.
|
// It's caller's responsibility to assign organization ID.
|
||||||
func NewTeam(ctx context.Context, t *organization.Team) (err error) {
|
func NewTeam(ctx context.Context, t *organization.Team) (err error) {
|
||||||
@ -204,7 +76,7 @@ func NewTeam(ctx context.Context, t *organization.Team) (err error) {
|
|||||||
|
|
||||||
// Add all repositories to the team if it has access to all of them.
|
// Add all repositories to the team if it has access to all of them.
|
||||||
if t.IncludesAllRepositories {
|
if t.IncludesAllRepositories {
|
||||||
err = addAllRepositories(ctx, t)
|
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("addAllRepositories: %w", err)
|
return fmt.Errorf("addAllRepositories: %w", err)
|
||||||
}
|
}
|
||||||
@ -282,7 +154,7 @@ func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeA
|
|||||||
|
|
||||||
// Add all repositories to the team if it has access to all of them.
|
// Add all repositories to the team if it has access to all of them.
|
||||||
if includeAllChanged && t.IncludesAllRepositories {
|
if includeAllChanged && t.IncludesAllRepositories {
|
||||||
err = addAllRepositories(ctx, t)
|
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("addAllRepositories: %w", err)
|
return fmt.Errorf("addAllRepositories: %w", err)
|
||||||
}
|
}
|
||||||
@ -324,10 +196,8 @@ func DeleteTeam(ctx context.Context, t *organization.Team) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !t.IncludesAllRepositories {
|
if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil {
|
||||||
if err := removeAllRepositories(ctx, t); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.DeleteBeans(ctx,
|
if err := db.DeleteBeans(ctx,
|
||||||
@ -486,12 +356,12 @@ func removeTeamMember(ctx context.Context, team *organization.Team, user *user_m
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove watches from now unaccessible
|
// Remove watches from now unaccessible
|
||||||
if err := ReconsiderWatches(ctx, repo, user); err != nil {
|
if err := repo_service.ReconsiderWatches(ctx, repo, user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove issue assignments from now unaccessible
|
// Remove issue assignments from now unaccessible
|
||||||
if err := ReconsiderRepoIssuesAssignee(ctx, repo, user); err != nil {
|
if err := repo_service.ReconsiderRepoIssuesAssignee(ctx, repo, user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -529,28 +399,3 @@ func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_m
|
|||||||
}
|
}
|
||||||
return committer.Commit()
|
return committer.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
|
||||||
if canAssigned, err := access_model.CanBeAssigned(ctx, user, repo, true); err != nil || canAssigned {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.GetEngine(ctx).Where(builder.Eq{"assignee_id": user.ID}).
|
|
||||||
In("issue_id", builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repo.ID})).
|
|
||||||
Delete(&issues_model.IssueAssignees{}); err != nil {
|
|
||||||
return fmt.Errorf("Could not delete assignee[%d] %w", user.ID, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
|
||||||
if has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo); err != nil || has {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all IssueWatches a user has subscribed to in the repository
|
|
||||||
return issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID)
|
|
||||||
}
|
|
@ -1,9 +1,10 @@
|
|||||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package models
|
package org
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -14,6 +15,8 @@ import (
|
|||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -60,11 +63,6 @@ func TestTeam_RemoveMember(t *testing.T) {
|
|||||||
assert.True(t, organization.IsErrLastOrgOwner(err))
|
assert.True(t, organization.IsErrLastOrgOwner(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsUsableTeamName(t *testing.T) {
|
|
||||||
assert.NoError(t, organization.IsUsableTeamName("usable"))
|
|
||||||
assert.True(t, db.IsErrNameReserved(organization.IsUsableTeamName("new")))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewTeam(t *testing.T) {
|
func TestNewTeam(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
@ -185,3 +183,132 @@ func TestRepository_RecalculateAccesses3(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, has)
|
assert.True(t, has)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIncludesAllRepositoriesTeams(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
testTeamRepositories := func(teamID int64, repoIDs []int64) {
|
||||||
|
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
|
||||||
|
assert.NoError(t, team.LoadRepositories(db.DefaultContext), "%s: GetRepositories", team.Name)
|
||||||
|
assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
|
||||||
|
assert.Len(t, team.Repos, len(repoIDs), "%s: repo count", team.Name)
|
||||||
|
for i, rid := range repoIDs {
|
||||||
|
if rid > 0 {
|
||||||
|
assert.True(t, repo_service.HasRepository(db.DefaultContext, team, rid), "%s: HasRepository(%d) %d", rid, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an admin user.
|
||||||
|
user, err := user_model.GetUserByID(db.DefaultContext, 1)
|
||||||
|
assert.NoError(t, err, "GetUserByID")
|
||||||
|
|
||||||
|
// Create org.
|
||||||
|
org := &organization.Organization{
|
||||||
|
Name: "All_repo",
|
||||||
|
IsActive: true,
|
||||||
|
Type: user_model.UserTypeOrganization,
|
||||||
|
Visibility: structs.VisibleTypePublic,
|
||||||
|
}
|
||||||
|
assert.NoError(t, organization.CreateOrganization(db.DefaultContext, org, user), "CreateOrganization")
|
||||||
|
|
||||||
|
// Check Owner team.
|
||||||
|
ownerTeam, err := org.GetOwnerTeam(db.DefaultContext)
|
||||||
|
assert.NoError(t, err, "GetOwnerTeam")
|
||||||
|
assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories")
|
||||||
|
|
||||||
|
// Create repos.
|
||||||
|
repoIDs := make([]int64, 0)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
r, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), repo_service.CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
|
||||||
|
assert.NoError(t, err, "CreateRepository %d", i)
|
||||||
|
if r != nil {
|
||||||
|
repoIDs = append(repoIDs, r.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get fresh copy of Owner team after creating repos.
|
||||||
|
ownerTeam, err = org.GetOwnerTeam(db.DefaultContext)
|
||||||
|
assert.NoError(t, err, "GetOwnerTeam")
|
||||||
|
|
||||||
|
// Create teams and check repositories.
|
||||||
|
teams := []*organization.Team{
|
||||||
|
ownerTeam,
|
||||||
|
{
|
||||||
|
OrgID: org.ID,
|
||||||
|
Name: "team one",
|
||||||
|
AccessMode: perm.AccessModeRead,
|
||||||
|
IncludesAllRepositories: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OrgID: org.ID,
|
||||||
|
Name: "team 2",
|
||||||
|
AccessMode: perm.AccessModeRead,
|
||||||
|
IncludesAllRepositories: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OrgID: org.ID,
|
||||||
|
Name: "team three",
|
||||||
|
AccessMode: perm.AccessModeWrite,
|
||||||
|
IncludesAllRepositories: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OrgID: org.ID,
|
||||||
|
Name: "team 4",
|
||||||
|
AccessMode: perm.AccessModeWrite,
|
||||||
|
IncludesAllRepositories: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
teamRepos := [][]int64{
|
||||||
|
repoIDs,
|
||||||
|
repoIDs,
|
||||||
|
{},
|
||||||
|
repoIDs,
|
||||||
|
{},
|
||||||
|
}
|
||||||
|
for i, team := range teams {
|
||||||
|
if i > 0 { // first team is Owner.
|
||||||
|
assert.NoError(t, NewTeam(db.DefaultContext, team), "%s: NewTeam", team.Name)
|
||||||
|
}
|
||||||
|
testTeamRepositories(team.ID, teamRepos[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update teams and check repositories.
|
||||||
|
teams[3].IncludesAllRepositories = false
|
||||||
|
teams[4].IncludesAllRepositories = true
|
||||||
|
teamRepos[4] = repoIDs
|
||||||
|
for i, team := range teams {
|
||||||
|
assert.NoError(t, UpdateTeam(db.DefaultContext, team, false, true), "%s: UpdateTeam", team.Name)
|
||||||
|
testTeamRepositories(team.ID, teamRepos[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create repo and check teams repositories.
|
||||||
|
r, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), repo_service.CreateRepoOptions{Name: "repo-last"})
|
||||||
|
assert.NoError(t, err, "CreateRepository last")
|
||||||
|
if r != nil {
|
||||||
|
repoIDs = append(repoIDs, r.ID)
|
||||||
|
}
|
||||||
|
teamRepos[0] = repoIDs
|
||||||
|
teamRepos[1] = repoIDs
|
||||||
|
teamRepos[4] = repoIDs
|
||||||
|
for i, team := range teams {
|
||||||
|
testTeamRepositories(team.ID, teamRepos[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove repo and check teams repositories.
|
||||||
|
assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repoIDs[0]), "DeleteRepository")
|
||||||
|
teamRepos[0] = repoIDs[1:]
|
||||||
|
teamRepos[1] = repoIDs[1:]
|
||||||
|
teamRepos[3] = repoIDs[1:3]
|
||||||
|
teamRepos[4] = repoIDs[1:]
|
||||||
|
for i, team := range teams {
|
||||||
|
testTeamRepositories(team.ID, teamRepos[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wipe created items.
|
||||||
|
for i, rid := range repoIDs {
|
||||||
|
if i > 0 { // first repo already deleted.
|
||||||
|
assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package models
|
package org
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package models
|
package org
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
@ -6,9 +6,10 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
@ -94,14 +95,39 @@ func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, colla
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = models.ReconsiderWatches(ctx, repo, collaborator); err != nil {
|
if err = ReconsiderWatches(ctx, repo, collaborator); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unassign a user from any issue (s)he has been assigned to in the repository
|
// Unassign a user from any issue (s)he has been assigned to in the repository
|
||||||
if err := models.ReconsiderRepoIssuesAssignee(ctx, repo, collaborator); err != nil {
|
if err := ReconsiderRepoIssuesAssignee(ctx, repo, collaborator); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return committer.Commit()
|
return committer.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
||||||
|
if canAssigned, err := access_model.CanBeAssigned(ctx, user, repo, true); err != nil || canAssigned {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.GetEngine(ctx).Where(builder.Eq{"assignee_id": user.ID}).
|
||||||
|
In("issue_id", builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repo.ID})).
|
||||||
|
Delete(&issues_model.IssueAssignees{}); err != nil {
|
||||||
|
return fmt.Errorf("Could not delete assignee[%d] %w", user.ID, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
||||||
|
if has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo); err != nil || has {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all IssueWatches a user has subscribed to in the repository
|
||||||
|
return issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID)
|
||||||
|
}
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
@ -448,7 +447,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
|
|||||||
}
|
}
|
||||||
for _, t := range teams {
|
for _, t := range teams {
|
||||||
if t.IncludesAllRepositories {
|
if t.IncludesAllRepositories {
|
||||||
if err := models.AddRepository(ctx, t, repo); err != nil {
|
if err := addRepositoryToTeam(ctx, t, repo); err != nil {
|
||||||
return fmt.Errorf("AddRepository: %w", err)
|
return fmt.Errorf("AddRepository: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,148 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/models/organization"
|
|
||||||
"code.gitea.io/gitea/models/perm"
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
|
||||||
"code.gitea.io/gitea/modules/structs"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIncludesAllRepositoriesTeams(t *testing.T) {
|
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
|
||||||
|
|
||||||
testTeamRepositories := func(teamID int64, repoIDs []int64) {
|
|
||||||
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
|
|
||||||
assert.NoError(t, team.LoadRepositories(db.DefaultContext), "%s: GetRepositories", team.Name)
|
|
||||||
assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
|
|
||||||
assert.Len(t, team.Repos, len(repoIDs), "%s: repo count", team.Name)
|
|
||||||
for i, rid := range repoIDs {
|
|
||||||
if rid > 0 {
|
|
||||||
assert.True(t, HasRepository(db.DefaultContext, team, rid), "%s: HasRepository(%d) %d", rid, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get an admin user.
|
|
||||||
user, err := user_model.GetUserByID(db.DefaultContext, 1)
|
|
||||||
assert.NoError(t, err, "GetUserByID")
|
|
||||||
|
|
||||||
// Create org.
|
|
||||||
org := &organization.Organization{
|
|
||||||
Name: "All_repo",
|
|
||||||
IsActive: true,
|
|
||||||
Type: user_model.UserTypeOrganization,
|
|
||||||
Visibility: structs.VisibleTypePublic,
|
|
||||||
}
|
|
||||||
assert.NoError(t, organization.CreateOrganization(db.DefaultContext, org, user), "CreateOrganization")
|
|
||||||
|
|
||||||
// Check Owner team.
|
|
||||||
ownerTeam, err := org.GetOwnerTeam(db.DefaultContext)
|
|
||||||
assert.NoError(t, err, "GetOwnerTeam")
|
|
||||||
assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories")
|
|
||||||
|
|
||||||
// Create repos.
|
|
||||||
repoIDs := make([]int64, 0)
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
|
|
||||||
assert.NoError(t, err, "CreateRepository %d", i)
|
|
||||||
if r != nil {
|
|
||||||
repoIDs = append(repoIDs, r.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get fresh copy of Owner team after creating repos.
|
|
||||||
ownerTeam, err = org.GetOwnerTeam(db.DefaultContext)
|
|
||||||
assert.NoError(t, err, "GetOwnerTeam")
|
|
||||||
|
|
||||||
// Create teams and check repositories.
|
|
||||||
teams := []*organization.Team{
|
|
||||||
ownerTeam,
|
|
||||||
{
|
|
||||||
OrgID: org.ID,
|
|
||||||
Name: "team one",
|
|
||||||
AccessMode: perm.AccessModeRead,
|
|
||||||
IncludesAllRepositories: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
OrgID: org.ID,
|
|
||||||
Name: "team 2",
|
|
||||||
AccessMode: perm.AccessModeRead,
|
|
||||||
IncludesAllRepositories: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
OrgID: org.ID,
|
|
||||||
Name: "team three",
|
|
||||||
AccessMode: perm.AccessModeWrite,
|
|
||||||
IncludesAllRepositories: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
OrgID: org.ID,
|
|
||||||
Name: "team 4",
|
|
||||||
AccessMode: perm.AccessModeWrite,
|
|
||||||
IncludesAllRepositories: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
teamRepos := [][]int64{
|
|
||||||
repoIDs,
|
|
||||||
repoIDs,
|
|
||||||
{},
|
|
||||||
repoIDs,
|
|
||||||
{},
|
|
||||||
}
|
|
||||||
for i, team := range teams {
|
|
||||||
if i > 0 { // first team is Owner.
|
|
||||||
assert.NoError(t, models.NewTeam(db.DefaultContext, team), "%s: NewTeam", team.Name)
|
|
||||||
}
|
|
||||||
testTeamRepositories(team.ID, teamRepos[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update teams and check repositories.
|
|
||||||
teams[3].IncludesAllRepositories = false
|
|
||||||
teams[4].IncludesAllRepositories = true
|
|
||||||
teamRepos[4] = repoIDs
|
|
||||||
for i, team := range teams {
|
|
||||||
assert.NoError(t, models.UpdateTeam(db.DefaultContext, team, false, true), "%s: UpdateTeam", team.Name)
|
|
||||||
testTeamRepositories(team.ID, teamRepos[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create repo and check teams repositories.
|
|
||||||
r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: "repo-last"})
|
|
||||||
assert.NoError(t, err, "CreateRepository last")
|
|
||||||
if r != nil {
|
|
||||||
repoIDs = append(repoIDs, r.ID)
|
|
||||||
}
|
|
||||||
teamRepos[0] = repoIDs
|
|
||||||
teamRepos[1] = repoIDs
|
|
||||||
teamRepos[4] = repoIDs
|
|
||||||
for i, team := range teams {
|
|
||||||
testTeamRepositories(team.ID, teamRepos[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove repo and check teams repositories.
|
|
||||||
assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, repoIDs[0]), "DeleteRepository")
|
|
||||||
teamRepos[0] = repoIDs[1:]
|
|
||||||
teamRepos[1] = repoIDs[1:]
|
|
||||||
teamRepos[3] = repoIDs[1:3]
|
|
||||||
teamRepos[4] = repoIDs[1:]
|
|
||||||
for i, team := range teams {
|
|
||||||
testTeamRepositories(team.ID, teamRepos[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wipe created items.
|
|
||||||
for i, rid := range repoIDs {
|
|
||||||
if i > 0 { // first repo already deleted.
|
|
||||||
assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
|
|
||||||
}
|
|
@ -348,87 +348,6 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeRepositoryFromTeam removes a repository from a team and recalculates access
|
|
||||||
// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
|
|
||||||
func removeRepositoryFromTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository, recalculate bool) (err error) {
|
|
||||||
e := db.GetEngine(ctx)
|
|
||||||
if err = organization.RemoveTeamRepo(ctx, t.ID, repo.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
t.NumRepos--
|
|
||||||
if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't need to recalculate when delete a repository from organization.
|
|
||||||
if recalculate {
|
|
||||||
if err = access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{
|
|
||||||
TeamID: t.ID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("GetTeamMembers: %w", err)
|
|
||||||
}
|
|
||||||
for _, member := range teamMembers {
|
|
||||||
has, err := access_model.HasAnyUnitAccess(ctx, member.ID, repo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = repo_model.WatchRepo(ctx, member, repo, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all IssueWatches a user has subscribed to in the repositories
|
|
||||||
if err := issues_model.RemoveIssueWatchersByRepoID(ctx, member.ID, repo.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasRepository returns true if given repository belong to team.
|
|
||||||
func HasRepository(ctx context.Context, t *organization.Team, repoID int64) bool {
|
|
||||||
return organization.HasTeamRepo(ctx, t.OrgID, t.ID, repoID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveRepositoryFromTeam removes repository from team of organization.
|
|
||||||
// If the team shall include all repositories the request is ignored.
|
|
||||||
func RemoveRepositoryFromTeam(ctx context.Context, t *organization.Team, repoID int64) error {
|
|
||||||
if !HasRepository(ctx, t, repoID) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.IncludesAllRepositories {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = removeRepositoryFromTeam(ctx, t, repo, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteOwnerRepositoriesDirectly calls DeleteRepositoryDirectly for all repos of the given owner
|
// DeleteOwnerRepositoriesDirectly calls DeleteRepositoryDirectly for all repos of the given owner
|
||||||
func DeleteOwnerRepositoriesDirectly(ctx context.Context, owner *user_model.User) error {
|
func DeleteOwnerRepositoriesDirectly(ctx context.Context, owner *user_model.User) error {
|
||||||
for {
|
for {
|
||||||
|
226
services/repository/repo_team.go
Normal file
226
services/repository/repo_team.go
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
"code.gitea.io/gitea/models/organization"
|
||||||
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TeamAddRepository adds new repository to team of organization.
|
||||||
|
func TeamAddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
|
||||||
|
if repo.OwnerID != t.OrgID {
|
||||||
|
return errors.New("repository does not belong to organization")
|
||||||
|
} else if organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
return addRepositoryToTeam(ctx, t, repo)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func addRepositoryToTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
|
||||||
|
if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil {
|
||||||
|
return fmt.Errorf("update team: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.NumRepos++
|
||||||
|
|
||||||
|
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
|
||||||
|
return fmt.Errorf("recalculateAccesses: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make all team members watch this repo if enabled in global settings
|
||||||
|
if setting.Service.AutoWatchNewRepos {
|
||||||
|
if err = t.LoadMembers(ctx); err != nil {
|
||||||
|
return fmt.Errorf("getMembers: %w", err)
|
||||||
|
}
|
||||||
|
for _, u := range t.Members {
|
||||||
|
if err = repo_model.WatchRepo(ctx, u, repo, true); err != nil {
|
||||||
|
return fmt.Errorf("watchRepo: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAllRepositoriesToTeam adds all repositories to the team.
|
||||||
|
// If the team already has some repositories they will be left unchanged.
|
||||||
|
func AddAllRepositoriesToTeam(ctx context.Context, t *organization.Team) error {
|
||||||
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
orgRepos, err := organization.GetOrgRepositories(ctx, t.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get org repos: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, repo := range orgRepos {
|
||||||
|
if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
|
||||||
|
if err := addRepositoryToTeam(ctx, t, repo); err != nil {
|
||||||
|
return fmt.Errorf("AddRepository: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAllRepositoriesFromTeam removes all repositories from team and recalculates access
|
||||||
|
func RemoveAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (err error) {
|
||||||
|
if t.IncludesAllRepositories {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer committer.Close()
|
||||||
|
|
||||||
|
if err = removeAllRepositoriesFromTeam(ctx, t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return committer.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeAllRepositoriesFromTeam removes all repositories from team and recalculates access
|
||||||
|
// Note: Shall not be called if team includes all repositories
|
||||||
|
func removeAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (err error) {
|
||||||
|
e := db.GetEngine(ctx)
|
||||||
|
// Delete all accesses.
|
||||||
|
for _, repo := range t.Repos {
|
||||||
|
if err := access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove watches from all users and now unaccessible repos
|
||||||
|
for _, user := range t.Members {
|
||||||
|
has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if has {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all IssueWatches a user has subscribed to in the repositories
|
||||||
|
if err = issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete team-repo
|
||||||
|
if _, err := e.
|
||||||
|
Where("team_id=?", t.ID).
|
||||||
|
Delete(new(organization.TeamRepo)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.NumRepos = 0
|
||||||
|
if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRepositoryFromTeam removes repository from team of organization.
|
||||||
|
// If the team shall include all repositories the request is ignored.
|
||||||
|
func RemoveRepositoryFromTeam(ctx context.Context, t *organization.Team, repoID int64) error {
|
||||||
|
if !HasRepository(ctx, t, repoID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.IncludesAllRepositories {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer committer.Close()
|
||||||
|
|
||||||
|
if err = removeRepositoryFromTeam(ctx, t, repo, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return committer.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeRepositoryFromTeam removes a repository from a team and recalculates access
|
||||||
|
// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
|
||||||
|
func removeRepositoryFromTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository, recalculate bool) (err error) {
|
||||||
|
e := db.GetEngine(ctx)
|
||||||
|
if err = organization.RemoveTeamRepo(ctx, t.ID, repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.NumRepos--
|
||||||
|
if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't need to recalculate when delete a repository from organization.
|
||||||
|
if recalculate {
|
||||||
|
if err = access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{
|
||||||
|
TeamID: t.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetTeamMembers: %w", err)
|
||||||
|
}
|
||||||
|
for _, member := range teamMembers {
|
||||||
|
has, err := access_model.HasAnyUnitAccess(ctx, member.ID, repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if has {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = repo_model.WatchRepo(ctx, member, repo, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all IssueWatches a user has subscribed to in the repositories
|
||||||
|
if err := issues_model.RemoveIssueWatchersByRepoID(ctx, member.ID, repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasRepository returns true if given repository belong to team.
|
||||||
|
func HasRepository(ctx context.Context, t *organization.Team, repoID int64) bool {
|
||||||
|
return organization.HasTeamRepo(ctx, t.OrgID, t.ID, repoID)
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package org
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
@ -59,7 +59,7 @@ func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, rep
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, team := range teams {
|
for _, team := range teams {
|
||||||
if err := models.AddRepository(ctx, team, newRepo); err != nil {
|
if err := addRepositoryToTeam(ctx, team, newRepo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,7 +205,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
|
|||||||
}
|
}
|
||||||
for _, t := range teams {
|
for _, t := range teams {
|
||||||
if t.IncludesAllRepositories {
|
if t.IncludesAllRepositories {
|
||||||
if err := models.AddRepository(ctx, t, repo); err != nil {
|
if err := addRepositoryToTeam(ctx, t, repo); err != nil {
|
||||||
return fmt.Errorf("AddRepository: %w", err)
|
return fmt.Errorf("AddRepository: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
for _, org := range orgs {
|
for _, org := range orgs {
|
||||||
if err := models.RemoveOrgUser(ctx, org, u); err != nil {
|
if err := org_service.RemoveOrgUser(ctx, org, u); err != nil {
|
||||||
if organization.IsErrLastOrgOwner(err) {
|
if organization.IsErrLastOrgOwner(err) {
|
||||||
err = org_service.DeleteOrganization(ctx, org, true)
|
err = org_service.DeleteOrganization(ctx, org, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -44,7 +45,7 @@ func TestDeleteUser(t *testing.T) {
|
|||||||
assert.NoError(t, db.GetEngine(db.DefaultContext).Find(&orgUsers, &organization.OrgUser{UID: userID}))
|
assert.NoError(t, db.GetEngine(db.DefaultContext).Find(&orgUsers, &organization.OrgUser{UID: userID}))
|
||||||
for _, orgUser := range orgUsers {
|
for _, orgUser := range orgUsers {
|
||||||
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: orgUser.OrgID})
|
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: orgUser.OrgID})
|
||||||
if err := models.RemoveOrgUser(db.DefaultContext, org, user); err != nil {
|
if err := org_service.RemoveOrgUser(db.DefaultContext, org, user); err != nil {
|
||||||
assert.True(t, organization.IsErrLastOrgOwner(err))
|
assert.True(t, organization.IsErrLastOrgOwner(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,13 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -37,7 +37,7 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
|
|||||||
|
|
||||||
ownerTeam1, err := org_model.OrgFromUser(limitedOrg).GetOwnerTeam(db.DefaultContext)
|
ownerTeam1, err := org_model.OrgFromUser(limitedOrg).GetOwnerTeam(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, models.AddTeamMember(db.DefaultContext, ownerTeam1, user1))
|
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam1, user1))
|
||||||
user1Token := getTokenForLoggedInUser(t, user1Sess, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteOrganization)
|
user1Token := getTokenForLoggedInUser(t, user1Sess, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteOrganization)
|
||||||
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/forks", &api.CreateForkOption{
|
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/forks", &api.CreateForkOption{
|
||||||
Organization: &limitedOrg.Name,
|
Organization: &limitedOrg.Name,
|
||||||
@ -52,7 +52,7 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
|
|||||||
|
|
||||||
ownerTeam2, err := org_model.OrgFromUser(privateOrg).GetOwnerTeam(db.DefaultContext)
|
ownerTeam2, err := org_model.OrgFromUser(privateOrg).GetOwnerTeam(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, models.AddTeamMember(db.DefaultContext, ownerTeam2, user4))
|
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user4))
|
||||||
user4Token := getTokenForLoggedInUser(t, user4Sess, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteOrganization)
|
user4Token := getTokenForLoggedInUser(t, user4Sess, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteOrganization)
|
||||||
req = NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/forks", &api.CreateForkOption{
|
req = NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/forks", &api.CreateForkOption{
|
||||||
Organization: &privateOrg.Name,
|
Organization: &privateOrg.Name,
|
||||||
@ -84,7 +84,7 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
|
|||||||
assert.Len(t, forks, 1)
|
assert.Len(t, forks, 1)
|
||||||
assert.EqualValues(t, "1", resp.Header().Get("X-Total-Count"))
|
assert.EqualValues(t, "1", resp.Header().Get("X-Total-Count"))
|
||||||
|
|
||||||
assert.NoError(t, models.AddTeamMember(db.DefaultContext, ownerTeam2, user1))
|
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user1))
|
||||||
|
|
||||||
req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks").AddTokenAuth(user1Token)
|
req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks").AddTokenAuth(user1Token)
|
||||||
resp = MakeRequest(t, req, http.StatusOK)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
@ -19,6 +18,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
"code.gitea.io/gitea/services/auth"
|
"code.gitea.io/gitea/services/auth"
|
||||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
"code.gitea.io/gitea/services/auth/source/ldap"
|
||||||
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -429,9 +429,9 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) {
|
|||||||
isMember, err := organization.IsTeamMember(db.DefaultContext, usersOrgs[0].ID, team.ID, user.ID)
|
isMember, err := organization.IsTeamMember(db.DefaultContext, usersOrgs[0].ID, team.ID, user.ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, isMember, "Membership should be added to the right team")
|
assert.True(t, isMember, "Membership should be added to the right team")
|
||||||
err = models.RemoveTeamMember(db.DefaultContext, team, user)
|
err = org_service.RemoveTeamMember(db.DefaultContext, team, user)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = models.RemoveOrgUser(db.DefaultContext, usersOrgs[0], user)
|
err = org_service.RemoveOrgUser(db.DefaultContext, usersOrgs[0], user)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
} else {
|
} else {
|
||||||
// assert members of LDAP group "cn=admin_staff" keep initial team membership since mapped team does not exist
|
// assert members of LDAP group "cn=admin_staff" keep initial team membership since mapped team does not exist
|
||||||
@ -461,7 +461,7 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) {
|
|||||||
})
|
})
|
||||||
err = organization.AddOrgUser(db.DefaultContext, org.ID, user.ID)
|
err = organization.AddOrgUser(db.DefaultContext, org.ID, user.ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = models.AddTeamMember(db.DefaultContext, team, user)
|
err = org_service.AddTeamMember(db.DefaultContext, team, user)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
isMember, err := organization.IsOrganizationMember(db.DefaultContext, org.ID, user.ID)
|
isMember, err := organization.IsOrganizationMember(db.DefaultContext, org.ID, user.ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -9,12 +9,12 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
org_model "code.gitea.io/gitea/models/organization"
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
org_service "code.gitea.io/gitea/services/org"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -91,7 +91,7 @@ func TestForkListLimitedAndPrivateRepos(t *testing.T) {
|
|||||||
assert.EqualValues(t, structs.VisibleTypeLimited, limitedOrg.Visibility)
|
assert.EqualValues(t, structs.VisibleTypeLimited, limitedOrg.Visibility)
|
||||||
ownerTeam1, err := org_model.OrgFromUser(limitedOrg).GetOwnerTeam(db.DefaultContext)
|
ownerTeam1, err := org_model.OrgFromUser(limitedOrg).GetOwnerTeam(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, models.AddTeamMember(db.DefaultContext, ownerTeam1, user1))
|
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam1, user1))
|
||||||
testRepoFork(t, user1Sess, "user2", "repo1", limitedOrg.Name, "repo1", "")
|
testRepoFork(t, user1Sess, "user2", "repo1", limitedOrg.Name, "repo1", "")
|
||||||
|
|
||||||
// fork to a private org
|
// fork to a private org
|
||||||
@ -101,7 +101,7 @@ func TestForkListLimitedAndPrivateRepos(t *testing.T) {
|
|||||||
assert.EqualValues(t, structs.VisibleTypePrivate, privateOrg.Visibility)
|
assert.EqualValues(t, structs.VisibleTypePrivate, privateOrg.Visibility)
|
||||||
ownerTeam2, err := org_model.OrgFromUser(privateOrg).GetOwnerTeam(db.DefaultContext)
|
ownerTeam2, err := org_model.OrgFromUser(privateOrg).GetOwnerTeam(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, models.AddTeamMember(db.DefaultContext, ownerTeam2, user4))
|
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user4))
|
||||||
testRepoFork(t, user4Sess, "user2", "repo1", privateOrg.Name, "repo1", "")
|
testRepoFork(t, user4Sess, "user2", "repo1", privateOrg.Name, "repo1", "")
|
||||||
|
|
||||||
t.Run("Anonymous", func(t *testing.T) {
|
t.Run("Anonymous", func(t *testing.T) {
|
||||||
@ -120,7 +120,7 @@ func TestForkListLimitedAndPrivateRepos(t *testing.T) {
|
|||||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
assert.EqualValues(t, 1, htmlDoc.Find(forkItemSelector).Length())
|
assert.EqualValues(t, 1, htmlDoc.Find(forkItemSelector).Length())
|
||||||
|
|
||||||
assert.NoError(t, models.AddTeamMember(db.DefaultContext, ownerTeam2, user1))
|
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user1))
|
||||||
resp = user1Sess.MakeRequest(t, req, http.StatusOK)
|
resp = user1Sess.MakeRequest(t, req, http.StatusOK)
|
||||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||||
assert.EqualValues(t, 2, htmlDoc.Find(forkItemSelector).Length())
|
assert.EqualValues(t, 2, htmlDoc.Find(forkItemSelector).Length())
|
||||||
|
Loading…
Reference in New Issue
Block a user