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 }