code.gitea.io/gitea@v1.21.7/routers/api/v1/user/gpg_key.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package user
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"strings"
    10  
    11  	asymkey_model "code.gitea.io/gitea/models/asymkey"
    12  	"code.gitea.io/gitea/models/db"
    13  	"code.gitea.io/gitea/modules/context"
    14  	api "code.gitea.io/gitea/modules/structs"
    15  	"code.gitea.io/gitea/modules/web"
    16  	"code.gitea.io/gitea/routers/api/v1/utils"
    17  	"code.gitea.io/gitea/services/convert"
    18  )
    19  
    20  func listGPGKeys(ctx *context.APIContext, uid int64, listOptions db.ListOptions) {
    21  	keys, err := asymkey_model.ListGPGKeys(ctx, uid, listOptions)
    22  	if err != nil {
    23  		ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
    24  		return
    25  	}
    26  
    27  	apiKeys := make([]*api.GPGKey, len(keys))
    28  	for i := range keys {
    29  		apiKeys[i] = convert.ToGPGKey(keys[i])
    30  	}
    31  
    32  	total, err := asymkey_model.CountUserGPGKeys(ctx, uid)
    33  	if err != nil {
    34  		ctx.InternalServerError(err)
    35  		return
    36  	}
    37  
    38  	ctx.SetTotalCountHeader(total)
    39  	ctx.JSON(http.StatusOK, &apiKeys)
    40  }
    41  
    42  // ListGPGKeys get the GPG key list of a user
    43  func ListGPGKeys(ctx *context.APIContext) {
    44  	// swagger:operation GET /users/{username}/gpg_keys user userListGPGKeys
    45  	// ---
    46  	// summary: List the given user's GPG keys
    47  	// produces:
    48  	// - application/json
    49  	// parameters:
    50  	// - name: username
    51  	//   in: path
    52  	//   description: username of user
    53  	//   type: string
    54  	//   required: true
    55  	// - name: page
    56  	//   in: query
    57  	//   description: page number of results to return (1-based)
    58  	//   type: integer
    59  	// - name: limit
    60  	//   in: query
    61  	//   description: page size of results
    62  	//   type: integer
    63  	// responses:
    64  	//   "200":
    65  	//     "$ref": "#/responses/GPGKeyList"
    66  	//   "404":
    67  	//     "$ref": "#/responses/notFound"
    68  
    69  	listGPGKeys(ctx, ctx.ContextUser.ID, utils.GetListOptions(ctx))
    70  }
    71  
    72  // ListMyGPGKeys get the GPG key list of the authenticated user
    73  func ListMyGPGKeys(ctx *context.APIContext) {
    74  	// swagger:operation GET /user/gpg_keys user userCurrentListGPGKeys
    75  	// ---
    76  	// summary: List the authenticated user's GPG keys
    77  	// parameters:
    78  	// - name: page
    79  	//   in: query
    80  	//   description: page number of results to return (1-based)
    81  	//   type: integer
    82  	// - name: limit
    83  	//   in: query
    84  	//   description: page size of results
    85  	//   type: integer
    86  	// produces:
    87  	// - application/json
    88  	// responses:
    89  	//   "200":
    90  	//     "$ref": "#/responses/GPGKeyList"
    91  
    92  	listGPGKeys(ctx, ctx.Doer.ID, utils.GetListOptions(ctx))
    93  }
    94  
    95  // GetGPGKey get the GPG key based on a id
    96  func GetGPGKey(ctx *context.APIContext) {
    97  	// swagger:operation GET /user/gpg_keys/{id} user userCurrentGetGPGKey
    98  	// ---
    99  	// summary: Get a GPG key
   100  	// produces:
   101  	// - application/json
   102  	// parameters:
   103  	// - name: id
   104  	//   in: path
   105  	//   description: id of key to get
   106  	//   type: integer
   107  	//   format: int64
   108  	//   required: true
   109  	// responses:
   110  	//   "200":
   111  	//     "$ref": "#/responses/GPGKey"
   112  	//   "404":
   113  	//     "$ref": "#/responses/notFound"
   114  
   115  	key, err := asymkey_model.GetGPGKeyForUserByID(ctx, ctx.Doer.ID, ctx.ParamsInt64(":id"))
   116  	if err != nil {
   117  		if asymkey_model.IsErrGPGKeyNotExist(err) {
   118  			ctx.NotFound()
   119  		} else {
   120  			ctx.Error(http.StatusInternalServerError, "GetGPGKeyByID", err)
   121  		}
   122  		return
   123  	}
   124  	ctx.JSON(http.StatusOK, convert.ToGPGKey(key))
   125  }
   126  
   127  // CreateUserGPGKey creates new GPG key to given user by ID.
   128  func CreateUserGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption, uid int64) {
   129  	token := asymkey_model.VerificationToken(ctx.Doer, 1)
   130  	lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
   131  
   132  	keys, err := asymkey_model.AddGPGKey(ctx, uid, form.ArmoredKey, token, form.Signature)
   133  	if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
   134  		keys, err = asymkey_model.AddGPGKey(ctx, uid, form.ArmoredKey, lastToken, form.Signature)
   135  	}
   136  	if err != nil {
   137  		HandleAddGPGKeyError(ctx, err, token)
   138  		return
   139  	}
   140  	ctx.JSON(http.StatusCreated, convert.ToGPGKey(keys[0]))
   141  }
   142  
   143  // GetVerificationToken returns the current token to be signed for this user
   144  func GetVerificationToken(ctx *context.APIContext) {
   145  	// swagger:operation GET /user/gpg_key_token user getVerificationToken
   146  	// ---
   147  	// summary: Get a Token to verify
   148  	// produces:
   149  	// - text/plain
   150  	// parameters:
   151  	// responses:
   152  	//   "200":
   153  	//     "$ref": "#/responses/string"
   154  	//   "404":
   155  	//     "$ref": "#/responses/notFound"
   156  
   157  	token := asymkey_model.VerificationToken(ctx.Doer, 1)
   158  	ctx.PlainText(http.StatusOK, token)
   159  }
   160  
   161  // VerifyUserGPGKey creates new GPG key to given user by ID.
   162  func VerifyUserGPGKey(ctx *context.APIContext) {
   163  	// swagger:operation POST /user/gpg_key_verify user userVerifyGPGKey
   164  	// ---
   165  	// summary: Verify a GPG key
   166  	// consumes:
   167  	// - application/json
   168  	// produces:
   169  	// - application/json
   170  	// responses:
   171  	//   "201":
   172  	//     "$ref": "#/responses/GPGKey"
   173  	//   "404":
   174  	//     "$ref": "#/responses/notFound"
   175  	//   "422":
   176  	//     "$ref": "#/responses/validationError"
   177  
   178  	form := web.GetForm(ctx).(*api.VerifyGPGKeyOption)
   179  	token := asymkey_model.VerificationToken(ctx.Doer, 1)
   180  	lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
   181  
   182  	form.KeyID = strings.TrimLeft(form.KeyID, "0")
   183  	if form.KeyID == "" {
   184  		ctx.NotFound()
   185  		return
   186  	}
   187  
   188  	_, err := asymkey_model.VerifyGPGKey(ctx.Doer.ID, form.KeyID, token, form.Signature)
   189  	if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
   190  		_, err = asymkey_model.VerifyGPGKey(ctx.Doer.ID, form.KeyID, lastToken, form.Signature)
   191  	}
   192  
   193  	if err != nil {
   194  		if asymkey_model.IsErrGPGInvalidTokenSignature(err) {
   195  			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))
   196  			return
   197  		}
   198  		ctx.Error(http.StatusInternalServerError, "VerifyUserGPGKey", err)
   199  	}
   200  
   201  	key, err := asymkey_model.GetGPGKeysByKeyID(ctx, form.KeyID)
   202  	if err != nil {
   203  		if asymkey_model.IsErrGPGKeyNotExist(err) {
   204  			ctx.NotFound()
   205  		} else {
   206  			ctx.Error(http.StatusInternalServerError, "GetGPGKeysByKeyID", err)
   207  		}
   208  		return
   209  	}
   210  	ctx.JSON(http.StatusOK, convert.ToGPGKey(key[0]))
   211  }
   212  
   213  // swagger:parameters userCurrentPostGPGKey
   214  type swaggerUserCurrentPostGPGKey struct {
   215  	// in:body
   216  	Form api.CreateGPGKeyOption
   217  }
   218  
   219  // CreateGPGKey create a GPG key belonging to the authenticated user
   220  func CreateGPGKey(ctx *context.APIContext) {
   221  	// swagger:operation POST /user/gpg_keys user userCurrentPostGPGKey
   222  	// ---
   223  	// summary: Create a GPG key
   224  	// consumes:
   225  	// - application/json
   226  	// produces:
   227  	// - application/json
   228  	// responses:
   229  	//   "201":
   230  	//     "$ref": "#/responses/GPGKey"
   231  	//   "404":
   232  	//     "$ref": "#/responses/notFound"
   233  	//   "422":
   234  	//     "$ref": "#/responses/validationError"
   235  
   236  	form := web.GetForm(ctx).(*api.CreateGPGKeyOption)
   237  	CreateUserGPGKey(ctx, *form, ctx.Doer.ID)
   238  }
   239  
   240  // DeleteGPGKey remove a GPG key belonging to the authenticated user
   241  func DeleteGPGKey(ctx *context.APIContext) {
   242  	// swagger:operation DELETE /user/gpg_keys/{id} user userCurrentDeleteGPGKey
   243  	// ---
   244  	// summary: Remove a GPG key
   245  	// produces:
   246  	// - application/json
   247  	// parameters:
   248  	// - name: id
   249  	//   in: path
   250  	//   description: id of key to delete
   251  	//   type: integer
   252  	//   format: int64
   253  	//   required: true
   254  	// responses:
   255  	//   "204":
   256  	//     "$ref": "#/responses/empty"
   257  	//   "403":
   258  	//     "$ref": "#/responses/forbidden"
   259  	//   "404":
   260  	//     "$ref": "#/responses/notFound"
   261  
   262  	if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.ParamsInt64(":id")); err != nil {
   263  		if asymkey_model.IsErrGPGKeyAccessDenied(err) {
   264  			ctx.Error(http.StatusForbidden, "", "You do not have access to this key")
   265  		} else {
   266  			ctx.Error(http.StatusInternalServerError, "DeleteGPGKey", err)
   267  		}
   268  		return
   269  	}
   270  
   271  	ctx.Status(http.StatusNoContent)
   272  }
   273  
   274  // HandleAddGPGKeyError handle add GPGKey error
   275  func HandleAddGPGKeyError(ctx *context.APIContext, err error, token string) {
   276  	switch {
   277  	case asymkey_model.IsErrGPGKeyAccessDenied(err):
   278  		ctx.Error(http.StatusUnprocessableEntity, "GPGKeyAccessDenied", "You do not have access to this GPG key")
   279  	case asymkey_model.IsErrGPGKeyIDAlreadyUsed(err):
   280  		ctx.Error(http.StatusUnprocessableEntity, "GPGKeyIDAlreadyUsed", "A key with the same id already exists")
   281  	case asymkey_model.IsErrGPGKeyParsing(err):
   282  		ctx.Error(http.StatusUnprocessableEntity, "GPGKeyParsing", err)
   283  	case asymkey_model.IsErrGPGNoEmailFound(err):
   284  		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))
   285  	case asymkey_model.IsErrGPGInvalidTokenSignature(err):
   286  		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))
   287  	default:
   288  		ctx.Error(http.StatusInternalServerError, "AddGPGKey", err)
   289  	}
   290  }