From 68252e44e171b33b2c25b3b53f9730fa96928187 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Mon, 27 Nov 2017 17:06:09 -0800 Subject: [PATCH 01/24] Adding AAD token support. --- .../Amqp/AmqpEventHubClient.cs | 30 ++- .../Amqp/AmqpServiceClient.cs | 2 +- .../EventHubClient.cs | 168 +++++++++++++- .../Microsoft.Azure.EventHubs.csproj | 3 + .../AzureActiveDirectoryTokenProvider.cs | 127 +++++++++++ .../Primitives/ClientConstants.cs | 8 + .../EventHubsConnectionStringBuilder.cs | 55 +++-- .../Primitives/ITokenProvider.cs | 23 ++ .../Primitives/JsonSecurityToken.cs | 24 ++ .../Primitives/SecurityToken.cs | 136 +++--------- .../Primitives/SharedAccessSignatureToken.cs | 170 ++++++++++++++ .../SharedAccessSignatureTokenProvider.cs | 133 +++-------- .../Primitives/TenantIdProvider.cs | 63 ++++++ .../Primitives/TokenProvider.cs | 208 +++++++++++++----- .../Resources.Designer.cs | 11 + src/Microsoft.Azure.EventHubs/Resources.resx | 3 + .../Client/MiscTests.cs | 41 ---- .../Client/TokenProviderTests.cs | 168 ++++++++++++++ 18 files changed, 1034 insertions(+), 339 deletions(-) create mode 100644 src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs create mode 100644 src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs create mode 100644 src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs create mode 100644 src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs create mode 100644 src/Microsoft.Azure.EventHubs/Primitives/TenantIdProvider.cs create mode 100644 test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs diff --git a/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs b/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs index 6a3f739..4e50c58 100644 --- a/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs @@ -5,7 +5,6 @@ namespace Microsoft.Azure.EventHubs.Amqp { using System; using System.Linq; - using System.Net; using System.Threading.Tasks; using Microsoft.Azure.Amqp.Sasl; using Microsoft.Azure.Amqp; @@ -26,17 +25,33 @@ public AmqpEventHubClient(EventHubsConnectionStringBuilder csb) if (!string.IsNullOrWhiteSpace(csb.SharedAccessSignature)) { - this.TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SharedAccessSignature); + this.InternalTokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SharedAccessSignature); } else { - this.TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SasKeyName, csb.SasKey); + this.InternalTokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SasKeyName, csb.SasKey); } this.CbsTokenProvider = new TokenProviderAdapter(this); this.ConnectionManager = new FaultTolerantAmqpObject(this.CreateConnectionAsync, this.CloseConnection); } + public AmqpEventHubClient( + Uri endpoint, + string entityPath, + ITokenProvider tokenProvider, + TimeSpan operationTimeout, + TransportType transportType) + : base(new EventHubsConnectionStringBuilder(endpoint, entityPath, operationTimeout, transportType)) + { + this.ContainerId = Guid.NewGuid().ToString("N"); + this.AmqpVersion = new Version(1, 0, 0, 0); + this.MaxFrameSize = AmqpConstants.DefaultMaxFrameSize; + this.InternalTokenProvider = tokenProvider; + this.CbsTokenProvider = new TokenProviderAdapter(this); + this.ConnectionManager = new FaultTolerantAmqpObject(this.CreateConnectionAsync, this.CloseConnection); + } + internal ICbsTokenProvider CbsTokenProvider { get; } internal FaultTolerantAmqpObject ConnectionManager { get; } @@ -47,7 +62,7 @@ public AmqpEventHubClient(EventHubsConnectionStringBuilder csb) uint MaxFrameSize { get; } - internal TokenProvider TokenProvider { get; } + internal ITokenProvider InternalTokenProvider { get; } internal override EventDataSender OnCreateEventSender(string partitionId) { @@ -110,7 +125,7 @@ internal static AmqpSettings CreateAmqpSettings( string sslHostName = null, bool useWebSockets = false, bool sslStreamUpgrade = false, - NetworkCredential networkCredential = null, + System.Net.NetworkCredential networkCredential = null, bool forceTokenProvider = true) { var settings = new AmqpSettings(); @@ -267,10 +282,9 @@ public TokenProviderAdapter(AmqpEventHubClient eventHubClient) public async Task GetTokenAsync(Uri namespaceAddress, string appliesTo, string[] requiredClaims) { string claim = requiredClaims?.FirstOrDefault(); - var tokenProvider = this.eventHubClient.TokenProvider; var timeout = this.eventHubClient.ConnectionStringBuilder.OperationTimeout; - var token = await tokenProvider.GetTokenAsync(appliesTo, claim, timeout).ConfigureAwait(false); - return new CbsToken(token.TokenValue, CbsConstants.ServiceBusSasTokenType, token.ExpiresAtUtc); + var token = await this.eventHubClient.InternalTokenProvider.GetTokenAsync(appliesTo, claim, timeout).ConfigureAwait(false); + return new CbsToken(token.TokenValue, token.TokenType, token.ExpiresAtUtc); } } } diff --git a/src/Microsoft.Azure.EventHubs/Amqp/AmqpServiceClient.cs b/src/Microsoft.Azure.EventHubs/Amqp/AmqpServiceClient.cs index f78640a..8ac7ef3 100644 --- a/src/Microsoft.Azure.EventHubs/Amqp/AmqpServiceClient.cs +++ b/src/Microsoft.Azure.EventHubs/Amqp/AmqpServiceClient.cs @@ -150,7 +150,7 @@ async Task GetTokenString() // when checking for token expiry. if (this.token == null || DateTime.UtcNow > this.token.ExpiresAtUtc.Subtract(TimeSpan.FromMinutes(5))) { - this.token = await this.eventHubClient.TokenProvider.GetTokenAsync( + this.token = await this.eventHubClient.InternalTokenProvider.GetTokenAsync( this.eventHubClient.ConnectionStringBuilder.Endpoint.AbsoluteUri, ClaimConstants.Listen, this.eventHubClient.ConnectionStringBuilder.OperationTimeout).ConfigureAwait(false); } diff --git a/src/Microsoft.Azure.EventHubs/EventHubClient.cs b/src/Microsoft.Azure.EventHubs/EventHubClient.cs index e13133f..f01b5c1 100644 --- a/src/Microsoft.Azure.EventHubs/EventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/EventHubClient.cs @@ -3,11 +3,12 @@ namespace Microsoft.Azure.EventHubs { - using Amqp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; + using Microsoft.Azure.EventHubs.Amqp; + using Microsoft.IdentityModel.Clients.ActiveDirectory; /// /// Anchor class - all EventHub client operations start here. @@ -70,6 +71,171 @@ public static EventHubClient CreateFromConnectionString(string connectionString) return Create(csb); } + /// + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, and token provider. + /// + /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Event Hub path + /// Token provider which will generate security tokens for authorization. + /// + public static EventHubClient Create(Uri endpoint, string entityPath, ITokenProvider tokenProvider) + { + return Create(endpoint, entityPath, tokenProvider, ClientConstants.DefaultOperationTimeout); + } + + /// + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, and token provider. + /// + /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Event Hub path + /// Token provider which will generate security tokens for authorization. + /// Operation timeout for Event Hubs operations. + /// Transport type on connection. + /// + public static EventHubClient Create( + Uri endpoint, + string entityPath, + ITokenProvider tokenProvider, + TimeSpan operationTimeout, + TransportType transportType = TransportType.Amqp) + { + if (endpoint == null) + { + throw Fx.Exception.ArgumentNull(nameof(endpoint)); + } + + if (string.IsNullOrWhiteSpace(entityPath)) + { + throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(entityPath)); + } + + if (tokenProvider == null) + { + throw Fx.Exception.ArgumentNull(nameof(tokenProvider)); + } + + EventHubsEventSource.Log.EventHubClientCreateStart(endpoint.Host, entityPath); + EventHubClient eventHubClient = new AmqpEventHubClient( + endpoint, + entityPath, + tokenProvider, + operationTimeout, + transportType); + EventHubsEventSource.Log.EventHubClientCreateStop(eventHubClient.ClientId); + return eventHubClient; + } + + /// + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, subscription-id, and client credential. + /// + /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Event Hub path + /// Azure subsciption identifier. + /// The app credential. + /// Operation timeout for Event Hubs operations. + /// Transport type on connection. + /// + public static EventHubClient Create( + Uri endpoint, + string entityPath, + string azureSubscriptionId, + ClientCredential clientCredential, + TimeSpan operationTimeout, + TransportType transportType = TransportType.Amqp) + { + TokenProvider tokenProvider = TokenProvider.CreateAadTokenProvider(azureSubscriptionId, clientCredential); + + return Create(endpoint, entityPath, tokenProvider, operationTimeout, transportType); + } + + /// + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, subscription-id, and client credential. + /// + /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Event Hub path + /// Azure subsciption identifier. + /// The client assertion certificate credential. + /// Operation timeout for Event Hubs operations. + /// Transport type on connection. + /// + public static EventHubClient Create( + Uri endpoint, + string entityPath, + string azureSubscriptionId, + ClientAssertionCertificate clientAssertionCertificate, + TimeSpan operationTimeout, + TransportType transportType = TransportType.Amqp) + { + TokenProvider tokenProvider = TokenProvider.CreateAadTokenProvider(azureSubscriptionId, clientAssertionCertificate); + + return Create(endpoint, entityPath, tokenProvider, operationTimeout, transportType); + } + + /// + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, subscription-id, and client credential. + /// + /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Event Hub path + /// Azure subsciption identifier. + /// ClientId for AAD. + /// The redrectUri on Client App. + /// Platform parameters + /// Operation timeout for Event Hubs operations. + /// Transport type on connection. + /// + public static EventHubClient Create( + Uri endpoint, + string entityPath, + string azureSubscriptionId, + string clientId, + Uri redirectUri, + IPlatformParameters platformParameters, + TimeSpan operationTimeout, + TransportType transportType = TransportType.Amqp) + { + TokenProvider tokenProvider = TokenProvider.CreateAadTokenProvider( + azureSubscriptionId, + clientId, + redirectUri, + platformParameters); + + return Create(endpoint, entityPath, tokenProvider, operationTimeout, transportType); + } + + /// + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, subscription-id, and client credential. + /// + /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Event Hub path + /// Azure subsciption identifier. + /// ClientId for AAD. + /// The redrectUri on Client App. + /// Platform parameters + /// User Identifier + /// Operation timeout for Event Hubs operations. + /// Transport type on connection. + /// + public static EventHubClient Create( + Uri endpoint, + string entityPath, + string azureSubscriptionId, + string clientId, + Uri redirectUri, + IPlatformParameters platformParameters, + UserIdentifier userIdentifier, + TimeSpan operationTimeout, + TransportType transportType = TransportType.Amqp) + { + TokenProvider tokenProvider = TokenProvider.CreateAadTokenProvider( + azureSubscriptionId, + clientId, + redirectUri, + platformParameters, + userIdentifier); + + return Create(endpoint, entityPath, tokenProvider, operationTimeout, transportType); + } + static EventHubClient Create(EventHubsConnectionStringBuilder csb) { if (string.IsNullOrWhiteSpace(csb.EntityPath)) diff --git a/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj b/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj index 80ba2c2..0815960 100644 --- a/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj +++ b/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj @@ -55,7 +55,10 @@ + + + diff --git a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs new file mode 100644 index 0000000..9cf2d33 --- /dev/null +++ b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Azure.EventHubs +{ + using System; + using System.Threading.Tasks; + using Microsoft.IdentityModel.Clients.ActiveDirectory; + + /// Represents the Azure Active Directory token provider for the Service Bus and Event Hubs. + public class AzureActiveDirectoryTokenProvider : TokenProvider + { + const int DefaultCacheSize = 100; + const string Audience = "https://eventhubs.azure.net/"; + + AsyncLock authContextLock = new AsyncLock(); + AuthenticationContext authContext; + readonly string azureSubscriptionId; + readonly ClientCredential clientCredential; +#if !UAP10_0 + readonly ClientAssertionCertificate clientAssertionCertificate; +#endif + readonly string clientId; + readonly Uri redirectUri; + readonly IPlatformParameters platformParameters; + readonly UserIdentifier userIdentifier; + + enum AuthType + { + ClientCredential, + UserPasswordCredential, + ClientAssertionCertificate, + InteractiveUserLogin + } + + readonly AuthType authType; + + internal AzureActiveDirectoryTokenProvider(string azureSubscriptionId, ClientCredential credential) + : this(authContext: null, credential: credential) + { + this.azureSubscriptionId = azureSubscriptionId; + } + + internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, ClientCredential credential) + { + this.clientCredential = credential; + this.authContext = authContext; + this.authType = AuthType.ClientCredential; + this.clientId = clientCredential.ClientId; + } + +#if !UAP10_0 + internal AzureActiveDirectoryTokenProvider(string azureSubscriptionId, ClientAssertionCertificate clientAssertionCertificate) + : this(authContext: null, clientAssertionCertificate: clientAssertionCertificate) + { + this.azureSubscriptionId = azureSubscriptionId; + } + + internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, ClientAssertionCertificate clientAssertionCertificate) + { + this.clientAssertionCertificate = clientAssertionCertificate; + this.authContext = authContext; + this.authType = AuthType.ClientAssertionCertificate; + this.clientId = clientAssertionCertificate.ClientId; + } +#endif + + internal AzureActiveDirectoryTokenProvider(string azureSubscriptionId, string clientId, Uri redirectUri, IPlatformParameters platformParameters, UserIdentifier userIdentifier) + : this(authContext: null, clientId: clientId, redirectUri: redirectUri, platformParameters: platformParameters, userIdentifier: userIdentifier) + { + this.azureSubscriptionId = azureSubscriptionId; + } + + internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, string clientId, Uri redirectUri, IPlatformParameters platformParameters, UserIdentifier userIdentifier) + { + this.authContext = authContext; + this.clientId = clientId; + this.redirectUri = redirectUri; + this.platformParameters = platformParameters; + this.userIdentifier = userIdentifier; + this.authType = AuthType.InteractiveUserLogin; + } + + /// + /// Gets a for the given audience and duration. + /// + /// The URI which the access token applies to + /// The request action + /// The time span that specifies the timeout value for the message that gets the security token + /// + public override async Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout) + { + AuthenticationResult authResult; + + if (this.authContext == null) + { + using (await this.authContextLock.LockAsync()) + { + string authority = await TenantIdProvider.GetTenantUri(this.azureSubscriptionId); + this.authContext = new AuthenticationContext(authority); + } + } + + switch (this.authType) + { + case AuthType.ClientCredential: + authResult = await this.authContext.AcquireTokenAsync(Audience, this.clientCredential); + break; + +#if !UAP10_0 + case AuthType.ClientAssertionCertificate: + authResult = await this.authContext.AcquireTokenAsync(Audience, this.clientAssertionCertificate); + break; +#endif + + case AuthType.InteractiveUserLogin: + authResult = await this.authContext.AcquireTokenAsync(Audience, this.clientId, this.redirectUri, this.platformParameters, this.userIdentifier); + break; + + default: + throw new NotSupportedException(); + } + + return new JsonSecurityToken(authResult.AccessToken, appliesTo); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Azure.EventHubs/Primitives/ClientConstants.cs b/src/Microsoft.Azure.EventHubs/Primitives/ClientConstants.cs index a968675..639805b 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/ClientConstants.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/ClientConstants.cs @@ -3,9 +3,17 @@ namespace Microsoft.Azure.EventHubs { + using System; + static class ClientConstants { public const int TimerToleranceInSeconds = 5; public const int ServerBusyBaseSleepTimeInSecs = 4; + + public const string SasTokenType = "servicebus.windows.net:sastoken"; + public const string JsonWebTokenType = "jwt"; + + public static TimeSpan DefaultOperationTimeout = TimeSpan.FromMinutes(1); + public static TransportType DefaultTransportType = TransportType.Amqp; } } diff --git a/src/Microsoft.Azure.EventHubs/Primitives/EventHubsConnectionStringBuilder.cs b/src/Microsoft.Azure.EventHubs/Primitives/EventHubsConnectionStringBuilder.cs index 8c5aaea..46f2979 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/EventHubsConnectionStringBuilder.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/EventHubsConnectionStringBuilder.cs @@ -49,8 +49,6 @@ public class EventHubsConnectionStringBuilder const char KeyValueSeparator = '='; const char KeyValuePairDelimiter = ';'; - static readonly TimeSpan DefaultOperationTimeout = TimeSpan.FromMinutes(1); - static readonly TransportType DefaultTransportType = TransportType.Amqp; static readonly string EndpointScheme = "amqps"; static readonly string EndpointConfigName = "Endpoint"; static readonly string SharedAccessKeyNameConfigName = "SharedAccessKeyName"; @@ -73,7 +71,7 @@ public EventHubsConnectionStringBuilder( string entityPath, string sharedAccessKeyName, string sharedAccessKey) - : this (endpointAddress, entityPath, sharedAccessKeyName, sharedAccessKey, DefaultOperationTimeout) + : this (endpointAddress, entityPath, sharedAccessKeyName, sharedAccessKey, ClientConstants.DefaultOperationTimeout) { } @@ -124,10 +122,31 @@ public EventHubsConnectionStringBuilder( this.SharedAccessSignature = sharedAccessSignature; } - EventHubsConnectionStringBuilder( + /// + /// ConnectionString format: + /// Endpoint=sb://namespace_DNS_Name;EntityPath=EVENT_HUB_NAME;SharedAccessKeyName=SHARED_ACCESS_KEY_NAME;SharedAccessKey=SHARED_ACCESS_KEY + /// + /// Event Hubs ConnectionString + public EventHubsConnectionStringBuilder(string connectionString) + { + if (string.IsNullOrWhiteSpace(connectionString)) + { + throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(connectionString)); + } + + // Assign default values. + this.OperationTimeout = ClientConstants.DefaultOperationTimeout; + this.TransportType = TransportType.Amqp; + + // Parse the connection string now and override default values if any provided. + this.ParseConnectionString(connectionString); + } + + internal EventHubsConnectionStringBuilder( Uri endpointAddress, string entityPath, - TimeSpan operationTimeout) + TimeSpan operationTimeout, + TransportType transportType = TransportType.Amqp) { if (endpointAddress == null) { @@ -147,27 +166,7 @@ public EventHubsConnectionStringBuilder( this.EntityPath = entityPath; this.OperationTimeout = operationTimeout; - this.TransportType = DefaultTransportType; - } - - /// - /// ConnectionString format: - /// Endpoint=sb://namespace_DNS_Name;EntityPath=EVENT_HUB_NAME;SharedAccessKeyName=SHARED_ACCESS_KEY_NAME;SharedAccessKey=SHARED_ACCESS_KEY - /// - /// Event Hubs ConnectionString - public EventHubsConnectionStringBuilder(string connectionString) - { - if (string.IsNullOrWhiteSpace(connectionString)) - { - throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(connectionString)); - } - - // Assign default values. - this.OperationTimeout = DefaultOperationTimeout; - this.TransportType = TransportType.Amqp; - - // Parse the connection string now and override default values if any provided. - this.ParseConnectionString(connectionString); + this.TransportType = transportType; } /// @@ -253,12 +252,12 @@ public override string ToString() connectionStringBuilder.Append($"{SharedAccessSignatureConfigName}{KeyValueSeparator}{this.SharedAccessSignature}{KeyValuePairDelimiter}"); } - if (this.OperationTimeout != DefaultOperationTimeout) + if (this.OperationTimeout != ClientConstants.DefaultOperationTimeout) { connectionStringBuilder.Append($"{OperationTimeoutConfigName}{KeyValueSeparator}{this.OperationTimeout}{KeyValuePairDelimiter}"); } - if (this.TransportType != DefaultTransportType) + if (this.TransportType != ClientConstants.DefaultTransportType) { connectionStringBuilder.Append($"{TransportTypeConfigName}{KeyValueSeparator}{TransportType}{KeyValuePairDelimiter}"); } diff --git a/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs new file mode 100644 index 0000000..4617d26 --- /dev/null +++ b/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Azure.EventHubs +{ + /// + /// Provides interface definition of a token provider. + /// + public interface ITokenProvider + { + /// + /// Gets a . + /// + /// The URI which the access token applies to + /// The request action + /// The time span that specifies the timeout value for the message that gets the security token + /// + Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout); + } +} diff --git a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs new file mode 100644 index 0000000..68ae0df --- /dev/null +++ b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Azure.EventHubs +{ + using System; + using System.Collections.ObjectModel; + using System.IdentityModel.Tokens; + using System.IdentityModel.Tokens.Jwt; + + class JsonSecurityToken : SecurityToken + { + JwtSecurityToken internalToken; + + public JsonSecurityToken(string rawToken, string audience) + { + this.internalToken = new JwtSecurityToken(rawToken); + this.tokenType = ClientConstants.JsonWebTokenType; + this.token = rawToken; + this.expiresAtUtc = this.internalToken.ValidTo; + this.audience = audience; + } + } +} diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs index 9223606..992538e 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs @@ -14,38 +14,28 @@ namespace Microsoft.Azure.EventHubs /// public class SecurityToken { - // per Simple Web Token draft specification - private const string TokenAudience = "Audience"; - private const string TokenExpiresOn = "ExpiresOn"; - private const string TokenIssuer = "Issuer"; - private const string TokenDigest256 = "HMACSHA256"; + /// + /// Token literal + /// + protected string token; - const string InternalExpiresOnFieldName = "ExpiresOn"; - const string InternalAudienceFieldName = TokenAudience; - const string InternalKeyValueSeparator = "="; - const string InternalPairSeparator = "&"; - static readonly Func Decoder = WebUtility.UrlDecode; - static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - readonly string token; - readonly DateTime expiresAtUtc; - readonly string audience; + /// + /// Expiry date-time + /// + protected DateTime expiresAtUtc; + + /// + /// Token audience + /// + protected string audience; /// - /// Creates a new instance of the class. + /// Token type /// - /// The token - /// The expiration time - /// The audience - public SecurityToken(string tokenString, DateTime expiresAtUtc, string audience) - { - if (tokenString == null || audience == null) - { - throw Fx.Exception.ArgumentNull(tokenString == null ? nameof(tokenString) : nameof(audience)); - } + protected string tokenType; - this.token = tokenString; - this.expiresAtUtc = expiresAtUtc; - this.audience = audience; + internal SecurityToken() + { } /// @@ -53,31 +43,24 @@ public SecurityToken(string tokenString, DateTime expiresAtUtc, string audience) /// /// The token /// The expiration time - public SecurityToken(string tokenString, DateTime expiresAtUtc) + /// The audience + /// The type of the token + public SecurityToken(string tokenString, DateTime expiresAtUtc, string audience, string tokenType = ClientConstants.SasTokenType) { - if (tokenString == null) + if (string.IsNullOrEmpty(tokenString)) { - throw Fx.Exception.ArgumentNull(nameof(tokenString)); + throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(tokenString)); } - this.token = tokenString; - this.expiresAtUtc = expiresAtUtc; - this.audience = GetAudienceFromToken(tokenString); - } - - /// - /// Creates a new instance of the class. - /// - /// The token - public SecurityToken(string tokenString) - { - if (tokenString == null) + if (string.IsNullOrEmpty(audience)) { - throw Fx.Exception.ArgumentNull(nameof(tokenString)); + throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(audience)); } this.token = tokenString; - GetExpirationDateAndAudienceFromToken(tokenString, out this.expiresAtUtc, out this.audience); + this.expiresAtUtc = expiresAtUtc; + this.audience = audience; + this.tokenType = tokenType; } /// @@ -90,69 +73,14 @@ public SecurityToken(string tokenString) /// public DateTime ExpiresAtUtc => this.expiresAtUtc; - /// - protected virtual string ExpiresOnFieldName => InternalExpiresOnFieldName; - - /// - protected virtual string AudienceFieldName => InternalAudienceFieldName; - - /// - protected virtual string KeyValueSeparator => InternalKeyValueSeparator; - - /// - protected virtual string PairSeparator => InternalPairSeparator; - /// /// Gets the actual token. /// - public object TokenValue => this.token; - - string GetAudienceFromToken(string token) - { - string audience; - IDictionary decodedToken = Decode(token, Decoder, Decoder, this.KeyValueSeparator, this.PairSeparator); - if (!decodedToken.TryGetValue(AudienceFieldName, out audience)) - { - throw new FormatException(Resources.TokenMissingAudience); - } - - return audience; - } - - void GetExpirationDateAndAudienceFromToken(string token, out DateTime expiresOn, out string audience) - { - string expiresIn; - IDictionary decodedToken = Decode(token, Decoder, Decoder, this.KeyValueSeparator, this.PairSeparator); - if (!decodedToken.TryGetValue(ExpiresOnFieldName, out expiresIn)) - { - throw new FormatException(Resources.TokenMissingExpiresOn); - } + public virtual string TokenValue => this.token; - if (!decodedToken.TryGetValue(AudienceFieldName, out audience)) - { - throw new FormatException(Resources.TokenMissingAudience); - } - - expiresOn = (EpochTime + TimeSpan.FromSeconds(double.Parse(expiresIn, CultureInfo.InvariantCulture))); - } - - static IDictionary Decode(string encodedString, Func keyDecoder, Func valueDecoder, string keyValueSeparator, string pairSeparator) - { - IDictionary dictionary = new Dictionary(); - IEnumerable valueEncodedPairs = encodedString.Split(new[] { pairSeparator }, StringSplitOptions.None); - foreach (string valueEncodedPair in valueEncodedPairs) - { - string[] pair = valueEncodedPair.Split(new[] { keyValueSeparator }, StringSplitOptions.None); - if (pair.Length != 2) - { - throw new FormatException(Resources.InvalidEncoding); - } - - dictionary.Add(keyDecoder(pair[0]), valueDecoder(pair[1])); - } - - return dictionary; - } + /// + /// Gets the token type. + /// + public virtual string TokenType => this.tokenType; } - } diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs new file mode 100644 index 0000000..8280149 --- /dev/null +++ b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Azure.EventHubs +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Net; + + /// + /// A WCF SecurityToken that wraps a Shared Access Signature + /// + class SharedAccessSignatureToken : SecurityToken + { + internal const string SharedAccessSignature = "SharedAccessSignature"; + internal const string SignedResource = "sr"; + internal const string Signature = "sig"; + internal const string SignedKeyName = "skn"; + internal const string SignedExpiry = "se"; + internal const int MaxKeyNameLength = 256; + internal const int MaxKeyLength = 256; + + const string SignedResourceFullFieldName = SharedAccessSignature + " " + SignedResource; + const string SasPairSeparator = "&"; + const string SasKeyValueSeparator = "="; + + static readonly Func Decoder = WebUtility.UrlDecode; + + /// + /// Creates a new instance of the class. + /// + /// The token + public SharedAccessSignatureToken(string tokenString) + { + if (tokenString == null) + { + throw Fx.Exception.ArgumentNull(nameof(tokenString)); + } + + this.token = tokenString; + this.tokenType = ClientConstants.SasTokenType; + GetExpirationDateAndAudienceFromToken(tokenString, out this.expiresAtUtc, out this.audience); + } + + /// + /// Creates a new instance of the class. + /// + /// The token + /// The expiration time + public SharedAccessSignatureToken(string tokenString, DateTime expiresAtUtc) + : base(tokenString, expiresAtUtc, GetAudienceFromToken(tokenString), ClientConstants.SasTokenType) + { + } + + internal static void Validate(string sharedAccessSignature) + { + if (string.IsNullOrEmpty(sharedAccessSignature)) + { + throw new ArgumentNullException(nameof(sharedAccessSignature)); + } + + IDictionary parsedFields = ExtractFieldValues(sharedAccessSignature); + + string signature; + if (!parsedFields.TryGetValue(Signature, out signature)) + { + throw new ArgumentNullException(Signature); + } + + string expiry; + if (!parsedFields.TryGetValue(SignedExpiry, out expiry)) + { + throw new ArgumentNullException(SignedExpiry); + } + + string keyName; + if (!parsedFields.TryGetValue(SignedKeyName, out keyName)) + { + throw new ArgumentNullException(SignedKeyName); + } + + string encodedAudience; + if (!parsedFields.TryGetValue(SignedResource, out encodedAudience)) + { + throw new ArgumentNullException(SignedResource); + } + } + + static IDictionary ExtractFieldValues(string sharedAccessSignature) + { + string[] tokenLines = sharedAccessSignature.Split(); + + if (!string.Equals(tokenLines[0].Trim(), SharedAccessSignature, StringComparison.OrdinalIgnoreCase) || tokenLines.Length != 2) + { + throw new ArgumentNullException(nameof(sharedAccessSignature)); + } + + IDictionary parsedFields = new Dictionary(StringComparer.OrdinalIgnoreCase); + string[] tokenFields = tokenLines[1].Trim().Split(new[] { SasPairSeparator }, StringSplitOptions.None); + + foreach (string tokenField in tokenFields) + { + if (tokenField != string.Empty) + { + string[] fieldParts = tokenField.Split(new[] { SasKeyValueSeparator }, StringSplitOptions.None); + if (string.Equals(fieldParts[0], SignedResource, StringComparison.OrdinalIgnoreCase)) + { + // We need to preserve the casing of the escape characters in the audience, + // so defer decoding the URL until later. + parsedFields.Add(fieldParts[0], fieldParts[1]); + } + else + { + parsedFields.Add(fieldParts[0], WebUtility.UrlDecode(fieldParts[1])); + } + } + } + + return parsedFields; + } + + static string GetAudienceFromToken(string token) + { + string audience; + IDictionary decodedToken = Decode(token, Decoder, Decoder, SasKeyValueSeparator, SasPairSeparator); + if (!decodedToken.TryGetValue(SignedResourceFullFieldName, out audience)) + { + throw new FormatException(Resources.TokenMissingAudience); + } + + return audience; + } + + static void GetExpirationDateAndAudienceFromToken(string token, out DateTime expiresOn, out string audience) + { + string expiresIn; + IDictionary decodedToken = Decode(token, Decoder, Decoder, SasKeyValueSeparator, SasPairSeparator); + if (!decodedToken.TryGetValue(SignedExpiry, out expiresIn)) + { + throw new FormatException(Resources.TokenMissingExpiresOn); + } + + if (!decodedToken.TryGetValue(SignedResourceFullFieldName, out audience)) + { + throw new FormatException(Resources.TokenMissingAudience); + } + + expiresOn = (SharedAccessSignatureTokenProvider.EpochTime + TimeSpan.FromSeconds(double.Parse(expiresIn, CultureInfo.InvariantCulture))); + } + + static IDictionary Decode(string encodedString, Func keyDecoder, Func valueDecoder, string keyValueSeparator, string pairSeparator) + { + IDictionary dictionary = new Dictionary(); + IEnumerable valueEncodedPairs = encodedString.Split(new[] { pairSeparator }, StringSplitOptions.None); + foreach (string valueEncodedPair in valueEncodedPairs) + { + string[] pair = valueEncodedPair.Split(new[] { keyValueSeparator }, StringSplitOptions.None); + if (pair.Length != 2) + { + throw new FormatException(Resources.InvalidEncoding); + } + + dictionary.Add(keyDecoder(pair[0]), valueDecoder(pair[1])); + } + + return dictionary; + } + } +} diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureTokenProvider.cs index 187df6f..2e8f94d 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureTokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureTokenProvider.cs @@ -17,22 +17,33 @@ namespace Microsoft.Azure.EventHubs /// public class SharedAccessSignatureTokenProvider : TokenProvider { + const TokenScope DefaultTokenScope = TokenScope.Entity; + + internal static readonly TimeSpan DefaultTokenTimeout = TimeSpan.FromMinutes(60); + /// /// Represents 00:00:00 UTC Thursday 1, January 1970. /// public static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + readonly byte[] encodedSharedAccessKey; readonly string keyName; readonly TimeSpan tokenTimeToLive; + readonly TokenScope tokenScope; readonly string sharedAccessSignature; + internal static readonly Func MessagingTokenProviderKeyEncoder = Encoding.UTF8.GetBytes; internal SharedAccessSignatureTokenProvider(string sharedAccessSignature) - : base(TokenScope.Entity) { SharedAccessSignatureToken.Validate(sharedAccessSignature); this.sharedAccessSignature = sharedAccessSignature; } + internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TokenScope tokenScope = TokenScope.Entity) + : this(keyName, sharedAccessKey, MessagingTokenProviderKeyEncoder, DefaultTokenTimeout, tokenScope) + { + } + internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive, TokenScope tokenScope = TokenScope.Entity) : this(keyName, sharedAccessKey, MessagingTokenProviderKeyEncoder, tokenTimeToLive, tokenScope) { @@ -45,7 +56,6 @@ internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessK /// /// protected SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, Func customKeyEncoder, TimeSpan tokenTimeToLive, TokenScope tokenScope) - : base(tokenScope) { if (string.IsNullOrEmpty(keyName)) { @@ -76,15 +86,20 @@ protected SharedAccessSignatureTokenProvider(string keyName, string sharedAccess this.encodedSharedAccessKey = customKeyEncoder != null ? customKeyEncoder(sharedAccessKey) : MessagingTokenProviderKeyEncoder(sharedAccessKey); + this.tokenScope = tokenScope; } - /// - /// - /// - /// - /// - protected override Task OnGetTokenAsync(string appliesTo, string action, TimeSpan timeout) + /// + /// Gets a for the given audience and duration. + /// + /// The URI which the access token applies to + /// The request action + /// The time span that specifies the timeout value for the message that gets the security token + /// + public override Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout) { + TimeoutHelper.ThrowIfNegativeArgument(timeout); + appliesTo = NormalizeAppliesTo(appliesTo); string tokenString = this.BuildSignature(appliesTo); var securityToken = new SharedAccessSignatureToken(tokenString); return Task.FromResult(securityToken); @@ -104,6 +119,11 @@ protected virtual string BuildSignature(string targetUri) : this.sharedAccessSignature; } + string NormalizeAppliesTo(string appliesTo) + { + return EventHubsUriHelper.NormalizeUri(appliesTo, "http", true, stripPath: this.tokenScope == TokenScope.Namespace, ensureTrailingSlash: true); + } + static class SharedAccessSignatureBuilder { [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Uris are normalized to lowercase")] @@ -152,102 +172,5 @@ static string Sign(string requestString, byte[] encodedSharedAccessKey) } } } - - /// - /// A WCF SecurityToken that wraps a Shared Access Signature - /// - class SharedAccessSignatureToken : SecurityToken - { - public const int MaxKeyNameLength = 256; - public const int MaxKeyLength = 256; - public const string SharedAccessSignature = "SharedAccessSignature"; - public const string SignedResource = "sr"; - public const string Signature = "sig"; - public const string SignedKeyName = "skn"; - public const string SignedExpiry = "se"; - public const string SignedResourceFullFieldName = SharedAccessSignature + " " + SignedResource; - public const string SasKeyValueSeparator = "="; - public const string SasPairSeparator = "&"; - - public SharedAccessSignatureToken(string tokenString) - : base(tokenString) - { - } - - protected override string AudienceFieldName => SignedResourceFullFieldName; - - protected override string ExpiresOnFieldName => SignedExpiry; - - protected override string KeyValueSeparator => SasKeyValueSeparator; - - protected override string PairSeparator => SasPairSeparator; - - internal static void Validate(string sharedAccessSignature) - { - if (string.IsNullOrEmpty(sharedAccessSignature)) - { - throw new ArgumentNullException(nameof(sharedAccessSignature)); - } - - IDictionary parsedFields = ExtractFieldValues(sharedAccessSignature); - - string signature; - if (!parsedFields.TryGetValue(Signature, out signature)) - { - throw new ArgumentNullException(Signature); - } - - string expiry; - if (!parsedFields.TryGetValue(SignedExpiry, out expiry)) - { - throw new ArgumentNullException(SignedExpiry); - } - - string keyName; - if (!parsedFields.TryGetValue(SignedKeyName, out keyName)) - { - throw new ArgumentNullException(SignedKeyName); - } - - string encodedAudience; - if (!parsedFields.TryGetValue(SignedResource, out encodedAudience)) - { - throw new ArgumentNullException(SignedResource); - } - } - - static IDictionary ExtractFieldValues(string sharedAccessSignature) - { - string[] tokenLines = sharedAccessSignature.Split(); - - if (!string.Equals(tokenLines[0].Trim(), SharedAccessSignature, StringComparison.OrdinalIgnoreCase) || tokenLines.Length != 2) - { - throw new ArgumentNullException(nameof(sharedAccessSignature)); - } - - IDictionary parsedFields = new Dictionary(StringComparer.OrdinalIgnoreCase); - string[] tokenFields = tokenLines[1].Trim().Split(new[] { SasPairSeparator }, StringSplitOptions.None); - - foreach (string tokenField in tokenFields) - { - if (tokenField != string.Empty) - { - string[] fieldParts = tokenField.Split(new[] { SasKeyValueSeparator }, StringSplitOptions.None); - if (string.Equals(fieldParts[0], SignedResource, StringComparison.OrdinalIgnoreCase)) - { - // We need to preserve the casing of the escape characters in the audience, - // so defer decoding the URL until later. - parsedFields.Add(fieldParts[0], fieldParts[1]); - } - else - { - parsedFields.Add(fieldParts[0], WebUtility.UrlDecode(fieldParts[1])); - } - } - } - - return parsedFields; - } - } } } diff --git a/src/Microsoft.Azure.EventHubs/Primitives/TenantIdProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/TenantIdProvider.cs new file mode 100644 index 0000000..cc8ebf9 --- /dev/null +++ b/src/Microsoft.Azure.EventHubs/Primitives/TenantIdProvider.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Azure.EventHubs +{ + using System; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Threading.Tasks; + + class TenantIdProvider + { + const string ArmApiVersion = "2016-06-01"; + static readonly HttpClient httpClient; + + static TenantIdProvider() + { + httpClient = new HttpClient(); + } + + public static async Task GetTenantUri(string subscriptionId, string armEndpoint = "https://management.azure.com/") + { + Uri armUri = new Uri($"{armEndpoint}subscriptions/{subscriptionId}?api-version={ArmApiVersion}"); + + var request = new HttpRequestMessage + { + RequestUri = armUri, + Method = HttpMethod.Get, + }; + + string tenantUri; + + using (var response = await httpClient.SendAsync(request)) + { + string responseContent = await response.Content.ReadAsStringAsync(); + + var authHeader = response.Headers.GetValues("WWW-Authenticate").FirstOrDefault(); + if (string.IsNullOrEmpty(authHeader)) + { + throw new InvalidOperationException(string.Format(Resources.TenantIdLookupFailed, "'WWW-Authenticate' cannot be located from the authHeader")); + } + + /* e.g. + WWW-Authenticate: Bearer authorization_uri="https://login.windows.net/b06452b1-437c-4d7d-8363-54769cb614fa", error="invalid_token", error_description="The access token is from the wrong issuer. + It must match the tenant associated with this subscription. Please use correct authority to get the token." + */ + string authUri = "authorization_uri"; + int indexOfAuthUri = authHeader.IndexOf(authUri, StringComparison.OrdinalIgnoreCase); + + if (indexOfAuthUri == -1) + { + throw new InvalidOperationException(string.Format(Resources.TenantIdLookupFailed, "'authorization_uri' cannot be located from the authHeader")); + } + + int indexOfTenantUri = authHeader.IndexOf("\"", indexOfAuthUri, StringComparison.OrdinalIgnoreCase) + 1; + tenantUri = authHeader.Substring(indexOfTenantUri, authHeader.IndexOf("\"", indexOfTenantUri + 1, StringComparison.OrdinalIgnoreCase) - indexOfTenantUri); + } + + return tenantUri; + } + } +} diff --git a/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs index c29f505..b91c750 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs @@ -4,40 +4,14 @@ namespace Microsoft.Azure.EventHubs { using System; - using System.Text; using System.Threading.Tasks; + using Microsoft.IdentityModel.Clients.ActiveDirectory; /// /// This abstract base class can be extended to implement additional token providers. /// - public abstract class TokenProvider + public abstract class TokenProvider : ITokenProvider { - internal static readonly TimeSpan DefaultTokenTimeout = TimeSpan.FromMinutes(60); - internal static readonly Func MessagingTokenProviderKeyEncoder = Encoding.UTF8.GetBytes; - const TokenScope DefaultTokenScope = TokenScope.Entity; - - /// - protected TokenProvider() - : this(TokenProvider.DefaultTokenScope) - { - } - - /// - /// - protected TokenProvider(TokenScope tokenScope) - { - this.TokenScope = tokenScope; - this.ThisLock = new object(); - } - - /// - /// Gets the scope or permissions associated with the token. - /// - public TokenScope TokenScope { get; } - - /// - protected object ThisLock { get; } - /// /// Construct a TokenProvider based on a sharedAccessSignature. /// @@ -56,7 +30,7 @@ public static TokenProvider CreateSharedAccessSignatureTokenProvider(string shar /// A TokenProvider initialized with the provided RuleId and Password public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey) { - return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, DefaultTokenTimeout); + return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey); } //internal static TokenProvider CreateIoTTokenProvider(string keyName, string sharedAccessKey) @@ -85,7 +59,7 @@ public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyN /// A TokenProvider initialized with the provided RuleId and Password public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TokenScope tokenScope) { - return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, DefaultTokenTimeout, tokenScope); + return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, tokenScope); } /// @@ -101,6 +75,158 @@ public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyN return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, tokenTimeToLive, tokenScope); } + /// Creates an Azure Active Directory token provider. + /// Azure subsciption identifier. + /// The app credential. + /// The for returning Json web token. + public static TokenProvider CreateAadTokenProvider(string azureSubscriptionId, ClientCredential clientCredential) + { + if (string.IsNullOrEmpty(azureSubscriptionId)) + { + throw new ArgumentNullException(nameof(azureSubscriptionId)); + } + + if (clientCredential == null) + { + throw new ArgumentNullException(nameof(clientCredential)); + } + + return new AzureActiveDirectoryTokenProvider(azureSubscriptionId, clientCredential); + } + + /// Creates an Azure Active Directory token provider. + /// AuthenticationContext for AAD. + /// The app credential. + /// The for returning Json web token. + public static TokenProvider CreateAadTokenProvider(AuthenticationContext authContext, ClientCredential clientCredential) + { + if (authContext == null) + { + throw new ArgumentNullException(nameof(authContext)); + } + + if (clientCredential == null) + { + throw new ArgumentNullException(nameof(clientCredential)); + } + + return new AzureActiveDirectoryTokenProvider(authContext, clientCredential); + } + + /// Creates an Azure Active Directory token provider. + /// Azure subsciption identifier. + /// ClientId for AAD. + /// The redrectUri on Client App. + /// Platform parameters + /// User Identifier + /// The for returning Json web token. + public static TokenProvider CreateAadTokenProvider( + string azureSubscriptionId, + string clientId, + Uri redirectUri, + IPlatformParameters platformParameters, + UserIdentifier userIdentifier = null) + { + if (string.IsNullOrEmpty(azureSubscriptionId)) + { + throw new ArgumentNullException(nameof(azureSubscriptionId)); + } + + if (string.IsNullOrEmpty(clientId)) + { + throw new ArgumentNullException(nameof(clientId)); + } + + if (redirectUri == null) + { + throw new ArgumentNullException(nameof(redirectUri)); + } + + if (platformParameters == null) + { + throw new ArgumentNullException(nameof(platformParameters)); + } + + return new AzureActiveDirectoryTokenProvider(azureSubscriptionId, clientId, redirectUri, platformParameters, userIdentifier); + } + + /// Creates an Azure Active Directory token provider. + /// AuthenticationContext for AAD. + /// ClientId for AAD. + /// The redrectUri on Client App. + /// Platform parameters + /// User Identifier + /// The for returning Json web token. + public static TokenProvider CreateAadTokenProvider( + AuthenticationContext authContext, + string clientId, + Uri redirectUri, + IPlatformParameters platformParameters, + UserIdentifier userIdentifier = null) + { + if (authContext == null) + { + throw new ArgumentNullException(nameof(authContext)); + } + + if (string.IsNullOrEmpty(clientId)) + { + throw new ArgumentNullException(nameof(clientId)); + } + + if (redirectUri == null) + { + throw new ArgumentNullException(nameof(redirectUri)); + } + + if (platformParameters == null) + { + throw new ArgumentNullException(nameof(platformParameters)); + } + + return new AzureActiveDirectoryTokenProvider(authContext, clientId, redirectUri, platformParameters, userIdentifier); + } + +#if !UAP10_0 + /// Creates an Azure Active Directory token provider. + /// Azure subsciption identifier. + /// The client assertion certificate credential. + /// The for returning Json web token. + public static TokenProvider CreateAadTokenProvider(string azureSubscriptionId, ClientAssertionCertificate clientAssertionCertificate) + { + if (string.IsNullOrEmpty(azureSubscriptionId)) + { + throw new ArgumentNullException(nameof(azureSubscriptionId)); + } + + if (clientAssertionCertificate == null) + { + throw new ArgumentNullException(nameof(clientAssertionCertificate)); + } + + return new AzureActiveDirectoryTokenProvider(azureSubscriptionId, clientAssertionCertificate); + } + + /// Creates an Azure Active Directory token provider. + /// AuthenticationContext for AAD. + /// The client assertion certificate credential. + /// The for returning Json web token. + public static TokenProvider CreateAadTokenProvider(AuthenticationContext authContext, ClientAssertionCertificate clientAssertionCertificate) + { + if (authContext == null) + { + throw new ArgumentNullException(nameof(authContext)); + } + + if (clientAssertionCertificate == null) + { + throw new ArgumentNullException(nameof(clientAssertionCertificate)); + } + + return new AzureActiveDirectoryTokenProvider(authContext, clientAssertionCertificate); + } +#endif + /// /// Gets a for the given audience and duration. /// @@ -108,26 +234,6 @@ public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyN /// The request action /// The time span that specifies the timeout value for the message that gets the security token /// - public Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout) - { - TimeoutHelper.ThrowIfNegativeArgument(timeout); - appliesTo = NormalizeAppliesTo(appliesTo); - return this.OnGetTokenAsync(appliesTo, action, timeout); - } - - /// - /// - /// - /// - /// - protected abstract Task OnGetTokenAsync(string appliesTo, string action, TimeSpan timeout); - - /// - /// - /// - protected virtual string NormalizeAppliesTo(string appliesTo) - { - return EventHubsUriHelper.NormalizeUri(appliesTo, "http", true, stripPath: this.TokenScope == TokenScope.Namespace, ensureTrailingSlash: true); - } + public abstract Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout); } } diff --git a/src/Microsoft.Azure.EventHubs/Resources.Designer.cs b/src/Microsoft.Azure.EventHubs/Resources.Designer.cs index c58eb9b..7734177 100644 --- a/src/Microsoft.Azure.EventHubs/Resources.Designer.cs +++ b/src/Microsoft.Azure.EventHubs/Resources.Designer.cs @@ -188,5 +188,16 @@ public static string ArgumentInvalidCombination return ResourceManager.GetString("ArgumentInvalidCombination", resourceCulture); } } + + /// + /// Gets localized string like: Tenant-Id lookup failed with error '{0}' + /// + public static string TenantIdLookupFailed + { + get + { + return ResourceManager.GetString("TenantIdLookupFailed", resourceCulture); + } + } } } diff --git a/src/Microsoft.Azure.EventHubs/Resources.resx b/src/Microsoft.Azure.EventHubs/Resources.resx index 6bc7778..8dfa562 100644 --- a/src/Microsoft.Azure.EventHubs/Resources.resx +++ b/src/Microsoft.Azure.EventHubs/Resources.resx @@ -147,6 +147,9 @@ {0} cannot be specified along with {1}. {0} alone should be sufficient to Authenticate the request. + + Tenant-Id lookup failed with error '{0}' + Argument {0} must be a non-negative timeout value. The provided value was {1}. diff --git a/test/Microsoft.Azure.EventHubs.Tests/Client/MiscTests.cs b/test/Microsoft.Azure.EventHubs.Tests/Client/MiscTests.cs index 574db00..796b534 100644 --- a/test/Microsoft.Azure.EventHubs.Tests/Client/MiscTests.cs +++ b/test/Microsoft.Azure.EventHubs.Tests/Client/MiscTests.cs @@ -75,46 +75,5 @@ async Task PartitionKeyValidation() Assert.True(totalReceived == NumberOfMessagesToSend, $"Didn't receive the same number of messages that we sent. Sent: {NumberOfMessagesToSend}, Received: {totalReceived}"); } - - [Fact] - [DisplayTestMethodName] - async Task UseSharedAccessSignature() - { - // Generate shared access token. - var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); - var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SasKeyName, csb.SasKey); - var token = await tokenProvider.GetTokenAsync(csb.Endpoint.ToString(), "Send,Receive", TimeSpan.FromSeconds(120)); - var sas = token.TokenValue.ToString(); - - // Update connection string builder to use shared access signature instead. - csb.SasKey = ""; - csb.SasKeyName = ""; - csb.SharedAccessSignature = sas; - - // Create new client with updated connection string. - var ehClient = EventHubClient.CreateFromConnectionString(csb.ToString()); - - // Send one event - TestUtility.Log($"Sending one message."); - var ehSender = ehClient.CreatePartitionSender("0"); - var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); - await ehSender.SendAsync(eventData); - - // Receive event. - TestUtility.Log($"Receiving one message."); - var ehReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", PartitionReceiver.StartOfStream); - var msg = await ehReceiver.ReceiveAsync(1); - Assert.True(msg != null, "Failed to receive message."); - - // Get EH runtime information. - TestUtility.Log($"Getting Event Hub runtime information."); - var ehInfo = await ehClient.GetRuntimeInformationAsync(); - Assert.True(ehInfo != null, "Failed to get runtime information."); - - // Get EH partition runtime information. - TestUtility.Log($"Getting Event Hub partition '0' runtime information."); - var partitionInfo = await ehClient.GetPartitionRuntimeInformationAsync("0"); - Assert.True(ehInfo != null, "Failed to get runtime partition information."); - } } } diff --git a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs new file mode 100644 index 0000000..ad49008 --- /dev/null +++ b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs @@ -0,0 +1,168 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Azure.EventHubs.Tests.Client +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Threading.Tasks; + using Microsoft.IdentityModel.Clients.ActiveDirectory; + using Xunit; + + public class TokenProviderTests : ClientTestBase + { + [Fact] + [DisplayTestMethodName] + async Task UseSharedAccessSignature() + { + // Generate shared access token. + var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); + var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SasKeyName, csb.SasKey); + var token = await tokenProvider.GetTokenAsync(csb.Endpoint.ToString(), "Send,Receive", TimeSpan.FromSeconds(120)); + var sas = token.TokenValue.ToString(); + + // Update connection string builder to use shared access signature instead. + csb.SasKey = ""; + csb.SasKeyName = ""; + csb.SharedAccessSignature = sas; + + // Create new client with updated connection string. + var ehClient = EventHubClient.CreateFromConnectionString(csb.ToString()); + + // Send one event + TestUtility.Log($"Sending one message."); + var ehSender = ehClient.CreatePartitionSender("0"); + var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); + await ehSender.SendAsync(eventData); + + // Receive event. + TestUtility.Log($"Receiving one message."); + var ehReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", PartitionReceiver.StartOfStream); + var msg = await ehReceiver.ReceiveAsync(1); + Assert.True(msg != null, "Failed to receive message."); + + // Get EH runtime information. + TestUtility.Log($"Getting Event Hub runtime information."); + var ehInfo = await ehClient.GetRuntimeInformationAsync(); + Assert.True(ehInfo != null, "Failed to get runtime information."); + + // Get EH partition runtime information. + TestUtility.Log($"Getting Event Hub partition '0' runtime information."); + var partitionInfo = await ehClient.GetPartitionRuntimeInformationAsync("0"); + Assert.True(ehInfo != null, "Failed to get runtime partition information."); + } + + [Fact] + [DisplayTestMethodName] + async Task UseITokenProviderWithSas() + { + // Generate SAS token provider. + var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); + var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SasKeyName, csb.SasKey); + + // Create new client with updated connection string. + var ehClient = EventHubClient.Create(csb.Endpoint, csb.EntityPath, tokenProvider); + + // Send one event + TestUtility.Log($"Sending one message."); + var ehSender = ehClient.CreatePartitionSender("0"); + var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); + await ehSender.SendAsync(eventData); + + // Receive event. + TestUtility.Log($"Receiving one message."); + var ehReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", PartitionReceiver.StartOfStream); + var msg = await ehReceiver.ReceiveAsync(1); + Assert.True(msg != null, "Failed to receive message."); + + // Get EH runtime information. + TestUtility.Log($"Getting Event Hub runtime information."); + var ehInfo = await ehClient.GetRuntimeInformationAsync(); + Assert.True(ehInfo != null, "Failed to get runtime information."); + + // Get EH partition runtime information. + TestUtility.Log($"Getting Event Hub partition '0' runtime information."); + var partitionInfo = await ehClient.GetPartitionRuntimeInformationAsync("0"); + Assert.True(ehInfo != null, "Failed to get runtime partition information."); + } + + /// + /// This test is for manual only purpose. Fill in the tenant-id, app-id and app-secret before running. + /// + /// + [Fact] + [DisplayTestMethodName] + async Task UseITokenProviderWithAad() + { + // Generate SAS token provider. + var tenantId = ""; + var aadAppId = ""; + var aadAppSecret = ""; + + if (string.IsNullOrEmpty(tenantId)) + { + TestUtility.Log($"Skipping test during scheduled runs."); + return; + } + + var authContext = new AuthenticationContext($"https://login.windows.net/{tenantId}"); + var cc = new ClientCredential(aadAppId, aadAppSecret); + var tokenProvider = TokenProvider.CreateAadTokenProvider(authContext, cc); + + // Create new client with updated connection string. + var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); + var ehClient = EventHubClient.Create(csb.Endpoint, csb.EntityPath, tokenProvider); + + // Send one event + TestUtility.Log($"Sending one message."); + var ehSender = ehClient.CreatePartitionSender("0"); + var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); + await ehSender.SendAsync(eventData); + + // Receive event. + TestUtility.Log($"Receiving one message."); + var ehReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", PartitionReceiver.StartOfStream); + var msg = await ehReceiver.ReceiveAsync(1); + Assert.True(msg != null, "Failed to receive message."); + } + + /// + /// This test is for manual only purpose. Fill in the subscription-id, app-id and app-secret before running. + /// + /// + [Fact] + [DisplayTestMethodName] + async Task CreateClientWithSubscriptionId() + { + // Generate SAS token provider. + var subscriptionId = ""; + var aadAppId = ""; + var aadAppSecret = ""; + + if (string.IsNullOrEmpty(subscriptionId)) + { + TestUtility.Log($"Skipping test during scheduled runs."); + return; + } + + var cc = new ClientCredential(aadAppId, aadAppSecret); + + // Create new client with updated connection string. + var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); + var ehClient = EventHubClient.Create(csb.Endpoint, csb.EntityPath, subscriptionId, cc, TimeSpan.FromSeconds(60)); + + // Send one event + TestUtility.Log($"Sending one message."); + var ehSender = ehClient.CreatePartitionSender("0"); + var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); + await ehSender.SendAsync(eventData); + + // Receive event. + TestUtility.Log($"Receiving one message."); + var ehReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", PartitionReceiver.StartOfStream); + var msg = await ehReceiver.ReceiveAsync(1); + Assert.True(msg != null, "Failed to receive message."); + } + } +} From 35a0cd901bf400c24507b0f45b34311cb1675376 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Mon, 27 Nov 2017 17:20:18 -0800 Subject: [PATCH 02/24] improve lock --- .../Primitives/AzureActiveDirectoryTokenProvider.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs index 9cf2d33..76399d0 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs @@ -96,8 +96,11 @@ public override async Task GetTokenAsync(string appliesTo, string { using (await this.authContextLock.LockAsync()) { - string authority = await TenantIdProvider.GetTenantUri(this.azureSubscriptionId); - this.authContext = new AuthenticationContext(authority); + if (this.authContext == null) + { + string authority = await TenantIdProvider.GetTenantUri(this.azureSubscriptionId); + this.authContext = new AuthenticationContext(authority); + } } } From 7218a68946f1b4b83da87f0741293e39d2c8daa0 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Wed, 29 Nov 2017 17:55:29 -0800 Subject: [PATCH 03/24] Adding MSI provider. --- .../EventHubClient.cs | 125 +++++++++--------- .../Microsoft.Azure.EventHubs.csproj | 1 + .../AzureActiveDirectoryTokenProvider.cs | 46 +------ .../Primitives/ClientConstants.cs | 1 + .../Primitives/ITokenProvider.cs | 13 +- .../Primitives/JsonSecurityToken.cs | 16 ++- .../ManagedServiceIdentityTokenProvider.cs | 31 +++++ .../Primitives/TokenProvider.cs | 82 +----------- .../Client/ClientTestBase.cs | 2 +- .../Client/TokenProviderTests.cs | 38 ------ 10 files changed, 128 insertions(+), 227 deletions(-) create mode 100644 src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs diff --git a/src/Microsoft.Azure.EventHubs/EventHubClient.cs b/src/Microsoft.Azure.EventHubs/EventHubClient.cs index f01b5c1..c64b9d2 100644 --- a/src/Microsoft.Azure.EventHubs/EventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/EventHubClient.cs @@ -96,7 +96,7 @@ public static EventHubClient Create( Uri endpoint, string entityPath, ITokenProvider tokenProvider, - TimeSpan operationTimeout, + TimeSpan? operationTimeout = null, TransportType transportType = TransportType.Amqp) { if (endpoint == null) @@ -119,18 +119,18 @@ public static EventHubClient Create( endpoint, entityPath, tokenProvider, - operationTimeout, + operationTimeout?? ClientConstants.DefaultOperationTimeout, transportType); EventHubsEventSource.Log.EventHubClientCreateStop(eventHubClient.ClientId); return eventHubClient; } /// - /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, subscription-id, and client credential. + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, AAD authentication context. /// /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net /// Event Hub path - /// Azure subsciption identifier. + /// AuthenticationContext for AAD. /// The app credential. /// Operation timeout for Event Hubs operations. /// Transport type on connection. @@ -138,102 +138,99 @@ public static EventHubClient Create( public static EventHubClient Create( Uri endpoint, string entityPath, - string azureSubscriptionId, - ClientCredential clientCredential, - TimeSpan operationTimeout, + AuthenticationContext authContext, + ClientCredential clientCredential, + TimeSpan? operationTimeout = null, TransportType transportType = TransportType.Amqp) { - TokenProvider tokenProvider = TokenProvider.CreateAadTokenProvider(azureSubscriptionId, clientCredential); - - return Create(endpoint, entityPath, tokenProvider, operationTimeout, transportType); + return Create( + endpoint, + entityPath, + TokenProvider.CreateAadTokenProvider(authContext, clientCredential), + operationTimeout, + transportType); } /// - /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, subscription-id, and client credential. + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, AAD authentication context. /// /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net /// Event Hub path - /// Azure subsciption identifier. - /// The client assertion certificate credential. + /// AuthenticationContext for AAD. + /// ClientId for AAD. + /// The redrectUri on Client App. + /// Platform parameters + /// User Identifier /// Operation timeout for Event Hubs operations. /// Transport type on connection. /// public static EventHubClient Create( Uri endpoint, string entityPath, - string azureSubscriptionId, - ClientAssertionCertificate clientAssertionCertificate, - TimeSpan operationTimeout, + AuthenticationContext authContext, + string clientId, + Uri redirectUri, + IPlatformParameters platformParameters, + UserIdentifier userIdentifier = null, + TimeSpan? operationTimeout = null, TransportType transportType = TransportType.Amqp) { - TokenProvider tokenProvider = TokenProvider.CreateAadTokenProvider(azureSubscriptionId, clientAssertionCertificate); - - return Create(endpoint, entityPath, tokenProvider, operationTimeout, transportType); + return Create( + endpoint, + entityPath, + TokenProvider.CreateAadTokenProvider(authContext, clientId, redirectUri, platformParameters, userIdentifier), + operationTimeout, + transportType); } - + +#if !UAP10_0 /// - /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, subscription-id, and client credential. - /// - /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net - /// Event Hub path - /// Azure subsciption identifier. - /// ClientId for AAD. - /// The redrectUri on Client App. - /// Platform parameters - /// Operation timeout for Event Hubs operations. - /// Transport type on connection. - /// + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, AAD authentication context. + /// + /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Event Hub path + /// AuthenticationContext for AAD. + /// The client assertion certificate credential. + /// Operation timeout for Event Hubs operations. + /// Transport type on connection. + /// public static EventHubClient Create( Uri endpoint, string entityPath, - string azureSubscriptionId, - string clientId, - Uri redirectUri, - IPlatformParameters platformParameters, - TimeSpan operationTimeout, + AuthenticationContext authContext, + ClientAssertionCertificate clientAssertionCertificate, + TimeSpan? operationTimeout = null, TransportType transportType = TransportType.Amqp) { - TokenProvider tokenProvider = TokenProvider.CreateAadTokenProvider( - azureSubscriptionId, - clientId, - redirectUri, - platformParameters); - - return Create(endpoint, entityPath, tokenProvider, operationTimeout, transportType); + return Create( + endpoint, + entityPath, + TokenProvider.CreateAadTokenProvider(authContext, clientAssertionCertificate), + operationTimeout, + transportType); } +#endif /// - /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, subscription-id, and client credential. + /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path on Azure Managed Service Identity authentication. /// /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net /// Event Hub path - /// Azure subsciption identifier. - /// ClientId for AAD. - /// The redrectUri on Client App. - /// Platform parameters - /// User Identifier /// Operation timeout for Event Hubs operations. /// Transport type on connection. /// - public static EventHubClient Create( + public static EventHubClient CreateByManagedServiceIdentity( Uri endpoint, string entityPath, - string azureSubscriptionId, - string clientId, - Uri redirectUri, - IPlatformParameters platformParameters, - UserIdentifier userIdentifier, - TimeSpan operationTimeout, + TimeSpan? operationTimeout = null, TransportType transportType = TransportType.Amqp) { - TokenProvider tokenProvider = TokenProvider.CreateAadTokenProvider( - azureSubscriptionId, - clientId, - redirectUri, - platformParameters, - userIdentifier); - - return Create(endpoint, entityPath, tokenProvider, operationTimeout, transportType); + return Create( + endpoint, + entityPath, + TokenProvider.CreateManagedServiceIdentityTokenProvider(), + operationTimeout, + transportType); } static EventHubClient Create(EventHubsConnectionStringBuilder csb) diff --git a/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj b/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj index 0815960..d937182 100644 --- a/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj +++ b/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj @@ -55,6 +55,7 @@ + diff --git a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs index 76399d0..ad4f455 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs @@ -7,15 +7,13 @@ namespace Microsoft.Azure.EventHubs using System.Threading.Tasks; using Microsoft.IdentityModel.Clients.ActiveDirectory; - /// Represents the Azure Active Directory token provider for the Service Bus and Event Hubs. + /// + /// Represents the Azure Active Directory token provider for the Event Hubs. + /// public class AzureActiveDirectoryTokenProvider : TokenProvider { - const int DefaultCacheSize = 100; - const string Audience = "https://eventhubs.azure.net/"; - AsyncLock authContextLock = new AsyncLock(); - AuthenticationContext authContext; - readonly string azureSubscriptionId; + readonly AuthenticationContext authContext; readonly ClientCredential clientCredential; #if !UAP10_0 readonly ClientAssertionCertificate clientAssertionCertificate; @@ -35,12 +33,6 @@ enum AuthType readonly AuthType authType; - internal AzureActiveDirectoryTokenProvider(string azureSubscriptionId, ClientCredential credential) - : this(authContext: null, credential: credential) - { - this.azureSubscriptionId = azureSubscriptionId; - } - internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, ClientCredential credential) { this.clientCredential = credential; @@ -50,12 +42,6 @@ internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, Cl } #if !UAP10_0 - internal AzureActiveDirectoryTokenProvider(string azureSubscriptionId, ClientAssertionCertificate clientAssertionCertificate) - : this(authContext: null, clientAssertionCertificate: clientAssertionCertificate) - { - this.azureSubscriptionId = azureSubscriptionId; - } - internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, ClientAssertionCertificate clientAssertionCertificate) { this.clientAssertionCertificate = clientAssertionCertificate; @@ -65,12 +51,6 @@ internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, Cl } #endif - internal AzureActiveDirectoryTokenProvider(string azureSubscriptionId, string clientId, Uri redirectUri, IPlatformParameters platformParameters, UserIdentifier userIdentifier) - : this(authContext: null, clientId: clientId, redirectUri: redirectUri, platformParameters: platformParameters, userIdentifier: userIdentifier) - { - this.azureSubscriptionId = azureSubscriptionId; - } - internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, string clientId, Uri redirectUri, IPlatformParameters platformParameters, UserIdentifier userIdentifier) { this.authContext = authContext; @@ -92,32 +72,20 @@ public override async Task GetTokenAsync(string appliesTo, string { AuthenticationResult authResult; - if (this.authContext == null) - { - using (await this.authContextLock.LockAsync()) - { - if (this.authContext == null) - { - string authority = await TenantIdProvider.GetTenantUri(this.azureSubscriptionId); - this.authContext = new AuthenticationContext(authority); - } - } - } - switch (this.authType) { case AuthType.ClientCredential: - authResult = await this.authContext.AcquireTokenAsync(Audience, this.clientCredential); + authResult = await this.authContext.AcquireTokenAsync(ClientConstants.AadEventHubsAudience, this.clientCredential); break; #if !UAP10_0 case AuthType.ClientAssertionCertificate: - authResult = await this.authContext.AcquireTokenAsync(Audience, this.clientAssertionCertificate); + authResult = await this.authContext.AcquireTokenAsync(ClientConstants.AadEventHubsAudience, this.clientAssertionCertificate); break; #endif case AuthType.InteractiveUserLogin: - authResult = await this.authContext.AcquireTokenAsync(Audience, this.clientId, this.redirectUri, this.platformParameters, this.userIdentifier); + authResult = await this.authContext.AcquireTokenAsync(ClientConstants.AadEventHubsAudience, this.clientId, this.redirectUri, this.platformParameters, this.userIdentifier); break; default: diff --git a/src/Microsoft.Azure.EventHubs/Primitives/ClientConstants.cs b/src/Microsoft.Azure.EventHubs/Primitives/ClientConstants.cs index 639805b..a2d6bc4 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/ClientConstants.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/ClientConstants.cs @@ -12,6 +12,7 @@ static class ClientConstants public const string SasTokenType = "servicebus.windows.net:sastoken"; public const string JsonWebTokenType = "jwt"; + public const string AadEventHubsAudience = "https://eventhubs.azure.net/"; public static TimeSpan DefaultOperationTimeout = TimeSpan.FromMinutes(1); public static TransportType DefaultTransportType = TransportType.Amqp; diff --git a/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs index 4617d26..e771f93 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs @@ -1,11 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.Azure.EventHubs { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + /// /// Provides interface definition of a token provider. /// diff --git a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs index 68ae0df..0c73b4c 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs @@ -8,16 +8,22 @@ namespace Microsoft.Azure.EventHubs using System.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; - class JsonSecurityToken : SecurityToken + /// + /// Extends SecurityToken for JWT specific properties + /// + public class JsonSecurityToken : SecurityToken { - JwtSecurityToken internalToken; - + /// + /// Creates a new instance of the class. + /// + /// Raw JSON Web Token string + /// The audience public JsonSecurityToken(string rawToken, string audience) { - this.internalToken = new JwtSecurityToken(rawToken); + var jwtSecurityToken = new JwtSecurityToken(rawToken); this.tokenType = ClientConstants.JsonWebTokenType; this.token = rawToken; - this.expiresAtUtc = this.internalToken.ValidTo; + this.expiresAtUtc = jwtSecurityToken.ValidTo; this.audience = audience; } } diff --git a/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs new file mode 100644 index 0000000..2aa7fb1 --- /dev/null +++ b/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Azure.EventHubs +{ + using System; + using System.IdentityModel.Tokens; + using System.Threading.Tasks; + using Azure.Services.AppAuthentication; + + /// + /// Represents the Azure Active Directory token provider for Azure Managed Service Identity integration. + /// + public class ManagedServiceIdentityTokenProvider : TokenProvider + { + static AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(); + + /// + /// Gets a for the given audience and duration. + /// + /// The URI which the access token applies to + /// The request action + /// The time span that specifies the timeout value for the message that gets the security token + /// + public async override Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout) + { + string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(ClientConstants.AadEventHubsAudience); + return new JsonSecurityToken(accessToken, appliesTo); + } + } +} diff --git a/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs index b91c750..1109394 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs @@ -75,25 +75,6 @@ public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyN return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, tokenTimeToLive, tokenScope); } - /// Creates an Azure Active Directory token provider. - /// Azure subsciption identifier. - /// The app credential. - /// The for returning Json web token. - public static TokenProvider CreateAadTokenProvider(string azureSubscriptionId, ClientCredential clientCredential) - { - if (string.IsNullOrEmpty(azureSubscriptionId)) - { - throw new ArgumentNullException(nameof(azureSubscriptionId)); - } - - if (clientCredential == null) - { - throw new ArgumentNullException(nameof(clientCredential)); - } - - return new AzureActiveDirectoryTokenProvider(azureSubscriptionId, clientCredential); - } - /// Creates an Azure Active Directory token provider. /// AuthenticationContext for AAD. /// The app credential. @@ -113,43 +94,6 @@ public static TokenProvider CreateAadTokenProvider(AuthenticationContext authCon return new AzureActiveDirectoryTokenProvider(authContext, clientCredential); } - /// Creates an Azure Active Directory token provider. - /// Azure subsciption identifier. - /// ClientId for AAD. - /// The redrectUri on Client App. - /// Platform parameters - /// User Identifier - /// The for returning Json web token. - public static TokenProvider CreateAadTokenProvider( - string azureSubscriptionId, - string clientId, - Uri redirectUri, - IPlatformParameters platformParameters, - UserIdentifier userIdentifier = null) - { - if (string.IsNullOrEmpty(azureSubscriptionId)) - { - throw new ArgumentNullException(nameof(azureSubscriptionId)); - } - - if (string.IsNullOrEmpty(clientId)) - { - throw new ArgumentNullException(nameof(clientId)); - } - - if (redirectUri == null) - { - throw new ArgumentNullException(nameof(redirectUri)); - } - - if (platformParameters == null) - { - throw new ArgumentNullException(nameof(platformParameters)); - } - - return new AzureActiveDirectoryTokenProvider(azureSubscriptionId, clientId, redirectUri, platformParameters, userIdentifier); - } - /// Creates an Azure Active Directory token provider. /// AuthenticationContext for AAD. /// ClientId for AAD. @@ -188,25 +132,6 @@ public static TokenProvider CreateAadTokenProvider( } #if !UAP10_0 - /// Creates an Azure Active Directory token provider. - /// Azure subsciption identifier. - /// The client assertion certificate credential. - /// The for returning Json web token. - public static TokenProvider CreateAadTokenProvider(string azureSubscriptionId, ClientAssertionCertificate clientAssertionCertificate) - { - if (string.IsNullOrEmpty(azureSubscriptionId)) - { - throw new ArgumentNullException(nameof(azureSubscriptionId)); - } - - if (clientAssertionCertificate == null) - { - throw new ArgumentNullException(nameof(clientAssertionCertificate)); - } - - return new AzureActiveDirectoryTokenProvider(azureSubscriptionId, clientAssertionCertificate); - } - /// Creates an Azure Active Directory token provider. /// AuthenticationContext for AAD. /// The client assertion certificate credential. @@ -227,6 +152,13 @@ public static TokenProvider CreateAadTokenProvider(AuthenticationContext authCon } #endif + /// Creates Azure Managed Service Identity token provider. + /// The for returning Json web token. + public static TokenProvider CreateManagedServiceIdentityTokenProvider() + { + return new ManagedServiceIdentityTokenProvider(); + } + /// /// Gets a for the given audience and duration. /// diff --git a/test/Microsoft.Azure.EventHubs.Tests/Client/ClientTestBase.cs b/test/Microsoft.Azure.EventHubs.Tests/Client/ClientTestBase.cs index 44e90ad..0ed71c3 100644 --- a/test/Microsoft.Azure.EventHubs.Tests/Client/ClientTestBase.cs +++ b/test/Microsoft.Azure.EventHubs.Tests/Client/ClientTestBase.cs @@ -18,7 +18,7 @@ public ClientTestBase() { // Create default EH client. this.EventHubClient = EventHubClient.CreateFromConnectionString(TestUtility.EventHubsConnectionString); - + // Discover partition ids. var eventHubInfo = this.EventHubClient.GetRuntimeInformationAsync().Result; this.PartitionIds = eventHubInfo.PartitionIds; diff --git a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs index ad49008..37a619f 100644 --- a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs +++ b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs @@ -126,43 +126,5 @@ async Task UseITokenProviderWithAad() var msg = await ehReceiver.ReceiveAsync(1); Assert.True(msg != null, "Failed to receive message."); } - - /// - /// This test is for manual only purpose. Fill in the subscription-id, app-id and app-secret before running. - /// - /// - [Fact] - [DisplayTestMethodName] - async Task CreateClientWithSubscriptionId() - { - // Generate SAS token provider. - var subscriptionId = ""; - var aadAppId = ""; - var aadAppSecret = ""; - - if (string.IsNullOrEmpty(subscriptionId)) - { - TestUtility.Log($"Skipping test during scheduled runs."); - return; - } - - var cc = new ClientCredential(aadAppId, aadAppSecret); - - // Create new client with updated connection string. - var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); - var ehClient = EventHubClient.Create(csb.Endpoint, csb.EntityPath, subscriptionId, cc, TimeSpan.FromSeconds(60)); - - // Send one event - TestUtility.Log($"Sending one message."); - var ehSender = ehClient.CreatePartitionSender("0"); - var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); - await ehSender.SendAsync(eventData); - - // Receive event. - TestUtility.Log($"Receiving one message."); - var ehReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", PartitionReceiver.StartOfStream); - var msg = await ehReceiver.ReceiveAsync(1); - Assert.True(msg != null, "Failed to receive message."); - } } } From e1c822a948cf2d67b91e54c4f195f962d06c806a Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 5 Dec 2017 13:49:13 -0800 Subject: [PATCH 04/24] Some small changes --- ...Microsoft.Azure.EventHubs.Processor.csproj | 1 - .../Microsoft.Azure.EventHubs.csproj | 10 +++- .../Client/TokenProviderTests.cs | 52 ++++++++++++++++--- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs.Processor/Microsoft.Azure.EventHubs.Processor.csproj b/src/Microsoft.Azure.EventHubs.Processor/Microsoft.Azure.EventHubs.Processor.csproj index 850bb1d..07cc13a 100644 --- a/src/Microsoft.Azure.EventHubs.Processor/Microsoft.Azure.EventHubs.Processor.csproj +++ b/src/Microsoft.Azure.EventHubs.Processor/Microsoft.Azure.EventHubs.Processor.csproj @@ -29,7 +29,6 @@ - $(DefineConstants);UAP10_0 UAP,Version=v10.0 UAP 10.0.14393.0 diff --git a/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj b/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj index d937182..92709c8 100644 --- a/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj +++ b/src/Microsoft.Azure.EventHubs/Microsoft.Azure.EventHubs.csproj @@ -29,6 +29,10 @@ 2.0.0.0 + + $(DefineConstants);NET461 + + $(DefineConstants);UAP10_0 UAP,Version=v10.0 @@ -39,6 +43,10 @@ v5.0 + + $(DefineConstants);NETSTANDARD2_0 + + @@ -50,7 +58,7 @@ - + diff --git a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs index 37a619f..d380bc2 100644 --- a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs +++ b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs @@ -33,7 +33,7 @@ async Task UseSharedAccessSignature() // Send one event TestUtility.Log($"Sending one message."); var ehSender = ehClient.CreatePartitionSender("0"); - var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); + var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub!")); await ehSender.SendAsync(eventData); // Receive event. @@ -67,7 +67,7 @@ async Task UseITokenProviderWithSas() // Send one event TestUtility.Log($"Sending one message."); var ehSender = ehClient.CreatePartitionSender("0"); - var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); + var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub!")); await ehSender.SendAsync(eventData); // Receive event. @@ -95,10 +95,9 @@ async Task UseITokenProviderWithSas() [DisplayTestMethodName] async Task UseITokenProviderWithAad() { - // Generate SAS token provider. - var tenantId = ""; - var aadAppId = ""; - var aadAppSecret = ""; + var tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; + var aadAppId = "6d464024-d6ff-4cc3-9e6c-b47c7a1a283b"; + var aadAppSecret = "BWYFFJJ7H8i0yA7i2zgdeJTi3R6A0ty8fb9ph8Qz14s="; if (string.IsNullOrEmpty(tenantId)) { @@ -117,7 +116,46 @@ async Task UseITokenProviderWithAad() // Send one event TestUtility.Log($"Sending one message."); var ehSender = ehClient.CreatePartitionSender("0"); - var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub by partitionKey!")); + var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub!")); + await ehSender.SendAsync(eventData); + + // Receive event. + TestUtility.Log($"Receiving one message."); + var ehReceiver = ehClient.CreateReceiver(PartitionReceiver.DefaultConsumerGroupName, "0", PartitionReceiver.StartOfStream); + var msg = await ehReceiver.ReceiveAsync(1); + Assert.True(msg != null, "Failed to receive message."); + } + + + /// + /// This test is for manual only purpose. Fill in the tenant-id, app-id and app-secret before running. + /// + /// + [Fact] + [DisplayTestMethodName] + async Task UseCreateApiWithAad() + { + var tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; + var aadAppId = "6d464024-d6ff-4cc3-9e6c-b47c7a1a283b"; + var aadAppSecret = "BWYFFJJ7H8i0yA7i2zgdeJTi3R6A0ty8fb9ph8Qz14s="; + + if (string.IsNullOrEmpty(tenantId)) + { + TestUtility.Log($"Skipping test during scheduled runs."); + return; + } + + var authContext = new AuthenticationContext($"https://login.windows.net/{tenantId}"); + var cc = new ClientCredential(aadAppId, aadAppSecret); + + // Create new client with updated connection string. + var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); + var ehClient = EventHubClient.Create(csb.Endpoint, csb.EntityPath, authContext, cc); + + // Send one event + TestUtility.Log($"Sending one message."); + var ehSender = ehClient.CreatePartitionSender("0"); + var eventData = new EventData(Encoding.UTF8.GetBytes("Hello EventHub!")); await ehSender.SendAsync(eventData); // Receive event. From b97eb3f0f0934852ef82f021af1da0b02b3866d5 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 5 Dec 2017 16:10:57 -0800 Subject: [PATCH 05/24] Adddresing comments and couple small fixes. --- .../Amqp/AmqpEventHubClient.cs | 3 +- .../Amqp/AmqpServiceClient.cs | 2 +- .../EventHubClient.cs | 12 ---- .../AzureActiveDirectoryTokenProvider.cs | 3 +- .../Primitives/ITokenProvider.cs | 3 +- .../ManagedServiceIdentityTokenProvider.cs | 3 +- .../SharedAccessSignatureTokenProvider.cs | 3 +- .../Primitives/TenantIdProvider.cs | 63 ------------------- .../Primitives/TokenProvider.cs | 3 +- .../Client/TokenProviderTests.cs | 2 +- 10 files changed, 8 insertions(+), 89 deletions(-) delete mode 100644 src/Microsoft.Azure.EventHubs/Primitives/TenantIdProvider.cs diff --git a/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs b/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs index 4e50c58..41d510d 100644 --- a/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs @@ -281,9 +281,8 @@ public TokenProviderAdapter(AmqpEventHubClient eventHubClient) public async Task GetTokenAsync(Uri namespaceAddress, string appliesTo, string[] requiredClaims) { - string claim = requiredClaims?.FirstOrDefault(); var timeout = this.eventHubClient.ConnectionStringBuilder.OperationTimeout; - var token = await this.eventHubClient.InternalTokenProvider.GetTokenAsync(appliesTo, claim, timeout).ConfigureAwait(false); + var token = await this.eventHubClient.InternalTokenProvider.GetTokenAsync(appliesTo, timeout).ConfigureAwait(false); return new CbsToken(token.TokenValue, token.TokenType, token.ExpiresAtUtc); } } diff --git a/src/Microsoft.Azure.EventHubs/Amqp/AmqpServiceClient.cs b/src/Microsoft.Azure.EventHubs/Amqp/AmqpServiceClient.cs index 8ac7ef3..06ec5bd 100644 --- a/src/Microsoft.Azure.EventHubs/Amqp/AmqpServiceClient.cs +++ b/src/Microsoft.Azure.EventHubs/Amqp/AmqpServiceClient.cs @@ -152,7 +152,7 @@ async Task GetTokenString() { this.token = await this.eventHubClient.InternalTokenProvider.GetTokenAsync( this.eventHubClient.ConnectionStringBuilder.Endpoint.AbsoluteUri, - ClaimConstants.Listen, this.eventHubClient.ConnectionStringBuilder.OperationTimeout).ConfigureAwait(false); + this.eventHubClient.ConnectionStringBuilder.OperationTimeout).ConfigureAwait(false); } return this.token.TokenValue.ToString(); diff --git a/src/Microsoft.Azure.EventHubs/EventHubClient.cs b/src/Microsoft.Azure.EventHubs/EventHubClient.cs index c64b9d2..18d9204 100644 --- a/src/Microsoft.Azure.EventHubs/EventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/EventHubClient.cs @@ -71,18 +71,6 @@ public static EventHubClient CreateFromConnectionString(string connectionString) return Create(csb); } - /// - /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, and token provider. - /// - /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net - /// Event Hub path - /// Token provider which will generate security tokens for authorization. - /// - public static EventHubClient Create(Uri endpoint, string entityPath, ITokenProvider tokenProvider) - { - return Create(endpoint, entityPath, tokenProvider, ClientConstants.DefaultOperationTimeout); - } - /// /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, and token provider. /// diff --git a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs index ad4f455..5e1ebf2 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs @@ -65,10 +65,9 @@ internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, st /// Gets a for the given audience and duration. /// /// The URI which the access token applies to - /// The request action /// The time span that specifies the timeout value for the message that gets the security token /// - public override async Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout) + public override async Task GetTokenAsync(string appliesTo, TimeSpan timeout) { AuthenticationResult authResult; diff --git a/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs index e771f93..f1c4fd9 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/ITokenProvider.cs @@ -18,9 +18,8 @@ public interface ITokenProvider /// Gets a . /// /// The URI which the access token applies to - /// The request action /// The time span that specifies the timeout value for the message that gets the security token /// - Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout); + Task GetTokenAsync(string appliesTo, TimeSpan timeout); } } diff --git a/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs index 2aa7fb1..8f07324 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs @@ -19,10 +19,9 @@ public class ManagedServiceIdentityTokenProvider : TokenProvider /// Gets a for the given audience and duration. /// /// The URI which the access token applies to - /// The request action /// The time span that specifies the timeout value for the message that gets the security token /// - public async override Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout) + public async override Task GetTokenAsync(string appliesTo, TimeSpan timeout) { string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(ClientConstants.AadEventHubsAudience); return new JsonSecurityToken(accessToken, appliesTo); diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureTokenProvider.cs index 2e8f94d..58730a9 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureTokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureTokenProvider.cs @@ -93,10 +93,9 @@ protected SharedAccessSignatureTokenProvider(string keyName, string sharedAccess /// Gets a for the given audience and duration. /// /// The URI which the access token applies to - /// The request action /// The time span that specifies the timeout value for the message that gets the security token /// - public override Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout) + public override Task GetTokenAsync(string appliesTo, TimeSpan timeout) { TimeoutHelper.ThrowIfNegativeArgument(timeout); appliesTo = NormalizeAppliesTo(appliesTo); diff --git a/src/Microsoft.Azure.EventHubs/Primitives/TenantIdProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/TenantIdProvider.cs deleted file mode 100644 index cc8ebf9..0000000 --- a/src/Microsoft.Azure.EventHubs/Primitives/TenantIdProvider.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace Microsoft.Azure.EventHubs -{ - using System; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Threading.Tasks; - - class TenantIdProvider - { - const string ArmApiVersion = "2016-06-01"; - static readonly HttpClient httpClient; - - static TenantIdProvider() - { - httpClient = new HttpClient(); - } - - public static async Task GetTenantUri(string subscriptionId, string armEndpoint = "https://management.azure.com/") - { - Uri armUri = new Uri($"{armEndpoint}subscriptions/{subscriptionId}?api-version={ArmApiVersion}"); - - var request = new HttpRequestMessage - { - RequestUri = armUri, - Method = HttpMethod.Get, - }; - - string tenantUri; - - using (var response = await httpClient.SendAsync(request)) - { - string responseContent = await response.Content.ReadAsStringAsync(); - - var authHeader = response.Headers.GetValues("WWW-Authenticate").FirstOrDefault(); - if (string.IsNullOrEmpty(authHeader)) - { - throw new InvalidOperationException(string.Format(Resources.TenantIdLookupFailed, "'WWW-Authenticate' cannot be located from the authHeader")); - } - - /* e.g. - WWW-Authenticate: Bearer authorization_uri="https://login.windows.net/b06452b1-437c-4d7d-8363-54769cb614fa", error="invalid_token", error_description="The access token is from the wrong issuer. - It must match the tenant associated with this subscription. Please use correct authority to get the token." - */ - string authUri = "authorization_uri"; - int indexOfAuthUri = authHeader.IndexOf(authUri, StringComparison.OrdinalIgnoreCase); - - if (indexOfAuthUri == -1) - { - throw new InvalidOperationException(string.Format(Resources.TenantIdLookupFailed, "'authorization_uri' cannot be located from the authHeader")); - } - - int indexOfTenantUri = authHeader.IndexOf("\"", indexOfAuthUri, StringComparison.OrdinalIgnoreCase) + 1; - tenantUri = authHeader.Substring(indexOfTenantUri, authHeader.IndexOf("\"", indexOfTenantUri + 1, StringComparison.OrdinalIgnoreCase) - indexOfTenantUri); - } - - return tenantUri; - } - } -} diff --git a/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs index 1109394..ef2ae19 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs @@ -163,9 +163,8 @@ public static TokenProvider CreateManagedServiceIdentityTokenProvider() /// Gets a for the given audience and duration. /// /// The URI which the access token applies to - /// The request action /// The time span that specifies the timeout value for the message that gets the security token /// - public abstract Task GetTokenAsync(string appliesTo, string action, TimeSpan timeout); + public abstract Task GetTokenAsync(string appliesTo, TimeSpan timeout); } } diff --git a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs index d380bc2..ecde092 100644 --- a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs +++ b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs @@ -19,7 +19,7 @@ async Task UseSharedAccessSignature() // Generate shared access token. var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SasKeyName, csb.SasKey); - var token = await tokenProvider.GetTokenAsync(csb.Endpoint.ToString(), "Send,Receive", TimeSpan.FromSeconds(120)); + var token = await tokenProvider.GetTokenAsync(csb.Endpoint.ToString(), TimeSpan.FromSeconds(120)); var sas = token.TokenValue.ToString(); // Update connection string builder to use shared access signature instead. From 1274d84bae46f37661878b22c3044e95f3492429 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 5 Dec 2017 16:12:54 -0800 Subject: [PATCH 06/24] Remove string resource --- src/Microsoft.Azure.EventHubs/Resources.Designer.cs | 11 ----------- src/Microsoft.Azure.EventHubs/Resources.resx | 3 --- 2 files changed, 14 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Resources.Designer.cs b/src/Microsoft.Azure.EventHubs/Resources.Designer.cs index 7734177..c58eb9b 100644 --- a/src/Microsoft.Azure.EventHubs/Resources.Designer.cs +++ b/src/Microsoft.Azure.EventHubs/Resources.Designer.cs @@ -188,16 +188,5 @@ public static string ArgumentInvalidCombination return ResourceManager.GetString("ArgumentInvalidCombination", resourceCulture); } } - - /// - /// Gets localized string like: Tenant-Id lookup failed with error '{0}' - /// - public static string TenantIdLookupFailed - { - get - { - return ResourceManager.GetString("TenantIdLookupFailed", resourceCulture); - } - } } } diff --git a/src/Microsoft.Azure.EventHubs/Resources.resx b/src/Microsoft.Azure.EventHubs/Resources.resx index 8dfa562..6bc7778 100644 --- a/src/Microsoft.Azure.EventHubs/Resources.resx +++ b/src/Microsoft.Azure.EventHubs/Resources.resx @@ -147,9 +147,6 @@ {0} cannot be specified along with {1}. {0} alone should be sufficient to Authenticate the request. - - Tenant-Id lookup failed with error '{0}' - Argument {0} must be a non-negative timeout value. The provided value was {1}. From c88f523d4fa6372e245f807f8cafdac533d8c515 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 5 Dec 2017 16:23:34 -0800 Subject: [PATCH 07/24] Remove test secrets --- .../Client/TokenProviderTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs index ecde092..7e8be95 100644 --- a/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs +++ b/test/Microsoft.Azure.EventHubs.Tests/Client/TokenProviderTests.cs @@ -95,9 +95,9 @@ async Task UseITokenProviderWithSas() [DisplayTestMethodName] async Task UseITokenProviderWithAad() { - var tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; - var aadAppId = "6d464024-d6ff-4cc3-9e6c-b47c7a1a283b"; - var aadAppSecret = "BWYFFJJ7H8i0yA7i2zgdeJTi3R6A0ty8fb9ph8Qz14s="; + var tenantId = ""; + var aadAppId = ""; + var aadAppSecret = ""; if (string.IsNullOrEmpty(tenantId)) { @@ -135,9 +135,9 @@ async Task UseITokenProviderWithAad() [DisplayTestMethodName] async Task UseCreateApiWithAad() { - var tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; - var aadAppId = "6d464024-d6ff-4cc3-9e6c-b47c7a1a283b"; - var aadAppSecret = "BWYFFJJ7H8i0yA7i2zgdeJTi3R6A0ty8fb9ph8Qz14s="; + var tenantId = ""; + var aadAppId = ""; + var aadAppSecret = ""; if (string.IsNullOrEmpty(tenantId)) { From 34c6207d97024c03abc34002ee673a740ed443a3 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Wed, 6 Dec 2017 15:54:37 -0800 Subject: [PATCH 08/24] Renaming paramater endpoint to endpointAddress --- .../Amqp/AmqpEventHubClient.cs | 4 +-- .../EventHubClient.cs | 32 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs b/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs index 41d510d..624354e 100644 --- a/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/Amqp/AmqpEventHubClient.cs @@ -37,12 +37,12 @@ public AmqpEventHubClient(EventHubsConnectionStringBuilder csb) } public AmqpEventHubClient( - Uri endpoint, + Uri endpointAddress, string entityPath, ITokenProvider tokenProvider, TimeSpan operationTimeout, TransportType transportType) - : base(new EventHubsConnectionStringBuilder(endpoint, entityPath, operationTimeout, transportType)) + : base(new EventHubsConnectionStringBuilder(endpointAddress, entityPath, operationTimeout, transportType)) { this.ContainerId = Guid.NewGuid().ToString("N"); this.AmqpVersion = new Version(1, 0, 0, 0); diff --git a/src/Microsoft.Azure.EventHubs/EventHubClient.cs b/src/Microsoft.Azure.EventHubs/EventHubClient.cs index 18d9204..d5c149d 100644 --- a/src/Microsoft.Azure.EventHubs/EventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/EventHubClient.cs @@ -74,22 +74,22 @@ public static EventHubClient CreateFromConnectionString(string connectionString) /// /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, and token provider. /// - /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Fully qualified domain name for Event Hubs. Most likely, {yournamespace}.servicebus.windows.net /// Event Hub path /// Token provider which will generate security tokens for authorization. /// Operation timeout for Event Hubs operations. /// Transport type on connection. /// public static EventHubClient Create( - Uri endpoint, + Uri endpointAddress, string entityPath, ITokenProvider tokenProvider, TimeSpan? operationTimeout = null, TransportType transportType = TransportType.Amqp) { - if (endpoint == null) + if (endpointAddress == null) { - throw Fx.Exception.ArgumentNull(nameof(endpoint)); + throw Fx.Exception.ArgumentNull(nameof(endpointAddress)); } if (string.IsNullOrWhiteSpace(entityPath)) @@ -116,7 +116,7 @@ public static EventHubClient Create( /// /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, AAD authentication context. /// - /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Fully qualified domain name for Event Hubs. Most likely, {yournamespace}.servicebus.windows.net /// Event Hub path /// AuthenticationContext for AAD. /// The app credential. @@ -124,7 +124,7 @@ public static EventHubClient Create( /// Transport type on connection. /// public static EventHubClient Create( - Uri endpoint, + Uri endpointAddress, string entityPath, AuthenticationContext authContext, ClientCredential clientCredential, @@ -132,7 +132,7 @@ public static EventHubClient Create( TransportType transportType = TransportType.Amqp) { return Create( - endpoint, + endpointAddress, entityPath, TokenProvider.CreateAadTokenProvider(authContext, clientCredential), operationTimeout, @@ -142,7 +142,7 @@ public static EventHubClient Create( /// /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, AAD authentication context. /// - /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Fully qualified domain name for Event Hubs. Most likely, {yournamespace}.servicebus.windows.net /// Event Hub path /// AuthenticationContext for AAD. /// ClientId for AAD. @@ -153,7 +153,7 @@ public static EventHubClient Create( /// Transport type on connection. /// public static EventHubClient Create( - Uri endpoint, + Uri endpointAddress, string entityPath, AuthenticationContext authContext, string clientId, @@ -164,7 +164,7 @@ public static EventHubClient Create( TransportType transportType = TransportType.Amqp) { return Create( - endpoint, + endpointAddress, entityPath, TokenProvider.CreateAadTokenProvider(authContext, clientId, redirectUri, platformParameters, userIdentifier), operationTimeout, @@ -175,7 +175,7 @@ public static EventHubClient Create( /// /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path, AAD authentication context. /// - /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Fully qualified domain name for Event Hubs. Most likely, {yournamespace}.servicebus.windows.net /// Event Hub path /// AuthenticationContext for AAD. /// The client assertion certificate credential. @@ -183,7 +183,7 @@ public static EventHubClient Create( /// Transport type on connection. /// public static EventHubClient Create( - Uri endpoint, + Uri endpointAddress, string entityPath, AuthenticationContext authContext, ClientAssertionCertificate clientAssertionCertificate, @@ -191,7 +191,7 @@ public static EventHubClient Create( TransportType transportType = TransportType.Amqp) { return Create( - endpoint, + endpointAddress, entityPath, TokenProvider.CreateAadTokenProvider(authContext, clientAssertionCertificate), operationTimeout, @@ -202,19 +202,19 @@ public static EventHubClient Create( /// /// Creates a new instance of the Event Hubs client using the specified endpoint, entity path on Azure Managed Service Identity authentication. /// - /// Endpoint for the subject Event Hubs namespace. For example, sb://mynamespace.servicebus.windows.net + /// Fully qualified domain name for Event Hubs. Most likely, {yournamespace}.servicebus.windows.net /// Event Hub path /// Operation timeout for Event Hubs operations. /// Transport type on connection. /// public static EventHubClient CreateByManagedServiceIdentity( - Uri endpoint, + Uri endpointAddress, string entityPath, TimeSpan? operationTimeout = null, TransportType transportType = TransportType.Amqp) { return Create( - endpoint, + endpointAddress, entityPath, TokenProvider.CreateManagedServiceIdentityTokenProvider(), operationTimeout, From 6de5ac332d8bf1e4a47008f9ab07cd54c8c3f7b5 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Wed, 6 Dec 2017 15:55:29 -0800 Subject: [PATCH 09/24] Renaming paramater endpoint to endpointAddress --- src/Microsoft.Azure.EventHubs/EventHubClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/EventHubClient.cs b/src/Microsoft.Azure.EventHubs/EventHubClient.cs index d5c149d..e042013 100644 --- a/src/Microsoft.Azure.EventHubs/EventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/EventHubClient.cs @@ -102,9 +102,9 @@ public static EventHubClient Create( throw Fx.Exception.ArgumentNull(nameof(tokenProvider)); } - EventHubsEventSource.Log.EventHubClientCreateStart(endpoint.Host, entityPath); + EventHubsEventSource.Log.EventHubClientCreateStart(endpointAddress.Host, entityPath); EventHubClient eventHubClient = new AmqpEventHubClient( - endpoint, + endpointAddress, entityPath, tokenProvider, operationTimeout?? ClientConstants.DefaultOperationTimeout, From b5c02d11880402c502e3bee1ab8b7c782ff443f4 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Fri, 8 Dec 2017 13:42:45 -0800 Subject: [PATCH 10/24] Remove unused AsyncLock --- .../Primitives/AzureActiveDirectoryTokenProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs index 5e1ebf2..6877cc6 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/AzureActiveDirectoryTokenProvider.cs @@ -12,7 +12,6 @@ namespace Microsoft.Azure.EventHubs /// public class AzureActiveDirectoryTokenProvider : TokenProvider { - AsyncLock authContextLock = new AsyncLock(); readonly AuthenticationContext authContext; readonly ClientCredential clientCredential; #if !UAP10_0 From ff61d65234d1b766b385e9446d78b7c3847f198e Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Mon, 11 Dec 2017 11:33:09 -0800 Subject: [PATCH 11/24] Remove redundant config name --- .../Primitives/EventHubsConnectionStringBuilder.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/EventHubsConnectionStringBuilder.cs b/src/Microsoft.Azure.EventHubs/Primitives/EventHubsConnectionStringBuilder.cs index 46f2979..9ddfb7e 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/EventHubsConnectionStringBuilder.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/EventHubsConnectionStringBuilder.cs @@ -56,7 +56,6 @@ public class EventHubsConnectionStringBuilder static readonly string EntityPathConfigName = "EntityPath"; static readonly string OperationTimeoutConfigName = "OperationTimeout"; static readonly string TransportTypeConfigName = "TransportType"; - static readonly string OperationTimeoutName = "OperationTimeout"; static readonly string SharedAccessSignatureConfigName = "SharedAccessSignature"; /// @@ -338,7 +337,7 @@ void ParseConnectionString(string connectionString) { this.SharedAccessSignature = value; } - else if (key.Equals(OperationTimeoutName, StringComparison.OrdinalIgnoreCase)) + else if (key.Equals(OperationTimeoutConfigName, StringComparison.OrdinalIgnoreCase)) { this.OperationTimeout = TimeSpan.Parse(value); } From 983e7c845e60e318fb23923c938c4f0092e76617 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Mon, 11 Dec 2017 15:18:48 -0800 Subject: [PATCH 12/24] Remove using --- .../Primitives/ManagedServiceIdentityTokenProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs index 8f07324..ff3618b 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/ManagedServiceIdentityTokenProvider.cs @@ -4,7 +4,6 @@ namespace Microsoft.Azure.EventHubs { using System; - using System.IdentityModel.Tokens; using System.Threading.Tasks; using Azure.Services.AppAuthentication; From dad74f0fd836f853b23ad683df14310ef192640b Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Mon, 11 Dec 2017 15:45:38 -0800 Subject: [PATCH 13/24] Remove usings --- src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs index 992538e..02fce90 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs @@ -4,10 +4,6 @@ namespace Microsoft.Azure.EventHubs { using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Net; /// /// Provides information about a security token such as audience, expiry time, and the string token value. From e31ade18f0a628f31f99917931373c195e8d13c1 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 09:07:05 -0800 Subject: [PATCH 14/24] Merge fix --- .../Client/ConnectionStringBuilderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.Azure.EventHubs.Tests/Client/ConnectionStringBuilderTests.cs b/test/Microsoft.Azure.EventHubs.Tests/Client/ConnectionStringBuilderTests.cs index 33eb6b9..5628a54 100644 --- a/test/Microsoft.Azure.EventHubs.Tests/Client/ConnectionStringBuilderTests.cs +++ b/test/Microsoft.Azure.EventHubs.Tests/Client/ConnectionStringBuilderTests.cs @@ -121,7 +121,7 @@ async Task UseSharedAccessSignatureApi() // Generate shared access token. var csb = new EventHubsConnectionStringBuilder(TestUtility.EventHubsConnectionString); var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(csb.SasKeyName, csb.SasKey); - var token = await tokenProvider.GetTokenAsync(csb.Endpoint.ToString(), "Send,Receive", TimeSpan.FromSeconds(120)); + var token = await tokenProvider.GetTokenAsync(csb.Endpoint.ToString(), TimeSpan.FromSeconds(120)); var sharedAccessSignature = token.TokenValue.ToString(); // Create connection string builder by SharedAccessSignature overload. From ad7da0b5eeaebfa5c26ce3b27bc151ff3c410437 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 10:34:39 -0800 Subject: [PATCH 15/24] API name change --- src/Microsoft.Azure.EventHubs/EventHubClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Azure.EventHubs/EventHubClient.cs b/src/Microsoft.Azure.EventHubs/EventHubClient.cs index e042013..352963e 100644 --- a/src/Microsoft.Azure.EventHubs/EventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/EventHubClient.cs @@ -207,7 +207,7 @@ public static EventHubClient Create( /// Operation timeout for Event Hubs operations. /// Transport type on connection. /// - public static EventHubClient CreateByManagedServiceIdentity( + public static EventHubClient CreateWithManagedServiceIdentity( Uri endpointAddress, string entityPath, TimeSpan? operationTimeout = null, From 0232bbd6d31d98ec4ad9523653c9e6d54e8b3feb Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 12:24:53 -0800 Subject: [PATCH 16/24] Xmldoc type fix. --- src/Microsoft.Azure.EventHubs/EventHubClient.cs | 2 +- src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/EventHubClient.cs b/src/Microsoft.Azure.EventHubs/EventHubClient.cs index 352963e..602cd33 100644 --- a/src/Microsoft.Azure.EventHubs/EventHubClient.cs +++ b/src/Microsoft.Azure.EventHubs/EventHubClient.cs @@ -146,7 +146,7 @@ public static EventHubClient Create( /// Event Hub path /// AuthenticationContext for AAD. /// ClientId for AAD. - /// The redrectUri on Client App. + /// The redirectUri on Client App. /// Platform parameters /// User Identifier /// Operation timeout for Event Hubs operations. diff --git a/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs b/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs index ef2ae19..145049e 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/TokenProvider.cs @@ -97,7 +97,7 @@ public static TokenProvider CreateAadTokenProvider(AuthenticationContext authCon /// Creates an Azure Active Directory token provider. /// AuthenticationContext for AAD. /// ClientId for AAD. - /// The redrectUri on Client App. + /// The redirectUri on Client App. /// Platform parameters /// User Identifier /// The for returning Json web token. From 096add2757af0c8d3aec465d479487de7de44eac Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 17:21:02 -0800 Subject: [PATCH 17/24] Remove SecurityToken internal constructor --- src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs | 1 + src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs | 4 ---- .../Primitives/SharedAccessSignatureToken.cs | 1 + 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs index 0c73b4c..f431769 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs @@ -19,6 +19,7 @@ public class JsonSecurityToken : SecurityToken /// Raw JSON Web Token string /// The audience public JsonSecurityToken(string rawToken, string audience) + : base(rawToken, DateTime.MinValue, audience, Constants.JsonWebTokenType) { var jwtSecurityToken = new JwtSecurityToken(rawToken); this.tokenType = ClientConstants.JsonWebTokenType; diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs index 02fce90..6915e19 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs @@ -30,10 +30,6 @@ public class SecurityToken /// protected string tokenType; - internal SecurityToken() - { - } - /// /// Creates a new instance of the class. /// diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs index 8280149..27e68e7 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs @@ -32,6 +32,7 @@ class SharedAccessSignatureToken : SecurityToken /// /// The token public SharedAccessSignatureToken(string tokenString) + : base(tokenString, DateTime.MinValue, string.Empty) { if (tokenString == null) { From 9bbb2bbede908d8309acdf5b9ab83bc5a6c27ed9 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 17:22:04 -0800 Subject: [PATCH 18/24] Build error fix --- src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs index f431769..05b414e 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs @@ -19,7 +19,7 @@ public class JsonSecurityToken : SecurityToken /// Raw JSON Web Token string /// The audience public JsonSecurityToken(string rawToken, string audience) - : base(rawToken, DateTime.MinValue, audience, Constants.JsonWebTokenType) + : base(rawToken, DateTime.MinValue, audience, ClientConstants.JsonWebTokenType) { var jwtSecurityToken = new JwtSecurityToken(rawToken); this.tokenType = ClientConstants.JsonWebTokenType; From 21a5ad60adddb8a6274dc57f12d587246333d022 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 17:23:04 -0800 Subject: [PATCH 19/24] Cleanup code --- src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs index 05b414e..41885d5 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs @@ -21,11 +21,9 @@ public class JsonSecurityToken : SecurityToken public JsonSecurityToken(string rawToken, string audience) : base(rawToken, DateTime.MinValue, audience, ClientConstants.JsonWebTokenType) { + // Extract expiresAtUtc from token and override it here. var jwtSecurityToken = new JwtSecurityToken(rawToken); - this.tokenType = ClientConstants.JsonWebTokenType; - this.token = rawToken; this.expiresAtUtc = jwtSecurityToken.ValidTo; - this.audience = audience; } } } From 33e1c8b1f91e6427d37cdc80c6b6023c20577183 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 17:39:42 -0800 Subject: [PATCH 20/24] Pass temporary audience instead of empty string to SecurityToken. --- .../Primitives/SharedAccessSignatureToken.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs index 27e68e7..969c73f 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs @@ -32,7 +32,7 @@ class SharedAccessSignatureToken : SecurityToken /// /// The token public SharedAccessSignatureToken(string tokenString) - : base(tokenString, DateTime.MinValue, string.Empty) + : base(tokenString, DateTime.MinValue, "tmp") { if (tokenString == null) { From e1b3aad148defcd8689071a1bb9e7e7e9b2bc306 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 17:42:56 -0800 Subject: [PATCH 21/24] Make token type required --- src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs | 2 +- .../Primitives/SharedAccessSignatureToken.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs index 6915e19..36f1e57 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs @@ -37,7 +37,7 @@ public class SecurityToken /// The expiration time /// The audience /// The type of the token - public SecurityToken(string tokenString, DateTime expiresAtUtc, string audience, string tokenType = ClientConstants.SasTokenType) + public SecurityToken(string tokenString, DateTime expiresAtUtc, string audience, string tokenType) { if (string.IsNullOrEmpty(tokenString)) { diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs index 969c73f..84b998a 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs @@ -32,7 +32,7 @@ class SharedAccessSignatureToken : SecurityToken /// /// The token public SharedAccessSignatureToken(string tokenString) - : base(tokenString, DateTime.MinValue, "tmp") + : base(tokenString, DateTime.MinValue, "tmp", ClientConstants.SasTokenType) { if (tokenString == null) { From d8c69bd8dac7358ed349f1f7c9f1403df0c61596 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Tue, 12 Dec 2017 17:49:03 -0800 Subject: [PATCH 22/24] Refactoring SharedAccessSignatureToken for cleanup --- .../Primitives/SharedAccessSignatureToken.cs | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs index 84b998a..5dff768 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs @@ -32,25 +32,7 @@ class SharedAccessSignatureToken : SecurityToken /// /// The token public SharedAccessSignatureToken(string tokenString) - : base(tokenString, DateTime.MinValue, "tmp", ClientConstants.SasTokenType) - { - if (tokenString == null) - { - throw Fx.Exception.ArgumentNull(nameof(tokenString)); - } - - this.token = tokenString; - this.tokenType = ClientConstants.SasTokenType; - GetExpirationDateAndAudienceFromToken(tokenString, out this.expiresAtUtc, out this.audience); - } - - /// - /// Creates a new instance of the class. - /// - /// The token - /// The expiration time - public SharedAccessSignatureToken(string tokenString, DateTime expiresAtUtc) - : base(tokenString, expiresAtUtc, GetAudienceFromToken(tokenString), ClientConstants.SasTokenType) + : base(tokenString, GetExpirationDateFromToken(tokenString), GetAudienceFromToken(tokenString), ClientConstants.SasTokenType) { } @@ -133,7 +115,7 @@ static string GetAudienceFromToken(string token) return audience; } - static void GetExpirationDateAndAudienceFromToken(string token, out DateTime expiresOn, out string audience) + static DateTime GetExpirationDateFromToken(string token) { string expiresIn; IDictionary decodedToken = Decode(token, Decoder, Decoder, SasKeyValueSeparator, SasPairSeparator); @@ -142,12 +124,9 @@ static void GetExpirationDateAndAudienceFromToken(string token, out DateTime exp throw new FormatException(Resources.TokenMissingExpiresOn); } - if (!decodedToken.TryGetValue(SignedResourceFullFieldName, out audience)) - { - throw new FormatException(Resources.TokenMissingAudience); - } + var expiresOn = (SharedAccessSignatureTokenProvider.EpochTime + TimeSpan.FromSeconds(double.Parse(expiresIn, CultureInfo.InvariantCulture))); - expiresOn = (SharedAccessSignatureTokenProvider.EpochTime + TimeSpan.FromSeconds(double.Parse(expiresIn, CultureInfo.InvariantCulture))); + return expiresOn; } static IDictionary Decode(string encodedString, Func keyDecoder, Func valueDecoder, string keyValueSeparator, string pairSeparator) From 9da051de334dbc1eceb6d86355b3175b02a31d66 Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Wed, 13 Dec 2017 09:26:19 -0800 Subject: [PATCH 23/24] Function renaming to carry UTC notion. --- .../Primitives/SharedAccessSignatureToken.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs index 5dff768..dec4701 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SharedAccessSignatureToken.cs @@ -32,7 +32,7 @@ class SharedAccessSignatureToken : SecurityToken /// /// The token public SharedAccessSignatureToken(string tokenString) - : base(tokenString, GetExpirationDateFromToken(tokenString), GetAudienceFromToken(tokenString), ClientConstants.SasTokenType) + : base(tokenString, GetExpirationDateTimeUtcFromToken(tokenString), GetAudienceFromToken(tokenString), ClientConstants.SasTokenType) { } @@ -115,7 +115,7 @@ static string GetAudienceFromToken(string token) return audience; } - static DateTime GetExpirationDateFromToken(string token) + static DateTime GetExpirationDateTimeUtcFromToken(string token) { string expiresIn; IDictionary decodedToken = Decode(token, Decoder, Decoder, SasKeyValueSeparator, SasPairSeparator); From 3443722049fccb462a465b1739bbfa13dc5da14c Mon Sep 17 00:00:00 2001 From: Serkant Karaca Date: Wed, 13 Dec 2017 14:21:24 -0800 Subject: [PATCH 24/24] Remove procted accesors from SecurityToken class properties. --- .../Primitives/JsonSecurityToken.cs | 11 +++++++---- .../Primitives/SecurityToken.cs | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs index 41885d5..ae2f789 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/JsonSecurityToken.cs @@ -19,11 +19,14 @@ public class JsonSecurityToken : SecurityToken /// Raw JSON Web Token string /// The audience public JsonSecurityToken(string rawToken, string audience) - : base(rawToken, DateTime.MinValue, audience, ClientConstants.JsonWebTokenType) + : base(rawToken, GetExpirationDateTimeUtcFromToken(rawToken), audience, ClientConstants.JsonWebTokenType) { - // Extract expiresAtUtc from token and override it here. - var jwtSecurityToken = new JwtSecurityToken(rawToken); - this.expiresAtUtc = jwtSecurityToken.ValidTo; + } + + static DateTime GetExpirationDateTimeUtcFromToken(string token) + { + var jwtSecurityToken = new JwtSecurityToken(token); + return jwtSecurityToken.ValidTo; } } } diff --git a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs index 36f1e57..fbc537a 100644 --- a/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs +++ b/src/Microsoft.Azure.EventHubs/Primitives/SecurityToken.cs @@ -13,22 +13,22 @@ public class SecurityToken /// /// Token literal /// - protected string token; + string token; /// /// Expiry date-time /// - protected DateTime expiresAtUtc; + DateTime expiresAtUtc; /// /// Token audience /// - protected string audience; + string audience; /// /// Token type /// - protected string tokenType; + string tokenType; /// /// Creates a new instance of the class.