blob: 95484b3f13324205cb3a96562404d7e4fff96795 [file] [log] [blame]
// Copyright 2014 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 "chromeos/components/proximity_auth/proximity_auth_system.h"
#include "base/command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromeos/components/multidevice/logging/logging.h"
#include "chromeos/components/multidevice/remote_device_ref.h"
#include "chromeos/components/multidevice/remote_device_test_util.h"
#include "chromeos/components/multidevice/software_feature_state.h"
#include "chromeos/components/proximity_auth/fake_lock_handler.h"
#include "chromeos/components/proximity_auth/fake_remote_device_life_cycle.h"
#include "chromeos/components/proximity_auth/mock_proximity_auth_client.h"
#include "chromeos/components/proximity_auth/proximity_auth_profile_pref_manager.h"
#include "chromeos/components/proximity_auth/unlock_manager.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
#include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::AnyNumber;
using testing::AtLeast;
using testing::InSequence;
using testing::NiceMock;
using testing::NotNull;
using testing::Return;
using testing::SaveArg;
namespace proximity_auth {
namespace {
const char kUser1[] = "user1";
const char kUser2[] = "user2";
void CompareRemoteDeviceRefLists(
const chromeos::multidevice::RemoteDeviceRefList& list1,
const chromeos::multidevice::RemoteDeviceRefList& list2) {
ASSERT_EQ(list1.size(), list2.size());
for (size_t i = 0; i < list1.size(); ++i) {
chromeos::multidevice::RemoteDeviceRef device1 = list1[i];
chromeos::multidevice::RemoteDeviceRef device2 = list2[i];
EXPECT_EQ(device1.public_key(), device2.public_key());
}
}
// Creates a RemoteDeviceRef object for |user_id| with |name|.
chromeos::multidevice::RemoteDeviceRef CreateRemoteDevice(
const std::string& user_email,
const std::string& name) {
return chromeos::multidevice::RemoteDeviceRefBuilder()
.SetUserEmail(user_email)
.SetName(name)
.Build();
}
// Mock implementation of UnlockManager.
class MockUnlockManager : public UnlockManager {
public:
MockUnlockManager() {}
~MockUnlockManager() override {}
MOCK_METHOD0(IsUnlockAllowed, bool());
MOCK_METHOD1(SetRemoteDeviceLifeCycle, void(RemoteDeviceLifeCycle*));
MOCK_METHOD1(OnAuthAttempted, void(mojom::AuthType));
MOCK_METHOD0(CancelConnectionAttempt, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockUnlockManager);
};
// Mock implementation of ProximityAuthProfilePrefManager.
class MockProximityAuthPrefManager : public ProximityAuthProfilePrefManager {
public:
MockProximityAuthPrefManager(
chromeos::multidevice_setup::FakeMultiDeviceSetupClient*
fake_multidevice_setup_client)
: ProximityAuthProfilePrefManager(nullptr,
fake_multidevice_setup_client) {}
~MockProximityAuthPrefManager() override {}
MOCK_CONST_METHOD0(GetLastPasswordEntryTimestampMs, int64_t());
private:
DISALLOW_COPY_AND_ASSIGN(MockProximityAuthPrefManager);
};
// Harness for ProximityAuthSystem to make it testable.
class TestableProximityAuthSystem : public ProximityAuthSystem {
public:
TestableProximityAuthSystem(
chromeos::secure_channel::SecureChannelClient* secure_channel_client,
std::unique_ptr<UnlockManager> unlock_manager,
ProximityAuthPrefManager* pref_manager)
: ProximityAuthSystem(secure_channel_client, std::move(unlock_manager)),
life_cycle_(nullptr) {}
~TestableProximityAuthSystem() override {}
FakeRemoteDeviceLifeCycle* life_cycle() { return life_cycle_; }
private:
std::unique_ptr<RemoteDeviceLifeCycle> CreateRemoteDeviceLifeCycle(
chromeos::multidevice::RemoteDeviceRef remote_device,
base::Optional<chromeos::multidevice::RemoteDeviceRef> local_device)
override {
std::unique_ptr<FakeRemoteDeviceLifeCycle> life_cycle(
new FakeRemoteDeviceLifeCycle(remote_device, local_device));
life_cycle_ = life_cycle.get();
return std::move(life_cycle);
}
FakeRemoteDeviceLifeCycle* life_cycle_;
DISALLOW_COPY_AND_ASSIGN(TestableProximityAuthSystem);
};
} // namespace
class ProximityAuthSystemTest : public testing::Test {
protected:
ProximityAuthSystemTest()
: user1_local_device_(CreateRemoteDevice(kUser1, "user1_local_device")),
user2_local_device_(CreateRemoteDevice(kUser2, "user2_local_device")),
task_runner_(new base::TestSimpleTaskRunner()),
thread_task_runner_handle_(task_runner_) {}
void TearDown() override {
UnlockScreen();
pref_manager_.reset();
}
void SetUp() override {
fake_multidevice_setup_client_ = std::make_unique<
chromeos::multidevice_setup::FakeMultiDeviceSetupClient>();
pref_manager_ = std::make_unique<NiceMock<MockProximityAuthPrefManager>>(
fake_multidevice_setup_client_.get());
user1_remote_devices_.push_back(
CreateRemoteDevice(kUser1, "user1_device1"));
user1_remote_devices_.push_back(
CreateRemoteDevice(kUser1, "user1_device2"));
user2_remote_devices_.push_back(
CreateRemoteDevice(kUser2, "user2_device1"));
user2_remote_devices_.push_back(
CreateRemoteDevice(kUser2, "user2_device2"));
user2_remote_devices_.push_back(
CreateRemoteDevice(kUser2, "user2_device3"));
std::unique_ptr<MockUnlockManager> unlock_manager(
new NiceMock<MockUnlockManager>());
unlock_manager_ = unlock_manager.get();
fake_secure_channel_client_ =
std::make_unique<chromeos::secure_channel::FakeSecureChannelClient>();
proximity_auth_system_.reset(new TestableProximityAuthSystem(
fake_secure_channel_client_.get(), std::move(unlock_manager),
pref_manager_.get()));
proximity_auth_system_->SetRemoteDevicesForUser(
AccountId::FromUserEmail(kUser1), user1_remote_devices_,
user1_local_device_);
proximity_auth_system_->Start();
LockScreen();
}
void LockScreen() {
ScreenlockBridge::Get()->SetFocusedUser(AccountId());
ScreenlockBridge::Get()->SetLockHandler(&lock_handler_);
}
void FocusUser(const std::string& user_email) {
ScreenlockBridge::Get()->SetFocusedUser(
AccountId::FromUserEmail(user_email));
}
void UnlockScreen() { ScreenlockBridge::Get()->SetLockHandler(nullptr); }
void SimulateSuspend() {
proximity_auth_system_->OnSuspend();
proximity_auth_system_->OnSuspendDone();
task_runner_->RunUntilIdle();
}
FakeRemoteDeviceLifeCycle* life_cycle() {
return proximity_auth_system_->life_cycle();
}
FakeLockHandler lock_handler_;
NiceMock<MockProximityAuthClient> proximity_auth_client_;
std::unique_ptr<chromeos::secure_channel::FakeSecureChannelClient>
fake_secure_channel_client_;
std::unique_ptr<TestableProximityAuthSystem> proximity_auth_system_;
MockUnlockManager* unlock_manager_;
std::unique_ptr<MockProximityAuthPrefManager> pref_manager_;
std::unique_ptr<chromeos::multidevice_setup::FakeMultiDeviceSetupClient>
fake_multidevice_setup_client_;
chromeos::multidevice::RemoteDeviceRef user1_local_device_;
chromeos::multidevice::RemoteDeviceRef user2_local_device_;
chromeos::multidevice::RemoteDeviceRefList user1_remote_devices_;
chromeos::multidevice::RemoteDeviceRefList user2_remote_devices_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle thread_task_runner_handle_;
private:
chromeos::multidevice::ScopedDisableLoggingForTesting disable_logging_;
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(ProximityAuthSystemTest);
};
TEST_F(ProximityAuthSystemTest, SetRemoteDevicesForUser_NotStarted) {
AccountId account1 = AccountId::FromUserEmail(kUser1);
AccountId account2 = AccountId::FromUserEmail(kUser2);
proximity_auth_system_->SetRemoteDevicesForUser(
account1, user1_remote_devices_, user1_local_device_);
proximity_auth_system_->SetRemoteDevicesForUser(
account2, user2_remote_devices_, user1_local_device_);
CompareRemoteDeviceRefLists(
user1_remote_devices_,
proximity_auth_system_->GetRemoteDevicesForUser(account1));
CompareRemoteDeviceRefLists(
user2_remote_devices_,
proximity_auth_system_->GetRemoteDevicesForUser(account2));
CompareRemoteDeviceRefLists(
chromeos::multidevice::RemoteDeviceRefList(),
proximity_auth_system_->GetRemoteDevicesForUser(
AccountId::FromUserEmail("non_existent_user@google.com")));
}
TEST_F(ProximityAuthSystemTest, SetRemoteDevicesForUser_Started) {
AccountId account1 = AccountId::FromUserEmail(kUser1);
AccountId account2 = AccountId::FromUserEmail(kUser2);
proximity_auth_system_->SetRemoteDevicesForUser(
account1, user1_remote_devices_, user1_local_device_);
proximity_auth_system_->Start();
proximity_auth_system_->SetRemoteDevicesForUser(
account2, user2_remote_devices_, user2_local_device_);
CompareRemoteDeviceRefLists(
user1_remote_devices_,
proximity_auth_system_->GetRemoteDevicesForUser(account1));
CompareRemoteDeviceRefLists(
user2_remote_devices_,
proximity_auth_system_->GetRemoteDevicesForUser(account2));
}
TEST_F(ProximityAuthSystemTest, FocusRegisteredUser) {
EXPECT_FALSE(life_cycle());
EXPECT_EQ(std::string(),
ScreenlockBridge::Get()->focused_account_id().GetUserEmail());
RemoteDeviceLifeCycle* unlock_manager_life_cycle = nullptr;
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(_))
.WillOnce(SaveArg<0>(&unlock_manager_life_cycle));
FocusUser(kUser1);
EXPECT_EQ(life_cycle(), unlock_manager_life_cycle);
EXPECT_TRUE(life_cycle());
EXPECT_FALSE(life_cycle()->started());
EXPECT_EQ(kUser1, life_cycle()->GetRemoteDevice().user_email());
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AtLeast(1));
}
TEST_F(ProximityAuthSystemTest, FocusUnregisteredUser) {
EXPECT_FALSE(life_cycle());
EXPECT_EQ(std::string(),
ScreenlockBridge::Get()->focused_account_id().GetUserEmail());
EXPECT_FALSE(life_cycle());
FocusUser(kUser2);
EXPECT_FALSE(life_cycle());
}
TEST_F(ProximityAuthSystemTest, ToggleFocus_RegisteredUsers) {
proximity_auth_system_->SetRemoteDevicesForUser(
AccountId::FromUserEmail(kUser1), user1_remote_devices_,
user1_local_device_);
proximity_auth_system_->SetRemoteDevicesForUser(
AccountId::FromUserEmail(kUser2), user2_remote_devices_,
user2_local_device_);
RemoteDeviceLifeCycle* life_cycle1 = nullptr;
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(_))
.WillOnce(SaveArg<0>(&life_cycle1));
FocusUser(kUser1);
EXPECT_EQ(kUser1, life_cycle1->GetRemoteDevice().user_email());
EXPECT_EQ(user1_local_device_, life_cycle()->local_device());
RemoteDeviceLifeCycle* life_cycle2 = nullptr;
{
InSequence sequence;
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AtLeast(1));
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(_))
.WillOnce(SaveArg<0>(&life_cycle2));
}
FocusUser(kUser2);
EXPECT_EQ(kUser2, life_cycle2->GetRemoteDevice().user_email());
EXPECT_EQ(user2_local_device_, life_cycle()->local_device());
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AtLeast(1));
}
TEST_F(ProximityAuthSystemTest, ToggleFocus_UnregisteredUsers) {
FocusUser(kUser2);
EXPECT_FALSE(life_cycle());
FocusUser("unregistered-user");
EXPECT_FALSE(life_cycle());
FocusUser(kUser2);
EXPECT_FALSE(life_cycle());
}
TEST_F(ProximityAuthSystemTest, ToggleFocus_RegisteredAndUnregisteredUsers) {
// Focus User 1, who is registered. This should create a new life cycle.
RemoteDeviceLifeCycle* life_cycle = nullptr;
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(_))
.WillOnce(SaveArg<0>(&life_cycle));
FocusUser(kUser1);
EXPECT_EQ(kUser1, life_cycle->GetRemoteDevice().user_email());
// User 2 has not been registered yet, so focusing them should not create a
// new life cycle.
life_cycle = nullptr;
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr));
FocusUser(kUser2);
EXPECT_FALSE(life_cycle);
// Focusing back to User 1 should recreate a new life cycle.
life_cycle = nullptr;
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(_))
.WillOnce(SaveArg<0>(&life_cycle));
FocusUser(kUser1);
EXPECT_EQ(kUser1, life_cycle->GetRemoteDevice().user_email());
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AtLeast(1));
}
TEST_F(ProximityAuthSystemTest, ToggleFocus_SameUserRefocused) {
RemoteDeviceLifeCycle* life_cycle = nullptr;
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(_))
.WillOnce(SaveArg<0>(&life_cycle));
FocusUser(kUser1);
EXPECT_EQ(kUser1, life_cycle->GetRemoteDevice().user_email());
// Focusing the user again should be idempotent. The screenlock UI may call
// focus on the same user multiple times.
// SetRemoteDeviceLifeCycle() is only expected to be called once.
FocusUser(kUser1);
// The RemoteDeviceLifeCycle should be nulled upon destruction.
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AtLeast(1));
}
TEST_F(ProximityAuthSystemTest, RestartSystem_UnregisteredUserFocused) {
FocusUser(kUser2);
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AnyNumber());
proximity_auth_system_->Stop();
proximity_auth_system_->Start();
EXPECT_FALSE(life_cycle());
}
TEST_F(ProximityAuthSystemTest, StopSystem_RegisteredUserFocused) {
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(NotNull()));
FocusUser(kUser1);
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AtLeast(1));
proximity_auth_system_->Stop();
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(NotNull()));
proximity_auth_system_->Start();
EXPECT_EQ(kUser1, life_cycle()->GetRemoteDevice().user_email());
}
TEST_F(ProximityAuthSystemTest, OnAuthAttempted) {
FocusUser(kUser1);
EXPECT_CALL(*unlock_manager_, OnAuthAttempted(_));
proximity_auth_system_->OnAuthAttempted();
}
TEST_F(ProximityAuthSystemTest, Suspend_ScreenUnlocked) {
UnlockScreen();
EXPECT_FALSE(life_cycle());
SimulateSuspend();
EXPECT_FALSE(life_cycle());
}
TEST_F(ProximityAuthSystemTest, Suspend_UnregisteredUserFocused) {
SimulateSuspend();
EXPECT_FALSE(life_cycle());
}
TEST_F(ProximityAuthSystemTest, Suspend_RegisteredUserFocused) {
FocusUser(kUser1);
{
InSequence sequence;
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AtLeast(1));
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(NotNull()));
SimulateSuspend();
}
EXPECT_EQ(kUser1, life_cycle()->GetRemoteDevice().user_email());
EXPECT_CALL(*unlock_manager_, SetRemoteDeviceLifeCycle(nullptr))
.Times(AtLeast(1));
}
} // namespace proximity_auth