chromium/chrome/browser/ash/smb_client/smb_kerberos_credentials_updater_unittest.cc

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

#include "chrome/browser/ash/smb_client/smb_kerberos_credentials_updater.h"

#include <memory>

#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/values.h"
#include "chrome/browser/ash/kerberos/kerberos_credentials_manager.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/dbus/kerberos/kerberos_client.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::base::test::RunClosure;

namespace ash::smb_client {

namespace {

constexpr char kProfileEmail[] = "[email protected]";
constexpr char kPrincipal[] = "[email protected]";
constexpr char kOtherPrincipal[] = "[email protected]";
constexpr char kPassword[] = "m1sst1ped>_<";
constexpr char kConfig[] = "[libdefaults]";

}  // namespace

class SmbKerberosCredentialsUpdaterTest : public testing::Test {
 public:
  SmbKerberosCredentialsUpdaterTest()
      : scoped_user_manager_(std::make_unique<FakeChromeUserManager>()),
        local_state_(TestingBrowserProcess::GetGlobal()) {
    // Enable Kerberos via policy.
    SetPref(prefs::kKerberosEnabled, base::Value(true));

    auto* user_manager =
        static_cast<FakeChromeUserManager*>(user_manager::UserManager::Get());
    user_manager->AddUser(AccountId::FromUserEmail(kProfileEmail));

    // Initialize User, Profile and KerberosCredentialsManager.
    KerberosClient::InitializeFake();
    TestingProfile::Builder profile_builder;
    profile_builder.SetProfileName(kProfileEmail);
    profile_ = profile_builder.Build();
    credentials_manager_ = std::make_unique<KerberosCredentialsManager>(
        local_state_.Get(), profile_.get());
  }

  ~SmbKerberosCredentialsUpdaterTest() override {
    // Remove KerberosCredentialsManager instance, before shutting down
    // KerberosClient.
    credentials_updater_.reset();
    credentials_manager_.reset();
    KerberosClient::Shutdown();
  }

 protected:
  void AddAccountAndAuthenticate(
      const std::string& principal_name,
      KerberosCredentialsManager::ResultCallback callback) {
    credentials_manager_->AddAccountAndAuthenticate(
        principal_name, /*is_managed=*/false, kPassword,
        /*remember_password=*/true, kConfig, /*allow_existing=*/false,
        std::move(callback));
  }

  void SetPref(const char* name, base::Value value) {
    local_state_.Get()->SetManagedPref(
        name, std::make_unique<base::Value>(std::move(value)));
  }

  void CreateCredentialsUpdater(
      SmbKerberosCredentialsUpdater::ActiveAccountChangedCallback callback) {
    credentials_updater_ = std::make_unique<SmbKerberosCredentialsUpdater>(
        credentials_manager_.get(), std::move(callback));
  }

  content::BrowserTaskEnvironment task_environment_;
  user_manager::ScopedUserManager scoped_user_manager_;
  ScopedTestingLocalState local_state_;
  std::unique_ptr<TestingProfile> profile_;
  std::unique_ptr<KerberosCredentialsManager> credentials_manager_;
  std::unique_ptr<SmbKerberosCredentialsUpdater> credentials_updater_;
};

TEST_F(SmbKerberosCredentialsUpdaterTest, FirstAccountAdded) {
  base::RunLoop run_loop;
  base::MockRepeatingCallback<void(const std::string& account_identifier)>
      account_changed_callback;
  base::MockOnceCallback<void(kerberos::ErrorType)> result_callback;

  CreateCredentialsUpdater(account_changed_callback.Get());
  EXPECT_CALL(account_changed_callback, Run(kPrincipal)).Times(1);
  EXPECT_CALL(result_callback, Run(kerberos::ErrorType::ERROR_NONE))
      .WillOnce(RunClosure(run_loop.QuitClosure()));

  AddAccountAndAuthenticate(kPrincipal, result_callback.Get());
  run_loop.Run();

  EXPECT_EQ(credentials_updater_->active_account_name(), kPrincipal);

  // Set the active user to the same one.
  credentials_manager_->SetActiveAccount(kPrincipal);

  EXPECT_EQ(credentials_updater_->active_account_name(), kPrincipal);
}

TEST_F(SmbKerberosCredentialsUpdaterTest, SecondAccountAdded) {
  base::RunLoop run_loop;
  base::RunLoop other_run_loop;
  base::MockRepeatingCallback<void(const std::string& account_identifier)>
      account_changed_callback;
  base::MockOnceCallback<void(kerberos::ErrorType)> result_callback;
  base::MockOnceCallback<void(kerberos::ErrorType)> other_result_callback;

  CreateCredentialsUpdater(account_changed_callback.Get());
  EXPECT_CALL(account_changed_callback, Run(kPrincipal)).Times(2);
  EXPECT_CALL(account_changed_callback, Run(kOtherPrincipal)).Times(1);
  EXPECT_CALL(result_callback, Run(kerberos::ErrorType::ERROR_NONE))
      .WillOnce(RunClosure(run_loop.QuitClosure()));
  EXPECT_CALL(other_result_callback, Run(kerberos::ErrorType::ERROR_NONE))
      .WillOnce(RunClosure(other_run_loop.QuitClosure()));

  AddAccountAndAuthenticate(kPrincipal, result_callback.Get());
  run_loop.Run();

  EXPECT_EQ(credentials_updater_->active_account_name(), kPrincipal);

  AddAccountAndAuthenticate(kOtherPrincipal, other_result_callback.Get());
  other_run_loop.Run();

  EXPECT_EQ(credentials_updater_->active_account_name(), kOtherPrincipal);

  // Change the active user back to the first one.
  credentials_manager_->SetActiveAccount(kPrincipal);

  EXPECT_EQ(credentials_updater_->active_account_name(), kPrincipal);
}

TEST_F(SmbKerberosCredentialsUpdaterTest, KerberosGetsDisabled) {
  base::RunLoop run_loop;
  base::MockRepeatingCallback<void(const std::string& account_identifier)>
      account_changed_callback;
  base::MockOnceCallback<void(kerberos::ErrorType)> result_callback;

  EXPECT_CALL(account_changed_callback, Run("")).Times(1);
  EXPECT_CALL(result_callback, Run(kerberos::ErrorType::ERROR_NONE))
      .WillOnce(RunClosure(run_loop.QuitClosure()));

  // Starting with one account added.
  AddAccountAndAuthenticate(kPrincipal, result_callback.Get());
  run_loop.Run();

  CreateCredentialsUpdater(account_changed_callback.Get());

  EXPECT_EQ(credentials_updater_->active_account_name(), kPrincipal);

  // Disable Kerberos via policy.
  SetPref(prefs::kKerberosEnabled, base::Value(false));

  EXPECT_TRUE(credentials_updater_->active_account_name().empty());
}

TEST_F(SmbKerberosCredentialsUpdaterTest, KerberosGetsEnabled) {
  base::RunLoop run_loop;
  base::MockRepeatingCallback<void(const std::string& account_identifier)>
      account_changed_callback;
  base::MockOnceCallback<void(kerberos::ErrorType)> result_callback;

  EXPECT_CALL(account_changed_callback, Run(kPrincipal)).Times(1);
  EXPECT_CALL(result_callback, Run(kerberos::ErrorType::ERROR_NONE))
      .WillOnce(RunClosure(run_loop.QuitClosure()));

  // Starting with Kerberos disabled via policy.
  SetPref(prefs::kKerberosEnabled, base::Value(false));

  CreateCredentialsUpdater(account_changed_callback.Get());

  EXPECT_TRUE(credentials_updater_->active_account_name().empty());

  // Enable Kerberos via policy, then add an account.
  SetPref(prefs::kKerberosEnabled, base::Value(true));
  AddAccountAndAuthenticate(kPrincipal, result_callback.Get());
  run_loop.Run();

  EXPECT_EQ(credentials_updater_->active_account_name(), kPrincipal);
}

}  // namespace ash::smb_client