summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordzhioev <dzhioev@chromium.org>2015-05-28 11:43:40 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-28 18:44:00 +0000
commit1e76fed394f8a90b049ce2c06ca06f677c899024 (patch)
treee143c7c3245af22e9dab44d8f88ad5e9a84b2911
parentd2adf1e487ba54a15d92947ce29fbe18b8edd686 (diff)
downloadchromium_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.cc279
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--google_apis/gaia/fake_gaia.cc55
-rw-r--r--google_apis/gaia/fake_gaia.h21
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);
};