generic_oauth.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package social
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. "github.com/grafana/grafana/pkg/models"
  8. "golang.org/x/oauth2"
  9. )
  10. type GenericOAuth struct {
  11. *oauth2.Config
  12. allowedDomains []string
  13. allowedOrganizations []string
  14. apiUrl string
  15. allowSignup bool
  16. teamIds []int
  17. }
  18. func (s *GenericOAuth) Type() int {
  19. return int(models.GENERIC)
  20. }
  21. func (s *GenericOAuth) IsEmailAllowed(email string) bool {
  22. return isEmailAllowed(email, s.allowedDomains)
  23. }
  24. func (s *GenericOAuth) IsSignupAllowed() bool {
  25. return s.allowSignup
  26. }
  27. func (s *GenericOAuth) IsTeamMember(client *http.Client) bool {
  28. if len(s.teamIds) == 0 {
  29. return true
  30. }
  31. teamMemberships, err := s.FetchTeamMemberships(client)
  32. if err != nil {
  33. return false
  34. }
  35. for _, teamId := range s.teamIds {
  36. for _, membershipId := range teamMemberships {
  37. if teamId == membershipId {
  38. return true
  39. }
  40. }
  41. }
  42. return false
  43. }
  44. func (s *GenericOAuth) IsOrganizationMember(client *http.Client) bool {
  45. if len(s.allowedOrganizations) == 0 {
  46. return true
  47. }
  48. organizations, err := s.FetchOrganizations(client)
  49. if err != nil {
  50. return false
  51. }
  52. for _, allowedOrganization := range s.allowedOrganizations {
  53. for _, organization := range organizations {
  54. if organization == allowedOrganization {
  55. return true
  56. }
  57. }
  58. }
  59. return false
  60. }
  61. func (s *GenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) {
  62. type Record struct {
  63. Email string `json:"email"`
  64. Primary bool `json:"primary"`
  65. IsPrimary bool `json:"is_primary"`
  66. Verified bool `json:"verified"`
  67. IsConfirmed bool `json:"is_confirmed"`
  68. }
  69. response, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/emails"))
  70. if err != nil {
  71. return "", fmt.Errorf("Error getting email address: %s", err)
  72. }
  73. var records []Record
  74. err = json.Unmarshal(response.Body, &records)
  75. if err != nil {
  76. var data struct {
  77. Values []Record `json:"values"`
  78. }
  79. err = json.Unmarshal(response.Body, &data)
  80. if err != nil {
  81. return "", fmt.Errorf("Error getting email address: %s", err)
  82. }
  83. records = data.Values
  84. }
  85. var email = ""
  86. for _, record := range records {
  87. if record.Primary || record.IsPrimary {
  88. email = record.Email
  89. break
  90. }
  91. }
  92. return email, nil
  93. }
  94. func (s *GenericOAuth) FetchTeamMemberships(client *http.Client) ([]int, error) {
  95. type Record struct {
  96. Id int `json:"id"`
  97. }
  98. response, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/teams"))
  99. if err != nil {
  100. return nil, fmt.Errorf("Error getting team memberships: %s", err)
  101. }
  102. var records []Record
  103. err = json.Unmarshal(response.Body, &records)
  104. if err != nil {
  105. return nil, fmt.Errorf("Error getting team memberships: %s", err)
  106. }
  107. var ids = make([]int, len(records))
  108. for i, record := range records {
  109. ids[i] = record.Id
  110. }
  111. return ids, nil
  112. }
  113. func (s *GenericOAuth) FetchOrganizations(client *http.Client) ([]string, error) {
  114. type Record struct {
  115. Login string `json:"login"`
  116. }
  117. response, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/orgs"))
  118. if err != nil {
  119. return nil, fmt.Errorf("Error getting organizations: %s", err)
  120. }
  121. var records []Record
  122. err = json.Unmarshal(response.Body, &records)
  123. if err != nil {
  124. return nil, fmt.Errorf("Error getting organizations: %s", err)
  125. }
  126. var logins = make([]string, len(records))
  127. for i, record := range records {
  128. logins[i] = record.Login
  129. }
  130. return logins, nil
  131. }
  132. func (s *GenericOAuth) UserInfo(client *http.Client) (*BasicUserInfo, error) {
  133. var data struct {
  134. Name string `json:"name"`
  135. DisplayName string `json:"display_name"`
  136. Login string `json:"login"`
  137. Username string `json:"username"`
  138. Email string `json:"email"`
  139. Attributes map[string][]string `json:"attributes"`
  140. }
  141. response, err := HttpGet(client, s.apiUrl)
  142. if err != nil {
  143. return nil, fmt.Errorf("Error getting user info: %s", err)
  144. }
  145. err = json.Unmarshal(response.Body, &data)
  146. if err != nil {
  147. return nil, fmt.Errorf("Error getting user info: %s", err)
  148. }
  149. userInfo := &BasicUserInfo{
  150. Name: data.Name,
  151. Login: data.Login,
  152. Email: data.Email,
  153. }
  154. if userInfo.Email == "" && data.Attributes["email:primary"] != nil {
  155. userInfo.Email = data.Attributes["email:primary"][0]
  156. }
  157. if userInfo.Email == "" {
  158. userInfo.Email, err = s.FetchPrivateEmail(client)
  159. if err != nil {
  160. return nil, err
  161. }
  162. }
  163. if userInfo.Name == "" && data.DisplayName != "" {
  164. userInfo.Name = data.DisplayName
  165. }
  166. if userInfo.Login == "" && data.Username != "" {
  167. userInfo.Login = data.Username
  168. }
  169. if userInfo.Login == "" {
  170. userInfo.Login = data.Email
  171. }
  172. if !s.IsTeamMember(client) {
  173. return nil, errors.New("User not a member of one of the required teams")
  174. }
  175. if !s.IsOrganizationMember(client) {
  176. return nil, errors.New("User not a member of one of the required organizations")
  177. }
  178. return userInfo, nil
  179. }