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.9KB

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