diff options
author | dzhioev <dzhioev@chromium.org> | 2015-05-28 11:43:40 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-28 18:44:00 +0000 |
commit | 1e76fed394f8a90b049ce2c06ca06f677c899024 (patch) | |
tree | e143c7c3245af22e9dab44d8f88ad5e9a84b2911 | |
parent | d2adf1e487ba54a15d92947ce29fbe18b8edd686 (diff) | |
download | chromium_src-1e76fed394f8a90b049ce2c06ca06f677c899024.zip chromium_src-1e76fed394f8a90b049ce2c06ca06f677c899024.tar.gz chromium_src-1e76fed394f8a90b049ce2c06ca06f677c899024.tar.bz2 |
Browser tests for the new way of handling device ID in Chrome OS.
BUG=486044,486136
TEST=browser_tests --gtest_filter=DeviceIDTest.*
Review URL: https://codereview.chromium.org/1141163002
Cr-Commit-Position: refs/heads/master@{#331828}
-rw-r--r-- | chrome/browser/chromeos/login/signin/device_id_browsertest.cc | 279 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | google_apis/gaia/fake_gaia.cc | 55 | ||||
-rw-r--r-- | google_apis/gaia/fake_gaia.h | 21 |
4 files changed, 354 insertions, 2 deletions
diff --git a/chrome/browser/chromeos/login/signin/device_id_browsertest.cc b/chrome/browser/chromeos/login/signin/device_id_browsertest.cc new file mode 100644 index 0000000..b3ea8d0 --- /dev/null +++ b/chrome/browser/chromeos/login/signin/device_id_browsertest.cc @@ -0,0 +1,279 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/message_loop/message_loop.h" +#include "base/prefs/pref_service.h" +#include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "chrome/browser/chromeos/login/test/oobe_base_test.h" +#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" +#include "chrome/browser/chromeos/login/ui/oobe_display.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/signin/chrome_signin_client_factory.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chromeos/chromeos_switches.h" +#include "components/signin/core/common/signin_pref_names.h" +#include "components/user_manager/remove_user_delegate.h" +#include "components/user_manager/user_manager.h" + +namespace { + +char kRefreshToken1[] = "refresh_token_1"; +char kRefreshToken2[] = "refresh_token_2"; +const base::FilePath::CharType kRefreshTokenToDeviceIdMapFile[] = + FILE_PATH_LITERAL("refrest_token_to_device_id.json"); + +char kSecondUserEmail[] = "second_user@gmail.com"; +char kSecondUserPassword[] = "password"; +char kSecondUserRefreshToken1[] = "refresh_token_second_user_1"; +char kSecondUserRefreshToken2[] = "refresh_token_second_user_2"; + +} // namespace + +namespace chromeos { + +class DeviceIDTest : public OobeBaseTest, + public user_manager::RemoveUserDelegate { + public: + void SetUpCommandLine(base::CommandLine* command_line) override { + OobeBaseTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kOobeSkipPostLogin); + } + + void SetUpOnMainThread() override { + OobeBaseTest::SetUpOnMainThread(); + LoadRefreshTokenToDeviceIdMap(); + } + + void TearDownOnMainThread() override { + SaveRefreshTokenToDeviceIdMap(); + OobeBaseTest::TearDownOnMainThread(); + } + + std::string GetDeviceId(const std::string& user_id) { + return user_manager::UserManager::Get()->GetKnownUserDeviceId(user_id); + } + + std::string GetDeviceIdFromSigninClient(const std::string& user_id) { + return ChromeSigninClientFactory::GetForProfile( + ProfileHelper::Get()->GetProfileByUser( + user_manager::UserManager::Get()->FindUser(user_id))) + ->GetSigninScopedDeviceId(); + } + + std::string GetDeviceIdFromGAIA(const std::string& refresh_token) { + return fake_gaia_->GetDeviceIdByRefreshToken(refresh_token); + } + + // Checks that user's device ID retrieved from UserManager and SigninClient + // are the same. + // If |refresh_token| is not empty, checks that device ID associated with the + // |refresh_token| in GAIA is the same as ID saved on device. + void CheckDeviceIDIsConsistent(const std::string& user_id, + const std::string& refresh_token) { + const std::string device_id_in_signin_client = + GetDeviceIdFromSigninClient(user_id); + const std::string device_id_in_local_state = GetDeviceId(user_id); + + EXPECT_FALSE(device_id_in_signin_client.empty()); + EXPECT_EQ(device_id_in_signin_client, device_id_in_local_state); + + if (!refresh_token.empty()) { + const std::string device_id_in_gaia = GetDeviceIdFromGAIA(refresh_token); + EXPECT_EQ(device_id_in_signin_client, device_id_in_gaia); + } + } + + void WaitForSessionStart() { + content::WindowedNotificationObserver( + chrome::NOTIFICATION_SESSION_STARTED, + content::NotificationService::AllSources()).Wait(); + } + + void SignInOnline(const std::string& user_id, + const std::string& password, + const std::string& refresh_token) { + WaitForGaiaPageLoad(); + + FakeGaia::MergeSessionParams params; + params.email = user_id; + params.refresh_token = refresh_token; + fake_gaia_->UpdateMergeSessionParams(params); + + GetLoginDisplay()->ShowSigninScreenForCreds(user_id, password); + WaitForSessionStart(); + } + + void SignInOffline(const std::string& user_id, const std::string& password) { + WaitForSigninScreen(); + + JS().Execute( + base::StringPrintf("chrome.send('authenticateUser', ['%s', '%s'])", + user_id.c_str(), password.c_str())); + WaitForSessionStart(); + } + + void RemoveUser(const std::string& user_id) { + user_manager::UserManager::Get()->RemoveUser(user_id, this); + user_removal_loop_.Run(); + } + + private: + // user_manager::RemoveUserDelegate: + void OnBeforeUserRemoved(const std::string& username) override {} + + void OnUserRemoved(const std::string& username) override { + user_removal_loop_.Quit(); + } + + base::FilePath GetRefreshTokenToDeviceIdMapFilePath() const { + return base::CommandLine::ForCurrentProcess() + ->GetSwitchValuePath(::switches::kUserDataDir) + .Append(kRefreshTokenToDeviceIdMapFile); + } + + void LoadRefreshTokenToDeviceIdMap() { + std::string file_contents; + if (!base::ReadFileToString(GetRefreshTokenToDeviceIdMapFilePath(), + &file_contents)) + return; + scoped_ptr<base::Value> value(base::JSONReader::Read(file_contents)); + base::DictionaryValue* dictionary; + EXPECT_TRUE(value->GetAsDictionary(&dictionary)); + FakeGaia::RefreshTokenToDeviceIdMap map; + for (base::DictionaryValue::Iterator it(*dictionary); !it.IsAtEnd(); + it.Advance()) { + std::string device_id; + EXPECT_TRUE(it.value().GetAsString(&device_id)); + map[it.key()] = device_id; + } + fake_gaia_->SetRefreshTokenToDeviceIdMap(map); + } + + void SaveRefreshTokenToDeviceIdMap() { + base::DictionaryValue dictionary; + for (const auto& kv : fake_gaia_->refresh_token_to_device_id_map()) + dictionary.SetStringWithoutPathExpansion(kv.first, kv.second); + std::string json; + EXPECT_TRUE(base::JSONWriter::Write(dictionary, &json)); + EXPECT_TRUE(base::WriteFile(GetRefreshTokenToDeviceIdMapFilePath(), + json.c_str(), json.length())); + } + + base::RunLoop user_removal_loop_; +}; + +// Add the first user and check that device ID is consistent. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_PRE_PRE_PRE_PRE_NewUsers) { + SignInOnline(kFakeUserEmail, kFakeUserPassword, kRefreshToken1); + CheckDeviceIDIsConsistent(kFakeUserEmail, kRefreshToken1); +} + +// Authenticate the first user through GAIA and verify that device ID remains +// the same. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_PRE_PRE_PRE_NewUsers) { + const std::string device_id = GetDeviceId(kFakeUserEmail); + EXPECT_FALSE(device_id.empty()); + EXPECT_EQ(device_id, GetDeviceIdFromGAIA(kRefreshToken1)); + + SignInOnline(kFakeUserEmail, kFakeUserPassword, kRefreshToken2); + CheckDeviceIDIsConsistent(kFakeUserEmail, kRefreshToken2); + + CHECK_EQ(device_id, GetDeviceId(kFakeUserEmail)); +} + +// Authenticate the first user offline and verify that device ID remains +// the same. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_PRE_PRE_NewUsers) { + const std::string device_id = GetDeviceId(kFakeUserEmail); + EXPECT_FALSE(device_id.empty()); + + SignInOffline(kFakeUserEmail, kFakeUserPassword); + CheckDeviceIDIsConsistent(kFakeUserEmail, kRefreshToken2); + + // Verify that device ID remained the same after offline auth. + CHECK_EQ(device_id, GetDeviceId(kFakeUserEmail)); +} + +// Add the second user. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_PRE_NewUsers) { + WaitForSigninScreen(); + JS().Execute("chrome.send('showAddUser')"); + SignInOnline(kSecondUserEmail, kSecondUserPassword, kSecondUserRefreshToken1); + CheckDeviceIDIsConsistent(kSecondUserEmail, kSecondUserRefreshToken1); +} + +// Remove the second user. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_NewUsers) { + WaitForSigninScreen(); + RemoveUser(kSecondUserEmail); +} + +// Add the second user back. Verify that device ID has been changed. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, NewUsers) { + EXPECT_TRUE(GetDeviceId(kSecondUserEmail).empty()); + SignInOnline(kSecondUserEmail, kSecondUserPassword, kSecondUserRefreshToken2); + CheckDeviceIDIsConsistent(kSecondUserEmail, kSecondUserRefreshToken2); + EXPECT_NE(GetDeviceIdFromGAIA(kSecondUserRefreshToken1), + GetDeviceId(kSecondUserEmail)); +} + +// Set up a user that has a device ID stored in preference only. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_Migration) { + SignInOnline(kFakeUserEmail, kFakeUserPassword, kRefreshToken1); + + // Simulate user that has device ID saved only in preferences (pre-M44). + PrefService* prefs = + ProfileHelper::Get() + ->GetProfileByUser(user_manager::UserManager::Get()->GetActiveUser()) + ->GetPrefs(); + prefs->SetString(prefs::kGoogleServicesSigninScopedDeviceId, + GetDeviceId(kFakeUserEmail)); + + // Can't use SetKnownUserDeviceId here, because it forbids changing a device + // ID. + user_manager::UserManager::Get()->SetKnownUserStringPref( + kFakeUserEmail, "device_id", std::string()); +} + +// Tests that after the first sign in the device ID has been moved to the Local +// state. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, Migration) { + EXPECT_TRUE(GetDeviceId(kFakeUserEmail).empty()); + SignInOffline(kFakeUserEmail, kFakeUserPassword); + CheckDeviceIDIsConsistent(kFakeUserEmail, kRefreshToken1); +} + +// Set up a user that doesn't have a device ID. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_LegacyUsers) { + SignInOnline(kFakeUserEmail, kFakeUserPassword, kRefreshToken1); + + PrefService* prefs = + ProfileHelper::Get() + ->GetProfileByUser(user_manager::UserManager::Get()->GetActiveUser()) + ->GetPrefs(); + EXPECT_TRUE( + prefs->GetString(prefs::kGoogleServicesSigninScopedDeviceId).empty()); + + // Can't use SetKnownUserDeviceId here, because it forbids changing a device + // ID. + user_manager::UserManager::Get()->SetKnownUserStringPref( + kFakeUserEmail, "device_id", std::string()); +} + +// Tests that device ID has been generated after the first sign in. +IN_PROC_BROWSER_TEST_F(DeviceIDTest, LegacyUsers) { + EXPECT_TRUE(GetDeviceId(kFakeUserEmail).empty()); + SignInOffline(kFakeUserEmail, kFakeUserPassword); + // Last param |auth_code| is empty, because we don't pass a device ID to GAIA + // in this case. + CheckDeviceIDIsConsistent(kFakeUserEmail, std::string()); +} + +} // namespace chromeos diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index fe3ba26..0a1156b 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -712,6 +712,7 @@ 'browser/chromeos/login/screens/update_screen_browsertest.cc', 'browser/chromeos/login/session_login_browsertest.cc', 'browser/chromeos/login/signin/oauth2_browsertest.cc', + 'browser/chromeos/login/signin/device_id_browsertest.cc', 'browser/chromeos/login/supervised/supervised_user_creation_browsertest.cc', 'browser/chromeos/login/supervised/supervised_user_password_browsertest.cc', 'browser/chromeos/login/supervised/supervised_user_test_base.cc', diff --git a/google_apis/gaia/fake_gaia.cc b/google_apis/gaia/fake_gaia.cc index 02fccd1..88fb274 100644 --- a/google_apis/gaia/fake_gaia.cc +++ b/google_apis/gaia/fake_gaia.cc @@ -136,6 +136,25 @@ FakeGaia::MergeSessionParams::MergeSessionParams() { FakeGaia::MergeSessionParams::~MergeSessionParams() { } +void FakeGaia::MergeSessionParams::Update(const MergeSessionParams& update) { + // This lambda uses a pointer to data member to merge attributes. + auto maybe_update_field = + [this, &update](std::string MergeSessionParams::* field_ptr) { + if (!(update.*field_ptr).empty()) + this->*field_ptr = update.*field_ptr; + }; + + maybe_update_field(&MergeSessionParams::auth_sid_cookie); + maybe_update_field(&MergeSessionParams::auth_lsid_cookie); + maybe_update_field(&MergeSessionParams::auth_code); + maybe_update_field(&MergeSessionParams::refresh_token); + maybe_update_field(&MergeSessionParams::access_token); + maybe_update_field(&MergeSessionParams::gaia_uber_token); + maybe_update_field(&MergeSessionParams::session_sid_cookie); + maybe_update_field(&MergeSessionParams::session_lsid_cookie); + maybe_update_field(&MergeSessionParams::email); +} + FakeGaia::FakeGaia() : issue_oauth_code_cookie_(false) { base::FilePath source_root_dir; PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir); @@ -171,6 +190,10 @@ void FakeGaia::SetMergeSessionParams( merge_session_params_ = params; } +void FakeGaia::UpdateMergeSessionParams(const MergeSessionParams& params) { + merge_session_params_.Update(params); +} + void FakeGaia::MapEmailToGaiaId(const std::string& email, const std::string& gaia_id) { DCHECK(!email.empty()); @@ -180,7 +203,7 @@ void FakeGaia::MapEmailToGaiaId(const std::string& email, std::string FakeGaia::GetGaiaIdOfEmail(const std::string& email) const { DCHECK(!email.empty()); - auto it = email_to_gaia_id_map_.find(email); + const auto it = email_to_gaia_id_map_.find(email); return it == email_to_gaia_id_map_.end() ? std::string(kDefaultGaiaId) : it->second; } @@ -306,6 +329,18 @@ bool FakeGaia::GetQueryParameter(const std::string& query, return net::GetValueForKeyInQuery(query_url, key, value); } +std::string FakeGaia::GetDeviceIdByRefreshToken( + const std::string& refresh_token) const { + auto it = refresh_token_to_device_id_map_.find(refresh_token); + return it != refresh_token_to_device_id_map_.end() ? it->second + : std::string(); +} + +void FakeGaia::SetRefreshTokenToDeviceIdMap( + const RefreshTokenToDeviceIdMap& refresh_token_to_device_id_map) { + refresh_token_to_device_id_map_ = refresh_token_to_device_id_map; +} + void FakeGaia::HandleMergeSession(const HttpRequest& request, BasicHttpResponse* http_response) { http_response->set_code(net::HTTP_UNAUTHORIZED); @@ -602,9 +637,27 @@ void FakeGaia::HandleAuthToken(const HttpRequest& request, return; } + std::string device_id; + if (GetQueryParameter(request.content, "device_id", &device_id)) { + std::string device_type; + if (!GetQueryParameter(request.content, "device_type", &device_type)) { + http_response->set_code(net::HTTP_BAD_REQUEST); + LOG(ERROR) << "'device_type' should be set if 'device_id' is set."; + return; + } + if (device_type != "chrome") { + http_response->set_code(net::HTTP_BAD_REQUEST); + LOG(ERROR) << "'device_type' is not 'chrome'."; + return; + } + } + base::DictionaryValue response_dict; response_dict.SetString("refresh_token", merge_session_params_.refresh_token); + if (!device_id.empty()) + refresh_token_to_device_id_map_[merge_session_params_.refresh_token] = + device_id; response_dict.SetString("access_token", merge_session_params_.access_token); response_dict.SetInteger("expires_in", 3600); diff --git a/google_apis/gaia/fake_gaia.h b/google_apis/gaia/fake_gaia.h index e50b7cb..1f1a3ef 100644 --- a/google_apis/gaia/fake_gaia.h +++ b/google_apis/gaia/fake_gaia.h @@ -31,7 +31,8 @@ class HttpResponse; // be registered as an additional request handler. class FakeGaia { public: - typedef std::set<std::string> ScopeSet; + using ScopeSet = std::set<std::string>; + using RefreshTokenToDeviceIdMap = std::map<std::string, std::string>; // Access token details used for token minting and the token info endpoint. struct AccessTokenInfo { @@ -52,6 +53,9 @@ class FakeGaia { MergeSessionParams(); ~MergeSessionParams(); + // Updates params with non-empty values from |params|. + void Update(const MergeSessionParams& params); + // Values of SID and LSID cookie that are set by /ServiceLoginAuth or its // equivalent at the end of the SAML login flow. std::string auth_sid_cookie; @@ -86,6 +90,9 @@ class FakeGaia { // Sets the initial value of tokens and cookies. void SetMergeSessionParams(const MergeSessionParams& params); + // Updates various params with non-empty values from |params|. + void UpdateMergeSessionParams(const MergeSessionParams& params); + // Sets the specified |gaia_id| as corresponding to the given |email| // address when setting GAIA response headers. If no mapping is given for // an email address, a default GAIA Id is used. @@ -122,6 +129,17 @@ class FakeGaia { static bool GetQueryParameter(const std::string& query, const std::string& key, std::string* value); + + // Returns a device ID associated with a given |refresh_token|. + std::string GetDeviceIdByRefreshToken(const std::string& refresh_token) const; + + void SetRefreshTokenToDeviceIdMap( + const RefreshTokenToDeviceIdMap& refresh_token_to_device_id_map); + + const RefreshTokenToDeviceIdMap& refresh_token_to_device_id_map() const { + return refresh_token_to_device_id_map_; + } + protected: // HTTP handler for /MergeSession. virtual void HandleMergeSession( @@ -210,6 +228,7 @@ class FakeGaia { std::string embedded_setup_chromeos_response_; SamlAccountIdpMap saml_account_idp_map_; bool issue_oauth_code_cookie_; + RefreshTokenToDeviceIdMap refresh_token_to_device_id_map_; DISALLOW_COPY_AND_ASSIGN(FakeGaia); }; |