From 41a09aea875fed022f813083a60727ea378ed4ff Mon Sep 17 00:00:00 2001 From: azurepowershell Date: Thu, 14 Dec 2023 03:03:29 +0000 Subject: [PATCH] Sync tools folder from main branch to generation branch --- .../Common.Netcore.Dependencies.Test.targets | 3 +- .../Gen2Master/MoveFromGeneration2Master.ps1 | 6 +- tools/TestFx/EnvironmentSetupHelper.cs | 1 + tools/TestFx/Mocks/MockClientFactory.cs | 13 +- tools/TestFx/Mocks/MockContext.cs | 80 ++------- tools/TestFx/TestEndpoints.cs | 2 +- tools/TestFx/TestEnvironment.cs | 158 ++++++++---------- 7 files changed, 101 insertions(+), 162 deletions(-) diff --git a/tools/Common.Netcore.Dependencies.Test.targets b/tools/Common.Netcore.Dependencies.Test.targets index 35af6f388244..cadadff43711 100644 --- a/tools/Common.Netcore.Dependencies.Test.targets +++ b/tools/Common.Netcore.Dependencies.Test.targets @@ -10,7 +10,6 @@ - @@ -40,7 +39,7 @@ - + diff --git a/tools/Gen2Master/MoveFromGeneration2Master.ps1 b/tools/Gen2Master/MoveFromGeneration2Master.ps1 index 90bfc6bdd013..5bae4cd8154f 100644 --- a/tools/Gen2Master/MoveFromGeneration2Master.ps1 +++ b/tools/Gen2Master/MoveFromGeneration2Master.ps1 @@ -142,9 +142,9 @@ Function Move-Generation2Master { dotnet sln $slnFilePath add $_.FullName --solution-folder Accounts } } - Get-ChildItem -Filter *.csproj -File -Path $DestPath -Recurse | ForEach-Object { - dotnet sln $slnFilePath add $_.FullName - } + } + Get-ChildItem -Filter *.csproj -File -Path $DestPath -Recurse | ForEach-Object { + dotnet sln $slnFilePath add $_.FullName } $Psd1Metadata.RequiredAssemblies = Unique-PathList $Psd1Metadata.RequiredAssemblies diff --git a/tools/TestFx/EnvironmentSetupHelper.cs b/tools/TestFx/EnvironmentSetupHelper.cs index ca175ef0ac2c..448aa2b7e66d 100644 --- a/tools/TestFx/EnvironmentSetupHelper.cs +++ b/tools/TestFx/EnvironmentSetupHelper.cs @@ -406,6 +406,7 @@ public void SetupAzureEnvironmentFromEnvironmentVariables(AzureModule mode) environment.StorageEndpointSuffix = AzureEnvironmentConstants.AzureStorageEndpointSuffix; environment.AzureKeyVaultDnsSuffix = AzureEnvironmentConstants.AzureKeyVaultDnsSuffix; environment.AzureKeyVaultServiceEndpointResourceId = AzureEnvironmentConstants.AzureKeyVaultServiceEndpointResourceId; + environment.ExtendedProperties.SetProperty(AzureEnvironment.ExtendedEndpoint.MicrosoftGraphUrl, currentEnvironment.Endpoints.GraphUri.AbsoluteUri); environment.ExtendedProperties.SetProperty(AzureEnvironment.ExtendedEndpoint.OperationalInsightsEndpoint, "https://api.loganalytics.io/v1"); environment.ExtendedProperties.SetProperty(AzureEnvironment.ExtendedEndpoint.OperationalInsightsEndpointResourceId, "https://api.loganalytics.io"); if (!AzureRmProfileProvider.Instance.GetProfile().EnvironmentTable.ContainsKey(TestEnvironmentName)) diff --git a/tools/TestFx/Mocks/MockClientFactory.cs b/tools/TestFx/Mocks/MockClientFactory.cs index bd53fcfb2dc3..219711084f81 100644 --- a/tools/TestFx/Mocks/MockClientFactory.cs +++ b/tools/TestFx/Mocks/MockClientFactory.cs @@ -31,6 +31,8 @@ using Microsoft.Azure.Commands.TestFx.DelegatingHandlers; using Microsoft.Rest.ClientRuntime.Azure.TestFramework; using Microsoft.Azure.Test.HttpRecorder; +using Microsoft.Azure.Commands.Common.MSGraph.Version1_0; + #if NETSTANDARD using Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core; #endif @@ -81,9 +83,16 @@ public TClient CreateArmClient(IAzureContext context, string endpoint) public TClient CreateCustomArmClient(params object[] parameters) where TClient : Microsoft.Rest.ServiceClient { - if (!(_serviceClients.FirstOrDefault(o => o is TClient) is TClient client)) + if (_serviceClients.FirstOrDefault(o => o is TClient) is not TClient client) { - client = _mockContext?.GetServiceClient(); + if (typeof(TClient) == typeof(MicrosoftGraphClient)) + { + client = _mockContext?.GetGraphServiceClient(); + } + else + { + client = _mockContext?.GetServiceClient(); + } } if (client == null) diff --git a/tools/TestFx/Mocks/MockContext.cs b/tools/TestFx/Mocks/MockContext.cs index e481e51e5aa6..d1784d72a333 100644 --- a/tools/TestFx/Mocks/MockContext.cs +++ b/tools/TestFx/Mocks/MockContext.cs @@ -99,6 +99,13 @@ public T GetServiceClient(bool internalBaseUri = false, params DelegatingHand /// public T GetServiceClient(TestEnvironment currentEnvironment, bool internalBaseUri = false, params DelegatingHandler[] handlers) where T : class { + if (!currentEnvironment.TokenInfo.ContainsKey(TokenAudience.Management)) + { + throw new ArgumentNullException( + "currentEnvironment.TokenInfo[TokenAudience.Management]", + $"Unable to create Service Client because {nameof(T)} authentication token was not acquired during Login."); + } + return GetServiceClientWithCredentials(currentEnvironment, currentEnvironment.TokenInfo[TokenAudience.Management], internalBaseUri, handlers); } @@ -108,9 +115,7 @@ public T GetServiceClient(TestEnvironment currentEnvironment, bool internalBa /// /// Delegating existingHandlers /// - public T GetGraphServiceClient( - bool internalBaseUri = false, - params DelegatingHandler[] handlers) where T : class + public T GetGraphServiceClient(bool internalBaseUri = false, params DelegatingHandler[] handlers) where T : class { return GetGraphServiceClient(TestFxEnvironment, internalBaseUri, handlers); } @@ -121,52 +126,16 @@ public T GetGraphServiceClient( /// /// Delegating existingHandlers /// - public T GetGraphServiceClient( - TestEnvironment currentEnvironment, - bool internalBaseUri = false, - params DelegatingHandler[] handlers) where T : class + public T GetGraphServiceClient(TestEnvironment currentEnvironment, bool internalBaseUri = false, params DelegatingHandler[] handlers) where T : class { if (!currentEnvironment.TokenInfo.ContainsKey(TokenAudience.Graph)) { throw new ArgumentNullException( "currentEnvironment.TokenInfo[TokenAudience.Graph]", - "Unable to create Graph Management client because Graph authentication token was not acquired during Login."); + "Unable to create Graph Client because Graph authentication token was not acquired during Login."); } - return GetServiceClientWithCredentials( - currentEnvironment, - currentEnvironment.TokenInfo[TokenAudience.Graph], - currentEnvironment.Endpoints.GraphUri, - internalBaseUri, - handlers); - } - - /// - /// Get a test environment using default options - /// - /// The type of the service client to return - /// Credentials - /// Delegating existingHandlers - /// A Service client using credentials and base uri from the current environment - public T GetServiceClientWithCredentials(object credentials, params DelegatingHandler[] handlers) where T : class - { - return GetServiceClientWithCredentials(TestFxEnvironment, credentials, handlers: handlers); - } - - /// - /// Get a test environment, allowing the test to customize the creation options - /// - /// - /// Credentials - /// Delegating existingHandlers - /// - public T GetServiceClientWithCredentials( - TestEnvironment currentEnvironment, - object credentials, - bool internalBaseUri = false, - params DelegatingHandler[] handlers) where T : class - { - return GetServiceClientWithCredentials(currentEnvironment, credentials, currentEnvironment.BaseUri, internalBaseUri, handlers); + return GetServiceClientWithCredentials(currentEnvironment, currentEnvironment.TokenInfo[TokenAudience.Graph], internalBaseUri, handlers); } /// @@ -177,21 +146,14 @@ public T GetServiceClientWithCredentials( /// Base Uri /// Delegating existingHandlers /// - public T GetServiceClientWithCredentials( - TestEnvironment currentEnvironment, - object credentials, - Uri baseUri, - bool internalBaseUri = false, - params DelegatingHandler[] handlers) where T : class + public T GetServiceClientWithCredentials(TestEnvironment currentEnvironment, object credentials, bool internalBaseUri = false, params DelegatingHandler[] handlers) where T : class { T client; handlers = AddHandlers(currentEnvironment, handlers); var constructors = typeof(T).GetConstructors(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic); ConstructorInfo constructor = null; - //We no longer use UseCustomUri function, rather check if BaseUri is notNull - //UseCustomeUri use to return true when BaseUri was set to some value - if ((currentEnvironment.BaseUri != null) && !internalBaseUri) + if (!internalBaseUri && currentEnvironment.BaseUri != null) { foreach (var c in constructors) { @@ -207,12 +169,11 @@ public T GetServiceClientWithCredentials( } if (constructor == null) { - throw new InvalidOperationException( - "can't find constructor (uri, ServiceClientCredentials, DelegatingHandler[]) to create client"); + throw new InvalidOperationException("Cannot find constructor (uri, ServiceClientCredentials, DelegatingHandler[]) to create service client"); } client = constructor.Invoke(new object[] { - baseUri, + currentEnvironment.BaseUri, credentials, handlers }) as T; @@ -232,8 +193,7 @@ public T GetServiceClientWithCredentials( } if (constructor == null) { - throw new InvalidOperationException( - "can't find constructor (ServiceClientCredentials, DelegatingHandler[]) to create client"); + throw new InvalidOperationException("Cannot find constructor (ServiceClientCredentials, DelegatingHandler[]) to create service client"); } client = constructor.Invoke(new object[] { @@ -262,15 +222,11 @@ private void SetLongRunningOperationTimeouts(T client) where T : class if (HttpMockServer.Mode == HttpRecorderMode.Playback) { PropertyInfo retryTimeout = typeof(T).GetProperty("LongRunningOperationRetryTimeout"); - if (retryTimeout != null) - { - retryTimeout.SetValue(client, 0); - } + retryTimeout?.SetValue(client, 0); } } - public DelegatingHandler[] AddHandlers(TestEnvironment currentEnvironment, - params DelegatingHandler[] existingHandlers) + public DelegatingHandler[] AddHandlers(TestEnvironment currentEnvironment, params DelegatingHandler[] existingHandlers) { HttpMockServer server; diff --git a/tools/TestFx/TestEndpoints.cs b/tools/TestFx/TestEndpoints.cs index c4513abd55a3..810b8665ba7d 100644 --- a/tools/TestFx/TestEndpoints.cs +++ b/tools/TestFx/TestEndpoints.cs @@ -127,7 +127,7 @@ internal TestEndpoints(TestEnvironmentName testEnvNames) { case TestEnvironmentName.Prod: Name = TestEnvironmentName.Prod; - GraphUri = new Uri("https://graph.windows.net/"); + GraphUri = new Uri("https://graph.microsoft.com/"); AADAuthUri = new Uri("https://login.microsoftonline.com/"); IbizaPortalUri = new Uri("https://portal.azure.com/"); RdfePortalUri = new Uri("https://manage.windowsazure.com/"); diff --git a/tools/TestFx/TestEnvironment.cs b/tools/TestFx/TestEnvironment.cs index 71b3766f6ca3..16db63a76324 100644 --- a/tools/TestFx/TestEnvironment.cs +++ b/tools/TestFx/TestEnvironment.cs @@ -13,8 +13,8 @@ // ---------------------------------------------------------------------------------- using Microsoft.Azure.Test.HttpRecorder; +using Microsoft.Identity.Client; using Microsoft.Rest; -using Microsoft.Rest.Azure.Authentication; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; @@ -32,35 +32,49 @@ public class TestEnvironment { private const string DummyTenantId = "395544B0-BF41-429D-921F-E1CA2252FCF4"; + private const string MicrosoftLoginUrl = "https://login.microsoftonline.com"; + + private const string MicrosoftGraphUrl = "https://graph.microsoft.com"; + /// /// Base Uri used by the Test Environment /// - public Uri BaseUri { get; set; } + public Uri BaseUri { get; private set; } /// /// Base Azure Graph Uri used by the Test Environment /// - public Uri GraphUri { get; set; } + public Uri GraphUri { get; private set; } /// - /// UserName used by the Test Environment + /// Tenant Id used by the Test Environment /// - public string UserName { get; set; } + public string TenantId { get; private set; } + + /// + /// Subscription Id used by the Test Environment + /// + public string SubscriptionId { get; private set; } /// - /// Tenant used by the Test Environment + /// Service principal client id used by the Test Environment /// - public string TenantId { get; set; } + public string ServicePrincipalClientId { get; private set; } /// - /// Subscription Id used by the Test Environment + /// Service principal secret used by the Test Environment + /// + public string ServicePrincipalSecret { get; private set; } + + /// + /// UserName used by the Test Environment /// - public string SubscriptionId { get; set; } + public string UserName { get; set; } /// /// Active TestEndpoint being used by the Test Environment /// - public TestEndpoints Endpoints { get; set; } + public TestEndpoints Endpoints { get; private set; } /// /// Holds default endpoints for all the supported environments @@ -87,6 +101,8 @@ public TestEnvironment(string connectionString) SubscriptionId = ConnectionString.GetValue(ConnectionStringKeys.SubscriptionIdKey); TenantId = ConnectionString.GetValue(ConnectionStringKeys.TenantIdKey); + ServicePrincipalClientId = ConnectionString.GetValue(ConnectionStringKeys.ServicePrincipalKey); + ServicePrincipalSecret = ConnectionString.GetValue(ConnectionStringKeys.ServicePrincipalSecretKey); UserName = ConnectionString.GetValue(ConnectionStringKeys.UserIdKey); OptimizeRecordedFile = ConnectionString.GetValue(ConnectionStringKeys.OptimizeRecordedFileKey); @@ -103,7 +119,7 @@ public TestEnvironment(string connectionString) { GraphUri = Endpoints.GraphUri; } - else if (!string.IsNullOrWhiteSpace(ConnectionString.GetValue(ConnectionStringKeys.GraphUriKey))) + else { GraphUri = new Uri(ConnectionString.GetValue(ConnectionStringKeys.GraphUriKey)); } @@ -127,17 +143,14 @@ private void InitTestEndPoints() private void LoadDefaultEnvironmentEndpoints() { - if (EnvEndpoints == null) + EnvEndpoints ??= new Dictionary() { - EnvEndpoints = new Dictionary() - { - { TestEnvironmentName.Prod, new TestEndpoints(TestEnvironmentName.Prod) }, - { TestEnvironmentName.Dogfood, new TestEndpoints(TestEnvironmentName.Dogfood) }, - { TestEnvironmentName.Next, new TestEndpoints(TestEnvironmentName.Next) }, - { TestEnvironmentName.Current, new TestEndpoints(TestEnvironmentName.Current) }, - { TestEnvironmentName.Custom, new TestEndpoints(TestEnvironmentName.Custom) } - }; - } + { TestEnvironmentName.Prod, new TestEndpoints(TestEnvironmentName.Prod) }, + { TestEnvironmentName.Dogfood, new TestEndpoints(TestEnvironmentName.Dogfood) }, + { TestEnvironmentName.Next, new TestEndpoints(TestEnvironmentName.Next) }, + { TestEnvironmentName.Current, new TestEndpoints(TestEnvironmentName.Current) }, + { TestEnvironmentName.Custom, new TestEndpoints(TestEnvironmentName.Custom) } + }; } private void InitTokenDictionary() @@ -187,85 +200,46 @@ private void SetupHttpRecorderMode() private void Login() { - string userPassword = ConnectionString.GetValue(ConnectionStringKeys.PasswordKey); - string spnClientId = ConnectionString.GetValue(ConnectionStringKeys.ServicePrincipalKey); - string spnClientSecret = ConnectionString.GetValue(ConnectionStringKeys.ServicePrincipalSecretKey); - //We use this because when login silently using userTokenProvider, we need to provide a well known ClientId for an app that has delegating permissions. - //All first party app have that permissions, so we use PowerShell app ClientId -#if net452 - string PowerShellClientId = "1950a258-227b-4e31-a9cf-717495945fc2"; -#endif - /* - * Currently we prioritize login as below: - * 1) ServicePrincipal/ServicePrincipal Secret Key - * 2) UserName / Password combination - * 3) Interactive Login (where user will be presented with prompt to login) - */ - - ActiveDirectoryServiceSettings aadServiceSettings = new ActiveDirectoryServiceSettings() - { - AuthenticationEndpoint = new Uri(Endpoints.AADAuthUri.ToString() + ConnectionString.GetValue(ConnectionStringKeys.TenantIdKey)), - TokenAudience = Endpoints.AADTokenAudienceUri - }; + UpdateTokenInfo(TokenAudience.Management, new[] { "https://management.azure.com/.default" }); + UpdateTokenInfo(TokenAudience.Graph, new[] { "https://graph.microsoft.com/.default" }); + } - ActiveDirectoryServiceSettings graphAADServiceSettings = new ActiveDirectoryServiceSettings() - { - AuthenticationEndpoint = new Uri(Endpoints.AADAuthUri.ToString() + ConnectionString.GetValue(ConnectionStringKeys.TenantIdKey)), - TokenAudience = Endpoints.GraphTokenAudienceUri - }; + private void UpdateTokenInfo(TokenAudience tokenAudience, IEnumerable scopes) + { + var accessToken = GetServicePrincipalAccessToken(scopes); + TokenInfo[tokenAudience] = new TokenCredentials(accessToken); + } - if ((!string.IsNullOrEmpty(spnClientId)) && (!string.IsNullOrEmpty(spnClientSecret))) - { - Task mgmAuthResult = Task.Run(async () => (TokenCredentials)await ApplicationTokenProvider.LoginSilentAsync(TenantId, spnClientId, spnClientSecret, aadServiceSettings).ConfigureAwait(false)); - TokenInfo[TokenAudience.Management] = mgmAuthResult.Result; - UpdateTokenInfoWithGraphToken(graphAADServiceSettings, spnClientId: spnClientId, spnClientSecret: spnClientSecret); - } - else if ((!string.IsNullOrEmpty(UserName)) && (!string.IsNullOrEmpty(userPassword))) - { - //#if FullNetFx -#if net452 - Task mgmAuthResult = Task.Run(async () => (TokenCredentials)await UserTokenProvider.LoginSilentAsync(PowerShellClientId, TenantId, UserName, userPassword, aadServiceSettings).ConfigureAwait(false)); - this.TokenInfo[TokenAudience.Management] = mgmAuthResult.Result; - UpdateTokenInfoWithGraphToken(graphAADServiceSettings, userName: UserName, password: userPassword, psClientId: PowerShellClientId); -#else - throw new NotSupportedException("Username/Password login is supported only in NET452 and above projects"); -#endif - } - else + public string GetServicePrincipalAccessToken(IEnumerable scopes, string cloudInstanceUri = null) + { + if (string.IsNullOrWhiteSpace(cloudInstanceUri)) { - //#if FullNetFx -#if net452 - InteractiveLogin(TenantId, PowerShellClientId, aadServiceSettings, graphAADServiceSettings); -#else - throw new NotSupportedException("Interactive Login is supported only in NET452 and above projects"); -#endif + cloudInstanceUri = MicrosoftLoginUrl; } + + var spn = ConfidentialClientApplicationBuilder + .Create(ServicePrincipalClientId) + .WithClientSecret(ServicePrincipalSecret) + .WithAuthority(cloudInstanceUri, TenantId) + .Build(); + var authResult = Task.Run(async () => await spn.AcquireTokenForClient(scopes).ExecuteAsync().ConfigureAwait(false)); + return authResult.Result.AccessToken; } - private void UpdateTokenInfoWithGraphToken(ActiveDirectoryServiceSettings graphAADServiceSettings, string spnClientId = "", string spnClientSecret = "", string userName = "", string password = "", string psClientId = "") + public string GetServicePrincipalAccessToken(IEnumerable scopes, Uri authorityUri) { - Task graphAuthResult = null; - try - { - if (!string.IsNullOrWhiteSpace(userName) && !string.IsNullOrWhiteSpace(password)) - { - //#if FullNetFx -#if net452 - graphAuthResult = Task.Run(async () => (TokenCredentials)await UserTokenProvider.LoginSilentAsync(psClientId, TenantId, userName, password, graphAADServiceSettings).ConfigureAwait(false)); -#endif - } - else if (!string.IsNullOrWhiteSpace(spnClientId) && !string.IsNullOrWhiteSpace(spnClientSecret)) - { - graphAuthResult = Task.Run(async () => (TokenCredentials)await ApplicationTokenProvider.LoginSilentAsync(TenantId, spnClientId, spnClientSecret, graphAADServiceSettings).ConfigureAwait(false)); - } - - TokenInfo[TokenAudience.Graph] = graphAuthResult?.Result; - } - catch (Exception ex) + if (authorityUri == null) { - Debug.WriteLine($"Error while acquiring Graph Token: '{ex}'"); - // Not all accounts are registered to have access to Graph endpoints. + throw new ArgumentNullException(nameof(authorityUri)); } + + var spn = ConfidentialClientApplicationBuilder + .Create(ServicePrincipalClientId) + .WithClientSecret(ServicePrincipalSecret) + .WithAuthority(authorityUri) + .Build(); + var authResult = Task.Run(async () => await spn.AcquireTokenForClient(scopes).ExecuteAsync().ConfigureAwait(false)); + return authResult.Result.AccessToken; } private void VerifyAuthTokens() @@ -318,7 +292,7 @@ private void VerifySubscription() // we then check if the retrieved subscription list has exactly 1 subscription, if yes we will just use that one. if (string.IsNullOrEmpty(SubscriptionId)) { - if (subscriptionList.Count() == 1) + if (subscriptionList.Count == 1) { ConnectionString.KeyValuePairs[ConnectionStringKeys.SubscriptionIdKey] = subscriptionList.First().SubscriptionId; }