mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-09 17:37:20 -06:00
`PUT/DELETE /repos/{owner}/{repo}/issues/{index}/lock` passed
`ctx.ContextUser` as the doer of `LockIssue` / `UnlockIssue`. In the
repo routes that is the owner of the repository, so on a repository of
an organization the lock and unlock timeline comments were created by
the organization instead of the user who did it. The web handler already
uses the signed-in user; this uses `ctx.Doer` as well.
Adds a subtest `DoerOnOrganizationRepo` to the existing
`TestAPILockIssue`: a member of an organization locks and unlocks an
issue of an organization repository, and the comments must be posted by
that member.
Found while testing audit logging of the issue lock events downstream.
_Authored by Codet (Claude)._
138 lines
2.9 KiB
Go
138 lines
2.9 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"
|
|
// default:
|
|
// "$ref": "#/responses/error"
|
|
|
|
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.Doer,
|
|
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"
|
|
// default:
|
|
// "$ref": "#/responses/error"
|
|
|
|
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.Doer,
|
|
Issue: issue,
|
|
}
|
|
|
|
issue.Repo = ctx.Repo.Repository
|
|
err := issues_model.UnlockIssue(ctx, opt)
|
|
if err != nil {
|
|
ctx.APIErrorInternal(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
ctx.Status(http.StatusNoContent)
|
|
}
|