social.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. ApiUrl: sec.Key("api_url").String(),
  43. Enabled: sec.Key("enabled").MustBool(),
  44. AllowedDomains: sec.Key("allowed_domains").Strings(" "),
  45. }
  46. if !info.Enabled {
  47. continue
  48. }
  49. setting.OAuthService.OAuthInfos[name] = info
  50. config := oauth2.Config{
  51. ClientID: info.ClientId,
  52. ClientSecret: info.ClientSecret,
  53. Endpoint: oauth2.Endpoint{
  54. AuthURL: info.AuthUrl,
  55. TokenURL: info.TokenUrl,
  56. },
  57. RedirectURL: strings.TrimSuffix(setting.AppUrl, "/") + SocialBaseUrl + name,
  58. Scopes: info.Scopes,
  59. }
  60. // GitHub.
  61. if name == "github" {
  62. setting.OAuthService.GitHub = true
  63. SocialMap["github"] = &SocialGithub{Config: &config, allowedDomains: info.AllowedDomains, ApiUrl: info.ApiUrl}
  64. }
  65. // Google.
  66. if name == "google" {
  67. setting.OAuthService.Google = true
  68. SocialMap["google"] = &SocialGoogle{Config: &config, allowedDomains: info.AllowedDomains}
  69. }
  70. }
  71. }
  72. func isEmailAllowed(email string, allowedDomains []string) bool {
  73. if len(allowedDomains) == 0 {
  74. return true
  75. }
  76. valid := false
  77. for _, domain := range allowedDomains {
  78. emailSuffix := fmt.Sprintf("@%s", domain)
  79. valid = valid || strings.HasSuffix(email, emailSuffix)
  80. }
  81. return valid
  82. }
  83. type SocialGithub struct {
  84. *oauth2.Config
  85. allowedDomains []string
  86. ApiUrl string
  87. }
  88. func (s *SocialGithub) Type() int {
  89. return int(models.GITHUB)
  90. }
  91. func (s *SocialGithub) IsEmailAllowed(email string) bool {
  92. return isEmailAllowed(email, s.allowedDomains)
  93. }
  94. func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
  95. var data struct {
  96. Id int `json:"id"`
  97. Name string `json:"login"`
  98. Email string `json:"email"`
  99. }
  100. var err error
  101. client := s.Client(oauth2.NoContext, token)
  102. r, err := client.Get(s.ApiUrl)
  103. if err != nil {
  104. return nil, err
  105. }
  106. defer r.Body.Close()
  107. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  108. return nil, err
  109. }
  110. return &BasicUserInfo{
  111. Identity: strconv.Itoa(data.Id),
  112. Name: data.Name,
  113. Email: data.Email,
  114. }, nil
  115. }
  116. // ________ .__
  117. // / _____/ ____ ____ ____ | | ____
  118. // / \ ___ / _ \ / _ \ / ___\| | _/ __ \
  119. // \ \_\ ( <_> | <_> ) /_/ > |_\ ___/
  120. // \______ /\____/ \____/\___ /|____/\___ >
  121. // \/ /_____/ \/
  122. type SocialGoogle struct {
  123. *oauth2.Config
  124. allowedDomains []string
  125. }
  126. func (s *SocialGoogle) Type() int {
  127. return int(models.GOOGLE)
  128. }
  129. func (s *SocialGoogle) IsEmailAllowed(email string) bool {
  130. return isEmailAllowed(email, s.allowedDomains)
  131. }
  132. func (s *SocialGoogle) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
  133. var data struct {
  134. Id string `json:"id"`
  135. Name string `json:"name"`
  136. Email string `json:"email"`
  137. }
  138. var err error
  139. reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
  140. client := s.Client(oauth2.NoContext, token)
  141. r, err := client.Get(reqUrl)
  142. if err != nil {
  143. return nil, err
  144. }
  145. defer r.Body.Close()
  146. if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
  147. return nil, err
  148. }
  149. return &BasicUserInfo{
  150. Identity: data.Id,
  151. Name: data.Name,
  152. Email: data.Email,
  153. }, nil
  154. }