|
|
@@ -1,19 +1,21 @@
|
|
|
package social
|
|
|
|
|
|
import (
|
|
|
+ "encoding/base64"
|
|
|
"encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"net/http"
|
|
|
"net/mail"
|
|
|
+ "regexp"
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/models"
|
|
|
|
|
|
"golang.org/x/oauth2"
|
|
|
)
|
|
|
|
|
|
-type GenericOAuth struct {
|
|
|
- *oauth2.Config
|
|
|
+type SocialGenericOAuth struct {
|
|
|
+ *SocialBase
|
|
|
allowedDomains []string
|
|
|
allowedOrganizations []string
|
|
|
apiUrl string
|
|
|
@@ -21,19 +23,19 @@ type GenericOAuth struct {
|
|
|
teamIds []int
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) Type() int {
|
|
|
+func (s *SocialGenericOAuth) Type() int {
|
|
|
return int(models.GENERIC)
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) IsEmailAllowed(email string) bool {
|
|
|
+func (s *SocialGenericOAuth) IsEmailAllowed(email string) bool {
|
|
|
return isEmailAllowed(email, s.allowedDomains)
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) IsSignupAllowed() bool {
|
|
|
+func (s *SocialGenericOAuth) IsSignupAllowed() bool {
|
|
|
return s.allowSignup
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) IsTeamMember(client *http.Client) bool {
|
|
|
+func (s *SocialGenericOAuth) IsTeamMember(client *http.Client) bool {
|
|
|
if len(s.teamIds) == 0 {
|
|
|
return true
|
|
|
}
|
|
|
@@ -54,7 +56,7 @@ func (s *GenericOAuth) IsTeamMember(client *http.Client) bool {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) IsOrganizationMember(client *http.Client) bool {
|
|
|
+func (s *SocialGenericOAuth) IsOrganizationMember(client *http.Client) bool {
|
|
|
if len(s.allowedOrganizations) == 0 {
|
|
|
return true
|
|
|
}
|
|
|
@@ -75,7 +77,7 @@ func (s *GenericOAuth) IsOrganizationMember(client *http.Client) bool {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) {
|
|
|
+func (s *SocialGenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) {
|
|
|
type Record struct {
|
|
|
Email string `json:"email"`
|
|
|
Primary bool `json:"primary"`
|
|
|
@@ -116,7 +118,7 @@ func (s *GenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) {
|
|
|
return email, nil
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) FetchTeamMemberships(client *http.Client) ([]int, error) {
|
|
|
+func (s *SocialGenericOAuth) FetchTeamMemberships(client *http.Client) ([]int, error) {
|
|
|
type Record struct {
|
|
|
Id int `json:"id"`
|
|
|
}
|
|
|
@@ -141,7 +143,7 @@ func (s *GenericOAuth) FetchTeamMemberships(client *http.Client) ([]int, error)
|
|
|
return ids, nil
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) FetchOrganizations(client *http.Client) ([]string, error) {
|
|
|
+func (s *SocialGenericOAuth) FetchOrganizations(client *http.Client) ([]string, error) {
|
|
|
type Record struct {
|
|
|
Login string `json:"login"`
|
|
|
}
|
|
|
@@ -176,17 +178,19 @@ type UserInfoJson struct {
|
|
|
Attributes map[string][]string `json:"attributes"`
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) UserInfo(client *http.Client) (*BasicUserInfo, error) {
|
|
|
+func (s *SocialGenericOAuth) UserInfo(client *http.Client, token *oauth2.Token) (*BasicUserInfo, error) {
|
|
|
var data UserInfoJson
|
|
|
|
|
|
- response, err := HttpGet(client, s.apiUrl)
|
|
|
- if err != nil {
|
|
|
- return nil, fmt.Errorf("Error getting user info: %s", err)
|
|
|
- }
|
|
|
+ if s.extractToken(&data, token) != true {
|
|
|
+ response, err := HttpGet(client, s.apiUrl)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("Error getting user info: %s", err)
|
|
|
+ }
|
|
|
|
|
|
- err = json.Unmarshal(response.Body, &data)
|
|
|
- if err != nil {
|
|
|
- return nil, fmt.Errorf("Error getting user info: %s", err)
|
|
|
+ err = json.Unmarshal(response.Body, &data)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("Error decoding user info JSON: %s", err)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
name, err := s.extractName(data)
|
|
|
@@ -221,7 +225,37 @@ func (s *GenericOAuth) UserInfo(client *http.Client) (*BasicUserInfo, error) {
|
|
|
return userInfo, nil
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) extractEmail(data UserInfoJson, client *http.Client) (string, error) {
|
|
|
+func (s *SocialGenericOAuth) extractToken(data *UserInfoJson, token *oauth2.Token) bool {
|
|
|
+ idToken := token.Extra("id_token")
|
|
|
+ if idToken == nil {
|
|
|
+ s.log.Debug("No id_token found", "token", token)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ jwtRegexp := regexp.MustCompile("^([-_a-zA-Z0-9]+)[.]([-_a-zA-Z0-9]+)[.]([-_a-zA-Z0-9]+)$")
|
|
|
+ matched := jwtRegexp.FindStringSubmatch(idToken.(string))
|
|
|
+ if matched == nil {
|
|
|
+ s.log.Debug("id_token is not in JWT format", "id_token", idToken.(string))
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ payload, err := base64.RawURLEncoding.DecodeString(matched[2])
|
|
|
+ if err != nil {
|
|
|
+ s.log.Error("Error base64 decoding id_token", "raw_payload", matched[2], "err", err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ err = json.Unmarshal(payload, data)
|
|
|
+ if err != nil {
|
|
|
+ s.log.Error("Error decoding id_token JSON", "payload", string(payload), "err", err)
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ s.log.Debug("Received id_token", "json", string(payload), "data", data)
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (s *SocialGenericOAuth) extractEmail(data UserInfoJson, client *http.Client) (string, error) {
|
|
|
if data.Email != "" {
|
|
|
return data.Email, nil
|
|
|
}
|
|
|
@@ -240,7 +274,7 @@ func (s *GenericOAuth) extractEmail(data UserInfoJson, client *http.Client) (str
|
|
|
return s.FetchPrivateEmail(client)
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) extractLogin(data UserInfoJson, email string) (string, error) {
|
|
|
+func (s *SocialGenericOAuth) extractLogin(data UserInfoJson, email string) (string, error) {
|
|
|
if data.Login != "" {
|
|
|
return data.Login, nil
|
|
|
}
|
|
|
@@ -252,7 +286,7 @@ func (s *GenericOAuth) extractLogin(data UserInfoJson, email string) (string, er
|
|
|
return email, nil
|
|
|
}
|
|
|
|
|
|
-func (s *GenericOAuth) extractName(data UserInfoJson) (string, error) {
|
|
|
+func (s *SocialGenericOAuth) extractName(data UserInfoJson) (string, error) {
|
|
|
if data.Name != "" {
|
|
|
return data.Name, nil
|
|
|
}
|