Commit 490ed7f4 authored by Yusuf Sengul's avatar Yusuf Sengul Committed by Commit Bot

Use installer data argument for installing GCPW from MSI

Bug: 1152690
Change-Id: I1cecd128f89a04eee61076b87d54b3f55e98dde9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2559804Reviewed-by: default avatarRobin Lewis <wrlewis@google.com>
Commit-Queue: Yusuf Sengul <yusufsn@google.com>
Cr-Commit-Position: refs/heads/master@{#831186}
parent 2de81f1c
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <string> #include <string>
#include "base/files/file_util.h"
#include "base/json/json_string_value_serializer.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h" #include "base/win/registry.h"
#include "chrome/common/chrome_version.h" #include "chrome/common/chrome_version.h"
...@@ -29,11 +31,42 @@ const char kUninstall[] = "uninstall"; ...@@ -29,11 +31,42 @@ const char kUninstall[] = "uninstall";
const char kEnableStats[] = "enable-stats"; const char kEnableStats[] = "enable-stats";
const char kDisableStats[] = "disable-stats"; const char kDisableStats[] = "disable-stats";
const char kInstallerData[] = "installerdata";
const char kStandaloneInstall[] = "standalone"; const char kStandaloneInstall[] = "standalone";
} // namespace switches } // namespace switches
namespace {
// Path to the msi json value inside the dictionary which is parsed from
// installer data argument in the command line.
const char kMsiJsonPath[] = "distribution.msi";
// The registry name which is saved to indicate installation source.
const wchar_t kMsiInstall[] = L"msi";
// Parses the json data and returns it as a dictionary. If the json data isn't
// valid, returns nullptr.
base::DictionaryValue* ParseDistributionPreferences(
const std::string& json_data) {
JSONStringValueDeserializer json(json_data);
std::string error;
std::unique_ptr<base::Value> root(json.Deserialize(nullptr, &error));
if (!root.get()) {
LOGFN(WARNING) << "Failed to parse initial prefs file: " << error;
return nullptr;
}
if (!root->is_dict()) {
LOGFN(WARNING) << "Failed to parse installer data file";
return nullptr;
}
return static_cast<base::DictionaryValue*>(root.release());
}
} // namespace
StandaloneInstallerConfigurator::StandaloneInstallerConfigurator() StandaloneInstallerConfigurator::StandaloneInstallerConfigurator()
: is_standalone_installation_(false) {} : is_msi_installation_(false) {}
StandaloneInstallerConfigurator::~StandaloneInstallerConfigurator() {} StandaloneInstallerConfigurator::~StandaloneInstallerConfigurator() {}
...@@ -51,21 +84,43 @@ StandaloneInstallerConfigurator* StandaloneInstallerConfigurator::Get() { ...@@ -51,21 +84,43 @@ StandaloneInstallerConfigurator* StandaloneInstallerConfigurator::Get() {
return *GetInstanceStorage(); return *GetInstanceStorage();
} }
// Sets the installer source for GCPW. When installed through standalone // Sets the installer source for GCPW. When installed through MSI,
// installer, |kStandaloneInstall| switch is present in the commandline // contains installer data file name as argument.
// arguments.
void StandaloneInstallerConfigurator::ConfigureInstallationType( void StandaloneInstallerConfigurator::ConfigureInstallationType(
const base::CommandLine& cmdline) { const base::CommandLine& cmdline) {
base::string16 standalone_install16 = // There are following scenarios for installations:
base::UTF8ToUTF16(switches::kStandaloneInstall); // First time install from MSI
// First time install from EXE
// MSIs before this kMsiInstall registry gets auto-updated
// MSIs with kMsiInstall registry gets auto-updated
// EXEs with kMsiInstall registry gets auto-updated
// |kStandaloneInstall| indicates fresh installation.
if (cmdline.HasSwitch(switches::kStandaloneInstall)) { if (cmdline.HasSwitch(switches::kStandaloneInstall)) {
is_standalone_installation_ = true; base::Value* is_msi = nullptr;
HRESULT hr = SetUpdaterClientsAppPathFlag(standalone_install16, 1); if (cmdline.HasSwitch(switches::kInstallerData)) {
base::FilePath prefs_path(
cmdline.GetSwitchValuePath(switches::kInstallerData));
if (InitializeFromInstallerData(prefs_path))
is_msi = installer_data_dictionary_->FindPath(kMsiJsonPath);
}
is_msi_installation_ = false;
if (is_msi && is_msi->is_bool() && is_msi->GetBool()) {
is_msi_installation_ = true;
}
} else {
// Honor the registry if it is found, otherwise fall back to MSI
// installation.
is_msi_installation_ =
GetUpdaterClientsAppPathFlagOrDefault(kMsiInstall, 1);
}
HRESULT hr =
SetUpdaterClientsAppPathFlag(kMsiInstall, is_msi_installation_ ? 1 : 0);
if (FAILED(hr)) if (FAILED(hr))
LOGFN(ERROR) << "SetGlobalFlag failed" << putHR(hr); LOGFN(ERROR) << "SetGlobalFlag failed" << putHR(hr);
} else if (GetUpdaterClientsAppPathFlagOrDefault(standalone_install16, 0)) {
is_standalone_installation_ = true;
}
} }
base::string16 StandaloneInstallerConfigurator::GetCurrentDate() { base::string16 StandaloneInstallerConfigurator::GetCurrentDate() {
...@@ -84,14 +139,14 @@ base::string16 StandaloneInstallerConfigurator::GetCurrentDate() { ...@@ -84,14 +139,14 @@ base::string16 StandaloneInstallerConfigurator::GetCurrentDate() {
} }
bool StandaloneInstallerConfigurator::IsStandaloneInstallation() const { bool StandaloneInstallerConfigurator::IsStandaloneInstallation() const {
return is_standalone_installation_; return !is_msi_installation_;
} }
HRESULT StandaloneInstallerConfigurator::AddUninstallKey( HRESULT StandaloneInstallerConfigurator::AddUninstallKey(
const base::FilePath& install_path) { const base::FilePath& install_path) {
LOGFN(VERBOSE); LOGFN(VERBOSE);
if (!is_standalone_installation_) if (is_msi_installation_)
return S_OK; return S_OK;
std::wstring uninstall_reg = kRegUninstall; std::wstring uninstall_reg = kRegUninstall;
...@@ -242,4 +297,28 @@ HRESULT StandaloneInstallerConfigurator::RemoveUninstallKey() { ...@@ -242,4 +297,28 @@ HRESULT StandaloneInstallerConfigurator::RemoveUninstallKey() {
return S_OK; return S_OK;
} }
bool StandaloneInstallerConfigurator::InitializeFromInstallerData(
base::FilePath prefs_path) {
std::string json_data;
if (base::PathExists(prefs_path) &&
!base::ReadFileToString(prefs_path, &json_data)) {
LOGFN(ERROR) << "Failed to read preferences from " << prefs_path.value();
return false;
}
if (json_data.empty()) {
LOGFN(WARNING) << "Installer data is empty!";
return false;
}
installer_data_dictionary_.reset(ParseDistributionPreferences(json_data));
if (!installer_data_dictionary_) {
LOGFN(WARNING) << "Installer data is empty!";
return false;
}
return true;
}
} // namespace credential_provider } // namespace credential_provider
...@@ -11,6 +11,11 @@ ...@@ -11,6 +11,11 @@
#include "base/files/file.h" #include "base/files/file.h"
#include "base/win/windows_types.h" #include "base/win/windows_types.h"
namespace base {
class DictionaryValue;
class FilePath;
} // namespace base
namespace credential_provider { namespace credential_provider {
namespace switches { namespace switches {
...@@ -36,8 +41,12 @@ extern const char kUninstall[]; ...@@ -36,8 +41,12 @@ extern const char kUninstall[];
extern const char kEnableStats[]; extern const char kEnableStats[];
extern const char kDisableStats[]; extern const char kDisableStats[];
// Switch that indicates the fresh installation.
extern const char kStandaloneInstall[]; extern const char kStandaloneInstall[];
// Dynamic install parameter switch which is only set for MSIs.
extern const char kInstallerData[];
} // namespace switches } // namespace switches
class StandaloneInstallerConfigurator { class StandaloneInstallerConfigurator {
...@@ -63,10 +72,17 @@ class StandaloneInstallerConfigurator { ...@@ -63,10 +72,17 @@ class StandaloneInstallerConfigurator {
base::string16 GetCurrentDate(); base::string16 GetCurrentDate();
bool is_standalone_installation_; // Parse the provided installer data argument and load into
}; // |installer_data_dictionary_|.
bool InitializeFromInstallerData(base::FilePath prefs_path);
bool IsStandaloneInstallation(const base::CommandLine& command_line); // Indicates that GCPW installation source is MSI.
bool is_msi_installation_;
// Dictionary is parsed from the installer data argument which is set only for
// MSIs.
std::unique_ptr<base::DictionaryValue> installer_data_dictionary_;
};
} // namespace credential_provider } // namespace credential_provider
......
...@@ -100,6 +100,15 @@ class GcpSetupTest : public ::testing::Test { ...@@ -100,6 +100,15 @@ class GcpSetupTest : public ::testing::Test {
return uninstall_reg; return uninstall_reg;
} }
base::FilePath CreateFilePath(const std::string& file_name) {
return temp_dir_.GetPath().AppendASCII(file_name.c_str());
}
void CreateJsonFile(const base::FilePath& path, const std::string& data) {
ASSERT_EQ(static_cast<int>(data.size()),
base::WriteFile(path, data.data(), data.size()));
}
void assert_addremove_reg_exists() { void assert_addremove_reg_exists() {
base::win::RegKey uninstall_key; base::win::RegKey uninstall_key;
ASSERT_EQ(ERROR_SUCCESS, ASSERT_EQ(ERROR_SUCCESS,
...@@ -166,9 +175,9 @@ class GcpSetupTest : public ::testing::Test { ...@@ -166,9 +175,9 @@ class GcpSetupTest : public ::testing::Test {
ASSERT_EQ(version_minor, static_cast<DWORD>(version_components[3])); ASSERT_EQ(version_minor, static_cast<DWORD>(version_components[3]));
} }
private:
void SetUp() override; void SetUp() override;
private:
base::string16 GetCurrentDateForTesting() { base::string16 GetCurrentDateForTesting() {
static const wchar_t kDateFormat[] = L"yyyyMMdd"; static const wchar_t kDateFormat[] = L"yyyyMMdd";
wchar_t date_str[base::size(kDateFormat)] = {0}; wchar_t date_str[base::size(kDateFormat)] = {0};
...@@ -192,6 +201,7 @@ class GcpSetupTest : public ::testing::Test { ...@@ -192,6 +201,7 @@ class GcpSetupTest : public ::testing::Test {
base::ScopedTempDir scoped_temp_prog_dir_; base::ScopedTempDir scoped_temp_prog_dir_;
base::ScopedTempDir scoped_temp_start_menu_dir_; base::ScopedTempDir scoped_temp_start_menu_dir_;
base::ScopedTempDir scoped_temp_progdata_dir_; base::ScopedTempDir scoped_temp_progdata_dir_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<base::ScopedPathOverride> program_files_override_; std::unique_ptr<base::ScopedPathOverride> program_files_override_;
std::unique_ptr<base::ScopedPathOverride> start_menu_override_; std::unique_ptr<base::ScopedPathOverride> start_menu_override_;
std::unique_ptr<base::ScopedPathOverride> programdata_override_; std::unique_ptr<base::ScopedPathOverride> programdata_override_;
...@@ -344,6 +354,8 @@ void GcpSetupTest::SetUp() { ...@@ -344,6 +354,8 @@ void GcpSetupTest::SetUp() {
programdata_override_.reset(new base::ScopedPathOverride( programdata_override_.reset(new base::ScopedPathOverride(
base::DIR_COMMON_APP_DATA, scoped_temp_progdata_dir_.GetPath())); base::DIR_COMMON_APP_DATA, scoped_temp_progdata_dir_.GetPath()));
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
// In non-component builds, base::FILE_MODULE will always return the path // In non-component builds, base::FILE_MODULE will always return the path
// to base.dll because of the way CURRENT_MODULE works. Therefore overriding // to base.dll because of the way CURRENT_MODULE works. Therefore overriding
// to point to gaia1_0.dll's destination path (i.e. after it is installed). // to point to gaia1_0.dll's destination path (i.e. after it is installed).
...@@ -407,28 +419,37 @@ TEST_F(GcpSetupTest, DoInstallWithExtension) { ...@@ -407,28 +419,37 @@ TEST_F(GcpSetupTest, DoInstallWithExtension) {
} }
// Tests install over old install for different types of installations. // Tests install over old install for different types of installations.
// 0 - Indicates that initial installation isn't standalone. // 0 - Indicates that initial installation is standalone.
// 1 - Indicates that the initial installation is through a standalone // 1 - Indicates that the initial installation is through MSI.
// installer.
class GcpInstallOverOldInstallTest : public GcpSetupTest, class GcpInstallOverOldInstallTest : public GcpSetupTest,
public ::testing::WithParamInterface<int> { public ::testing::WithParamInterface<int> {
public: public:
void SetInstallerConfig(bool append_standalone_flag) { void SetInstallerConfig(bool add_installer_data) {
if (append_standalone_flag) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM); base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
// Indicates fresh installation.
command_line.AppendSwitch(switches::kStandaloneInstall); command_line.AppendSwitch(switches::kStandaloneInstall);
if (add_installer_data) {
// Only set if installation source is MSI.
std::string installer_json = "{\"distribution\": {\"msi\": true}}";
base::FilePath installer_data_path = CreateFilePath("myfile.txt");
CreateJsonFile(installer_data_path, installer_json);
command_line.AppendSwitchPath(switches::kInstallerData,
installer_data_path);
}
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType( StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line); command_line);
} }
}
}; };
TEST_P(GcpInstallOverOldInstallTest, DoInstallOverOldInstall) { TEST_P(GcpInstallOverOldInstallTest, DoInstallOverOldInstall) {
logging::ResetEventSourceForTesting(); logging::ResetEventSourceForTesting();
// Set installer config as if --standalone flag was provided as indicated by // Set installer data argument to indicate the installation source.
// test parameter.
SetInstallerConfig(GetParam()); SetInstallerConfig(GetParam());
// Install using some old version. // Install using some old version.
...@@ -449,12 +470,16 @@ TEST_P(GcpInstallOverOldInstallTest, DoInstallOverOldInstall) { ...@@ -449,12 +470,16 @@ TEST_P(GcpInstallOverOldInstallTest, DoInstallOverOldInstall) {
logging::ResetEventSourceForTesting(); logging::ResetEventSourceForTesting();
// Don't include standalone flag as it should have already been indicated in // Don't include any flag to indicate the installation source as
// the registry the first time this is called. // the registry was set the first time this is called.
SetInstallerConfig(false); base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line);
EXPECT_TRUE( EXPECT_TRUE(
StandaloneInstallerConfigurator::Get()->IsStandaloneInstallation() == StandaloneInstallerConfigurator::Get()->IsStandaloneInstallation() ==
GetParam()); !GetParam());
// Now install a newer version. // Now install a newer version.
ASSERT_EQ(S_OK, ASSERT_EQ(S_OK,
...@@ -559,6 +584,9 @@ TEST_F(GcpSetupTest, LaunchGcpAfterInstall) { ...@@ -559,6 +584,9 @@ TEST_F(GcpSetupTest, LaunchGcpAfterInstall) {
ExpectAllFilesToExist(false, old_version); ExpectAllFilesToExist(false, old_version);
} }
// Tests installations from exe and MSI.
// 0 - installation is not standalone.
// 1 - installation is standalone.
class GcpInstallerTest : public GcpSetupTest, class GcpInstallerTest : public GcpSetupTest,
public ::testing::WithParamInterface<int> {}; public ::testing::WithParamInterface<int> {};
...@@ -567,13 +595,21 @@ TEST_P(GcpInstallerTest, DoUninstall) { ...@@ -567,13 +595,21 @@ TEST_P(GcpInstallerTest, DoUninstall) {
logging::ResetEventSourceForTesting(); logging::ResetEventSourceForTesting();
if (standalone_installer) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM); base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
command_line.AppendSwitch(switches::kStandaloneInstall); command_line.AppendSwitch(switches::kStandaloneInstall);
if (!standalone_installer) {
std::string installer_json = "{\"distribution\": {\"msi\": true}}";
base::FilePath installer_data_path = CreateFilePath("myfile.txt");
CreateJsonFile(installer_data_path, installer_json);
command_line.AppendSwitchPath(switches::kInstallerData,
installer_data_path);
}
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType( StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line); command_line);
}
ASSERT_EQ(S_OK, ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing())); DoInstall(module_path(), product_version(), fakes_for_testing()));
...@@ -766,6 +802,8 @@ TEST_F(GcpSetupTest, WriteCredentialProviderRegistryValues) { ...@@ -766,6 +802,8 @@ TEST_F(GcpSetupTest, WriteCredentialProviderRegistryValues) {
KEY_ALL_ACCESS)); KEY_ALL_ACCESS));
base::CommandLine command_line(base::CommandLine::NO_PROGRAM); base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
// Set the standalone registry, but not installer data so that EXE
// installation is indicated.
command_line.AppendSwitch(switches::kStandaloneInstall); command_line.AppendSwitch(switches::kStandaloneInstall);
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType( StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment