Commit e6e5cc6e by Arve Knudsen Committed by GitHub

Chore: Simplify generic OAuth code (#26779)

* Generic OAuth: Simplify

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Rename test variables for consistency

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
parent b646a1d6
...@@ -36,6 +36,13 @@ func New(logger string, ctx ...interface{}) Logger { ...@@ -36,6 +36,13 @@ func New(logger string, ctx ...interface{}) Logger {
return Root.New(params...) return Root.New(params...)
} }
// NewWithLevel returns a new logger with a certain level.
func NewWithLevel(name string, level log15.Lvl) Logger {
logger := Root.New("logger", name)
logger.SetHandler(log15.LvlFilterHandler(level, log15.StreamHandler(os.Stdout, getLogFormat("console"))))
return logger
}
func Tracef(format string, v ...interface{}) { func Tracef(format string, v ...interface{}) {
var message string var message string
if len(v) > 0 { if len(v) > 0 {
......
...@@ -82,6 +82,7 @@ type UserInfoJson struct { ...@@ -82,6 +82,7 @@ type UserInfoJson struct {
Upn string `json:"upn"` Upn string `json:"upn"`
Attributes map[string][]string `json:"attributes"` Attributes map[string][]string `json:"attributes"`
rawJSON []byte rawJSON []byte
source string
} }
func (info *UserInfoJson) String() string { func (info *UserInfoJson) String() string {
...@@ -91,64 +92,97 @@ func (info *UserInfoJson) String() string { ...@@ -91,64 +92,97 @@ func (info *UserInfoJson) String() string {
} }
func (s *SocialGenericOAuth) UserInfo(client *http.Client, token *oauth2.Token) (*BasicUserInfo, error) { func (s *SocialGenericOAuth) UserInfo(client *http.Client, token *oauth2.Token) (*BasicUserInfo, error) {
var data UserInfoJson s.log.Debug("Getting user info")
var err error tokenData := s.extractFromToken(token)
apiData := s.extractFromAPI(client)
userInfo := &BasicUserInfo{} userInfo := &BasicUserInfo{}
for _, data := range []*UserInfoJson{tokenData, apiData} {
if s.extractToken(&data, token) { if data == nil {
s.fillUserInfo(userInfo, &data) continue
} }
if s.extractAPI(&data, client) { s.log.Debug("Processing external user info", "source", data.source, "data", data)
s.fillUserInfo(userInfo, &data)
}
if userInfo.Email == "" { if userInfo.Name == "" {
userInfo.Email, err = s.FetchPrivateEmail(client) if data.Name != "" {
if err != nil { s.log.Debug("Setting user info name from name field")
return nil, err userInfo.Name = data.Name
} else if data.DisplayName != "" {
s.log.Debug("Setting user info name from display name field")
userInfo.Name = data.DisplayName
} }
} }
if userInfo.Login == "" { if userInfo.Login == "" {
userInfo.Login = userInfo.Email if data.Login != "" {
s.log.Debug("Setting user info login from login field", "login", data.Login)
userInfo.Login = data.Login
} else {
if s.loginAttributePath != "" {
s.log.Debug("Searching for login among JSON", "loginAttributePath", s.loginAttributePath)
login, err := s.searchJSONForAttr(s.loginAttributePath, data.rawJSON)
if err != nil {
s.log.Error("Failed to search JSON for login attribute", "error", err)
} else if login != "" {
userInfo.Login = login
s.log.Debug("Setting user info login from login field", "login", login)
} }
if !s.IsTeamMember(client) {
return nil, errors.New("User not a member of one of the required teams")
} }
if !s.IsOrganizationMember(client) { if userInfo.Login == "" && data.Username != "" {
return nil, errors.New("User not a member of one of the required organizations") s.log.Debug("Setting user info login from username field", "username", data.Username)
userInfo.Login = data.Username
}
}
} }
s.log.Debug("User info result", "result", userInfo)
return userInfo, nil
}
func (s *SocialGenericOAuth) fillUserInfo(userInfo *BasicUserInfo, data *UserInfoJson) {
if userInfo.Email == "" { if userInfo.Email == "" {
userInfo.Email = s.extractEmail(data) userInfo.Email = s.extractEmail(data)
if userInfo.Email != "" {
s.log.Debug("Set user info email from extracted email", "email", userInfo.Email)
}
} }
if userInfo.Role == "" { if userInfo.Role == "" {
role, err := s.extractRole(data) role, err := s.extractRole(data)
if err != nil { if err != nil {
s.log.Error("Failed to extract role", "error", err) s.log.Error("Failed to extract role", "error", err)
} else { } else if role != "" {
s.log.Debug("Setting user info role from extracted role")
userInfo.Role = role userInfo.Role = role
} }
} }
if userInfo.Name == "" {
userInfo.Name = s.extractName(data)
} }
if userInfo.Email == "" {
var err error
userInfo.Email, err = s.FetchPrivateEmail(client)
if err != nil {
return nil, err
}
s.log.Debug("Setting email from fetched private email", "email", userInfo.Email)
}
if userInfo.Login == "" { if userInfo.Login == "" {
userInfo.Login = s.extractLogin(data) s.log.Debug("Defaulting to using email for user info login", "email", userInfo.Email)
userInfo.Login = userInfo.Email
} }
if !s.IsTeamMember(client) {
return nil, errors.New("user not a member of one of the required teams")
}
if !s.IsOrganizationMember(client) {
return nil, errors.New("user not a member of one of the required organizations")
}
s.log.Debug("User info result", "result", userInfo)
return userInfo, nil
} }
func (s *SocialGenericOAuth) extractToken(data *UserInfoJson, token *oauth2.Token) bool { func (s *SocialGenericOAuth) extractFromToken(token *oauth2.Token) *UserInfoJson {
var err error s.log.Debug("Extracting user info from OAuth token")
idTokenAttribute := "id_token" idTokenAttribute := "id_token"
if s.idTokenAttributeName != "" { if s.idTokenAttributeName != "" {
...@@ -159,50 +193,54 @@ func (s *SocialGenericOAuth) extractToken(data *UserInfoJson, token *oauth2.Toke ...@@ -159,50 +193,54 @@ func (s *SocialGenericOAuth) extractToken(data *UserInfoJson, token *oauth2.Toke
idToken := token.Extra(idTokenAttribute) idToken := token.Extra(idTokenAttribute)
if idToken == nil { if idToken == nil {
s.log.Debug("No id_token found", "token", token) s.log.Debug("No id_token found", "token", token)
return false return nil
} }
jwtRegexp := regexp.MustCompile("^([-_a-zA-Z0-9=]+)[.]([-_a-zA-Z0-9=]+)[.]([-_a-zA-Z0-9=]+)$") jwtRegexp := regexp.MustCompile("^([-_a-zA-Z0-9=]+)[.]([-_a-zA-Z0-9=]+)[.]([-_a-zA-Z0-9=]+)$")
matched := jwtRegexp.FindStringSubmatch(idToken.(string)) matched := jwtRegexp.FindStringSubmatch(idToken.(string))
if matched == nil { if matched == nil {
s.log.Debug("id_token is not in JWT format", "id_token", idToken.(string)) s.log.Debug("id_token is not in JWT format", "id_token", idToken.(string))
return false return nil
} }
data.rawJSON, err = base64.RawURLEncoding.DecodeString(matched[2]) rawJSON, err := base64.RawURLEncoding.DecodeString(matched[2])
if err != nil { if err != nil {
s.log.Error("Error base64 decoding id_token", "raw_payload", matched[2], "error", err) s.log.Error("Error base64 decoding id_token", "raw_payload", matched[2], "error", err)
return false return nil
} }
err = json.Unmarshal(data.rawJSON, data) var data UserInfoJson
if err != nil { if err := json.Unmarshal(rawJSON, &data); err != nil {
s.log.Error("Error decoding id_token JSON", "raw_json", string(data.rawJSON), "error", err) s.log.Error("Error decoding id_token JSON", "raw_json", string(data.rawJSON), "error", err)
data.rawJSON = []byte{} return nil
return false
} }
data.rawJSON = rawJSON
data.source = "token"
s.log.Debug("Received id_token", "raw_json", string(data.rawJSON), "data", data) s.log.Debug("Received id_token", "raw_json", string(data.rawJSON), "data", data)
return true return &data
} }
func (s *SocialGenericOAuth) extractAPI(data *UserInfoJson, client *http.Client) bool { func (s *SocialGenericOAuth) extractFromAPI(client *http.Client) *UserInfoJson {
s.log.Debug("Getting user info from API")
rawUserInfoResponse, err := HttpGet(client, s.apiUrl) rawUserInfoResponse, err := HttpGet(client, s.apiUrl)
if err != nil { if err != nil {
s.log.Debug("Error getting user info", "url", s.apiUrl, "error", err) s.log.Debug("Error getting user info from API", "url", s.apiUrl, "error", err)
return false return nil
} }
data.rawJSON = rawUserInfoResponse.Body
err = json.Unmarshal(data.rawJSON, data) rawJSON := rawUserInfoResponse.Body
if err != nil {
s.log.Error("Error decoding user info response", "raw_json", data.rawJSON, "error", err) var data UserInfoJson
data.rawJSON = []byte{} if err := json.Unmarshal(rawJSON, &data); err != nil {
return false s.log.Error("Error decoding user info response", "raw_json", rawJSON, "error", err)
return nil
} }
s.log.Debug("Received user info response", "raw_json", string(data.rawJSON), "data", data) data.rawJSON = rawJSON
return true data.source = "API"
s.log.Debug("Received user info response from API", "raw_json", string(rawJSON), "data", data)
return &data
} }
func (s *SocialGenericOAuth) extractEmail(data *UserInfoJson) string { func (s *SocialGenericOAuth) extractEmail(data *UserInfoJson) string {
...@@ -247,39 +285,6 @@ func (s *SocialGenericOAuth) extractRole(data *UserInfoJson) (string, error) { ...@@ -247,39 +285,6 @@ func (s *SocialGenericOAuth) extractRole(data *UserInfoJson) (string, error) {
return role, nil return role, nil
} }
func (s *SocialGenericOAuth) extractLogin(data *UserInfoJson) string {
if data.Login != "" {
return data.Login
}
if s.loginAttributePath != "" {
login, err := s.searchJSONForAttr(s.loginAttributePath, data.rawJSON)
if err != nil {
s.log.Error("Failed to search JSON for attribute", "error", err)
} else if login != "" {
return login
}
}
if data.Username != "" {
return data.Username
}
return ""
}
func (s *SocialGenericOAuth) extractName(data *UserInfoJson) string {
if data.Name != "" {
return data.Name
}
if data.DisplayName != "" {
return data.DisplayName
}
return ""
}
func (s *SocialGenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) { func (s *SocialGenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) {
type Record struct { type Record struct {
Email string `json:"email"` Email string `json:"email"`
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment