/*
* Copyright 2019 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Amazon.Util.Internal;
using Amazon.Runtime;
#if BCL || NETSTANDARD
using Amazon.Runtime.CredentialManagement;
#endif
using Amazon;
using System.Globalization;
namespace Amazon.SecurityToken
{
public partial class AmazonSecurityTokenServiceConfig : ClientConfig
{
///
/// StsRegionalEndpoints should be set to StsRegionalEndpointsValue.Legacy to resolve to the global
/// sts endpoint (only for legacy global regions) or StsRegionalEndpointsValue.Regional to resolve to
/// the regional sts endpoint. The default value for StsRegionalEndpoints is StsRegionalEndpointsValue.Legacy.
///
public StsRegionalEndpointsValue StsRegionalEndpoints { get; set; }
private const string AwsProfileEnvironmentVariable = "AWS_PROFILE";
private const string DefaultProfileName = "default";
private const string AwsStsRegionalEndpointsEnvironmentVariable = "AWS_STS_REGIONAL_ENDPOINTS";
private const string StsDefaultHostname = "https://sts.amazonaws.com";
#if BCL || NETSTANDARD
private static CredentialProfileStoreChain credentialProfileChain = new CredentialProfileStoreChain();
#endif
#if BCL35 || UNITY
private readonly HashSet legacyGlobalRegions = new HashSet
{
RegionEndpoint.USEast1,
RegionEndpoint.USEast2,
RegionEndpoint.USWest1,
RegionEndpoint.USWest2,
RegionEndpoint.SAEast1,
RegionEndpoint.EUWest1,
RegionEndpoint.EUWest2,
RegionEndpoint.EUWest3,
RegionEndpoint.EUNorth1,
RegionEndpoint.EUCentral1,
RegionEndpoint.CACentral1,
RegionEndpoint.APSoutheast1,
RegionEndpoint.APSoutheast2,
RegionEndpoint.APSouth1,
RegionEndpoint.APNortheast1
};
#else
private readonly ISet legacyGlobalRegions = new HashSet
{
RegionEndpoint.USEast1,
RegionEndpoint.USEast2,
RegionEndpoint.USWest1,
RegionEndpoint.USWest2,
RegionEndpoint.SAEast1,
RegionEndpoint.EUWest1,
RegionEndpoint.EUWest2,
RegionEndpoint.EUWest3,
RegionEndpoint.EUNorth1,
RegionEndpoint.EUCentral1,
RegionEndpoint.CACentral1,
RegionEndpoint.APSoutheast1,
RegionEndpoint.APSoutheast2,
RegionEndpoint.APSouth1,
RegionEndpoint.APNortheast1
};
#endif
///
/// Initialize the Sts Regional Flag value
/// by checking the environment variable
/// and shared credentials file field
///
protected override void Initialize()
{
var tempStsRegionalEndpoints = CheckSTSEnvironmentVariable() ?? CheckCredentialsFile();
this.StsRegionalEndpoints = tempStsRegionalEndpoints ?? StsRegionalEndpointsValue.Legacy;
}
///
/// Override DetermineServiceURL to set the url to the
/// global endpoint if the sts regional flag is equal to legacy
/// and the region is a legacy global region
///
/// url: A string url for the request
public override string DetermineServiceURL()
{
if (this.ServiceURL != null)
{
return this.ServiceURL;
}
// also check if the region is within the list of legacy global regions
else if (this.StsRegionalEndpoints == StsRegionalEndpointsValue.Legacy && legacyGlobalRegions.Contains(this.RegionEndpoint))
{
this.AuthenticationRegion = "us-east-1";
return StsDefaultHostname;
}
else
{
return GetUrl(this.RegionEndpoint, this.RegionEndpointServiceName, this.UseHttp, this.UseDualstackEndpoint);
}
}
internal static string GetUrl(RegionEndpoint regionEndpoint, string regionEndpointServiceName, bool useHttp, bool useDualStack)
{
var endpoint = regionEndpoint.GetEndpointForService(regionEndpointServiceName, useDualStack);
string url = new Uri(string.Format(CultureInfo.InvariantCulture, "{0}{1}", useHttp ? "http://" : "https://", endpoint.Hostname)).AbsoluteUri;
return url;
}
///
/// If the sts regional flag environment variable is set, then first validate that
/// it is an acceptable value, if not, then throw an error. Then
/// set the sts regional flag to that value.
///
/// _isRegionalFlagSet: a boolean for whether or not
/// the environment variable set the regional flag
private static StsRegionalEndpointsValue? CheckSTSEnvironmentVariable()
{
#if BCL || NETSTANDARD
string stsRegionalFlag = Environment.GetEnvironmentVariable(AwsStsRegionalEndpointsEnvironmentVariable);
if (!string.IsNullOrEmpty(stsRegionalFlag))
{
#if BCL35
StsRegionalEndpointsValue? stsRegionalFlagValue = null;
try
{
stsRegionalFlagValue = (StsRegionalEndpointsValue)Enum.Parse(typeof(StsRegionalEndpointsValue), stsRegionalFlag, true);
}
catch (Exception)
{
throw new InvalidOperationException("Invalid value for AWS_STS_REGIONAL_ENDPOINTS environment variable. A string regional/legacy is expected.");
}
#else
if (!Enum.TryParse(stsRegionalFlag, true, out var stsRegionalFlagValue))
{
throw new InvalidOperationException("Invalid value for AWS_STS_REGIONAL_ENDPOINTS environment variable. A string regional/legacy is expected.");
}
#endif
return stsRegionalFlagValue;
}
#endif
return null;
}
///
/// Check the credential file for an sts regional endpoints
/// option. If it is set within the file, then set the sts
/// regional flag to that value.
///
/// _isRegionalFlagSet: A boolean for whether
/// or not the credentials file set the regional flag
private static StsRegionalEndpointsValue? CheckCredentialsFile()
{
#if BCL || NETSTANDARD
CredentialProfile profile;
var profileName = Environment.GetEnvironmentVariable(AwsProfileEnvironmentVariable) ?? DefaultProfileName;
credentialProfileChain.TryGetProfile(profileName, out profile);
return profile?.StsRegionalEndpoints;
#else
return null;
#endif
}
}
}