diff --git a/providers/dropbox/dropbox.go b/providers/dropbox/dropbox.go index 262905806..6133626f2 100644 --- a/providers/dropbox/dropbox.go +++ b/providers/dropbox/dropbox.go @@ -2,9 +2,11 @@ package dropbox import ( + "bytes" "encoding/json" "errors" "io" + "io/ioutil" "net/http" "strings" @@ -25,6 +27,7 @@ type Provider struct { ClientKey string Secret string CallbackURL string + AccountURL string HTTPClient *http.Client config *oauth2.Config providerName string @@ -44,6 +47,7 @@ func New(clientKey, secret, callbackURL string, scopes ...string) *Provider { ClientKey: clientKey, Secret: secret, CallbackURL: callbackURL, + AccountURL: accountURL, providerName: "dropbox", } p.config = newConfig(p, scopes) @@ -87,7 +91,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) { return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName) } - req, err := http.NewRequest("POST", accountURL, nil) + req, err := http.NewRequest("POST", p.AccountURL, nil) if err != nil { return user, err } @@ -102,7 +106,17 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) { return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, resp.StatusCode) } - err = userFromReader(resp.Body, &user) + bits, err := ioutil.ReadAll(resp.Body) + if err != nil { + return user, err + } + + err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData) + if err != nil { + return user, err + } + + err = userFromReader(bytes.NewReader(bits), &user) return user, err } @@ -162,22 +176,29 @@ func newConfig(p *Provider, scopes []string) *oauth2.Config { func userFromReader(r io.Reader, user *goth.User) error { u := struct { - Name string `json:"display_name"` - NameDetails struct { - NickName string `json:"familiar_name"` - } `json:"name_details"` - Location string `json:"country"` - Email string `json:"email"` + AccountID string `json:"account_id"` + Name struct { + GivenName string `json:"given_name"` + Surname string `json:"surname"` + DisplayName string `json:"display_name"` + } `json:"name"` + Country string `json:"country"` + Email string `json:"email"` + ProfilePhotoURL string `json:"profile_photo_url"` }{} err := json.NewDecoder(r).Decode(&u) if err != nil { return err } + user.UserID = u.AccountID // The user's unique Dropbox ID. + user.FirstName = u.Name.GivenName + user.LastName = u.Name.Surname + user.Name = strings.TrimSpace(fmt.Sprintf("%s %s", u.Name.GivenName, u.Name.Surname)) + user.Description = u.Name.DisplayName // Full name plus parenthetical team naem user.Email = u.Email - user.Name = u.Name - user.NickName = u.NameDetails.NickName - user.UserID = u.Email // Dropbox doesn't provide a separate user ID - user.Location = u.Location + user.NickName = u.Email // Email is the dropbox username + user.Location = u.Country + user.AvatarURL = u.ProfilePhotoURL // May be blank return nil } diff --git a/providers/dropbox/dropbox_test.go b/providers/dropbox/dropbox_test.go index 7f5f77539..fe69232cb 100644 --- a/providers/dropbox/dropbox_test.go +++ b/providers/dropbox/dropbox_test.go @@ -1,6 +1,8 @@ package dropbox import ( + "net/http" + "net/http/httptest" "os" "testing" @@ -45,6 +47,41 @@ func Test_BeginAuth(t *testing.T) { a.Contains(s.AuthURL, "www.dropbox.com/oauth2/authorize") } +func Test_FetchUser(t *testing.T) { + accountPath := "/2/users/get_current_account" + + t.Parallel() + a := assert.New(t) + p := provider() + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + a.Equal(r.Header.Get("Authorization"), "Bearer 1234567890") + a.Equal(r.Method, "POST") + a.Equal(r.URL.Path, accountPath) + w.Write([]byte(testAccountResponse)) + })) + p.AccountURL = ts.URL + accountPath + + // AuthURL is superfluous for this test but ok + session, err := p.UnmarshalSession(`{"AuthURL":"https://www.dropbox.com/oauth2/authorize","Token":"1234567890"}`) + a.NoError(err) + user, err := p.FetchUser(session) + a.NoError(err) + a.Equal(user.UserID, "dbid:AAH4f99T0taONIb-OurWxbNQ6ywGRopQngc") + a.Equal(user.FirstName, "Franz") + a.Equal(user.LastName, "Ferdinand") + a.Equal(user.Name, "Franz Ferdinand") + a.Equal(user.Description, "Franz Ferdinand (Personal)") + a.Equal(user.NickName, "franz@dropbox.com") + a.Equal(user.Email, "franz@dropbox.com") + a.Equal(user.Location, "US") + a.Equal(user.AccessToken, "1234567890") + a.Equal(user.AccessTokenSecret, "") + a.Equal(user.AvatarURL, "https://dl-web.dropbox.com/account_photo/get/dbid%3AAAH4f99T0taONIb-OurWxbNQ6ywGRopQngc?vers=1453416673259\u0026size=128x128") + a.Equal(user.Provider, "dropbox") + a.Len(user.RawData, 14) +} + func Test_SessionFromJSON(t *testing.T) { t.Parallel() a := assert.New(t) @@ -79,3 +116,51 @@ func Test_GetAuthURL(t *testing.T) { url, _ := s.GetAuthURL() a.Equal(url, "/foo") } + +var testAccountResponse = ` +{ + "account_id": "dbid:AAH4f99T0taONIb-OurWxbNQ6ywGRopQngc", + "name": { + "given_name": "Franz", + "surname": "Ferdinand", + "familiar_name": "Franz", + "display_name": "Franz Ferdinand (Personal)", + "abbreviated_name": "FF" + }, + "email": "franz@dropbox.com", + "email_verified": true, + "disabled": false, + "locale": "en", + "referral_link": "https://db.tt/ZITNuhtI", + "is_paired": true, + "account_type": { + ".tag": "business" + }, + "root_info": { + ".tag": "user", + "root_namespace_id": "3235641", + "home_namespace_id": "3235641" + }, + "country": "US", + "team": { + "id": "dbtid:AAFdgehTzw7WlXhZJsbGCLePe8RvQGYDr-I", + "name": "Acme, Inc.", + "sharing_policies": { + "shared_folder_member_policy": { + ".tag": "team" + }, + "shared_folder_join_policy": { + ".tag": "from_anyone" + }, + "shared_link_create_policy": { + ".tag": "team_only" + } + }, + "office_addin_policy": { + ".tag": "disabled" + } + }, + "profile_photo_url": "https://dl-web.dropbox.com/account_photo/get/dbid%3AAAH4f99T0taONIb-OurWxbNQ6ywGRopQngc?vers=1453416673259\u0026size=128x128", + "team_member_id": "dbmid:AAHhy7WsR0x-u4ZCqiDl5Fz5zvuL3kmspwU" +} +`