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