/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ using Amazon.Runtime.CredentialManagement; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; namespace Amazon.Runtime.CredentialManagement.Internal { /// /// Class to easily convert from Dictionary<string, string> to ProfileOptions and back. /// public class CredentialProfilePropertyMapping { private static readonly HashSet TypePropertySet = new HashSet(typeof(CredentialProfileOptions).GetProperties().Select((p) => p.Name), StringComparer.OrdinalIgnoreCase); private static readonly PropertyInfo[] CredentialProfileReflectionProperties = typeof(CredentialProfileOptions).GetProperties(); private readonly Dictionary _nameMapping; private readonly HashSet _mappedNames; public CredentialProfilePropertyMapping(Dictionary nameMapping) { if (!TypePropertySet.SetEquals(new HashSet(nameMapping.Keys, StringComparer.OrdinalIgnoreCase))) { throw new ArgumentException("The nameMapping Dictionary must contain a name mapping for each ProfileOptions property, and no additional keys."); } this._nameMapping = nameMapping; _mappedNames = new HashSet(nameMapping.Values.Where(v => !string.IsNullOrEmpty(v)), StringComparer.OrdinalIgnoreCase); } /// /// Separate the profileDictionary into its parts. /// profileDictionary = profileOptions + reservedProperties + userProperties /// /// Dictionary with everything in it /// Keys to ignore /// The resulting CredentialProfileOptions /// The properties that are left public void ExtractProfileParts(Dictionary profileDictionary, HashSet reservedKeys, out CredentialProfileOptions profileOptions, out Dictionary userProperties) { Dictionary reservedProperties; ExtractProfileParts(profileDictionary, reservedKeys, out profileOptions, out reservedProperties, out userProperties); } /// /// Separate the profileDictionary into its parts. /// profileDictionary = profileOptions + reservedProperties + userProperties /// /// Dictionary with everything in it /// Keys for the reservedKeys dictionary /// The resulting CredentialProfileOptions /// The resulting reserved properties /// The properties that are left public void ExtractProfileParts( Dictionary profileDictionary, HashSet reservedKeys, out CredentialProfileOptions profileOptions, out Dictionary reservedProperties, out Dictionary userProperties) { // profileDictionary = profileOptions + reservedProperties + userProperties // algorithm: userProperties = profileDictionary - profileOptions - reservedProperties // userProperties = profileDictionary userProperties = new Dictionary(profileDictionary); // userProperties -= profileOptions profileOptions = new CredentialProfileOptions(); foreach (var reflectionProperty in CredentialProfileReflectionProperties) { string value = null; var mappedName = _nameMapping[reflectionProperty.Name]; if (mappedName != null) { if (userProperties.TryGetValue(mappedName, out value)) { reflectionProperty.SetValue(profileOptions, value, null); userProperties.Remove(mappedName); } } } // userProperties -= reservedProperties if (reservedKeys == null) { reservedProperties = null; } else { reservedProperties = new Dictionary(); foreach (var key in reservedKeys) { string value = null; if (userProperties.TryGetValue(key, out value)) { reservedProperties.Add(key, value); userProperties.Remove(key); } } } } /// /// Validate the userProperties and then combine profileOptions, reservedProperties, and userProperties into one Dictionary. /// /// /// /// /// /// public Dictionary CombineProfileParts(CredentialProfileOptions profileOptions, HashSet reservedPropertyNames, Dictionary reservedProperties, Dictionary userProperties) { ValidateNoProfileOptionsProperties(userProperties); ValidateNoReservedProperties(reservedPropertyNames, userProperties); var profileDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var pair in Convert(profileOptions).Concat(reservedProperties).Concat(userProperties)) { profileDictionary.Add(pair.Key, pair.Value); } return profileDictionary; } /// /// Make sure the userProperties dictionary doesn't contain any keys that are reserved. /// Check is case-insensitive for added safety. /// /// /// private static void ValidateNoReservedProperties(HashSet reservedPropertyNames, Dictionary userProperties) { if (userProperties != null) { List reservedKeys = new List(); foreach (var key in reservedPropertyNames) { if (userProperties.Keys.Contains(key, StringComparer.OrdinalIgnoreCase)) { reservedKeys.Add(key); } } if (reservedKeys.Count > 0) throw new ArgumentException("The profile properties cannot contain reserved names as keys: " + string.Join(" or ", reservedKeys.ToArray())); } } /// /// Make sure the userProperties dictionary doesn't contain any keys that /// overlap with the names of mapped names for CredentialProfileOptions property names. /// Check is case-insensitive for added safety. /// /// private void ValidateNoProfileOptionsProperties(Dictionary userProperties) { if (userProperties != null) { foreach (var key in userProperties.Keys) { if (_mappedNames.Contains(key, StringComparer.OrdinalIgnoreCase)) { throw new ArgumentException("The profile properties dictionary cannot contain a key named " + key + " because it is in the name mapping dictionary."); } } } } private Dictionary Convert(CredentialProfileOptions profileOptions) { var dictionary = new Dictionary(); // if profileOptions.IsEmpty then leave all the credentials keys untouched if (!profileOptions.IsEmpty) { var properties = typeof(CredentialProfileOptions).GetProperties(); // ensure repeatable order Array.Sort(properties.Select((p) => p.Name).ToArray(), properties); foreach (var property in properties) { var value = (string)property.GetValue(profileOptions, null); if (string.IsNullOrEmpty(value)) value = null; if (_nameMapping[property.Name] != null) { dictionary.Add(_nameMapping[property.Name], value); } } } return dictionary; } } }