general_protocol.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright 2019 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 srcgraph
  5. import (
  6. "github.com/Unknwon/com"
  7. "net/http"
  8. "time"
  9. adapter "github.com/sourcegraph/external-service-adapter"
  10. log "gopkg.in/clog.v1"
  11. "github.com/gogs/gogs/models"
  12. "github.com/gogs/gogs/models/errors"
  13. "github.com/gogs/gogs/pkg/setting"
  14. )
  15. func NewHandler() http.HandlerFunc {
  16. h := adapter.NewHandler(externalService{}, adapter.Options{
  17. URL: setting.AppURL,
  18. PathPrefix: "/-/srcgraph",
  19. MaxPageLen: 100000, // Current version returns all repositories at once, does not matter
  20. TokenAsUsername: true,
  21. })
  22. return h.ServeHTTP
  23. }
  24. type externalService struct{}
  25. func (es externalService) ListRepos(ai adapter.AuthInfo, params adapter.Params) ([]*adapter.Repo, adapter.Page, error) {
  26. return es.listUserRepos("", ai, params)
  27. }
  28. func (es externalService) ListUserRepos(user string, ai adapter.AuthInfo, params adapter.Params) ([]*adapter.Repo, adapter.Page, error) {
  29. return es.listUserRepos(user, ai, params)
  30. }
  31. func toRepo(r *models.Repository) *adapter.Repo {
  32. var parent *adapter.Repo
  33. if r.IsFork {
  34. parent = toRepo(r.BaseRepo)
  35. }
  36. cl := r.CloneLink()
  37. return &adapter.Repo{
  38. ID: com.ToStr(r.ID),
  39. Name: r.Name,
  40. Slug: r.Name,
  41. FullName: r.FullName(),
  42. SCM: "git",
  43. Description: r.Description,
  44. IsPrivate: r.IsPrivate,
  45. Parent: parent,
  46. Links: []adapter.Link{
  47. {adapter.CloneSSH, cl.SSH},
  48. {adapter.CloneHTTP, cl.HTTPS},
  49. },
  50. }
  51. }
  52. func (es externalService) listUserRepos(username string, ai adapter.AuthInfo, params adapter.Params) ([]*adapter.Repo, adapter.Page, error) {
  53. authUser, err := userFromAuthInfo(ai)
  54. if err != nil {
  55. if errors.IsUserNotExist(err) {
  56. return nil, adapter.Page{}, errors.New("403 Forbidden")
  57. }
  58. log.Error(2, "Failed to get user from auth info: %v", err)
  59. return nil, adapter.Page{}, errors.New("500 Internal Server Error")
  60. }
  61. // Fall back to authenticated user
  62. if username == "" {
  63. username = authUser.Name
  64. }
  65. user, err := models.GetUserByName(username)
  66. if err != nil {
  67. if errors.IsUserNotExist(err) {
  68. return nil, adapter.Page{}, errors.New("404 Not Found")
  69. }
  70. log.Error(2, "Failed to get user by username %q: %v", username, err)
  71. return nil, adapter.Page{}, errors.New("500 Internal Server Error")
  72. }
  73. // Only list public repositories if user requests someone else's repository list,
  74. // or an organization isn't a member of.
  75. var ownRepos []*models.Repository
  76. if user.IsOrganization() {
  77. ownRepos, _, err = user.GetUserRepositories(authUser.ID, params.Page, user.NumRepos)
  78. } else {
  79. ownRepos, err = models.GetUserRepositories(&models.UserRepoOptions{
  80. UserID: user.ID,
  81. Private: authUser.ID == user.ID,
  82. Page: params.Page,
  83. PageSize: user.NumRepos,
  84. })
  85. }
  86. if err != nil {
  87. log.Error(2, "Failed to get repositories of user %q: %v", username, err)
  88. return nil, adapter.Page{}, errors.New("500 Internal Server Error")
  89. }
  90. if err = models.RepositoryList(ownRepos).LoadAttributes(); err != nil {
  91. log.Error(2, "Failed to load attributes of repositories: %v", err)
  92. return nil, adapter.Page{}, errors.New("500 Internal Server Error")
  93. }
  94. // Early return for querying other user's repositories
  95. if authUser.ID != user.ID {
  96. repos := make([]*adapter.Repo, len(ownRepos))
  97. for i := range ownRepos {
  98. repos[i] = toRepo(ownRepos[i])
  99. }
  100. return repos, adapter.Page{Last: 1}, nil
  101. }
  102. accessibleRepos, err := user.GetRepositoryAccesses()
  103. if err != nil {
  104. log.Error(2, "Failed to get accessible repositories of user %q: %v", username, err)
  105. return nil, adapter.Page{}, errors.New("500 Internal Server Error")
  106. }
  107. numOwnRepos := len(ownRepos)
  108. repos := make([]*adapter.Repo, numOwnRepos+len(accessibleRepos))
  109. for i := range ownRepos {
  110. repos[i] = toRepo(ownRepos[i])
  111. }
  112. i := numOwnRepos
  113. for repo := range accessibleRepos {
  114. repos[i] = toRepo(repo)
  115. i++
  116. }
  117. return repos, adapter.Page{Last: 1}, nil
  118. }
  119. func userFromAuthInfo(ai adapter.AuthInfo) (*models.User, error) {
  120. u, err := models.UserLogin(ai.Username, ai.Password, -1)
  121. if err != nil && !errors.IsUserNotExist(err) {
  122. return nil, err
  123. }
  124. if u != nil {
  125. if u.IsEnabledTwoFactor() {
  126. return nil, errors.New(
  127. "User with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password." +
  128. " Please create and use personal access token on user settings page.")
  129. }
  130. return u, nil
  131. }
  132. t, err := models.GetAccessTokenBySHA(ai.Username)
  133. if err != nil {
  134. if models.IsErrAccessTokenEmpty(err) || models.IsErrAccessTokenNotExist(err) {
  135. return nil, errors.UserNotExist{}
  136. }
  137. return nil, err
  138. }
  139. t.Updated = time.Now()
  140. u, err = models.GetUserByID(t.UID)
  141. if err != nil {
  142. return nil, err
  143. }
  144. return u, models.UpdateAccessToken(t)
  145. }