From 3e5256b6ca55289e7cd75b2c132d3c94ed683f06 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Thu, 22 Oct 2026 05:27:46 -0700 Subject: [PATCH] fix(api): check unit read permission in issue sub-resource endpoints (#39885) (#39938) Backport #39885 by @basavaraj-sm05 Several `/repos/{owner}/{repo}/issues/{index}/...` API endpoints load the issue by index without checking `CanReadIssuesOrPulls(issue.IsPull)`, unlike `GetIssue`, `ListIssueComments` and `GetIssueReactions`. The route group's `mustEnableIssuesOrPulls` passes when the caller can read either unit, so someone who can read issues but not pull requests (a team with only the Issues unit, or per-unit anonymous access on a public repo) can read a pull request's whole conversation through `/timeline`, plus its labels, subscribers, attachments and tracked times, and can subscribe to it. The reverse holds for issues. These handlers now return 404 in that case, like their siblings. Co-authored-by: Basavaraj S m Co-authored-by: silverwind Co-authored-by: wxiaoguang --- routers/api/v1/repo/issue.go | 55 +++++++++++------------ routers/api/v1/repo/issue_assignee.go | 15 ++----- routers/api/v1/repo/issue_attachment.go | 20 ++------- routers/api/v1/repo/issue_comment.go | 27 +++-------- routers/api/v1/repo/issue_comment_test.go | 44 ++++++++++++++++++ routers/api/v1/repo/issue_dependency.go | 38 ++++------------ routers/api/v1/repo/issue_label.go | 38 ++++------------ routers/api/v1/repo/issue_lock.go | 14 +++--- routers/api/v1/repo/issue_pin.go | 35 ++++----------- routers/api/v1/repo/issue_stopwatch.go | 9 +--- routers/api/v1/repo/issue_subscription.go | 30 +++---------- routers/api/v1/repo/issue_tracked_time.go | 24 +++++----- 12 files changed, 135 insertions(+), 214 deletions(-) create mode 100644 routers/api/v1/repo/issue_comment_test.go diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 6342f020a74..619ad3a5615 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -513,17 +513,12 @@ func GetIssue(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } - if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { - ctx.APIErrorNotFound() + if err := issue.LoadAttributes(ctx); err != nil { + ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue)) @@ -698,19 +693,13 @@ func EditIssue(ctx *context.APIContext) { // "$ref": "#/responses/repoArchivedError" form := web.GetForm[*api.EditIssueOption](ctx) - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } - issue.Repo = ctx.Repo.Repository canWrite := ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) - err = issue.LoadAttributes(ctx) + err := issue.LoadAttributes(ctx) if err != nil { ctx.APIErrorInternal(err) return @@ -901,13 +890,12 @@ func DeleteIssue(ctx *context.APIContext) { // "$ref": "#/responses/notFound" // "423": // "$ref": "#/responses/repoArchivedError" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } - if err = issue_service.DeleteIssue(ctx, ctx.Doer, issue); err != nil { + if err := issue_service.DeleteIssue(ctx, ctx.Doer, issue); err != nil { ctx.APIErrorInternal(err) return } @@ -953,13 +941,8 @@ func UpdateIssueDeadline(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" form := web.GetForm[*api.EditDeadlineOption](ctx) - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -999,3 +982,17 @@ func closeOrReopenIssue(ctx *context.APIContext, issue *issues_model.Issue, stat } } } + +func getRepoIssueByIndexForDoer(ctx *context.APIContext, index int64) *issues_model.Issue { + issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, index) + if err != nil { + ctx.APIErrorAuto(err) + return nil + } + if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { + ctx.APIErrorNotFound() + return nil + } + issue.Repo = ctx.Repo.Repository + return issue +} diff --git a/routers/api/v1/repo/issue_assignee.go b/routers/api/v1/repo/issue_assignee.go index adc0240eee1..0718ba8d382 100644 --- a/routers/api/v1/repo/issue_assignee.go +++ b/routers/api/v1/repo/issue_assignee.go @@ -146,14 +146,8 @@ func CheckIssueAssignee(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) - return - } - - if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { - ctx.APIErrorNotFound() + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -187,9 +181,8 @@ func checkAssignableUser(ctx *context.APIContext, assigneeName string, repo *rep } func updateIssueAssignees(ctx *context.APIContext, opts api.IssueAssigneesOption, isAdd bool) { - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index 4391bb10ca9..3bb3ac99a4e 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -57,7 +57,7 @@ func GetIssueAttachment(ctx *context.APIContext) { // "404": // "$ref": "#/responses/error" - issue := getIssueFromContext(ctx) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if issue == nil { return } @@ -100,7 +100,7 @@ func ListIssueAttachments(ctx *context.APIContext) { // "404": // "$ref": "#/responses/error" - issue := getIssueFromContext(ctx) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if issue == nil { return } @@ -163,7 +163,7 @@ func CreateIssueAttachment(ctx *context.APIContext) { // "423": // "$ref": "#/responses/repoArchivedError" - issue := getIssueFromContext(ctx) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if issue == nil { return } @@ -333,20 +333,8 @@ func DeleteIssueAttachment(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -func getIssueFromContext(ctx *context.APIContext) *issues_model.Issue { - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) - return nil - } - - issue.Repo = ctx.Repo.Repository - - return issue -} - func getIssueAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Attachment { - issue := getIssueFromContext(ctx) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if issue == nil { return nil } diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 162bc5286fe..974d8872398 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -69,17 +69,10 @@ func ListIssueComments(ctx *context.APIContext) { ctx.APIError(http.StatusUnprocessableEntity, err.Error()) return } - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorInternal(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } - if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { - ctx.APIErrorNotFound() - return - } - - issue.Repo = ctx.Repo.Repository opts := &issues_model.FindCommentsOptions{ IssueID: issue.ID, @@ -173,12 +166,10 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) { ctx.APIError(http.StatusUnprocessableEntity, err.Error()) return } - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorInternal(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } - issue.Repo = ctx.Repo.Repository opts := &issues_model.FindCommentsOptions{ ListOptions: utils.GetListOptions(ctx), @@ -381,14 +372,8 @@ func CreateIssueComment(ctx *context.APIContext) { // "$ref": "#/responses/repoArchivedError" form := web.GetForm[*api.CreateIssueCommentOption](ctx) - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorInternal(err) - return - } - - if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { - ctx.APIErrorNotFound() + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } diff --git a/routers/api/v1/repo/issue_comment_test.go b/routers/api/v1/repo/issue_comment_test.go new file mode 100644 index 00000000000..78afdaa460b --- /dev/null +++ b/routers/api/v1/repo/issue_comment_test.go @@ -0,0 +1,44 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "net/http" + "testing" + + perm_model "gitea.dev/models/perm" + access_model "gitea.dev/models/perm/access" + repo_model "gitea.dev/models/repo" + "gitea.dev/models/unit" + "gitea.dev/models/unittest" + "gitea.dev/services/context" + "gitea.dev/services/contexttest" + + "github.com/stretchr/testify/assert" +) + +func TestIssueReadEndpointsRequireUnitPermission(t *testing.T) { + unittest.PrepareTestEnv(t) + + for name, handler := range map[string]func(*context.APIContext){ + "timeline": ListIssueCommentsAndTimeline, + "labels": ListIssueLabels, + "subscribers": GetIssueSubscribers, + "attachments": ListIssueAttachments, + } { + t.Run(name, func(t *testing.T) { + request := func(index string) int { + ctx, resp := contexttest.MockAPIContext(t, "user2/repo1") + contexttest.LoadRepo(t, ctx, 1) + ctx.Repo.Permission = access_model.Permission{} + ctx.Repo.Permission.SetUnitsWithDefaultAccessMode([]*repo_model.RepoUnit{{Type: unit.TypeIssues}}, perm_model.AccessModeRead) + ctx.SetPathParam("index", index) + handler(ctx) + return resp.Code + } + assert.Equal(t, http.StatusOK, request("1")) + assert.Equal(t, http.StatusNotFound, request("2")) + }) + } +} diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go index 615eea9a365..c7a23d01d4a 100644 --- a/routers/api/v1/repo/issue_dependency.go +++ b/routers/api/v1/repo/issue_dependency.go @@ -61,15 +61,8 @@ func GetIssueDependencies(ctx *context.APIContext) { return } - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) - return - } - - // 1. We must be able to read this issue - if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { - ctx.APIErrorNotFound() + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -79,7 +72,7 @@ func GetIssueDependencies(ctx *context.APIContext) { blockerIssues := make([]*issues_model.Issue, 0, min(listOptions.PageSize, setting.API.MaxResponseItems)) - // 2. Get the issues this issue depends on, i.e. the `<#b>`: ` <- <#b>` + // Get the issues this issue depends on, i.e. the `<#b>`: ` <- <#b>` blockersInfo, total, err := issue.BlockedByDependencies(ctx, listOptions) if err != nil { ctx.APIErrorInternal(err) @@ -177,7 +170,7 @@ func CreateIssueDependency(ctx *context.APIContext) { // "$ref": "#/responses/repoArchivedError" // We want to make <:index> depend on
, i.e. <:index> is the target - target := getParamsIssue(ctx) + target := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if ctx.Written() { return } @@ -238,7 +231,7 @@ func RemoveIssueDependency(ctx *context.APIContext) { // "$ref": "#/responses/repoArchivedError" // We want to make <:index> depend on , i.e. <:index> is the target - target := getParamsIssue(ctx) + target := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if ctx.Written() { return } @@ -303,16 +296,11 @@ func GetIssueBlocks(ctx *context.APIContext) { // We need to list the issues that DEPEND on this issue not the other way round // Therefore whether dependencies are enabled or not in this repository is potentially irrelevant. - issue := getParamsIssue(ctx) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if ctx.Written() { return } - if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) { - ctx.APIErrorNotFound() - return - } - page := max(ctx.FormInt("page"), 1) limit := ctx.FormInt("limit") if limit <= 1 { @@ -399,7 +387,7 @@ func CreateIssueBlocking(ctx *context.APIContext) { // "404": // description: the issue does not exist - dependency := getParamsIssue(ctx) + dependency := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if ctx.Written() { return } @@ -456,7 +444,7 @@ func RemoveIssueBlocking(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - dependency := getParamsIssue(ctx) + dependency := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) if ctx.Written() { return } @@ -480,16 +468,6 @@ func RemoveIssueBlocking(ctx *context.APIContext) { ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency)) } -func getParamsIssue(ctx *context.APIContext) *issues_model.Issue { - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) - return nil - } - issue.Repo = ctx.Repo.Repository - return issue -} - func getFormIssue(ctx *context.APIContext, form *api.IssueMeta) *issues_model.Issue { var repo *repo_model.Repository if form.Owner != ctx.Repo.Repository.OwnerName || form.Name != ctx.Repo.Repository.Name { diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 48854390a44..88632d9eabb 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -47,13 +47,8 @@ func ListIssueLabels(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -167,13 +162,8 @@ func DeleteIssueLabel(ctx *context.APIContext) { // "423": // "$ref": "#/responses/repoArchivedError" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -292,13 +282,8 @@ func ClearIssueLabels(ctx *context.APIContext) { // "423": // "$ref": "#/responses/repoArchivedError" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -316,14 +301,9 @@ func ClearIssueLabels(ctx *context.APIContext) { } func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) (*issues_model.Issue, []*issues_model.Label, error) { - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } - return nil, nil, err + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { + return nil, nil, errors.New("issue not found") } if !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) { diff --git a/routers/api/v1/repo/issue_lock.go b/routers/api/v1/repo/issue_lock.go index 14f2768fe31..f0ac1847b08 100644 --- a/routers/api/v1/repo/issue_lock.go +++ b/routers/api/v1/repo/issue_lock.go @@ -51,9 +51,8 @@ func LockIssue(ctx *context.APIContext) { // "$ref": "#/responses/notFound" reason := web.GetForm[*api.LockIssueOption](ctx).Reason - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -70,7 +69,7 @@ func LockIssue(ctx *context.APIContext) { } issue.Repo = ctx.Repo.Repository - err = issues_model.LockIssue(ctx, opt) + err := issues_model.LockIssue(ctx, opt) if err != nil { ctx.APIErrorInternal(err) return @@ -114,9 +113,8 @@ func UnlockIssue(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -132,7 +130,7 @@ func UnlockIssue(ctx *context.APIContext) { } issue.Repo = ctx.Repo.Repository - err = issues_model.UnlockIssue(ctx, opt) + err := issues_model.UnlockIssue(ctx, opt) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go index c71649aeca4..1570f9f8ddb 100644 --- a/routers/api/v1/repo/issue_pin.go +++ b/routers/api/v1/repo/issue_pin.go @@ -41,20 +41,13 @@ func PinIssue(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else if issues_model.IsErrIssueMaxPinReached(err) { - ctx.APIError(http.StatusBadRequest, err.Error()) - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } // If we don't do this, it will crash when trying to add the pin event to the comment history - err = issue.LoadRepo(ctx) + err := issue.LoadRepo(ctx) if err != nil { ctx.APIErrorInternal(err) return @@ -98,18 +91,13 @@ func UnpinIssue(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } // If we don't do this, it will crash when trying to add the unpin event to the comment history - err = issue.LoadRepo(ctx) + err := issue.LoadRepo(ctx) if err != nil { ctx.APIErrorInternal(err) return @@ -159,17 +147,12 @@ func MoveIssuePin(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } - err = issues_model.MovePin(ctx, issue, ctx.PathParamInt("position")) + err := issues_model.MovePin(ctx, issue, ctx.PathParamInt("position")) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go index 130b76f880b..92c05a03446 100644 --- a/routers/api/v1/repo/issue_stopwatch.go +++ b/routers/api/v1/repo/issue_stopwatch.go @@ -172,13 +172,8 @@ func DeleteIssueStopwatch(ctx *context.APIContext) { } func prepareIssueForStopwatch(ctx *context.APIContext) *issues_model.Issue { - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return nil } diff --git a/routers/api/v1/repo/issue_subscription.go b/routers/api/v1/repo/issue_subscription.go index 84af3194df2..ed66443740f 100644 --- a/routers/api/v1/repo/issue_subscription.go +++ b/routers/api/v1/repo/issue_subscription.go @@ -104,14 +104,8 @@ func DelIssueSubscription(ctx *context.APIContext) { } func setIssueSubscription(ctx *context.APIContext, watch bool) { - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } - + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -185,14 +179,8 @@ func CheckIssueSubscription(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } - + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -251,14 +239,8 @@ func GetIssueSubscribers(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - if issues_model.IsErrIssueNotExist(err) { - ctx.APIErrorNotFound() - } else { - ctx.APIErrorInternal(err) - } - + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index 6adb8f9367b..fa28662662c 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -75,9 +75,8 @@ func ListTrackedTimes(ctx *context.APIContext) { ctx.APIErrorNotFound("timetracker is disabled") return } - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -100,6 +99,7 @@ func ListTrackedTimes(ctx *context.APIContext) { opts.UserID = user.ID } + var err error if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil { ctx.APIError(http.StatusUnprocessableEntity, err.Error()) return @@ -200,9 +200,8 @@ func AddTime(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" form := web.GetForm[*api.AddTimeOption](ctx) - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -219,6 +218,7 @@ func AddTime(ctx *context.APIContext) { if form.User != "" { if (ctx.IsUserRepoAdmin() && ctx.Doer.Name != form.User) || ctx.Doer.IsAdmin { // allow only RepoAdmin, Admin and User to add time + var err error user, err = user_model.GetUserByName(ctx, form.User) if err != nil { ctx.APIErrorAuto(err) @@ -280,9 +280,8 @@ func ResetIssueTime(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return } @@ -295,7 +294,7 @@ func ResetIssueTime(ctx *context.APIContext) { return } - err = issues_model.DeleteIssueUserTimes(ctx, issue, ctx.Doer) + err := issues_model.DeleteIssueUserTimes(ctx, issue, ctx.Doer) if err != nil { if db.IsErrNotExist(err) { ctx.APIError(http.StatusNotFound, err.Error()) @@ -349,9 +348,8 @@ func DeleteTime(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index")) - if err != nil { - ctx.APIErrorAuto(err) + issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index")) + if issue == nil { return }