social.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  2. // Copyright 2014 The Gogs Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package social
  6. import (
  7. "encoding/json"
  8. "io/ioutil"
  9. "net/http"
  10. "net/url"
  11. "strconv"
  12. "github.com/macaron-contrib/oauth2"
  13. "github.com/gogits/gogs/models"
  14. "github.com/gogits/gogs/modules/log"
  15. "github.com/gogits/gogs/modules/setting"
  16. )
  17. type BasicUserInfo struct {
  18. Identity string
  19. Name string
  20. Email string
  21. }
  22. type SocialConnector interface {
  23. Type() int
  24. UserInfo(*oauth2.Token, *url.URL) (*BasicUserInfo, error)
  25. }
  26. var (
  27. SocialMap = make(map[string]SocialConnector)
  28. )
  29. func NewOauthService() {
  30. if !setting.Cfg.Section("oauth").Key("ENABLED").MustBool() {
  31. return
  32. }
  33. oauth2.AppSubUrl = setting.AppSubUrl
  34. setting.OauthService = &setting.Oauther{}
  35. setting.OauthService.OauthInfos = make(map[string]*setting.OauthInfo)
  36. socialConfigs := make(map[string]*oauth2.Options)
  37. allOauthes := []string{"github", "google", "qq", "twitter", "weibo"}
  38. // Load all OAuth config data.
  39. for _, name := range allOauthes {
  40. sec := setting.Cfg.Section("oauth." + name)
  41. if !sec.Key("ENABLED").MustBool() {
  42. continue
  43. }
  44. setting.OauthService.OauthInfos[name] = &setting.OauthInfo{
  45. Options: oauth2.Options{
  46. ClientID: sec.Key("CLIENT_ID").String(),
  47. ClientSecret: sec.Key("CLIENT_SECRET").String(),
  48. Scopes: sec.Key("SCOPES").Strings(" "),
  49. PathLogin: "/user/login/oauth2/" + name,
  50. PathCallback: setting.AppSubUrl + "/user/login/" + name,
  51. RedirectURL: setting.AppUrl + "user/login/" + name,
  52. },
  53. AuthUrl: sec.Key("AUTH_URL").String(),
  54. TokenUrl: sec.Key("TOKEN_URL").String(),
  55. }
  56. socialConfigs[name] = &oauth2.Options{
  57. ClientID: setting.OauthService.OauthInfos[name].ClientID,
  58. ClientSecret: setting.OauthService.OauthInfos[name].ClientSecret,
  59. Scopes: setting.OauthService.OauthInfos[name].Scopes,
  60. }
  61. }
  62. enabledOauths := make([]string, 0, 10)
  63. // GitHub.
  64. if setting.Cfg.Section("oauth.github").Key("ENABLED").MustBool() {
  65. setting.OauthService.GitHub = true
  66. newGitHubOauth(socialConfigs["github"])
  67. enabledOauths = append(enabledOauths, "GitHub")
  68. }
  69. // Google.
  70. if setting.Cfg.Section("oauth.google").Key("ENABLED").MustBool() {
  71. setting.OauthService.Google = true
  72. newGoogleOauth(socialConfigs["google"])
  73. enabledOauths = append(enabledOauths, "Google")
  74. }
  75. // QQ.
  76. if setting.Cfg.Section("oauth.qq").Key("ENABLED").MustBool() {
  77. setting.OauthService.Tencent = true
  78. newTencentOauth(socialConfigs["qq"])
  79. enabledOauths = append(enabledOauths, "QQ")
  80. }
  81. // Twitter.
  82. // if setting.Cfg.Section("oauth.twitter").Key( "ENABLED").MustBool() {
  83. // setting.OauthService.Twitter = true
  84. // newTwitterOauth(socialConfigs["twitter"])
  85. // enabledOauths = append(enabledOauths, "Twitter")
  86. // }
  87. // Weibo.
  88. if setting.Cfg.Section("oauth.weibo").Key("ENABLED").MustBool() {
  89. setting.OauthService.Weibo = true
  90. newWeiboOauth(socialConfigs["weibo"])
  91. enabledOauths = append(enabledOauths, "Weibo")
  92. }
  93. log.Info("Oauth Service Enabled %s", enabledOauths)
  94. }
  95. // ________.__ __ ___ ___ ___.
  96. // / _____/|__|/ |_ / | \ __ _\_ |__
  97. // / \ ___| \ __\/ ~ \ | \ __ \
  98. // \ \_\ \ || | \ Y / | / \_\ \
  99. // \______ /__||__| \___|_ /|____/|___ /
  100. // \/ \/ \/
  101. type SocialGithub struct {
  102. opts *oauth2.Options
  103. }
  104. func newGitHubOauth(opts *oauth2.Options) {
  105. SocialMap["github"] = &SocialGithub{opts}
  106. }
  107. func (s *SocialGithub) Type() int {
  108. return int(models.GITHUB)
  109. }
  110. func (s *SocialGithub) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
  111. transport := s.opts.NewTransportFromToken(token)
  112. var data struct {
  113. Id int `json:"id"`
  114. Name string `json:"login"`
  115. Email string `json:"email"`
  116. }
  117. r, err := transport.Client().Get("https://api.github.com/user")
  118. if err != nil {
  119. return nil, err
  120. }
  121. defer r.Body.Close()
  122. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  123. return nil, err
  124. }
  125. return &BasicUserInfo{
  126. Identity: strconv.Itoa(data.Id),
  127. Name: data.Name,
  128. Email: data.Email,
  129. }, nil
  130. }
  131. // ________ .__
  132. // / _____/ ____ ____ ____ | | ____
  133. // / \ ___ / _ \ / _ \ / ___\| | _/ __ \
  134. // \ \_\ ( <_> | <_> ) /_/ > |_\ ___/
  135. // \______ /\____/ \____/\___ /|____/\___ >
  136. // \/ /_____/ \/
  137. type SocialGoogle struct {
  138. opts *oauth2.Options
  139. }
  140. func (s *SocialGoogle) Type() int {
  141. return int(models.GOOGLE)
  142. }
  143. func newGoogleOauth(opts *oauth2.Options) {
  144. SocialMap["google"] = &SocialGoogle{opts}
  145. }
  146. func (s *SocialGoogle) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
  147. transport := s.opts.NewTransportFromToken(token)
  148. var data struct {
  149. Id string `json:"id"`
  150. Name string `json:"name"`
  151. Email string `json:"email"`
  152. }
  153. r, err := transport.Client().Get("https://www.googleapis.com/userinfo/v2/me")
  154. if err != nil {
  155. return nil, err
  156. }
  157. defer r.Body.Close()
  158. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  159. return nil, err
  160. }
  161. return &BasicUserInfo{
  162. Identity: data.Id,
  163. Name: data.Name,
  164. Email: data.Email,
  165. }, nil
  166. }
  167. // ________ ________
  168. // \_____ \ \_____ \
  169. // / / \ \ / / \ \
  170. // / \_/. \/ \_/. \
  171. // \_____\ \_/\_____\ \_/
  172. // \__> \__>
  173. type SocialTencent struct {
  174. opts *oauth2.Options
  175. }
  176. func newTencentOauth(opts *oauth2.Options) {
  177. SocialMap["qq"] = &SocialTencent{opts}
  178. }
  179. func (s *SocialTencent) Type() int {
  180. return int(models.QQ)
  181. }
  182. func (s *SocialTencent) UserInfo(token *oauth2.Token, URL *url.URL) (*BasicUserInfo, error) {
  183. r, err := http.Get("https://graph.z.qq.com/moc2/me?access_token=" + url.QueryEscape(token.AccessToken))
  184. if err != nil {
  185. return nil, err
  186. }
  187. defer r.Body.Close()
  188. body, err := ioutil.ReadAll(r.Body)
  189. if err != nil {
  190. return nil, err
  191. }
  192. vals, err := url.ParseQuery(string(body))
  193. if err != nil {
  194. return nil, err
  195. }
  196. return &BasicUserInfo{
  197. Identity: vals.Get("openid"),
  198. }, nil
  199. }
  200. // ___________ .__ __ __
  201. // \__ ___/_ _ _|__|/ |__/ |_ ___________
  202. // | | \ \/ \/ / \ __\ __\/ __ \_ __ \
  203. // | | \ /| || | | | \ ___/| | \/
  204. // |____| \/\_/ |__||__| |__| \___ >__|
  205. // \/
  206. // type SocialTwitter struct {
  207. // Token *oauth2.Token
  208. // *oauth2.Transport
  209. // }
  210. // func (s *SocialTwitter) Type() int {
  211. // return int(models.TWITTER)
  212. // }
  213. // func newTwitterOauth(config *oauth2.Config) {
  214. // SocialMap["twitter"] = &SocialTwitter{
  215. // Transport: &oauth.Transport{
  216. // Config: config,
  217. // Transport: http.DefaultTransport,
  218. // },
  219. // }
  220. // }
  221. // func (s *SocialTwitter) SetRedirectUrl(url string) {
  222. // s.Transport.Config.RedirectURL = url
  223. // }
  224. // //https://github.com/mrjones/oauth
  225. // func (s *SocialTwitter) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
  226. // // transport := &oauth.Transport{Token: token}
  227. // // var data struct {
  228. // // Id string `json:"id"`
  229. // // Name string `json:"name"`
  230. // // Email string `json:"email"`
  231. // // }
  232. // // var err error
  233. // // reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
  234. // // r, err := transport.Client().Get(reqUrl)
  235. // // if err != nil {
  236. // // return nil, err
  237. // // }
  238. // // defer r.Body.Close()
  239. // // if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  240. // // return nil, err
  241. // // }
  242. // // return &BasicUserInfo{
  243. // // Identity: data.Id,
  244. // // Name: data.Name,
  245. // // Email: data.Email,
  246. // // }, nil
  247. // return nil, nil
  248. // }
  249. // __ __ ._____.
  250. // / \ / \ ____ |__\_ |__ ____
  251. // \ \/\/ // __ \| || __ \ / _ \
  252. // \ /\ ___/| || \_\ ( <_> )
  253. // \__/\ / \___ >__||___ /\____/
  254. // \/ \/ \/
  255. type SocialWeibo struct {
  256. opts *oauth2.Options
  257. }
  258. func newWeiboOauth(opts *oauth2.Options) {
  259. SocialMap["weibo"] = &SocialWeibo{opts}
  260. }
  261. func (s *SocialWeibo) Type() int {
  262. return int(models.WEIBO)
  263. }
  264. func (s *SocialWeibo) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
  265. transport := s.opts.NewTransportFromToken(token)
  266. var data struct {
  267. Name string `json:"name"`
  268. }
  269. var urls = url.Values{
  270. "access_token": {token.AccessToken},
  271. "uid": {token.Extra("uid")},
  272. }
  273. reqUrl := "https://api.weibo.com/2/users/show.json"
  274. r, err := transport.Client().Get(reqUrl + "?" + urls.Encode())
  275. if err != nil {
  276. return nil, err
  277. }
  278. defer r.Body.Close()
  279. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  280. return nil, err
  281. }
  282. return &BasicUserInfo{
  283. Identity: token.Extra("uid"),
  284. Name: data.Name,
  285. }, nil
  286. }