mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-26 06:16:56 +09:00
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: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
142 lines
3.1 KiB
Go
142 lines
3.1 KiB
Go
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
issues_model "gitea.dev/models/issues"
|
|
api "gitea.dev/modules/structs"
|
|
"gitea.dev/modules/web"
|
|
"gitea.dev/services/context"
|
|
)
|
|
|
|
// LockIssue lock an issue
|
|
func LockIssue(ctx *context.APIContext) {
|
|
// swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/lock issue issueLockIssue
|
|
// ---
|
|
// summary: Lock an issue
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: owner
|
|
// in: path
|
|
// description: owner of the repo
|
|
// type: string
|
|
// required: true
|
|
// - name: repo
|
|
// in: path
|
|
// description: name of the repo
|
|
// type: string
|
|
// required: true
|
|
// - name: index
|
|
// in: path
|
|
// description: index of the issue
|
|
// type: integer
|
|
// format: int64
|
|
// required: true
|
|
// - name: body
|
|
// in: body
|
|
// schema:
|
|
// "$ref": "#/definitions/LockIssueOption"
|
|
// responses:
|
|
// "204":
|
|
// "$ref": "#/responses/empty"
|
|
// "403":
|
|
// "$ref": "#/responses/forbidden"
|
|
// "404":
|
|
// "$ref": "#/responses/notFound"
|
|
|
|
reason := web.GetForm[*api.LockIssueOption](ctx).Reason
|
|
issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index"))
|
|
if issue == nil {
|
|
return
|
|
}
|
|
|
|
if !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) {
|
|
ctx.APIError(http.StatusForbidden, "no permission to lock this issue")
|
|
return
|
|
}
|
|
|
|
if !issue.IsLocked {
|
|
opt := &issues_model.IssueLockOptions{
|
|
Doer: ctx.ContextUser,
|
|
Issue: issue,
|
|
Reason: reason,
|
|
}
|
|
|
|
issue.Repo = ctx.Repo.Repository
|
|
err := issues_model.LockIssue(ctx, opt)
|
|
if err != nil {
|
|
ctx.APIErrorInternal(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
ctx.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// UnlockIssue unlock an issue
|
|
func UnlockIssue(ctx *context.APIContext) {
|
|
// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/lock issue issueUnlockIssue
|
|
// ---
|
|
// summary: Unlock an issue
|
|
// consumes:
|
|
// - application/json
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: owner
|
|
// in: path
|
|
// description: owner of the repo
|
|
// type: string
|
|
// required: true
|
|
// - name: repo
|
|
// in: path
|
|
// description: name of the repo
|
|
// type: string
|
|
// required: true
|
|
// - name: index
|
|
// in: path
|
|
// description: index of the issue
|
|
// type: integer
|
|
// format: int64
|
|
// required: true
|
|
// responses:
|
|
// "204":
|
|
// "$ref": "#/responses/empty"
|
|
// "403":
|
|
// "$ref": "#/responses/forbidden"
|
|
// "404":
|
|
// "$ref": "#/responses/notFound"
|
|
|
|
issue := getRepoIssueByIndexForDoer(ctx, ctx.PathParamInt64("index"))
|
|
if issue == nil {
|
|
return
|
|
}
|
|
|
|
if !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) {
|
|
ctx.APIError(http.StatusForbidden, "no permission to unlock this issue")
|
|
return
|
|
}
|
|
|
|
if issue.IsLocked {
|
|
opt := &issues_model.IssueLockOptions{
|
|
Doer: ctx.ContextUser,
|
|
Issue: issue,
|
|
}
|
|
|
|
issue.Repo = ctx.Repo.Repository
|
|
err := issues_model.UnlockIssue(ctx, opt)
|
|
if err != nil {
|
|
ctx.APIErrorInternal(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
ctx.Status(http.StatusNoContent)
|
|
}
|