From 43b06bfda0f7e14bbf6316b53b165a0ae8e57c48 Mon Sep 17 00:00:00 2001 From: Hovsep Mkrtchyan Date: Fri, 24 Jul 2015 18:33:12 -0700 Subject: [PATCH] Added creation of AutoRest ARM client in the ClientFactory --- .../Common.Authentication.Tests.csproj | 8 ++ .../MockCertificateAuthenticationFactory.cs | 6 ++ .../Mocks/MockClientFactory.cs | 11 +++ .../Mocks/MockTokenAuthenticationFactory.cs | 7 ++ .../packages.config | 2 + .../Authentication/AdalConfiguration.cs | 2 +- .../ServicePrincipalKeyStore.cs | 6 ++ .../Common.Authentication.csproj | 8 ++ .../Factories/AuthenticationFactory.cs | 84 ++++++++++++++++++- .../Factories/ClientFactory.cs | 44 ++++++++++ .../Interfaces/IAuthenticationFactory.cs | 3 + .../Interfaces/IClientFactory.cs | 4 + ...oft.Azure.Common.Authentication.nuget.proj | 2 +- .../Properties/AssemblyInfo.cs | 2 +- .../Common.Authentication/packages.config | 2 + 15 files changed, 187 insertions(+), 4 deletions(-) diff --git a/src/Authentication/Common.Authentication.Tests/Common.Authentication.Tests.csproj b/src/Authentication/Common.Authentication.Tests/Common.Authentication.Tests.csproj index 43c8032b0f2e3..616c1ae4c39ab 100644 --- a/src/Authentication/Common.Authentication.Tests/Common.Authentication.Tests.csproj +++ b/src/Authentication/Common.Authentication.Tests/Common.Authentication.Tests.csproj @@ -36,6 +36,14 @@ ..\..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.5.207081303-alpha\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll True + + ..\..\..\packages\Microsoft.Rest.ClientRuntime.1.1.1\lib\net45\Microsoft.Rest.ClientRuntime.dll + True + + + ..\..\..\packages\Microsoft.Rest.ClientRuntime.Azure.1.0.18-preview\lib\net45\Microsoft.Rest.ClientRuntime.Azure.dll + True + $(LibraryNugetPackageFolder)\Microsoft.WindowsAzure.Management.Storage.5.0.0\lib\net40\Microsoft.WindowsAzure.Management.Storage.dll diff --git a/src/Authentication/Common.Authentication.Tests/Mocks/MockCertificateAuthenticationFactory.cs b/src/Authentication/Common.Authentication.Tests/Mocks/MockCertificateAuthenticationFactory.cs index 65328c311d271..3caad13605a25 100644 --- a/src/Authentication/Common.Authentication.Tests/Mocks/MockCertificateAuthenticationFactory.cs +++ b/src/Authentication/Common.Authentication.Tests/Mocks/MockCertificateAuthenticationFactory.cs @@ -56,5 +56,11 @@ public SubscriptionCloudCredentials GetSubscriptionCloudCredentials(AzureContext { return new CertificateCloudCredentials(context.Subscription.Id.ToString(), Certificate); } + + + public Rest.ServiceClientCredentials GetServiceClientCredentials(AzureContext context) + { + throw new System.NotImplementedException(); + } } } diff --git a/src/Authentication/Common.Authentication.Tests/Mocks/MockClientFactory.cs b/src/Authentication/Common.Authentication.Tests/Mocks/MockClientFactory.cs index 5585fd7aa6d9e..444ef936d9abf 100644 --- a/src/Authentication/Common.Authentication.Tests/Mocks/MockClientFactory.cs +++ b/src/Authentication/Common.Authentication.Tests/Mocks/MockClientFactory.cs @@ -90,5 +90,16 @@ public List UserAgents throw new NotImplementedException(); } } + + public TClient CreateArmClient(AzureContext context, AzureEnvironment.Endpoint endpoint) where TClient : Rest.ServiceClient + { + var creds = AzureSession.AuthenticationFactory.GetServiceClientCredentials(context); + return CreateCustomArmClient(creds, context.Environment.GetEndpointAsUri(endpoint)); + } + + public TClient CreateCustomArmClient(params object[] parameters) where TClient : Rest.ServiceClient + { + return ManagementClients.FirstOrDefault(o => o is TClient) as TClient; + } } } diff --git a/src/Authentication/Common.Authentication.Tests/Mocks/MockTokenAuthenticationFactory.cs b/src/Authentication/Common.Authentication.Tests/Mocks/MockTokenAuthenticationFactory.cs index c7b323287cfe6..85f1e406f1633 100644 --- a/src/Authentication/Common.Authentication.Tests/Mocks/MockTokenAuthenticationFactory.cs +++ b/src/Authentication/Common.Authentication.Tests/Mocks/MockTokenAuthenticationFactory.cs @@ -15,6 +15,7 @@ using Microsoft.Azure; using Microsoft.Azure.Common.Authentication; using Microsoft.Azure.Common.Authentication.Models; +using Microsoft.Rest; using System; using System.Security; @@ -68,5 +69,11 @@ public SubscriptionCloudCredentials GetSubscriptionCloudCredentials(AzureContext { return new AccessTokenCredential(context.Subscription.Id, Token); } + + + public ServiceClientCredentials GetServiceClientCredentials(AzureContext context) + { + return new TokenCredentials(Token.AccessToken); + } } } diff --git a/src/Authentication/Common.Authentication.Tests/packages.config b/src/Authentication/Common.Authentication.Tests/packages.config index dd1fa243f43aa..b34cddfab6a5a 100644 --- a/src/Authentication/Common.Authentication.Tests/packages.config +++ b/src/Authentication/Common.Authentication.Tests/packages.config @@ -8,6 +8,8 @@ + + diff --git a/src/Authentication/Common.Authentication/Authentication/AdalConfiguration.cs b/src/Authentication/Common.Authentication/Authentication/AdalConfiguration.cs index 1032394029c89..5caadd6d56b0c 100644 --- a/src/Authentication/Common.Authentication/Authentication/AdalConfiguration.cs +++ b/src/Authentication/Common.Authentication/Authentication/AdalConfiguration.cs @@ -28,7 +28,7 @@ public class AdalConfiguration // These constants define the default values to use for AD authentication // against RDFE // - private const string PowerShellClientId = "1950a258-227b-4e31-a9cf-717495945fc2"; + public const string PowerShellClientId = "1950a258-227b-4e31-a9cf-717495945fc2"; public static readonly Uri PowerShellRedirectUri = new Uri("urn:ietf:wg:oauth:2.0:oob"); diff --git a/src/Authentication/Common.Authentication/Authentication/ServicePrincipalKeyStore.cs b/src/Authentication/Common.Authentication/Authentication/ServicePrincipalKeyStore.cs index c5f47119a5492..8932ceab8e34c 100644 --- a/src/Authentication/Common.Authentication/Authentication/ServicePrincipalKeyStore.cs +++ b/src/Authentication/Common.Authentication/Authentication/ServicePrincipalKeyStore.cs @@ -88,6 +88,10 @@ public static SecureString GetKey(string appId, string tenantId) } return null; } + catch + { + // we could be running in an environment that does not have credentials store + } finally { if (pCredential != IntPtr.Zero) @@ -95,6 +99,8 @@ public static SecureString GetKey(string appId, string tenantId) CredStore.NativeMethods.CredFree(pCredential); } } + + return null; } diff --git a/src/Authentication/Common.Authentication/Common.Authentication.csproj b/src/Authentication/Common.Authentication/Common.Authentication.csproj index cafe8403be862..a9d19cab33537 100644 --- a/src/Authentication/Common.Authentication/Common.Authentication.csproj +++ b/src/Authentication/Common.Authentication/Common.Authentication.csproj @@ -144,6 +144,14 @@ ..\..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.5.207081303-alpha\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll True + + ..\..\..\packages\Microsoft.Rest.ClientRuntime.1.1.1\lib\net45\Microsoft.Rest.ClientRuntime.dll + True + + + ..\..\..\packages\Microsoft.Rest.ClientRuntime.Azure.1.0.18-preview\lib\net45\Microsoft.Rest.ClientRuntime.Azure.dll + True + diff --git a/src/Authentication/Common.Authentication/Factories/AuthenticationFactory.cs b/src/Authentication/Common.Authentication/Factories/AuthenticationFactory.cs index 5238af8f55553..f4d78fc34d66f 100644 --- a/src/Authentication/Common.Authentication/Factories/AuthenticationFactory.cs +++ b/src/Authentication/Common.Authentication/Factories/AuthenticationFactory.cs @@ -18,6 +18,9 @@ using System.Linq; using System.Security; using Hyak.Common; +using Microsoft.Rest; +using Microsoft.Rest.Azure.Authentication; +using Microsoft.IdentityModel.Clients.ActiveDirectory; namespace Microsoft.Azure.Common.Authentication.Factories { @@ -32,7 +35,12 @@ public AuthenticationFactory() public ITokenProvider TokenProvider { get; set; } - public IAccessToken Authenticate(AzureAccount account, AzureEnvironment environment, string tenant, SecureString password, ShowDialog promptBehavior, + public IAccessToken Authenticate( + AzureAccount account, + AzureEnvironment environment, + string tenant, + SecureString password, + ShowDialog promptBehavior, AzureEnvironment.Endpoint resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId) { var configuration = GetAdalConfiguration(environment, tenant, resourceId); @@ -109,5 +117,79 @@ private AdalConfiguration GetAdalConfiguration(AzureEnvironment environment, str ValidateAuthority = !environment.OnPremise }; } + + public ServiceClientCredentials GetServiceClientCredentials(AzureContext context) + { + if (context.Subscription == null) + { + throw new ApplicationException(Resources.InvalidDefaultSubscription); + } + + if (context.Account == null) + { + throw new ArgumentException(Resources.AccountNotFound); + } + + if (context.Account.Type == AzureAccount.AccountType.Certificate) + { + throw new NotSupportedException(AzureAccount.AccountType.Certificate.ToString()); + } + + if (context.Account.Type == AzureAccount.AccountType.AccessToken) + { + return new TokenCredentials(context.Account.GetProperty(AzureAccount.Property.AccessToken)); + } + + var tenant = context.Subscription.GetPropertyAsArray(AzureSubscription.Property.Tenants) + .Intersect(context.Account.GetPropertyAsArray(AzureAccount.Property.Tenants)) + .FirstOrDefault(); + + if (tenant == null) + { + throw new ArgumentException(Resources.TenantNotFound); + } + + try + { + TracingAdapter.Information(Resources.UPNAuthenticationTrace, + context.Account.Id, context.Environment.Name, tenant); + + // TODO: When we will refactor the code, need to add tracing + /*TracingAdapter.Information(Resources.UPNAuthenticationTokenTrace, + token.LoginType, token.TenantId, token.UserId);*/ + + var env = new ActiveDirectoryEnvironment + { + AuthenticationEndpoint = context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ActiveDirectory), + TokenAudience = context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId), + ValidateAuthority = !context.Environment.OnPremise + }; + + if(context.Account.Type == AzureAccount.AccountType.User) + { + return new UserTokenCredentials( + AdalConfiguration.PowerShellClientId, + tenant, + AdalConfiguration.PowerShellRedirectUri, + env, + new PlatformParameters(PromptBehavior.Never, null), + AzureSession.TokenCache); + } + else if (context.Account.Type == AzureAccount.AccountType.ServicePrincipal) + { + return new Microsoft.Rest.Azure.Authentication.ApplicationTokenCredentials( + context.Account.Id, + tenant, + UserTokenProvider.ConvertToString(ServicePrincipalKeyStore.GetKey(context.Account.Id, tenant)), + env); + } + throw new NotSupportedException(context.Account.Type.ToString()); + } + catch (Exception ex) + { + TracingAdapter.Information(Resources.AdalAuthException, ex.Message); + throw new ArgumentException(Resources.InvalidSubscriptionState, ex); + } + } } } diff --git a/src/Authentication/Common.Authentication/Factories/ClientFactory.cs b/src/Authentication/Common.Authentication/Factories/ClientFactory.cs index 3b66d4d975404..eeaf37c4426f0 100644 --- a/src/Authentication/Common.Authentication/Factories/ClientFactory.cs +++ b/src/Authentication/Common.Authentication/Factories/ClientFactory.cs @@ -35,6 +35,50 @@ public ClientFactory() UserAgents = new List(); } + public virtual TClient CreateArmClient(AzureContext context, AzureEnvironment.Endpoint endpoint) where TClient : Microsoft.Rest.ServiceClient + { + if (context == null) + { + throw new ApplicationException(Resources.InvalidDefaultSubscription); + } + + var creds = AzureSession.AuthenticationFactory.GetServiceClientCredentials(context); + TClient client = CreateCustomArmClient(context.Environment.GetEndpointAsUri(endpoint), creds, new DelegatingHandler[]{}); + + var subscriptionId = typeof(TClient).GetProperty("SubscriptionId"); + if (subscriptionId != null && context.Subscription != null) + { + subscriptionId.SetValue(client, context.Subscription.Id.ToString()); + } + + return client; + } + + public virtual TClient CreateCustomArmClient(params object[] parameters) where TClient : Microsoft.Rest.ServiceClient + { + List types = new List(); + foreach (object obj in parameters) + { + types.Add(obj.GetType()); + } + + var constructor = typeof(TClient).GetConstructor(types.ToArray()); + + if (constructor == null) + { + throw new InvalidOperationException(string.Format(Resources.InvalidManagementClientType, typeof(TClient).Name)); + } + + TClient client = (TClient)constructor.Invoke(parameters); + + foreach (ProductInfoHeaderValue userAgent in UserAgents) + { + client.UserAgent.Add(userAgent); + } + + return client; + } + public virtual TClient CreateClient(AzureContext context, AzureEnvironment.Endpoint endpoint) where TClient : ServiceClient { if (context == null) diff --git a/src/Authentication/Common.Authentication/Interfaces/IAuthenticationFactory.cs b/src/Authentication/Common.Authentication/Interfaces/IAuthenticationFactory.cs index 7559523a781a6..5279afed7a983 100644 --- a/src/Authentication/Common.Authentication/Interfaces/IAuthenticationFactory.cs +++ b/src/Authentication/Common.Authentication/Interfaces/IAuthenticationFactory.cs @@ -13,6 +13,7 @@ // ---------------------------------------------------------------------------------- using Microsoft.Azure.Common.Authentication.Models; +using Microsoft.Rest; using System.Security; namespace Microsoft.Azure.Common.Authentication @@ -33,5 +34,7 @@ IAccessToken Authenticate(AzureAccount account, AzureEnvironment environment, st AzureEnvironment.Endpoint resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId); SubscriptionCloudCredentials GetSubscriptionCloudCredentials(AzureContext context); + + ServiceClientCredentials GetServiceClientCredentials(AzureContext context); } } diff --git a/src/Authentication/Common.Authentication/Interfaces/IClientFactory.cs b/src/Authentication/Common.Authentication/Interfaces/IClientFactory.cs index 75738c184d22d..b45c75a32c3ac 100644 --- a/src/Authentication/Common.Authentication/Interfaces/IClientFactory.cs +++ b/src/Authentication/Common.Authentication/Interfaces/IClientFactory.cs @@ -24,6 +24,10 @@ namespace Microsoft.Azure.Common.Authentication { public interface IClientFactory { + TClient CreateArmClient(AzureContext context, AzureEnvironment.Endpoint endpoint) where TClient : Microsoft.Rest.ServiceClient; + + TClient CreateCustomArmClient(params object[] parameters) where TClient : Microsoft.Rest.ServiceClient; + TClient CreateClient(AzureContext context, AzureEnvironment.Endpoint endpoint) where TClient : ServiceClient; TClient CreateClient(AzureProfile profile, AzureEnvironment.Endpoint endpoint) where TClient : ServiceClient; diff --git a/src/Authentication/Common.Authentication/Microsoft.Azure.Common.Authentication.nuget.proj b/src/Authentication/Common.Authentication/Microsoft.Azure.Common.Authentication.nuget.proj index b29dadc77395b..2e5bbe7021b3e 100644 --- a/src/Authentication/Common.Authentication/Microsoft.Azure.Common.Authentication.nuget.proj +++ b/src/Authentication/Common.Authentication/Microsoft.Azure.Common.Authentication.nuget.proj @@ -5,7 +5,7 @@ Microsoft.Azure.Common.Authentication --> - 1.1.0-preview + 1.1.1-preview $(MSBuildThisFileDirectory) diff --git a/src/Authentication/Common.Authentication/Properties/AssemblyInfo.cs b/src/Authentication/Common.Authentication/Properties/AssemblyInfo.cs index 57ffdc7319780..d894518ba2525 100644 --- a/src/Authentication/Common.Authentication/Properties/AssemblyInfo.cs +++ b/src/Authentication/Common.Authentication/Properties/AssemblyInfo.cs @@ -26,7 +26,7 @@ [assembly: CLSCompliant(false)] [assembly: Guid("4f3ab2e4-cc7a-43ac-bb15-f481fcf94d58")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.1.0")] #if CODESIGN [assembly: InternalsVisibleTo("Common.Authentication.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/Authentication/Common.Authentication/packages.config b/src/Authentication/Common.Authentication/packages.config index 917c99ba77d6e..d88435b03fd61 100644 --- a/src/Authentication/Common.Authentication/packages.config +++ b/src/Authentication/Common.Authentication/packages.config @@ -8,5 +8,7 @@ + + \ No newline at end of file