/// <summary> /// Creates the timer event handler to support refreshing AMQP link authorization /// on a recurring basis. /// </summary> /// /// <param name="connection">The AMQP connection to which the link being refreshed is bound to.</param> /// <param name="amqpLink">The AMQO link to refresh authorization for.</param> /// <param name="tokenProvider">The <see cref="CbsTokenProvider" /> to use for obtaining access tokens.</param> /// <param name="endpoint">The Event Hubs service endpoint that the AMQP link is communicating with.</param> /// <param name="audience">The audience associated with the authorization. This is likely the <paramref name="endpoint"/> absolute URI.</param> /// <param name="resource">The resource associated with the authorization. This is likely the <paramref name="endpoint"/> absolute URI.</param> /// <param name="requiredClaims">The set of claims required to support the operations of the AMQP link.</param> /// <param name="refreshTimeout">The timeout to apply when requesting authorization refresh.</param> /// <param name="refreshTimerFactory">A function to allow retrieving the <see cref="Timer" /> associated with the link authorization.</param> /// /// <returns>A <see cref="TimerCallback"/> delegate to perform the refresh when a timer is due.</returns> /// protected virtual TimerCallback CreateAuthorizationRefreshHandler(AmqpConnection connection, AmqpObject amqpLink, CbsTokenProvider tokenProvider, Uri endpoint, string audience, string resource, string[] requiredClaims, TimeSpan refreshTimeout, Func <Timer> refreshTimerFactory) { return(async _ => { EventHubsEventSource.Log.AmqpLinkAuthorizationRefreshStart(EventHubName, endpoint.AbsoluteUri); var refreshTimer = refreshTimerFactory(); try { var authExpirationUtc = await RequestAuthorizationUsingCbsAsync(connection, tokenProvider, endpoint, audience, resource, requiredClaims, refreshTimeout).ConfigureAwait(false); // Reset the timer for the next refresh. if (authExpirationUtc >= DateTimeOffset.UtcNow) { refreshTimer.Change(CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan); } EventHubsEventSource.Log.AmqpLinkAuthorizationRefreshComplete(EventHubName, endpoint.AbsoluteUri); } catch (Exception ex) { EventHubsEventSource.Log.AmqpLinkAuthorizationRefreshError(EventHubName, endpoint.AbsoluteUri, ex.Message); refreshTimer.Change(Timeout.Infinite, Timeout.Infinite); } }); }
/// <summary> /// Initializes a new instance of the <see cref="AmqpConnectionScope"/> class. /// </summary> /// /// <param name="serviceEndpoint">Endpoint for the Event Hubs service to which the scope is associated.</param> /// <param name="eventHubName"> The name of the Event Hub to which the scope is associated</param> /// <param name="credential">The credential to use for authorization with the Event Hubs service.</param> /// <param name="transport">The transport to use for communication.</param> /// <param name="proxy">The proxy, if any, to use for communication.</param> /// <param name="identifier">The identifier to assign this scope; if not provided, one will be generated.</param> /// public AmqpConnectionScope(Uri serviceEndpoint, string eventHubName, EventHubTokenCredential credential, EventHubsTransportType transport, IWebProxy proxy, string identifier = default) { Argument.AssertNotNull(serviceEndpoint, nameof(serviceEndpoint)); Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName)); Argument.AssertNotNull(credential, nameof(credential)); ValidateTransport(transport); ServiceEndpoint = serviceEndpoint; EventHubName = eventHubName; Transport = transport; Proxy = proxy; TokenProvider = new CbsTokenProvider(new EventHubTokenCredential(credential, serviceEndpoint.ToString()), OperationCancellationSource.Token); Id = identifier ?? $"{ eventHubName }-{ Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture).Substring(0, 8) }"; #pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for testing purposes. Task <AmqpConnection> connectionFactory(TimeSpan timeout) => CreateAndOpenConnectionAsync(AmqpVersion, ServiceEndpoint, Transport, Proxy, Id, timeout); #pragma warning restore CA2214 // Do not call overridable methods in constructors ActiveConnection = new FaultTolerantAmqpObject <AmqpConnection>(connectionFactory, CloseConnection); }
/// <summary> /// Creates the timer event handler to support refreshing AMQP link authorization /// on a recurring basis. /// </summary> /// /// <param name="connection">The AMQP connection to which the link being refreshed is bound to.</param> /// <param name="amqpLink">The AMQO link to refresh authorization for.</param> /// <param name="tokenProvider">The <see cref="CbsTokenProvider" /> to use for obtaining access tokens.</param> /// <param name="endpoint">The Event Hubs service endpoint that the AMQP link is communicating with.</param> /// <param name="audience">The audience associated with the authorization. This is likely the <paramref name="endpoint"/> absolute URI.</param> /// <param name="resource">The resource associated with the authorization. This is likely the <paramref name="endpoint"/> absolute URI.</param> /// <param name="requiredClaims">The set of claims required to support the operations of the AMQP link.</param> /// <param name="refreshTimeout">The timeout to apply when requesting authorization refresh.</param> /// <param name="refreshTimerFactory">A function to allow retrieving the <see cref="Timer" /> associated with the link authorization.</param> /// /// <returns>A <see cref="TimerCallback"/> delegate to perform the refresh when a timer is due.</returns> /// protected virtual TimerCallback CreateAuthorizationRefreshHandler(AmqpConnection connection, AmqpObject amqpLink, CbsTokenProvider tokenProvider, Uri endpoint, string audience, string resource, string[] requiredClaims, TimeSpan refreshTimeout, Func <Timer> refreshTimerFactory) { return(async _ => { EventHubsEventSource.Log.AmqpLinkAuthorizationRefreshStart(EventHubName, endpoint.AbsoluteUri); var refreshTimer = refreshTimerFactory(); try { if (refreshTimer == null) { return; } var authExpirationUtc = await RequestAuthorizationUsingCbsAsync(connection, tokenProvider, endpoint, audience, resource, requiredClaims, refreshTimeout).ConfigureAwait(false); // Reset the timer for the next refresh. if (authExpirationUtc >= DateTimeOffset.UtcNow) { refreshTimer.Change(CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan); } } catch (ObjectDisposedException) { // This can occur if the connection is closed or the scope disposed after the factory // is called but before the timer is updated. The callback may also fire while the timer is // in the act of disposing. Do not consider it an error. } catch (Exception ex) { EventHubsEventSource.Log.AmqpLinkAuthorizationRefreshError(EventHubName, endpoint.AbsoluteUri, ex.Message); // Attempt to unset the timer; there's a decent chance that it has been disposed at this point or // that the connection has been closed. Ignore potential exceptions, as they won't impact operation. // At worse, another timer tick will occur and the operation will be retried. try { refreshTimer.Change(Timeout.Infinite, Timeout.Infinite); } catch { } } finally { EventHubsEventSource.Log.AmqpLinkAuthorizationRefreshComplete(EventHubName, endpoint.AbsoluteUri); } }); }
/// <summary> /// Requests authorization for a connection or link using a connection via the CBS mechanism. /// </summary> /// /// <param name="connection">The AMQP connection for which the authorization is associated.</param> /// <param name="tokenProvider">The <see cref="CbsTokenProvider" /> to use for obtaining access tokens.</param> /// <param name="endpoint">The Event Hubs service endpoint that the authorization is requested for.</param> /// <param name="audience">The audience associated with the authorization. This is likely the <paramref name="endpoint"/> absolute URI.</param> /// <param name="resource">The resource associated with the authorization. This is likely the <paramref name="endpoint"/> absolute URI.</param> /// <param name="requiredClaims">The set of claims required to support the operations of the AMQP link.</param> /// <param name="timeout">The timeout to apply when requesting authorization.</param> /// /// <returns>The date/time, in UTC, when the authorization expires.</returns> /// /// <remarks> /// It is assumed that there is a valid <see cref="AmqpCbsLink" /> already associated /// with the connection; this will be used as the transport for the authorization /// credentials. /// </remarks> /// protected virtual Task <DateTime> RequestAuthorizationUsingCbsAsync(AmqpConnection connection, CbsTokenProvider tokenProvider, Uri endpoint, string audience, string resource, string[] requiredClaims, TimeSpan timeout) { var authLink = connection.Extensions.Find <AmqpCbsLink>(); return(authLink.SendTokenAsync(TokenProvider, endpoint, audience, resource, requiredClaims, timeout)); }
/// <summary> /// Initializes a new instance of the <see cref="AmqpConnectionScope"/> class. /// </summary> /// /// <param name="serviceEndpoint">Endpoint for the Event Hubs service to which the scope is associated.</param> /// <param name="eventHubName"> The name of the Event Hub to which the scope is associated</param> /// <param name="credential">The credential to use for authorization with the Event Hubs service.</param> /// <param name="transport">The transport to use for communication.</param> /// <param name="proxy">The proxy, if any, to use for communication.</param> /// <param name="identifier">The identifier to assign this scope; if not provided, one will be generated.</param> /// public AmqpConnectionScope(Uri serviceEndpoint, string eventHubName, TokenCredential credential, TransportType transport, IWebProxy proxy, string identifier = default) { Argument.AssertNotNull(serviceEndpoint, nameof(serviceEndpoint)); Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName)); Argument.AssertNotNull(credential, nameof(credential)); ValidateTransport(transport); ServiceEndpoint = serviceEndpoint; EventHubName = eventHubName; Transport = transport; Proxy = proxy; TokenProvider = new CbsTokenProvider(new EventHubTokenCredential(credential, serviceEndpoint.ToString()), OperationCancellationSource.Token); Id = identifier ?? $"{ eventHubName }-{ Guid.NewGuid().ToString("D").Substring(0, 8) }"; Task <AmqpConnection> connectionFactory(TimeSpan timeout) => CreateAndOpenConnectionAsync(AmqpVersion, ServiceEndpoint, Transport, Proxy, Id, timeout); ActiveConnection = new FaultTolerantAmqpObject <AmqpConnection>(connectionFactory, CloseConnection); }