winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

ConfigurationSetParser.cpp (24742B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "ConfigurationSetParser.h"
      5 #include "ParsingMacros.h"
      6 #include "ArgumentValidation.h"
      7 
      8 #include <AppInstallerErrors.h>
      9 #include <AppInstallerLogging.h>
     10 #include <AppInstallerStrings.h>
     11 #include <AppInstallerVersions.h>
     12 
     13 #include "ConfigurationSetUtilities.h"
     14 #include "ConfigurationSetParserError.h"
     15 #include "ConfigurationSetParser_0_1.h"
     16 #include "ConfigurationSetParser_0_2.h"
     17 #include "ConfigurationSetParser_0_3.h"
     18 
     19 using namespace AppInstaller::Utility;
     20 using namespace AppInstaller::YAML;
     21 
     22 namespace winrt::Microsoft::Management::Configuration::implementation
     23 {
     24     namespace
     25     {
     26         struct SchemaVersionAndUri
     27         {
     28             std::string_view Version;
     29             std::wstring_view VersionWide;
     30             std::string_view Uri;
     31             std::wstring_view UriWide;
     32         };
     33 
     34 #define SCHEMA_VERSION_MAP_ITEM(_version_,_uri_) _version_, TEXT(_version_), _uri_, TEXT(_uri_)
     35 
     36         // Please keep in sorted order with the highest version last.
     37         // Duplicate URIs are supported, but duplicate versions are not. The highest version for a URI will be the one mapped to, the lower versions will be aliases.
     38         SchemaVersionAndUri SchemaVersionAndUriMap[] =
     39         {
     40             { SCHEMA_VERSION_MAP_ITEM("0.1", "") },
     41             { SCHEMA_VERSION_MAP_ITEM("0.2", "") },
     42             { SCHEMA_VERSION_MAP_ITEM("0.3", "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json") },
     43         };
     44 
     45         Windows::Foundation::IInspectable GetIInspectableFromNode(const Node& node);
     46 
     47         // Fills the ValueSet from the given node, which is assumed to be a map.
     48         void FillValueSetFromMap(const Node& mapNode, const Windows::Foundation::Collections::ValueSet& valueSet)
     49         {
     50             for (const auto& mapItem : mapNode.Mapping())
     51             {
     52                 // Insert returns true if it replaces an existing key, and that indicates an invalid map.
     53                 THROW_HR_IF(WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE, valueSet.Insert(mapItem.first.as<std::wstring>(), GetIInspectableFromNode(mapItem.second)));
     54             }
     55         }
     56 
     57         // Returns the appropriate IPropertyValue for the given node, which is assumed to be a scalar.
     58         Windows::Foundation::IInspectable GetPropertyValueFromScalar(const Node& node)
     59         {
     60             ::winrt::Windows::Foundation::IInspectable result;
     61 
     62             switch (node.GetTagType())
     63             {
     64             case Node::TagType::Null:
     65                 return Windows::Foundation::PropertyValue::CreateEmpty();
     66             case Node::TagType::Bool:
     67                 return Windows::Foundation::PropertyValue::CreateBoolean(node.as<bool>());
     68             case Node::TagType::Str:
     69                 return Windows::Foundation::PropertyValue::CreateString(node.as<std::wstring>());
     70             case Node::TagType::Int:
     71                 return Windows::Foundation::PropertyValue::CreateInt64(node.as<int64_t>());
     72             case Node::TagType::Float:
     73                 THROW_HR(E_NOTIMPL);
     74             case Node::TagType::Timestamp:
     75                 THROW_HR(E_NOTIMPL);
     76             default:
     77                 THROW_HR(E_UNEXPECTED);
     78             }
     79         }
     80 
     81         // Returns the appropriate IPropertyValue for the given node, which is assumed to be a scalar.
     82         Windows::Foundation::IInspectable GetPropertyValueFromSequence(const Node& sequenceNode)
     83         {
     84             Windows::Foundation::Collections::ValueSet result;
     85             size_t index = 0;
     86 
     87             for (const Node& sequenceItem : sequenceNode.Sequence())
     88             {
     89                 std::wostringstream strstr;
     90                 strstr << index++;
     91                 result.Insert(strstr.str(), GetIInspectableFromNode(sequenceItem));
     92             }
     93 
     94             result.Insert(L"treatAsArray", Windows::Foundation::PropertyValue::CreateBoolean(true));
     95             return result;
     96         }
     97 
     98         // Returns the appropriate IInspectable for the given node.
     99         Windows::Foundation::IInspectable GetIInspectableFromNode(const Node& node)
    100         {
    101             ::winrt::Windows::Foundation::IInspectable result;
    102 
    103             switch (node.GetType())
    104             {
    105             case Node::Type::Invalid:
    106             case Node::Type::None:
    107                 // Leave value as null
    108                 break;
    109             case Node::Type::Scalar:
    110                 result = GetPropertyValueFromScalar(node);
    111                 break;
    112             case Node::Type::Sequence:
    113                 result = GetPropertyValueFromSequence(node);
    114                 break;
    115             case Node::Type::Mapping:
    116             {
    117                 Windows::Foundation::Collections::ValueSet subset;
    118                 FillValueSetFromMap(node, subset);
    119                 result = std::move(subset);
    120             }
    121             break;
    122             default:
    123                 THROW_HR(E_UNEXPECTED);
    124             }
    125 
    126             return result;
    127         }
    128 
    129         // Contains the qualified resource name information.
    130         struct QualifiedResourceName
    131         {
    132             QualifiedResourceName(hstring input)
    133             {
    134                 std::wstring_view inputView = input;
    135                 size_t pos = inputView.find('/');
    136 
    137                 if (pos != std::wstring_view::npos)
    138                 {
    139                     Module = inputView.substr(0, pos);
    140                     Resource = inputView.substr(pos + 1);
    141                 }
    142                 else
    143                 {
    144                     Resource = input;
    145                 }
    146             }
    147 
    148             hstring Module;
    149             hstring Resource;
    150         };
    151     }
    152 
    153     std::unique_ptr<ConfigurationSetParser> ConfigurationSetParser::Create(std::string_view input)
    154     {
    155         AICLI_LOG_LARGE_STRING(Config, Verbose, << "Parsing configuration set:", input);
    156 
    157         Node document;
    158         std::string documentError;
    159         Mark documentErrorMark;
    160 
    161         try
    162         {
    163             document = Load(input);
    164         }
    165         catch (const Exception& exc)
    166         {
    167             documentError = exc.what();
    168             documentErrorMark = exc.GetMark();
    169         }
    170         CATCH_LOG();
    171 
    172         if (!document.IsMap())
    173         {
    174             AICLI_LOG(Config, Error, << "Invalid YAML: " << documentError << " at [line " << documentErrorMark.line << ", col " << documentErrorMark.column << "]");
    175             return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_INVALID_YAML, documentError, documentErrorMark);
    176         }
    177 
    178         // The schema version for parsing the rest of the document
    179         std::string schemaUriString;
    180         std::string schemaVersionString;
    181 
    182         Node& schemaNode = document[GetConfigurationFieldName(ConfigurationField::Schema)];
    183         if (schemaNode.IsScalar())
    184         {
    185             schemaUriString = schemaNode.as<std::string>();
    186             schemaVersionString = GetSchemaVersionForUri(schemaUriString);
    187             AICLI_LOG(Config, Verbose, << "Configuration schema `" << schemaNode.as<std::string>() << "` mapped to version `" << schemaVersionString << "`.");
    188         }
    189 
    190         // If we recognize the schema, use that version.
    191         // If we didn't recognize it, try using the older format.
    192         if (schemaVersionString.empty())
    193         {
    194             std::unique_ptr<ConfigurationSetParser> oldFormatError = GetSchemaVersionFromOldFormat(document, schemaVersionString);
    195 
    196             // We have no schema version at all...
    197             if (oldFormatError)
    198             {
    199                 // If the schema was provided and we didn't recognize it, make that the error.
    200                 if (schemaNode.IsScalar())
    201                 {
    202                     AICLI_LOG(Config, Error, << "Unknown configuration schema: " << schemaUriString);
    203                     return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, GetConfigurationFieldName(ConfigurationField::Schema), schemaUriString);
    204                 }
    205                 else
    206                 {
    207                     // Otherwise, this is an older format file (or neither). The proper error came back from that function.
    208                     return oldFormatError;
    209                 }
    210             }
    211         }
    212 
    213         // Create the parser based on the version selected
    214         auto result = CreateForSchemaVersion(std::move(schemaVersionString));
    215         result->SetDocument(std::move(document));
    216         return result;
    217     }
    218 
    219     std::unique_ptr<ConfigurationSetParser> ConfigurationSetParser::CreateForSchemaVersion(std::string input)
    220     {
    221         SemanticVersion schemaVersion(std::move(input));
    222 
    223         // TODO: Consider having the version/uri/type information all together in the future
    224         if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 1)
    225         {
    226             return std::make_unique<ConfigurationSetParser_0_1>();
    227         }
    228         else if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 2)
    229         {
    230             return std::make_unique<ConfigurationSetParser_0_2>();
    231         }
    232         else if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 3)
    233         {
    234             return std::make_unique<ConfigurationSetParser_0_3>();
    235         }
    236 
    237         AICLI_LOG(Config, Error, << "Unknown configuration version: " << schemaVersion.ToString());
    238         return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion), schemaVersion.ToString());
    239     }
    240 
    241     bool ConfigurationSetParser::IsRecognizedSchemaVersion(hstring value) try
    242     {
    243         SemanticVersion schemaVersion(ConvertToUTF8(value));
    244 
    245         for (const auto& item : SchemaVersionAndUriMap)
    246         {
    247             if (schemaVersion == SemanticVersion{ std::string{ item.Version } })
    248             {
    249                 return true;
    250             }
    251         }
    252 
    253         return false;
    254     }
    255     catch (...) { LOG_CAUGHT_EXCEPTION(); return false; }
    256 
    257     bool ConfigurationSetParser::IsRecognizedSchemaUri(const Windows::Foundation::Uri& value)
    258     {
    259         return !GetSchemaVersionForUri(value).empty();
    260     }
    261 
    262     Windows::Foundation::Uri ConfigurationSetParser::GetSchemaUriForVersion(hstring value)
    263     {
    264         for (const auto& item : SchemaVersionAndUriMap)
    265         {
    266             if (value == item.VersionWide)
    267             {
    268                 return item.Uri.empty() ? nullptr : Windows::Foundation::Uri{ item.UriWide };
    269             }
    270         }
    271 
    272         return nullptr;
    273     }
    274 
    275     hstring ConfigurationSetParser::GetSchemaVersionForUri(Windows::Foundation::Uri value)
    276     {
    277         // Do a reverse search in order to give the highest version back for a given URI.
    278         auto itr = std::rbegin(SchemaVersionAndUriMap);
    279         auto end = std::rend(SchemaVersionAndUriMap);
    280         for (; itr != end; ++itr)
    281         {
    282             const auto& item = *itr;
    283             if (!item.Uri.empty())
    284             {
    285                 Windows::Foundation::Uri uri{ item.UriWide };
    286                 if (value.Equals(uri))
    287                 {
    288                     return hstring{ item.VersionWide };
    289                 }
    290             }
    291         }
    292 
    293         return {};
    294     }
    295 
    296     std::string ConfigurationSetParser::GetSchemaVersionForUri(std::string_view value)
    297     {
    298         // Do a reverse search in order to give the highest version back for a given URI.
    299         auto itr = std::rbegin(SchemaVersionAndUriMap);
    300         auto end = std::rend(SchemaVersionAndUriMap);
    301         for (; itr != end; ++itr)
    302         {
    303             const auto& item = *itr;
    304             if (!item.Uri.empty())
    305             {
    306                 if (item.Uri == value)
    307                 {
    308                     return std::string{ item.Version };
    309                 }
    310             }
    311         }
    312 
    313         return {};
    314     }
    315 
    316     std::pair<hstring, Windows::Foundation::Uri> ConfigurationSetParser::LatestVersion()
    317     {
    318         auto latest = std::rbegin(SchemaVersionAndUriMap);
    319         return { hstring{ latest->VersionWide }, Windows::Foundation::Uri{ latest->UriWide } };
    320     }
    321 
    322     Windows::Foundation::Collections::ValueSet ConfigurationSetParser::ParseValueSet(std::string_view input)
    323     {
    324         Windows::Foundation::Collections::ValueSet result;
    325         FillValueSetFromMap(Load(input), result);
    326         return result;
    327     }
    328 
    329     std::vector<hstring> ConfigurationSetParser::ParseStringArray(std::string_view input)
    330     {
    331         std::vector<hstring> result;
    332         ParseSequence(Load(input), "string_array", Node::Type::Scalar, [&](const AppInstaller::YAML::Node& item)
    333         {
    334             result.emplace_back(item.as<std::wstring>());
    335         });
    336         return result;
    337     }
    338 
    339     void ConfigurationSetParser::SetError(hresult result, std::string_view field, std::string_view value, uint32_t line, uint32_t column)
    340     {
    341         AICLI_LOG(Config, Error, << "ConfigurationSetParser error: " << AppInstaller::Logging::SetHRFormat << result << " for " << field << " with value `" << value << "` at [line " << line << ", col " << column << "]");
    342         m_result = result;
    343         m_field = ConvertToUTF16(field);
    344         m_value = ConvertToUTF16(value);
    345         m_line = line;
    346         m_column = column;
    347     }
    348 
    349     void ConfigurationSetParser::SetError(hresult result, std::string_view field, const Mark& mark, std::string_view value)
    350     {
    351         SetError(result, field, value, static_cast<uint32_t>(mark.line), static_cast<uint32_t>(mark.column));
    352     }
    353 
    354     const Node& ConfigurationSetParser::GetAndEnsureField(const Node& parent, ConfigurationField field, bool required, std::optional<Node::Type> type)
    355     {
    356         const Node& fieldNode = parent[GetConfigurationFieldName(field)];
    357 
    358         if (fieldNode)
    359         {
    360             if (type && fieldNode.GetType() != type.value())
    361             {
    362                 SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(field), fieldNode.Mark());
    363             }
    364         }
    365         else if (required)
    366         {
    367             SetError(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(field));
    368         }
    369 
    370         return fieldNode;
    371     }
    372 
    373     void ConfigurationSetParser::EnsureFieldAbsent(const Node& parent, ConfigurationField field)
    374     {
    375         const Node& fieldNode = parent[GetConfigurationFieldName(field)];
    376 
    377         if (fieldNode)
    378         {
    379             SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, GetConfigurationFieldName(field), fieldNode.Mark(), fieldNode.as<std::string>());
    380         }
    381     }
    382 
    383     void ConfigurationSetParser::ParseValueSet(const Node& node, ConfigurationField field, bool required, const Windows::Foundation::Collections::ValueSet& valueSet)
    384     {
    385         const Node& mapNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Mapping));
    386 
    387         if (mapNode)
    388         {
    389             FillValueSetFromMap(mapNode, valueSet);
    390         }
    391     }
    392 
    393     void ConfigurationSetParser::ParseMapping(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, AppInstaller::YAML::Node::Type elementType, std::function<void(std::string, const AppInstaller::YAML::Node&)> operation)
    394     {
    395         const Node& mapNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Mapping));
    396         if (!mapNode)
    397         {
    398             return;
    399         }
    400 
    401         std::ostringstream strstr;
    402         strstr << GetConfigurationFieldName(field);
    403         size_t index = 0;
    404 
    405         for (const auto& mapItem : mapNode.Mapping())
    406         {
    407             std::string name = mapItem.first.as<std::string>();
    408             if (name.empty())
    409             {
    410                 strstr << '[' << index << ']';
    411                 FIELD_VALUE_ERROR(strstr.str(), name, mapItem.first.Mark());
    412             }
    413 
    414             if (mapItem.second.GetType() != elementType)
    415             {
    416                 strstr << '[' << index << ']';
    417                 FIELD_TYPE_ERROR(strstr.str(), mapItem.second.Mark());
    418             }
    419             index++;
    420 
    421             CHECK_ERROR(operation(std::move(name), mapItem.second));
    422         }
    423     }
    424 
    425     void ConfigurationSetParser::ParseSequence(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, std::optional<Node::Type> elementType, std::function<void(const AppInstaller::YAML::Node&)> operation)
    426     {
    427         const Node& sequenceNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Sequence));
    428         if (!sequenceNode)
    429         {
    430             return;
    431         }
    432 
    433         ParseSequence(sequenceNode, GetConfigurationFieldName(field), elementType, operation);
    434     }
    435 
    436     void ConfigurationSetParser::ParseSequence(const AppInstaller::YAML::Node& node, std::string_view nameForErrors, std::optional<Node::Type> elementType, std::function<void(const AppInstaller::YAML::Node&)> operation)
    437     {
    438         std::ostringstream strstr;
    439         strstr << nameForErrors;
    440         size_t index = 0;
    441 
    442         for (const Node& item : node.Sequence())
    443         {
    444             if (elementType && item.GetType() != elementType.value())
    445             {
    446                 strstr << '[' << index << ']';
    447                 FIELD_TYPE_ERROR(strstr.str(), item.Mark());
    448             }
    449             index++;
    450 
    451             CHECK_ERROR(operation(item));
    452         }
    453     }
    454 
    455     std::unique_ptr<ConfigurationSetParser> ConfigurationSetParser::GetSchemaVersionFromOldFormat(AppInstaller::YAML::Node& document, std::string& schemaVersionString)
    456     {
    457         Node& propertiesNode = document[GetConfigurationFieldName(ConfigurationField::Properties)];
    458         if (!propertiesNode)
    459         {
    460             AICLI_LOG(Config, Error, << "No properties");
    461             // Even though this is for the "older" format, if there is no properties entry then give an error for the newer format since this is probably neither.
    462             return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(ConfigurationField::Schema));
    463         }
    464         else if (!propertiesNode.IsMap())
    465         {
    466             AICLI_LOG(Config, Error, << "Invalid properties type");
    467             return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(ConfigurationField::Properties), propertiesNode.Mark());
    468         }
    469 
    470         Node& versionNode = propertiesNode[GetConfigurationFieldName(ConfigurationField::ConfigurationVersion)];
    471         if (!versionNode)
    472         {
    473             AICLI_LOG(Config, Error, << "No configuration version");
    474             return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion));
    475         }
    476         else if (!versionNode.IsScalar())
    477         {
    478             AICLI_LOG(Config, Error, << "Invalid configuration version type");
    479             return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion), versionNode.Mark());
    480         }
    481 
    482         schemaVersionString = versionNode.as<std::string>();
    483         return {};
    484     }
    485 
    486     void ConfigurationSetParser::GetStringValueForUnit(const Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value))
    487     {
    488         const Node& valueNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Scalar));
    489 
    490         if (valueNode)
    491         {
    492             hstring value{ valueNode.as<std::wstring>() };
    493             FIELD_MISSING_ERROR_IF(value.empty() && required, GetConfigurationFieldName(field));
    494 
    495             (unit->*propertyFunction)(std::move(value));
    496         }
    497     }
    498 
    499     void ConfigurationSetParser::GetStringArrayForUnit(const Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector<hstring>&& value))
    500     {
    501         std::vector<hstring> arrayValue;
    502         CHECK_ERROR(ParseSequence(node, field, required, Node::Type::Scalar, [&](const AppInstaller::YAML::Node& item)
    503             {
    504                 arrayValue.emplace_back(item.as<std::wstring>());
    505             }));
    506 
    507         if (!arrayValue.empty())
    508         {
    509             (unit->*propertyFunction)(std::move(arrayValue));
    510         }
    511     }
    512 
    513     void ConfigurationSetParser::ValidateType(ConfigurationUnit* unit, const Node& unitNode, ConfigurationField typeField, bool moveModuleNameToMetadata, bool moduleNameRequiredInType)
    514     {
    515         QualifiedResourceName qualifiedName{ unit->Type() };
    516 
    517         const Node& typeNode = CHECK_ERROR(GetAndEnsureField(unitNode, typeField, true, Node::Type::Scalar));
    518         FIELD_VALUE_ERROR_IF(qualifiedName.Resource.empty(), GetConfigurationFieldName(typeField), ConvertToUTF8(unit->Type()), typeNode.Mark());
    519 
    520         if (!qualifiedName.Module.empty())
    521         {
    522             // If the module is provided in both the resource name and the directives, ensure that it matches
    523             hstring moduleDirectiveFieldName = GetConfigurationFieldNameHString(ConfigurationField::ModuleDirective);
    524             auto moduleDirective = unit->Metadata().TryLookup(moduleDirectiveFieldName);
    525             if (moduleDirective)
    526             {
    527                 auto moduleProperty = moduleDirective.try_as<Windows::Foundation::IPropertyValue>();
    528                 FIELD_TYPE_ERROR_IF(!moduleProperty, GetConfigurationFieldName(ConfigurationField::ModuleDirective), unitNode.Mark());
    529                 FIELD_TYPE_ERROR_IF(moduleProperty.Type() != Windows::Foundation::PropertyType::String, GetConfigurationFieldName(ConfigurationField::ModuleDirective), unitNode.Mark());
    530                 hstring moduleValue = moduleProperty.GetString();
    531                 FIELD_VALUE_ERROR_IF(qualifiedName.Module != moduleValue, GetConfigurationFieldName(ConfigurationField::ModuleDirective), ConvertToUTF8(moduleValue), unitNode.Mark());
    532             }
    533             else if (moveModuleNameToMetadata)
    534             {
    535                 unit->Metadata().Insert(moduleDirectiveFieldName, Windows::Foundation::PropertyValue::CreateString(qualifiedName.Module));
    536             }
    537 
    538             if (moveModuleNameToMetadata)
    539             {
    540                 // Set the unit name to be just the resource portion
    541                 unit->Type(qualifiedName.Resource);
    542             }
    543         }
    544         else if (moduleNameRequiredInType)
    545         {
    546             FIELD_VALUE_ERROR(GetConfigurationFieldName(typeField), ConvertToUTF8(unit->Type()), typeNode.Mark());
    547         }
    548     }
    549 
    550     void ConfigurationSetParser::ParseObject(const Node& node, ConfigurationField fieldForErrors, Windows::Foundation::PropertyType type, Windows::Foundation::IInspectable& result)
    551     {
    552         try
    553         {
    554             Windows::Foundation::IInspectable object = GetIInspectableFromNode(node);
    555             FIELD_VALUE_ERROR_IF(!IsValidObjectType(object, type), GetConfigurationFieldName(fieldForErrors), node.as<std::string>(), node.Mark());
    556             result = std::move(object);
    557         }
    558         catch (...)
    559         {
    560             LOG_CAUGHT_EXCEPTION();
    561             FIELD_VALUE_ERROR(GetConfigurationFieldName(fieldForErrors), node.as<std::string>(), node.Mark());
    562         }
    563     }
    564 
    565     void ConfigurationSetParser::ExtractSecurityContext(implementation::ConfigurationUnit* unit, SecurityContext defaultContext)
    566     {
    567         THROW_HR_IF_NULL(E_POINTER, unit);
    568 
    569         ExtractSecurityContext(unit->Metadata(), unit->EnvironmentInternal(), defaultContext);
    570     }
    571 
    572     void ConfigurationSetParser::ExtractSecurityContext(Windows::Foundation::Collections::ValueSet metadata, implementation::ConfigurationEnvironment& environment, SecurityContext defaultContext)
    573     {
    574         SecurityContext computedContext = defaultContext;
    575 
    576         auto securityContext = TryLookupProperty(metadata, ConfigurationField::SecurityContextMetadata, Windows::Foundation::PropertyType::String);
    577         if (securityContext)
    578         {
    579             TryParseSecurityContext(securityContext.GetString(), computedContext);
    580             metadata.Remove(GetConfigurationFieldNameHString(ConfigurationField::SecurityContextMetadata));
    581         }
    582 
    583         environment.Context(computedContext);
    584     }
    585 }