From a0a91732d12814d8771b302c5129ab3de6cf328c Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Thu, 17 Nov 2022 14:15:07 -0800 Subject: [PATCH 1/9] Export admin settings --- .../Commands/SettingsCommand.cpp | 32 ++++++++++++++- .../Commands/SettingsCommand.h | 14 +++++++ src/AppInstallerCLICore/Resources.h | 2 + .../Workflows/SettingsFlow.cpp | 40 +++++++++++++++++++ .../Workflows/SettingsFlow.h | 6 +++ .../Shared/Strings/en-us/winget.resw | 6 +++ 6 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp index 3b8e112d2d..ee86e58617 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp @@ -16,6 +16,14 @@ namespace AppInstaller::CLI constexpr Utility::LocIndView s_ArgumentName_Enable = "enable"_liv; constexpr Utility::LocIndView s_ArgumentName_Disable = "disable"_liv; constexpr Utility::LocIndView s_ArgName_EnableAndDisable = "enable|disable"_liv; + static constexpr std::string_view s_SettingsCommand_HelpLink = "https://aka.ms/winget-settings"sv; + } + + std::vector> SettingsCommand::GetCommands() const + { + return InitializeFromMoveOnly>>({ + std::make_unique(FullName()), + }); } std::vector SettingsCommand::GetArguments() const @@ -38,7 +46,7 @@ namespace AppInstaller::CLI std::string SettingsCommand::HelpLink() const { - return "https://aka.ms/winget-settings"; + return std::string{ s_SettingsCommand_HelpLink }; } void SettingsCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const @@ -78,5 +86,27 @@ namespace AppInstaller::CLI { context << Workflow::OpenUserSetting; } + } + + Resource::LocString SettingsExportCommand::ShortDescription() const + { + return { Resource::String::SettingsExportCommandShortDescription }; + } + + Resource::LocString SettingsExportCommand::LongDescription() const + { + return { Resource::String::SettingsExportCommandLongDescription }; + } + + std::string SettingsExportCommand::HelpLink() const + { + return std::string{ s_SettingsCommand_HelpLink }; + } + + void SettingsExportCommand::ExecuteInternal(Execution::Context& context) const + { + context << + Workflow::EnsureRunningAsAdmin << + Workflow::ExportAdminSettings; } } diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.h b/src/AppInstallerCLICore/Commands/SettingsCommand.h index 2bd2aeeb18..112b41105d 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.h +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.h @@ -9,6 +9,7 @@ namespace AppInstaller::CLI { SettingsCommand(std::string_view parent) : Command("settings", { "config" }, parent, Settings::TogglePolicy::Policy::Settings) {} + std::vector> GetCommands() const override; std::vector GetArguments() const override; virtual Resource::LocString ShortDescription() const override; @@ -20,4 +21,17 @@ namespace AppInstaller::CLI void ValidateArgumentsInternal(Execution::Args& execArgs) const override; void ExecuteInternal(Execution::Context& context) const override; }; + + struct SettingsExportCommand final : public Command + { + SettingsExportCommand(std::string_view parent) : Command("export", parent) {} + + Resource::LocString ShortDescription() const override; + Resource::LocString LongDescription() const override; + + std::string HelpLink() const override; + + protected: + void ExecuteInternal(Execution::Context& context) const override; + }; } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 9ac6f3d06d..6be9678487 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -283,6 +283,8 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(SettingLoadFailure); WINGET_DEFINE_RESOURCE_STRINGID(SettingsCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsCommandShortDescription); + WINGET_DEFINE_RESOURCE_STRINGID(SettingsExportCommandLongDescription); + WINGET_DEFINE_RESOURCE_STRINGID(SettingsExportCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningField); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarnings); WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningValue); diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp index bbfa21f628..49eb4f6109 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp @@ -12,6 +12,28 @@ namespace AppInstaller::CLI::Workflow using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; + namespace + { + struct AdminSettings + { + void Add(AdminSetting setting) + { + auto str = std::string{ Settings::AdminSettingToString(setting) }; + root[str] = Settings::IsAdminSettingEnabled(setting); + } + + std::string ToJsonString() const + { + Json::StreamWriterBuilder writerBuilder; + writerBuilder.settings_["indentation"] = ""; + return Json::writeString(writerBuilder, root); + } + + private: + Json::Value root{ Json::ValueType::objectValue }; + }; + } + void EnableAdminSetting(Execution::Context& context) { std::string_view adminSettingString = context.Args.GetArg(Execution::Args::Type::AdminSettingEnable); @@ -99,4 +121,22 @@ namespace AppInstaller::CLI::Workflow ShellExecuteW(nullptr, nullptr, L"notepad", filePathUTF16.c_str(), nullptr, SW_SHOW); } } + + void ExportAdminSettings(Execution::Context& context) + { + AdminSetting settings[] = + { + AdminSetting::LocalManifestFiles, + AdminSetting::BypassCertificatePinningForMicrosoftStore + }; + + AdminSettings adminSettings; + + for (auto& setting : settings) + { + adminSettings.Add(setting); + } + + context.Reporter.Info() << adminSettings.ToJsonString() << std::endl; + } } diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.h b/src/AppInstallerCLICore/Workflows/SettingsFlow.h index 578ff34b98..cb7cfbf118 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.h +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.h @@ -22,4 +22,10 @@ namespace AppInstaller::CLI::Workflow // Inputs: None // Outputs: None void OpenUserSetting(Execution::Context& context); + + // Lists the state of admin settings. + // Required Args: None + // Inputs: None + // Outputs: None + void ExportAdminSettings(Execution::Context& context); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 00ee0ece0c..a428860913 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1483,4 +1483,10 @@ Please specify one of them using the `--source` option to proceed. Cannot disable %1. This setting is controlled by policy. For more information contact your system administrator. {Locked="%1"} The value will be replaced with the feature name + + Export administrator settings as JSON + + + Export administrator settings + \ No newline at end of file From 34d6e3cb5dfe91abc3e644c71bb7dd2c0d68b4e4 Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Thu, 17 Nov 2022 17:34:47 -0800 Subject: [PATCH 2/9] adminsettings --- src/AppInstallerCLICore/Commands/SettingsCommand.cpp | 2 +- src/AppInstallerCLICore/Workflows/SettingsFlow.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp index ee86e58617..19eaaa17a9 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp @@ -107,6 +107,6 @@ namespace AppInstaller::CLI { context << Workflow::EnsureRunningAsAdmin << - Workflow::ExportAdminSettings; + Workflow::ExportAdminSettings; } } diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp index 49eb4f6109..6af6103825 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp @@ -16,10 +16,15 @@ namespace AppInstaller::CLI::Workflow { struct AdminSettings { + AdminSettings() + { + root["adminSettings"] = Json::ValueType::objectValue; + } + void Add(AdminSetting setting) { auto str = std::string{ Settings::AdminSettingToString(setting) }; - root[str] = Settings::IsAdminSettingEnabled(setting); + root["adminSettings"][str] = Settings::IsAdminSettingEnabled(setting); } std::string ToJsonString() const From 1f31a7bc1bdab47c744fcf8f2b394b930e95b408 Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Tue, 22 Nov 2022 15:47:07 -0800 Subject: [PATCH 3/9] UTs --- .../Commands/SettingsCommand.cpp | 66 +++++++++---------- src/AppInstallerCLITests/TestCommon.cpp | 33 ++++++++++ src/AppInstallerCLITests/TestCommon.h | 6 ++ src/AppInstallerCLITests/WorkFlow.cpp | 42 +++++++++++- src/AppInstallerCLITests/pch.h | 2 + .../Public/winget/AdminSettings.h | 1 + 6 files changed, 116 insertions(+), 34 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp index 19eaaa17a9..2995edbd36 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp @@ -19,19 +19,19 @@ namespace AppInstaller::CLI static constexpr std::string_view s_SettingsCommand_HelpLink = "https://aka.ms/winget-settings"sv; } - std::vector> SettingsCommand::GetCommands() const - { - return InitializeFromMoveOnly>>({ - std::make_unique(FullName()), - }); + std::vector> SettingsCommand::GetCommands() const + { + return InitializeFromMoveOnly>>({ + std::make_unique(FullName()), + }); } - std::vector SettingsCommand::GetArguments() const - { - return { - Argument{ s_ArgumentName_Enable, Argument::NoAlias, Execution::Args::Type::AdminSettingEnable, Resource::String::AdminSettingEnableDescription, ArgumentType::Standard, Argument::Visibility::Help }, - Argument{ s_ArgumentName_Disable, Argument::NoAlias, Execution::Args::Type::AdminSettingDisable, Resource::String::AdminSettingDisableDescription, ArgumentType::Standard, Argument::Visibility::Help }, - }; + std::vector SettingsCommand::GetArguments() const + { + return { + Argument{ s_ArgumentName_Enable, Argument::NoAlias, Execution::Args::Type::AdminSettingEnable, Resource::String::AdminSettingEnableDescription, ArgumentType::Standard, Argument::Visibility::Help }, + Argument{ s_ArgumentName_Disable, Argument::NoAlias, Execution::Args::Type::AdminSettingDisable, Resource::String::AdminSettingDisableDescription, ArgumentType::Standard, Argument::Visibility::Help }, + }; } Resource::LocString SettingsCommand::ShortDescription() const @@ -86,27 +86,27 @@ namespace AppInstaller::CLI { context << Workflow::OpenUserSetting; } - } - - Resource::LocString SettingsExportCommand::ShortDescription() const - { - return { Resource::String::SettingsExportCommandShortDescription }; - } - - Resource::LocString SettingsExportCommand::LongDescription() const - { - return { Resource::String::SettingsExportCommandLongDescription }; - } - - std::string SettingsExportCommand::HelpLink() const - { - return std::string{ s_SettingsCommand_HelpLink }; - } - - void SettingsExportCommand::ExecuteInternal(Execution::Context& context) const - { - context << - Workflow::EnsureRunningAsAdmin << - Workflow::ExportAdminSettings; + } + + Resource::LocString SettingsExportCommand::ShortDescription() const + { + return { Resource::String::SettingsExportCommandShortDescription }; + } + + Resource::LocString SettingsExportCommand::LongDescription() const + { + return { Resource::String::SettingsExportCommandLongDescription }; + } + + std::string SettingsExportCommand::HelpLink() const + { + return std::string{ s_SettingsCommand_HelpLink }; + } + + void SettingsExportCommand::ExecuteInternal(Execution::Context& context) const + { + context << + Workflow::EnsureRunningAsAdmin << + Workflow::ExportAdminSettings; } } diff --git a/src/AppInstallerCLITests/TestCommon.cpp b/src/AppInstallerCLITests/TestCommon.cpp index f2520865dc..ff76b22cc0 100644 --- a/src/AppInstallerCLITests/TestCommon.cpp +++ b/src/AppInstallerCLITests/TestCommon.cpp @@ -326,4 +326,37 @@ namespace TestCommon return AppInstaller::Msix::GetPackageReader(stream.Get(), &packageReader) && SUCCEEDED(packageReader->GetManifest(manifestReader)); } + + std::string RemoveConsoleFormat(const std::string& str) + { + // We are looking something in the lines of "\x1b[0mthisisastring" + if (!str.empty() && str[0] == '\x1b') + { + // Find first m + auto pos = str.find("m"); + if (pos != std::string::npos) + { + return str.substr(pos + 1); + } + } + + return str; + } + + Json::Value ConvertToJson(const std::string& content) + { + auto contentClean = RemoveConsoleFormat(content); + + Json::Value root; + Json::CharReaderBuilder builder; + const std::unique_ptr reader(builder.newCharReader()); + std::string error; + + if (!reader->parse(contentClean.c_str(), contentClean.c_str() + contentClean.size(), &root, &error)) + { + throw error; + } + + return root; + } } diff --git a/src/AppInstallerCLITests/TestCommon.h b/src/AppInstallerCLITests/TestCommon.h index 31bb57ad26..77358d4dd3 100644 --- a/src/AppInstallerCLITests/TestCommon.h +++ b/src/AppInstallerCLITests/TestCommon.h @@ -145,4 +145,10 @@ namespace TestCommon // Get manifest reader from a msix file path bool GetMsixPackageManifestReader(std::string_view testFileName, IAppxManifestReader** manifestReader); + + // Removes console format + std::string RemoveConsoleFormat(const std::string& str); + + // Convert to Json::Value + Json::Value ConvertToJson(const std::string& content); } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 9536d2165f..be5fb5973e 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -3724,4 +3724,44 @@ TEST_CASE("InstallFlow_FoundInstalledAndUpgradeNotAvailable", "[UpdateFlow][work REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UpdateNotApplicable).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE); -} \ No newline at end of file +} + +TEST_CASE("Export_Settings", "[Settings][workflow]") +{ + RemoveSetting(Stream::AdminSettings); + + { + // No admin settings, local manifest should be false. + std::ostringstream exportOutput; + TestContext context{ exportOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); + SettingsExportCommand settingsExportCommand({}); + settingsExportCommand.Execute(context); + + auto json = ConvertToJson(exportOutput.str()); + REQUIRE(!json.isNull()); + REQUIRE_FALSE(json["adminSettings"]["LocalManifestFiles"].asBool()); + } + + { + // Enable local manifest and verify export works. + std::ostringstream settingsOutput; + TestContext context{ settingsOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + context.Args.AddArg(Execution::Args::Type::AdminSettingEnable, "LocalManifestFiles"sv); + context.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); + SettingsCommand settings({}); + settings.Execute(context); + + std::ostringstream exportOutput; + TestContext context2{ exportOutput, std::cin }; + auto previousThreadGlobals2 = context2.SetForCurrentThread(); + context2.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); + SettingsExportCommand settingsExportCommand({}); + settingsExportCommand.Execute(context2); + auto json = ConvertToJson(exportOutput.str()); + REQUIRE(!json.isNull()); + REQUIRE(json["adminSettings"]["LocalManifestFiles"].asBool()); + } +} diff --git a/src/AppInstallerCLITests/pch.h b/src/AppInstallerCLITests/pch.h index 0a0be3f741..328fa5c20f 100644 --- a/src/AppInstallerCLITests/pch.h +++ b/src/AppInstallerCLITests/pch.h @@ -12,6 +12,8 @@ #include +#include + #include #include #include diff --git a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h index 67301c722c..83730df877 100644 --- a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h @@ -7,6 +7,7 @@ namespace AppInstaller::Settings { // Enum of admin settings. + // When a new one is added, it should be also added to SettingsCommand::ExportAdminSettings enum class AdminSetting { Unknown, From 9497bf292d519c16a4ca4f2fcee8ea9a1b1d3bfa Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Wed, 7 Dec 2022 18:44:22 -0800 Subject: [PATCH 4/9] Add settings file location to info and settings export --- .../Commands/RootCommand.cpp | 1 + .../Commands/SettingsCommand.cpp | 3 +-- src/AppInstallerCLICore/Resources.h | 1 + .../Workflows/SettingsFlow.cpp | 26 +++++++++---------- .../Workflows/SettingsFlow.h | 4 +-- .../Shared/Strings/en-us/winget.resw | 3 +++ src/AppInstallerCLITests/WorkFlow.cpp | 10 +++++-- .../Public/AppInstallerRuntime.h | 2 ++ .../Public/winget/AdminSettings.h | 4 +-- src/AppInstallerCommonCore/Runtime.cpp | 10 ++++++- 10 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index f07358655e..8e68751f56 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -191,6 +191,7 @@ namespace AppInstaller::CLI }; info << std::endl << Resource::String::Logs << ": "_liv << Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string() << std::endl; + info << std::endl << Resource::String::UserSettings << ": "_liv << Runtime::GetPathTo(Runtime::PathName::UserSettingsFileLocation).u8string() << std::endl; info << std::endl; diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp index 2995edbd36..cb1aa953d0 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp @@ -106,7 +106,6 @@ namespace AppInstaller::CLI void SettingsExportCommand::ExecuteInternal(Execution::Context& context) const { context << - Workflow::EnsureRunningAsAdmin << - Workflow::ExportAdminSettings; + Workflow::ExportSettings; } } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 6be9678487..4283005a77 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -410,6 +410,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionCount); WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionExplanation); WINGET_DEFINE_RESOURCE_STRINGID(Usage); + WINGET_DEFINE_RESOURCE_STRINGID(UserSettings); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandShortDescription); diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp index 6af6103825..2052bb1ffa 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp @@ -14,14 +14,16 @@ namespace AppInstaller::CLI::Workflow namespace { - struct AdminSettings + struct ExportSettingsJson { - AdminSettings() + ExportSettingsJson() { + // TODO: add schema root["adminSettings"] = Json::ValueType::objectValue; + root["userSettingsFile"] = Runtime::GetPathTo(Runtime::PathName::UserSettingsFileLocation).u8string(); } - void Add(AdminSetting setting) + void AddAdminSetting(AdminSetting setting) { auto str = std::string{ Settings::AdminSettingToString(setting) }; root["adminSettings"][str] = Settings::IsAdminSettingEnabled(setting); @@ -127,21 +129,17 @@ namespace AppInstaller::CLI::Workflow } } - void ExportAdminSettings(Execution::Context& context) + void ExportSettings(Execution::Context& context) { - AdminSetting settings[] = - { - AdminSetting::LocalManifestFiles, - AdminSetting::BypassCertificatePinningForMicrosoftStore - }; - - AdminSettings adminSettings; + ExportSettingsJson exportSettingsJson; + using AdminSetting_t = std::underlying_type_t; - for (auto& setting : settings) + // Skip Unknown. + for (AdminSetting_t i = 1 + static_cast(AdminSetting::Unknown); i < static_cast(AdminSetting::Max); ++i) { - adminSettings.Add(setting); + exportSettingsJson.AddAdminSetting(static_cast(i)); } - context.Reporter.Info() << adminSettings.ToJsonString() << std::endl; + context.Reporter.Info() << exportSettingsJson.ToJsonString() << std::endl; } } diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.h b/src/AppInstallerCLICore/Workflows/SettingsFlow.h index cb7cfbf118..e40a0051c9 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.h +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.h @@ -23,9 +23,9 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void OpenUserSetting(Execution::Context& context); - // Lists the state of admin settings. + // Lists the state of settings. // Required Args: None // Inputs: None // Outputs: None - void ExportAdminSettings(Execution::Context& context); + void ExportSettings(Execution::Context& context); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index a428860913..56efd224fa 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1489,4 +1489,7 @@ Please specify one of them using the `--source` option to proceed. Export administrator settings + + User Settings + \ No newline at end of file diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index be5fb5973e..a206d3a66d 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -3735,13 +3735,16 @@ TEST_CASE("Export_Settings", "[Settings][workflow]") std::ostringstream exportOutput; TestContext context{ exportOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); - context.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); SettingsExportCommand settingsExportCommand({}); settingsExportCommand.Execute(context); auto json = ConvertToJson(exportOutput.str()); REQUIRE(!json.isNull()); REQUIRE_FALSE(json["adminSettings"]["LocalManifestFiles"].asBool()); + + auto userSettingsFileValue = std::string(json["userSettingsFile"].asCString()); + REQUIRE(userSettingsFileValue.find("%LOCALAPPDATA%") != std::string::npos); + REQUIRE(userSettingsFileValue.find("settings.json") != std::string::npos); } { @@ -3757,11 +3760,14 @@ TEST_CASE("Export_Settings", "[Settings][workflow]") std::ostringstream exportOutput; TestContext context2{ exportOutput, std::cin }; auto previousThreadGlobals2 = context2.SetForCurrentThread(); - context2.Override({ EnsureRunningAsAdmin, [](TestContext&) {} }); SettingsExportCommand settingsExportCommand({}); settingsExportCommand.Execute(context2); auto json = ConvertToJson(exportOutput.str()); REQUIRE(!json.isNull()); REQUIRE(json["adminSettings"]["LocalManifestFiles"].asBool()); + + auto userSettingsFileValue = std::string(json["userSettingsFile"].asCString()); + REQUIRE(userSettingsFileValue.find("%LOCALAPPDATA%") != std::string::npos); + REQUIRE(userSettingsFileValue.find("settings.json") != std::string::npos); } } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h index 451a2fc33b..def0149825 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h @@ -62,6 +62,8 @@ namespace AppInstaller::Runtime PortableLinksUserLocation, // The location where symlinks to portable packages are stored under machine scope. PortableLinksMachineLocation, + // The location of the user settings json file. + UserSettingsFileLocation, }; // The principal that an ACE applies to. diff --git a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h index 83730df877..d172f497d7 100644 --- a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h @@ -7,12 +7,12 @@ namespace AppInstaller::Settings { // Enum of admin settings. - // When a new one is added, it should be also added to SettingsCommand::ExportAdminSettings enum class AdminSetting { - Unknown, + Unknown = 0, LocalManifestFiles, BypassCertificatePinningForMicrosoftStore, + Max, }; AdminSetting StringToAdminSetting(std::string_view in); diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index e7a8429502..18fd8f7d0d 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -23,7 +23,7 @@ namespace AppInstaller::Runtime constexpr std::string_view s_DefaultTempDirectory = "WinGet"sv; constexpr std::string_view s_AppDataDir_Settings = "Settings"sv; constexpr std::string_view s_AppDataDir_State = "State"sv; - constexpr std::string_view s_SecureSettings_Base = "Microsoft/WinGet"sv; + constexpr std::string_view s_SecureSettings_Base = "Microsoft\\WinGet"sv; constexpr std::string_view s_SecureSettings_UserRelative = "settings"sv; constexpr std::string_view s_SecureSettings_Relative_Unpackaged = "win"sv; constexpr std::string_view s_PortablePackageUserRoot_Base = "Microsoft"sv; @@ -547,6 +547,10 @@ namespace AppInstaller::Runtime case PathName::PortableLinksMachineLocation: result = GetPathDetailsCommon(path); break; + case PathName::UserSettingsFileLocation: + result.Path = UserSettings::SettingsFilePath(); + ReplaceCommonPathPrefix(result.Path, GetKnownFolderPath(FOLDERID_LocalAppData), "%LOCALAPPDATA%"); + break; default: THROW_HR(E_UNEXPECTED); } @@ -617,6 +621,10 @@ namespace AppInstaller::Runtime case PathName::PortableLinksMachineLocation: result = GetPathDetailsCommon(path); break; + case PathName::UserSettingsFileLocation: + result.Path = UserSettings::SettingsFilePath(); + ReplaceCommonPathPrefix(result.Path, GetKnownFolderPath(FOLDERID_LocalAppData), "%LOCALAPPDATA%"); + break; default: THROW_HR(E_UNEXPECTED); } From aa36a8688261f55c8dcd6a101ba08f43a4168996 Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Thu, 8 Dec 2022 16:45:09 -0800 Subject: [PATCH 5/9] Add schema --- .../settings/exportsettings.schema.0.2.json | 43 +++++++++++++++++++ .../Workflows/SettingsFlow.cpp | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 schemas/JSON/settings/exportsettings.schema.0.2.json diff --git a/schemas/JSON/settings/exportsettings.schema.0.2.json b/schemas/JSON/settings/exportsettings.schema.0.2.json new file mode 100644 index 0000000000..17d0e8d5e4 --- /dev/null +++ b/schemas/JSON/settings/exportsettings.schema.0.2.json @@ -0,0 +1,43 @@ +{ + "$id": "https://aka.ms/winget-settings-export.schema.json", + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "title": "Microsoft's Windows Package Manager Settings Export Schema", + "definitions": { + "AdminSettings": { + "description": "Administrator settings", + "type": "object", + "properties": { + "BypassCertificatePinningForMicrosoftStore": { + "description": "Bypass Certificate Pinning For Microsoft Store", + "type": "boolean", + "default": false + }, + "LocalManifestFiles": { + "description": "Enable installing local manifests.", + "type": "boolean", + "default": false + } + } + }, + "UserSettingsFile": { + "description": "Path for the winget's user settings file.", + "type": "string", + "maxLength": 32767 + } + }, + "allOf": [ + { + "properties": { + "adminSettings": { "$ref": "#/definitions/AdminSettings" } + }, + "additionalItems": true + }, + { + "properties": { + "userSettingsFile": { "$ref": "#/definitions/UserSettingsFile" } + }, + "additionalItems": true + } + ], + "additionalProperties": true +} diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp index 90d014f7e1..f6fba40a0b 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp @@ -18,7 +18,7 @@ namespace AppInstaller::CLI::Workflow { ExportSettingsJson() { - // TODO: add schema + root["$schema"] = "https://aka.ms/winget-settings-export.schema.json"; root["adminSettings"] = Json::ValueType::objectValue; root["userSettingsFile"] = Runtime::GetPathTo(Runtime::PathName::UserSettingsFileLocation).u8string(); } From 0b7e4d41b1857b29331f972a2ba3d9b5823b48ef Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Thu, 8 Dec 2022 16:56:28 -0800 Subject: [PATCH 6/9] I can't have test strings lol --- ...settings.schema.0.2.json => settings.export.schema.0.1.json} | 0 src/AppInstallerCLITests/TestCommon.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename schemas/JSON/settings/{exportsettings.schema.0.2.json => settings.export.schema.0.1.json} (100%) diff --git a/schemas/JSON/settings/exportsettings.schema.0.2.json b/schemas/JSON/settings/settings.export.schema.0.1.json similarity index 100% rename from schemas/JSON/settings/exportsettings.schema.0.2.json rename to schemas/JSON/settings/settings.export.schema.0.1.json diff --git a/src/AppInstallerCLITests/TestCommon.cpp b/src/AppInstallerCLITests/TestCommon.cpp index ff76b22cc0..f0728d4de6 100644 --- a/src/AppInstallerCLITests/TestCommon.cpp +++ b/src/AppInstallerCLITests/TestCommon.cpp @@ -329,7 +329,7 @@ namespace TestCommon std::string RemoveConsoleFormat(const std::string& str) { - // We are looking something in the lines of "\x1b[0mthisisastring" + // We are looking something that starts with "\x1b[0m" if (!str.empty() && str[0] == '\x1b') { // Find first m From 47d1ba396007ac4d06364942209792c90669d8ed Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Fri, 9 Dec 2022 10:55:04 -0800 Subject: [PATCH 7/9] Bad merge --- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 0f2ee3c380..0cc5d78a90 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1534,4 +1534,5 @@ Please specify one of them using the --source option to proceed. User Settings + \ No newline at end of file From b140188233df180207efb40dcb985c5cb66d6cf1 Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Mon, 12 Dec 2022 15:58:55 -0800 Subject: [PATCH 8/9] For display --- src/AppInstallerCLICore/Commands/RootCommand.cpp | 2 +- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 4 ++-- src/AppInstallerCommonCore/Public/AppInstallerRuntime.h | 2 ++ src/AppInstallerCommonCore/Runtime.cpp | 6 ++++++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index 3b3b4dc7aa..c9864bc1f6 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -193,7 +193,7 @@ namespace AppInstaller::CLI }; info << std::endl << Resource::String::Logs << ": "_liv << Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string() << std::endl; - info << std::endl << Resource::String::UserSettings << ": "_liv << Runtime::GetPathTo(Runtime::PathName::UserSettingsFileLocation).u8string() << std::endl; + info << std::endl << Resource::String::UserSettings << ": "_liv << Runtime::GetPathTo(Runtime::PathName::UserSettingsFileLocationForDisplay).u8string() << std::endl; info << std::endl; diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 0cc5d78a90..4306966168 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1527,10 +1527,10 @@ Please specify one of them using the --source option to proceed. Block from updating until the pin is removed, preventing override arguments - Export administrator settings as JSON + Export settings as JSON - Export administrator settings + Export settings User Settings diff --git a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h index def0149825..336b749fbc 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h @@ -64,6 +64,8 @@ namespace AppInstaller::Runtime PortableLinksMachineLocation, // The location of the user settings json file. UserSettingsFileLocation, + // The location of the user settings json file, anonymized using environment variables. + UserSettingsFileLocationForDisplay, }; // The principal that an ACE applies to. diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 18fd8f7d0d..cd4f730b4a 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -548,6 +548,9 @@ namespace AppInstaller::Runtime result = GetPathDetailsCommon(path); break; case PathName::UserSettingsFileLocation: + result.Path = UserSettings::SettingsFilePath(); + break; + case PathName::UserSettingsFileLocationForDisplay: result.Path = UserSettings::SettingsFilePath(); ReplaceCommonPathPrefix(result.Path, GetKnownFolderPath(FOLDERID_LocalAppData), "%LOCALAPPDATA%"); break; @@ -622,6 +625,9 @@ namespace AppInstaller::Runtime result = GetPathDetailsCommon(path); break; case PathName::UserSettingsFileLocation: + result.Path = UserSettings::SettingsFilePath(); + break; + case PathName::UserSettingsFileLocationForDisplay: result.Path = UserSettings::SettingsFilePath(); ReplaceCommonPathPrefix(result.Path, GetKnownFolderPath(FOLDERID_LocalAppData), "%LOCALAPPDATA%"); break; From 2e686690f7ea15b124ee1199209dac465089c677 Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Mon, 12 Dec 2022 18:24:05 -0800 Subject: [PATCH 9/9] Fix test --- src/AppInstallerCLITests/WorkFlow.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 862fbf5e46..f217ac0ab4 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -3915,7 +3915,6 @@ TEST_CASE("Export_Settings", "[Settings][workflow]") REQUIRE_FALSE(json["adminSettings"]["LocalManifestFiles"].asBool()); auto userSettingsFileValue = std::string(json["userSettingsFile"].asCString()); - REQUIRE(userSettingsFileValue.find("%LOCALAPPDATA%") != std::string::npos); REQUIRE(userSettingsFileValue.find("settings.json") != std::string::npos); } @@ -3939,7 +3938,6 @@ TEST_CASE("Export_Settings", "[Settings][workflow]") REQUIRE(json["adminSettings"]["LocalManifestFiles"].asBool()); auto userSettingsFileValue = std::string(json["userSettingsFile"].asCString()); - REQUIRE(userSettingsFileValue.find("%LOCALAPPDATA%") != std::string::npos); REQUIRE(userSettingsFileValue.find("settings.json") != std::string::npos); } }