public void IsSharedAccessCredentialRecognizesSasCredentials(TokenCredential credential, bool expectedResult) { var ServiceBusCredential = new ServiceBusTokenCredential(credential); Assert.That(ServiceBusCredential.IsSharedAccessCredential, Is.EqualTo(expectedResult)); }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConnection"/> class. /// </summary> /// /// <param name="fullyQualifiedNamespace">The fully qualified Service Bus namespace to connect to. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="options">A set of options to apply when configuring the connection.</param> internal ServiceBusConnection( string fullyQualifiedNamespace, TokenCredential credential, ServiceBusClientOptions options) { Argument.AssertWellFormedServiceBusNamespace(fullyQualifiedNamespace, nameof(fullyQualifiedNamespace)); Argument.AssertNotNull(credential, nameof(credential)); ValidateConnectionOptions(options); switch (credential) { case SharedAccessSignatureCredential _: break; case ServiceBusSharedKeyCredential sharedKeyCredential: credential = sharedKeyCredential.AsSharedAccessSignatureCredential(BuildAudienceResource(options.TransportType, fullyQualifiedNamespace, EntityPath)); break; } var tokenCredential = new ServiceBusTokenCredential(credential, BuildAudienceResource(options.TransportType, fullyQualifiedNamespace, EntityPath)); FullyQualifiedNamespace = fullyQualifiedNamespace; TransportType = options.TransportType; RetryOptions = options.RetryOptions; #pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for testing purposes. _innerClient = CreateTransportClient(tokenCredential, options); #pragma warning restore CA2214 // Do not call overridable methods in constructors }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConnection"/> class. /// </summary> /// /// <param name="connectionString">The connection string to use for connecting to the Service Bus namespace.</param> /// <param name="options">A set of options to apply when configuring the connection.</param> /// /// <remarks> /// If the connection string is copied from the Service Bus entity itself, it will contain the name of the desired Service Bus entity, /// and can be used directly without passing the name="entityName" />. The name of the Service Bus entity should be /// passed only once, either as part of the connection string or separately. /// </remarks> /// internal ServiceBusConnection( string connectionString, ServiceBusClientOptions options) { Argument.AssertNotNullOrEmpty(connectionString, nameof(connectionString)); Argument.AssertNotNull(options, nameof(options)); options = options.Clone(); ValidateConnectionOptions(options); var builder = new ServiceBusConnectionStringBuilder(connectionString); FullyQualifiedNamespace = builder.FullyQualifiedNamespace; TransportType = options.TransportType; EntityPath = builder.EntityName; var sharedAccessSignature = new SharedAccessSignature ( BuildAudienceResource(options.TransportType, FullyQualifiedNamespace, EntityPath), builder.SasKeyName, builder.SasKey ); var sharedCredentials = new SharedAccessSignatureCredential(sharedAccessSignature); var tokenCredentials = new ServiceBusTokenCredential( sharedCredentials, BuildAudienceResource(options.TransportType, FullyQualifiedNamespace, EntityPath)); _innerClient = CreateTransportClient(tokenCredentials, options); }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConnection"/> class. /// </summary> /// /// <param name="fullyQualifiedNamespace">The fully qualified Service Bus namespace to connect to. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="entityName">The name of the specific Service Bus entity to associate the connection with.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="connectionOptions">A set of options to apply when configuring the connection.</param> /// public ServiceBusConnection( string fullyQualifiedNamespace, string entityName, TokenCredential credential, ServiceBusConnectionOptions connectionOptions = default) { Argument.AssertNotNullOrEmpty(fullyQualifiedNamespace, nameof(fullyQualifiedNamespace)); Argument.AssertNotNullOrEmpty(entityName, nameof(entityName)); Argument.AssertNotNull(credential, nameof(credential)); connectionOptions = connectionOptions?.Clone() ?? new ServiceBusConnectionOptions(); ValidateConnectionOptions(connectionOptions); switch (credential) { case SharedAccessSignatureCredential _: break; case ServiceBusSharedKeyCredential sharedKeyCredential: credential = sharedKeyCredential.AsSharedAccessSignatureCredential(BuildAudienceResource(connectionOptions.TransportType, fullyQualifiedNamespace, entityName)); break; } var tokenCredential = new ServiceBusTokenCredential(credential, BuildAudienceResource(connectionOptions.TransportType, fullyQualifiedNamespace, entityName)); FullyQualifiedNamespace = fullyQualifiedNamespace; EntityName = entityName; TransportType = connectionOptions.TransportType; InnerClient = CreateTransportClient(fullyQualifiedNamespace, entityName, tokenCredential, connectionOptions); }
/// <summary> /// Initializes a new instance of the <see cref="AmqpConnectionScope"/> class. /// </summary> /// /// <param name="serviceEndpoint">Endpoint for the Service Bus service to which the scope is associated.</param> /// <param name="credential">The credential to use for authorization with the Service Bus 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, ServiceBusTokenCredential credential, ServiceBusTransportType transport, IWebProxy proxy, string identifier = default) { Argument.AssertNotNull(serviceEndpoint, nameof(serviceEndpoint)); Argument.AssertNotNull(credential, nameof(credential)); ValidateTransport(transport); ServiceEndpoint = serviceEndpoint; Transport = transport; Proxy = proxy; TokenProvider = new CbsTokenProvider(new ServiceBusTokenCredential(credential, serviceEndpoint.ToString()), OperationCancellationSource.Token); Id = identifier ?? $"{ ServiceEndpoint }-{ 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); TransactionController = new FaultTolerantAmqpObject <Controller>( CreateControllerAsync, CloseController); }
/// <summary> /// Initializes a new instance of the <see cref="AmqpClient"/> class. /// </summary> /// /// <param name="host">The fully qualified host name for the Service Bus namespace. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="options">A set of options to apply when configuring the client.</param> /// /// <remarks> /// As an internal type, this class performs only basic sanity checks against its arguments. It /// is assumed that callers are trusted and have performed deep validation. /// /// Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose; /// creation of clones or otherwise protecting the parameters is assumed to be the purview of the /// caller. /// </remarks> /// internal AmqpClient( string host, ServiceBusTokenCredential credential, ServiceBusClientOptions options) { Argument.AssertNotNullOrEmpty(host, nameof(host)); Argument.AssertNotNull(credential, nameof(credential)); Argument.AssertNotNull(options, nameof(options)); ServiceEndpoint = new UriBuilder { Scheme = options.TransportType.GetUriScheme(), Host = host }.Uri; Credential = credential; if (options.EnableTransportMetrics) { TransportMetrics = new ServiceBusTransportMetrics(); } ConnectionScope = new AmqpConnectionScope( ServiceEndpoint, credential, options.TransportType, options.WebProxy, options.EnableCrossEntityTransactions, options.RetryOptions.TryTimeout, TransportMetrics); }
/// <summary> /// Initializes a new instance of the <see cref="AmqpClient"/> class. /// </summary> /// /// <param name="host">The fully qualified host name for the Service Bus namespace. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="clientOptions">A set of options to apply when configuring the client.</param> /// <param name="connectionScope">The optional scope to use for AMQP connection management. If <c>null</c>, a new scope will be created.</param> /// /// <remarks> /// As an internal type, this class performs only basic sanity checks against its arguments. It /// is assumed that callers are trusted and have performed deep validation. /// /// Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose; /// creation of clones or otherwise protecting the parameters is assumed to be the purview of the /// caller. /// </remarks> /// protected AmqpClient( string host, ServiceBusTokenCredential credential, ServiceBusClientOptions clientOptions, AmqpConnectionScope connectionScope) { Argument.AssertNotNullOrEmpty(host, nameof(host)); Argument.AssertNotNull(credential, nameof(credential)); Argument.AssertNotNull(clientOptions, nameof(clientOptions)); try { //TODO add event ServiceBusEventSource.Log.ClientCreateStart(host, entityName); ServiceEndpoint = new UriBuilder { Scheme = clientOptions.TransportType.GetUriScheme(), Host = host }.Uri; Credential = credential; ConnectionScope = connectionScope ?? new AmqpConnectionScope(ServiceEndpoint, credential, clientOptions.TransportType, clientOptions.Proxy); } finally { // TODO add event ServiceBusEventSource.Log.ServiceBusClientCreateComplete(host, entityName); } }
public void ConstructorValidatesExpirationBuffer() { var mockCredential = new Mock <TokenCredential>(); var credential = new ServiceBusTokenCredential(mockCredential.Object); Assert.That(() => new CbsTokenProvider(credential, TimeSpan.FromMilliseconds(-1), CancellationToken.None), Throws.InstanceOf <ArgumentOutOfRangeException>()); }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConnection"/> class. /// </summary> /// /// <param name="connectionString">The connection string to use for connecting to the Service Bus namespace.</param> /// <param name="entityName">The name of the specific Service Bus entity to associate the connection with (if not contained in connectionString).</param> /// <param name="connectionOptions">A set of options to apply when configuring the connection.</param> /// /// <remarks> /// If the connection string is copied from the Service Bus entity itself, it will contain the name of the desired Service Bus entity, /// and can be used directly without passing the <paramref name="entityName" />. The name of the Service Bus entity should be /// passed only once, either as part of the connection string or separately. /// </remarks> /// public ServiceBusConnection( string connectionString, string entityName, ServiceBusConnectionOptions connectionOptions) { Argument.AssertNotNullOrEmpty(connectionString, nameof(connectionString)); connectionOptions = connectionOptions?.Clone() ?? new ServiceBusConnectionOptions(); ValidateConnectionOptions(connectionOptions); var builder = new ServiceBusConnectionStringBuilder(connectionString); var fullyQualifiedNamespace = builder.FullyQualifiedNamespace; if (string.IsNullOrEmpty(entityName)) { entityName = builder.EntityName; } var sharedAccessSignature = new SharedAccessSignature ( BuildAudienceResource(connectionOptions.TransportType, fullyQualifiedNamespace, entityName), builder.SasKeyName, builder.SasKey ); var sharedCredentials = new SharedAccessSignatureCredential(sharedAccessSignature); var tokenCredentials = new ServiceBusTokenCredential(sharedCredentials, BuildAudienceResource(connectionOptions.TransportType, fullyQualifiedNamespace, entityName)); FullyQualifiedNamespace = fullyQualifiedNamespace; EntityName = entityName; InnerClient = CreateTransportClient(fullyQualifiedNamespace, entityName, tokenCredentials, connectionOptions); TransportType = connectionOptions.TransportType; }
/// <summary> /// Initializes a new instance of the <see cref="AmqpConnectionScope"/> class. /// </summary> /// /// <param name="serviceEndpoint">Endpoint for the Service Bus service to which the scope is associated.</param> /// <param name="credential">The credential to use for authorization with the Service Bus 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, ServiceBusTokenCredential credential, ServiceBusTransportType transport, IWebProxy proxy, string identifier = default) { Argument.AssertNotNull(serviceEndpoint, nameof(serviceEndpoint)); Argument.AssertNotNull(credential, nameof(credential)); ValidateTransport(transport); ServiceEndpoint = serviceEndpoint; Transport = transport; Proxy = proxy; TokenProvider = new CbsTokenProvider(new ServiceBusTokenCredential(credential, serviceEndpoint.ToString()), OperationCancellationSource.Token); Id = identifier ?? $"{ ServiceEndpoint }-{ 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); TransactionController = new FaultTolerantAmqpObject <Controller>( CreateControllerAsync, CloseController); }
public async Task GetTokenAsyncDoesNotCacheSharedAccessCredential() { var value = "TOkEn!"; var signature = new SharedAccessSignature("hub-name", "keyName", "key", value, DateTimeOffset.UtcNow.AddHours(4)); var mockCredential = new Mock <SharedAccessCredential>(signature) { CallBase = true }; var credential = new ServiceBusTokenCredential(mockCredential.Object); using var provider = new CbsTokenProvider(credential, TimeSpan.Zero, default); var firstCallToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); var nextCallToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); Assert.That(firstCallToken, Is.Not.Null, "The first call token should have been produced."); Assert.That(nextCallToken, Is.Not.Null, "The next call token should have been produced."); Assert.That(firstCallToken, Is.Not.SameAs(nextCallToken), "The token should have been expired and returned two unique instances."); mockCredential .Verify(credential => credential.GetTokenAsync( It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>()), Times.Exactly(2)); }
public async Task GetTokenAsyncRespectsExpirationBufferForJwtTokens() { var tokenValue = "ValuE_oF_tHE_tokEn"; var buffer = TimeSpan.FromMinutes(5); var expires = DateTimeOffset.UtcNow.Subtract(buffer).AddSeconds(-10); var mockCredential = new Mock <TokenCredential>(); var credential = new ServiceBusTokenCredential(mockCredential.Object); using var provider = new CbsTokenProvider(credential, TimeSpan.Zero, default); mockCredential .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>())) .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, expires))); var firstCallToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); var nextCallToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); Assert.That(firstCallToken, Is.Not.Null, "The first call token should have been produced."); Assert.That(nextCallToken, Is.Not.Null, "The next call token should have been produced."); Assert.That(firstCallToken, Is.Not.SameAs(nextCallToken), "The token should have been expired and returned two unique instances."); mockCredential .Verify(credential => credential.GetTokenAsync( It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>()), Times.Exactly(2)); }
public async Task GetTokenAsyncRespectsCacheForJwtTokens() { var tokenValue = "ValuE_oF_tHE_tokEn"; var expires = DateTimeOffset.UtcNow.AddDays(1); var mockCredential = new Mock <TokenCredential>(); var credential = new ServiceBusTokenCredential(mockCredential.Object); using var provider = new CbsTokenProvider(credential, TimeSpan.Zero, default); mockCredential .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>())) .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, expires))); var firstCallToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); var nextCallToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); Assert.That(firstCallToken, Is.Not.Null, "The first call token should have been produced."); Assert.That(nextCallToken, Is.Not.Null, "The next call token should have been produced."); Assert.That(firstCallToken, Is.SameAs(nextCallToken), "The same token instance should have been returned for both calls."); mockCredential .Verify(credential => credential.GetTokenAsync( It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>()), Times.Once); }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConnection"/> class. /// </summary> /// /// <param name="fullyQualifiedNamespace">The fully qualified Service Bus namespace to connect to. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="options">A set of options to apply when configuring the connection.</param> /// internal ServiceBusConnection( string fullyQualifiedNamespace, TokenCredential credential, ServiceBusClientOptions options) { Argument.AssertWellFormedServiceBusNamespace(fullyQualifiedNamespace, nameof(fullyQualifiedNamespace)); Argument.AssertNotNull(credential, nameof(credential)); options = options?.Clone() ?? new ServiceBusClientOptions(); ValidateConnectionOptions(options); switch (credential) { case SharedAccessSignatureCredential _: break; case ServiceBusSharedKeyCredential sharedKeyCredential: credential = sharedKeyCredential.AsSharedAccessSignatureCredential(BuildAudienceResource(options.TransportType, fullyQualifiedNamespace, EntityPath)); break; } var tokenCredential = new ServiceBusTokenCredential(credential, BuildAudienceResource(options.TransportType, fullyQualifiedNamespace, EntityPath)); FullyQualifiedNamespace = fullyQualifiedNamespace; TransportType = options.TransportType; Options = options; RetryOptions = options.RetryOptions; _innerClient = CreateTransportClient(tokenCredential, options); }
internal override TransportClient CreateTransportClient(ServiceBusTokenCredential credential, ServiceBusClientOptions options) { TransportClientCredential = credential; TransportClient ??= new(); return(TransportClient); }
public MockConnectionMockScope( Uri serviceEndpoint, ServiceBusTokenCredential credential, ServiceBusTransportType transport, IWebProxy proxy) : base(serviceEndpoint, credential, transport, proxy) { MockConnection = new Mock <AmqpConnection>(new MockTransport(), CreateMockAmqpSettings(), new AmqpConnectionSettings()); }
public void ConstructorValidatesInitializesProperties() { TokenCredential sourceCredential = Mock.Of <TokenCredential>(); var credential = new ServiceBusTokenCredential(sourceCredential); var credentialPropertyValue = typeof(ServiceBusTokenCredential) .GetField("_credential", BindingFlags.Instance | BindingFlags.NonPublic) .GetValue(credential); Assert.That(credentialPropertyValue, Is.SameAs(sourceCredential), "The source credential should have been retained."); }
/// <summary> /// Initializes a new instance of the <see cref="CbsTokenProvider"/> class. /// </summary> /// /// <param name="credential">The credential to use for access token generation.</param> /// <param name="cancellationToken">The cancellation token to consider when making requests.</param> /// public CbsTokenProvider(ServiceBusTokenCredential credential, CancellationToken cancellationToken) { Argument.AssertNotNull(credential, nameof(credential)); Credential = credential; CancellationToken = cancellationToken; TokenType = (credential.IsSharedAccessCredential) ? SharedAccessSignatureTokenType : JsonWebTokenType; }
///// <summary> ///// Creates a producer strongly aligned with the active protocol and transport, ///// responsible for publishing <see cref="ServiceBusMessage" /> to the Service Bus entity. ///// </summary> ///// <param name="entityName"></param> ///// <param name="entityConnectionString"></param> ///// ///// <param name="retryPolicy">The policy which governs retry behavior and try timeouts.</param> ///// ///// <returns>A <see cref="TransportSender"/> configured in the requested manner.</returns> ///// //internal virtual TransportSender CreateTransportProducer( // ServiceBusRetryPolicy retryPolicy, // string entityName = default, // string entityConnectionString = default) //{ // CreateTransportClient(entityName, entityConnectionString, _connectionOptions); // Argument.AssertNotNull(retryPolicy, nameof(retryPolicy)); // return InnerClient.CreateSender(retryPolicy); //} ///// <summary> ///// Creates a consumer strongly aligned with the active protocol and transport, responsible ///// for reading <see cref="ServiceBusMessage" /> from a specific Service Bus entity. ///// </summary> ///// ///// <param name="retryPolicy">The policy which governs retry behavior and try timeouts.</param> ///// <param name="receiveMode">The <see cref="ReceiveMode"/> used to specify how messages are received. Defaults to PeekLock mode.</param> ///// <param name="prefetchCount">Controls the number of events received and queued locally without regard to whether an operation was requested. If <c>null</c> a default will be used.</param> ///// <param name="sessionId"></param> ///// <param name="isSessionReceiver"></param> ///// ///// <returns>A <see cref="TransportConsumer" /> configured in the requested manner.</returns> ///// //internal virtual TransportConsumer CreateTransportConsumer( // ServiceBusRetryPolicy retryPolicy, // ReceiveMode receiveMode = default, // int? prefetchCount = default, // string sessionId = default, // bool isSessionReceiver = default) //{ // Argument.AssertNotNull(retryPolicy, nameof(retryPolicy)); // return InnerClient.CreateConsumer(retryPolicy, receiveMode, prefetchCount, sessionId, isSessionReceiver); //} /// <summary> /// Builds a Service Bus client specific to the protocol and transport specified by the /// requested connection type of the _connectionOptions />. /// </summary> /// /// <param name="credential">The Azure managed identity credential to use for authorization.</param> /// <param name="options"></param> /// /// <returns>A client generalization specific to the specified protocol/transport to which operations may be delegated.</returns> /// /// <remarks> /// As an internal method, only basic sanity checks are performed against arguments. It is /// assumed that callers are trusted and have performed deep validation. /// /// Parameters passed are also assumed to be owned by thee transport client and safe to mutate or dispose; /// creation of clones or otherwise protecting the parameters is assumed to be the purview of the caller. /// </remarks> /// internal virtual TransportClient CreateTransportClient(ServiceBusTokenCredential credential, ServiceBusClientOptions options) { switch (TransportType) { case ServiceBusTransportType.AmqpTcp: case ServiceBusTransportType.AmqpWebSockets: return(new AmqpClient(FullyQualifiedNamespace, credential, options)); default: throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources1.InvalidTransportType, options.TransportType.ToString()), nameof(options.TransportType)); } }
public async Task GetTokenAsyncSetsTheCorrectTypeForSharedAccessTokens() { var value = "TOkEn!"; var signature = new SharedAccessSignature("hub", "keyName", "key", value, DateTimeOffset.Parse("2015-10-27T00:00:00Z")); var sasCredential = new SharedAccessCredential(signature); var credential = new ServiceBusTokenCredential(sasCredential); using var provider = new CbsTokenProvider(credential, TimeSpan.Zero, default); var cbsToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); Assert.That(cbsToken, Is.Not.Null, "The token should have been produced"); Assert.That(cbsToken.TokenType, Is.EqualTo(GetSharedAccessTokenType()), "The token type should match"); }
public async Task GetTokenAsyncDelegatesToTheSourceCredential() { var mockCredential = new Mock <TokenCredential>(); var accessToken = new AccessToken("token", new DateTimeOffset(2015, 10, 27, 12, 0, 0, TimeSpan.Zero)); var resource = "the resource value"; var credential = new ServiceBusTokenCredential(mockCredential.Object); mockCredential .Setup(cred => cred.GetTokenAsync(It.Is <TokenRequestContext>(value => value.Scopes.FirstOrDefault() == resource), It.IsAny <CancellationToken>())) .ReturnsAsync(accessToken) .Verifiable("The source credential GetToken method should have been called."); AccessToken tokenResult = await credential.GetTokenAsync(new TokenRequestContext(new[] { resource }), CancellationToken.None); Assert.That(tokenResult, Is.EqualTo(accessToken), "The access token should match the return of the delegated call."); mockCredential.VerifyAll(); }
public async Task GetTokenAsyncSetsTheCorrectTypeForOtherTokens() { var tokenValue = "ValuE_oF_tHE_tokEn"; var expires = DateTimeOffset.Parse("2015-10-27T00:00:00Z"); var mockCredential = new Mock <TokenCredential>(); var credential = new ServiceBusTokenCredential(mockCredential.Object); using var provider = new CbsTokenProvider(credential, TimeSpan.Zero, default); mockCredential .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>())) .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, expires))); var cbsToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); Assert.That(cbsToken, Is.Not.Null, "The token should have been produced"); Assert.That(cbsToken.TokenType, Is.EqualTo(GetGenericTokenType()), "The token type should match"); }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConnection"/> class. /// </summary> /// /// <param name="fullyQualifiedNamespace">The fully qualified Service Bus namespace to connect to. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="options">A set of options to apply when configuring the connection.</param> internal ServiceBusConnection( string fullyQualifiedNamespace, TokenCredential credential, ServiceBusClientOptions options) { Argument.AssertWellFormedServiceBusNamespace(fullyQualifiedNamespace, nameof(fullyQualifiedNamespace)); Argument.AssertNotNull(credential, nameof(credential)); ValidateConnectionOptions(options); var tokenCredential = new ServiceBusTokenCredential(credential); FullyQualifiedNamespace = fullyQualifiedNamespace; TransportType = options.TransportType; RetryOptions = options.RetryOptions; #pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for testing purposes. _innerClient = CreateTransportClient(tokenCredential, options); #pragma warning restore CA2214 // Do not call overridable methods in constructors }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConnection"/> class. /// </summary> /// /// <param name="connectionString">The connection string to use for connecting to the Service Bus namespace.</param> /// <param name="options">A set of options to apply when configuring the connection.</param> /// /// <remarks> /// If the connection string is copied from the Service Bus entity itself, it will contain the name of the desired Service Bus entity, /// and can be used directly without passing the name="entityName" />. The name of the Service Bus entity should be /// passed only once, either as part of the connection string or separately. /// </remarks> /// internal ServiceBusConnection( string connectionString, ServiceBusClientOptions options) { Argument.AssertNotNullOrEmpty(connectionString, nameof(connectionString)); options = options?.Clone() ?? new ServiceBusClientOptions(); ValidateConnectionOptions(options); ConnectionStringProperties connectionStringProperties = ConnectionStringParser.Parse(connectionString); if (string.IsNullOrEmpty(connectionStringProperties.Endpoint?.Host) || string.IsNullOrEmpty(connectionStringProperties.SharedAccessKeyName) || string.IsNullOrEmpty(connectionStringProperties.SharedAccessKey)) { throw new ArgumentException(Resources.MissingConnectionInformation, nameof(connectionString)); } FullyQualifiedNamespace = connectionStringProperties.Endpoint.Host; TransportType = options.TransportType; EntityPath = connectionStringProperties.EntityPath; Options = options; RetryOptions = options.RetryOptions; var sharedAccessSignature = new SharedAccessSignature ( BuildAudienceResource(options.TransportType, FullyQualifiedNamespace, EntityPath), connectionStringProperties.SharedAccessKeyName, connectionStringProperties.SharedAccessKey ); var sharedCredential = new SharedAccessSignatureCredential(sharedAccessSignature); var tokenCredential = new ServiceBusTokenCredential( sharedCredential, BuildAudienceResource(TransportType, FullyQualifiedNamespace, EntityPath)); #pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for testing purposes. _innerClient = CreateTransportClient(tokenCredential, options); #pragma warning restore CA2214 // Do not call overridable methods in constructors }
public async Task GetTokenAsyncSynchonizesMultipleRefreshAttemptsForJwtTokens() { var tokenValue = "ValuE_oF_tHE_tokEn"; var buffer = TimeSpan.FromMinutes(5); var expires = DateTimeOffset.UtcNow.Subtract(buffer).AddSeconds(-10); var mockCredential = new Mock <TokenCredential>(); var credential = new ServiceBusTokenCredential(mockCredential.Object); using var provider = new CbsTokenProvider(credential, TimeSpan.Zero, default); mockCredential .SetupSequence(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>())) .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, expires))) .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, DateTimeOffset.UtcNow.AddDays(1)))); var firstCallToken = await provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]); Assert.That(firstCallToken, Is.Not.Null, "The first call token should have been produced."); var otherTokens = await Task.WhenAll( Enumerable.Range(0, 25) .Select(index => provider.GetTokenAsync(new Uri("http://www.here.com"), "nobody", new string[0]))); for (var index = 0; index < otherTokens.Length; ++index) { Assert.That(otherTokens[index], Is.Not.Null, $"The token at index `{ index }` should have been produced."); Assert.That(firstCallToken, Is.Not.SameAs(otherTokens[index]), $"The token at index `{ index } ` should not have matched the first call instance."); if (index > 0) { Assert.That(otherTokens[0], Is.SameAs(otherTokens[index]), $"The other tokens should all be the same instance. The token at index `{ index } ` did not match index `0`."); } } mockCredential .Verify(credential => credential.GetTokenAsync( It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>()), Times.Exactly(2)); }
/// <summary> /// Initializes a new instance of the <see cref="AmqpClient"/> class. /// </summary> /// /// <param name="host">The fully qualified host name for the Service Bus namespace. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="options">A set of options to apply when configuring the client.</param> /// /// <remarks> /// As an internal type, this class performs only basic sanity checks against its arguments. It /// is assumed that callers are trusted and have performed deep validation. /// /// Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose; /// creation of clones or otherwise protecting the parameters is assumed to be the purview of the /// caller. /// </remarks> /// internal AmqpClient( string host, ServiceBusTokenCredential credential, ServiceBusClientOptions options) { Argument.AssertNotNullOrEmpty(host, nameof(host)); Argument.AssertNotNull(credential, nameof(credential)); Argument.AssertNotNull(options, nameof(options)); ServiceEndpoint = new UriBuilder { Scheme = options.TransportType.GetUriScheme(), Host = host }.Uri; Credential = credential; ConnectionScope = new AmqpConnectionScope( ServiceEndpoint, credential, options.TransportType, options.WebProxy); }
/// <summary> /// Initializes a new instance of the <see cref="ServiceBusConnection"/> class. /// </summary> /// /// <param name="connectionString">The connection string to use for connecting to the Service Bus namespace.</param> /// <param name="options">A set of options to apply when configuring the connection.</param> /// /// <remarks> /// If the connection string is copied from the Service Bus entity itself, it will contain the name of the desired Service Bus entity, /// and can be used directly without passing the name="entityName" />. The name of the Service Bus entity should be /// passed only once, either as part of the connection string or separately. /// </remarks> /// internal ServiceBusConnection( string connectionString, ServiceBusClientOptions options) { Argument.AssertNotNullOrEmpty(connectionString, nameof(connectionString)); ValidateConnectionOptions(options); var connectionStringProperties = ConnectionStringParser.Parse(connectionString); ValidateConnectionStringProperties(connectionStringProperties, nameof(connectionString)); FullyQualifiedNamespace = connectionStringProperties.Endpoint.Host; TransportType = options.TransportType; EntityPath = connectionStringProperties.EntityPath; RetryOptions = options.RetryOptions; SharedAccessSignature sharedAccessSignature; if (string.IsNullOrEmpty(connectionStringProperties.SharedAccessSignature)) { sharedAccessSignature = new SharedAccessSignature( BuildConnectionResource(options.TransportType, FullyQualifiedNamespace, EntityPath), connectionStringProperties.SharedAccessKeyName, connectionStringProperties.SharedAccessKey); } else { sharedAccessSignature = new SharedAccessSignature(connectionStringProperties.SharedAccessSignature); } var sharedCredential = new SharedAccessSignatureCredential(sharedAccessSignature); var tokenCredential = new ServiceBusTokenCredential( sharedCredential, BuildConnectionResource(TransportType, FullyQualifiedNamespace, EntityPath)); #pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for testing purposes. _innerClient = CreateTransportClient(tokenCredential, options); #pragma warning restore CA2214 // Do not call overridable methods in constructors }
/// <summary> /// Initializes a new instance of the <see cref="AmqpClient"/> class. /// </summary> /// /// <param name="host">The fully qualified host name for the Service Bus namespace. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="entityName">The name of the specific Service Bus entity to connect the client to.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="clientOptions">A set of options to apply when configuring the client.</param> /// <param name="connectionScope">The optional scope to use for AMQP connection management. If <c>null</c>, a new scope will be created.</param> /// /// <remarks> /// As an internal type, this class performs only basic sanity checks against its arguments. It /// is assumed that callers are trusted and have performed deep validation. /// /// Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose; /// creation of clones or otherwise protecting the parameters is assumed to be the purview of the /// caller. /// </remarks> /// protected AmqpClient(string host, string entityName, ServiceBusTokenCredential credential, ServiceBusConnectionOptions clientOptions, AmqpConnectionScope connectionScope) { Argument.AssertNotNullOrEmpty(host, nameof(host)); Argument.AssertNotNullOrEmpty(entityName, nameof(entityName)); Argument.AssertNotNull(credential, nameof(credential)); Argument.AssertNotNull(clientOptions, nameof(clientOptions)); try { //TODO add event ServiceBusEventSource.Log.ClientCreateStart(host, entityName); ServiceEndpoint = new UriBuilder { Scheme = clientOptions.TransportType.GetUriScheme(), Host = host }.Uri; EntityName = entityName; Credential = credential; ConnectionScope = connectionScope ?? new AmqpConnectionScope(ServiceEndpoint, entityName, credential, clientOptions.TransportType, clientOptions.Proxy); ManagementLink = new FaultTolerantAmqpObject <RequestResponseAmqpLink>( timeout => ConnectionScope.OpenManagementLinkAsync(timeout, CancellationToken.None), link => { link.Session?.SafeClose(); link.SafeClose(); }); } finally { // TODO add event ServiceBusEventSource.Log.ServiceBusClientCreateComplete(host, entityName); } }
/// <summary> /// Initializes a new instance of the <see cref="CbsTokenProvider"/> class. /// </summary> /// /// <param name="credential">The credential to use for access token generation.</param> /// <param name="tokenExpirationBuffer">The amount of time to buffer expiration</param> /// <param name="cancellationToken">The cancellation token to consider when making requests.</param> /// public CbsTokenProvider(ServiceBusTokenCredential credential, TimeSpan tokenExpirationBuffer, CancellationToken cancellationToken) { Argument.AssertNotNull(credential, nameof(credential)); Argument.AssertNotNegative(tokenExpirationBuffer, nameof(tokenExpirationBuffer)); Credential = credential; TokenExpirationBuffer = tokenExpirationBuffer; CancellationToken = cancellationToken; TokenType = credential.IsSharedAccessCredential ? SharedAccessTokenType : JsonWebTokenType; // Tokens are only cached for JWT-based credentials; no need // to instantiate the semaphore if no caching is taking place. if (!credential.IsSharedAccessCredential) { TokenAcquireSemaphore = new SemaphoreSlim(1, 1); } }
/// <summary> /// Initializes a new instance of the <see cref="AmqpClient"/> class. /// </summary> /// /// <param name="host">The fully qualified host name for the Service Bus namespace. This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param> /// <param name="entityName">The name of the specific Service Bus entity to connect the client to.</param> /// <param name="credential">The Azure managed identity credential to use for authorization. Access controls may be specified by the Service Bus namespace or the requested Service Bus entity, depending on Azure configuration.</param> /// <param name="clientOptions">A set of options to apply when configuring the client.</param> /// /// <remarks> /// As an internal type, this class performs only basic sanity checks against its arguments. It /// is assumed that callers are trusted and have performed deep validation. /// /// Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose; /// creation of clones or otherwise protecting the parameters is assumed to be the purview of the /// caller. /// </remarks> /// public AmqpClient(string host, string entityName, ServiceBusTokenCredential credential, ServiceBusConnectionOptions clientOptions) : this(host, entityName, credential, clientOptions, null) { }