DscAdminSettingsResource.cpp (10743B)
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 }