You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

396 lines
9.7 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  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. "net/http"
  9. "net/url"
  10. "strconv"
  11. "strings"
  12. oauth "github.com/gogits/oauth2"
  13. "github.com/gogits/gogs/models"
  14. "github.com/gogits/gogs/modules/base"
  15. "github.com/gogits/gogs/modules/log"
  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. var (
  30. SocialBaseUrl = "/user/login"
  31. SocialMap = make(map[string]SocialConnector)
  32. )
  33. func NewOauthService() {
  34. if !base.Cfg.MustBool("oauth", "ENABLED") {
  35. return
  36. }
  37. base.OauthService = &base.Oauther{}
  38. base.OauthService.OauthInfos = make(map[string]*base.OauthInfo)
  39. socialConfigs := make(map[string]*oauth.Config)
  40. allOauthes := []string{"github", "google", "qq", "twitter", "weibo"}
  41. // Load all OAuth config data.
  42. for _, name := range allOauthes {
  43. base.OauthService.OauthInfos[name] = &base.OauthInfo{
  44. ClientId: base.Cfg.MustValue("oauth."+name, "CLIENT_ID"),
  45. ClientSecret: base.Cfg.MustValue("oauth."+name, "CLIENT_SECRET"),
  46. Scopes: base.Cfg.MustValue("oauth."+name, "SCOPES"),
  47. AuthUrl: base.Cfg.MustValue("oauth."+name, "AUTH_URL"),
  48. TokenUrl: base.Cfg.MustValue("oauth."+name, "TOKEN_URL"),
  49. }
  50. socialConfigs[name] = &oauth.Config{
  51. ClientId: base.OauthService.OauthInfos[name].ClientId,
  52. ClientSecret: base.OauthService.OauthInfos[name].ClientSecret,
  53. RedirectURL: strings.TrimSuffix(base.AppUrl, "/") + SocialBaseUrl + name,
  54. Scope: base.OauthService.OauthInfos[name].Scopes,
  55. AuthURL: base.OauthService.OauthInfos[name].AuthUrl,
  56. TokenURL: base.OauthService.OauthInfos[name].TokenUrl,
  57. }
  58. }
  59. enabledOauths := make([]string, 0, 10)
  60. // GitHub.
  61. if base.Cfg.MustBool("oauth.github", "ENABLED") {
  62. base.OauthService.GitHub = true
  63. newGitHubOauth(socialConfigs["github"])
  64. enabledOauths = append(enabledOauths, "GitHub")
  65. }
  66. // Google.
  67. if base.Cfg.MustBool("oauth.google", "ENABLED") {
  68. base.OauthService.Google = true
  69. newGoogleOauth(socialConfigs["google"])
  70. enabledOauths = append(enabledOauths, "Google")
  71. }
  72. // QQ.
  73. if base.Cfg.MustBool("oauth.qq", "ENABLED") {
  74. base.OauthService.Tencent = true
  75. newTencentOauth(socialConfigs["qq"])
  76. enabledOauths = append(enabledOauths, "QQ")
  77. }
  78. // Twitter.
  79. if base.Cfg.MustBool("oauth.twitter", "ENABLED") {
  80. base.OauthService.Twitter = true
  81. newTwitterOauth(socialConfigs["twitter"])
  82. enabledOauths = append(enabledOauths, "Twitter")
  83. }
  84. // Weibo.
  85. if base.Cfg.MustBool("oauth.weibo", "ENABLED") {
  86. base.OauthService.Weibo = true
  87. newWeiboOauth(socialConfigs["weibo"])
  88. enabledOauths = append(enabledOauths, "Weibo")
  89. }
  90. log.Info("Oauth Service Enabled %s", enabledOauths)
  91. }
  92. // ________.__ __ ___ ___ ___.
  93. // / _____/|__|/ |_ / | \ __ _\_ |__
  94. // / \ ___| \ __\/ ~ \ | \ __ \
  95. // \ \_\ \ || | \ Y / | / \_\ \
  96. // \______ /__||__| \___|_ /|____/|___ /
  97. // \/ \/ \/
  98. type SocialGithub struct {
  99. Token *oauth.Token
  100. *oauth.Transport
  101. }
  102. func (s *SocialGithub) Type() int {
  103. return models.OT_GITHUB
  104. }
  105. func newGitHubOauth(config *oauth.Config) {
  106. SocialMap["github"] = &SocialGithub{
  107. Transport: &oauth.Transport{
  108. Config: config,
  109. Transport: http.DefaultTransport,
  110. },
  111. }
  112. }
  113. func (s *SocialGithub) SetRedirectUrl(url string) {
  114. s.Transport.Config.RedirectURL = url
  115. }
  116. func (s *SocialGithub) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
  117. transport := &oauth.Transport{
  118. Token: token,
  119. }
  120. var data struct {
  121. Id int `json:"id"`
  122. Name string `json:"login"`
  123. Email string `json:"email"`
  124. }
  125. var err error
  126. r, err := transport.Client().Get(s.Transport.Scope)
  127. if err != nil {
  128. return nil, err
  129. }
  130. defer r.Body.Close()
  131. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  132. return nil, err
  133. }
  134. return &BasicUserInfo{
  135. Identity: strconv.Itoa(data.Id),
  136. Name: data.Name,
  137. Email: data.Email,
  138. }, nil
  139. }
  140. // ________ .__
  141. // / _____/ ____ ____ ____ | | ____
  142. // / \ ___ / _ \ / _ \ / ___\| | _/ __ \
  143. // \ \_\ ( <_> | <_> ) /_/ > |_\ ___/
  144. // \______ /\____/ \____/\___ /|____/\___ >
  145. // \/ /_____/ \/
  146. type SocialGoogle struct {
  147. Token *oauth.Token
  148. *oauth.Transport
  149. }
  150. func (s *SocialGoogle) Type() int {
  151. return models.OT_GOOGLE
  152. }
  153. func newGoogleOauth(config *oauth.Config) {
  154. SocialMap["google"] = &SocialGoogle{
  155. Transport: &oauth.Transport{
  156. Config: config,
  157. Transport: http.DefaultTransport,
  158. },
  159. }
  160. }
  161. func (s *SocialGoogle) SetRedirectUrl(url string) {
  162. s.Transport.Config.RedirectURL = url
  163. }
  164. func (s *SocialGoogle) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
  165. transport := &oauth.Transport{Token: token}
  166. var data struct {
  167. Id string `json:"id"`
  168. Name string `json:"name"`
  169. Email string `json:"email"`
  170. }
  171. var err error
  172. reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
  173. r, err := transport.Client().Get(reqUrl)
  174. if err != nil {
  175. return nil, err
  176. }
  177. defer r.Body.Close()
  178. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  179. return nil, err
  180. }
  181. return &BasicUserInfo{
  182. Identity: data.Id,
  183. Name: data.Name,
  184. Email: data.Email,
  185. }, nil
  186. }
  187. // ________ ________
  188. // \_____ \ \_____ \
  189. // / / \ \ / / \ \
  190. // / \_/. \/ \_/. \
  191. // \_____\ \_/\_____\ \_/
  192. // \__> \__>
  193. type SocialTencent struct {
  194. Token *oauth.Token
  195. *oauth.Transport
  196. reqUrl string
  197. }
  198. func (s *SocialTencent) Type() int {
  199. return models.OT_QQ
  200. }
  201. func newTencentOauth(config *oauth.Config) {
  202. SocialMap["qq"] = &SocialTencent{
  203. reqUrl: "https://open.t.qq.com/api/user/info",
  204. Transport: &oauth.Transport{
  205. Config: config,
  206. Transport: http.DefaultTransport,
  207. },
  208. }
  209. }
  210. func (s *SocialTencent) SetRedirectUrl(url string) {
  211. s.Transport.Config.RedirectURL = url
  212. }
  213. func (s *SocialTencent) UserInfo(token *oauth.Token, URL *url.URL) (*BasicUserInfo, error) {
  214. var data struct {
  215. Data struct {
  216. Id string `json:"openid"`
  217. Name string `json:"name"`
  218. Email string `json:"email"`
  219. } `json:"data"`
  220. }
  221. var err error
  222. // https://open.t.qq.com/api/user/info?
  223. //oauth_consumer_key=APP_KEY&
  224. //access_token=ACCESSTOKEN&openid=openid
  225. //clientip=CLIENTIP&oauth_version=2.a
  226. //scope=all
  227. var urls = url.Values{
  228. "oauth_consumer_key": {s.Transport.Config.ClientId},
  229. "access_token": {token.AccessToken},
  230. "openid": URL.Query()["openid"],
  231. "oauth_version": {"2.a"},
  232. "scope": {"all"},
  233. }
  234. r, err := http.Get(s.reqUrl + "?" + urls.Encode())
  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.Data.Id,
  244. Name: data.Data.Name,
  245. Email: data.Data.Email,
  246. }, nil
  247. }
  248. // ___________ .__ __ __
  249. // \__ ___/_ _ _|__|/ |__/ |_ ___________
  250. // | | \ \/ \/ / \ __\ __\/ __ \_ __ \
  251. // | | \ /| || | | | \ ___/| | \/
  252. // |____| \/\_/ |__||__| |__| \___ >__|
  253. // \/
  254. type SocialTwitter struct {
  255. Token *oauth.Token
  256. *oauth.Transport
  257. }
  258. func (s *SocialTwitter) Type() int {
  259. return models.OT_TWITTER
  260. }
  261. func newTwitterOauth(config *oauth.Config) {
  262. SocialMap["twitter"] = &SocialTwitter{
  263. Transport: &oauth.Transport{
  264. Config: config,
  265. Transport: http.DefaultTransport,
  266. },
  267. }
  268. }
  269. func (s *SocialTwitter) SetRedirectUrl(url string) {
  270. s.Transport.Config.RedirectURL = url
  271. }
  272. //https://github.com/mrjones/oauth
  273. func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
  274. // transport := &oauth.Transport{Token: token}
  275. // var data struct {
  276. // Id string `json:"id"`
  277. // Name string `json:"name"`
  278. // Email string `json:"email"`
  279. // }
  280. // var err error
  281. // reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
  282. // r, err := transport.Client().Get(reqUrl)
  283. // if err != nil {
  284. // return nil, err
  285. // }
  286. // defer r.Body.Close()
  287. // if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  288. // return nil, err
  289. // }
  290. // return &BasicUserInfo{
  291. // Identity: data.Id,
  292. // Name: data.Name,
  293. // Email: data.Email,
  294. // }, nil
  295. return nil, nil
  296. }
  297. // __ __ ._____.
  298. // / \ / \ ____ |__\_ |__ ____
  299. // \ \/\/ // __ \| || __ \ / _ \
  300. // \ /\ ___/| || \_\ ( <_> )
  301. // \__/\ / \___ >__||___ /\____/
  302. // \/ \/ \/
  303. type SocialWeibo struct {
  304. Token *oauth.Token
  305. *oauth.Transport
  306. }
  307. func (s *SocialWeibo) Type() int {
  308. return models.OT_WEIBO
  309. }
  310. func newWeiboOauth(config *oauth.Config) {
  311. SocialMap["weibo"] = &SocialWeibo{
  312. Transport: &oauth.Transport{
  313. Config: config,
  314. Transport: http.DefaultTransport,
  315. },
  316. }
  317. }
  318. func (s *SocialWeibo) SetRedirectUrl(url string) {
  319. s.Transport.Config.RedirectURL = url
  320. }
  321. func (s *SocialWeibo) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
  322. transport := &oauth.Transport{Token: token}
  323. var data struct {
  324. Name string `json:"name"`
  325. }
  326. var err error
  327. var urls = url.Values{
  328. "access_token": {token.AccessToken},
  329. "uid": {token.Extra["id_token"]},
  330. }
  331. reqUrl := "https://api.weibo.com/2/users/show.json"
  332. r, err := transport.Client().Get(reqUrl + "?" + urls.Encode())
  333. if err != nil {
  334. return nil, err
  335. }
  336. defer r.Body.Close()
  337. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  338. return nil, err
  339. }
  340. return &BasicUserInfo{
  341. Identity: token.Extra["id_token"],
  342. Name: data.Name,
  343. }, nil
  344. return nil, nil
  345. }