Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix certificate validation E2E tests for the hub and provisioning clients #2960

Merged
merged 11 commits into from
Nov 18, 2022
125 changes: 90 additions & 35 deletions e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Net.WebSockets;
using System.Net;
using System.Security.Authentication;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Azure.Devices.Client;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand All @@ -18,42 +19,65 @@ public class IotHubCertificateValidationE2ETest : E2EMsTestBase
[Timeout(TestTimeoutMilliseconds)]
public async Task ServiceClient_QueryDevicesInvalidServiceCertificateHttp_Fails()
{
// arrange
using var sc = new IotHubServiceClient(TestConfiguration.IotHub.ConnectionStringInvalidServiceCertificate);
var exception = await Assert.ThrowsExceptionAsync<IotHubServiceException>(
() => sc.Query.CreateAsync<ClientTwin>("select * from devices")).ConfigureAwait(false);

// act
Func<Task> act = async () => await sc.Query.CreateAsync<ClientTwin>("select * from devices").ConfigureAwait(false);

// assert
var error = await act.Should().ThrowAsync<IotHubServiceException>();
error.And.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
error.And.ErrorCode.Should().Be(IotHubServiceErrorCode.IotHubUnauthorizedAccess);
error.And.IsTransient.Should().BeFalse();
#if NET472
Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException));
error.And.InnerException.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
#else
Assert.IsInstanceOfType(exception.InnerException.InnerException, typeof(AuthenticationException));
error.And.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
#endif
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task ServiceClient_SendMessageToDeviceInvalidServiceCertificateAmqpTcp_Fails()
{
// arrange
IotHubTransportProtocol protocol = IotHubTransportProtocol.Tcp;
await Assert.ThrowsExceptionAsync<AuthenticationException>(
() => TestServiceClientInvalidServiceCertificate(protocol)).ConfigureAwait(false);

// act
Func<Task> act = async () => await TestServiceClientInvalidServiceCertificateAsync(protocol).ConfigureAwait(false);

// assert
var error = await act.Should().ThrowAsync<IotHubServiceException>();
error.And.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
error.And.ErrorCode.Should().Be(IotHubServiceErrorCode.IotHubUnauthorizedAccess);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.Should().BeOfType<AuthenticationException>();
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task ServiceClient_SendMessageToDeviceInvalidServiceCertificateAmqpWs_Fails()
{
// arrange
IotHubTransportProtocol protocol = IotHubTransportProtocol.WebSocket;
WebSocketException exception = await Assert.ThrowsExceptionAsync<WebSocketException>(
() => TestServiceClientInvalidServiceCertificate(protocol)).ConfigureAwait(false);

Assert.IsInstanceOfType(exception.InnerException.InnerException, typeof(AuthenticationException));

// act
Func<Task> act = async () => await TestServiceClientInvalidServiceCertificateAsync(protocol).ConfigureAwait(false);

//assert
var error = await act.Should().ThrowAsync<IotHubServiceException>();
error.And.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
error.And.ErrorCode.Should().Be(IotHubServiceErrorCode.IotHubUnauthorizedAccess);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
}

private static async Task TestServiceClientInvalidServiceCertificate(IotHubTransportProtocol protocol)
private static async Task TestServiceClientInvalidServiceCertificateAsync(IotHubTransportProtocol protocol)
{
IotHubServiceClientOptions options = new IotHubServiceClientOptions
var options = new IotHubServiceClientOptions
{
Protocol = protocol
Protocol = protocol,
};
using var service = new IotHubServiceClient(TestConfiguration.IotHub.ConnectionStringInvalidServiceCertificate, options);
var testMessage = new Message();
Expand All @@ -66,68 +90,99 @@ private static async Task TestServiceClientInvalidServiceCertificate(IotHubTrans
[Timeout(TestTimeoutMilliseconds)]
public async Task JobClient_ScheduleTwinUpdateInvalidServiceCertificateHttp_Fails()
{
// arrange
using var sc = new IotHubServiceClient(TestConfiguration.IotHub.ConnectionStringInvalidServiceCertificate);
var ScheduledTwinUpdateOptions = new ScheduledJobsOptions
{
JobId = "testDevice",
MaxExecutionTime = TimeSpan.FromSeconds(60)
};
var exception = await Assert.ThrowsExceptionAsync<IotHubServiceException>(
() => sc.ScheduledJobs.ScheduleTwinUpdateAsync(

// act
Func<Task> act = async () =>
await sc.ScheduledJobs.ScheduleTwinUpdateAsync(
"DeviceId IN ['testDevice']",
new ClientTwin(),
DateTimeOffset.UtcNow,
ScheduledTwinUpdateOptions))
ScheduledTwinUpdateOptions)
.ConfigureAwait(false);

// assert
var error = await act.Should().ThrowAsync<IotHubServiceException>();
error.And.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
error.And.ErrorCode.Should().Be(IotHubServiceErrorCode.IotHubUnauthorizedAccess);
error.And.IsTransient.Should().BeFalse();
#if NET472
Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException));
error.And.InnerException.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
#else
Assert.IsInstanceOfType(exception.InnerException.InnerException, typeof(AuthenticationException));
error.And.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
#endif
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task IotHubDeviceClient_SendAsyncInvalidServiceCertificateAmqpTcp_Fails()
{
await Assert.ThrowsExceptionAsync<AuthenticationException>(
() => TestDeviceClientInvalidServiceCertificate(new IotHubClientAmqpSettings())).ConfigureAwait(false);
// act
Func<Task> act = async () => await TestDeviceClientInvalidServiceCertificateAsync(new IotHubClientAmqpSettings()).ConfigureAwait(false);

// assert
var error = await act.Should().ThrowAsync<IotHubClientException>();
error.And.ErrorCode.Should().Be(IotHubClientErrorCode.TlsAuthenticationError);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.Should().BeOfType<AuthenticationException>();
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task IotHubDeviceClient_SendAsyncInvalidServiceCertificateMqttTcp_Fails()
{
await Assert.ThrowsExceptionAsync<AuthenticationException>(
() => TestDeviceClientInvalidServiceCertificate(new IotHubClientMqttSettings())).ConfigureAwait(false);
// act
Func<Task> act = async () => await TestDeviceClientInvalidServiceCertificateAsync(new IotHubClientMqttSettings()).ConfigureAwait(false);

// assert
var error = await act.Should().ThrowAsync<IotHubClientException>();
error.And.ErrorCode.Should().Be(IotHubClientErrorCode.TlsAuthenticationError);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task IotHubDeviceClient_SendAsyncInvalidServiceCertificateAmqpWs_Fails()
{
AuthenticationException exception = await Assert.ThrowsExceptionAsync<AuthenticationException>(
() => TestDeviceClientInvalidServiceCertificate(new IotHubClientAmqpSettings(IotHubClientTransportProtocol.WebSocket))).ConfigureAwait(false);

Assert.IsInstanceOfType(exception.InnerException.InnerException, typeof(AuthenticationException));
// act
Func<Task> act = async () =>
await TestDeviceClientInvalidServiceCertificateAsync(new IotHubClientAmqpSettings(IotHubClientTransportProtocol.WebSocket)).ConfigureAwait(false);

// assert
var error = await act.Should().ThrowAsync<IotHubClientException>();
error.And.ErrorCode.Should().Be(IotHubClientErrorCode.TlsAuthenticationError);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task IotHubDeviceClient_SendAsyncInvalidServiceCertificateMqttWs_Fails()
{
AuthenticationException exception = await Assert.ThrowsExceptionAsync<AuthenticationException>(
() => TestDeviceClientInvalidServiceCertificate(new IotHubClientMqttSettings(IotHubClientTransportProtocol.WebSocket))).ConfigureAwait(false);

Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException));
// act
Func<Task> act = async () =>
await TestDeviceClientInvalidServiceCertificateAsync(new IotHubClientMqttSettings(IotHubClientTransportProtocol.WebSocket)).ConfigureAwait(false);

// assert
var error = await act.Should().ThrowAsync<IotHubClientException>();
error.And.ErrorCode.Should().Be(IotHubClientErrorCode.TlsAuthenticationError);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.InnerException.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
}

private static async Task TestDeviceClientInvalidServiceCertificate(IotHubClientTransportSettings transportSettings)
private static async Task TestDeviceClientInvalidServiceCertificateAsync(IotHubClientTransportSettings transportSettings)
{
await using var deviceClient = new IotHubDeviceClient(
TestConfiguration.IotHub.DeviceConnectionStringInvalidServiceCertificate,
new IotHubClientOptions(transportSettings));
await using var deviceClient =
new IotHubDeviceClient(
TestConfiguration.IotHub.DeviceConnectionStringInvalidServiceCertificate,
new IotHubClientOptions(transportSettings));
var testMessage = new TelemetryMessage();
await deviceClient.OpenAsync().ConfigureAwait(false);
await deviceClient.SendTelemetryAsync(testMessage).ConfigureAwait(false);
Expand Down
66 changes: 44 additions & 22 deletions e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
Expand Down Expand Up @@ -34,73 +35,94 @@ public static void TestClassSetup(TestContext _)
[Timeout(TestTimeoutMilliseconds)]
public async Task ProvisioningServiceClient_QueryInvalidServiceCertificateHttp_Fails()
{
using var provisioningServiceClient = new ProvisioningServiceClient(
TestConfiguration.Provisioning.ConnectionStringInvalidServiceCertificate);
Query q = provisioningServiceClient.EnrollmentGroups.CreateQuery(
"SELECT * FROM enrollmentGroups");
// arrange
using var provisioningServiceClient = new ProvisioningServiceClient(TestConfiguration.Provisioning.ConnectionStringInvalidServiceCertificate);
Query q = provisioningServiceClient.EnrollmentGroups.CreateQuery("SELECT * FROM enrollmentGroups");

// act
Func<Task> act = async () => await q.NextAsync();

// assert
var error = await act.Should().ThrowAsync<ProvisioningServiceException>().ConfigureAwait(false);
error.And.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
error.And.ErrorCode.Should().Be(0);
error.And.IsTransient.Should().BeFalse();
#if NET472
Assert.IsInstanceOfType(error.And.InnerException.InnerException.InnerException, typeof(AuthenticationException));
error.And.InnerException.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
#else
Assert.IsInstanceOfType(error.And.InnerException.InnerException, typeof(AuthenticationException));
error.And.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
#endif
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateAmqpTcp_Fails()
{
// arrange
var clientOptions = new ProvisioningClientOptions(new ProvisioningClientAmqpSettings(ProvisioningClientTransportProtocol.Tcp));
Func<Task> act = async () => await TestInvalidServiceCertificate(clientOptions);

// act
Func<Task> act = async () => await TestInvalidServiceCertificateAsync(clientOptions);

//assert
var error = await act.Should().ThrowAsync<ProvisioningClientException>().ConfigureAwait(false);
Assert.IsInstanceOfType(error.And.InnerException, typeof(AuthenticationException));
error.And.ErrorCode.Should().Be(0);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.Should().BeOfType<AuthenticationException>();
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateMqttTcp_Fails()
{
// arrange
var clientOptions = new ProvisioningClientOptions(new ProvisioningClientMqttSettings(ProvisioningClientTransportProtocol.Tcp));
Func<Task> act = async () => await TestInvalidServiceCertificate(clientOptions);

// act
Func<Task> act = async () => await TestInvalidServiceCertificateAsync(clientOptions);

// assert
var error = await act.Should().ThrowAsync<ProvisioningClientException>().ConfigureAwait(false);
if (error.And.InnerException == null)
{
Assert.AreEqual("MQTT Protocol Exception: Channel closed.", error.And.Message);
}
else
{
Assert.IsInstanceOfType(error.And.InnerException, typeof(AuthenticationException));
}
error.And.ErrorCode.Should().Be(0);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateAmqpWs_Fails()
{
// arrange
var clientOptions = new ProvisioningClientOptions(new ProvisioningClientAmqpSettings(ProvisioningClientTransportProtocol.WebSocket));
Func<Task> act = async () => await TestInvalidServiceCertificate(clientOptions);

// act
Func<Task> act = async () => await TestInvalidServiceCertificateAsync(clientOptions);

// assert
var error = await act.Should().ThrowAsync<ProvisioningClientException>().ConfigureAwait(false);
Assert.IsInstanceOfType(error.And.InnerException.InnerException.InnerException, typeof(AuthenticationException));
error.And.ErrorCode.Should().Be(0);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
}

[TestMethod]
[Timeout(TestTimeoutMilliseconds)]
public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateMqttWs_Fails()
{
// arrange
var clientOptions = new ProvisioningClientOptions(new ProvisioningClientMqttSettings(ProvisioningClientTransportProtocol.WebSocket));
Func<Task> act = async () => await TestInvalidServiceCertificate(clientOptions);

// act
Func<Task> act = async () => await TestInvalidServiceCertificateAsync(clientOptions);

// assert
var error = await act.Should().ThrowAsync<ProvisioningClientException>().ConfigureAwait(false);
Assert.IsInstanceOfType(error.And.InnerException.InnerException.InnerException, typeof(AuthenticationException));
error.And.ErrorCode.Should().Be(0);
error.And.IsTransient.Should().BeFalse();
error.And.InnerException.InnerException.InnerException.InnerException.Should().BeOfType<AuthenticationException>();
}

private async Task TestInvalidServiceCertificate(ProvisioningClientOptions clientOptions)
private static async Task TestInvalidServiceCertificateAsync(ProvisioningClientOptions clientOptions)
{
// Shorten the file name to avoid overall file path become too long and cause error in the test
string certificateSubject = "cert-" + Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('+', '-').Replace('/', '.').Trim('=');
Expand Down
2 changes: 1 addition & 1 deletion iothub/device/src/Fx/Fx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.Azure.Devices.Client
{
internal static class Fx
{
public static bool IsFatal(Exception ex)
internal static bool IsFatal(Exception ex)
{
while (ex != null)
{
Expand Down
1 change: 1 addition & 0 deletions iothub/device/src/IotHubClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ internal IotHubClientOptions Clone()
ModelId = ModelId,
SdkAssignsMessageId = SdkAssignsMessageId,
AdditionalUserAgentInfo = AdditionalUserAgentInfo,
RetryPolicy = RetryPolicy,
};
}
}
Expand Down
1 change: 0 additions & 1 deletion iothub/device/src/Pipeline/ClientPipelineBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using Azure.Core;
using Microsoft.Azure.Devices.Client.Transport;

namespace Microsoft.Azure.Devices.Client
Expand Down
Loading