chromium/chrome/credential_provider/test/test_credential.h

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

#ifndef CHROME_CREDENTIAL_PROVIDER_TEST_TEST_CREDENTIAL_H_
#define CHROME_CREDENTIAL_PROVIDER_TEST_TEST_CREDENTIAL_H_

#include <credentialprovider.h>

#include <memory>
#include <string>

#include "base/command_line.h"
#include "base/strings/string_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/win/atl.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_base.h"
#include "chrome/credential_provider/test/gls_runner_test_base.h"

namespace credential_provider {

namespace testing {

class DECLSPEC_UUID("3710aa3a-13c7-44c2-bc38-09ba137804d8") ITestCredential
    : public IUnknown {
 public:
  virtual HRESULT STDMETHODCALLTYPE
  SetDefaultExitCode(UiExitCodes default_exit_code) = 0;
  virtual HRESULT STDMETHODCALLTYPE
  SetGlsEmailAddress(const std::string& email) = 0;
  virtual HRESULT STDMETHODCALLTYPE
  SetGlsGaiaPassword(const std::string& gaia_password) = 0;
  virtual HRESULT STDMETHODCALLTYPE
  SetGaiaIdOverride(const std::string& gaia_id,
                    bool ignore_expected_gaia_id) = 0;
  virtual HRESULT STDMETHODCALLTYPE
  SetGaiaFullNameOverride(const std::string& full_name) = 0;
  virtual HRESULT STDMETHODCALLTYPE WaitForGls() = 0;
  virtual HRESULT STDMETHODCALLTYPE
  SetStartGlsEventName(const std::wstring& event_name) = 0;
  virtual HRESULT STDMETHODCALLTYPE FailLoadingGaiaLogonStub() = 0;
  virtual HRESULT STDMETHODCALLTYPE
  UseRealGlsBaseCommandLine(bool use_real_gls_base_command_line) = 0;
  virtual BSTR STDMETHODCALLTYPE GetFinalUsername() = 0;
  virtual std::string STDMETHODCALLTYPE GetFinalEmail() = 0;
  virtual bool STDMETHODCALLTYPE IsAuthenticationResultsEmpty() = 0;
  virtual BSTR STDMETHODCALLTYPE GetErrorText() = 0;
  virtual bool STDMETHODCALLTYPE AreCredentialsValid() = 0;
  virtual bool STDMETHODCALLTYPE CanAttemptWindowsLogon() = 0;
  virtual bool STDMETHODCALLTYPE IsWindowsPasswordValidForStoredUser() = 0;
  virtual bool STDMETHODCALLTYPE IsGlsRunning() = 0;
  virtual bool STDMETHODCALLTYPE IsAdJoinedUser() = 0;
  virtual bool STDMETHODCALLTYPE ContainsIsAdJoinedUser() = 0;
  virtual base::CommandLine STDMETHODCALLTYPE GetTestGlsCommandline() = 0;
  virtual std::string STDMETHODCALLTYPE GetShowTosFromCmdLine() = 0;
};

// Test implementation of an ICredentialProviderCredential backed by a Gaia
// account.  This class overrides some methods for testing purposes.
// The template parameter T specifies which class that implements
// CGaiaCredentialBase should be the base for this test class.
// A CGaiaCredentialBase is required to call base class functions in the
// following ITestCredential implementations:
// GetFinalUsername, GetFinalEmail, AreCredentialsValid,
// CanAttemptWindowsLogon, IsWindowsPasswordValidForStoredUser,
// SetWindowsPassword.
// Also the following IGaiaCredential/CGaiaCredentialBase functions need to be
// overridden: OnUserAuthenticated, ReportError, GetBaseGlsCommandline,
// DisplayErrorInUI, ForkGaiaLogonStub, ResetInternalState.
template <class T>
class ATL_NO_VTABLE CTestCredentialBase : public T, public ITestCredential {
 public:
  CTestCredentialBase();
  ~CTestCredentialBase();

  // ITestCredential.
  IFACEMETHODIMP SetDefaultExitCode(UiExitCodes default_exit_code) override;
  IFACEMETHODIMP SetGlsEmailAddress(const std::string& email) override;
  IFACEMETHODIMP SetGlsGaiaPassword(const std::string& gaia_password) override;
  IFACEMETHODIMP SetGaiaIdOverride(const std::string& gaia_id,
                                   bool ignore_expected_gaia_id) override;
  IFACEMETHODIMP SetGaiaFullNameOverride(const std::string& full_name) override;
  IFACEMETHODIMP FailLoadingGaiaLogonStub() override;
  IFACEMETHODIMP WaitForGls() override;
  IFACEMETHODIMP SetStartGlsEventName(const std::wstring& event_name) override;
  IFACEMETHODIMP UseRealGlsBaseCommandLine(
      bool use_real_gls_base_command_line) override;
  BSTR STDMETHODCALLTYPE GetFinalUsername() override;
  std::string STDMETHODCALLTYPE GetFinalEmail() override;
  bool STDMETHODCALLTYPE IsAuthenticationResultsEmpty() override;
  BSTR STDMETHODCALLTYPE GetErrorText() override;
  bool STDMETHODCALLTYPE AreCredentialsValid() override;
  bool STDMETHODCALLTYPE CanAttemptWindowsLogon() override;
  bool STDMETHODCALLTYPE IsWindowsPasswordValidForStoredUser() override;
  bool STDMETHODCALLTYPE IsGlsRunning() override;
  bool STDMETHODCALLTYPE IsAdJoinedUser() override;
  bool STDMETHODCALLTYPE ContainsIsAdJoinedUser() override;
  base::CommandLine STDMETHODCALLTYPE GetTestGlsCommandline() override;
  std::string STDMETHODCALLTYPE GetShowTosFromCmdLine() override;

  void SignalGlsCompletion();

  // IGaiaCredential.
  IFACEMETHODIMP OnUserAuthenticated(BSTR authentication_info,
                                     BSTR* status_text) override;
  IFACEMETHODIMP ReportError(LONG status,
                             LONG substatus,
                             BSTR status_text) override;
  // CGaiaCredentialBase.

  // Override to catch completion of the GLS process on failure and also log
  // the error message.
  void DisplayErrorInUI(LONG status, LONG substatus, BSTR status_text) override;

  // Overrides to build a dummy command line for testing.
  HRESULT GetBaseGlsCommandline(base::CommandLine* command_line) override;

  // Overrides to check correct startup of GLS process.
  HRESULT ForkGaiaLogonStub(
      OSProcessManager* process_manager,
      const base::CommandLine& command_line,
      CGaiaCredentialBase::UIProcessInfo* uiprocinfo) override;

  // Overrides to directly save to a fake scoped user profile.
  HRESULT ForkPerformPostSigninActionsStub(const base::Value::Dict& dict,
                                           BSTR* status_text) override;

  UiExitCodes default_exit_code_ = kUiecSuccess;
  std::string gls_email_;
  std::string gaia_password_;
  std::string gaia_id_override_;
  std::string full_name_override_;
  base::WaitableEvent gls_done_;
  base::win::ScopedHandle process_continue_event_;
  std::wstring start_gls_event_name_;
  CComBSTR error_text_;
  bool gls_process_started_ = false;
  bool ignore_expected_gaia_id_ = false;
  bool fail_loading_gaia_logon_stub_ = false;
  std::string show_tos_command_line_;
  bool use_real_gls_base_command_line_ = false;
};

template <class T>
CTestCredentialBase<T>::CTestCredentialBase()
    : gls_email_(kDefaultEmail),
      gls_done_(base::WaitableEvent::ResetPolicy::MANUAL,
                base::WaitableEvent::InitialState::NOT_SIGNALED) {}

template <class T>
CTestCredentialBase<T>::~CTestCredentialBase() {}

template <class T>
HRESULT CTestCredentialBase<T>::SetDefaultExitCode(
    UiExitCodes default_exit_code) {
  default_exit_code_ = default_exit_code;
  return S_OK;
}

template <class T>
HRESULT CTestCredentialBase<T>::FailLoadingGaiaLogonStub() {
  fail_loading_gaia_logon_stub_ = true;
  return S_OK;
}

template <class T>
HRESULT CTestCredentialBase<T>::SetGlsEmailAddress(const std::string& email) {
  gls_email_ = email;
  return S_OK;
}

template <class T>
HRESULT CTestCredentialBase<T>::SetGlsGaiaPassword(
    const std::string& gaia_password) {
  gaia_password_ = gaia_password;
  return S_OK;
}

template <class T>
HRESULT CTestCredentialBase<T>::SetGaiaIdOverride(
    const std::string& gaia_id,
    bool ignore_expected_gaia_id) {
  ignore_expected_gaia_id_ = ignore_expected_gaia_id;
  gaia_id_override_ = gaia_id;
  return S_OK;
}

template <class T>
HRESULT CTestCredentialBase<T>::SetGaiaFullNameOverride(
    const std::string& full_name) {
  full_name_override_ = full_name;
  return S_OK;
}

template <class T>
HRESULT CTestCredentialBase<T>::WaitForGls() {
  return !gls_process_started_ || gls_done_.TimedWait(base::Seconds(30))
             ? S_OK
             : HRESULT_FROM_WIN32(WAIT_TIMEOUT);
}

template <class T>
HRESULT CTestCredentialBase<T>::SetStartGlsEventName(
    const std::wstring& event_name) {
  if (!start_gls_event_name_.empty())
    return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  start_gls_event_name_ = event_name;
  return S_OK;
}

template <class T>
BSTR CTestCredentialBase<T>::GetFinalUsername() {
  return this->get_username();
}

template <class T>
bool CTestCredentialBase<T>::IsAuthenticationResultsEmpty() {
  const auto& results = this->get_authentication_results();

  return !results || results->empty();
}

template <class T>
std::string CTestCredentialBase<T>::GetFinalEmail() {
  const auto& results = this->get_authentication_results();

  if (!results)
    return std::string();

  const std::string* email_value = results->FindString(kKeyEmail);

  if (!email_value)
    return std::string();
  return *email_value;
}

template <class T>
bool CTestCredentialBase<T>::IsAdJoinedUser() {
  const auto& results = this->get_authentication_results();

  if (!results)
    return false;

  const std::string* is_ad_joined_user =
      results->FindString(kKeyIsAdJoinedUser);

  if (!is_ad_joined_user)
    return false;
  return base::CompareCaseInsensitiveASCII(*is_ad_joined_user, "true") == 0;
}

template <class T>
bool CTestCredentialBase<T>::ContainsIsAdJoinedUser() {
  const auto& results = this->get_authentication_results();

  if (!results)
    return false;

  const std::string* is_ad_joined_user =
      results->FindString(kKeyIsAdJoinedUser);

  if (!is_ad_joined_user)
    return false;
  return true;
}

template <class T>
std::string CTestCredentialBase<T>::GetShowTosFromCmdLine() {
  return show_tos_command_line_;
}

template <class T>
HRESULT CTestCredentialBase<T>::UseRealGlsBaseCommandLine(
    bool use_real_gls_base_command_line) {
  use_real_gls_base_command_line_ = use_real_gls_base_command_line;
  return S_OK;
}

template <class T>
base::CommandLine CTestCredentialBase<T>::GetTestGlsCommandline() {
  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
  T::GetGlsCommandline(&command_line);
  return command_line;
}

template <class T>
BSTR CTestCredentialBase<T>::GetErrorText() {
  return error_text_;
}

template <class T>
bool CTestCredentialBase<T>::AreCredentialsValid() {
  return T::AreCredentialsValid();
}

template <class T>
bool CTestCredentialBase<T>::CanAttemptWindowsLogon() {
  return T::CanAttemptWindowsLogon();
}

template <class T>
bool CTestCredentialBase<T>::IsWindowsPasswordValidForStoredUser() {
  return T::IsWindowsPasswordValidForStoredUser(
             this->get_current_windows_password()) == S_OK;
}

template <class T>
bool CTestCredentialBase<T>::IsGlsRunning() {
  return this->IsGaiaLogonStubRunning();
}

template <class T>
void CTestCredentialBase<T>::SignalGlsCompletion() {
  gls_done_.Signal();
}

template <class T>
HRESULT CTestCredentialBase<T>::GetBaseGlsCommandline(
    base::CommandLine* command_line) {
  if (use_real_gls_base_command_line_)
    return T::GetBaseGlsCommandline(command_line);

  return GlsRunnerTestBase::GetFakeGlsCommandline(
      default_exit_code_, gls_email_, gaia_id_override_, gaia_password_,
      full_name_override_, start_gls_event_name_, ignore_expected_gaia_id_,
      command_line);
}

template <class T>
HRESULT CTestCredentialBase<T>::ForkGaiaLogonStub(
    OSProcessManager* process_manager,
    const base::CommandLine& command_line,
    CGaiaCredentialBase::UIProcessInfo* uiprocinfo) {
  // Record command_line parameter "show_tos" into global variable.
  std::string gcpw_path =
      command_line.GetSwitchValueASCII(kGcpwEndpointPathSwitch);
  show_tos_command_line_ =
      (gcpw_path.find("show_tos=1") != std::string::npos) ? "1" : "0";

  if (fail_loading_gaia_logon_stub_)
    return E_FAIL;

  HRESULT hr = T::ForkGaiaLogonStub(process_manager, command_line, uiprocinfo);

  if (SUCCEEDED(hr)) {
    gls_process_started_ = true;
    // Reset the manual event since GLS has started.
    gls_done_.Reset();
  }

  return hr;
}

template <class T>
HRESULT CTestCredentialBase<T>::ForkPerformPostSigninActionsStub(
    const base::Value::Dict& dict,
    BSTR* status_text) {
  return CGaiaCredentialBase::PerformPostSigninActions(
      dict, /* com_initialized */ true);
}

template <class T>
HRESULT CTestCredentialBase<T>::OnUserAuthenticated(BSTR authentication_info,
                                                    BSTR* status_text) {
  HRESULT hr = T::OnUserAuthenticated(authentication_info, status_text);
  // Only signal completion if OnUserAuthenticated succeeded otherwise
  // there will be a call to ReportError right after which should signal
  // the completion. This is needed to prevent a race condition in tests
  // where it checks for a failure message, but the failure message is
  // set after gls_done_ is signalled causing the test to be flaky.
  if (SUCCEEDED(hr))
    SignalGlsCompletion();
  return hr;
}

template <class T>
HRESULT CTestCredentialBase<T>::ReportError(LONG status,
                                            LONG substatus,
                                            BSTR status_text) {
  // This function is called instead of (or after) OnUserAuthenticated() when
  // errors occur, so signal that GLS is done.
  HRESULT hr = T::ReportError(status, substatus, status_text);
  SignalGlsCompletion();
  return hr;
}

template <class T>
void CTestCredentialBase<T>::DisplayErrorInUI(LONG status,
                                              LONG substatus,
                                              BSTR status_text) {
  error_text_ = status_text;
  T::DisplayErrorInUI(status, substatus, status_text);
}

// This class is used to implement a test credential based off a fully
// implemented CGaiaCredentialBase class that does not expose
// ICredentialProviderCredential2.
template <class T>
class ATL_NO_VTABLE CTestCredentialForBaseInherited
    : public CTestCredentialBase<T> {
 public:
  DECLARE_NO_REGISTRY()

  CTestCredentialForBaseInherited();
  ~CTestCredentialForBaseInherited();

 private:
  BEGIN_COM_MAP(CTestCredentialForBaseInherited)
  COM_INTERFACE_ENTRY(IGaiaCredential)
  COM_INTERFACE_ENTRY(ICredentialProviderCredential)
  COM_INTERFACE_ENTRY(ITestCredential)
  END_COM_MAP()
};

template <class T>
CTestCredentialForBaseInherited<T>::CTestCredentialForBaseInherited() = default;

template <class T>
CTestCredentialForBaseInherited<T>::~CTestCredentialForBaseInherited() =
    default;

// This class is used to implement a test credential based off a fully
// implemented CGaiaCredentialBase class. The additional InterfaceT parameter
// is used to specify any additional interfaces that should be registerd for
// this class that is not part of CGaiaCredentialBase (this is used to
// implement a test credential for CReauthCredential which implements the
// additional IReauthCredential interface)
template <class T, class InterfaceT>
class ATL_NO_VTABLE CTestCredentialForInherited
    : public CTestCredentialBase<T> {
 public:
  DECLARE_NO_REGISTRY()

  CTestCredentialForInherited();
  ~CTestCredentialForInherited();

 private:
  BEGIN_COM_MAP(CTestCredentialForInherited)
  COM_INTERFACE_ENTRY(IGaiaCredential)
  COM_INTERFACE_ENTRY(ICredentialProviderCredential)
  COM_INTERFACE_ENTRY(ICredentialProviderCredential2)
  COM_INTERFACE_ENTRY(InterfaceT)
  COM_INTERFACE_ENTRY(ITestCredential)
  END_COM_MAP()
};

template <class T, class InterfaceT>
CTestCredentialForInherited<T, InterfaceT>::CTestCredentialForInherited() =
    default;

template <class T, class InterfaceT>
CTestCredentialForInherited<T, InterfaceT>::~CTestCredentialForInherited() =
    default;

}  // namespace testing
}  // namespace credential_provider

#endif  // CHROME_CREDENTIAL_PROVIDER_TEST_TEST_CREDENTIAL_H_