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