Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Unified Diff: chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc

Issue 17109006: Device robot refresh token integrity validation. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Extend device_oauth2_token_service_unittest.cc to cover refresh token validation cases. Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
index 6424ccbb6b88a905e52a55eeed9c7b3bdbcfc432..b066cebd20cf5c118329fcb30762d8b2075d4aad 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
@@ -6,11 +6,16 @@
#include "base/message_loop.h"
#include "base/prefs/testing_pref_service.h"
+#include "chrome/browser/signin/oauth2_token_service_test_util.h"
+#include "chrome/common/pref_names.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/cryptohome/mock_cryptohome_library.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -23,25 +28,156 @@ using ::testing::StrictMock;
namespace chromeos {
+class TestDeviceOAuth2TokenService : public DeviceOAuth2TokenService {
+ public:
+ explicit TestDeviceOAuth2TokenService(net::URLRequestContextGetter* getter,
+ PrefService* local_state)
+ : DeviceOAuth2TokenService(getter, local_state) {
+ }
+ void SetRobotAccountIdPolicyValue(const std::string& id) {
+ robot_account_id_ = id;
+ }
Mattias Nissler (ping if slow) 2013/06/19 17:53:17 nit: newline
David Roche 2013/06/20 17:49:29 Done.
+ protected:
+ // Skip calling into the policy subsystem and return our test value.
+ virtual std::string GetRobotAccountId() OVERRIDE {
+ return robot_account_id_;
+ }
Mattias Nissler (ping if slow) 2013/06/19 17:53:17 nit: newline
David Roche 2013/06/20 17:49:29 Done.
+ private:
+ std::string robot_account_id_;
+ DISALLOW_COPY_AND_ASSIGN(TestDeviceOAuth2TokenService);
+};
+
class DeviceOAuth2TokenServiceTest : public testing::Test {
public:
DeviceOAuth2TokenServiceTest()
: ui_thread_(content::BrowserThread::UI, &message_loop_),
- scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()) {}
+ scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
+ request_context_getter_(new net::TestURLRequestContextGetter(
+ message_loop_.message_loop_proxy())),
+ cryptohome_library_(chromeos::CryptohomeLibrary::GetTestImpl()) {}
virtual ~DeviceOAuth2TokenServiceTest() {}
virtual void SetUp() OVERRIDE {
+ oauth2_service_ = new TestDeviceOAuth2TokenService(
+ request_context_getter_,
+ scoped_testing_local_state_.Get());
+ oauth2_service_->max_refresh_token_validation_retries_ = 0;
+ oauth2_service_->set_max_authorization_token_fetch_retries_for_testing(0);
}
virtual void TearDown() OVERRIDE {
+ delete oauth2_service_;
Mattias Nissler (ping if slow) 2013/06/19 17:53:17 wrap in a scoped_ptr_, or make a plain class membe
David Roche 2013/06/20 17:49:29 Can't use scoped_ptr, since the constructor/destru
+ CryptohomeLibrary::SetForTest(NULL);
}
+ // Utility method to set a value in Local State for the device refresh token
+ // (it must have a non-empty value or it won't be used).
+ void SetDeviceRefreshTokenInLocalState(const std::string& refresh_token) {
+ chromeos::CryptohomeLibrary::SetForTest(cryptohome_library_.get());
+ scoped_testing_local_state_.Get()->SetManagedPref(
+ prefs::kDeviceRobotAnyApiRefreshToken,
+ Value::CreateStringValue(refresh_token));
+ }
+
+ std::string GetValidTokenInfoResponse(const std::string issued_to) {
+ return "{ \"issued_to\": \"" + issued_to + "\","
+ " \"user_id\": \"1234567890\" }";
+ }
+
+ // See implementation below for comments.
+ void AssertRefreshTokenValidation(
+ net::HttpStatusCode tokeninfo_access_token_fetch_response_code,
+ const std::string& tokeninfo_access_token_fetch_response_string,
+ int tokeninfo_access_token_fetch_successful_tokens,
+ int tokeninfo_access_token_fetch_number_of_errors,
+ net::HttpStatusCode tokeninfo_api_call_response_code,
+ const std::string& tokeninfo_api_call_response_string,
+ int tokeninfo_api_call_successful_tokens,
+ int tokeninfo_api_call_number_of_errors,
+ net::HttpStatusCode cloudprint_access_token_fetch_response_code,
+ const std::string& cloudprint_access_token_fetch_response_string,
+ int cloudprint_access_token_fetch_successful_tokens,
+ int cloudprint_access_token_fetch_number_of_errors);
+
protected:
base::MessageLoop message_loop_;
content::TestBrowserThread ui_thread_;
ScopedTestingLocalState scoped_testing_local_state_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+ net::TestURLFetcherFactory factory_;
+ TestDeviceOAuth2TokenService* oauth2_service_;
+ TestingOAuth2TokenServiceConsumer consumer_;
+ scoped_ptr<chromeos::CryptohomeLibrary> cryptohome_library_;
+
};
+void DeviceOAuth2TokenServiceTest::AssertRefreshTokenValidation(
+ // Step 1: fetch the access token for the tokeninfo API.
+ net::HttpStatusCode tokeninfo_access_token_fetch_response_code,
+ const std::string& tokeninfo_access_token_fetch_response_string,
+ int tokeninfo_access_token_fetch_successful_tokens,
+ int tokeninfo_access_token_fetch_number_of_errors,
+
+ // Step 2: call the tokeninfo API.
+ net::HttpStatusCode tokeninfo_api_call_response_code,
+ const std::string& tokeninfo_api_call_response_string,
+ int tokeninfo_api_call_successful_tokens,
+ int tokeninfo_api_call_number_of_errors,
+
+ // Step 3: if steps 1 and 2 pass, fetch the access token for the requested
+ // scope (in this case, cloudprint).
+ net::HttpStatusCode cloudprint_access_token_fetch_response_code,
+ const std::string& cloudprint_access_token_fetch_response_string,
+ int cloudprint_access_token_fetch_successful_tokens,
+ int cloudprint_access_token_fetch_number_of_errors) {
+
+ SetDeviceRefreshTokenInLocalState("device_refresh_token_4_test");
+
+ scoped_ptr<OAuth2TokenService::Request> request(
+ oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+ message_loop_.RunUntilIdle();
+
+ EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(0, consumer_.number_of_errors_);
+
+ // First the access token with tokeninfo scope is fetched.
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(tokeninfo_access_token_fetch_response_code);
+ fetcher->SetResponseString(tokeninfo_access_token_fetch_response_string);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(tokeninfo_access_token_fetch_successful_tokens,
+ consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(tokeninfo_access_token_fetch_number_of_errors,
+ consumer_.number_of_errors_);
+
+ // Then the actual tokeninfo call is made.
+ if (consumer_.number_of_successful_tokens_ == 0 &&
+ consumer_.number_of_errors_ == 0) {
+ fetcher = factory_.GetFetcherByID(0);
+ fetcher->set_response_code(tokeninfo_api_call_response_code);
+ fetcher->SetResponseString(tokeninfo_api_call_response_string);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(tokeninfo_api_call_successful_tokens,
+ consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(tokeninfo_api_call_number_of_errors,
+ consumer_.number_of_errors_);
+ }
+
+ // Finally, the token for the requested scope is requested.
+ if (consumer_.number_of_successful_tokens_ == 0 &&
+ consumer_.number_of_errors_ == 0) {
+ fetcher = factory_.GetFetcherByID(0);
+ fetcher->set_response_code(cloudprint_access_token_fetch_response_code);
+ fetcher->SetResponseString(cloudprint_access_token_fetch_response_string);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ EXPECT_EQ(cloudprint_access_token_fetch_successful_tokens,
+ consumer_.number_of_successful_tokens_);
+ EXPECT_EQ(cloudprint_access_token_fetch_number_of_errors,
+ consumer_.number_of_errors_);
+ }
+}
+
TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedToken) {
StrictMock<MockCryptohomeLibrary> mock_cryptohome_library;
CryptohomeLibrary::SetForTest(&mock_cryptohome_library);
@@ -58,16 +194,192 @@ TEST_F(DeviceOAuth2TokenServiceTest, SaveEncryptedToken) {
.Times(1)
.WillOnce(Return("test-token"));
- DeviceOAuth2TokenService oauth2_service(
- new net::TestURLRequestContextGetter(message_loop_.message_loop_proxy()),
- scoped_testing_local_state_.Get());
-
- ASSERT_EQ("", oauth2_service.GetRefreshToken());
- oauth2_service.SetAndSaveRefreshToken("test-token");
- ASSERT_EQ("test-token", oauth2_service.GetRefreshToken());
+ ASSERT_EQ("", oauth2_service_->GetRefreshToken());
+ oauth2_service_->SetAndSaveRefreshToken("test-token");
+ ASSERT_EQ("test-token", oauth2_service_->GetRefreshToken());
// This call won't invoke decrypt again, since the value is cached.
- ASSERT_EQ("test-token", oauth2_service.GetRefreshToken());
+ ASSERT_EQ("test-token", oauth2_service_->GetRefreshToken());
+}
+
+TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Success) {
+ oauth2_service_->SetRobotAccountIdPolicyValue("service_acct@g.com");
+
+ AssertRefreshTokenValidation(
+
+ // TokenInfo-scoped access token fetch.
+ net::HTTP_OK,
+ GetValidTokenResponse("tokeninfo_access_token", 3600),
+ 0, // tokens
+ 0, // errors
+
+ // TokenInfo API call.
+ net::HTTP_OK,
+ GetValidTokenInfoResponse("service_acct@g.com"),
+ 0, // tokens
+ 0, // errors
+
+ // CloudPrint access token fetch.
+ net::HTTP_OK,
+ GetValidTokenResponse("scoped_access_token", 3600),
+ 1, // tokens
+ 0); // errors
+
+ EXPECT_EQ("scoped_access_token", consumer_.last_token_);
+}
+
+TEST_F(DeviceOAuth2TokenServiceTest,
+ RefreshTokenValidation_Failure_TokenInfoAccessTokenHttpError) {
+ oauth2_service_->SetRobotAccountIdPolicyValue("service_acct@g.com");
+
+ AssertRefreshTokenValidation(
+
+ // TokenInfo-scoped access token fetch.
+ net::HTTP_UNAUTHORIZED,
+ "",
+ 0, // tokens
+ 1, // errors
+
+ // TokenInfo API call skipped (error returned in previous step).
+ net::HTTP_OK, "", 0, 0,
+
+ // CloudPrint access token fetch skipped (error returned in previous step).
+ net::HTTP_OK, "", 0, 0);
+}
+
+TEST_F(DeviceOAuth2TokenServiceTest,
+ RefreshTokenValidation_Failure_TokenInfoAccessTokenInvalidResponse) {
+ oauth2_service_->SetRobotAccountIdPolicyValue("service_acct@g.com");
+
+ AssertRefreshTokenValidation(
+
+ // TokenInfo-scoped access token fetch.
+ net::HTTP_OK,
+ "invalid response",
+ 0, // tokens
+ 1, // errors
+
+ // TokenInfo API call skipped (error returned in previous step).
+ net::HTTP_OK, "", 0, 0,
+
+ // CloudPrint access token fetch skipped (error returned in previous step).
+ net::HTTP_OK, "", 0, 0);
+}
+
+TEST_F(DeviceOAuth2TokenServiceTest,
+ RefreshTokenValidation_Failure_TokenInfoApiCallHttpError) {
+ oauth2_service_->SetRobotAccountIdPolicyValue("service_acct@g.com");
+
+ AssertRefreshTokenValidation(
+
+ // TokenInfo-scoped access token fetch.
+ net::HTTP_OK,
+ GetValidTokenResponse("tokeninfo_access_token", 3600),
+ 0, // tokens
+ 0, // errors
+
+ // TokenInfo API call.
+ net::HTTP_INTERNAL_SERVER_ERROR,
+ "",
+ 0, // tokens
+ 1, // errors
+
+ // CloudPrint access token fetch skipped (error returned in previous step).
+ net::HTTP_OK, "", 0, 0);
+}
+
+TEST_F(DeviceOAuth2TokenServiceTest,
+ RefreshTokenValidation_Failure_TokenInfoApiCallInvalidResponse) {
+ oauth2_service_->SetRobotAccountIdPolicyValue("service_acct@g.com");
+
+ AssertRefreshTokenValidation(
+
+ // TokenInfo-scoped access token fetch.
+ net::HTTP_OK,
+ GetValidTokenResponse("tokeninfo_access_token", 3600),
+ 0, // tokens
+ 0, // errors
+
+ // TokenInfo API call.
+ net::HTTP_OK,
+ "invalid response",
+ 0, // tokens
+ 1, // errors
+
+ // CloudPrint access token fetch skipped (error returned in previous step).
+ net::HTTP_OK, "", 0, 0);
+}
+
+TEST_F(DeviceOAuth2TokenServiceTest,
+ RefreshTokenValidation_Failure_CloudPrintAccessTokenHttpError) {
+ oauth2_service_->SetRobotAccountIdPolicyValue("service_acct@g.com");
+
+ AssertRefreshTokenValidation(
+
+ // TokenInfo-scoped access token fetch.
+ net::HTTP_OK,
+ GetValidTokenResponse("tokeninfo_access_token", 3600),
+ 0, // tokens
+ 0, // errors
+
+ // TokenInfo API call.
+ net::HTTP_OK,
+ GetValidTokenInfoResponse("service_acct@g.com"),
+ 0, // tokens
+ 0, // errors
+
+ // CloudPrint access token fetch.
+ net::HTTP_BAD_REQUEST,
+ "",
+ 0, // tokens
+ 1); // errors
+}
+
+TEST_F(DeviceOAuth2TokenServiceTest,
+ RefreshTokenValidation_Failure_CloudPrintAccessTokenInvalidResponse) {
+ oauth2_service_->SetRobotAccountIdPolicyValue("service_acct@g.com");
+
+ AssertRefreshTokenValidation(
+
+ // TokenInfo-scoped access token fetch.
+ net::HTTP_OK,
+ GetValidTokenResponse("tokeninfo_access_token", 3600),
+ 0, // tokens
+ 0, // errors
+
+ // TokenInfo API call.
+ net::HTTP_OK,
+ GetValidTokenInfoResponse("service_acct@g.com"),
+ 0, // tokens
+ 0, // errors
+
+ // CloudPrint access token fetch.
+ net::HTTP_OK,
+ "invalid request",
+ 0, // tokens
+ 1); // errors
+}
+
+TEST_F(DeviceOAuth2TokenServiceTest, RefreshTokenValidation_Failure_BadOwner) {
+ oauth2_service_->SetRobotAccountIdPolicyValue("WRONG_service_acct@g.com");
+
+ AssertRefreshTokenValidation(
+
+ // TokenInfo-scoped access token fetch.
+ net::HTTP_OK,
+ GetValidTokenResponse("tokeninfo_access_token", 3600),
+ 0, // tokens
+ 0, // errors
+
+ // TokenInfo API call.
+ // The token owner doesn't match the policy value; an error is returned.
+ net::HTTP_OK,
+ GetValidTokenInfoResponse("service_acct@g.com"),
+ 0, // tokens
+ 1, // errors
+
+ // CloudPrint access token fetch is skipped.
+ net::HTTP_OK, "", 0, 0);
}
} // namespace chromeos

Powered by Google App Engine
This is Rietveld 408576698