Skip to content

Commit 1b24d12

Browse files
authored
DSCv3 admin settings resource (#5458)
## Change Adds an admin settings resource to mirror the existing v2. Also adds `winget settings reset --all` to reset all admin settings to their default values (respecting group policy of course).
1 parent cefeec7 commit 1b24d12

File tree

15 files changed

+767
-4
lines changed

15 files changed

+767
-4
lines changed

src/AppInstallerCLICore/AppInstallerCLICore.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@
302302
<ClInclude Include="Commands\ConfigureValidateCommand.h" />
303303
<ClInclude Include="Commands\DebugCommand.h" />
304304
<ClInclude Include="Commands\DownloadCommand.h" />
305+
<ClInclude Include="Commands\DscAdminSettingsResource.h" />
305306
<ClInclude Include="Commands\DscCommand.h" />
306307
<ClInclude Include="Commands\DscCommandBase.h" />
307308
<ClInclude Include="Commands\DscComposableObject.h" />
@@ -390,6 +391,7 @@
390391
<ClCompile Include="Commands\ConfigureValidateCommand.cpp" />
391392
<ClCompile Include="Commands\DebugCommand.cpp" />
392393
<ClCompile Include="Commands\DownloadCommand.cpp" />
394+
<ClCompile Include="Commands\DscAdminSettingsResource.cpp" />
393395
<ClCompile Include="Commands\DscCommand.cpp" />
394396
<ClCompile Include="Commands\DscCommandBase.cpp" />
395397
<ClCompile Include="Commands\DscComposableObject.cpp" />

src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@
290290
<ClInclude Include="Commands\DscUserSettingsFileResource.h">
291291
<Filter>Commands\Configuration</Filter>
292292
</ClInclude>
293+
<ClInclude Include="Commands\DscAdminSettingsResource.h">
294+
<Filter>Commands\Configuration</Filter>
295+
</ClInclude>
293296
</ItemGroup>
294297
<ItemGroup>
295298
<ClCompile Include="pch.cpp">
@@ -547,6 +550,9 @@
547550
<ClCompile Include="Commands\DscUserSettingsFileResource.cpp">
548551
<Filter>Commands\Configuration</Filter>
549552
</ClCompile>
553+
<ClCompile Include="Commands\DscAdminSettingsResource.cpp">
554+
<Filter>Commands\Configuration</Filter>
555+
</ClCompile>
550556
</ItemGroup>
551557
<ItemGroup>
552558
<None Include="PropertySheet.props" />
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#include "pch.h"
4+
#include "DscAdminSettingsResource.h"
5+
#include "DscComposableObject.h"
6+
#include "Resources.h"
7+
#include <winget/AdminSettings.h>
8+
9+
using namespace AppInstaller::Utility::literals;
10+
using namespace AppInstaller::Repository;
11+
12+
namespace AppInstaller::CLI
13+
{
14+
namespace AdminSettingsDetails
15+
{
16+
// The base for admin settings.
17+
struct IAdminSetting
18+
{
19+
virtual ~IAdminSetting() = default;
20+
21+
// Gets the name of the setting.
22+
virtual Utility::LocIndView SettingName() const = 0;
23+
24+
// Tests the value.
25+
// Returns true if in the desired state; false if not.
26+
virtual bool Test() const = 0;
27+
28+
// Sets the value.
29+
// Returns true if the value could be set; false if not.
30+
virtual bool Set() const = 0;
31+
};
32+
33+
// A boolean based admin setting
34+
struct AdminSetting_Bool : public IAdminSetting
35+
{
36+
AdminSetting_Bool(Settings::BoolAdminSetting setting, bool value) : m_setting(setting), m_value(value) {}
37+
38+
Utility::LocIndView SettingName() const override
39+
{
40+
return Settings::AdminSettingToString(m_setting);
41+
}
42+
43+
bool Test() const override
44+
{
45+
return Settings::IsAdminSettingEnabled(m_setting) == m_value;
46+
}
47+
48+
bool Set() const override
49+
{
50+
return m_value ? Settings::EnableAdminSetting(m_setting) : Settings::DisableAdminSetting(m_setting);
51+
}
52+
53+
private:
54+
Settings::BoolAdminSetting m_setting;
55+
bool m_value;
56+
};
57+
58+
// A string based admin setting
59+
struct AdminSetting_String : public IAdminSetting
60+
{
61+
AdminSetting_String(Settings::StringAdminSetting setting, std::optional<std::string> value) : m_setting(setting), m_value(std::move(value)) {}
62+
63+
Utility::LocIndView SettingName() const override
64+
{
65+
return Settings::AdminSettingToString(m_setting);
66+
}
67+
68+
bool Test() const override
69+
{
70+
return Settings::GetAdminSetting(m_setting) == m_value;
71+
}
72+
73+
bool Set() const override
74+
{
75+
return m_value ? Settings::SetAdminSetting(m_setting, m_value.value()) : Settings::ResetAdminSetting(m_setting);
76+
}
77+
78+
private:
79+
Settings::StringAdminSetting m_setting;
80+
std::optional<std::string> m_value;
81+
};
82+
}
83+
84+
namespace
85+
{
86+
WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(SettingsProperty, Json::Value, Settings, "settings", Resource::String::DscResourcePropertyDescriptionAdminSettingsSettings);
87+
88+
using AdminSettingsResourceObject = DscComposableObject<StandardInDesiredStateProperty, SettingsProperty>;
89+
90+
struct AdminSettingsFunctionData
91+
{
92+
AdminSettingsFunctionData() = default;
93+
94+
AdminSettingsFunctionData(const std::optional<Json::Value>& json) :
95+
Input(json)
96+
{
97+
}
98+
99+
const AdminSettingsResourceObject Input;
100+
AdminSettingsResourceObject Output;
101+
std::vector<std::unique_ptr<AdminSettingsDetails::IAdminSetting>> InputSettings;
102+
103+
// Converts the input settings into the appropriate settings object.
104+
void ParseSettings()
105+
{
106+
if (Input.Settings())
107+
{
108+
const Json::Value& inputSettings = Input.Settings().value();
109+
for (const auto& property : inputSettings.getMemberNames())
110+
{
111+
auto boolSetting = Settings::StringToBoolAdminSetting(property);
112+
if (boolSetting != Settings::BoolAdminSetting::Unknown)
113+
{
114+
bool value = inputSettings[property].asBool();
115+
AICLI_LOG(Config, Info, << "Bool admin setting: " << property << " => " << (value ? "true" : "false"));
116+
InputSettings.emplace_back(std::make_unique<AdminSettingsDetails::AdminSetting_Bool>(boolSetting, value));
117+
continue;
118+
}
119+
120+
auto stringSetting = Settings::StringToStringAdminSetting(property);
121+
if (stringSetting != Settings::StringAdminSetting::Unknown)
122+
{
123+
const auto& propertyNode = inputSettings[property];
124+
std::optional<std::string> value;
125+
126+
if (propertyNode.isNull())
127+
{
128+
AICLI_LOG(Config, Info, << "String admin setting: " << property << " => null");
129+
}
130+
else
131+
{
132+
value = propertyNode.asString();
133+
AICLI_LOG(Config, Info, << "String admin setting: " << property << " => `" << value.value() << "`");
134+
}
135+
136+
InputSettings.emplace_back(std::make_unique<AdminSettingsDetails::AdminSetting_String>(stringSetting, std::move(value)));
137+
continue;
138+
}
139+
140+
AICLI_LOG(Config, Warning, << "Unknown admin setting: " << property);
141+
}
142+
}
143+
}
144+
145+
// Fills the Output object with the current state
146+
void Get()
147+
{
148+
Json::Value adminSettings{ Json::objectValue };
149+
150+
for (const auto& setting : Settings::GetAllBoolAdminSettings())
151+
{
152+
auto str = std::string{ Settings::AdminSettingToString(setting) };
153+
adminSettings[str] = Settings::IsAdminSettingEnabled(setting);
154+
}
155+
156+
for (const auto& setting : Settings::GetAllStringAdminSettings())
157+
{
158+
auto name = std::string{ Settings::AdminSettingToString(setting) };
159+
auto value = Settings::GetAdminSetting(setting);
160+
if (value)
161+
{
162+
adminSettings[name] = value.value();
163+
}
164+
}
165+
166+
Output.Settings(std::move(adminSettings));
167+
}
168+
169+
// Determines if the current Output values match the Input values state.
170+
bool Test()
171+
{
172+
for (const auto& setting : InputSettings)
173+
{
174+
if (!setting->Test())
175+
{
176+
return false;
177+
}
178+
}
179+
180+
return true;
181+
}
182+
183+
// Sets all of the input settings.
184+
void Set()
185+
{
186+
for (const auto& setting : InputSettings)
187+
{
188+
if (!setting->Test())
189+
{
190+
if (!setting->Set())
191+
{
192+
auto message = Resource::String::DisabledByGroupPolicy(setting->SettingName());
193+
THROW_HR_MSG(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, "%hs", message.get().c_str());
194+
}
195+
}
196+
}
197+
}
198+
199+
Json::Value DiffJson(bool inDesiredState)
200+
{
201+
Json::Value result{ Json::ValueType::arrayValue };
202+
203+
if (!inDesiredState)
204+
{
205+
result.append(std::string{ SettingsProperty::Name() });
206+
}
207+
208+
return result;
209+
}
210+
};
211+
}
212+
213+
DscAdminSettingsResource::DscAdminSettingsResource(std::string_view parent) :
214+
DscCommandBase(parent, "admin-settings", DscResourceKind::Resource,
215+
DscFunctions::Get | DscFunctions::Set | DscFunctions::Test | DscFunctions::Export | DscFunctions::Schema,
216+
DscFunctionModifiers::ImplementsPretest | DscFunctionModifiers::ReturnsStateAndDiff)
217+
{
218+
}
219+
220+
Resource::LocString DscAdminSettingsResource::ShortDescription() const
221+
{
222+
return Resource::String::DscAdminSettingsResourceShortDescription;
223+
}
224+
225+
Resource::LocString DscAdminSettingsResource::LongDescription() const
226+
{
227+
return Resource::String::DscAdminSettingsResourceLongDescription;
228+
}
229+
230+
std::string DscAdminSettingsResource::ResourceType() const
231+
{
232+
return "AdminSettings";
233+
}
234+
235+
void DscAdminSettingsResource::ResourceFunctionGet(Execution::Context& context) const
236+
{
237+
AdminSettingsFunctionData data;
238+
data.Get();
239+
WriteJsonOutputLine(context, data.Output.ToJson());
240+
}
241+
242+
void DscAdminSettingsResource::ResourceFunctionSet(Execution::Context& context) const
243+
{
244+
if (auto json = GetJsonFromInput(context))
245+
{
246+
AdminSettingsFunctionData data{ json };
247+
248+
data.ParseSettings();
249+
250+
bool inDesiredState = data.Test();
251+
if (!inDesiredState)
252+
{
253+
Workflow::EnsureRunningAsAdmin(context);
254+
255+
if (context.IsTerminated())
256+
{
257+
return;
258+
}
259+
260+
data.Set();
261+
}
262+
263+
data.Get();
264+
WriteJsonOutputLine(context, data.Output.ToJson());
265+
WriteJsonOutputLine(context, data.DiffJson(inDesiredState));
266+
}
267+
}
268+
269+
void DscAdminSettingsResource::ResourceFunctionTest(Execution::Context& context) const
270+
{
271+
if (auto json = GetJsonFromInput(context))
272+
{
273+
AdminSettingsFunctionData data{ json };
274+
275+
data.ParseSettings();
276+
277+
data.Get();
278+
data.Output.InDesiredState(data.Test());
279+
280+
WriteJsonOutputLine(context, data.Output.ToJson());
281+
WriteJsonOutputLine(context, data.DiffJson(data.Output.InDesiredState().value()));
282+
}
283+
}
284+
285+
void DscAdminSettingsResource::ResourceFunctionExport(Execution::Context& context) const
286+
{
287+
ResourceFunctionGet(context);
288+
}
289+
290+
void DscAdminSettingsResource::ResourceFunctionSchema(Execution::Context& context) const
291+
{
292+
WriteJsonOutputLine(context, AdminSettingsResourceObject::Schema(ResourceType()));
293+
}
294+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#pragma once
4+
#include "DscCommandBase.h"
5+
6+
namespace AppInstaller::CLI
7+
{
8+
// A resource for managing admin settings.
9+
struct DscAdminSettingsResource : public DscCommandBase
10+
{
11+
DscAdminSettingsResource(std::string_view parent);
12+
13+
Resource::LocString ShortDescription() const override;
14+
Resource::LocString LongDescription() const override;
15+
16+
protected:
17+
std::string ResourceType() const override;
18+
19+
void ResourceFunctionGet(Execution::Context& context) const override;
20+
void ResourceFunctionSet(Execution::Context& context) const override;
21+
void ResourceFunctionTest(Execution::Context& context) const override;
22+
void ResourceFunctionExport(Execution::Context& context) const override;
23+
void ResourceFunctionSchema(Execution::Context& context) const override;
24+
};
25+
}

src/AppInstallerCLICore/Commands/DscCommand.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "DscPackageResource.h"
66
#include "DscUserSettingsFileResource.h"
77
#include "DscSourceResource.h"
8+
#include "DscAdminSettingsResource.h"
89

910
#ifndef AICLI_DISABLE_TEST_HOOKS
1011
#include "DscTestFileResource.h"
@@ -33,6 +34,7 @@ namespace AppInstaller::CLI
3334
std::make_unique<DscPackageResource>(FullName()),
3435
std::make_unique<DscSourceResource>(FullName()),
3536
std::make_unique<DscUserSettingsFileResource>(FullName()),
37+
std::make_unique<DscAdminSettingsResource>(FullName()),
3638
#ifndef AICLI_DISABLE_TEST_HOOKS
3739
std::make_unique<DscTestFileResource>(FullName()),
3840
std::make_unique<DscTestJsonResource>(FullName()),

src/AppInstallerCLICore/Commands/DscComposableObject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ namespace AppInstaller::CLI
162162

163163
static void FromJson(Derived* self, const Json::Value* value, bool ignoreFieldRequirements)
164164
{
165-
if (value)
165+
if (value && !value->isNull())
166166
{
167167
self->m_value = GetJsonTypeValue<PropertyType>::Get(*value);
168168
}

0 commit comments

Comments
 (0)