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.

key.go 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2020 The Gitea Authors.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "fmt"
  8. "net/http"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/context"
  11. "code.gitea.io/gitea/modules/convert"
  12. "code.gitea.io/gitea/modules/setting"
  13. api "code.gitea.io/gitea/modules/structs"
  14. "code.gitea.io/gitea/routers/api/v1/utils"
  15. )
  16. // appendPrivateInformation appends the owner and key type information to api.PublicKey
  17. func appendPrivateInformation(apiKey *api.DeployKey, key *models.DeployKey, repository *models.Repository) (*api.DeployKey, error) {
  18. apiKey.ReadOnly = key.Mode == models.AccessModeRead
  19. if repository.ID == key.RepoID {
  20. apiKey.Repository = repository.APIFormat(key.Mode)
  21. } else {
  22. repo, err := models.GetRepositoryByID(key.RepoID)
  23. if err != nil {
  24. return apiKey, err
  25. }
  26. apiKey.Repository = repo.APIFormat(key.Mode)
  27. }
  28. return apiKey, nil
  29. }
  30. func composeDeployKeysAPILink(repoPath string) string {
  31. return setting.AppURL + "api/v1/repos/" + repoPath + "/keys/"
  32. }
  33. // ListDeployKeys list all the deploy keys of a repository
  34. func ListDeployKeys(ctx *context.APIContext) {
  35. // swagger:operation GET /repos/{owner}/{repo}/keys repository repoListKeys
  36. // ---
  37. // summary: List a repository's keys
  38. // produces:
  39. // - application/json
  40. // parameters:
  41. // - name: owner
  42. // in: path
  43. // description: owner of the repo
  44. // type: string
  45. // required: true
  46. // - name: repo
  47. // in: path
  48. // description: name of the repo
  49. // type: string
  50. // required: true
  51. // - name: key_id
  52. // in: query
  53. // description: the key_id to search for
  54. // type: integer
  55. // - name: fingerprint
  56. // in: query
  57. // description: fingerprint of the key
  58. // type: string
  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/DeployKeyList"
  70. var keys []*models.DeployKey
  71. var err error
  72. fingerprint := ctx.Query("fingerprint")
  73. keyID := ctx.QueryInt64("key_id")
  74. if fingerprint != "" || keyID != 0 {
  75. keys, err = models.SearchDeployKeys(ctx.Repo.Repository.ID, keyID, fingerprint)
  76. } else {
  77. keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
  78. }
  79. if err != nil {
  80. ctx.Error(http.StatusInternalServerError, "ListDeployKeys", err)
  81. return
  82. }
  83. apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
  84. apiKeys := make([]*api.DeployKey, len(keys))
  85. for i := range keys {
  86. if err = keys[i].GetContent(); err != nil {
  87. ctx.Error(http.StatusInternalServerError, "GetContent", err)
  88. return
  89. }
  90. apiKeys[i] = convert.ToDeployKey(apiLink, keys[i])
  91. if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == keys[i].RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
  92. apiKeys[i], _ = appendPrivateInformation(apiKeys[i], keys[i], ctx.Repo.Repository)
  93. }
  94. }
  95. ctx.JSON(http.StatusOK, &apiKeys)
  96. }
  97. // GetDeployKey get a deploy key by id
  98. func GetDeployKey(ctx *context.APIContext) {
  99. // swagger:operation GET /repos/{owner}/{repo}/keys/{id} repository repoGetKey
  100. // ---
  101. // summary: Get a repository's key by id
  102. // produces:
  103. // - application/json
  104. // parameters:
  105. // - name: owner
  106. // in: path
  107. // description: owner of the repo
  108. // type: string
  109. // required: true
  110. // - name: repo
  111. // in: path
  112. // description: name of the repo
  113. // type: string
  114. // required: true
  115. // - name: id
  116. // in: path
  117. // description: id of the key to get
  118. // type: integer
  119. // format: int64
  120. // required: true
  121. // responses:
  122. // "200":
  123. // "$ref": "#/responses/DeployKey"
  124. key, err := models.GetDeployKeyByID(ctx.ParamsInt64(":id"))
  125. if err != nil {
  126. if models.IsErrDeployKeyNotExist(err) {
  127. ctx.NotFound()
  128. } else {
  129. ctx.Error(http.StatusInternalServerError, "GetDeployKeyByID", err)
  130. }
  131. return
  132. }
  133. if err = key.GetContent(); err != nil {
  134. ctx.Error(http.StatusInternalServerError, "GetContent", err)
  135. return
  136. }
  137. apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
  138. apiKey := convert.ToDeployKey(apiLink, key)
  139. if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == key.RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
  140. apiKey, _ = appendPrivateInformation(apiKey, key, ctx.Repo.Repository)
  141. }
  142. ctx.JSON(http.StatusOK, apiKey)
  143. }
  144. // HandleCheckKeyStringError handle check key error
  145. func HandleCheckKeyStringError(ctx *context.APIContext, err error) {
  146. if models.IsErrSSHDisabled(err) {
  147. ctx.Error(http.StatusUnprocessableEntity, "", "SSH is disabled")
  148. } else if models.IsErrKeyUnableVerify(err) {
  149. ctx.Error(http.StatusUnprocessableEntity, "", "Unable to verify key content")
  150. } else {
  151. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid key content: %v", err))
  152. }
  153. }
  154. // HandleAddKeyError handle add key error
  155. func HandleAddKeyError(ctx *context.APIContext, err error) {
  156. switch {
  157. case models.IsErrDeployKeyAlreadyExist(err):
  158. ctx.Error(http.StatusUnprocessableEntity, "", "This key has already been added to this repository")
  159. case models.IsErrKeyAlreadyExist(err):
  160. ctx.Error(http.StatusUnprocessableEntity, "", "Key content has been used as non-deploy key")
  161. case models.IsErrKeyNameAlreadyUsed(err):
  162. ctx.Error(http.StatusUnprocessableEntity, "", "Key title has been used")
  163. default:
  164. ctx.Error(http.StatusInternalServerError, "AddKey", err)
  165. }
  166. }
  167. // CreateDeployKey create deploy key for a repository
  168. func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) {
  169. // swagger:operation POST /repos/{owner}/{repo}/keys repository repoCreateKey
  170. // ---
  171. // summary: Add a key to a repository
  172. // consumes:
  173. // - application/json
  174. // produces:
  175. // - application/json
  176. // parameters:
  177. // - name: owner
  178. // in: path
  179. // description: owner of the repo
  180. // type: string
  181. // required: true
  182. // - name: repo
  183. // in: path
  184. // description: name of the repo
  185. // type: string
  186. // required: true
  187. // - name: body
  188. // in: body
  189. // schema:
  190. // "$ref": "#/definitions/CreateKeyOption"
  191. // responses:
  192. // "201":
  193. // "$ref": "#/responses/DeployKey"
  194. // "422":
  195. // "$ref": "#/responses/validationError"
  196. content, err := models.CheckPublicKeyString(form.Key)
  197. if err != nil {
  198. HandleCheckKeyStringError(ctx, err)
  199. return
  200. }
  201. key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, form.ReadOnly)
  202. if err != nil {
  203. HandleAddKeyError(ctx, err)
  204. return
  205. }
  206. key.Content = content
  207. apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
  208. ctx.JSON(http.StatusCreated, convert.ToDeployKey(apiLink, key))
  209. }
  210. // DeleteDeploykey delete deploy key for a repository
  211. func DeleteDeploykey(ctx *context.APIContext) {
  212. // swagger:operation DELETE /repos/{owner}/{repo}/keys/{id} repository repoDeleteKey
  213. // ---
  214. // summary: Delete a key from a repository
  215. // parameters:
  216. // - name: owner
  217. // in: path
  218. // description: owner of the repo
  219. // type: string
  220. // required: true
  221. // - name: repo
  222. // in: path
  223. // description: name of the repo
  224. // type: string
  225. // required: true
  226. // - name: id
  227. // in: path
  228. // description: id of the key to delete
  229. // type: integer
  230. // format: int64
  231. // required: true
  232. // responses:
  233. // "204":
  234. // "$ref": "#/responses/empty"
  235. // "403":
  236. // "$ref": "#/responses/forbidden"
  237. if err := models.DeleteDeployKey(ctx.User, ctx.ParamsInt64(":id")); err != nil {
  238. if models.IsErrKeyAccessDenied(err) {
  239. ctx.Error(http.StatusForbidden, "", "You do not have access to this key")
  240. } else {
  241. ctx.Error(http.StatusInternalServerError, "DeleteDeployKey", err)
  242. }
  243. return
  244. }
  245. ctx.Status(http.StatusNoContent)
  246. }