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