1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2025-10-05 19:30:58 +00:00
forgejo/routers/api/v1/user/gpg_key.go
toras9000 5c6645a8af Update Swagger annotations to reflect actual behavior (#9138)
## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.

----

This is a fix for the Swagger annotations reported in #8918.
Most of the changes are corrections to annotation comments, with some additions of wrapper struct definitions for Swagger auto‑generation.

Co-authored-by: toras9000 <toras9000@example.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9138
Reviewed-by: Lucas <sclu1034@noreply.codeberg.org>
Co-authored-by: toras9000 <toras9000@noreply.codeberg.org>
Co-committed-by: toras9000 <toras9000@noreply.codeberg.org>
2025-09-15 11:55:49 +02:00

339 lines
9.9 KiB
Go

// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"errors"
"fmt"
"net/http"
"strings"
asymkey_model "forgejo.org/models/asymkey"
"forgejo.org/models/db"
user_model "forgejo.org/models/user"
"forgejo.org/modules/setting"
api "forgejo.org/modules/structs"
"forgejo.org/modules/web"
"forgejo.org/routers/api/v1/utils"
"forgejo.org/services/context"
"forgejo.org/services/convert"
)
func listGPGKeys(ctx *context.APIContext, uid int64, listOptions db.ListOptions) {
keys, total, err := db.FindAndCount[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
ListOptions: listOptions,
OwnerID: uid,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
return
}
if err := asymkey_model.GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
return
}
apiKeys := make([]*api.GPGKey, len(keys))
for i := range keys {
apiKeys[i] = convert.ToGPGKey(keys[i])
}
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, &apiKeys)
}
// ListGPGKeys get the GPG key list of a user
func ListGPGKeys(ctx *context.APIContext) {
// swagger:operation GET /users/{username}/gpg_keys user userListGPGKeys
// ---
// summary: List the given user's GPG keys
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user
// type: string
// required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/GPGKeyList"
// "404":
// "$ref": "#/responses/notFound"
listGPGKeys(ctx, ctx.ContextUser.ID, utils.GetListOptions(ctx))
}
// ListMyGPGKeys get the GPG key list of the authenticated user
func ListMyGPGKeys(ctx *context.APIContext) {
// swagger:operation GET /user/gpg_keys user userCurrentListGPGKeys
// ---
// summary: List the authenticated user's GPG keys
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// produces:
// - application/json
// responses:
// "200":
// "$ref": "#/responses/GPGKeyList"
// "401":
// "$ref": "#/responses/unauthorized"
// "403":
// "$ref": "#/responses/forbidden"
listGPGKeys(ctx, ctx.Doer.ID, utils.GetListOptions(ctx))
}
// GetGPGKey get the GPG key based on a id
func GetGPGKey(ctx *context.APIContext) {
// swagger:operation GET /user/gpg_keys/{id} user userCurrentGetGPGKey
// ---
// summary: Get a GPG key
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of key to get
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/GPGKey"
// "401":
// "$ref": "#/responses/unauthorized"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
key, err := asymkey_model.GetGPGKeyForUserByID(ctx, ctx.Doer.ID, ctx.ParamsInt64(":id"))
if err != nil {
if asymkey_model.IsErrGPGKeyNotExist(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "GetGPGKeyByID", err)
}
return
}
if err := key.LoadSubKeys(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, "LoadSubKeys", err)
return
}
ctx.JSON(http.StatusOK, convert.ToGPGKey(key))
}
// CreateUserGPGKey creates new GPG key to given user by ID.
func CreateUserGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption, uid int64) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited"))
return
}
token := asymkey_model.VerificationToken(ctx.Doer, 1)
lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
keys, err := asymkey_model.AddGPGKey(ctx, uid, form.ArmoredKey, token, form.Signature)
if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
keys, err = asymkey_model.AddGPGKey(ctx, uid, form.ArmoredKey, lastToken, form.Signature)
}
if err != nil {
HandleAddGPGKeyError(ctx, err, token)
return
}
ctx.JSON(http.StatusCreated, convert.ToGPGKey(keys[0]))
}
// GetVerificationToken returns the current token to be signed for this user
func GetVerificationToken(ctx *context.APIContext) {
// swagger:operation GET /user/gpg_key_token user getVerificationToken
// ---
// summary: Get a Token to verify
// produces:
// - text/plain
// parameters:
// responses:
// "200":
// "$ref": "#/responses/string"
// "401":
// "$ref": "#/responses/unauthorized"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
token := asymkey_model.VerificationToken(ctx.Doer, 1)
ctx.PlainText(http.StatusOK, token)
}
// swagger:parameters userVerifyGPGKey
type swaggerUserVerifyGPGKey struct {
// in:body
Form api.VerifyGPGKeyOption
}
// VerifyUserGPGKey creates new GPG key to given user by ID.
func VerifyUserGPGKey(ctx *context.APIContext) {
// swagger:operation POST /user/gpg_key_verify user userVerifyGPGKey
// ---
// summary: Verify a GPG key
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/VerifyGPGKeyOption"
// responses:
// "201":
// "$ref": "#/responses/GPGKey"
// "401":
// "$ref": "#/responses/unauthorized"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.VerifyGPGKeyOption)
token := asymkey_model.VerificationToken(ctx.Doer, 1)
lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
form.KeyID = strings.TrimLeft(form.KeyID, "0")
if form.KeyID == "" {
ctx.NotFound()
return
}
_, err := asymkey_model.VerifyGPGKey(ctx, ctx.Doer.ID, form.KeyID, token, form.Signature)
if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
_, err = asymkey_model.VerifyGPGKey(ctx, ctx.Doer.ID, form.KeyID, lastToken, form.Signature)
}
if err != nil {
if asymkey_model.IsErrGPGInvalidTokenSignature(err) {
ctx.Error(http.StatusUnprocessableEntity, "GPGInvalidSignature", fmt.Sprintf("The provided GPG key, signature and token do not match or token is out of date. Provide a valid signature for the token: %s", token))
return
}
ctx.Error(http.StatusInternalServerError, "VerifyUserGPGKey", err)
}
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
KeyID: form.KeyID,
IncludeSubKeys: true,
})
if err != nil {
if asymkey_model.IsErrGPGKeyNotExist(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "GetGPGKeysByKeyID", err)
}
return
}
ctx.JSON(http.StatusOK, convert.ToGPGKey(keys[0]))
}
// swagger:parameters userCurrentPostGPGKey
type swaggerUserCurrentPostGPGKey struct {
// in:body
Form api.CreateGPGKeyOption
}
// CreateGPGKey adds a GPG public key doer's account
func CreateGPGKey(ctx *context.APIContext) {
// swagger:operation POST /user/gpg_keys user userCurrentPostGPGKey
// ---
// summary: Add a GPG public key to current user's account
// consumes:
// - application/json
// produces:
// - application/json
// responses:
// "201":
// "$ref": "#/responses/GPGKey"
// "401":
// "$ref": "#/responses/unauthorized"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateGPGKeyOption)
CreateUserGPGKey(ctx, *form, ctx.Doer.ID)
}
// DeleteGPGKey removes a GPG public key from doer's account
func DeleteGPGKey(ctx *context.APIContext) {
// swagger:operation DELETE /user/gpg_keys/{id} user userCurrentDeleteGPGKey
// ---
// summary: Remove a GPG public key from current user's account
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of key to delete
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "401":
// "$ref": "#/responses/unauthorized"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited"))
return
}
if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.ParamsInt64(":id")); err != nil {
ctx.Error(http.StatusInternalServerError, "DeleteGPGKey", err)
return
}
ctx.Status(http.StatusNoContent)
}
// HandleAddGPGKeyError handle add GPGKey error
func HandleAddGPGKeyError(ctx *context.APIContext, err error, token string) {
switch {
case asymkey_model.IsErrGPGKeyIDAlreadyUsed(err):
ctx.Error(http.StatusUnprocessableEntity, "GPGKeyIDAlreadyUsed", "A key with the same id already exists")
case asymkey_model.IsErrGPGKeyParsing(err):
ctx.Error(http.StatusUnprocessableEntity, "GPGKeyParsing", err)
case asymkey_model.IsErrGPGNoEmailFound(err):
ctx.Error(http.StatusNotFound, "GPGNoEmailFound", fmt.Sprintf("None of the emails attached to the GPG key could be found. It may still be added if you provide a valid signature for the token: %s", token))
case asymkey_model.IsErrGPGInvalidTokenSignature(err):
ctx.Error(http.StatusUnprocessableEntity, "GPGInvalidSignature", fmt.Sprintf("The provided GPG key, signature and token do not match or token is out of date. Provide a valid signature for the token: %s", token))
default:
ctx.Error(http.StatusInternalServerError, "AddGPGKey", err)
}
}