fix: check the permission of canceling automerge

The API already checked the permission sufficiently if auto merge could
be cancelled by the doer. The web route did not. Consolidate this check
in the function that lives in the services directory.
This commit is contained in:
Gusted 2026-02-20 20:42:56 +01:00 committed by Mathieu Fenniak
parent 8c8947f4a5
commit 663aa50eec
4 changed files with 60 additions and 49 deletions

View file

@ -20,5 +20,6 @@
"themes.names.forgejo-dark": "Forgejo dark",
"settings.adopt": "Adopt",
"install.invalid_lfs_path": "Unable to create the LFS root at the specified path: %[1]s",
"install.lfs_jwt_secret_failed": "Unable to generate a LFS JWT secret: %[1]s"
"install.lfs_jwt_secret_failed": "Unable to generate a LFS JWT secret: %[1]s",
"repo.pulls.auto_merge.no_permission": "You do not have permission to cancel this pull request's auto merge."
}

View file

@ -14,6 +14,7 @@ import (
"forgejo.org/models"
activities_model "forgejo.org/models/activities"
"forgejo.org/models/db"
git_model "forgejo.org/models/git"
issues_model "forgejo.org/models/issues"
access_model "forgejo.org/models/perm/access"
@ -1382,34 +1383,19 @@ func CancelScheduledAutoMerge(ctx *context.APIContext) {
return
}
exist, autoMerge, err := pull_model.GetScheduledMergeByPullID(ctx, pull.ID)
if err != nil {
ctx.InternalServerError(err)
return
}
if !exist {
ctx.NotFound()
return
}
if ctx.Doer.ID != autoMerge.DoerID {
allowed, err := access_model.IsUserRepoAdmin(ctx, ctx.Repo.Repository, ctx.Doer)
if err != nil {
ctx.InternalServerError(err)
return
}
if !allowed {
if err := automerge.RemoveScheduledAutoMerge(ctx, ctx.Doer, pull, ctx.Repo.Permission); err != nil {
switch {
case errors.Is(err, util.ErrPermissionDenied):
ctx.Error(http.StatusForbidden, "No permission to cancel", "user has no permission to cancel the scheduled auto merge")
case db.IsErrNotExist(err):
ctx.NotFound()
default:
ctx.InternalServerError(err)
}
return
}
}
if err := automerge.RemoveScheduledAutoMerge(ctx, ctx.Doer, pull); err != nil {
ctx.InternalServerError(err)
} else {
ctx.Status(http.StatusNoContent)
}
}
// GetPullRequestCommits gets all commits associated with a given PR
func GetPullRequestCommits(ctx *context.APIContext) {

View file

@ -1427,17 +1427,22 @@ func CancelAutoMergePullRequest(ctx *context.Context) {
return
}
if err := automerge.RemoveScheduledAutoMerge(ctx, ctx.Doer, issue.PullRequest); err != nil {
if db.IsErrNotExist(err) {
if err := automerge.RemoveScheduledAutoMerge(ctx, ctx.Doer, issue.PullRequest, ctx.Repo.Permission); err != nil {
switch {
case errors.Is(err, util.ErrPermissionDenied):
ctx.Flash.Error(ctx.Tr("repo.pulls.auto_merge.no_permission"))
ctx.Redirect(issue.HTMLURL())
case db.IsErrNotExist(err):
ctx.Flash.Error(ctx.Tr("repo.pulls.auto_merge_not_scheduled"))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, issue.Index))
return
}
ctx.Redirect(issue.HTMLURL())
default:
ctx.ServerError("RemoveScheduledAutoMerge", err)
}
return
}
ctx.Flash.Success(ctx.Tr("repo.pulls.auto_merge_canceled_schedule"))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, issue.Index))
ctx.Redirect(issue.HTMLURL())
}
func stopTimerIfAvailable(ctx *context.Context, user *user_model.User, issue *issues_model.Issue) error {

View file

@ -20,6 +20,7 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/process"
"forgejo.org/modules/queue"
"forgejo.org/modules/util"
notify_service "forgejo.org/services/notify"
pull_service "forgejo.org/services/pull"
repo_service "forgejo.org/services/repository"
@ -67,7 +68,25 @@ func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_
}
// RemoveScheduledAutoMerge cancels a previously scheduled pull request
func RemoveScheduledAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest) error {
func RemoveScheduledAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest, repoPerms access_model.Permission) error {
exist, autoMerge, err := pull_model.GetScheduledMergeByPullID(ctx, pull.ID)
if err != nil {
return err
}
if !exist {
return db.ErrNotExist{Resource: "auto_merge", ID: pull.ID}
}
if doer.ID != autoMerge.DoerID {
allowed, err := pull_service.IsUserAllowedToMerge(ctx, pull, repoPerms, doer)
if err != nil {
return err
}
if !allowed {
return util.ErrPermissionDenied
}
}
return db.WithTx(ctx, func(ctx context.Context) error {
if err := pull_model.DeleteScheduledAutoMerge(ctx, pull.ID); err != nil {
return err