social.go 8.5 KB

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