social.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. package social
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "github.com/grafana/grafana/pkg/models"
  8. "github.com/grafana/grafana/pkg/setting"
  9. "golang.org/x/net/context"
  10. "golang.org/x/oauth2"
  11. )
  12. type BasicUserInfo struct {
  13. Identity string
  14. Name string
  15. Email string
  16. Login string
  17. Company string
  18. }
  19. type SocialConnector interface {
  20. Type() int
  21. UserInfo(token *oauth2.Token) (*BasicUserInfo, error)
  22. IsEmailAllowed(email string) bool
  23. AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
  24. Exchange(ctx context.Context, code string) (*oauth2.Token, error)
  25. }
  26. var (
  27. SocialBaseUrl = "/login/"
  28. SocialMap = make(map[string]SocialConnector)
  29. )
  30. func NewOAuthService() {
  31. setting.OAuthService = &setting.OAuther{}
  32. setting.OAuthService.OAuthInfos = make(map[string]*setting.OAuthInfo)
  33. allOauthes := []string{"github", "google"}
  34. for _, name := range allOauthes {
  35. sec := setting.Cfg.Section("auth." + name)
  36. info := &setting.OAuthInfo{
  37. ClientId: sec.Key("client_id").String(),
  38. ClientSecret: sec.Key("client_secret").String(),
  39. Scopes: sec.Key("scopes").Strings(" "),
  40. AuthUrl: sec.Key("auth_url").String(),
  41. TokenUrl: sec.Key("token_url").String(),
  42. Enabled: sec.Key("enabled").MustBool(),
  43. AllowedDomains: sec.Key("allowed_domains").Strings(" "),
  44. }
  45. if !info.Enabled {
  46. continue
  47. }
  48. setting.OAuthService.OAuthInfos[name] = info
  49. config := oauth2.Config{
  50. ClientID: info.ClientId,
  51. ClientSecret: info.ClientSecret,
  52. Endpoint: oauth2.Endpoint{
  53. AuthURL: info.AuthUrl,
  54. TokenURL: info.TokenUrl,
  55. },
  56. RedirectURL: strings.TrimSuffix(setting.AppUrl, "/") + SocialBaseUrl + name,
  57. Scopes: info.Scopes,
  58. }
  59. // GitHub.
  60. if name == "github" {
  61. setting.OAuthService.GitHub = true
  62. SocialMap["github"] = &SocialGithub{Config: &config, allowedDomains: info.AllowedDomains}
  63. }
  64. // Google.
  65. if name == "google" {
  66. setting.OAuthService.Google = true
  67. SocialMap["google"] = &SocialGoogle{Config: &config, allowedDomains: info.AllowedDomains}
  68. }
  69. }
  70. }
  71. func isEmailAllowed(email string, allowedDomains []string) bool {
  72. if len(allowedDomains) == 0 {
  73. return true
  74. }
  75. valid := false
  76. for _, domain := range allowedDomains {
  77. emailSuffix := fmt.Sprintf("@%s", domain)
  78. valid = valid || strings.HasSuffix(email, emailSuffix)
  79. }
  80. return valid
  81. }
  82. type SocialGithub struct {
  83. *oauth2.Config
  84. allowedDomains []string
  85. }
  86. func (s *SocialGithub) Type() int {
  87. return int(models.GITHUB)
  88. }
  89. func (s *SocialGithub) IsEmailAllowed(email string) bool {
  90. return isEmailAllowed(email, s.allowedDomains)
  91. }
  92. func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
  93. var data struct {
  94. Id int `json:"id"`
  95. Name string `json:"login"`
  96. Email string `json:"email"`
  97. }
  98. var err error
  99. client := s.Client(oauth2.NoContext, token)
  100. r, err := client.Get("https://api.github.com/user")
  101. if err != nil {
  102. return nil, err
  103. }
  104. defer r.Body.Close()
  105. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  106. return nil, err
  107. }
  108. return &BasicUserInfo{
  109. Identity: strconv.Itoa(data.Id),
  110. Name: data.Name,
  111. Email: data.Email,
  112. }, nil
  113. }
  114. // ________ .__
  115. // / _____/ ____ ____ ____ | | ____
  116. // / \ ___ / _ \ / _ \ / ___\| | _/ __ \
  117. // \ \_\ ( <_> | <_> ) /_/ > |_\ ___/
  118. // \______ /\____/ \____/\___ /|____/\___ >
  119. // \/ /_____/ \/
  120. type SocialGoogle struct {
  121. *oauth2.Config
  122. allowedDomains []string
  123. }
  124. func (s *SocialGoogle) Type() int {
  125. return int(models.GOOGLE)
  126. }
  127. func (s *SocialGoogle) IsEmailAllowed(email string) bool {
  128. return isEmailAllowed(email, s.allowedDomains)
  129. }
  130. func (s *SocialGoogle) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
  131. var data struct {
  132. Id string `json:"id"`
  133. Name string `json:"name"`
  134. Email string `json:"email"`
  135. }
  136. var err error
  137. reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
  138. client := s.Client(oauth2.NoContext, token)
  139. r, err := client.Get(reqUrl)
  140. if err != nil {
  141. return nil, err
  142. }
  143. defer r.Body.Close()
  144. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  145. return nil, err
  146. }
  147. return &BasicUserInfo{
  148. Identity: data.Id,
  149. Name: data.Name,
  150. Email: data.Email,
  151. }, nil
  152. }