Skip to content

Commit 1115b94

Browse files
authored
Fix the bugs for AzKeyStore (#20768)
* Fix AzKeyStore issues and optimize initialization and update * Enabled credential to be found only by applicationId while tenant was not matched when accquire token. [#20484] * When Az.Accounts ran in parallel, the waiters were allowed to wait infinitely to avoid throw exception in automation enviroment. [#20455] * Used Lazy load for AzKeyStore. * Used update on change mechanism for AzKeyStore and remove `Flush` interface. * Add parallel test case of Az.Accounts to smoke test * Address review comments Co-authored-by: Yeming Liu <[email protected]> Address review comments * Fix failed test cases. * Fix an issue of AzKeyStore when context autosaving switching
1 parent bc78f20 commit 1115b94

28 files changed

+1057
-360
lines changed

.azure-pipelines/util/smoke-test-steps.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ jobs:
7878
Write-Host "List artifacts..."
7979
Get-ChildItem "$(Pipeline.Workspace)\\LocalRepo\\"
8080
81+
- task: NuGetCommand@2
82+
condition: and(succeeded(), eq('${{ parameters.psVersion }}', '5.1.14'))
83+
displayName: 'Download ThreadJob .nupkg File for PowerShell 5.1.14'
84+
inputs:
85+
command: custom
86+
arguments: 'install ThreadJob -directdownload -packagesavemode nupkg -source https://www.powershellgallery.com/api/v2 -OutputDirectory packages'
87+
8188
- task: NuGetCommand@2
8289
condition: and(succeeded(), eq(variables['GalleryName'], 'LocalRepo'))
8390
displayName: 'Download Previous Az .nupkg Files'

src/Accounts/Accounts.Test/AutosaveTests.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,7 @@ private AzKeyStore SetMockedAzKeyStore()
5151
storageMocker.Setup(f => f.Create()).Returns(storageMocker.Object);
5252
storageMocker.Setup(f => f.ReadData()).Returns(new byte[0]);
5353
storageMocker.Setup(f => f.WriteData(It.IsAny<byte[]>())).Callback((byte[] s) => {});
54-
var keyStore = new AzKeyStore(AzureSession.Instance.ARMProfileDirectory, "azkeystore", false, false, storageMocker.Object);
55-
AzKeyStore.RegisterJsonConverter(typeof(ServicePrincipalKey), typeof(ServicePrincipalKey).Name);
56-
AzKeyStore.RegisterJsonConverter(typeof(SecureString), typeof(SecureString).Name, new SecureStringConverter());
54+
var keyStore = new AzKeyStore(AzureSession.Instance.ARMProfileDirectory, "azkeystore", true, storageMocker.Object);
5755
return keyStore;
5856
}
5957

src/Accounts/Accounts.Test/ContextCmdletTests.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,21 @@
2323
using Microsoft.WindowsAzure.Commands.ScenarioTest;
2424
using Microsoft.WindowsAzure.Commands.Test.Utilities.Common;
2525
using Microsoft.WindowsAzure.Commands.Utilities.Common;
26-
using Xunit;
27-
using Xunit.Abstractions;
2826
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
29-
using System;
27+
using Microsoft.Azure.Commands.Common.Authentication.Properties;
3028
using Microsoft.Azure.Commands.Profile.Context;
31-
using System.Linq;
3229
using Microsoft.Azure.Commands.Common.Authentication.ResourceManager;
3330
using Microsoft.Azure.Commands.Profile.Common;
3431
using Microsoft.Azure.Commands.ScenarioTest.Mocks;
3532
using Microsoft.Azure.Commands.TestFx.Mocks;
3633
using Microsoft.Azure.Commands.TestFx;
34+
using Microsoft.Azure.Commands.ResourceManager.Common;
35+
using Moq;
36+
using System;
37+
using System.IO;
38+
using System.Linq;
39+
using Xunit;
40+
using Xunit.Abstractions;
3741

3842
namespace Microsoft.Azure.Commands.Profile.Test
3943
{
@@ -56,6 +60,12 @@ public ContextCmdletTests(ITestOutputHelper output)
5660
tokenCacheProviderMock = new MockPowerShellTokenCacheProvider();
5761
AzureSession.Instance.RegisterComponent<PowerShellTokenCacheProvider>(PowerShellTokenCacheProvider.PowerShellTokenCacheProviderKey, () => tokenCacheProviderMock);
5862
Environment.SetEnvironmentVariable("Azure_PS_Data_Collection", "True");
63+
64+
Mock<IStorage> storageMocker = new Mock<IStorage>();
65+
AzKeyStore azKeyStore = null;
66+
string profilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), Resources.AzureDirectoryName);
67+
azKeyStore = new AzKeyStore(profilePath, AzureSession.Instance.KeyStoreFile, true, storageMocker.Object);
68+
AzureSession.Instance.RegisterComponent(AzKeyStore.Name, () => azKeyStore, true);
5969
}
6070

6171
[Fact]

src/Accounts/Accounts.Test/ProfileCmdletTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ private AzKeyStore SetMockedAzKeyStore()
5555
storageMocker.Setup(f => f.Create()).Returns(storageMocker.Object);
5656
storageMocker.Setup(f => f.ReadData()).Returns(new byte[0]);
5757
storageMocker.Setup(f => f.WriteData(It.IsAny<byte[]>())).Callback((byte[] s) => { });
58-
var keyStore = new AzKeyStore(AzureSession.Instance.ARMProfileDirectory, "azkeystore", false, false, storageMocker.Object);
58+
var keyStore = new AzKeyStore(AzureSession.Instance.ARMProfileDirectory, "azkeystore", false, storageMocker.Object);
5959
return keyStore;
6060
}
6161

src/Accounts/Accounts/Account/ConnectAzureRmAccount.cs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ public override void ExecuteCmdlet()
425425
azureAccount.SetProperty(AzureAccount.Property.CertificatePath, resolvedPath);
426426
if (CertificatePassword != null)
427427
{
428-
keyStore?.SaveKey(new ServicePrincipalKey(AzureAccount.Property.CertificatePassword, azureAccount.Id, Tenant), CertificatePassword);
428+
keyStore?.SaveSecureString(new ServicePrincipalKey(AzureAccount.Property.CertificatePassword, azureAccount.Id, Tenant), CertificatePassword);
429429
if (GetContextModificationScope() == ContextModificationScope.CurrentUser && !keyStore.IsProtected)
430430
{
431431
WriteWarning(string.Format(Resources.ServicePrincipalWarning, AzureSession.Instance.KeyStoreFile, AzureSession.Instance.ARMProfileDirectory));
@@ -451,7 +451,7 @@ public override void ExecuteCmdlet()
451451

452452
if (azureAccount.Type == AzureAccount.AccountType.ServicePrincipal && password != null)
453453
{
454-
keyStore?.SaveKey(new ServicePrincipalKey(AzureAccount.Property.ServicePrincipalSecret
454+
keyStore?.SaveSecureString(new ServicePrincipalKey(AzureAccount.Property.ServicePrincipalSecret
455455
,azureAccount.Id, Tenant), password);
456456
if (GetContextModificationScope() == ContextModificationScope.CurrentUser && !keyStore.IsProtected)
457457
{
@@ -713,9 +713,7 @@ public void OnImport()
713713
}
714714

715715
AzKeyStore keyStore = null;
716-
keyStore = new AzKeyStore(AzureSession.Instance.ARMProfileDirectory, AzureSession.Instance.KeyStoreFile, false, autoSaveEnabled);
717-
AzKeyStore.RegisterJsonConverter(typeof(ServicePrincipalKey), typeof(ServicePrincipalKey).Name);
718-
AzKeyStore.RegisterJsonConverter(typeof(SecureString), typeof(SecureString).Name, new SecureStringConverter());
716+
keyStore = new AzKeyStore(AzureSession.Instance.ARMProfileDirectory, AzureSession.Instance.KeyStoreFile, autoSaveEnabled);
719717
AzureSession.Instance.RegisterComponent(AzKeyStore.Name, () => keyStore);
720718

721719
if (!InitializeProfileProvider(autoSaveEnabled))
@@ -724,11 +722,6 @@ public void OnImport()
724722
autoSaveEnabled = false;
725723
}
726724

727-
if (!keyStore.LoadStorage())
728-
{
729-
WriteInitializationWarnings(Resources.KeyStoreLoadingError);
730-
}
731-
732725
IAuthenticatorBuilder builder = null;
733726
if (!AzureSession.Instance.TryGetComponent(AuthenticatorBuilder.AuthenticatorBuilderKey, out builder))
734727
{

src/Accounts/Accounts/AutoSave/DisableAzureRmContextAutosave.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ void DisableAutosave(IAzureSession session, bool writeAutoSaveFile, out ContextA
9494

9595
if (AzureSession.Instance.TryGetComponent(AzKeyStore.Name, out AzKeyStore keystore))
9696
{
97-
keystore.DisableAutoSaving();
97+
keystore.DisableSyncToStorage();
9898
}
9999

100100
if (writeAutoSaveFile)

src/Accounts/Accounts/AutoSave/EnableAzureRmContextAutosave.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,9 @@ void EnableAutosave(IAzureSession session, bool writeAutoSaveFile, out ContextAu
104104

105105
if (AzureSession.Instance.TryGetComponent(AzKeyStore.Name, out AzKeyStore keystore))
106106
{
107-
keystore.Flush();
108-
keystore.DisableAutoSaving();
107+
keystore.EnableSyncToStorage();
109108
}
110109

111-
112110
if (writeAutoSaveFile)
113111
{
114112
try

src/Accounts/Accounts/ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
## Upcoming Release
2222
* Supported Web Account Manager on ARM64-based Windows systems. Fixed an issue where `Connect-AzAccount` failed with error "Unable to load DLL 'msalruntime_arm64'". [#20700]
23+
* Enabled credential to be found only by applicationId while tenant was not matched when accquire token. [#20484]
24+
* When Az.Accounts ran in parallel, the waiters were allowed to wait infinitely to avoid throw exception in automation enviroment. [#20455]
2325

2426
## Version 2.11.1
2527
* Fixed an issue where Az.Accounts cannot be imported correctly. [#20615]

src/Accounts/Accounts/Context/ImportAzureRMContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,13 @@ void CopyProfile(AzureRmProfile source, IProfileOperations target)
7878
var secret = account.GetProperty(AzureAccount.Property.ServicePrincipalSecret);
7979
if (!string.IsNullOrEmpty(secret))
8080
{
81-
keyStore.SaveKey(new ServicePrincipalKey(AzureAccount.Property.ServicePrincipalSecret, account.Id, context.Value.Tenant?.Id)
81+
keyStore.SaveSecureString(new ServicePrincipalKey(AzureAccount.Property.ServicePrincipalSecret, account.Id, context.Value.Tenant?.Id)
8282
, secret.ConvertToSecureString());
8383
}
8484
var password = account.GetProperty(AzureAccount.Property.CertificatePassword);
8585
if (!string.IsNullOrEmpty(password))
8686
{
87-
keyStore.SaveKey(new ServicePrincipalKey(AzureAccount.Property.CertificatePassword, account.Id, context.Value.Tenant?.Id)
87+
keyStore.SaveSecureString(new ServicePrincipalKey(AzureAccount.Property.CertificatePassword, account.Id, context.Value.Tenant?.Id)
8888
,password.ConvertToSecureString());
8989
}
9090
}

src/Accounts/Accounts/Context/SetAzureRMContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,13 @@ public override void ExecuteCmdlet()
9797
var secret = account.GetProperty(AzureAccount.Property.ServicePrincipalSecret);
9898
if (!string.IsNullOrEmpty(secret))
9999
{
100-
keyStore.SaveKey(new ServicePrincipalKey(AzureAccount.Property.ServicePrincipalSecret, account.Id, Context.Tenant?.Id)
100+
keyStore.SaveSecureString(new ServicePrincipalKey(AzureAccount.Property.ServicePrincipalSecret, account.Id, Context.Tenant?.Id)
101101
, secret.ConvertToSecureString());
102102
}
103103
var password = account.GetProperty(AzureAccount.Property.CertificatePassword);
104104
if (!string.IsNullOrEmpty(password))
105105
{
106-
keyStore.SaveKey(new ServicePrincipalKey(AzureAccount.Property.CertificatePassword, account.Id, Context.Tenant?.Id)
106+
keyStore.SaveSecureString(new ServicePrincipalKey(AzureAccount.Property.CertificatePassword, account.Id, Context.Tenant?.Id)
107107
, password.ConvertToSecureString());
108108
}
109109
}

src/Accounts/Authentication.ResourceManager/AzureRmProfile.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,13 @@ private IAzureContext MigrateSecretToKeyStore(IAzureContext context, AzKeyStore
225225
var account = context.Account;
226226
if (account.IsPropertySet(AzureAccount.Property.ServicePrincipalSecret))
227227
{
228-
keystore?.SaveKey(new ServicePrincipalKey(AzureAccount.Property.ServicePrincipalSecret, account.Id, account.GetTenants().First())
228+
keystore?.SaveSecureString(new ServicePrincipalKey(AzureAccount.Property.ServicePrincipalSecret, account.Id, account.GetTenants().First())
229229
, account.ExtendedProperties.GetProperty(AzureAccount.Property.ServicePrincipalSecret).ConvertToSecureString());
230230
account.ExtendedProperties.Remove(AzureAccount.Property.ServicePrincipalSecret);
231231
}
232232
if (account.IsPropertySet(AzureAccount.Property.CertificatePassword))
233233
{
234-
keystore?.SaveKey(new ServicePrincipalKey(AzureAccount.Property.CertificatePassword, account.Id, account.GetTenants().First())
234+
keystore?.SaveSecureString(new ServicePrincipalKey(AzureAccount.Property.CertificatePassword, account.Id, account.GetTenants().First())
235235
, account.ExtendedProperties.GetProperty(AzureAccount.Property.CertificatePassword).ConvertToSecureString());
236236
account.ExtendedProperties.Remove(AzureAccount.Property.CertificatePassword);
237237
}
@@ -336,10 +336,6 @@ public void Save(IFileProvider provider, bool serializeCache = true)
336336
// so that previous data is overwritten
337337
provider.Stream.SetLength(provider.Stream.Position);
338338
}
339-
340-
AzKeyStore keystore = null;
341-
AzureSession.Instance.TryGetComponent(AzKeyStore.Name, out keystore);
342-
keystore?.Flush();
343339
}
344340
finally
345341
{

0 commit comments

Comments
 (0)