Skip to content

Commit

Permalink
Add configuration environment (#5139)
Browse files Browse the repository at this point in the history
## Change
This change adds a `ConfigurationEnvironment` type which is a property
of a `ConfigurationUnit`. It defines the environment in which the unit
should be run. This currently encompasses two properties; the security
context and the processor.

The values are parsed from the metadata, but the object is considered
authoritative at runtime and when serializing. They are also inherited
in schema 0.3, allowing the entire set to be defined in a single
environment in it's metadata.

The code that was using the `securityContext` metadata has been updated
to use the environment property instead. It also uses the unique
environment calculation function for the set to more efficiently
determine which contexts are present.
  • Loading branch information
JohnMcPMS authored Jan 21, 2025
1 parent 030a4c5 commit e69201b
Show file tree
Hide file tree
Showing 35 changed files with 1,808 additions and 157 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ STDAPI
STGM
storeedgefd
stpkgmanvalwestustest
stringable
STRINGID
STRINGIZE
STRSAFE
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Commands/DebugCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ namespace AppInstaller::CLI
OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::TestConfigurationUnitResult>>(context);
OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::IApplyGroupMemberSettingsResult>>(context);
OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ITestSettingsResult>>(context);
OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ConfigurationEnvironment>>(context);
OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationUnitProcessorDetails2>(context);
OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IGetAllSettingsConfigurationUnitProcessor>(context);
OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationStatics2>(context);
Expand Down
48 changes: 14 additions & 34 deletions src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ namespace AppInstaller::CLI::ConfigurationRemoting
// Check for multiple integrity level requirements
bool multipleIntegrityLevels = false;
bool higherIntegrityLevelsThanCurrent = false;
for (const auto& existingUnit : m_configurationSet.Units())
for (const auto& environment : m_configurationSet.GetUnitEnvironments())
{
auto integrityLevel = GetIntegrityLevelForUnit(existingUnit);
auto integrityLevel = SecurityContextToIntegrityLevel(environment.Context());
if (integrityLevel != m_currentIntegrityLevel)
{
multipleIntegrityLevels = true;
Expand Down Expand Up @@ -183,9 +183,9 @@ namespace AppInstaller::CLI::ConfigurationRemoting
std::call_once(m_createUnitSetProcessorsOnce,
[&]()
{
for (const auto& existingUnit : m_configurationSet.Units())
for (const auto& environment : m_configurationSet.GetUnitEnvironments())
{
Security::IntegrityLevel requiredIntegrityLevel = GetIntegrityLevelForUnit(existingUnit);
Security::IntegrityLevel requiredIntegrityLevel = SecurityContextToIntegrityLevel(environment.Context());

if (m_setProcessors.find(requiredIntegrityLevel) == m_setProcessors.end())
{
Expand All @@ -212,16 +212,13 @@ namespace AppInstaller::CLI::ConfigurationRemoting

private:
// Converts the string representation of SecurityContext to the integrity level
Security::IntegrityLevel SecurityContextToIntegrityLevel(winrt::hstring securityContext)
Security::IntegrityLevel SecurityContextToIntegrityLevel(SecurityContext securityContext)
{
std::wstring securityContextLower = Utility::ToLower(securityContext);

if (securityContextLower == L"elevated")
{
return Security::IntegrityLevel::High;
}
else if (securityContextLower == L"restricted")
switch (securityContext)
{
case SecurityContext::Current:
return m_currentIntegrityLevel;
case SecurityContext::Restricted:
#ifndef AICLI_DISABLE_TEST_HOOKS
if (m_enableRestrictedIntegrityLevel)
{
Expand All @@ -236,34 +233,17 @@ namespace AppInstaller::CLI::ConfigurationRemoting
// Technically this means the default level of the user token, so if UAC is disabled it would be the only integrity level (aka current).
// return Security::IntegrityLevel::Medium;
}
case SecurityContext::Elevated:
return Security::IntegrityLevel::High;
default:
THROW_WIN32(ERROR_NOT_SUPPORTED);
}
else if (securityContextLower == L"current")
{
return m_currentIntegrityLevel;
}

THROW_WIN32(ERROR_NOT_SUPPORTED);
}

// Gets the integrity level that the given unit should be run at
Security::IntegrityLevel GetIntegrityLevelForUnit(const ConfigurationUnit& unit)
{
// Support for 0.2 schema via metadata value
// TODO: Support case-insensitive lookup by iteration
auto unitMetadata = unit.Metadata();
auto securityContext = unitMetadata.TryLookup(L"securityContext");
if (securityContext)
{
auto securityContextProperty = securityContext.try_as<IPropertyValue>();
if (securityContextProperty && securityContextProperty.Type() == PropertyType::String)
{
return SecurityContextToIntegrityLevel(securityContextProperty.GetString());
}
}

// TODO: Support for 0.3 schema will require a group processor wrapper

return m_currentIntegrityLevel;
return SecurityContextToIntegrityLevel(unit.Environment().Context());
}

// Serializes the set properties to be sent to the remote server
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLIPackage/Package.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
<Interface Name="Windows.Foundation.Collections.IIterable`1&lt;Microsoft.Management.Configuration.TestConfigurationUnitResult&gt;" InterfaceId="73848262-86D4-5FFC-8353-8408C4E649DE" />
<Interface Name="Windows.Foundation.Collections.IIterable`1&lt;Microsoft.Management.Configuration.IApplyGroupMemberSettingsResult&gt;" InterfaceId="5086070C-F468-5B00-8352-50FB420BA8B0" />
<Interface Name="Windows.Foundation.Collections.IIterable`1&lt;Microsoft.Management.Configuration.ITestSettingsResult&gt;" InterfaceId="2D28E6AA-7036-5D78-9B58-9456F1E332FE" />
<Interface Name="Windows.Foundation.Collections.IIterable`1&lt;Microsoft.Management.Configuration.ConfigurationEnvironment&gt;" InterfaceId="47B18106-976B-5532-8E81-F58D304DFA43" />
<Interface Name="Microsoft.Management.Configuration.IConfigurationUnitProcessorDetails2" InterfaceId="E89623ED-76E2-5145-B920-D09659554E35" />
<Interface Name="Microsoft.Management.Configuration.IGetAllSettingsConfigurationUnitProcessor" InterfaceId="72EB8304-D8D3-57D4-9940-7C1C4AD8C40C" />
<Interface Name="Microsoft.Management.Configuration.IConfigurationStatics2" InterfaceId="540BE073-F2EF-5375-83AA-8E23086B0669" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// -----------------------------------------------------------------------------
// <copyright file="DictionaryExtensions.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
// -----------------------------------------------------------------------------

namespace Microsoft.Management.Configuration.Processor.Extensions
{
using System;
using System.Collections;
using System.Collections.Generic;
using Windows.Foundation.Collections;

/// <summary>
/// Extensions for dictionaries.
/// </summary>
internal static class DictionaryExtensions
{
/// <summary>
/// Performs a deep compare of the dictionaries.
/// </summary>
/// <param name="first">First dictionary.</param>
/// <param name="second">Second dictionary.</param>
/// <returns>Whether the two dictionaries equal.</returns>
internal static bool ContentEquals(this IDictionary<string, string> first, IDictionary<string, string> second)
{
if (first.Count != second.Count)
{
return false;
}

foreach (var keyValuePair in first)
{
string key = keyValuePair.Key;
if (!second.ContainsKey(key))
{
return false;
}

var firstValue = keyValuePair.Value;
var secondValue = second[key];

// Empty value check.
if (firstValue == null && secondValue == null)
{
continue;
}
else if (firstValue == null || secondValue == null)
{
return false;
}

if (firstValue != secondValue)
{
return false;
}
}

return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,14 @@ public static bool ContentEquals(this ValueSet first, ValueSet second)

foreach (var keyValuePair in first)
{
if (!second.ContainsKey(keyValuePair.Key))
string key = keyValuePair.Key;
if (!second.ContainsKey(key))
{
return false;
}

var firstValue = keyValuePair.Value;
var secondValue = second[keyValuePair.Key];
var secondValue = second[key];

// Empty value check.
if (firstValue == null && secondValue == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,25 @@ private static bool IsUnitTypeResourceName(string? schemaVersion)

private static bool ConfigurationUnitEquals(ConfigurationUnit first, ConfigurationUnit second)
{
if (first.Identifier != second.Identifier ||
first.Type != second.Type ||
first.Intent != second.Intent)
var firstIdentifier = first.Identifier;
var firstIntent = first.Intent;
var firstType = first.Type;
var secondIdentifier = second.Identifier;
var secondType = second.Type;
var secondIntent = second.Intent;

if (firstIdentifier != secondIdentifier ||
firstType != secondType ||
firstIntent != secondIntent)
{
return false;
}

var firstEnvironment = first.Environment;
var secondEnvironment = second.Environment;
if (firstEnvironment.Context != secondEnvironment.Context ||
firstEnvironment.ProcessorIdentifier != secondEnvironment.ProcessorIdentifier ||
!firstEnvironment.ProcessorProperties.ContentEquals(secondEnvironment.ProcessorProperties))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// -----------------------------------------------------------------------------
// <copyright file="ConfigurationEnvironmentData.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
// -----------------------------------------------------------------------------

namespace Microsoft.Management.Configuration.UnitTests.Helpers
{
using System;
using System.Collections.Generic;

/// <summary>
/// Contains the data defining a configuration environment.
/// </summary>
internal class ConfigurationEnvironmentData
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigurationEnvironmentData"/> class.
/// </summary>
internal ConfigurationEnvironmentData() { }

/// <summary>
/// Gets or sets the security context.
/// </summary>
internal SecurityContext Context { get; set; } = SecurityContext.Current;

/// <summary>
/// Gets or sets the processor identifier.
/// </summary>
internal string ProcessorIdentifier { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the processor properties.
/// </summary>
internal Dictionary<string, string> ProcessorProperties { get; set; } = new ();

/// <summary>
/// Applies this environment to the given unit.
/// </summary>
/// <param name="unit">The unit to apply to.</param>
/// <returns>The given unit.</returns>
internal ConfigurationUnit ApplyToUnit(ConfigurationUnit unit)
{
var environment = unit.Environment;

environment.Context = this.Context;
environment.ProcessorIdentifier = this.ProcessorIdentifier;
environment.ProcessorProperties.Clear();
foreach (var property in this.ProcessorProperties)
{
environment.ProcessorProperties.Add(property.Key, property.Value);
}

return unit;
}

/// <summary>
/// Tests whether the given properties match this object's properties.
/// </summary>
/// <param name="properties">The properties to test.</param>
/// <returns>True if the properties match; false if not.</returns>
internal bool PropertiesEqual(IDictionary<string, string> properties)
{
if (properties.Count != this.ProcessorProperties.Count)
{
return false;
}

foreach (var property in properties)
{
string? value = null;
if (!this.ProcessorProperties.TryGetValue(property.Key, out value))
{
return false;
}

if (property.Value != value)
{
return false;
}
}

return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// <copyright file="ConfigurationExtensions.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
Expand Down
Loading

0 comments on commit e69201b

Please sign in to comment.