gitea源码

gpg_key.go 9.0KB

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