diff --git a/generator/.DevConfigs/2cd7f333-8fa0-4666-9ad9-375ff5a7e780.json b/generator/.DevConfigs/2cd7f333-8fa0-4666-9ad9-375ff5a7e780.json new file mode 100644 index 000000000000..eddd5936089c --- /dev/null +++ b/generator/.DevConfigs/2cd7f333-8fa0-4666-9ad9-375ff5a7e780.json @@ -0,0 +1,10 @@ +{ + "core": { + "updateMinimum": true, + "type": "Patch", + "changeLogMessages": [ + "Update `PersistenceManager` not to throw an exception when the SDK is used with a read-only file system (https://github.com/aws/aws-sdk-net/issues/3801)", + "Add `AWSConfigs.DisableLegacyPersistenceStore` option to instruct the SDK not to use the [SDK Store](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/sdk-store.html) in its default profile resolution search" + ] + } +} \ No newline at end of file diff --git a/sdk/src/Core/AWSConfigs.cs b/sdk/src/Core/AWSConfigs.cs index 6a1a75ddacd5..531a0cb94d35 100644 --- a/sdk/src/Core/AWSConfigs.cs +++ b/sdk/src/Core/AWSConfigs.cs @@ -29,6 +29,7 @@ using Amazon.Runtime; using Amazon.Runtime.Telemetry; using Amazon.Runtime.Credentials; +using Amazon.Runtime.CredentialManagement; namespace Amazon { @@ -81,6 +82,7 @@ public static partial class AWSConfigs internal static string _awsAccountsLocation = GetConfig(AWSProfilesLocationKey); internal static bool _useSdkCache = GetConfigBool(UseSdkCacheKey, defaultValue: true); internal static bool _initializeCollections = GetConfigBool(InitializeCollectionsKey, defaultValue: false); + internal static bool _disableLegacyPersistenceStore = GetConfigBool(DisableLegacyPersistenceStoreKey, defaultValue: false); private static TelemetryProvider _telemetryProvider = new DefaultTelemetryProvider(); #if NET8_0_OR_GREATER @@ -214,6 +216,7 @@ public static string AWSProfilesLocation } #endregion + #region StreamingUtf8JsonReaderBufferSize /// @@ -296,7 +299,7 @@ public static bool InitializeCollections #if NET8_0_OR_GREATER /// /// Key for the DisableDangerousDisablePathAndQueryCanonicalization property. - /// + /// /// public const string DisableDangerousDisablePathAndQueryCanonicalizationKey = "AWSDisableDangerousDisablePathAndQueryCanonicalization"; @@ -340,7 +343,35 @@ public static List A get { return _rootConfig.AWSCredentialsGenerators; } set { _rootConfig.AWSCredentialsGenerators = value; } } - + + #endregion + + #region DisableLegacyPersistenceStore + + /// + /// Key for the property. + /// + public const string DisableLegacyPersistenceStoreKey = "AWSDisableLegacyPersistenceStore"; + + /// + /// When attempting to retrieve configuration options for a given profile, the AWS SDK for .NET will look at both + /// the shared config file and the SDK Store by default - via the . + /// + /// The SDK Store has a few limitations, such as only being available on Windows and being specific to a particular user on a particular host. + /// + /// Setting this property to true will instruct the SDK not to check the legacy persistence store when interacting with + /// profiles (this setting is only applicable to the and it's not considered when + /// interacting with the class directly). + /// + /// + /// The default value is false. + /// + public static bool DisableLegacyPersistenceStore + { + get { return _rootConfig.DisableLegacyPersistenceStore; } + set { _rootConfig.DisableLegacyPersistenceStore = value; } + } + #endregion #region AWS Config Sections diff --git a/sdk/src/Core/Amazon.Runtime/CredentialManagement/CredentialProfileStoreChain.cs b/sdk/src/Core/Amazon.Runtime/CredentialManagement/CredentialProfileStoreChain.cs index 400a989b99f3..b7841ab9946d 100644 --- a/sdk/src/Core/Amazon.Runtime/CredentialManagement/CredentialProfileStoreChain.cs +++ b/sdk/src/Core/Amazon.Runtime/CredentialManagement/CredentialProfileStoreChain.cs @@ -99,12 +99,15 @@ public bool TryGetAWSCredentials(string profileName, out AWSCredentials credenti /// True if the profile was found, false otherwise. public bool TryGetProfile(string profileName, out CredentialProfile profile) { - if (string.IsNullOrEmpty(ProfilesLocation) && UserCrypto.IsUserCryptAvailable) + if (!AWSConfigs.DisableLegacyPersistenceStore) { - var netCredentialsFile = new NetSDKCredentialsFile(); - if (netCredentialsFile.TryGetProfile(profileName, out profile)) + if (string.IsNullOrEmpty(ProfilesLocation) && UserCrypto.IsUserCryptAvailable) { - return true; + var netCredentialsFile = new NetSDKCredentialsFile(); + if (netCredentialsFile.TryGetProfile(profileName, out profile)) + { + return true; + } } } @@ -140,11 +143,15 @@ public List ListProfiles() { var profiles = new List(); - if (string.IsNullOrEmpty(ProfilesLocation) && UserCrypto.IsUserCryptAvailable) + if (!AWSConfigs.DisableLegacyPersistenceStore) { - var netSdkFile = new NetSDKCredentialsFile(); - profiles.AddRange(netSdkFile.ListProfiles()); + if (string.IsNullOrEmpty(ProfilesLocation) && UserCrypto.IsUserCryptAvailable) + { + var netSdkFile = new NetSDKCredentialsFile(); + profiles.AddRange(netSdkFile.ListProfiles()); + } } + var sharedFile = new SharedCredentialsFile(ProfilesLocation); profiles.AddRange(sharedFile.ListProfiles()); @@ -171,7 +178,7 @@ public List ListProfiles() /// The profile to register. public void RegisterProfile(CredentialProfile profile) { - if (string.IsNullOrEmpty(ProfilesLocation) && UserCrypto.IsUserCryptAvailable) + if (!AWSConfigs.DisableLegacyPersistenceStore && string.IsNullOrEmpty(ProfilesLocation) && UserCrypto.IsUserCryptAvailable) { new NetSDKCredentialsFile().RegisterProfile(profile); } diff --git a/sdk/src/Core/Amazon.Runtime/Internal/Settings/PersistenceManager.cs b/sdk/src/Core/Amazon.Runtime/Internal/Settings/PersistenceManager.cs index 7788e27e3cc0..aeeb94f965d1 100644 --- a/sdk/src/Core/Amazon.Runtime/Internal/Settings/PersistenceManager.cs +++ b/sdk/src/Core/Amazon.Runtime/Internal/Settings/PersistenceManager.cs @@ -88,27 +88,26 @@ static PersistenceManager() try { -#if BCL +#if NETFRAMEWORK SettingsStoreFolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData) + "/AWSToolkit"; #else SettingsStoreFolder = System.Environment.GetEnvironmentVariable("HOME"); if (string.IsNullOrEmpty(SettingsStoreFolder)) SettingsStoreFolder = System.Environment.GetEnvironmentVariable("USERPROFILE"); - SettingsStoreFolder = Path.Combine(SettingsStoreFolder, "AppData/Local/AWSToolkit"); + SettingsStoreFolder = Path.Combine(SettingsStoreFolder, "AppData", "Local", "AWSToolkit"); #endif if (!Directory.Exists(SettingsStoreFolder)) Directory.CreateDirectory(SettingsStoreFolder); Instance = new PersistenceManager(); } - catch (UnauthorizedAccessException ex) + catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException) { _logger.Error(ex, $"Unable to initialize '{nameof(PersistenceManager)}'. Falling back to '{nameof(InMemoryPersistenceManager)}'."); - Instance = new InMemoryPersistenceManager(); } - } + } #endregion diff --git a/sdk/src/Core/Amazon.Util/Internal/RootConfig.cs b/sdk/src/Core/Amazon.Util/Internal/RootConfig.cs index 14970d76cc1a..282dfcaea416 100644 --- a/sdk/src/Core/Amazon.Util/Internal/RootConfig.cs +++ b/sdk/src/Core/Amazon.Util/Internal/RootConfig.cs @@ -52,6 +52,8 @@ public RegionEndpoint RegionEndpoint public List AWSCredentialsGenerators { get; set; } + public bool DisableLegacyPersistenceStore { get; set; } + private const string _rootAwsSectionName = "aws"; public RootConfig() { @@ -65,6 +67,7 @@ public RootConfig() UseSdkCache = AWSConfigs._useSdkCache; InitializeCollections = AWSConfigs._initializeCollections; CorrectForClockSkew = true; + DisableLegacyPersistenceStore = AWSConfigs._disableLegacyPersistenceStore; #if NET8_0_OR_GREATER DisableDangerousDisablePathAndQueryCanonicalization = AWSConfigs._disableDangerousDisablePathAndQueryCanonicalization; @@ -105,5 +108,4 @@ public XElement GetServiceSection(string service) return null; } } - }