forgejo/routers/api/v1/repo/release_attachment.go
Lunny Xiao f4310d74ee
Fix comment permissions () ()
backport 

This PR will fix some missed checks for private repositories' data on
web routes and API routes.

(cherry picked from commit bc3d8bff73)
2023-11-26 13:56:50 +01:00

376 lines
10 KiB
Go

// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/attachment"
"code.gitea.io/gitea/services/convert"
)
func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool {
release, err := repo_model.GetReleaseByID(ctx, releaseID)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound()
return false
}
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
return false
}
if release.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound()
return false
}
return true
}
// GetReleaseAttachment gets a single attachment of the release
func GetReleaseAttachment(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoGetReleaseAttachment
// ---
// summary: Get a release attachment
// 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: id
// in: path
// description: id of the release
// type: integer
// format: int64
// required: true
// - name: attachment_id
// in: path
// description: id of the attachment to get
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/Attachment"
// "404":
// "$ref": "#/responses/notFound"
releaseID := ctx.ParamsInt64(":id")
if !checkReleaseMatchRepo(ctx, releaseID) {
return
}
attachID := ctx.ParamsInt64(":attachment_id")
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
ctx.NotFound()
return
}
ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
return
}
if attach.ReleaseID != releaseID {
log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID)
ctx.NotFound()
return
}
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
}
// ListReleaseAttachments lists all attachments of the release
func ListReleaseAttachments(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets repository repoListReleaseAttachments
// ---
// summary: List release's attachments
// 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: id
// in: path
// description: id of the release
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/AttachmentList"
// "404":
// "$ref": "#/responses/notFound"
releaseID := ctx.ParamsInt64(":id")
release, err := repo_model.GetReleaseByID(ctx, releaseID)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound()
return
}
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
return
}
if release.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound()
return
}
if err := release.LoadAttributes(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release).Attachments)
}
// CreateReleaseAttachment creates an attachment and saves the given file
func CreateReleaseAttachment(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/releases/{id}/assets repository repoCreateReleaseAttachment
// ---
// summary: Create a release attachment
// produces:
// - application/json
// consumes:
// - multipart/form-data
// 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: id
// in: path
// description: id of the release
// type: integer
// format: int64
// required: true
// - name: name
// in: query
// description: name of the attachment
// type: string
// required: false
// - name: attachment
// in: formData
// description: attachment to upload
// type: file
// required: true
// responses:
// "201":
// "$ref": "#/responses/Attachment"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
// Check if attachments are enabled
if !setting.Attachment.Enabled {
ctx.NotFound("Attachment is not enabled")
return
}
// Check if release exists an load release
releaseID := ctx.ParamsInt64(":id")
if !checkReleaseMatchRepo(ctx, releaseID) {
return
}
// Get uploaded file from request
file, header, err := ctx.Req.FormFile("attachment")
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetFile", err)
return
}
defer file.Close()
filename := header.Filename
if query := ctx.FormString("name"); query != "" {
filename = query
}
// Create a new attachment and save the file
attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, header.Size, &repo_model.Attachment{
Name: filename,
UploaderID: ctx.Doer.ID,
RepoID: ctx.Repo.Repository.ID,
ReleaseID: releaseID,
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
ctx.Error(http.StatusBadRequest, "DetectContentType", err)
return
}
ctx.Error(http.StatusInternalServerError, "NewAttachment", err)
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
}
// EditReleaseAttachment updates the given attachment
func EditReleaseAttachment(ctx *context.APIContext) {
// swagger:operation PATCH /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoEditReleaseAttachment
// ---
// summary: Edit a release attachment
// produces:
// - application/json
// consumes:
// - 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: id
// in: path
// description: id of the release
// type: integer
// format: int64
// required: true
// - name: attachment_id
// in: path
// description: id of the attachment to edit
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/EditAttachmentOptions"
// responses:
// "201":
// "$ref": "#/responses/Attachment"
// "404":
// "$ref": "#/responses/notFound"
form := web.GetForm(ctx).(*api.EditAttachmentOptions)
// Check if release exists an load release
releaseID := ctx.ParamsInt64(":id")
if !checkReleaseMatchRepo(ctx, releaseID) {
return
}
attachID := ctx.ParamsInt64(":attachment_id")
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
ctx.NotFound()
return
}
ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
return
}
if attach.ReleaseID != releaseID {
log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID)
ctx.NotFound()
return
}
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
if form.Name != "" {
attach.Name = form.Name
}
if err := repo_model.UpdateAttachment(ctx, attach); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach)
}
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
}
// DeleteReleaseAttachment delete a given attachment
func DeleteReleaseAttachment(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoDeleteReleaseAttachment
// ---
// summary: Delete a release attachment
// 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: id
// in: path
// description: id of the release
// type: integer
// format: int64
// required: true
// - name: attachment_id
// in: path
// description: id of the attachment to delete
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
// Check if release exists an load release
releaseID := ctx.ParamsInt64(":id")
if !checkReleaseMatchRepo(ctx, releaseID) {
return
}
attachID := ctx.ParamsInt64(":attachment_id")
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
ctx.NotFound()
return
}
ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
return
}
if attach.ReleaseID != releaseID {
log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID)
ctx.NotFound()
return
}
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
if err := repo_model.DeleteAttachment(ctx, attach, true); err != nil {
ctx.Error(http.StatusInternalServerError, "DeleteAttachment", err)
return
}
ctx.Status(http.StatusNoContent)
}