social.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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 user
  5. import (
  6. "encoding/json"
  7. "net/http"
  8. "net/url"
  9. "strconv"
  10. "strings"
  11. "code.google.com/p/goauth2/oauth"
  12. "github.com/gogits/gogs/models"
  13. "github.com/gogits/gogs/modules/base"
  14. "github.com/gogits/gogs/modules/log"
  15. "github.com/gogits/gogs/modules/middleware"
  16. )
  17. type SocialConnector interface {
  18. Identity() string
  19. Name() string
  20. Email() string
  21. TokenString() string
  22. }
  23. type SocialGithub struct {
  24. data struct {
  25. Id int `json:"id"`
  26. Name string `json:"login"`
  27. Email string `json:"email"`
  28. }
  29. Token *oauth.Token
  30. }
  31. func (s *SocialGithub) Identity() string {
  32. return strconv.Itoa(s.data.Id)
  33. }
  34. func (s *SocialGithub) Name() string {
  35. return s.data.Name
  36. }
  37. func (s *SocialGithub) Email() string {
  38. return s.data.Email
  39. }
  40. func (s *SocialGithub) TokenString() string {
  41. data, _ := json.Marshal(s.Token)
  42. return string(data)
  43. }
  44. // Github API refer: https://developer.github.com/v3/users/
  45. func (s *SocialGithub) Update() error {
  46. scope := "https://api.github.com/user"
  47. transport := &oauth.Transport{
  48. Token: s.Token,
  49. }
  50. log.Debug("update github info")
  51. r, err := transport.Client().Get(scope)
  52. if err != nil {
  53. return err
  54. }
  55. defer r.Body.Close()
  56. return json.NewDecoder(r.Body).Decode(&s.data)
  57. }
  58. func extractPath(next string) string {
  59. n, err := url.Parse(next)
  60. if err != nil {
  61. return "/"
  62. }
  63. return n.Path
  64. }
  65. // github && google && ...
  66. func SocialSignIn(ctx *middleware.Context) {
  67. //if base.OauthService != nil && base.OauthService.GitHub.Enabled {
  68. //}
  69. var socid int64
  70. var ok bool
  71. next := extractPath(ctx.Query("next"))
  72. log.Debug("social signed check %s", next)
  73. if socid, ok = ctx.Session.Get("socialId").(int64); ok && socid != 0 {
  74. // already login
  75. ctx.Redirect(next)
  76. log.Info("login soc id: %v", socid)
  77. return
  78. }
  79. config := &oauth.Config{
  80. ClientId: base.OauthService.GitHub.ClientId,
  81. ClientSecret: base.OauthService.GitHub.ClientSecret,
  82. RedirectURL: strings.TrimSuffix(base.AppUrl, "/") + ctx.Req.URL.RequestURI(),
  83. Scope: base.OauthService.GitHub.Scopes,
  84. AuthURL: "https://github.com/login/oauth/authorize",
  85. TokenURL: "https://github.com/login/oauth/access_token",
  86. }
  87. transport := &oauth.Transport{
  88. Config: config,
  89. Transport: http.DefaultTransport,
  90. }
  91. code := ctx.Query("code")
  92. if code == "" {
  93. // redirect to social login page
  94. ctx.Redirect(config.AuthCodeURL(next))
  95. return
  96. }
  97. // handle call back
  98. tk, err := transport.Exchange(code)
  99. if err != nil {
  100. log.Error("oauth2 handle callback error: %v", err)
  101. return // FIXME, need error page 501
  102. }
  103. next = extractPath(ctx.Query("state"))
  104. log.Debug("success token: %v", tk)
  105. gh := &SocialGithub{Token: tk}
  106. if err = gh.Update(); err != nil {
  107. // FIXME: handle error page 501
  108. log.Error("connect with github error: %s", err)
  109. return
  110. }
  111. var soc SocialConnector = gh
  112. log.Info("login: %s", soc.Name())
  113. oa, err := models.GetOauth2(soc.Identity())
  114. switch err {
  115. case nil:
  116. ctx.Session.Set("userId", oa.User.Id)
  117. ctx.Session.Set("userName", oa.User.Name)
  118. case models.ErrOauth2RecordNotExists:
  119. oa = &models.Oauth2{}
  120. oa.Uid = -1
  121. oa.Type = models.OT_GITHUB
  122. oa.Token = soc.TokenString()
  123. oa.Identity = soc.Identity()
  124. log.Debug("oa: %v", oa)
  125. if err = models.AddOauth2(oa); err != nil {
  126. log.Error("add oauth2 %v", err) // 501
  127. return
  128. }
  129. case models.ErrOauth2NotAssociatedWithUser:
  130. ctx.Session.Set("socialId", oa.Id)
  131. ctx.Session.Set("socialName", soc.Name())
  132. ctx.Session.Set("socialEmail", soc.Email())
  133. ctx.Redirect("/user/sign_up")
  134. return
  135. default:
  136. log.Error(err.Error()) // FIXME: handle error page
  137. return
  138. }
  139. ctx.Session.Set("socialId", oa.Id)
  140. log.Debug("socialId: %v", oa.Id)
  141. ctx.Redirect(next)
  142. }