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.

212 lines
5.6 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Copyright 2014 The Gogs 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 cmd
  5. import (
  6. "fmt"
  7. "os"
  8. "os/exec"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "time"
  13. "github.com/Unknwon/com"
  14. "github.com/codegangsta/cli"
  15. "github.com/gogits/gogs/models"
  16. "github.com/gogits/gogs/modules/log"
  17. "github.com/gogits/gogs/modules/setting"
  18. "github.com/gogits/gogs/modules/uuid"
  19. )
  20. var CmdServ = cli.Command{
  21. Name: "serv",
  22. Usage: "This command should only be called by SSH shell",
  23. Description: `Serv provide access auth for repositories`,
  24. Action: runServ,
  25. Flags: []cli.Flag{},
  26. }
  27. func setup(logPath string) {
  28. setting.NewConfigContext()
  29. log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
  30. models.LoadModelsConfig()
  31. if models.UseSQLite3 {
  32. workDir, _ := setting.WorkDir()
  33. os.Chdir(workDir)
  34. }
  35. models.SetEngine()
  36. }
  37. func parseCmd(cmd string) (string, string) {
  38. ss := strings.SplitN(cmd, " ", 2)
  39. if len(ss) != 2 {
  40. return "", ""
  41. }
  42. verb, args := ss[0], ss[1]
  43. if verb == "git" {
  44. ss = strings.SplitN(args, " ", 2)
  45. args = ss[1]
  46. verb = fmt.Sprintf("%s %s", verb, ss[0])
  47. }
  48. return verb, strings.Replace(args, "'/", "'", 1)
  49. }
  50. var (
  51. COMMANDS_READONLY = map[string]models.AccessType{
  52. "git-upload-pack": models.WRITABLE,
  53. "git upload-pack": models.WRITABLE,
  54. "git-upload-archive": models.WRITABLE,
  55. }
  56. COMMANDS_WRITE = map[string]models.AccessType{
  57. "git-receive-pack": models.READABLE,
  58. "git receive-pack": models.READABLE,
  59. }
  60. )
  61. func In(b string, sl map[string]models.AccessType) bool {
  62. _, e := sl[b]
  63. return e
  64. }
  65. func runServ(k *cli.Context) {
  66. setup("serv.log")
  67. keys := strings.Split(os.Args[2], "-")
  68. if len(keys) != 2 {
  69. println("Gogs: auth file format error")
  70. log.GitLogger.Fatal(2, "Invalid auth file format: %s", os.Args[2])
  71. }
  72. keyId, err := com.StrTo(keys[1]).Int64()
  73. if err != nil {
  74. println("Gogs: auth file format error")
  75. log.GitLogger.Fatal(2, "Invalid auth file format: %v", err)
  76. }
  77. user, err := models.GetUserByKeyId(keyId)
  78. if err != nil {
  79. if err == models.ErrUserNotKeyOwner {
  80. println("Gogs: you are not the owner of SSH key")
  81. log.GitLogger.Fatal(2, "Invalid owner of SSH key: %d", keyId)
  82. }
  83. println("Gogs: internal error:", err)
  84. log.GitLogger.Fatal(2, "Fail to get user by key ID(%d): %v", keyId, err)
  85. }
  86. cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
  87. if cmd == "" {
  88. println("Hi", user.Name, "! You've successfully authenticated, but Gogs does not provide shell access.")
  89. return
  90. }
  91. verb, args := parseCmd(cmd)
  92. repoPath := strings.Trim(args, "'")
  93. rr := strings.SplitN(repoPath, "/", 2)
  94. if len(rr) != 2 {
  95. println("Gogs: unavailable repository", args)
  96. log.GitLogger.Fatal(2, "Unavailable repository: %v", args)
  97. }
  98. repoUserName := rr[0]
  99. repoName := strings.TrimSuffix(rr[1], ".git")
  100. isWrite := In(verb, COMMANDS_WRITE)
  101. isRead := In(verb, COMMANDS_READONLY)
  102. repoUser, err := models.GetUserByName(repoUserName)
  103. if err != nil {
  104. if err == models.ErrUserNotExist {
  105. println("Gogs: given repository owner are not registered")
  106. log.GitLogger.Fatal(2, "Unregistered owner: %s", repoUserName)
  107. }
  108. println("Gogs: internal error:", err)
  109. log.GitLogger.Fatal(2, "Fail to get repository owner(%s): %v", repoUserName, err)
  110. }
  111. // Access check.
  112. switch {
  113. case isWrite:
  114. has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE)
  115. if err != nil {
  116. println("Gogs: internal error:", err)
  117. log.GitLogger.Fatal(2, "Fail to check write access:", err)
  118. } else if !has {
  119. println("You have no right to write this repository")
  120. log.GitLogger.Fatal(2, "User %s has no right to write repository %s", user.Name, repoPath)
  121. }
  122. case isRead:
  123. repo, err := models.GetRepositoryByName(repoUser.Id, repoName)
  124. if err != nil {
  125. if err == models.ErrRepoNotExist {
  126. println("Gogs: given repository does not exist")
  127. log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName)
  128. }
  129. println("Gogs: internal error:", err)
  130. log.GitLogger.Fatal(2, "Fail to get repository: %v", err)
  131. }
  132. if !repo.IsPrivate {
  133. break
  134. }
  135. has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE)
  136. if err != nil {
  137. println("Gogs: internal error:", err)
  138. log.GitLogger.Fatal(2, "Fail to check read access:", err)
  139. } else if !has {
  140. println("You have no right to access this repository")
  141. log.GitLogger.Fatal(2, "User %s has no right to read repository %s", user.Name, repoPath)
  142. }
  143. default:
  144. println("Unknown command")
  145. return
  146. }
  147. uuid := uuid.NewV4().String()
  148. os.Setenv("uuid", uuid)
  149. gitcmd := exec.Command(verb, repoPath)
  150. gitcmd.Dir = setting.RepoRootPath
  151. gitcmd.Stdout = os.Stdout
  152. gitcmd.Stdin = os.Stdin
  153. gitcmd.Stderr = os.Stderr
  154. if err = gitcmd.Run(); err != nil {
  155. println("Gogs: internal error:", err.Error())
  156. log.GitLogger.Fatal(2, "Fail to execute git command: %v", err)
  157. }
  158. if isWrite {
  159. tasks, err := models.GetUpdateTasksByUuid(uuid)
  160. if err != nil {
  161. log.GitLogger.Fatal(2, "GetUpdateTasksByUuid: %v", err)
  162. }
  163. for _, task := range tasks {
  164. err = models.Update(task.RefName, task.OldCommitId, task.NewCommitId,
  165. user.Name, repoUserName, repoName, user.Id)
  166. if err != nil {
  167. log.GitLogger.Error(2, "Fail to update: %v", err)
  168. }
  169. }
  170. if err = models.DelUpdateTasksByUuid(uuid); err != nil {
  171. log.GitLogger.Fatal(2, "DelUpdateTasksByUuid: %v", err)
  172. }
  173. }
  174. // Update key activity.
  175. key, err := models.GetPublicKeyById(keyId)
  176. if err != nil {
  177. log.GitLogger.Fatal(2, "GetPublicKeyById: %v", err)
  178. }
  179. key.Updated = time.Now()
  180. if err = models.UpdatePublicKey(key); err != nil {
  181. log.GitLogger.Fatal(2, "UpdatePublicKey: %v", err)
  182. }
  183. }