diff --git a/common/src/service/IotHubConnectionString.cs b/common/src/service/IotHubConnectionString.cs index a23c509351..2ea31ebf95 100644 --- a/common/src/service/IotHubConnectionString.cs +++ b/common/src/service/IotHubConnectionString.cs @@ -14,11 +14,12 @@ namespace Microsoft.Azure.Devices /// /// The properties required for authentication to IoT hub using a connection string. /// - internal sealed class IotHubConnectionString : IotHubCredential + internal sealed class IotHubConnectionString + : IotHubConnectionProperties { private static readonly TimeSpan _tokenTimeToLive = TimeSpan.FromHours(1); - public IotHubConnectionString(IotHubConnectionStringBuilder builder) : base(builder.HostName) + public IotHubConnectionString(IotHubConnectionStringBuilder builder) : base(builder?.HostName) { if (builder == null) { @@ -29,9 +30,6 @@ public IotHubConnectionString(IotHubConnectionStringBuilder builder) : base(buil SharedAccessKeyName = builder.SharedAccessKeyName; SharedAccessKey = builder.SharedAccessKey; SharedAccessSignature = builder.SharedAccessSignature; - DeviceId = builder.DeviceId; - ModuleId = builder.ModuleId; - GatewayHostName = builder.GatewayHostName; } public string Audience { get; private set; } @@ -42,12 +40,6 @@ public IotHubConnectionString(IotHubConnectionStringBuilder builder) : base(buil public string SharedAccessSignature { get; private set; } - public string DeviceId { get; private set; } - - public string ModuleId { get; private set; } - - public string GatewayHostName { get; private set; } - public string GetPassword() { return string.IsNullOrWhiteSpace(SharedAccessSignature) ? BuildToken(out _) : SharedAccessSignature; @@ -92,13 +84,6 @@ private string BuildToken(out TimeSpan ttl) Target = Audience }; - if (DeviceId != null) - { - builder.Target = string.IsNullOrEmpty(ModuleId) - ? "{0}/devices/{1}".FormatInvariant(Audience, WebUtility.UrlEncode(DeviceId)) - : "{0}/devices/{1}/modules/{2}".FormatInvariant(Audience, WebUtility.UrlEncode(DeviceId), WebUtility.UrlEncode(ModuleId)); - } - ttl = builder.TimeToLive; return builder.ToSignature(); diff --git a/iothub/service/src/IoTHubSasCredential.cs b/iothub/service/src/IoTHubSasCredential.cs new file mode 100644 index 0000000000..1079d063c2 --- /dev/null +++ b/iothub/service/src/IoTHubSasCredential.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; +using System.Text; + +#if !NET451 + +using Azure; + +namespace Microsoft.Azure.Devices +{ + /// + /// Shared access signature credential used to authenticate to an IoT hub. + /// + public abstract class IoTHubSasCredential : AzureSasCredential + { + /// + /// Creates and instance of . + /// + /// Shared access signature to use to authenticate with the IoT hub. + /// The shared access signature expiry in UTC. + public IoTHubSasCredential(string signature, DateTime expiresOnUtc) : base(signature) + { + ExpiresOnUtc = expiresOnUtc; + } + + /// + /// Updates the shared access signature. This is intended to be used when you've + /// regenerated your shared access signature and want to update clients. + /// + /// Shared access signature to use to authenticate with the IoT hub. + /// The shared access signature expiry in UTC. + public void Update(string signature, DateTime expiresOnUtc) + { + Update(signature); + ExpiresOnUtc = expiresOnUtc; + } + + /// + /// The shared access signature expiry in UTC. + /// + public DateTime ExpiresOnUtc { get; private set; } + } +} + +#endif diff --git a/iothub/service/src/IotHubConnection.cs b/iothub/service/src/IotHubConnection.cs index 5a17e2ede5..645f634345 100644 --- a/iothub/service/src/IotHubConnection.cs +++ b/iothub/service/src/IotHubConnection.cs @@ -51,7 +51,7 @@ internal sealed class IotHubConnection : IDisposable private IOThreadTimer _refreshTokenTimer; #endif - public IotHubConnection(IotHubCredential credential, AccessRights accessRights, bool useWebSocketOnly, ServiceClientTransportSettings transportSettings) + public IotHubConnection(IotHubConnectionProperties credential, AccessRights accessRights, bool useWebSocketOnly, ServiceClientTransportSettings transportSettings) { #if !NET451 _refreshTokenTimer = new IOThreadTimerSlim(s => ((IotHubConnection)s).OnRefreshTokenAsync(), this); @@ -71,7 +71,7 @@ internal IotHubConnection(Func> onCreate, Action(onCreate, onClose); } - internal IotHubCredential Credential { get; private set; } + internal IotHubConnectionProperties Credential { get; private set; } public Task OpenAsync(TimeSpan timeout) { diff --git a/iothub/service/src/IotHubCredential.cs b/iothub/service/src/IotHubConnectionProperties.cs similarity index 85% rename from iothub/service/src/IotHubCredential.cs rename to iothub/service/src/IotHubConnectionProperties.cs index 42ec547ae7..9f32b6b374 100644 --- a/iothub/service/src/IotHubCredential.cs +++ b/iothub/service/src/IotHubConnectionProperties.cs @@ -12,7 +12,7 @@ namespace Microsoft.Azure.Devices /// /// The properties required for authentication to IoT hub that are independent of the authentication type. /// - internal abstract class IotHubCredential + internal abstract class IotHubConnectionProperties : IAuthorizationHeaderProvider, ICbsTokenProvider { private const string HostNameSeparator = "."; @@ -20,12 +20,17 @@ internal abstract class IotHubCredential // Azure.Core (used in IotHubTokenCredential) is not available in NET451. // So we need this constructor for the build to pass. - protected IotHubCredential() + protected IotHubConnectionProperties() { } - protected IotHubCredential(string hostName) + protected IotHubConnectionProperties(string hostName) { + if (string.IsNullOrWhiteSpace(hostName)) + { + throw new ArgumentNullException(nameof(hostName)); + } + HostName = hostName; IotHubName = GetIotHubName(hostName); AmqpEndpoint = new UriBuilder(CommonConstants.AmqpsScheme, HostName, AmqpConstants.DefaultSecurePort).Uri; @@ -54,7 +59,7 @@ public Uri BuildLinkAddress(string path) return builder.Uri; } - private static string GetIotHubName(string hostName) + public static string GetIotHubName(string hostName) { if (string.IsNullOrWhiteSpace(hostName)) { diff --git a/iothub/service/src/IotHubSasCredentialProperties.cs b/iothub/service/src/IotHubSasCredentialProperties.cs new file mode 100644 index 0000000000..948543ec23 --- /dev/null +++ b/iothub/service/src/IotHubSasCredentialProperties.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Azure.Amqp; + +#if !NET451 + +using Azure; + +#endif + +namespace Microsoft.Azure.Devices +{ + internal class IotHubSasCredentialProperties : IotHubConnectionProperties + { +#if NET451 + + public IotHubSasCredentialProperties() + { + throw new InvalidOperationException("nameof(AzureSasCredential) is not supported in NET451"); + } +#else + private readonly IoTHubSasCredential _credential; + + public IotHubSasCredentialProperties(string hostName, IoTHubSasCredential credential) : base(hostName) + { + _credential = credential; + } + +#endif + + public override string GetAuthorizationHeader() + { +#if NET451 + throw new InvalidOperationException($"AzureSasCredential is not supported on NET451"); + +#else + return _credential.Signature; +#endif + } + + public override Task GetTokenAsync(Uri namespaceAddress, string appliesTo, string[] requiredClaims) + { +#if NET451 + throw new InvalidOperationException($"AzureSasCredential is not supported on NET451"); + +#else + var token = new CbsToken( + _credential.Signature, + CbsConstants.IotHubSasTokenType, + _credential.ExpiresOnUtc); + return Task.FromResult(token); +#endif + } + } +} diff --git a/iothub/service/src/IotHubTokenCredential.cs b/iothub/service/src/IotHubTokenCredentialProperties.cs similarity index 79% rename from iothub/service/src/IotHubTokenCredential.cs rename to iothub/service/src/IotHubTokenCredentialProperties.cs index bb2c1bdde5..8a62a53bef 100644 --- a/iothub/service/src/IotHubTokenCredential.cs +++ b/iothub/service/src/IotHubTokenCredentialProperties.cs @@ -18,11 +18,12 @@ namespace Microsoft.Azure.Devices /// /// The properties required for authentication to IoT hub using a token credential. /// - internal class IotHubTokenCredential : IotHubCredential + internal class IotHubTokenCrendentialProperties + : IotHubConnectionProperties { #if NET451 - public IotHubTokenCredential() + public IotHubTokenCrendentialProperties() { throw new InvalidOperationException("nameof(TokenCredential) is not supported in NET451"); } @@ -30,7 +31,7 @@ public IotHubTokenCredential() private const string _tokenType = "jwt"; private readonly TokenCredential _credential; - public IotHubTokenCredential(string hostName, TokenCredential credential) : base(hostName) + public IotHubTokenCrendentialProperties(string hostName, TokenCredential credential) : base(hostName) { _credential = credential; } @@ -40,7 +41,7 @@ public IotHubTokenCredential(string hostName, TokenCredential credential) : base public override string GetAuthorizationHeader() { #if NET451 - throw new InvalidOperationException($"{nameof(GetAuthorizationHeader)} is not supported on NET451"); + throw new InvalidOperationException($"TokenCredential is not supported on NET451"); #else AccessToken token = _credential.GetToken(new TokenRequestContext(), new CancellationToken()); @@ -55,7 +56,7 @@ public async override Task GetTokenAsync(Uri namespaceAddress, string { #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously #if NET451 - throw new InvalidOperationException($"{nameof(GetTokenAsync)} is not supported on NET451"); + throw new InvalidOperationException($"TokenCredential is not supported on NET451"); #else AccessToken token = await _credential.GetTokenAsync(new TokenRequestContext(), new CancellationToken()).ConfigureAwait(false); diff --git a/iothub/service/tests/ConnectionString/ServiceClientConnectionStringTests.cs b/iothub/service/tests/ConnectionString/ServiceClientConnectionStringTests.cs index c542f86aea..0feae6597f 100644 --- a/iothub/service/tests/ConnectionString/ServiceClientConnectionStringTests.cs +++ b/iothub/service/tests/ConnectionString/ServiceClientConnectionStringTests.cs @@ -186,10 +186,7 @@ public void ServiceClient_ConnectionString_ModuleIdentity_SharedAccessKeyCredent Assert.IsNotNull(iotHubConnectionString); Assert.AreEqual("testhub.azure-devices-int.net", iotHubConnectionString.Audience); Assert.AreEqual("testhub.azure-devices-int.net", iotHubConnectionString.HostName); - Assert.AreEqual("edgecapabledevice1", iotHubConnectionString.DeviceId); - Assert.AreEqual("testModule", iotHubConnectionString.ModuleId); Assert.AreEqual("dGVzdFN0cmluZzE=", iotHubConnectionString.SharedAccessKey); - Assert.AreEqual("edgehub1.ms.com", iotHubConnectionString.GatewayHostName); Assert.IsNotNull(iotHubConnectionString.GetPassword()); } } diff --git a/iothub/service/tests/IotHubConnectionPropertiesTests.cs b/iothub/service/tests/IotHubConnectionPropertiesTests.cs new file mode 100644 index 0000000000..3ba9c652e6 --- /dev/null +++ b/iothub/service/tests/IotHubConnectionPropertiesTests.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Collections.Generic; +using System.Text; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Azure.Devices.Tests +{ + [TestClass] + [TestCategory("Unit")] + public class IotHubConnectionPropertiesTests + { + [TestMethod] + [DataRow("acme.azure-devices.net", "acme")] + [DataRow("Acme-1.azure-devices.net", "Acme-1")] + [DataRow("acme2.azure-devices.net", "acme2")] + [DataRow("3acme.azure-devices.net", "3acme")] + [DataRow("4-acme.azure-devices.net", "4-acme")] + public void IotHubConnectionPropertiesGetHubNameTest(string hostName, string expectedHubName) + { + // act + string hubName = IotHubConnectionProperties.GetIotHubName(hostName); + + // assert + hubName.Should().Be(expectedHubName); + } + } +}