You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gpg_key.go 8.5KB

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