public void IsSharedAccessCredentialRecognizesSasCredentials(TokenCredential credential,
                                                                     bool expectedResult)
        {
            var ServiceBusCredential = new ServiceBusTokenCredential(credential);

            Assert.That(ServiceBusCredential.IsSharedAccessCredential, Is.EqualTo(expectedResult));
        }
Exemplo n.º 2
0
        /// <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
        }
Exemplo n.º 3
0
        /// <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);
        }
Exemplo n.º 4
0
        /// <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);
        }
Exemplo n.º 6
0
        /// <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);
        }
Exemplo n.º 7
0
        /// <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>());
        }
Exemplo n.º 9
0
        /// <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);
        }
Exemplo n.º 15
0
            internal override TransportClient CreateTransportClient(ServiceBusTokenCredential credential,
                                                                    ServiceBusClientOptions options)
            {
                TransportClientCredential = credential;
                TransportClient ??= new();

                return(TransportClient);
            }
Exemplo n.º 16
0
 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.");
        }
Exemplo n.º 18
0
        /// <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));
        }
Exemplo n.º 26
0
        /// <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
        }
Exemplo n.º 28
0
        /// <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);
            }
        }
Exemplo n.º 29
0
        /// <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);
            }
        }
Exemplo n.º 30
0
 /// <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)
 {
 }