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

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