/* * 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 System; using System.Collections.Generic; using System.Linq; using System.Text; using Amazon.Util.Internal; using Amazon.Runtime; using Amazon.Runtime.CredentialManagement; using Amazon; using System.Globalization; using Amazon.Internal; using Amazon.Runtime.Internal; namespace Amazon.SecurityToken { public partial class AmazonSecurityTokenServiceConfig : ClientConfig { private StsRegionalEndpointsValue? _stsRegionalEndpoints; private object _stsRegionalEndpointsLock = new object(); /// /// StsRegionalEndpoints should be set to 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. /// /// Get the Sts Regional Flag value by checking the environment variable, the shared credentials file field, /// or falling back to and using /// public StsRegionalEndpointsValue StsRegionalEndpoints { get { if (_stsRegionalEndpoints.HasValue) { return _stsRegionalEndpoints.GetValueOrDefault(); } lock (_stsRegionalEndpointsLock) { if (_stsRegionalEndpoints.HasValue) { return _stsRegionalEndpoints.Value; } _stsRegionalEndpoints = CheckSTSEnvironmentVariable() ?? CheckCredentialsFile() ?? DefaultConfiguration.StsRegionalEndpoints; return _stsRegionalEndpoints.GetValueOrDefault(); } } set { lock (_stsRegionalEndpointsLock) { _stsRegionalEndpoints = value; } } } 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"; private static readonly CredentialProfileStoreChain _credentialProfileChain = new CredentialProfileStoreChain(); // we cache this per execution process to avoid excessive file I/O private static CredentialProfile _profile; private static object _triedToResolveProfileLock = new object(); private static bool _triedToResolveProfile = false; #if BCL35 private static 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 }; private static readonly HashSet legacyGlobalRegionSystemNames = new HashSet(); #else private static 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 }; private static readonly ISet legacyGlobalRegionSystemNames = new HashSet(); #endif static AmazonSecurityTokenServiceConfig() { foreach (var legacyGlobalRegion in legacyGlobalRegions) { legacyGlobalRegionSystemNames.Add(legacyGlobalRegion.SystemName); } } /// /// 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 && (this.RegionEndpoint != null && legacyGlobalRegionSystemNames.Contains(this.RegionEndpoint?.SystemName))) { this.AuthenticationRegion = "us-east-1"; return StsDefaultHostname; } else { return GetUrl(this, RegionEndpoint); } } internal static string GetUrl(IClientConfig config, RegionEndpoint regionEndpoint) { var endpoint = regionEndpoint.GetEndpointForService( config.RegionEndpointServiceName, config.ToGetEndpointForServiceOptions()); string url = new Uri(string.Format(CultureInfo.InvariantCulture, "{0}{1}", config.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() { 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; } 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 (_triedToResolveProfile) { return _profile?.StsRegionalEndpoints; } lock (_triedToResolveProfileLock) { if (!_triedToResolveProfile) { var profileName = Environment.GetEnvironmentVariable(AwsProfileEnvironmentVariable) ?? DefaultProfileName; _credentialProfileChain.TryGetProfile(profileName, out _profile); _triedToResolveProfile = true; } } return _profile?.StsRegionalEndpoints; } } }