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 8.3KB

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