commit 71e3a17dccd51bc36067eed1fa71c772e1e72de5 parent 586bcbcfd0501ecdf840c530eeb37d60fa5b9e5f Author: JohnMcPMS <johnmcp@microsoft.com> Date: Wed, 5 Feb 2025 17:45:17 -0800 Update configuration environment behavior (#5182) ## Change In response to some other discussions, update the configuration environment behavior: 1. Add an environment at the set level 2. Don't inherit environment data into child units, and don't promote environment data when serializing Updated the one consumer of environment (dynamic factory) to be responsible for the flow of the security context from the set to the immediate child units. Also improved serialization of groups to output the non-resource properties. Diffstat:
12 files changed, 243 insertions(+), 71 deletions(-)
diff --git a/src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp b/src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp @@ -139,6 +139,9 @@ namespace AppInstaller::CLI::ConfigurationRemoting m_currentIntegrityLevel = Security::GetEffectiveIntegrityLevel(); #endif + m_setIntegrityLevel = m_currentIntegrityLevel; + m_setIntegrityLevel = SecurityContextToIntegrityLevel(m_configurationSet.Environment().Context()); + // Check for multiple integrity level requirements bool multipleIntegrityLevels = false; bool higherIntegrityLevelsThanCurrent = false; @@ -211,13 +214,13 @@ namespace AppInstaller::CLI::ConfigurationRemoting } private: - // Converts the string representation of SecurityContext to the integrity level + // Converts the string representation of SecurityContext to the target integrity level for this instance Security::IntegrityLevel SecurityContextToIntegrityLevel(SecurityContext securityContext) { switch (securityContext) { case SecurityContext::Current: - return m_currentIntegrityLevel; + return m_setIntegrityLevel; case SecurityContext::Restricted: #ifndef AICLI_DISABLE_TEST_HOOKS if (m_enableRestrictedIntegrityLevel) @@ -358,6 +361,7 @@ namespace AppInstaller::CLI::ConfigurationRemoting winrt::com_ptr<DynamicFactory> m_dynamicFactory; Security::IntegrityLevel m_currentIntegrityLevel; + Security::IntegrityLevel m_setIntegrityLevel; ProcessorMap m_setProcessors; ConfigurationSet m_configurationSet; std::once_flag m_createUnitSetProcessorsOnce; diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ValueSetExtensions.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ValueSetExtensions.cs @@ -0,0 +1,74 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ValueSetExtensions.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Helpers +{ + using System; + using System.Text; + using Windows.Foundation.Collections; + + /// <summary> + /// Extensions for ValueSet. + /// </summary> + internal static class ValueSetExtensions + { + /// <summary> + /// Converts the value set to YAML like output. + /// </summary> + /// <param name="set">The set to output.</param> + /// <returns>The string.</returns> + public static string ToYaml(this ValueSet set) + { + StringBuilder sb = new StringBuilder(); + ToYaml(set, sb); + return sb.ToString(); + } + + private static void ToYaml(ValueSet set, StringBuilder sb, int indentation = 0) + { + foreach (var keyValuePair in set) + { + bool addLine = true; + + sb.Append(' ', indentation); + sb.Append(keyValuePair.Key); + sb.Append(": "); + + if (keyValuePair.Value == null) + { + sb.Append("null"); + } + else + { + switch (keyValuePair.Value) + { + case int i: + sb.Append(i); + break; + case string s: + sb.Append(s); + break; + case bool b: + sb.Append(b); + break; + case ValueSet v: + sb.AppendLine(); + ToYaml(v, sb, indentation + 2); + addLine = false; + break; + default: + throw new NotImplementedException($"Add ToYaml type `{keyValuePair.Value.GetType().Name}`"); + } + } + + if (addLine) + { + sb.AppendLine(); + } + } + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs @@ -8,6 +8,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests { using System; using System.Collections.Generic; + using System.DirectoryServices; using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; @@ -588,6 +589,17 @@ resources: TestInt: 4321 Mapping: Key: TestValue + - type: FakeModule/FakeResource3 + name: TestId3 + metadata: + isGroup: true + properties: + other: value + resources: + - type: Grouped/Resource + name: Child + properties: + b: c ")); // Serialize set. @@ -604,7 +616,7 @@ resources: Assert.NotNull(set); Assert.Equal("0.3", set.SchemaVersion); - Assert.Equal(2, set.Units.Count); + Assert.Equal(3, set.Units.Count); this.VerifyValueSet(set.Metadata, new KeyValuePair<string, object>("description", "FakeSetDescription")); this.VerifyValueSet(set.Variables, new ("var1", "Test1"), new ("var2", 42)); @@ -626,6 +638,30 @@ resources: ValueSet mapping = new ValueSet(); mapping.Add("Key", "TestValue"); this.VerifyValueSet(set.Units[1].Settings, new ("TestString", "Bye"), new ("TestBool", true), new ("TestInt", 4321), new ("Mapping", mapping)); + + Assert.Equal("FakeModule/FakeResource3", set.Units[2].Type); + Assert.Equal("TestId3", set.Units[2].Identifier); + Assert.True(set.Units[2].IsGroup); + + ValueSet childResource = new ValueSet(); + childResource.Add("type", "Grouped/Resource"); + childResource.Add("name", "Child"); + ValueSet childResourceProperties = new ValueSet(); + childResourceProperties.Add("b", "c"); + childResource.Add("properties", childResourceProperties); + + ValueSet resourcesArray = new ValueSet(); + resourcesArray.Add("treatAsArray", true); + resourcesArray.Add("0", childResource); + + this.VerifyValueSet(set.Units[2].Settings, new ("other", "value"), new ("resources", resourcesArray)); + + var groupChildren = set.Units[2].Units; + Assert.Single(groupChildren); + + Assert.Equal("Grouped/Resource", groupChildren[0].Type); + Assert.Equal("Child", groupChildren[0].Identifier); + this.VerifyValueSet(groupChildren[0].Settings, new KeyValuePair<string, object>("b", "c")); } /// <summary> @@ -871,12 +907,13 @@ resources: Dictionary<string, string> environmentProperties = new Dictionary<string, string>(); environmentProperties.Add("a", "b"); + ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }; Dictionary<string, ConfigurationEnvironmentData> expectedEnvironments = new Dictionary<string, ConfigurationEnvironmentData>(); - expectedEnvironments.Add("first", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }); - expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }); + expectedEnvironments.Add("first", new ConfigurationEnvironmentData()); + expectedEnvironments.Add("second", new ConfigurationEnvironmentData()); - this.ValidateEnvironments(openResult, expectedEnvironments); + this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); @@ -887,7 +924,7 @@ resources: // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); - this.ValidateEnvironments(serializedSetResult, expectedEnvironments); + this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// <summary> @@ -923,12 +960,13 @@ resources: Dictionary<string, string> environmentProperties = new Dictionary<string, string>(); environmentProperties.Add("a", "b"); + ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }; Dictionary<string, ConfigurationEnvironmentData> expectedEnvironments = new Dictionary<string, ConfigurationEnvironmentData>(); - expectedEnvironments.Add("first", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }); - expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "not-pwsh" }); + expectedEnvironments.Add("first", new ConfigurationEnvironmentData()); + expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { ProcessorIdentifier = "not-pwsh" }); - this.ValidateEnvironments(openResult, expectedEnvironments); + this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); @@ -939,7 +977,7 @@ resources: // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); - this.ValidateEnvironments(serializedSetResult, expectedEnvironments); + this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// <summary> @@ -975,12 +1013,13 @@ resources: Dictionary<string, string> environmentProperties = new Dictionary<string, string>(); environmentProperties.Add("a", "b"); + ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }; Dictionary<string, ConfigurationEnvironmentData> expectedEnvironments = new Dictionary<string, ConfigurationEnvironmentData>(); - expectedEnvironments.Add("first", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }); - expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }); + expectedEnvironments.Add("first", new ConfigurationEnvironmentData()); + expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted }); - this.ValidateEnvironments(openResult, expectedEnvironments); + this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); @@ -991,7 +1030,7 @@ resources: // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); - this.ValidateEnvironments(serializedSetResult, expectedEnvironments); + this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// <summary> @@ -1024,11 +1063,13 @@ resources: identifier: pwsh ")); + ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData(); + Dictionary<string, ConfigurationEnvironmentData> expectedEnvironments = new Dictionary<string, ConfigurationEnvironmentData>(); expectedEnvironments.Add("first", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh" }); expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh" }); - this.ValidateEnvironments(openResult, expectedEnvironments); + this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); @@ -1039,7 +1080,7 @@ resources: // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); - this.ValidateEnvironments(serializedSetResult, expectedEnvironments); + this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// <summary> @@ -1072,11 +1113,13 @@ resources: identifier: pwsh ")); + ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData(); + Dictionary<string, ConfigurationEnvironmentData> expectedEnvironments = new Dictionary<string, ConfigurationEnvironmentData>(); expectedEnvironments.Add("first", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh" }); expectedEnvironments.Add("second", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted, ProcessorIdentifier = "pwsh" }); - this.ValidateEnvironments(openResult, expectedEnvironments); + this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); @@ -1087,7 +1130,7 @@ resources: // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); - this.ValidateEnvironments(serializedSetResult, expectedEnvironments); + this.ValidateEnvironments(serializedSetResult, setEnvironment, expectedEnvironments); } /// <summary> @@ -1135,16 +1178,17 @@ resources: Dictionary<string, string> environmentProperties = new Dictionary<string, string>(); environmentProperties.Add("a", "b"); + ConfigurationEnvironmentData setEnvironment = new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }; Dictionary<string, ConfigurationEnvironmentData> expectedEnvironments = new Dictionary<string, ConfigurationEnvironmentData>(); - expectedEnvironments.Add("non-group", new ConfigurationEnvironmentData() { Context = SecurityContext.Elevated, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }); - expectedEnvironments.Add("group", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }); + expectedEnvironments.Add("non-group", new ConfigurationEnvironmentData()); + expectedEnvironments.Add("group", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted }); Dictionary<string, ConfigurationEnvironmentData> groupExpectedEnvironments = new Dictionary<string, ConfigurationEnvironmentData>(); - groupExpectedEnvironments.Add("inherit", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted, ProcessorIdentifier = "pwsh", ProcessorProperties = environmentProperties }); - groupExpectedEnvironments.Add("override", new ConfigurationEnvironmentData() { Context = SecurityContext.Restricted, ProcessorIdentifier = "not-pwsh" }); + groupExpectedEnvironments.Add("inherit", new ConfigurationEnvironmentData()); + groupExpectedEnvironments.Add("override", new ConfigurationEnvironmentData() { ProcessorIdentifier = "not-pwsh" }); - this.ValidateEnvironments(openResult, expectedEnvironments, "group", groupExpectedEnvironments); + this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments, "group", groupExpectedEnvironments); // Serialize set. InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream(); @@ -1155,15 +1199,17 @@ resources: // Reopen configuration set from serialized string and verify values. OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput)); - this.ValidateEnvironments(openResult, expectedEnvironments, "group", groupExpectedEnvironments); + this.ValidateEnvironments(openResult, setEnvironment, expectedEnvironments, "group", groupExpectedEnvironments); } - private void ValidateEnvironments(OpenConfigurationSetResult openResult, Dictionary<string, ConfigurationEnvironmentData> expectedEnvironments, string? groupToCheck = null, Dictionary<string, ConfigurationEnvironmentData>? groupExpectedEnvironments = null) + private void ValidateEnvironments(OpenConfigurationSetResult openResult, ConfigurationEnvironmentData setEnvironment, Dictionary<string, ConfigurationEnvironmentData> expectedEnvironments, string? groupToCheck = null, Dictionary<string, ConfigurationEnvironmentData>? groupExpectedEnvironments = null) { Assert.Null(openResult.ResultCode); Assert.NotNull(openResult.Set); ConfigurationSet configurationSet = openResult.Set; + this.ValidateEnvironment(setEnvironment, configurationSet.Environment); + var units = configurationSet.Units; this.ValidateEnvironments(units, expectedEnvironments, groupToCheck, groupExpectedEnvironments); } @@ -1175,10 +1221,7 @@ resources: { ConfigurationEnvironmentData? expectedEnvironment = null; Assert.True(expectedEnvironments.TryGetValue(unit.Identifier, out expectedEnvironment)); - Assert.NotNull(expectedEnvironment); - Assert.Equal(expectedEnvironment.Context, unit.Environment.Context); - Assert.Equal(expectedEnvironment.ProcessorIdentifier, unit.Environment.ProcessorIdentifier); - Assert.True(expectedEnvironment.PropertiesEqual(unit.Environment.ProcessorProperties)); + this.ValidateEnvironment(expectedEnvironment, unit.Environment); if (unit.Identifier == groupToCheck) { @@ -1190,6 +1233,15 @@ resources: } } + private void ValidateEnvironment(ConfigurationEnvironmentData? expectedEnvironment, ConfigurationEnvironment? actualEnvironment) + { + Assert.NotNull(expectedEnvironment); + Assert.NotNull(actualEnvironment); + Assert.Equal(expectedEnvironment.Context, actualEnvironment.Context); + Assert.Equal(expectedEnvironment.ProcessorIdentifier, actualEnvironment.ProcessorIdentifier); + Assert.True(expectedEnvironment.PropertiesEqual(actualEnvironment.ProcessorProperties)); + } + private void ValidateSecurityContexts(OpenConfigurationSetResult openResult, Dictionary<string, SecurityContext> expectedContexts) { Assert.Null(openResult.ResultCode); @@ -1239,7 +1291,7 @@ parameters: Assert.Equal(secure, parameters[0].IsSecure); Assert.NotNull(expectedValue); - this.VerifyObject(expectedValue, parameters[0].DefaultValue); + this.VerifyObject(type, expectedValue, parameters[0].DefaultValue); } else { @@ -1270,7 +1322,7 @@ parameters: Assert.True(values.ContainsKey(expectation.Key), $"Not Found {expectation.Key}"); object value = values[expectation.Key]; - this.VerifyObject(expectation.Value, value); + this.VerifyObject(expectation.Key, expectation.Value, value); } } @@ -1278,6 +1330,20 @@ parameters: { Assert.NotNull(strings); Assert.Equal(expected.Length, strings.Count); + + foreach (var expectation in expected) + { + bool found = false; + foreach (var value in strings) + { + if (!found) + { + found = expectation == value; + } + } + + Assert.True(found, $"Did not find {expectation} in string array"); + } } private void VerifyParameter(ConfigurationParameter parameter, string name, Windows.Foundation.PropertyType type, bool secure, object? defaultValue = null) @@ -1285,10 +1351,10 @@ parameters: Assert.Equal(name, parameter.Name); Assert.Equal(type, parameter.Type); Assert.Equal(secure, parameter.IsSecure); - this.VerifyObject(defaultValue, parameter.DefaultValue); + this.VerifyObject(name, defaultValue, parameter.DefaultValue); } - private void VerifyObject(object? expectedValue, object? actualValue) + private void VerifyObject(string name, object? expectedValue, object? actualValue) { if (expectedValue != null) { @@ -1297,19 +1363,20 @@ parameters: switch (expectedValue) { case int i: - Assert.Equal(i, (int)(long)actualValue); + Assert.True(i == (int)(long)actualValue, $"{name}: expected[{i}], actual[{(int)(long)actualValue}]"); break; case string s: - Assert.Equal(s, (string)actualValue); + Assert.True(s == (string)actualValue, $"{name}: expected[{s}], actual[{(string)actualValue}]"); break; case bool b: - Assert.Equal(b, (bool)actualValue); + Assert.True(b == (bool)actualValue, $"{name}: expected[{b}], actual[{(bool)actualValue}]"); break; case ValueSet v: - Assert.True(v.ContentEquals(actualValue.As<ValueSet>())); + var actualValueSet = actualValue.As<ValueSet>(); + Assert.True(v.ContentEquals(actualValueSet), $"ValueSets not equal: {name}\n---expected---:\n{v.ToYaml()}\n---actual---:\n{actualValueSet.ToYaml()}"); break; default: - Assert.Fail($"Add expected type `{expectedValue.GetType().Name}` to switch statement."); + Assert.Fail($"Add expected type `{expectedValue.GetType().Name}` to switch statement for {name}."); break; } } diff --git a/src/Microsoft.Management.Configuration/ConfigurationSet.cpp b/src/Microsoft.Management.Configuration/ConfigurationSet.cpp @@ -283,6 +283,16 @@ namespace winrt::Microsoft::Management::Configuration::implementation m_schemaUri = value; } + Configuration::ConfigurationEnvironment ConfigurationSet::Environment() + { + return *m_environment; + } + + implementation::ConfigurationEnvironment& ConfigurationSet::EnvironmentInternal() + { + return *m_environment; + } + std::vector<Configuration::ConfigurationEnvironment> ConfigurationSet::GetUnitEnvironmentsInternal() { std::vector<impl::EnvironmentData> uniqueEnvironments; diff --git a/src/Microsoft.Management.Configuration/ConfigurationSet.h b/src/Microsoft.Management.Configuration/ConfigurationSet.h @@ -2,6 +2,7 @@ // Licensed under the MIT License. #pragma once #include "ConfigurationSet.g.h" +#include "ConfigurationEnvironment.h" #include "ConfigurationSetChangeData.h" #include "ConfigurationStatus.h" #include <winget/ILifetimeWatcher.h> @@ -25,6 +26,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation void Units(std::vector<Configuration::ConfigurationUnit>&& units); void Parameters(std::vector<Configuration::ConfigurationParameter>&& value); void ConfigurationSetChange(com_ptr<ConfigurationSetChangeData>& data, const std::optional<guid>& unitInstanceIdentifier); + implementation::ConfigurationEnvironment& EnvironmentInternal(); std::vector<Configuration::ConfigurationEnvironment> GetUnitEnvironmentsInternal(); #endif @@ -68,6 +70,8 @@ namespace winrt::Microsoft::Management::Configuration::implementation Windows::Foundation::Uri SchemaUri(); void SchemaUri(const Windows::Foundation::Uri& value); + Configuration::ConfigurationEnvironment Environment(); + Windows::Foundation::Collections::IVector<Configuration::ConfigurationEnvironment> GetUnitEnvironments(); HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); @@ -91,6 +95,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation Windows::Foundation::Uri m_schemaUri = nullptr; std::string m_inputHash; std::shared_ptr<ConfigurationStatus::SetChangeRegistration> m_setChangeRegistration; + com_ptr<implementation::ConfigurationEnvironment> m_environment{ make_self<implementation::ConfigurationEnvironment>() }; #endif }; } diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.cpp @@ -23,14 +23,13 @@ namespace winrt::Microsoft::Management::Configuration::implementation CHECK_ERROR(ParseValueSet(m_document, ConfigurationField::Metadata, false, result->Metadata())); - m_setEnvironment = make_self<implementation::ConfigurationEnvironment>(); - CHECK_ERROR(ExtractEnvironmentFromMetadata(result->Metadata(), *m_setEnvironment)); + CHECK_ERROR(ExtractEnvironmentFromMetadata(result->Metadata(), result->EnvironmentInternal())); CHECK_ERROR(ParseParameters(result)); CHECK_ERROR(ParseValueSet(m_document, ConfigurationField::Variables, false, result->Variables())); std::vector<Configuration::ConfigurationUnit> units; - CHECK_ERROR(ParseConfigurationUnitsFromField(m_document, ConfigurationField::Resources, *m_setEnvironment, units)); + CHECK_ERROR(ParseConfigurationUnitsFromField(m_document, ConfigurationField::Resources, units)); result->Units(std::move(units)); result->SchemaVersion(GetSchemaVersion()); @@ -177,17 +176,17 @@ namespace winrt::Microsoft::Management::Configuration::implementation } } - void ConfigurationSetParser_0_3::ParseConfigurationUnitsFromField(const Node& document, ConfigurationField field, const ConfigurationEnvironment& defaultEnvironment, std::vector<Configuration::ConfigurationUnit>& result) + void ConfigurationSetParser_0_3::ParseConfigurationUnitsFromField(const Node& document, ConfigurationField field, std::vector<Configuration::ConfigurationUnit>& result) { ParseSequence(document, field, false, Node::Type::Mapping, [&](const Node& item) { auto configurationUnit = make_self<ConfigurationUnit>(); - ParseConfigurationUnit(configurationUnit.get(), item, defaultEnvironment); + ParseConfigurationUnit(configurationUnit.get(), item); result.emplace_back(*configurationUnit); }); } - void ConfigurationSetParser_0_3::ParseConfigurationUnit(ConfigurationUnit* unit, const Node& unitNode, const ConfigurationEnvironment& defaultEnvironment) + void ConfigurationSetParser_0_3::ParseConfigurationUnit(ConfigurationUnit* unit, const Node& unitNode) { // Set unknown intent as the new schema doesn't express it directly unit->Intent(ConfigurationUnitIntent::Unknown); @@ -195,7 +194,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Name, true, unit, &ConfigurationUnit::Identifier)); CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Type, true, unit, &ConfigurationUnit::Type)); CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Metadata, false, unit->Metadata())); - CHECK_ERROR(ExtractEnvironmentForUnit(unit, defaultEnvironment)); + CHECK_ERROR(ExtractEnvironmentForUnit(unit)); CHECK_ERROR(ValidateType(unit, unitNode, ConfigurationField::Type, false, true)); CHECK_ERROR(GetStringArrayForUnit(unitNode, ConfigurationField::DependsOn, false, unit, &ConfigurationUnit::Dependencies)); @@ -212,7 +211,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation if (propertiesNode) { std::vector<Configuration::ConfigurationUnit> units; - CHECK_ERROR(ParseConfigurationUnitsFromField(propertiesNode, ConfigurationField::Resources, unit->EnvironmentInternal(), units)); + CHECK_ERROR(ParseConfigurationUnitsFromField(propertiesNode, ConfigurationField::Resources, units)); unit->Units(std::move(units)); } } @@ -294,13 +293,10 @@ namespace winrt::Microsoft::Management::Configuration::implementation } } - void ConfigurationSetParser_0_3::ExtractEnvironmentForUnit(ConfigurationUnit* unit, const ConfigurationEnvironment& defaultEnvironment) + void ConfigurationSetParser_0_3::ExtractEnvironmentForUnit(ConfigurationUnit* unit) { - auto& environmentInternal = unit->EnvironmentInternal(); - environmentInternal.DeepCopy(defaultEnvironment); - // Get unnested security context - ExtractSecurityContext(unit, defaultEnvironment.Context()); + ExtractSecurityContext(unit); // Get nested environment ExtractEnvironmentFromMetadata(unit->Metadata(), unit->EnvironmentInternal()); diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.h b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.h @@ -53,8 +53,8 @@ namespace winrt::Microsoft::Management::Configuration::implementation ConfigurationParameter* parameter, void(ConfigurationParameter::* propertyFunction)(const Windows::Foundation::IInspectable& value)); - void ParseConfigurationUnitsFromField(const AppInstaller::YAML::Node& document, ConfigurationField field, const ConfigurationEnvironment& defaultEnvironment, std::vector<Configuration::ConfigurationUnit>& result); - virtual void ParseConfigurationUnit(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode, const ConfigurationEnvironment& defaultEnvironment); + void ParseConfigurationUnitsFromField(const AppInstaller::YAML::Node& document, ConfigurationField field, std::vector<Configuration::ConfigurationUnit>& result); + virtual void ParseConfigurationUnit(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode); // Determines if the given unit should be converted to a group. bool ShouldConvertToGroup(ConfigurationUnit* unit); @@ -63,10 +63,9 @@ namespace winrt::Microsoft::Management::Configuration::implementation void ExtractEnvironmentFromMetadata(const Windows::Foundation::Collections::ValueSet& metadata, ConfigurationEnvironment& targetEnvironment); // Extracts the environment for a unit. - void ExtractEnvironmentForUnit(ConfigurationUnit* unit, const ConfigurationEnvironment& defaultEnvironment); + void ExtractEnvironmentForUnit(ConfigurationUnit* unit); AppInstaller::YAML::Node m_document; - com_ptr<ConfigurationEnvironment> m_setEnvironment; }; std::optional<std::pair<Windows::Foundation::PropertyType, bool>> ParseWindowsFoundationPropertyType(std::string_view value); diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.cpp @@ -77,6 +77,13 @@ namespace winrt::Microsoft::Management::Configuration::implementation { emitter << BeginMap; + WriteValues(emitter, WriteYamlValue); + + emitter << EndMap; + } + + void WriteValues(AppInstaller::YAML::Emitter& emitter, void(*WriteYamlValue)(AppInstaller::YAML::Emitter& emitter, const winrt::Windows::Foundation::IInspectable& value)) + { if (m_valueSet) { for (const auto& [key, value] : m_valueSet) @@ -100,8 +107,6 @@ namespace winrt::Microsoft::Management::Configuration::implementation WriteYamlValue(emitter, override.second); } } - - emitter << EndMap; } private: @@ -171,6 +176,12 @@ namespace winrt::Microsoft::Management::Configuration::implementation writer.Write(emitter, WriteYamlValue); } + void ConfigurationSetSerializer::WriteYamlValueSetValues(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet, const std::vector<std::pair<ConfigurationField, Windows::Foundation::IInspectable>>& overrides) + { + anon::ValueSetWriter writer{ valueSet, overrides }; + writer.WriteValues(emitter, WriteYamlValue); + } + void ConfigurationSetSerializer::WriteYamlStringArray(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::IVector<hstring>& values) { emitter << BeginSeq; diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.h b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.h @@ -36,6 +36,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation ConfigurationSetSerializer() = default; static void WriteYamlValueSet(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet, const std::vector<std::pair<ConfigurationField, Windows::Foundation::IInspectable>>& overrides = {}); + static void WriteYamlValueSetValues(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet, const std::vector<std::pair<ConfigurationField, Windows::Foundation::IInspectable>>& overrides = {}); static void WriteYamlValueSetIfNotEmpty(AppInstaller::YAML::Emitter& emitter, ConfigurationField key, const Windows::Foundation::Collections::ValueSet& valueSet, const std::vector<std::pair<ConfigurationField, Windows::Foundation::IInspectable>>& overrides = {}); static void WriteYamlValueSetAsArray(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSetArray); diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_3.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_3.cpp @@ -112,12 +112,10 @@ namespace winrt::Microsoft::Management::Configuration::implementation void AddEnvironmentToMetadata( Windows::Foundation::Collections::ValueSet& metadata, - const Configuration::ConfigurationEnvironment& environment, - const Configuration::ConfigurationEnvironment& commonEnvironment) + const Configuration::ConfigurationEnvironment& environment) { AddEnvironmentToMetadata(metadata, - environment.Context(), environment.ProcessorIdentifier(), environment.ProcessorProperties(), - commonEnvironment.Context(), commonEnvironment.ProcessorIdentifier(), commonEnvironment.ProcessorProperties()); + environment.Context(), environment.ProcessorIdentifier(), environment.ProcessorProperties()); } } @@ -131,8 +129,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation // Prepare an override if necessary Collections::ValueSet wingetMetadataOverride = nullptr; - auto commonEnvironment = ConfigurationEnvironment::CalculateCommonEnvironment(configurationSet->GetUnitEnvironmentsInternal()); - AddEnvironmentToMetadata(wingetMetadataOverride, commonEnvironment); + AddEnvironmentToMetadata(wingetMetadataOverride, configurationSet->EnvironmentInternal()); WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Metadata, configurationSet->Metadata(), { @@ -142,7 +139,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation WriteYamlParameters(emitter, configurationSet->Parameters()); WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Variables, configurationSet->Variables()); - WriteYamlConfigurationUnits(emitter, configurationSet->Units(), *commonEnvironment); + WriteYamlConfigurationUnits(emitter, configurationSet->Units()); emitter << EndMap; @@ -220,8 +217,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation void ConfigurationSetSerializer_0_3::WriteYamlConfigurationUnits( AppInstaller::YAML::Emitter& emitter, - const Windows::Foundation::Collections::IVector<Configuration::ConfigurationUnit>& values, - const Configuration::ConfigurationEnvironment& commonEnvironment) + const Windows::Foundation::Collections::IVector<Configuration::ConfigurationUnit>& values) { emitter << Key << GetConfigurationFieldName(ConfigurationField::Resources); @@ -242,7 +238,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation // Prepare an override if necessary Collections::ValueSet wingetMetadataOverride = nullptr; Configuration::ConfigurationEnvironment unitEnvironment = unit.Environment(); - AddEnvironmentToMetadata(wingetMetadataOverride, unitEnvironment, commonEnvironment); + AddEnvironmentToMetadata(wingetMetadataOverride, unitEnvironment); WriteYamlValueSetIfNotEmpty(emitter, ConfigurationField::Metadata, unit.Metadata(), { { ConfigurationField::WingetMetadataRoot, wingetMetadataOverride } }); @@ -272,7 +268,12 @@ namespace winrt::Microsoft::Management::Configuration::implementation emitter << Key << GetConfigurationFieldName(ConfigurationField::Properties); emitter << BeginMap; - WriteYamlConfigurationUnits(emitter, groupUnits, unitEnvironment); + // Write everything but the resources + WriteYamlValueSetValues(emitter, unit.Settings(), + { { ConfigurationField::Resources, nullptr } }); + + // Write the resources from the individual units + WriteYamlConfigurationUnits(emitter, groupUnits); emitter << EndMap; } diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_3.h b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_3.h @@ -24,7 +24,6 @@ namespace winrt::Microsoft::Management::Configuration::implementation void WriteYamlParameters(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::IVector<Configuration::ConfigurationParameter>& values); void WriteYamlConfigurationUnits( AppInstaller::YAML::Emitter& emitter, - const Windows::Foundation::Collections::IVector<Configuration::ConfigurationUnit>& values, - const Configuration::ConfigurationEnvironment& commonEnvironment); + const Windows::Foundation::Collections::IVector<Configuration::ConfigurationUnit>& values); }; } diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl @@ -432,6 +432,11 @@ namespace Microsoft.Management.Configuration [contract(Microsoft.Management.Configuration.Contract, 3)] { + // The environment in which to process the configuration set. + // This defines the initial processing environment state used by the configuration system, + // and may be overridden by the processor later. + ConfigurationEnvironment Environment{ get; }; + // Gets the union of environments as defined by all of the active units within the set. Windows.Foundation.Collections.IVector<ConfigurationEnvironment> GetUnitEnvironments(); }