Skip to content

Commit 6cecffc

Browse files
committed
Fix delete branch perm checking
1 parent f49d823 commit 6cecffc

File tree

3 files changed

+81
-39
lines changed

3 files changed

+81
-39
lines changed

models/perm/access/repo_permission.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ import (
1818
"code.gitea.io/gitea/modules/util"
1919
)
2020

21+
type ErrNoPermission struct {
22+
RepoID int64
23+
Unit unit.Type
24+
Perm perm_model.AccessMode
25+
}
26+
27+
func (e ErrNoPermission) Error() string {
28+
return fmt.Sprintf("no permission to access repo %d unit %s with mode %s", e.RepoID, e.Unit, e.Perm)
29+
}
30+
2131
// Permission contains all the permissions related variables to a repository for a user
2232
type Permission struct {
2333
AccessMode perm_model.AccessMode

routers/api/v1/repo/pull.go

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,49 +1057,51 @@ func MergePullRequest(ctx *context.APIContext) {
10571057
}
10581058
log.Trace("Pull request merged: %d", pr.ID)
10591059

1060-
if form.DeleteBranchAfterMerge {
1061-
// Don't cleanup when there are other PR's that use this branch as head branch.
1062-
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pr.HeadRepoID, pr.HeadBranch)
1063-
if err != nil {
1064-
ctx.ServerError("HasUnmergedPullRequestsByHeadInfo", err)
1065-
return
1066-
}
1067-
if exist {
1068-
ctx.Status(http.StatusOK)
1069-
return
1070-
}
1071-
1072-
var headRepo *git.Repository
1073-
if ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == pr.HeadRepoID && ctx.Repo.GitRepo != nil {
1074-
headRepo = ctx.Repo.GitRepo
1075-
} else {
1076-
headRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
1060+
if form.DeleteBranchAfterMerge && pr.Flow == issues_model.PullRequestFlowGithub {
1061+
if err := repo_service.CanDeleteBranch(ctx, pr.HeadRepo, pr.HeadBranch, ctx.Doer); err == nil {
1062+
// Don't cleanup when there are other PR's that use this branch as head branch.
1063+
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pr.HeadRepoID, pr.HeadBranch)
10771064
if err != nil {
1078-
ctx.ServerError(fmt.Sprintf("OpenRepository[%s]", pr.HeadRepo.FullName()), err)
1065+
ctx.ServerError("HasUnmergedPullRequestsByHeadInfo", err)
10791066
return
10801067
}
1081-
defer headRepo.Close()
1082-
}
1083-
if err := pull_service.RetargetChildrenOnMerge(ctx, ctx.Doer, pr); err != nil {
1084-
ctx.Error(http.StatusInternalServerError, "RetargetChildrenOnMerge", err)
1085-
return
1086-
}
1087-
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil {
1088-
switch {
1089-
case git.IsErrBranchNotExist(err):
1090-
ctx.NotFound(err)
1091-
case errors.Is(err, repo_service.ErrBranchIsDefault):
1092-
ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
1093-
case errors.Is(err, git_model.ErrBranchIsProtected):
1094-
ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
1095-
default:
1096-
ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
1068+
if exist {
1069+
ctx.Status(http.StatusOK)
1070+
return
1071+
}
1072+
1073+
var headRepo *git.Repository
1074+
if ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == pr.HeadRepoID && ctx.Repo.GitRepo != nil {
1075+
headRepo = ctx.Repo.GitRepo
1076+
} else {
1077+
headRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
1078+
if err != nil {
1079+
ctx.ServerError(fmt.Sprintf("OpenRepository[%s]", pr.HeadRepo.FullName()), err)
1080+
return
1081+
}
1082+
defer headRepo.Close()
1083+
}
1084+
if err := pull_service.RetargetChildrenOnMerge(ctx, ctx.Doer, pr); err != nil {
1085+
ctx.Error(http.StatusInternalServerError, "RetargetChildrenOnMerge", err)
1086+
return
1087+
}
1088+
if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil {
1089+
switch {
1090+
case git.IsErrBranchNotExist(err):
1091+
ctx.NotFound(err)
1092+
case errors.Is(err, repo_service.ErrBranchIsDefault):
1093+
ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
1094+
case errors.Is(err, git_model.ErrBranchIsProtected):
1095+
ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
1096+
default:
1097+
ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
1098+
}
1099+
return
1100+
}
1101+
if err := issues_model.AddDeletePRBranchComment(ctx, ctx.Doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
1102+
// Do not fail here as branch has already been deleted
1103+
log.Error("DeleteBranch: %v", err)
10971104
}
1098-
return
1099-
}
1100-
if err := issues_model.AddDeletePRBranchComment(ctx, ctx.Doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
1101-
// Do not fail here as branch has already been deleted
1102-
log.Error("DeleteBranch: %v", err)
11031105
}
11041106
}
11051107

services/repository/branch.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import (
1414
"code.gitea.io/gitea/models/db"
1515
git_model "code.gitea.io/gitea/models/git"
1616
issues_model "code.gitea.io/gitea/models/issues"
17+
perm_model "code.gitea.io/gitea/models/perm"
18+
access_model "code.gitea.io/gitea/models/perm/access"
1719
repo_model "code.gitea.io/gitea/models/repo"
20+
"code.gitea.io/gitea/models/unit"
1821
user_model "code.gitea.io/gitea/models/user"
1922
"code.gitea.io/gitea/modules/cache"
2023
"code.gitea.io/gitea/modules/git"
@@ -463,6 +466,33 @@ var (
463466
ErrBranchIsDefault = errors.New("branch is default")
464467
)
465468

469+
func CanDeleteBranch(ctx context.Context, repo *repo_model.Repository, branchName string, doer *user_model.User) error {
470+
if branchName == repo.DefaultBranch {
471+
return ErrBranchIsDefault
472+
}
473+
474+
perm, err := access_model.GetUserRepoPermission(ctx, repo, doer)
475+
if err != nil {
476+
return err
477+
}
478+
if !perm.CanWrite(unit.TypeCode) {
479+
return access_model.ErrNoPermission{
480+
RepoID: repo.ID,
481+
Unit: unit.TypeCode,
482+
Perm: perm_model.AccessModeWrite,
483+
}
484+
}
485+
486+
isProtected, err := git_model.IsBranchProtected(ctx, repo.ID, branchName)
487+
if err != nil {
488+
return err
489+
}
490+
if isProtected {
491+
return git_model.ErrBranchIsProtected
492+
}
493+
return nil
494+
}
495+
466496
// DeleteBranch delete branch
467497
func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error {
468498
err := repo.MustNotBeArchived()

0 commit comments

Comments
 (0)