Skip to content

Commit

Permalink
Use new people.get api instead of oauth2/v1/userinfo.
Browse files Browse the repository at this point in the history
Consolidate all uses into main helper class.

Details about new api: https://developers.google.com/+/api/latest/people/get

Format of returned response: https://developers.google.com/+/api/latest/people#resource

BUG=320354

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=267068

Review URL: https://codereview.chromium.org/257773002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@267374 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
rogerta@chromium.org committed Apr 30, 2014
1 parent fca61dc commit 431a075
Show file tree
Hide file tree
Showing 21 changed files with 290 additions and 216 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "content/public/browser/notification_source.h"
#include "content/public/test/test_utils.h"
#include "crypto/rsa_private_key.h"
#include "google_apis/gaia/gaia_oauth_client.h"
#include "google_apis/gaia/oauth2_token_service.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/url_request/test_url_fetcher_factory.h"
Expand Down Expand Up @@ -229,13 +230,15 @@ class UserImageManagerTest : public LoginManagerTest,

static_cast<OAuth2TokenService::Consumer*>(profile_downloader)->
OnGetTokenSuccess(NULL,
std::string(),
"token",
base::Time::Now() + base::TimeDelta::FromDays(1));

net::TestURLFetcher* fetcher = url_fetcher_factory->GetFetcherByID(0);
net::TestURLFetcher* fetcher =
url_fetcher_factory->GetFetcherByID(
gaia::GaiaOAuthClient::kUrlFetcherId);
ASSERT_TRUE(fetcher);
fetcher->SetResponseString(
"{ \"picture\": \"http://localhost/avatar.jpg\" }");
"{ \"image\": {\"url\": \"http://localhost/avatar.jpg\"} }");
fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
net::OK));
fetcher->set_response_code(200);
Expand Down
2 changes: 2 additions & 0 deletions chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ void PolicyOAuth2TokenFetcher::StartFetchingAccessToken() {
std::vector<std::string> scopes;
scopes.push_back(GaiaConstants::kDeviceManagementServiceOAuth);
scopes.push_back(GaiaConstants::kOAuthWrapBridgeUserInfoScope);
scopes.push_back(GaiaConstants::kGoogleUserInfoEmail);
scopes.push_back(GaiaConstants::kGoogleUserInfoProfile);
access_token_fetcher_.reset(
new OAuth2AccessTokenFetcherImpl(this,
system_context_getter_.get(),
Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/chromeos/policy/wildcard_login_checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace {

// Presence of this key in the userinfo response indicates whether the user is
// on a hosted domain.
const char kHostedDomainKey[] = "hd";
const char kHostedDomainKey[] = "domain";

// UMA histogram names.
const char kUMADelayPolicyTokenFetch[] =
Expand Down
13 changes: 10 additions & 3 deletions chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "content/public/browser/notification_source.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_oauth_client.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/http/http_status_code.h"
#include "net/url_request/test_url_fetcher_factory.h"
Expand Down Expand Up @@ -74,7 +75,7 @@ const char kValidTokenResponse[] =

const char kHostedDomainResponse[] =
"{"
" \"hd\": \"test.com\""
" \"domain\": \"test.com\""
"}";

class SigninManagerFake : public FakeSigninManager {
Expand Down Expand Up @@ -231,10 +232,15 @@ class UserPolicySigninServiceTest : public testing::Test {
return static_cast<FakeProfileOAuth2TokenService*>(service);
}

// Returns true if a request for policy information is active. A request
// is considered active if there is an active fetcher for an access token
// hosted domain information (i.e. the gaia oauth client) or some other
// fecther used in the code (id 0).
bool IsRequestActive() {
if (!GetTokenService()->GetPendingRequests().empty())
return true;
return url_factory_.GetFetcherByID(0);
return url_factory_.GetFetcherByID(0) ||
url_factory_.GetFetcherByID(gaia::GaiaOAuthClient::kUrlFetcherId);
}

void MakeOAuthTokenFetchSucceed() {
Expand All @@ -252,7 +258,8 @@ class UserPolicySigninServiceTest : public testing::Test {

void ReportHostedDomainStatus(bool is_hosted_domain) {
ASSERT_TRUE(IsRequestActive());
net::TestURLFetcher* fetcher = url_factory_.GetFetcherByID(0);
net::TestURLFetcher* fetcher =
url_factory_.GetFetcherByID(gaia::GaiaOAuthClient::kUrlFetcherId);
fetcher->set_response_code(net::HTTP_OK);
fetcher->SetResponseString(is_hosted_domain ? kHostedDomainResponse : "{}");
fetcher->delegate()->OnURLFetchComplete(fetcher);
Expand Down
120 changes: 51 additions & 69 deletions chrome/browser/profiles/profile_downloader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,16 @@ namespace {
const char kAuthorizationHeader[] =
"Authorization: Bearer %s";

// URL requesting user info.
const char kUserEntryURL[] =
"https://www.googleapis.com/oauth2/v1/userinfo?alt=json";

// OAuth scope for the user info API.
// For more info, see https://developers.google.com/accounts/docs/OAuth2LoginV1.
const char kAPIScope[] = "https://www.googleapis.com/auth/userinfo.profile";

// Path in JSON dictionary to user's photo thumbnail URL.
const char kPhotoThumbnailURLPath[] = "picture";
const char kPhotoThumbnailURLPath[] = "image.url";

// From the user info API, this field corresponds to the full name of the user.
const char kFullNamePath[] = "name";
const char kFullNamePath[] = "displayName";

const char kGivenNamePath[] = "given_name";
const char kGivenNamePath[] = "name.givenName";

// Path in JSON dictionary to user's preferred locale.
const char kLocalePath[] = "locale";
const char kLocalePath[] = "language";

// Path format for specifying thumbnail's size.
const char kThumbnailSizeFormat[] = "s%d-c";
Expand Down Expand Up @@ -133,7 +125,7 @@ bool GetImageURLWithSize(const GURL& old_url, int size, GURL* new_url) {
// Parses the entry response and gets the name and profile image URL.
// |data| should be the JSON formatted data return by the response.
// Returns false to indicate a parsing error.
bool ProfileDownloader::ParseProfileJSON(const std::string& data,
bool ProfileDownloader::ParseProfileJSON(base::DictionaryValue* root_dictionary,
base::string16* full_name,
base::string16* given_name,
std::string* url,
Expand All @@ -149,23 +141,6 @@ bool ProfileDownloader::ParseProfileJSON(const std::string& data,
*url = std::string();
*profile_locale = std::string();

int error_code = -1;
std::string error_message;
scoped_ptr<base::Value> root_value(base::JSONReader::ReadAndReturnError(
data, base::JSON_PARSE_RFC, &error_code, &error_message));
if (!root_value) {
LOG(ERROR) << "Error while parsing user entry response: "
<< error_message;
return false;
}
if (!root_value->IsType(base::Value::TYPE_DICTIONARY)) {
LOG(ERROR) << "JSON root is not a dictionary: "
<< root_value->GetType();
return false;
}
base::DictionaryValue* root_dictionary =
static_cast<base::DictionaryValue*>(root_value.get());

root_dictionary->GetString(kFullNamePath, full_name);
root_dictionary->GetString(kGivenNamePath, given_name);
root_dictionary->GetString(kLocalePath, profile_locale);
Expand Down Expand Up @@ -273,24 +248,17 @@ std::string ProfileDownloader::GetProfilePictureURL() const {
}

void ProfileDownloader::StartFetchingImage() {
DCHECK(!auth_token_.empty());
VLOG(1) << "Fetching user entry with token: " << auth_token_;
user_entry_fetcher_.reset(net::URLFetcher::Create(
GURL(kUserEntryURL), net::URLFetcher::GET, this));
user_entry_fetcher_->SetRequestContext(
delegate_->GetBrowserProfile()->GetRequestContext());
user_entry_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DO_NOT_SAVE_COOKIES);
if (!auth_token_.empty()) {
user_entry_fetcher_->SetExtraRequestHeaders(
base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
}
user_entry_fetcher_->Start();
gaia_client_.reset(new gaia::GaiaOAuthClient(
delegate_->GetBrowserProfile()->GetRequestContext()));
gaia_client_->GetUserInfo(auth_token_, 0, this);
}

void ProfileDownloader::StartFetchingOAuth2AccessToken() {
Profile* profile = delegate_->GetBrowserProfile();
OAuth2TokenService::ScopeSet scopes;
scopes.insert(kAPIScope);
scopes.insert(GaiaConstants::kGoogleUserInfoProfile);
ProfileOAuth2TokenService* token_service =
ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
oauth2_access_token_request_ = token_service->StartRequest(
Expand All @@ -307,27 +275,10 @@ ProfileDownloader::~ProfileDownloader() {
service->RemoveObserver(this);
}

void ProfileDownloader::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::string data;
source->GetResponseAsString(&data);
bool network_error =
source->GetStatus().status() != net::URLRequestStatus::SUCCESS;
if (network_error || source->GetResponseCode() != 200) {
LOG(WARNING) << "Fetching profile data failed";
DVLOG(1) << " Status: " << source->GetStatus().status();
DVLOG(1) << " Error: " << source->GetStatus().error();
DVLOG(1) << " Response code: " << source->GetResponseCode();
DVLOG(1) << " Url: " << source->GetURL().spec();
delegate_->OnProfileDownloadFailure(this, network_error ?
ProfileDownloaderDelegate::NETWORK_ERROR :
ProfileDownloaderDelegate::SERVICE_ERROR);
return;
}

if (source == user_entry_fetcher_.get()) {
void ProfileDownloader::OnGetUserInfoResponse(
scoped_ptr<base::DictionaryValue> user_info) {
std::string image_url;
if (!ParseProfileJSON(data,
if (!ParseProfileJSON(user_info.get(),
&profile_full_name_,
&profile_given_name_,
&image_url,
Expand Down Expand Up @@ -367,14 +318,45 @@ void ProfileDownloader::OnURLFetchComplete(const net::URLFetcher* source) {
base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
}
profile_image_fetcher_->Start();
} else if (source == profile_image_fetcher_.get()) {
VLOG(1) << "Decoding the image...";
scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
this, data, ImageDecoder::DEFAULT_CODEC);
scoped_refptr<base::MessageLoopProxy> task_runner =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
image_decoder->Start(task_runner);
}

void ProfileDownloader::OnOAuthError() {
LOG(WARNING) << "OnOAuthError: Fetching profile data failed";
delegate_->OnProfileDownloadFailure(
this, ProfileDownloaderDelegate::SERVICE_ERROR);
}

void ProfileDownloader::OnNetworkError(int response_code) {
LOG(WARNING) << "OnNetworkError: Fetching profile data failed";
DVLOG(1) << " Response code: " << response_code;
delegate_->OnProfileDownloadFailure(
this, ProfileDownloaderDelegate::NETWORK_ERROR);
}

void ProfileDownloader::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::string data;
source->GetResponseAsString(&data);
bool network_error =
source->GetStatus().status() != net::URLRequestStatus::SUCCESS;
if (network_error || source->GetResponseCode() != 200) {
LOG(WARNING) << "Fetching profile data failed";
DVLOG(1) << " Status: " << source->GetStatus().status();
DVLOG(1) << " Error: " << source->GetStatus().error();
DVLOG(1) << " Response code: " << source->GetResponseCode();
DVLOG(1) << " Url: " << source->GetURL().spec();
delegate_->OnProfileDownloadFailure(this, network_error ?
ProfileDownloaderDelegate::NETWORK_ERROR :
ProfileDownloaderDelegate::SERVICE_ERROR);
return;
}

VLOG(1) << "Decoding the image...";
scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
this, data, ImageDecoder::DEFAULT_CODEC);
scoped_refptr<base::MessageLoopProxy> task_runner =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
image_decoder->Start(task_runner);
}

void ProfileDownloader::OnImageDecoded(const ImageDecoder* decoder,
Expand Down
14 changes: 11 additions & 3 deletions chrome/browser/profiles/profile_downloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "chrome/browser/image_decoder.h"
#include "google_apis/gaia/gaia_oauth_client.h"
#include "google_apis/gaia/oauth2_token_service.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "third_party/skia/include/core/SkBitmap.h"
Expand All @@ -26,7 +27,8 @@ class URLFetcher;

// Downloads user profile information. The profile picture is decoded in a
// sandboxed process.
class ProfileDownloader : public net::URLFetcherDelegate,
class ProfileDownloader : public gaia::GaiaOAuthClient::Delegate,
public net::URLFetcherDelegate,
public ImageDecoder::Delegate,
public OAuth2TokenService::Observer,
public OAuth2TokenService::Consumer {
Expand Down Expand Up @@ -80,6 +82,12 @@ class ProfileDownloader : public net::URLFetcherDelegate,
FRIEND_TEST_ALL_PREFIXES(ProfileDownloaderTest, ParseData);
FRIEND_TEST_ALL_PREFIXES(ProfileDownloaderTest, DefaultURL);

// gaia::GaiaOAuthClient::Delegate implementation.
virtual void OnGetUserInfoResponse(
scoped_ptr<base::DictionaryValue> user_info) OVERRIDE;
virtual void OnOAuthError() OVERRIDE;
virtual void OnNetworkError(int response_code) OVERRIDE;

// Overriden from net::URLFetcherDelegate:
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;

Expand All @@ -101,7 +109,7 @@ class ProfileDownloader : public net::URLFetcherDelegate,
// Parses the entry response and gets the name, profile image URL and locale.
// |data| should be the JSON formatted data return by the response.
// Returns false to indicate a parsing error.
static bool ParseProfileJSON(const std::string& data,
static bool ParseProfileJSON(base::DictionaryValue* root_dictionary,
base::string16* full_name,
base::string16* given_name,
std::string* url,
Expand All @@ -123,7 +131,7 @@ class ProfileDownloader : public net::URLFetcherDelegate,
ProfileDownloaderDelegate* delegate_;
std::string account_id_;
std::string auth_token_;
scoped_ptr<net::URLFetcher> user_entry_fetcher_;
scoped_ptr<gaia::GaiaOAuthClient> gaia_client_;
scoped_ptr<net::URLFetcher> profile_image_fetcher_;
scoped_ptr<OAuth2TokenService::Request> oauth2_access_token_request_;
base::string16 profile_full_name_;
Expand Down
47 changes: 21 additions & 26 deletions chrome/browser/profiles/profile_downloader_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,31 @@

#include "chrome/browser/profiles/profile_downloader.h"

#include "base/json/json_reader.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

std::string GetJSonData(const std::string& full_name,
const std::string& given_name,
const std::string& url,
const std::string& locale) {
std::stringstream stream;
bool started = false;

stream << "{ ";
if (!full_name.empty()) {
stream << "\"name\": \"" << full_name << "\"";
started = true;
}
if (!given_name.empty()) {
stream << (started ? ", " : "") << "\"given_name\": \"" << given_name
<< "\"";
started = true;
}
if (!url.empty()) {
stream << (started ? ", " : "") << "\"picture\": \"" << url << "\"";
started = true;
}
void GetJSonData(const std::string& full_name,
const std::string& given_name,
const std::string& url,
const std::string& locale,
base::DictionaryValue* dict) {
if (!full_name.empty())
dict->SetString("displayName", full_name);

if (!locale.empty())
stream << (started ? ", " : "") << "\"locale\": \"" << locale << "\"";
if (!given_name.empty())
dict->SetString("name.givenName", given_name);

if (!url.empty())
dict->SetString("image.url", url);

stream << " }";
return stream.str();
if (!locale.empty())
dict->SetString("language", locale);
}

} // namespace
Expand All @@ -58,8 +51,10 @@ class ProfileDownloaderTest : public testing::Test {
base::string16 parsed_given_name;
std::string parsed_url;
std::string parsed_locale;
scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
GetJSonData(full_name, given_name, url, locale, dict.get());
bool result = ProfileDownloader::ParseProfileJSON(
GetJSonData(full_name, given_name, url, locale),
dict.get(),
&parsed_full_name,
&parsed_given_name,
&parsed_url,
Expand Down
Loading

0 comments on commit 431a075

Please sign in to comment.