Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@
<ClInclude Include="Commands\ConfigureValidateCommand.h" />
<ClInclude Include="Commands\DebugCommand.h" />
<ClInclude Include="Commands\DownloadCommand.h" />
<ClInclude Include="Commands\DscAdminSettingsResource.h" />
<ClInclude Include="Commands\DscCommand.h" />
<ClInclude Include="Commands\DscCommandBase.h" />
<ClInclude Include="Commands\DscComposableObject.h" />
Expand Down Expand Up @@ -390,6 +391,7 @@
<ClCompile Include="Commands\ConfigureValidateCommand.cpp" />
<ClCompile Include="Commands\DebugCommand.cpp" />
<ClCompile Include="Commands\DownloadCommand.cpp" />
<ClCompile Include="Commands\DscAdminSettingsResource.cpp" />
<ClCompile Include="Commands\DscCommand.cpp" />
<ClCompile Include="Commands\DscCommandBase.cpp" />
<ClCompile Include="Commands\DscComposableObject.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@
<ClInclude Include="Commands\DscUserSettingsFileResource.h">
<Filter>Commands\Configuration</Filter>
</ClInclude>
<ClInclude Include="Commands\DscAdminSettingsResource.h">
<Filter>Commands\Configuration</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
Expand Down Expand Up @@ -547,6 +550,9 @@
<ClCompile Include="Commands\DscUserSettingsFileResource.cpp">
<Filter>Commands\Configuration</Filter>
</ClCompile>
<ClCompile Include="Commands\DscAdminSettingsResource.cpp">
<Filter>Commands\Configuration</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
Expand Down
294 changes: 294 additions & 0 deletions src/AppInstallerCLICore/Commands/DscAdminSettingsResource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include "DscAdminSettingsResource.h"
#include "DscComposableObject.h"
#include "Resources.h"
#include <winget/AdminSettings.h>

using namespace AppInstaller::Utility::literals;
using namespace AppInstaller::Repository;

namespace AppInstaller::CLI
{
namespace AdminSettingsDetails
{
// The base for admin settings.
struct IAdminSetting
{
virtual ~IAdminSetting() = default;

// Gets the name of the setting.
virtual Utility::LocIndView SettingName() const = 0;

// Tests the value.
// Returns true if in the desired state; false if not.
virtual bool Test() const = 0;

// Sets the value.
// Returns true if the value could be set; false if not.
virtual bool Set() const = 0;
};

// A boolean based admin setting
struct AdminSetting_Bool : public IAdminSetting
{
AdminSetting_Bool(Settings::BoolAdminSetting setting, bool value) : m_setting(setting), m_value(value) {}

Utility::LocIndView SettingName() const override
{
return Settings::AdminSettingToString(m_setting);
}

bool Test() const override
{
return Settings::IsAdminSettingEnabled(m_setting) == m_value;
}

bool Set() const override
{
return m_value ? Settings::EnableAdminSetting(m_setting) : Settings::DisableAdminSetting(m_setting);
}

private:
Settings::BoolAdminSetting m_setting;
bool m_value;
};

// A string based admin setting
struct AdminSetting_String : public IAdminSetting
{
AdminSetting_String(Settings::StringAdminSetting setting, std::optional<std::string> value) : m_setting(setting), m_value(std::move(value)) {}

Utility::LocIndView SettingName() const override
{
return Settings::AdminSettingToString(m_setting);
}

bool Test() const override
{
return Settings::GetAdminSetting(m_setting) == m_value;
}

bool Set() const override
{
return m_value ? Settings::SetAdminSetting(m_setting, m_value.value()) : Settings::ResetAdminSetting(m_setting);
}

private:
Settings::StringAdminSetting m_setting;
std::optional<std::string> m_value;
};
}

namespace
{
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(SettingsProperty, Json::Value, Settings, "settings", Resource::String::DscResourcePropertyDescriptionAdminSettingsSettings);

using AdminSettingsResourceObject = DscComposableObject<StandardInDesiredStateProperty, SettingsProperty>;

struct AdminSettingsFunctionData
{
AdminSettingsFunctionData() = default;

AdminSettingsFunctionData(const std::optional<Json::Value>& json) :
Input(json)
{
}

const AdminSettingsResourceObject Input;
AdminSettingsResourceObject Output;
std::vector<std::unique_ptr<AdminSettingsDetails::IAdminSetting>> InputSettings;

// Converts the input settings into the appropriate settings object.
void ParseSettings()
{
if (Input.Settings())
{
const Json::Value& inputSettings = Input.Settings().value();
for (const auto& property : inputSettings.getMemberNames())
{
auto boolSetting = Settings::StringToBoolAdminSetting(property);
if (boolSetting != Settings::BoolAdminSetting::Unknown)
{
bool value = inputSettings[property].asBool();
AICLI_LOG(Config, Info, << "Bool admin setting: " << property << " => " << (value ? "true" : "false"));
InputSettings.emplace_back(std::make_unique<AdminSettingsDetails::AdminSetting_Bool>(boolSetting, value));
continue;
}

auto stringSetting = Settings::StringToStringAdminSetting(property);
if (stringSetting != Settings::StringAdminSetting::Unknown)
{
const auto& propertyNode = inputSettings[property];
std::optional<std::string> value;

if (propertyNode.isNull())
{
AICLI_LOG(Config, Info, << "String admin setting: " << property << " => null");
}
else
{
value = propertyNode.asString();
AICLI_LOG(Config, Info, << "String admin setting: " << property << " => `" << value.value() << "`");
}

InputSettings.emplace_back(std::make_unique<AdminSettingsDetails::AdminSetting_String>(stringSetting, std::move(value)));
continue;
}

AICLI_LOG(Config, Warning, << "Unknown admin setting: " << property);
}
}
}

// Fills the Output object with the current state
void Get()
{
Json::Value adminSettings{ Json::objectValue };

for (const auto& setting : Settings::GetAllBoolAdminSettings())
{
auto str = std::string{ Settings::AdminSettingToString(setting) };
adminSettings[str] = Settings::IsAdminSettingEnabled(setting);
}

for (const auto& setting : Settings::GetAllStringAdminSettings())
{
auto name = std::string{ Settings::AdminSettingToString(setting) };
auto value = Settings::GetAdminSetting(setting);
if (value)
{
adminSettings[name] = value.value();
}
}

Output.Settings(std::move(adminSettings));
}

// Determines if the current Output values match the Input values state.
bool Test()
{
for (const auto& setting : InputSettings)
{
if (!setting->Test())
{
return false;
}
}

return true;
}

// Sets all of the input settings.
void Set()
{
for (const auto& setting : InputSettings)
{
if (!setting->Test())
{
if (!setting->Set())
{
auto message = Resource::String::DisabledByGroupPolicy(setting->SettingName());
THROW_HR_MSG(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, "%hs", message.get().c_str());
}
}
}
}

Json::Value DiffJson(bool inDesiredState)
{
Json::Value result{ Json::ValueType::arrayValue };

if (!inDesiredState)
{
result.append(std::string{ SettingsProperty::Name() });
}

return result;
}
};
}

DscAdminSettingsResource::DscAdminSettingsResource(std::string_view parent) :
DscCommandBase(parent, "admin-settings", DscResourceKind::Resource,
DscFunctions::Get | DscFunctions::Set | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema,
DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::ReturnsStateAndDiff)
{
}

Resource::LocString DscAdminSettingsResource::ShortDescription() const
{
return Resource::String::DscAdminSettingsResourceShortDescription;
}

Resource::LocString DscAdminSettingsResource::LongDescription() const
{
return Resource::String::DscAdminSettingsResourceLongDescription;
}

std::string DscAdminSettingsResource::ResourceType() const
{
return "AdminSettings";
}

void DscAdminSettingsResource::ResourceFunctionGet(Execution::Context& context) const
{
AdminSettingsFunctionData data;
data.Get();
WriteJsonOutputLine(context, data.Output.ToJson());
}

void DscAdminSettingsResource::ResourceFunctionSet(Execution::Context& context) const
{
if (auto json = GetJsonFromInput(context))
{
AdminSettingsFunctionData data{ json };

data.ParseSettings();

bool inDesiredState = data.Test();
if (!inDesiredState)
{
Workflow::EnsureRunningAsAdmin(context);

if (context.IsTerminated())
{
return;
}

data.Set();
}

data.Get();
WriteJsonOutputLine(context, data.Output.ToJson());
WriteJsonOutputLine(context, data.DiffJson(inDesiredState));
}
}

void DscAdminSettingsResource::ResourceFunctionTest(Execution::Context& context) const
{
if (auto json = GetJsonFromInput(context))
{
AdminSettingsFunctionData data{ json };

data.ParseSettings();

data.Get();
data.Output.InDesiredState(data.Test());

WriteJsonOutputLine(context, data.Output.ToJson());
WriteJsonOutputLine(context, data.DiffJson(data.Output.InDesiredState().value()));
}
}

void DscAdminSettingsResource::ResourceFunctionExport(Execution::Context& context) const
{
ResourceFunctionGet(context);
}

void DscAdminSettingsResource::ResourceFunctionSchema(Execution::Context& context) const
{
WriteJsonOutputLine(context, AdminSettingsResourceObject::Schema(ResourceType()));
}
}
25 changes: 25 additions & 0 deletions src/AppInstallerCLICore/Commands/DscAdminSettingsResource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "DscCommandBase.h"

namespace AppInstaller::CLI
{
// A resource for managing admin settings.
struct DscAdminSettingsResource : public DscCommandBase
{
DscAdminSettingsResource(std::string_view parent);

Resource::LocString ShortDescription() const override;
Resource::LocString LongDescription() const override;

protected:
std::string ResourceType() const override;

void ResourceFunctionGet(Execution::Context& context) const override;
void ResourceFunctionSet(Execution::Context& context) const override;
void ResourceFunctionTest(Execution::Context& context) const override;
void ResourceFunctionExport(Execution::Context& context) const override;
void ResourceFunctionSchema(Execution::Context& context) const override;
};
}
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/Commands/DscCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "DscPackageResource.h"
#include "DscUserSettingsFileResource.h"
#include "DscSourceResource.h"
#include "DscAdminSettingsResource.h"

#ifndef AICLI_DISABLE_TEST_HOOKS
#include "DscTestFileResource.h"
Expand Down Expand Up @@ -33,6 +34,7 @@ namespace AppInstaller::CLI
std::make_unique<DscPackageResource>(FullName()),
std::make_unique<DscSourceResource>(FullName()),
std::make_unique<DscUserSettingsFileResource>(FullName()),
std::make_unique<DscAdminSettingsResource>(FullName()),
#ifndef AICLI_DISABLE_TEST_HOOKS
std::make_unique<DscTestFileResource>(FullName()),
std::make_unique<DscTestJsonResource>(FullName()),
Expand Down
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/Commands/DscComposableObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ namespace AppInstaller::CLI

static void FromJson(Derived* self, const Json::Value* value, bool ignoreFieldRequirements)
{
if (value)
if (value && !value->isNull())
{
self->m_value = GetJsonTypeValue<PropertyType>::Get(*value);
}
Expand Down
Loading