blob: f9992317fb029418bd816657caa6ae5594e25482 [file] [log] [blame]
// 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/run_loop.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/login/screens/gaia_view.h"
#include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
#include "chrome/browser/chromeos/login/test/js_checker.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/login_display_host.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/signin/chrome_device_id_helper.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/constants/chromeos_switches.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_pref_names.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/remove_user_delegate.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/notification_service.h"
#include "content/public/test/test_utils.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 kSecondUserGaiaId[] = "4321";
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 {
user_removal_loop_.reset(new base::RunLoop);
OobeBaseTest::SetUpOnMainThread();
LoadRefreshTokenToDeviceIdMap();
}
void TearDownOnMainThread() override {
SaveRefreshTokenToDeviceIdMap();
OobeBaseTest::TearDownOnMainThread();
}
std::string GetDeviceId(const AccountId& account_id) {
return user_manager::known_user::GetDeviceId(account_id);
}
std::string GetDeviceIdFromProfile(const AccountId& account_id) {
return GetSigninScopedDeviceIdForProfile(
ProfileHelper::Get()->GetProfileByUser(
user_manager::UserManager::Get()->FindUser(account_id)));
}
std::string GetDeviceIdFromGAIA(const std::string& refresh_token) {
return fake_gaia_.fake_gaia()->GetDeviceIdByRefreshToken(refresh_token);
}
// Checks that user's device ID retrieved from UserManager and Profile 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 AccountId& account_id,
const std::string& refresh_token) {
const std::string device_id_in_profile = GetDeviceIdFromProfile(account_id);
const std::string device_id_in_local_state = GetDeviceId(account_id);
EXPECT_FALSE(device_id_in_profile.empty());
EXPECT_EQ(device_id_in_profile, device_id_in_local_state);
if (!refresh_token.empty()) {
const std::string device_id_in_gaia = GetDeviceIdFromGAIA(refresh_token);
EXPECT_EQ(device_id_in_profile, 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,
const std::string& gaia_id) {
WaitForGaiaPageLoad();
FakeGaia::MergeSessionParams params;
params.email = user_id;
params.refresh_token = refresh_token;
fake_gaia_.fake_gaia()->UpdateMergeSessionParams(params);
fake_gaia_.fake_gaia()->MapEmailToGaiaId(user_id, gaia_id);
LoginDisplayHost::default_host()
->GetOobeUI()
->GetGaiaScreenView()
->ShowSigninScreenForTest(user_id, password, "[]");
WaitForSessionStart();
}
void SignInOffline(const std::string& user_id, const std::string& password) {
WaitForSigninScreen();
test::OobeJS().ExecuteAsync(base::StringPrintf(
"chrome.send('authenticateUser', ['%s', '%s', false])", user_id.c_str(),
password.c_str()));
WaitForSessionStart();
}
void RemoveUser(const AccountId& account_id) {
user_manager::UserManager::Get()->RemoveUser(account_id, this);
user_removal_loop_->Run();
}
private:
// user_manager::RemoveUserDelegate:
void OnBeforeUserRemoved(const AccountId& account_id) override {}
void OnUserRemoved(const AccountId& account_id) 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;
std::unique_ptr<base::Value> value(
base::JSONReader::ReadDeprecated(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_.fake_gaia()->SetRefreshTokenToDeviceIdMap(map);
}
void SaveRefreshTokenToDeviceIdMap() {
base::DictionaryValue dictionary;
for (const auto& kv :
fake_gaia_.fake_gaia()->refresh_token_to_device_id_map())
dictionary.SetKey(kv.first, base::Value(kv.second));
std::string json;
EXPECT_TRUE(base::JSONWriter::Write(dictionary, &json));
EXPECT_EQ(static_cast<int>(json.length()),
base::WriteFile(GetRefreshTokenToDeviceIdMapFilePath(),
json.c_str(), json.length()));
}
std::unique_ptr<base::RunLoop> user_removal_loop_;
FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()};
};
// Add the first user and check that device ID is consistent.
IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_PRE_PRE_PRE_PRE_NewUsers) {
SignInOnline(FakeGaiaMixin::kFakeUserEmail, FakeGaiaMixin::kFakeUserPassword,
kRefreshToken1, FakeGaiaMixin::kFakeUserGaiaId);
CheckDeviceIDIsConsistent(
AccountId::FromUserEmail(FakeGaiaMixin::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(AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail));
EXPECT_FALSE(device_id.empty());
EXPECT_EQ(device_id, GetDeviceIdFromGAIA(kRefreshToken1));
SignInOnline(FakeGaiaMixin::kFakeUserEmail, FakeGaiaMixin::kFakeUserPassword,
kRefreshToken2, FakeGaiaMixin::kFakeUserGaiaId);
CheckDeviceIDIsConsistent(
AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail), kRefreshToken2);
CHECK_EQ(
device_id,
GetDeviceId(AccountId::FromUserEmail(FakeGaiaMixin::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(AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail));
EXPECT_FALSE(device_id.empty());
SignInOffline(FakeGaiaMixin::kFakeUserEmail,
FakeGaiaMixin::kFakeUserPassword);
CheckDeviceIDIsConsistent(
AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail), kRefreshToken2);
// Verify that device ID remained the same after offline auth.
CHECK_EQ(
device_id,
GetDeviceId(AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail)));
}
// Add the second user.
IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_PRE_NewUsers) {
WaitForSigninScreen();
test::OobeJS().ExecuteAsync("chrome.send('showAddUser')");
SignInOnline(kSecondUserEmail, kSecondUserPassword, kSecondUserRefreshToken1,
kSecondUserGaiaId);
CheckDeviceIDIsConsistent(AccountId::FromUserEmail(kSecondUserEmail),
kSecondUserRefreshToken1);
}
// Remove the second user.
IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_NewUsers) {
WaitForSigninScreen();
RemoveUser(AccountId::FromUserEmail(kSecondUserEmail));
}
// Add the second user back. Verify that device ID has been changed.
IN_PROC_BROWSER_TEST_F(DeviceIDTest, NewUsers) {
EXPECT_TRUE(GetDeviceId(AccountId::FromUserEmail(kSecondUserEmail)).empty());
SignInOnline(kSecondUserEmail, kSecondUserPassword, kSecondUserRefreshToken2,
kSecondUserGaiaId);
CheckDeviceIDIsConsistent(AccountId::FromUserEmail(kSecondUserEmail),
kSecondUserRefreshToken2);
EXPECT_NE(GetDeviceIdFromGAIA(kSecondUserRefreshToken1),
GetDeviceId(AccountId::FromUserEmail(kSecondUserEmail)));
}
// Set up a user that has a device ID stored in preference only.
IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_Migration) {
SignInOnline(FakeGaiaMixin::kFakeUserEmail, FakeGaiaMixin::kFakeUserPassword,
kRefreshToken1, FakeGaiaMixin::kFakeUserGaiaId);
// 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(AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail)));
// Can't use SetKnownUserDeviceId here, because it forbids changing a device
// ID.
user_manager::known_user::SetStringPref(
AccountId::FromUserEmail(FakeGaiaMixin::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(AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail))
.empty());
SignInOffline(FakeGaiaMixin::kFakeUserEmail,
FakeGaiaMixin::kFakeUserPassword);
CheckDeviceIDIsConsistent(
AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail), kRefreshToken1);
}
// Set up a user that doesn't have a device ID.
IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_LegacyUsers) {
SignInOnline(FakeGaiaMixin::kFakeUserEmail, FakeGaiaMixin::kFakeUserPassword,
kRefreshToken1, FakeGaiaMixin::kFakeUserGaiaId);
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::known_user::SetStringPref(
AccountId::FromUserEmail(FakeGaiaMixin::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(AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail))
.empty());
SignInOffline(FakeGaiaMixin::kFakeUserEmail,
FakeGaiaMixin::kFakeUserPassword);
// Last param |auth_code| is empty, because we don't pass a device ID to GAIA
// in this case.
CheckDeviceIDIsConsistent(
AccountId::FromUserEmail(FakeGaiaMixin::kFakeUserEmail), std::string());
}
} // namespace chromeos