[2/4] Account Capabilities Fetcher
This CL adds a method for fetching account capabilities to GaiaOAuthClient. Callers to this method will be added in follow-up CLs. To call account capabilities API, a caller must provide an access token with the https://www.googleapis.com/auth/account.capabilities scope as well as a list of capabilities to fetch. GaiaOAuthClient doesn't completely parse the response. This is done one layer up, where AccountCapabilities definition is available. Bug: 1213071 Change-Id: Ifa5513515a4faf5fe9f016a4a89c71412ad61008 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2940533 Reviewed-by: Ramin Halavati <rhalavati@chromium.org> Reviewed-by: Mihai Sardarescu <msarda@chromium.org> Commit-Queue: Alex Ilin <alexilin@chromium.org> Cr-Commit-Position: refs/heads/master@{#890721}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
a9b5392e1a
commit
81ce2b504c
google_apis/gaia
tools/traffic_annotation/summary
@ -8,10 +8,12 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/values.h"
|
||||
#include "google_apis/gaia/gaia_auth_util.h"
|
||||
@ -78,6 +80,11 @@ class GaiaOAuthClient::Core
|
||||
void GetUserInfo(const std::string& oauth_access_token,
|
||||
int max_retries,
|
||||
Delegate* delegate);
|
||||
void GetAccountCapabilities(
|
||||
const std::string& oauth_access_token,
|
||||
const std::vector<std::string>& capabilities_names,
|
||||
int max_retries,
|
||||
Delegate* delegate);
|
||||
void GetTokenInfo(const std::string& qualifier,
|
||||
const std::string& query,
|
||||
int max_retries,
|
||||
@ -97,6 +104,7 @@ class GaiaOAuthClient::Core
|
||||
USER_EMAIL,
|
||||
USER_ID,
|
||||
USER_INFO,
|
||||
ACCOUNT_CAPABILITIES,
|
||||
};
|
||||
|
||||
~Core() {}
|
||||
@ -349,6 +357,59 @@ void GaiaOAuthClient::Core::GetTokenInfo(const std::string& qualifier,
|
||||
traffic_annotation);
|
||||
}
|
||||
|
||||
void GaiaOAuthClient::Core::GetAccountCapabilities(
|
||||
const std::string& oauth_access_token,
|
||||
const std::vector<std::string>& capabilities_names,
|
||||
int max_retries,
|
||||
Delegate* delegate) {
|
||||
DCHECK(!capabilities_names.empty());
|
||||
|
||||
std::string post_body = base::StrCat(
|
||||
{"names=", net::EscapeUrlEncodedData(*capabilities_names.begin(), true)});
|
||||
for (auto it = capabilities_names.begin() + 1; it != capabilities_names.end();
|
||||
++it) {
|
||||
base::StrAppend(&post_body,
|
||||
{"&names=", net::EscapeUrlEncodedData(*it, true)});
|
||||
}
|
||||
|
||||
std::string auth = base::StrCat({"Bearer ", oauth_access_token});
|
||||
|
||||
net::MutableNetworkTrafficAnnotationTag traffic_annotation(
|
||||
net::DefineNetworkTrafficAnnotation(
|
||||
"gaia_oauth_client_get_account_capabilities",
|
||||
R"(
|
||||
semantics {
|
||||
sender: "OAuth 2.0 calls"
|
||||
description:
|
||||
"This request is used to fetch account capabilities. Capabilities "
|
||||
"provide information about state and features of Gaia accounts."
|
||||
trigger:
|
||||
"AccountTrackerService fetches account capabilities soon after the "
|
||||
"user signs in. Afterwards, AccountTrackerService periodically "
|
||||
"triggers this request to keep account capabilities up to date for "
|
||||
"existing accounts."
|
||||
data:
|
||||
"The OAuth 2.0 access token of the account and a predefined list "
|
||||
"of capabilities to fetch."
|
||||
destination: GOOGLE_OWNED_SERVICE
|
||||
}
|
||||
policy {
|
||||
cookies_allowed: NO
|
||||
setting:
|
||||
"This feature cannot be disabled in settings, but if the user "
|
||||
"signs out of Chrome, this request would not be made."
|
||||
chrome_policy {
|
||||
SigninAllowed {
|
||||
SigninAllowed: false
|
||||
}
|
||||
}
|
||||
})"));
|
||||
|
||||
MakeRequest(ACCOUNT_CAPABILITIES,
|
||||
GURL(GaiaUrls::GetInstance()->account_capabilities_url()),
|
||||
post_body, auth, max_retries, delegate, traffic_annotation);
|
||||
}
|
||||
|
||||
void GaiaOAuthClient::Core::MakeRequest(
|
||||
RequestType type,
|
||||
const GURL& url,
|
||||
@ -524,7 +585,12 @@ void GaiaOAuthClient::Core::HandleResponse(std::unique_ptr<std::string> body,
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
case ACCOUNT_CAPABILITIES: {
|
||||
delegate_->OnGetAccountCapabilitiesResponse(std::move(response_dict));
|
||||
break;
|
||||
}
|
||||
|
||||
case NO_PENDING_REQUEST:
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
@ -593,4 +659,13 @@ void GaiaOAuthClient::GetTokenHandleInfo(const std::string& token_handle,
|
||||
delegate);
|
||||
}
|
||||
|
||||
void GaiaOAuthClient::GetAccountCapabilities(
|
||||
const std::string& oauth_access_token,
|
||||
const std::vector<std::string>& capabilities_names,
|
||||
int max_retries,
|
||||
Delegate* delegate) {
|
||||
return core_->GetAccountCapabilities(oauth_access_token, capabilities_names,
|
||||
max_retries, delegate);
|
||||
}
|
||||
|
||||
} // namespace gaia
|
||||
|
@ -51,6 +51,8 @@ class GaiaOAuthClient {
|
||||
// Invoked on a successful response to the GetTokenInfo request.
|
||||
virtual void OnGetTokenInfoResponse(
|
||||
std::unique_ptr<base::DictionaryValue> token_info) {}
|
||||
virtual void OnGetAccountCapabilitiesResponse(
|
||||
std::unique_ptr<base::Value> account_capabilities) {}
|
||||
// Invoked when there is an OAuth error with one of the requests.
|
||||
virtual void OnOAuthError() = 0;
|
||||
// Invoked when there is a network error or upon receiving an invalid
|
||||
@ -135,6 +137,17 @@ class GaiaOAuthClient {
|
||||
int max_retries,
|
||||
Delegate* delegate);
|
||||
|
||||
// Call the account capabilities API, returning a dictionary of response
|
||||
// values. Only fetches values for capabilities listed in
|
||||
// |capabilities_names|. The provided access token must have
|
||||
// https://www.googleapis.com/auth/account.capabilities in its scopes. See
|
||||
// |max_retries| docs above.
|
||||
void GetAccountCapabilities(
|
||||
const std::string& oauth_access_token,
|
||||
const std::vector<std::string>& capabilities_names,
|
||||
int max_retries,
|
||||
Delegate* delegate);
|
||||
|
||||
private:
|
||||
// The guts of the implementation live in this class.
|
||||
class Core;
|
||||
|
@ -162,10 +162,42 @@ const std::string kDummyTokenHandleInfoResult =
|
||||
"{\"audience\": \"1234567890.apps.googleusercontent.com\","
|
||||
"\"expires_in\":" + base::NumberToString(kTestExpiresIn) + "}";
|
||||
|
||||
const std::string kDummyAccountCapabilitiesResult =
|
||||
"{\"accountCapabilities\": ["
|
||||
"{\"name\": \"accountcapabilities/111\", \"booleanValue\": false},"
|
||||
"{\"name\": \"accountcapabilities/222\", \"booleanValue\": true}"
|
||||
"]}";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace gaia {
|
||||
|
||||
class MockGaiaOAuthClientDelegate : public gaia::GaiaOAuthClient::Delegate {
|
||||
public:
|
||||
MockGaiaOAuthClientDelegate() = default;
|
||||
|
||||
MockGaiaOAuthClientDelegate(const MockGaiaOAuthClientDelegate&) = delete;
|
||||
MockGaiaOAuthClientDelegate& operator=(const MockGaiaOAuthClientDelegate&) =
|
||||
delete;
|
||||
|
||||
MOCK_METHOD3(OnGetTokensResponse,
|
||||
void(const std::string& refresh_token,
|
||||
const std::string& access_token,
|
||||
int expires_in_seconds));
|
||||
MOCK_METHOD2(OnRefreshTokenResponse,
|
||||
void(const std::string& access_token, int expires_in_seconds));
|
||||
MOCK_METHOD1(OnGetUserEmailResponse, void(const std::string& user_email));
|
||||
MOCK_METHOD1(OnGetUserIdResponse, void(const std::string& user_id));
|
||||
MOCK_METHOD1(OnGetUserInfoResponse,
|
||||
void(std::unique_ptr<base::DictionaryValue> user_info));
|
||||
MOCK_METHOD1(OnGetTokenInfoResponse,
|
||||
void(std::unique_ptr<base::DictionaryValue> token_info));
|
||||
MOCK_METHOD1(OnGetAccountCapabilitiesResponse,
|
||||
void(std::unique_ptr<base::Value> account_capabilities));
|
||||
MOCK_METHOD0(OnOAuthError, void());
|
||||
MOCK_METHOD1(OnNetworkError, void(int response_code));
|
||||
};
|
||||
|
||||
class GaiaOAuthClientTest : public testing::Test {
|
||||
protected:
|
||||
GaiaOAuthClientTest()
|
||||
@ -191,52 +223,25 @@ class GaiaOAuthClientTest : public testing::Test {
|
||||
}
|
||||
|
||||
protected:
|
||||
void TestAccountCapabilitiesUploadData(
|
||||
const std::vector<std::string>& capabilities_names,
|
||||
const std::string& expected_body) {
|
||||
ResponseInjector injector(&url_loader_factory_);
|
||||
injector.set_complete_immediately(false);
|
||||
|
||||
MockGaiaOAuthClientDelegate delegate;
|
||||
GaiaOAuthClient auth(GetSharedURLLoaderFactory());
|
||||
auth.GetAccountCapabilities("some_token", capabilities_names, 1, &delegate);
|
||||
|
||||
EXPECT_EQ(injector.GetUploadData(), expected_body);
|
||||
}
|
||||
|
||||
base::test::TaskEnvironment task_environment_;
|
||||
network::TestURLLoaderFactory url_loader_factory_;
|
||||
|
||||
OAuthClientInfo client_info_;
|
||||
};
|
||||
|
||||
class MockGaiaOAuthClientDelegate : public gaia::GaiaOAuthClient::Delegate {
|
||||
public:
|
||||
MockGaiaOAuthClientDelegate() {}
|
||||
~MockGaiaOAuthClientDelegate() override {}
|
||||
|
||||
MOCK_METHOD3(OnGetTokensResponse, void(const std::string& refresh_token,
|
||||
const std::string& access_token,
|
||||
int expires_in_seconds));
|
||||
MOCK_METHOD2(OnRefreshTokenResponse, void(const std::string& access_token,
|
||||
int expires_in_seconds));
|
||||
MOCK_METHOD1(OnGetUserEmailResponse, void(const std::string& user_email));
|
||||
MOCK_METHOD1(OnGetUserIdResponse, void(const std::string& user_id));
|
||||
MOCK_METHOD0(OnOAuthError, void());
|
||||
MOCK_METHOD1(OnNetworkError, void(int response_code));
|
||||
|
||||
// gMock doesn't like methods that take or return scoped_ptr. A
|
||||
// work-around is to create a mock method that takes a raw ptr, and
|
||||
// override the problematic method to call through to it.
|
||||
// https://groups.google.com/a/chromium.org/d/msg/chromium-dev/01sDxsJ1OYw/I_S0xCBRF2oJ
|
||||
MOCK_METHOD1(OnGetUserInfoResponsePtr,
|
||||
void(const base::DictionaryValue* user_info));
|
||||
void OnGetUserInfoResponse(
|
||||
std::unique_ptr<base::DictionaryValue> user_info) override {
|
||||
user_info_ = std::move(user_info);
|
||||
OnGetUserInfoResponsePtr(user_info_.get());
|
||||
}
|
||||
MOCK_METHOD1(OnGetTokenInfoResponsePtr,
|
||||
void(const base::DictionaryValue* token_info));
|
||||
void OnGetTokenInfoResponse(
|
||||
std::unique_ptr<base::DictionaryValue> token_info) override {
|
||||
token_info_ = std::move(token_info);
|
||||
OnGetTokenInfoResponsePtr(token_info_.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<base::DictionaryValue> user_info_;
|
||||
std::unique_ptr<base::DictionaryValue> token_info_;
|
||||
DISALLOW_COPY_AND_ASSIGN(MockGaiaOAuthClientDelegate);
|
||||
};
|
||||
|
||||
TEST_F(GaiaOAuthClientTest, NetworkFailure) {
|
||||
int response_code = net::HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
@ -413,11 +418,13 @@ TEST_F(GaiaOAuthClientTest, GetUserId) {
|
||||
}
|
||||
|
||||
TEST_F(GaiaOAuthClientTest, GetUserInfo) {
|
||||
const base::DictionaryValue* captured_result;
|
||||
std::unique_ptr<base::DictionaryValue> captured_result;
|
||||
|
||||
MockGaiaOAuthClientDelegate delegate;
|
||||
EXPECT_CALL(delegate, OnGetUserInfoResponsePtr(_))
|
||||
.WillOnce(SaveArg<0>(&captured_result));
|
||||
EXPECT_CALL(delegate, OnGetUserInfoResponse(_))
|
||||
.WillOnce([&](std::unique_ptr<base::DictionaryValue> result) {
|
||||
captured_result = std::move(result);
|
||||
});
|
||||
|
||||
ResponseInjector injector(&url_loader_factory_);
|
||||
injector.set_results(kDummyFullUserInfoResult);
|
||||
@ -433,15 +440,17 @@ TEST_F(GaiaOAuthClientTest, GetUserInfo) {
|
||||
base::DictionaryValue* expected_result;
|
||||
value->GetAsDictionary(&expected_result);
|
||||
|
||||
ASSERT_TRUE(expected_result->Equals(captured_result));
|
||||
ASSERT_TRUE(expected_result->Equals(captured_result.get()));
|
||||
}
|
||||
|
||||
TEST_F(GaiaOAuthClientTest, GetTokenInfo) {
|
||||
const base::DictionaryValue* captured_result;
|
||||
std::unique_ptr<base::DictionaryValue> captured_result;
|
||||
|
||||
MockGaiaOAuthClientDelegate delegate;
|
||||
EXPECT_CALL(delegate, OnGetTokenInfoResponsePtr(_))
|
||||
.WillOnce(SaveArg<0>(&captured_result));
|
||||
EXPECT_CALL(delegate, OnGetTokenInfoResponse(_))
|
||||
.WillOnce([&](std::unique_ptr<base::DictionaryValue> result) {
|
||||
captured_result = std::move(result);
|
||||
});
|
||||
|
||||
ResponseInjector injector(&url_loader_factory_);
|
||||
injector.set_results(kDummyTokenInfoResult);
|
||||
@ -456,11 +465,13 @@ TEST_F(GaiaOAuthClientTest, GetTokenInfo) {
|
||||
}
|
||||
|
||||
TEST_F(GaiaOAuthClientTest, GetTokenHandleInfo) {
|
||||
const base::DictionaryValue* captured_result;
|
||||
std::unique_ptr<base::DictionaryValue> captured_result;
|
||||
|
||||
MockGaiaOAuthClientDelegate delegate;
|
||||
EXPECT_CALL(delegate, OnGetTokenInfoResponsePtr(_))
|
||||
.WillOnce(SaveArg<0>(&captured_result));
|
||||
EXPECT_CALL(delegate, OnGetTokenInfoResponse(_))
|
||||
.WillOnce([&](std::unique_ptr<base::DictionaryValue> result) {
|
||||
captured_result = std::move(result);
|
||||
});
|
||||
|
||||
ResponseInjector injector(&url_loader_factory_);
|
||||
injector.set_results(kDummyTokenHandleInfoResult);
|
||||
@ -474,4 +485,50 @@ TEST_F(GaiaOAuthClientTest, GetTokenHandleInfo) {
|
||||
ASSERT_EQ("1234567890.apps.googleusercontent.com", audience);
|
||||
}
|
||||
|
||||
TEST_F(GaiaOAuthClientTest, GetAccountCapabilities) {
|
||||
std::unique_ptr<base::Value> captured_result;
|
||||
|
||||
MockGaiaOAuthClientDelegate delegate;
|
||||
EXPECT_CALL(delegate, OnGetAccountCapabilitiesResponse(_))
|
||||
.WillOnce([&](std::unique_ptr<base::Value> result) {
|
||||
captured_result = std::move(result);
|
||||
});
|
||||
|
||||
ResponseInjector injector(&url_loader_factory_);
|
||||
injector.set_results(kDummyAccountCapabilitiesResult);
|
||||
injector.set_complete_immediately(false);
|
||||
|
||||
GaiaOAuthClient auth(GetSharedURLLoaderFactory());
|
||||
auth.GetAccountCapabilities("some_token",
|
||||
{"capability1", "capability2", "capability3"}, 1,
|
||||
&delegate);
|
||||
|
||||
EXPECT_EQ(injector.GetUploadData(),
|
||||
"names=capability1&names=capability2&names=capability3");
|
||||
injector.Finish();
|
||||
FlushNetwork();
|
||||
|
||||
auto capabilities =
|
||||
captured_result->FindListKey("accountCapabilities")->GetList();
|
||||
ASSERT_EQ(capabilities.size(), 2U);
|
||||
EXPECT_EQ(*capabilities[0].FindStringKey("name"), "accountcapabilities/111");
|
||||
EXPECT_FALSE(*capabilities[0].FindBoolKey("booleanValue"));
|
||||
EXPECT_EQ(*capabilities[1].FindStringKey("name"), "accountcapabilities/222");
|
||||
EXPECT_TRUE(*capabilities[1].FindBoolKey("booleanValue"));
|
||||
}
|
||||
|
||||
TEST_F(GaiaOAuthClientTest,
|
||||
GetAccountCapabilities_UploadData_OneCapabilityName) {
|
||||
TestAccountCapabilitiesUploadData({"capability"},
|
||||
/*expected_body=*/"names=capability");
|
||||
}
|
||||
|
||||
TEST_F(GaiaOAuthClientTest,
|
||||
GetAccountCapabilities_UploadData_MultipleCapabilityNames) {
|
||||
TestAccountCapabilitiesUploadData(
|
||||
{"capability1", "capability2", "capability3"},
|
||||
/*expected_body=*/
|
||||
"names=capability1&names=capability2&names=capability3");
|
||||
}
|
||||
|
||||
} // namespace gaia
|
||||
|
@ -141,6 +141,7 @@ Refer to README.md for content description and update process.
|
||||
<item id="gaia_auth_revoke_token" added_in_milestone="62" hash_code="133982351" type="0" content_hash_code="96665330" os_list="linux,windows" file_path="google_apis/gaia/gaia_auth_fetcher.cc"/>
|
||||
<item id="gaia_cookie_manager_external_cc_result" added_in_milestone="62" hash_code="4300475" type="0" content_hash_code="31188375" os_list="linux,windows" file_path="components/signin/internal/identity_manager/gaia_cookie_manager_service.cc"/>
|
||||
<item id="gaia_create_reauth_proof_token_for_parent" added_in_milestone="80" hash_code="67750043" type="0" content_hash_code="103500636" os_list="linux,windows" file_path="google_apis/gaia/gaia_auth_fetcher.cc"/>
|
||||
<item id="gaia_oauth_client_get_account_capabilities" added_in_milestone="93" hash_code="87437888" type="0" content_hash_code="51145869" os_list="linux,windows" file_path="google_apis/gaia/gaia_oauth_client.cc"/>
|
||||
<item id="gaia_oauth_client_get_token_info" added_in_milestone="62" hash_code="32585152" type="0" content_hash_code="128143346" os_list="linux,windows" file_path="google_apis/gaia/gaia_oauth_client.cc"/>
|
||||
<item id="gaia_oauth_client_get_tokens" added_in_milestone="62" hash_code="5637379" type="0" content_hash_code="12099176" os_list="linux,windows" file_path="google_apis/gaia/gaia_oauth_client.cc"/>
|
||||
<item id="gaia_oauth_client_get_user_info" added_in_milestone="62" hash_code="83476155" type="0" content_hash_code="35159007" os_list="linux,windows" file_path="google_apis/gaia/gaia_oauth_client.cc"/>
|
||||
|
@ -250,6 +250,7 @@ hidden="true" so that these annotations don't show up in the document.
|
||||
<traffic_annotation unique_id="gaia_auth_multilogin"/>
|
||||
<traffic_annotation unique_id="gaia_cookie_manager_external_cc_result"/>
|
||||
<traffic_annotation unique_id="gaia_oauth_client_get_token_info"/>
|
||||
<traffic_annotation unique_id="gaia_oauth_client_get_account_capabilities"/>
|
||||
<traffic_annotation unique_id="gaia_oauth_client_get_tokens"/>
|
||||
<traffic_annotation unique_id="gaia_oauth_client_get_user_info"/>
|
||||
<traffic_annotation unique_id="gaia_oauth_client_refresh_token"/>
|
||||
|
Reference in New Issue
Block a user