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.

serv.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. // Copyright 2019 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 private includes all internal routes. The package name internal is ideal but Golang is not allowed, so we use private as package name instead.
  5. package private
  6. import (
  7. "fmt"
  8. "net/http"
  9. "strings"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/context"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/private"
  14. "code.gitea.io/gitea/modules/setting"
  15. repo_service "code.gitea.io/gitea/services/repository"
  16. wiki_service "code.gitea.io/gitea/services/wiki"
  17. )
  18. // ServNoCommand returns information about the provided keyid
  19. func ServNoCommand(ctx *context.PrivateContext) {
  20. keyID := ctx.ParamsInt64(":keyid")
  21. if keyID <= 0 {
  22. ctx.JSON(http.StatusBadRequest, map[string]interface{}{
  23. "err": fmt.Sprintf("Bad key id: %d", keyID),
  24. })
  25. }
  26. results := private.KeyAndOwner{}
  27. key, err := models.GetPublicKeyByID(keyID)
  28. if err != nil {
  29. if models.IsErrKeyNotExist(err) {
  30. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  31. "err": fmt.Sprintf("Cannot find key: %d", keyID),
  32. })
  33. return
  34. }
  35. log.Error("Unable to get public key: %d Error: %v", keyID, err)
  36. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  37. "err": err.Error(),
  38. })
  39. return
  40. }
  41. results.Key = key
  42. if key.Type == models.KeyTypeUser || key.Type == models.KeyTypePrincipal {
  43. user, err := models.GetUserByID(key.OwnerID)
  44. if err != nil {
  45. if models.IsErrUserNotExist(err) {
  46. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  47. "err": fmt.Sprintf("Cannot find owner with id: %d for key: %d", key.OwnerID, keyID),
  48. })
  49. return
  50. }
  51. log.Error("Unable to get owner with id: %d for public key: %d Error: %v", key.OwnerID, keyID, err)
  52. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  53. "err": err.Error(),
  54. })
  55. return
  56. }
  57. if !user.IsActive || user.ProhibitLogin {
  58. ctx.JSON(http.StatusForbidden, map[string]interface{}{
  59. "err": "Your account is disabled.",
  60. })
  61. return
  62. }
  63. results.Owner = user
  64. }
  65. ctx.JSON(http.StatusOK, &results)
  66. }
  67. // ServCommand returns information about the provided keyid
  68. func ServCommand(ctx *context.PrivateContext) {
  69. keyID := ctx.ParamsInt64(":keyid")
  70. ownerName := ctx.Params(":owner")
  71. repoName := ctx.Params(":repo")
  72. mode := models.AccessMode(ctx.QueryInt("mode"))
  73. // Set the basic parts of the results to return
  74. results := private.ServCommandResults{
  75. RepoName: repoName,
  76. OwnerName: ownerName,
  77. KeyID: keyID,
  78. }
  79. // Now because we're not translating things properly let's just default some English strings here
  80. modeString := "read"
  81. if mode > models.AccessModeRead {
  82. modeString = "write to"
  83. }
  84. // The default unit we're trying to look at is code
  85. unitType := models.UnitTypeCode
  86. // Unless we're a wiki...
  87. if strings.HasSuffix(repoName, ".wiki") {
  88. // in which case we need to look at the wiki
  89. unitType = models.UnitTypeWiki
  90. // And we'd better munge the reponame and tell downstream we're looking at a wiki
  91. results.IsWiki = true
  92. results.RepoName = repoName[:len(repoName)-5]
  93. }
  94. owner, err := models.GetUserByName(results.OwnerName)
  95. if err != nil {
  96. log.Error("Unable to get repository owner: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
  97. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  98. "results": results,
  99. "type": "InternalServerError",
  100. "err": fmt.Sprintf("Unable to get repository owner: %s/%s %v", results.OwnerName, results.RepoName, err),
  101. })
  102. return
  103. }
  104. if !owner.IsOrganization() && !owner.IsActive {
  105. ctx.JSON(http.StatusForbidden, map[string]interface{}{
  106. "results": results,
  107. "type": "ForbiddenError",
  108. "err": "Repository cannot be accessed, you could retry it later",
  109. })
  110. return
  111. }
  112. // Now get the Repository and set the results section
  113. repoExist := true
  114. repo, err := models.GetRepositoryByName(owner.ID, results.RepoName)
  115. if err != nil {
  116. if models.IsErrRepoNotExist(err) {
  117. repoExist = false
  118. for _, verb := range ctx.QueryStrings("verb") {
  119. if "git-upload-pack" == verb {
  120. // User is fetching/cloning a non-existent repository
  121. log.Error("Failed authentication attempt (cannot find repository: %s/%s) from %s", results.OwnerName, results.RepoName, ctx.RemoteAddr())
  122. ctx.JSON(http.StatusNotFound, map[string]interface{}{
  123. "results": results,
  124. "type": "ErrRepoNotExist",
  125. "err": fmt.Sprintf("Cannot find repository: %s/%s", results.OwnerName, results.RepoName),
  126. })
  127. return
  128. }
  129. }
  130. } else {
  131. log.Error("Unable to get repository: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
  132. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  133. "results": results,
  134. "type": "InternalServerError",
  135. "err": fmt.Sprintf("Unable to get repository: %s/%s %v", results.OwnerName, results.RepoName, err),
  136. })
  137. return
  138. }
  139. }
  140. if repoExist {
  141. repo.Owner = owner
  142. repo.OwnerName = ownerName
  143. results.RepoID = repo.ID
  144. if repo.IsBeingCreated() {
  145. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  146. "results": results,
  147. "type": "InternalServerError",
  148. "err": "Repository is being created, you could retry after it finished",
  149. })
  150. return
  151. }
  152. // We can shortcut at this point if the repo is a mirror
  153. if mode > models.AccessModeRead && repo.IsMirror {
  154. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  155. "results": results,
  156. "type": "ErrMirrorReadOnly",
  157. "err": fmt.Sprintf("Mirror Repository %s/%s is read-only", results.OwnerName, results.RepoName),
  158. })
  159. return
  160. }
  161. }
  162. // Get the Public Key represented by the keyID
  163. key, err := models.GetPublicKeyByID(keyID)
  164. if err != nil {
  165. if models.IsErrKeyNotExist(err) {
  166. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  167. "results": results,
  168. "type": "ErrKeyNotExist",
  169. "err": fmt.Sprintf("Cannot find key: %d", keyID),
  170. })
  171. return
  172. }
  173. log.Error("Unable to get public key: %d Error: %v", keyID, err)
  174. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  175. "results": results,
  176. "type": "InternalServerError",
  177. "err": fmt.Sprintf("Unable to get key: %d Error: %v", keyID, err),
  178. })
  179. return
  180. }
  181. results.KeyName = key.Name
  182. results.KeyID = key.ID
  183. results.UserID = key.OwnerID
  184. // If repo doesn't exist, deploy key doesn't make sense
  185. if !repoExist && key.Type == models.KeyTypeDeploy {
  186. ctx.JSON(http.StatusNotFound, map[string]interface{}{
  187. "results": results,
  188. "type": "ErrRepoNotExist",
  189. "err": fmt.Sprintf("Cannot find repository %s/%s", results.OwnerName, results.RepoName),
  190. })
  191. return
  192. }
  193. // Deploy Keys have ownerID set to 0 therefore we can't use the owner
  194. // So now we need to check if the key is a deploy key
  195. // We'll keep hold of the deploy key here for permissions checking
  196. var deployKey *models.DeployKey
  197. var user *models.User
  198. if key.Type == models.KeyTypeDeploy {
  199. results.IsDeployKey = true
  200. var err error
  201. deployKey, err = models.GetDeployKeyByRepo(key.ID, repo.ID)
  202. if err != nil {
  203. if models.IsErrDeployKeyNotExist(err) {
  204. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  205. "results": results,
  206. "type": "ErrDeployKeyNotExist",
  207. "err": fmt.Sprintf("Public (Deploy) Key: %d:%s is not authorized to %s %s/%s.", key.ID, key.Name, modeString, results.OwnerName, results.RepoName),
  208. })
  209. return
  210. }
  211. log.Error("Unable to get deploy for public (deploy) key: %d in %-v Error: %v", key.ID, repo, err)
  212. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  213. "results": results,
  214. "type": "InternalServerError",
  215. "err": fmt.Sprintf("Unable to get Deploy Key for Public Key: %d:%s in %s/%s.", key.ID, key.Name, results.OwnerName, results.RepoName),
  216. })
  217. return
  218. }
  219. results.KeyName = deployKey.Name
  220. // FIXME: Deploy keys aren't really the owner of the repo pushing changes
  221. // however we don't have good way of representing deploy keys in hook.go
  222. // so for now use the owner of the repository
  223. results.UserName = results.OwnerName
  224. results.UserID = repo.OwnerID
  225. if !repo.Owner.KeepEmailPrivate {
  226. results.UserEmail = repo.Owner.Email
  227. }
  228. } else {
  229. // Get the user represented by the Key
  230. var err error
  231. user, err = models.GetUserByID(key.OwnerID)
  232. if err != nil {
  233. if models.IsErrUserNotExist(err) {
  234. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  235. "results": results,
  236. "type": "ErrUserNotExist",
  237. "err": fmt.Sprintf("Public Key: %d:%s owner %d does not exist.", key.ID, key.Name, key.OwnerID),
  238. })
  239. return
  240. }
  241. log.Error("Unable to get owner: %d for public key: %d:%s Error: %v", key.OwnerID, key.ID, key.Name, err)
  242. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  243. "results": results,
  244. "type": "InternalServerError",
  245. "err": fmt.Sprintf("Unable to get Owner: %d for Deploy Key: %d:%s in %s/%s.", key.OwnerID, key.ID, key.Name, ownerName, repoName),
  246. })
  247. return
  248. }
  249. if !user.IsActive || user.ProhibitLogin {
  250. ctx.JSON(http.StatusForbidden, map[string]interface{}{
  251. "err": "Your account is disabled.",
  252. })
  253. return
  254. }
  255. results.UserName = user.Name
  256. if !user.KeepEmailPrivate {
  257. results.UserEmail = user.Email
  258. }
  259. }
  260. // Don't allow pushing if the repo is archived
  261. if repoExist && mode > models.AccessModeRead && repo.IsArchived {
  262. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  263. "results": results,
  264. "type": "ErrRepoIsArchived",
  265. "err": fmt.Sprintf("Repo: %s/%s is archived.", results.OwnerName, results.RepoName),
  266. })
  267. return
  268. }
  269. // Permissions checking:
  270. if repoExist && (mode > models.AccessModeRead || repo.IsPrivate || setting.Service.RequireSignInView) {
  271. if key.Type == models.KeyTypeDeploy {
  272. if deployKey.Mode < mode {
  273. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  274. "results": results,
  275. "type": "ErrUnauthorized",
  276. "err": fmt.Sprintf("Deploy Key: %d:%s is not authorized to %s %s/%s.", key.ID, key.Name, modeString, results.OwnerName, results.RepoName),
  277. })
  278. return
  279. }
  280. } else {
  281. perm, err := models.GetUserRepoPermission(repo, user)
  282. if err != nil {
  283. log.Error("Unable to get permissions for %-v with key %d in %-v Error: %v", user, key.ID, repo, err)
  284. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  285. "results": results,
  286. "type": "InternalServerError",
  287. "err": fmt.Sprintf("Unable to get permissions for user %d:%s with key %d in %s/%s Error: %v", user.ID, user.Name, key.ID, results.OwnerName, results.RepoName, err),
  288. })
  289. return
  290. }
  291. userMode := perm.UnitAccessMode(unitType)
  292. if userMode < mode {
  293. log.Error("Failed authentication attempt for %s with key %s (not authorized to %s %s/%s) from %s", user.Name, key.Name, modeString, ownerName, repoName, ctx.RemoteAddr())
  294. ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
  295. "results": results,
  296. "type": "ErrUnauthorized",
  297. "err": fmt.Sprintf("User: %d:%s with Key: %d:%s is not authorized to %s %s/%s.", user.ID, user.Name, key.ID, key.Name, modeString, ownerName, repoName),
  298. })
  299. return
  300. }
  301. }
  302. }
  303. // We already know we aren't using a deploy key
  304. if !repoExist {
  305. owner, err := models.GetUserByName(ownerName)
  306. if err != nil {
  307. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  308. "results": results,
  309. "type": "InternalServerError",
  310. "err": fmt.Sprintf("Unable to get owner: %s %v", results.OwnerName, err),
  311. })
  312. return
  313. }
  314. if owner.IsOrganization() && !setting.Repository.EnablePushCreateOrg {
  315. ctx.JSON(http.StatusForbidden, map[string]interface{}{
  316. "results": results,
  317. "type": "ErrForbidden",
  318. "err": "Push to create is not enabled for organizations.",
  319. })
  320. return
  321. }
  322. if !owner.IsOrganization() && !setting.Repository.EnablePushCreateUser {
  323. ctx.JSON(http.StatusForbidden, map[string]interface{}{
  324. "results": results,
  325. "type": "ErrForbidden",
  326. "err": "Push to create is not enabled for users.",
  327. })
  328. return
  329. }
  330. repo, err = repo_service.PushCreateRepo(user, owner, results.RepoName)
  331. if err != nil {
  332. log.Error("pushCreateRepo: %v", err)
  333. ctx.JSON(http.StatusNotFound, map[string]interface{}{
  334. "results": results,
  335. "type": "ErrRepoNotExist",
  336. "err": fmt.Sprintf("Cannot find repository: %s/%s", results.OwnerName, results.RepoName),
  337. })
  338. return
  339. }
  340. results.RepoID = repo.ID
  341. }
  342. if results.IsWiki {
  343. // Ensure the wiki is enabled before we allow access to it
  344. if _, err := repo.GetUnit(models.UnitTypeWiki); err != nil {
  345. if models.IsErrUnitTypeNotExist(err) {
  346. ctx.JSON(http.StatusForbidden, map[string]interface{}{
  347. "results": results,
  348. "type": "ErrForbidden",
  349. "err": "repository wiki is disabled",
  350. })
  351. return
  352. }
  353. log.Error("Failed to get the wiki unit in %-v Error: %v", repo, err)
  354. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  355. "results": results,
  356. "type": "InternalServerError",
  357. "err": fmt.Sprintf("Failed to get the wiki unit in %s/%s Error: %v", ownerName, repoName, err),
  358. })
  359. return
  360. }
  361. // Finally if we're trying to touch the wiki we should init it
  362. if err = wiki_service.InitWiki(repo); err != nil {
  363. log.Error("Failed to initialize the wiki in %-v Error: %v", repo, err)
  364. ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
  365. "results": results,
  366. "type": "InternalServerError",
  367. "err": fmt.Sprintf("Failed to initialize the wiki in %s/%s Error: %v", ownerName, repoName, err),
  368. })
  369. return
  370. }
  371. }
  372. log.Debug("Serv Results:\nIsWiki: %t\nIsDeployKey: %t\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d",
  373. results.IsWiki,
  374. results.IsDeployKey,
  375. results.KeyID,
  376. results.KeyName,
  377. results.UserName,
  378. results.UserID,
  379. results.OwnerName,
  380. results.RepoName,
  381. results.RepoID)
  382. ctx.JSON(http.StatusOK, results)
  383. // We will update the keys in a different call.
  384. }