Merge pull request #5643 from influxdata/5399/oauth_bitbucket_emails
feat(oauth): support bitbucket primary email retrievalpull/5654/head
commit
44cddd5953
|
@ -1,9 +1,11 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
@ -163,6 +165,14 @@ type UserEmail struct {
|
|||
Email *string `json:"email,omitempty"`
|
||||
Primary *bool `json:"primary,omitempty"`
|
||||
Verified *bool `json:"verified,omitempty"`
|
||||
// support also indicators sent by bitbucket
|
||||
IsPrimary *bool `json:"is_primary,omitempty"`
|
||||
IsConfirmed *bool `json:"is_confirmed,omitempty"`
|
||||
}
|
||||
|
||||
// WrappedUserEmails represents (bitbucket's) structure that wraps email addresses in a values field
|
||||
type WrappedUserEmails struct {
|
||||
Emails []*UserEmail `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
// getPrimaryEmail gets the private email account for the authenticated user.
|
||||
|
@ -173,11 +183,27 @@ func (g *Generic) getPrimaryEmail(client *http.Client) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
emails := []*UserEmail{}
|
||||
if err = json.NewDecoder(r.Body).Decode(&emails); err != nil {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(body) == 0 {
|
||||
return "", errors.New("No response body from /emails")
|
||||
}
|
||||
emails := []*UserEmail{}
|
||||
if body[0] == '[' {
|
||||
// array of UserEmail
|
||||
if err = json.NewDecoder(bytes.NewReader(body)).Decode(&emails); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else if body[0] == '{' {
|
||||
// a struct with values that contain []*UserEmail{}
|
||||
wrapped := WrappedUserEmails{}
|
||||
if err = json.NewDecoder(bytes.NewReader(body)).Decode(&wrapped); err != nil {
|
||||
return "", err
|
||||
}
|
||||
emails = wrapped.Emails
|
||||
}
|
||||
|
||||
email, err := g.primaryEmail(emails)
|
||||
if err != nil {
|
||||
|
@ -188,12 +214,21 @@ func (g *Generic) getPrimaryEmail(client *http.Client) (string, error) {
|
|||
}
|
||||
|
||||
func (g *Generic) primaryEmail(emails []*UserEmail) (string, error) {
|
||||
var email string
|
||||
for _, m := range emails {
|
||||
if m != nil && m.Primary != nil && m.Verified != nil && m.Email != nil {
|
||||
return *m.Email, nil
|
||||
if m != nil && m.Email != nil && ((m.Verified != nil) || (m.IsConfirmed != nil)) {
|
||||
if email != "" {
|
||||
email = *m.Email
|
||||
}
|
||||
if (m.Primary != nil && *m.Primary) || (m.IsPrimary != nil && *m.IsPrimary) {
|
||||
return *m.Email, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("No primary email address")
|
||||
if email == "" {
|
||||
return "", errors.New("No primary email address")
|
||||
}
|
||||
return email, nil
|
||||
}
|
||||
|
||||
// ofDomain makes sure that the email is in one of the required domains
|
||||
|
|
|
@ -198,3 +198,76 @@ func TestGenericPrincipalIDDomain(t *testing.T) {
|
|||
t.Fatal("Retrieved email was not as expected. Want:", want, "Got:", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericPrincipalIDDomain_BitBucket(t *testing.T) {
|
||||
// Test of https://github.com/influxdata/chronograf/issues/5399
|
||||
t.Parallel()
|
||||
expectedGroup := "xyz.io"
|
||||
expectedPrincipalID := "pavel.zavora@xyz.io"
|
||||
emailsResponse := `{
|
||||
"pagelen": 10,
|
||||
"values": [
|
||||
{
|
||||
"is_primary": true,
|
||||
"is_confirmed": true,
|
||||
"type": "email",
|
||||
"email": "pavel.zavora@xyz.io",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/user/emails/pavel.zavora@xyz.io"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"page": 1,
|
||||
"size": 1
|
||||
}`
|
||||
|
||||
mockAPI := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
enc := json.NewEncoder(rw)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_ = enc.Encode(struct{}{})
|
||||
return
|
||||
}
|
||||
if r.URL.Path == "/emails" {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
rw.Write([]byte(emailsResponse))
|
||||
return
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
}))
|
||||
defer mockAPI.Close()
|
||||
|
||||
logger := clog.New(clog.ParseLevel("debug"))
|
||||
prov := oauth2.Generic{
|
||||
Logger: logger,
|
||||
Domains: []string{expectedGroup},
|
||||
}
|
||||
tt, err := oauth2.NewTestTripper(logger, mockAPI, http.DefaultTransport)
|
||||
if err != nil {
|
||||
t.Fatal("Error initializing TestTripper: err:", err)
|
||||
}
|
||||
|
||||
tc := &http.Client{
|
||||
Transport: tt,
|
||||
}
|
||||
|
||||
got, err := prov.PrincipalID(tc)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error while retrieiving PrincipalID: err:", err)
|
||||
}
|
||||
if got != expectedPrincipalID {
|
||||
t.Fatal("Retrieved email was not as expected. Want:", expectedPrincipalID, "Got:", got)
|
||||
}
|
||||
|
||||
group, err := prov.Group(tc)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error while retrieiving PrincipalID: err:", err)
|
||||
}
|
||||
if group != expectedGroup {
|
||||
t.Fatal("Retrieved group was not as expected. Want:", expectedGroup, "Got:", group)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue