/*
* 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;
}
}
}