/*
* 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.Internal;
using Amazon.Runtime.CredentialManagement.Internal;
using Amazon.Runtime.Internal.Settings;
using Amazon.Runtime.Internal.Util;
using Amazon.Util;
using Amazon.Util.Internal;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
namespace Amazon.Runtime.CredentialManagement
{
///
/// This class allows profiles supporting AWSCredentials to be registered with
/// the SDK so that they can later be reference by a profile name. The credential profiles will be available
/// for use in the AWS Toolkit for Visual Studio and the AWS Tools for Windows PowerShell.
///
/// The credentials are stored under the current users AppData folder encrypted using Windows Data Protection API.
///
///
/// This class is not threadsafe.
///
///
public class NetSDKCredentialsFile : ICredentialProfileStore
{
public const string DefaultProfileName = "Default";
// Values kept from ProfileManager to support backward compatibility.
private const string AWSCredentialsProfileType = "AWS";
private const string SAMLRoleProfileType = "SAML";
private const string DefaultConfigurationModeNameField = "DefaultsMode";
private const string RegionField = "Region";
private const string EndpointDiscoveryEnabledField = "EndpointDiscoveryEnabled";
private const string S3UseArnRegionField = "S3UseArnRegion";
private const string StsRegionalEndpointsField = "StsRegionalEndpoints";
private const string S3RegionalEndpointField = "S3RegionalEndpoint";
private const string S3DisableMultiRegionAccessPointsField = "S3DisableMultiRegionAccessPoints";
private const string RetryModeField = "RetryMode";
private const string MaxAttemptsField = "MaxAttempts";
private const string SsoAccountId = "sso_account_id";
private const string SsoRegion = "sso_region";
private const string SsoRoleName = "sso_role_name";
private const string SsoStartUrl = "sso_start_url";
private const string SsoSession = "sso_session";
private const string EndpointUrlField = "endpoint_url";
private const string ServicesField = "services";
private const string IgnoreConfiguredEndpointUrlsField = "ignore_configured_endpoint_urls";
private static readonly HashSet ReservedPropertyNames = new HashSet(StringComparer.OrdinalIgnoreCase)
{
SettingsConstants.DisplayNameField,
SettingsConstants.ProfileTypeField,
DefaultConfigurationModeNameField,
RegionField,
EndpointDiscoveryEnabledField,
S3UseArnRegionField,
StsRegionalEndpointsField,
S3RegionalEndpointField,
S3DisableMultiRegionAccessPointsField,
RetryModeField,
MaxAttemptsField,
SsoAccountId,
SsoRegion,
SsoRoleName,
SsoStartUrl,
EndpointUrlField,
ServicesField,
IgnoreConfiguredEndpointUrlsField
};
private static readonly CredentialProfilePropertyMapping PropertyMapping =
new CredentialProfilePropertyMapping(
new Dictionary(StringComparer.OrdinalIgnoreCase)
{
{ "AccessKey", SettingsConstants.AccessKeyField },
{ "CredentialSource", SettingsConstants.CredentialSourceField },
{ "EndpointName", SettingsConstants.EndpointNameField },
{ "ExternalID", SettingsConstants.ExternalIDField},
{ "MfaSerial", SettingsConstants.MfaSerialField},
{ "RoleArn", SettingsConstants.RoleArnField },
{ "RoleSessionName", SettingsConstants.RoleSessionName},
{ "SecretKey", SettingsConstants.SecretKeyField },
{ "SourceProfile", SettingsConstants.SourceProfileField },
{ "Token", SettingsConstants.SessionTokenField },
{ "UserIdentity", SettingsConstants.UserIdentityField },
{ "Services", SettingsConstants.Services },
{ "EndpointUrl", SettingsConstants.EndpointUrl },
// Not implemented for NetSDKCredentials. Applicable only
// for SharedCredentials
{ "CredentialProcess" , SettingsConstants.CredentialProcess },
{ "WebIdentityTokenFile", SettingsConstants.WebIdentityTokenFile },
#if !BCL35
{ nameof(CredentialProfileOptions.SsoAccountId), SsoAccountId },
{ nameof(CredentialProfileOptions.SsoRegion), SsoRegion },
{ nameof(CredentialProfileOptions.SsoRoleName), SsoRoleName },
{ nameof(CredentialProfileOptions.SsoStartUrl), SsoStartUrl },
{ nameof(CredentialProfileOptions.SsoSession), SsoSession}
#endif
}
);
private readonly NamedSettingsManager _settingsManager;
public NetSDKCredentialsFile()
{
_settingsManager = new NamedSettingsManager(SettingsConstants.RegisteredProfiles);
}
public List ListProfileNames()
{
return ListProfiles().Select(p => p.Name).ToList();
}
public List ListProfiles()
{
var profiles = new List();
foreach (var profileName in _settingsManager.ListObjectNames())
{
CredentialProfile profile = null;
if (TryGetProfile(profileName, out profile) && profile.CanCreateAWSCredentials)
{
profiles.Add(profile);
}
}
return profiles;
}
///
/// Get the profile with the name given, if it exists in this store.
///
/// The name of the profile to find.
/// The profile, if it was found, null otherwise
/// True if the profile was found, false otherwise.
public bool TryGetProfile(string profileName, out CredentialProfile profile)
{
Dictionary properties;
string uniqueKeyStr;
if (_settingsManager.TryGetObject(profileName, out uniqueKeyStr, out properties))
{
try
{
CredentialProfileOptions profileOptions;
Dictionary userProperties;
Dictionary reservedProperties;
PropertyMapping.ExtractProfileParts(
properties,
ReservedPropertyNames,
out profileOptions,
out reservedProperties,
out userProperties);
string defaultConfigurationModeName;
reservedProperties.TryGetValue(DefaultConfigurationModeNameField, out defaultConfigurationModeName);
string regionString;
RegionEndpoint region = null;
if (reservedProperties.TryGetValue(RegionField, out regionString))
{
region = RegionEndpoint.GetBySystemName(regionString);
}
Guid? uniqueKey = null;
if (!GuidUtils.TryParseNullableGuid(uniqueKeyStr, out uniqueKey))
{
profile = null;
return false;
}
string endpointDiscoveryEnabledString;
bool? endpointDiscoveryEnabled = null;
if (reservedProperties.TryGetValue(EndpointDiscoveryEnabledField, out endpointDiscoveryEnabledString))
{
bool endpointDiscoveryEnabledOut;
if (!bool.TryParse(endpointDiscoveryEnabledString, out endpointDiscoveryEnabledOut))
{
profile = null;
return false;
}
endpointDiscoveryEnabled = endpointDiscoveryEnabledOut;
}
StsRegionalEndpointsValue? stsRegionalEndpoints = null;
if (reservedProperties.TryGetValue(StsRegionalEndpointsField, out var stsRegionalEndpointsString))
{
#if BCL35
try
{
stsRegionalEndpoints = (StsRegionalEndpointsValue)Enum.Parse(typeof(StsRegionalEndpointsValue), stsRegionalEndpointsString, true);
}
catch (Exception)
{
profile = null;
return false;
}
#else
if (!Enum.TryParse(stsRegionalEndpointsString, true, out var tempStsRegionalEndpoints))
{
profile = null;
return false;
}
stsRegionalEndpoints = tempStsRegionalEndpoints;
#endif
}
string s3UseArnRegionString;
bool? s3UseArnRegion = null;
if(reservedProperties.TryGetValue(S3UseArnRegionField, out s3UseArnRegionString))
{
bool s3UseArnRegionOut;
if (!bool.TryParse(s3UseArnRegionString, out s3UseArnRegionOut))
{
profile = null;
return false;
}
s3UseArnRegion = s3UseArnRegionOut;
}
string s3DisableMultiRegionAccessPointsString;
bool? s3DisableMultiRegionAccessPoints = null;
if (reservedProperties.TryGetValue(S3DisableMultiRegionAccessPointsField, out s3DisableMultiRegionAccessPointsString))
{
bool s3DisableMultiRegionAccessPointsOut;
if (!bool.TryParse(s3DisableMultiRegionAccessPointsString, out s3DisableMultiRegionAccessPointsOut))
{
profile = null;
return false;
}
s3DisableMultiRegionAccessPoints = s3DisableMultiRegionAccessPointsOut;
}
S3UsEast1RegionalEndpointValue? s3RegionalEndpoint = null;
if (reservedProperties.TryGetValue(S3RegionalEndpointField, out var s3RegionalEndpointString))
{
#if BCL35
try
{
s3RegionalEndpoint = (S3UsEast1RegionalEndpointValue)Enum.Parse(typeof(S3UsEast1RegionalEndpointValue), s3RegionalEndpointString, true);
}
catch (Exception)
{
profile = null;
return false;
}
#else
if (!Enum.TryParse(s3RegionalEndpointString, true, out var tempS3RegionalEndpoint))
{
profile = null;
return false;
}
s3RegionalEndpoint = tempS3RegionalEndpoint;
#endif
}
RequestRetryMode? requestRetryMode = null;
if (reservedProperties.TryGetValue(RetryModeField, out var retryModeString))
{
#if BCL35
try
{
requestRetryMode = (RequestRetryMode)Enum.Parse(typeof(RequestRetryMode), retryModeString, true);
}
catch (Exception)
{
profile = null;
return false;
}
#else
if (!Enum.TryParse(retryModeString, true, out var tempRetryMode))
{
profile = null;
return false;
}
requestRetryMode = tempRetryMode;
#endif
}
int? maxAttempts = null;
if (reservedProperties.TryGetValue(MaxAttemptsField, out var maxAttemptsString))
{
if (!int.TryParse(maxAttemptsString, out var maxAttemptsTemp) || maxAttemptsTemp <= 0)
{
Logger.GetLogger(GetType()).InfoFormat("Invalid value {0} for {1} in profile {2}. A positive integer is expected.", maxAttemptsString, MaxAttemptsField, profileName);
profile = null;
return false;
}
maxAttempts = maxAttemptsTemp;
}
profile = new CredentialProfile(profileName, profileOptions)
{
UniqueKey = uniqueKey,
Properties = userProperties,
Region = region,
CredentialProfileStore = this,
DefaultConfigurationModeName = defaultConfigurationModeName,
EndpointDiscoveryEnabled = endpointDiscoveryEnabled,
StsRegionalEndpoints = stsRegionalEndpoints,
S3UseArnRegion = s3UseArnRegion,
S3RegionalEndpoint = s3RegionalEndpoint,
S3DisableMultiRegionAccessPoints = s3DisableMultiRegionAccessPoints,
RetryMode = requestRetryMode,
MaxAttempts = maxAttempts,
};
return true;
}
catch (ArgumentException)
{
profile = null;
return false;
}
}
else
{
profile = null;
return false;
}
}
///
/// Add the profile to this store, if it's valid.
///
/// The profile to add.
[SuppressMessage("Microsoft.Globalization", "CA1308", Justification = "Value is not surfaced to user. Booleans have been lowercased by SDK precedent.")]
public void RegisterProfile(CredentialProfile profile)
{
if (profile.CanCreateAWSCredentials || profile.Options.IsEmpty)
{
var reservedProperties = new Dictionary();
if (profile.CanCreateAWSCredentials)
{
// set profile type field for backward compatibility
SetProfileTypeField(reservedProperties, profile.ProfileType.Value);
}
if (profile.Region != null)
reservedProperties[RegionField] = profile.Region.SystemName;
if (profile.EndpointDiscoveryEnabled != null)
reservedProperties[EndpointDiscoveryEnabledField] = profile.EndpointDiscoveryEnabled.Value.ToString().ToLowerInvariant();
if (profile.StsRegionalEndpoints != null)
reservedProperties[StsRegionalEndpointsField] = profile.StsRegionalEndpoints.ToString().ToLowerInvariant();
if (profile.S3UseArnRegion != null)
reservedProperties[S3UseArnRegionField] = profile.S3UseArnRegion.Value.ToString().ToLowerInvariant();
if (profile.S3RegionalEndpoint != null)
reservedProperties[S3RegionalEndpointField] = profile.S3RegionalEndpoint.ToString().ToLowerInvariant();
if (profile.S3DisableMultiRegionAccessPoints != null)
reservedProperties[S3DisableMultiRegionAccessPointsField] = profile.S3DisableMultiRegionAccessPoints.ToString().ToLowerInvariant();
if (profile.RetryMode != null)
reservedProperties[RetryModeField] = profile.RetryMode.ToString().ToLowerInvariant();
if (profile.MaxAttempts != null)
reservedProperties[MaxAttemptsField] = profile.MaxAttempts.ToString().ToLowerInvariant();
if (profile.IgnoreConfiguredEndpointUrls != null)
reservedProperties[IgnoreConfiguredEndpointUrlsField] = profile.IgnoreConfiguredEndpointUrls.ToString().ToLowerInvariant();
if (profile.EndpointUrl != null)
reservedProperties[EndpointUrlField] = profile.EndpointUrl.ToString().ToLowerInvariant();
var profileDictionary = PropertyMapping.CombineProfileParts(
profile.Options, ReservedPropertyNames, reservedProperties, profile.Properties);
// Set the UniqueKey. It might change if the unique key is set by the objectManger,
// or if this is an update to an existing profile.
string newUniqueKeyStr = _settingsManager.RegisterObject(profile.Name, profileDictionary);
Guid? newUniqueKey;
if (GuidUtils.TryParseNullableGuid(newUniqueKeyStr, out newUniqueKey))
profile.UniqueKey = newUniqueKey;
profile.CredentialProfileStore = this;
}
else
{
throw new ArgumentException(String.Format(CultureInfo.InvariantCulture,
"Unable to register profile {0}. The CredentialProfileOptions provided is not valid.", profile.Name));
}
}
///
/// If there is a profile in the store with the given name, delete it.
///
/// The name of the profile to delete.
public void UnregisterProfile(string profileName)
{
_settingsManager.UnregisterObject(profileName);
}
///
/// Rename the profile with oldProfileName to newProfileName.
///
/// The profile to rename.
/// The new name for the profile.
public void RenameProfile(string oldProfileName, string newProfileName)
{
RenameProfile(oldProfileName, newProfileName, false);
}
///
/// Rename the profile with oldProfileName to newProfileName.
///
/// The profile to rename.
/// The new name for the profile.
/// If true and the destination profile exists it will be overwritten.
public void RenameProfile(string oldProfileName, string newProfileName, bool force)
{
_settingsManager.RenameObject(oldProfileName, newProfileName, force);
}
///
/// Make a copy of the profile with fromProfileName called toProfileName.
///
/// The name of the profile to copy from.
/// The name of the new profile.
public void CopyProfile(string fromProfileName, string toProfileName)
{
CopyProfile(fromProfileName, toProfileName, false);
}
///
/// Make a copy of the profile with fromProfileName called toProfileName.
///
/// The name of the profile to copy from.
/// The name of the new profile.
/// If true and the destination profile exists it will be overwritten.
public void CopyProfile(string fromProfileName, string toProfileName, bool force)
{
_settingsManager.CopyObject(fromProfileName, toProfileName, force);
}
///
/// Set the ProfileType field to maintain backward compatibility with ProfileManager.
/// The value is ignored when it's read back in.
///
///
///
private static void SetProfileTypeField(IDictionary properties, CredentialProfileType profileType)
{
switch (profileType)
{
case CredentialProfileType.Basic:
properties[SettingsConstants.ProfileTypeField] = AWSCredentialsProfileType;
break;
case CredentialProfileType.SAMLRole:
case CredentialProfileType.SAMLRoleUserIdentity:
properties[SettingsConstants.ProfileTypeField] = SAMLRoleProfileType;
break;
default:
properties[SettingsConstants.ProfileTypeField] = profileType.ToString();
break;
}
}
}
}