chromium/chromeos/ash/components/proximity_auth/proximity_auth_system_unittest.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/proximity_auth/proximity_auth_system.h"

#include <memory>

#include "base/command_line.h"
#include "base/memory/raw_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/components/multidevice/remote_device_ref.h"
#include "chromeos/ash/components/multidevice/remote_device_test_util.h"
#include "chromeos/ash/components/multidevice/software_feature_state.h"
#include "chromeos/ash/components/proximity_auth/fake_lock_handler.h"
#include "chromeos/ash/components/proximity_auth/fake_remote_device_life_cycle.h"
#include "chromeos/ash/components/proximity_auth/mock_proximity_auth_client.h"
#include "chromeos/ash/components/proximity_auth/proximity_auth_profile_pref_manager.h"
#include "chromeos/ash/components/proximity_auth/unlock_manager.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
#include "chromeos/ash/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 ash::multidevice::RemoteDeviceRefList& list1,
    const ash::multidevice::RemoteDeviceRefList& list2) {
  ASSERT_EQ(list1.size(), list2.size());
  for (size_t i = 0; i < list1.size(); ++i) {
    ash::multidevice::RemoteDeviceRef device1 = list1[i];
    ash::multidevice::RemoteDeviceRef device2 = list2[i];
    EXPECT_EQ(device1.public_key(), device2.public_key());
  }
}

// Creates a RemoteDeviceRef object for |user_id| with |name|.
ash::multidevice::RemoteDeviceRef CreateRemoteDevice(
    const std::string& user_email,
    const std::string& name) {
  return ash::multidevice::RemoteDeviceRefBuilder()
      .SetUserEmail(user_email)
      .SetName(name)
      .Build();
}

// Mock implementation of UnlockManager.
class MockUnlockManager : public UnlockManager {
 public:
  MockUnlockManager() {}

  MockUnlockManager(const MockUnlockManager&) = delete;
  MockUnlockManager& operator=(const MockUnlockManager&) = delete;

  ~MockUnlockManager() override {}
  MOCK_METHOD0(IsUnlockAllowed, bool());
  MOCK_METHOD1(SetRemoteDeviceLifeCycle, void(RemoteDeviceLifeCycle*));
  MOCK_METHOD1(OnAuthAttempted, void(mojom::AuthType));
  MOCK_METHOD0(CancelConnectionAttempt, void());
  MOCK_METHOD0(GetLastRemoteStatusUnlockForLogging, std::string());
};

// Mock implementation of ProximityAuthProfilePrefManager.
class MockProximityAuthPrefManager : public ProximityAuthProfilePrefManager {
 public:
  MockProximityAuthPrefManager(
      ash::multidevice_setup::FakeMultiDeviceSetupClient*
          fake_multidevice_setup_client)
      : ProximityAuthProfilePrefManager(nullptr,
                                        fake_multidevice_setup_client) {}

  MockProximityAuthPrefManager(const MockProximityAuthPrefManager&) = delete;
  MockProximityAuthPrefManager& operator=(const MockProximityAuthPrefManager&) =
      delete;

  MOCK_CONST_METHOD0(GetLastPasswordEntryTimestampMs, int64_t());
};

// Harness for ProximityAuthSystem to make it testable.
class TestableProximityAuthSystem : public ProximityAuthSystem {
 public:
  TestableProximityAuthSystem(
      ash::secure_channel::SecureChannelClient* secure_channel_client,
      std::unique_ptr<UnlockManager> unlock_manager,
      ProximityAuthProfilePrefManager* pref_manager)
      : ProximityAuthSystem(secure_channel_client, std::move(unlock_manager)),
        life_cycle_(nullptr) {}

  TestableProximityAuthSystem(const TestableProximityAuthSystem&) = delete;
  TestableProximityAuthSystem& operator=(const TestableProximityAuthSystem&) =
      delete;

  ~TestableProximityAuthSystem() override {}

  FakeRemoteDeviceLifeCycle* life_cycle() { return life_cycle_; }

 private:
  std::unique_ptr<RemoteDeviceLifeCycle> CreateRemoteDeviceLifeCycle(
      ash::multidevice::RemoteDeviceRef remote_device,
      std::optional<ash::multidevice::RemoteDeviceRef> local_device) override {
    std::unique_ptr<FakeRemoteDeviceLifeCycle> life_cycle(
        new FakeRemoteDeviceLifeCycle(remote_device, local_device));
    life_cycle_ = life_cycle.get();
    return life_cycle;
  }

  raw_ptr<FakeRemoteDeviceLifeCycle, DanglingUntriaged> life_cycle_;
};

}  // namespace

class ProximityAuthSystemTest : public testing::Test {
 public:
  ProximityAuthSystemTest(const ProximityAuthSystemTest&) = delete;
  ProximityAuthSystemTest& operator=(const ProximityAuthSystemTest&) = delete;

 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_current_default_handle_(task_runner_) {}

  void TearDown() override {
    UnlockScreen();
    pref_manager_.reset();
  }

  void SetUp() override {
    fake_multidevice_setup_client_ =
        std::make_unique<ash::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<ash::secure_channel::FakeSecureChannelClient>();

    proximity_auth_system_ = std::make_unique<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<ash::secure_channel::FakeSecureChannelClient>
      fake_secure_channel_client_;
  std::unique_ptr<TestableProximityAuthSystem> proximity_auth_system_;
  raw_ptr<MockUnlockManager> unlock_manager_;
  std::unique_ptr<MockProximityAuthPrefManager> pref_manager_;
  std::unique_ptr<ash::multidevice_setup::FakeMultiDeviceSetupClient>
      fake_multidevice_setup_client_;

  ash::multidevice::RemoteDeviceRef user1_local_device_;
  ash::multidevice::RemoteDeviceRef user2_local_device_;

  ash::multidevice::RemoteDeviceRefList user1_remote_devices_;
  ash::multidevice::RemoteDeviceRefList user2_remote_devices_;

  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
  base::SingleThreadTaskRunner::CurrentDefaultHandle
      thread_task_runner_current_default_handle_;

 private:
  ash::multidevice::ScopedDisableLoggingForTesting disable_logging_;
};

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(
      ash::multidevice::RemoteDeviceRefList(),
      proximity_auth_system_->GetRemoteDevicesForUser(
          AccountId::FromUserEmail("[email protected]")));
}

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