social.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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. "fmt"
  8. "net/url"
  9. "strings"
  10. "code.google.com/p/goauth2/oauth"
  11. "github.com/go-martini/martini"
  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 BasicUserInfo struct {
  18. Identity string
  19. Name string
  20. Email string
  21. }
  22. type SocialConnector interface {
  23. Type() int
  24. SetRedirectUrl(string)
  25. UserInfo(*oauth.Token, *url.URL) (*BasicUserInfo, error)
  26. AuthCodeURL(string) string
  27. Exchange(string) (*oauth.Token, error)
  28. }
  29. func extractPath(next string) string {
  30. n, err := url.Parse(next)
  31. if err != nil {
  32. return "/"
  33. }
  34. return n.Path
  35. }
  36. var (
  37. SocialBaseUrl = "/user/login"
  38. SocialMap = make(map[string]SocialConnector)
  39. )
  40. // github && google && ...
  41. func SocialSignIn(params martini.Params, ctx *middleware.Context) {
  42. if base.OauthService == nil || !base.OauthService.GitHub.Enabled {
  43. ctx.Handle(404, "social login not enabled", nil)
  44. return
  45. }
  46. next := extractPath(ctx.Query("next"))
  47. name := params["name"]
  48. connect, ok := SocialMap[name]
  49. if !ok {
  50. ctx.Handle(404, "social login", nil)
  51. return
  52. }
  53. code := ctx.Query("code")
  54. if code == "" {
  55. // redirect to social login page
  56. connect.SetRedirectUrl(strings.TrimSuffix(base.AppUrl, "/") + ctx.Req.URL.Host + ctx.Req.URL.Path)
  57. ctx.Redirect(connect.AuthCodeURL(next))
  58. return
  59. }
  60. // handle call back
  61. tk, err := connect.Exchange(code) // exchange for token
  62. if err != nil {
  63. log.Error("oauth2 handle callback error: %v", err)
  64. ctx.Handle(500, "exchange code error", nil)
  65. return
  66. }
  67. next = extractPath(ctx.Query("state"))
  68. log.Trace("success get token")
  69. ui, err := connect.UserInfo(tk, ctx.Req.URL)
  70. if err != nil {
  71. ctx.Handle(500, fmt.Sprintf("get infomation from %s error: %v", name, err), nil)
  72. log.Error("social connect error: %s", err)
  73. return
  74. }
  75. log.Info("social login: %s", ui)
  76. oa, err := models.GetOauth2(ui.Identity)
  77. switch err {
  78. case nil:
  79. ctx.Session.Set("userId", oa.User.Id)
  80. ctx.Session.Set("userName", oa.User.Name)
  81. case models.ErrOauth2RecordNotExists:
  82. oa = &models.Oauth2{}
  83. raw, _ := json.Marshal(tk) // json encode
  84. oa.Token = string(raw)
  85. oa.Uid = -1
  86. oa.Type = connect.Type()
  87. oa.Identity = ui.Identity
  88. log.Trace("oa: %v", oa)
  89. if err = models.AddOauth2(oa); err != nil {
  90. log.Error("add oauth2 %v", err) // 501
  91. return
  92. }
  93. case models.ErrOauth2NotAssociatedWithUser:
  94. next = "/user/sign_up"
  95. default:
  96. log.Error("other error: %v", err)
  97. ctx.Handle(500, err.Error(), nil)
  98. return
  99. }
  100. ctx.Session.Set("socialId", oa.Id)
  101. ctx.Session.Set("socialName", ui.Name)
  102. ctx.Session.Set("socialEmail", ui.Email)
  103. log.Trace("socialId: %v", oa.Id)
  104. ctx.Redirect(next)
  105. }