/// <summary> /// Provisions a device to a starting hub, tries to open a connection, send telemetry, /// and (if supported by the protocol) send a twin update. Then, this method updates the enrollment /// to provision the device to a different hub. Based on the provided reprovisioning settings, this /// method then checks that the device was/was not reprovisioned as expected, and that the device /// did/did not migrate twin data as expected. /// </summary> public async Task ProvisioningDeviceClient_ReprovisioningFlow( Client.TransportType transportProtocol, AttestationMechanismType attestationType, EnrollmentType?enrollmentType, bool setCustomProxy, ReprovisionPolicy reprovisionPolicy, AllocationPolicy allocationPolicy, CustomAllocationDefinition customAllocationDefinition, ICollection <string> iotHubsToStartAt, ICollection <string> iotHubsToReprovisionTo, string proxyServerAddress = null) { ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(s_proxyServerAddress); string groupId = _devicePrefix + AttestationTypeToString(attestationType) + "-" + Guid.NewGuid(); bool twinOperationsAllowed = transportProtocol != Client.TransportType.Http1; using ProvisioningTransportHandler transport = ProvisioningE2ETests.CreateTransportHandlerFromName(transportProtocol); using SecurityProvider security = await CreateSecurityProviderFromName( attestationType, enrollmentType, groupId, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iotHubsToStartAt) .ConfigureAwait(false); //Check basic provisioning if (ProvisioningE2ETests.ImplementsWebProxy(transportProtocol) && setCustomProxy) { transport.Proxy = (proxyServerAddress != null) ? new WebProxy(s_proxyServerAddress) : null; } ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, Configuration.Provisioning.IdScope, security, transport); using var cts = new CancellationTokenSource(PassingTimeoutMiliseconds); DeviceRegistrationResult result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); ValidateDeviceRegistrationResult(result); Client.IAuthenticationMethod auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId); await ConfirmRegisteredDeviceWorks(result, auth, transportProtocol, twinOperationsAllowed).ConfigureAwait(false); //Check reprovisioning await UpdateEnrollmentToForceReprovision(enrollmentType, provisioningServiceClient, iotHubsToReprovisionTo, security, groupId).ConfigureAwait(false); result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); ConfirmDeviceInExpectedHub(result, reprovisionPolicy, iotHubsToStartAt, iotHubsToReprovisionTo, allocationPolicy); await ConfirmDeviceWorksAfterReprovisioning(result, auth, transportProtocol, reprovisionPolicy, twinOperationsAllowed).ConfigureAwait(false); if (attestationType != AttestationMechanismType.X509) //x509 enrollments are hardcoded, should never be deleted { await ProvisioningE2ETests.DeleteCreatedEnrollmentAsync(enrollmentType, provisioningServiceClient, security, groupId).ConfigureAwait(false); } }
private static async Task <TestDevice> CreateDeviceAsync(TestDeviceType type, string prefix) { string deviceName = "E2E_" + prefix + Guid.NewGuid(); // Delete existing devices named this way and create a new one. using var rm = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); _logger.Trace($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}."); Client.IAuthenticationMethod auth = null; var requestDevice = new Device(deviceName); if (type == TestDeviceType.X509) { requestDevice.Authentication = new AuthenticationMechanism { X509Thumbprint = new X509Thumbprint { PrimaryThumbprint = Configuration.IoTHub.GetCertificateWithPrivateKey().Thumbprint } }; auth = new DeviceAuthenticationWithX509Certificate(deviceName, Configuration.IoTHub.GetCertificateWithPrivateKey()); } Device device = await rm.AddDeviceAsync(requestDevice).ConfigureAwait(false); _logger.Trace($"{nameof(GetTestDeviceAsync)}: Pausing for {DelayAfterDeviceCreationSeconds}s after device was created."); await Task.Delay(DelayAfterDeviceCreationSeconds * 1000).ConfigureAwait(false); await rm.CloseAsync().ConfigureAwait(false); return(new TestDevice(device, auth)); }
public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok( string transportType, string securityType, X509EnrollmentType?x509EnrollmentType, TransportFallbackType?transportFallback, bool setCustomProxy, string proxyServerAddress = null) { using (ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportType, transportFallback)) using (SecurityProvider security = await CreateSecurityProviderFromName(securityType, x509EnrollmentType).ConfigureAwait(false)) { _verboseLog.WriteLine("Creating device"); if (ImplementsWebProxy(transportType, transportFallback) && setCustomProxy) { transport.Proxy = (proxyServerAddress != null) ? new WebProxy(proxyServerAddress) : null; } ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, Configuration.Provisioning.IdScope, security, transport); var cts = new CancellationTokenSource(PassingTimeoutMiliseconds); _log.WriteLine("ProvisioningDeviceClient RegisterAsync . . . "); DeviceRegistrationResult result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); Assert.IsNotNull(result); _log.WriteLine($"{result.Status} (Error Code: {result.ErrorCode}; Error Message: {result.ErrorMessage})"); _log.WriteLine($"ProvisioningDeviceClient AssignedHub: {result.AssignedHub}; DeviceID: {result.DeviceId}"); Assert.AreEqual(ProvisioningRegistrationStatusType.Assigned, result.Status); Assert.IsNotNull(result.AssignedHub); Assert.IsNotNull(result.DeviceId); Client.IAuthenticationMethod auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId); // TODO: #591 - ProvisioningClient and DeviceClient should use the same protocol. using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, Client.TransportType.Http1)) { _log.WriteLine("DeviceClient OpenAsync."); await iotClient.OpenAsync().ConfigureAwait(false); _log.WriteLine("DeviceClient SendEventAsync."); await iotClient.SendEventAsync( new Client.Message(Encoding.UTF8.GetBytes("TestMessage"))).ConfigureAwait(false); _log.WriteLine("DeviceClient CloseAsync."); await iotClient.CloseAsync().ConfigureAwait(false); } } }
public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok( string transportType, string securityType, X509EnrollmentType?x509EnrollmentType, TransportFallbackType?transportFallback) { if (!ConfigurationFound()) { _log.WriteLine("Provisioning test configuration not found. Result inconclusive."); return; } using (ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportType, transportFallback)) using (SecurityProvider security = CreateSecurityProviderFromName(securityType, x509EnrollmentType)) { _verboseLog.WriteLine("Creating device"); ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, Configuration.Provisioning.IdScope, security, transport); var cts = new CancellationTokenSource(PassingTimeoutMiliseconds); _log.WriteLine("ProvisioningClient RegisterAsync . . . "); DeviceRegistrationResult result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); Assert.IsNotNull(result); _log.WriteLine($"{result.Status} (Error Code: {result.ErrorCode}; Error Message: {result.ErrorMessage})"); _log.WriteLine($"ProvisioningClient AssignedHub: {result.AssignedHub}; DeviceID: {result.DeviceId}"); Assert.AreEqual(ProvisioningRegistrationStatusType.Assigned, result.Status); Assert.IsNotNull(result.AssignedHub); Assert.IsNotNull(result.DeviceId); Client.IAuthenticationMethod auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId); using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, Client.TransportType.Mqtt_Tcp_Only)) { _log.WriteLine("DeviceClient OpenAsync."); await iotClient.OpenAsync().ConfigureAwait(false); _log.WriteLine("DeviceClient SendEventAsync."); await iotClient.SendEventAsync( new Client.Message(Encoding.UTF8.GetBytes("TestMessage"))).ConfigureAwait(false); _log.WriteLine("DeviceClient CloseAsync."); await iotClient.CloseAsync().ConfigureAwait(false); } } }
private static async Task <TestDevice> CreateDeviceAsync(TestDeviceType type, string prefix) { string deviceName = "E2E_" + prefix + Guid.NewGuid(); // Delete existing devices named this way and create a new one. using var rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); s_logger.Trace($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}."); Client.IAuthenticationMethod auth = null; var requestDevice = new Device(deviceName); X509Certificate2 authCertificate = null; if (type == TestDeviceType.X509) { requestDevice.Authentication = new AuthenticationMechanism { X509Thumbprint = new X509Thumbprint { PrimaryThumbprint = TestConfiguration.IoTHub.GetCertificateWithPrivateKey().Thumbprint } }; #pragma warning disable CA2000 // Dispose objects before losing scope - X509Certificate and DeviceAuthenticationWithX509Certificate are disposed when TestDevice is disposed. authCertificate = TestConfiguration.IoTHub.GetCertificateWithPrivateKey(); auth = new DeviceAuthenticationWithX509Certificate(deviceName, authCertificate); #pragma warning restore CA2000 // Dispose objects before losing scope - X509Certificate and DeviceAuthenticationWithX509Certificate are disposed when TestDevice is disposed. } Device device = null; await RetryOperationHelper .RetryOperationsAsync( async() => { device = await rm.AddDeviceAsync(requestDevice).ConfigureAwait(false); }, s_exponentialBackoffRetryStrategy, s_retryableExceptions, s_logger) .ConfigureAwait(false); await rm.CloseAsync().ConfigureAwait(false); return(device == null ? throw new Exception($"Exhausted attempts for creating device {device.Id}, requests got throttled.") : new TestDevice(device, auth) { _authCertificate = authCertificate, }); }
/// <summary> /// Factory method. /// IMPORTANT: Not thread safe! /// </summary> /// <param name="namePrefix"></param> /// <param name="type"></param> /// <returns></returns> public static async Task <TestDevice> GetTestDeviceAsync(string namePrefix, TestDeviceType type = TestDeviceType.Sasl) { string prefix = namePrefix + type + "_"; if (!s_deviceCache.TryGetValue(prefix, out TestDevice testDevice)) { string deviceName = prefix + Guid.NewGuid(); s_log.WriteLine($"{nameof(GetTestDeviceAsync)}: Device with prefix {prefix} not found. Removing old devices."); // Delete existing devices named this way and create a new one. RegistryManager rm = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); s_log.WriteLine($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}."); Client.IAuthenticationMethod auth = null; Device requestDevice = new Device(deviceName); if (type == TestDeviceType.X509) { requestDevice.Authentication = new AuthenticationMechanism() { X509Thumbprint = new X509Thumbprint() { PrimaryThumbprint = Configuration.IoTHub.GetCertificateWithPrivateKey().Thumbprint } }; auth = new DeviceAuthenticationWithX509Certificate(deviceName, Configuration.IoTHub.GetCertificateWithPrivateKey()); } Device device = await rm.AddDeviceAsync(requestDevice).ConfigureAwait(false); s_log.WriteLine($"{nameof(GetTestDeviceAsync)}: Pausing for {DelayAfterDeviceCreationSeconds}s after device was created."); await Task.Delay(DelayAfterDeviceCreationSeconds * 1000).ConfigureAwait(false); s_deviceCache[prefix] = new TestDevice(device, auth); await rm.CloseAsync().ConfigureAwait(false); } TestDevice ret = s_deviceCache[prefix]; s_log.WriteLine($"{nameof(GetTestDeviceAsync)}: Using device {ret.Id}."); return(ret); }
public void Dispose() { // X509Certificate needs to be disposed for implementations !NET451 (NET451 doesn't implement X509Certificates as IDisposable). // Normally we wouldn't be disposing the X509 Certificates here, but rather delegate that to whoever was creating the TestDevice. // For the design that our test suite follows, it is ok to dispose the X509 certificate here since it won't be referenced by anyone else // within the scope of the test using this TestDevice. if (_authCertificate is IDisposable disposableCert) { disposableCert?.Dispose(); } _authCertificate = null; if (AuthenticationMethod is DeviceAuthenticationWithX509Certificate x509Auth) { x509Auth?.Dispose(); } AuthenticationMethod = null; }
private TestDevice(Device device, Client.IAuthenticationMethod authenticationMethod) { Device = device; AuthenticationMethod = authenticationMethod; }
private async Task ConfirmDeviceWorksAfterReprovisioning(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, ReprovisionPolicy reprovisionPolicy, bool twinOperationsAllowed) { using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol)) { _log.WriteLine("DeviceClient OpenAsync."); await iotClient.OpenAsync().ConfigureAwait(false); _log.WriteLine("DeviceClient SendEventAsync."); await iotClient.SendEventAsync( new Client.Message(Encoding.UTF8.GetBytes("TestMessage"))).ConfigureAwait(false); //twin can be configured to revert back to default twin when provisioned, or to keep twin // from previous hub's records. if (twinOperationsAllowed) { Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false); if (reprovisionPolicy.MigrateDeviceData) { Assert.AreNotEqual(1, twin.Properties.Desired.Count); Assert.AreNotEqual(0, twin.Properties.Desired.Version); Assert.AreEqual(ProvisioningRegistrationSubstatusType.DeviceDataMigrated, result.Substatus); } else if (reprovisionPolicy.UpdateHubAssignment) { Assert.AreEqual(twin.Properties.Desired.Count, 0); Assert.AreEqual(ProvisioningRegistrationSubstatusType.DeviceDataReset, result.Substatus); } else { Assert.AreNotEqual(twin.Properties.Desired.Count, 1); Assert.AreEqual(ProvisioningRegistrationSubstatusType.InitialAssignment, result.Substatus); } } _log.WriteLine("DeviceClient CloseAsync."); await iotClient.CloseAsync().ConfigureAwait(false); } }
private async Task ConfirmExpectedDeviceCapabilities(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, DeviceCapabilities capabilities) { if (capabilities != null) { //hardcoding amqp since http does not support twin, but tests that call into this may use http using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, Client.TransportType.Amqp)) { //Confirm that the device twin reflects what the enrollment dictated Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false); Assert.AreEqual(capabilities.IotEdge, twin.Capabilities.IotEdge); } } }
/// <summary> /// Attempt to create device client instance from provided arguments, ensure that it can open a /// connection, ensure that it can send telemetry, and (optionally) send a reported property update /// </summary> private async Task ConfirmRegisteredDeviceWorks(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, bool sendReportedPropertiesUpdate) { using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol)) { _log.WriteLine("DeviceClient OpenAsync."); await iotClient.OpenAsync().ConfigureAwait(false); _log.WriteLine("DeviceClient SendEventAsync."); await iotClient.SendEventAsync( new Client.Message(Encoding.UTF8.GetBytes("TestMessage"))).ConfigureAwait(false); if (sendReportedPropertiesUpdate) { _log.WriteLine("DeviceClient updating desired properties."); Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false); await iotClient.UpdateReportedPropertiesAsync(new TwinCollection($"{{\"{new Guid()}\":\"{new Guid()}\"}}")).ConfigureAwait(false); } _log.WriteLine("DeviceClient CloseAsync."); await iotClient.CloseAsync().ConfigureAwait(false); } }
private async Task ConfirmDeviceWorksAfterReprovisioning( DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, ReprovisionPolicy reprovisionPolicy, bool transportProtocolSupportsTwinOperations) { using (var iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol)) { Logger.Trace("DeviceClient OpenAsync."); await iotClient.OpenAsync().ConfigureAwait(false); Logger.Trace("DeviceClient SendEventAsync."); using var testMessage = new Client.Message(Encoding.UTF8.GetBytes("TestMessage")); await iotClient.SendEventAsync(testMessage).ConfigureAwait(false); // Twin can be configured to revert back to default twin when provisioned, or to keep twin // from previous hub's records. if (transportProtocolSupportsTwinOperations) { Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false); // Reprovision if (reprovisionPolicy.UpdateHubAssignment) { // Migrate data if (reprovisionPolicy.MigrateDeviceData) { // The reprovisioned twin should have an entry for the reprorted property updated previously in the test // as a part of ConfirmRegisteredDeviceWorks. // On device creation the twin reported property version starts at 1. For this scenario the reported property update // operation should increment the version to 2. twin.Properties.Reported.Count.Should().Be(1); twin.Properties.Reported.Version.Should().Be(2); result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.DeviceDataMigrated); } // Reset to initial configuration else { // The reprovisioned twin should not have an entry for the reprorted property updated previously in the test // as a part of ConfirmRegisteredDeviceWorks. // On device creation the twin reported property version starts at 1. twin.Properties.Reported.Count.Should().Be(0); twin.Properties.Reported.Version.Should().Be(1); result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.DeviceDataReset); } } // Do not reprovision else { // The reprovisioned twin should have an entry for the reprorted property updated previously in the test // as a part of ConfirmRegisteredDeviceWorks. // On device creation the twin reported property version starts at 1. For this scenario the reported property update // operation should increment the version to 2. twin.Properties.Reported.Count.Should().Be(1); twin.Properties.Reported.Version.Should().Be(2); result.Substatus.Should().Be(ProvisioningRegistrationSubstatusType.InitialAssignment); } } Logger.Trace("DeviceClient CloseAsync."); await iotClient.CloseAsync().ConfigureAwait(false); } }
/// <summary> /// Attempt to create device client instance from provided arguments, ensure that it can open a /// connection, ensure that it can send telemetry, and (optionally) send a reported property update /// </summary> private async Task ConfirmRegisteredDeviceWorks(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, bool transportProtocolSupportsTwinOperations) { using (var iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol)) { Logger.Trace("DeviceClient OpenAsync."); await iotClient.OpenAsync().ConfigureAwait(false); Logger.Trace("DeviceClient SendEventAsync."); using var message = new Client.Message(Encoding.UTF8.GetBytes("TestMessage")); await iotClient.SendEventAsync(message).ConfigureAwait(false); if (transportProtocolSupportsTwinOperations) { Logger.Trace("DeviceClient updating reported property."); Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false); await iotClient.UpdateReportedPropertiesAsync(new TwinCollection($"{{\"{new Guid()}\":\"{new Guid()}\"}}")).ConfigureAwait(false); } Logger.Trace("DeviceClient CloseAsync."); await iotClient.CloseAsync().ConfigureAwait(false); } }
/// <summary> /// Provisions a device to a starting hub, tries to open a connection, send telemetry, /// and (if supported by the protocol) send a twin update. Then, this method updates the enrollment /// to provision the device to a different hub. Based on the provided reprovisioning settings, this /// method then checks that the device was/was not reprovisioned as expected, and that the device /// did/did not migrate twin data as expected. /// </summary> public async Task ProvisioningDeviceClient_ReprovisioningFlow( Client.TransportType transportProtocol, AttestationMechanismType attestationType, EnrollmentType? enrollmentType, bool setCustomProxy, ReprovisionPolicy reprovisionPolicy, AllocationPolicy allocationPolicy, CustomAllocationDefinition customAllocationDefinition, ICollection<string> iotHubsToStartAt, ICollection<string> iotHubsToReprovisionTo, string proxyServerAddress = null) { using ProvisioningServiceClient provisioningServiceClient = CreateProvisioningService(s_proxyServerAddress); string groupId = null; if (enrollmentType == EnrollmentType.Group) { if (attestationType == AttestationMechanismType.X509) { groupId = TestConfiguration.Provisioning.X509GroupEnrollmentName; } else { groupId = _idPrefix + AttestationTypeToString(attestationType) + "-" + Guid.NewGuid(); } } bool transportProtocolSupportsTwinOperations = transportProtocol != Client.TransportType.Http1; using ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportProtocol); using SecurityProvider security = await CreateSecurityProviderFromNameAsync( attestationType, enrollmentType, groupId, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iotHubsToStartAt) .ConfigureAwait(false); //Check basic provisioning if (ImplementsWebProxy(transportProtocol) && setCustomProxy) { transport.Proxy = (proxyServerAddress != null) ? new WebProxy(s_proxyServerAddress) : null; } var provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, TestConfiguration.Provisioning.IdScope, security, transport); using var cts = new CancellationTokenSource(PassingTimeoutMiliseconds); DeviceRegistrationResult result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); ValidateDeviceRegistrationResult(result); #pragma warning disable CA2000 // Dispose objects before losing scope // The certificate instance referenced in the DeviceAuthenticationWithX509Certificate instance is common for all tests in this class. It is disposed during class cleanup. Client.IAuthenticationMethod auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId); #pragma warning restore CA2000 // Dispose objects before losing scope await ConfirmRegisteredDeviceWorks(result, auth, transportProtocol, transportProtocolSupportsTwinOperations).ConfigureAwait(false); //Check reprovisioning await UpdateEnrollmentToForceReprovision(enrollmentType, provisioningServiceClient, iotHubsToReprovisionTo, security, groupId).ConfigureAwait(false); result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); ConfirmDeviceInExpectedHub(result, reprovisionPolicy, iotHubsToStartAt, iotHubsToReprovisionTo, allocationPolicy); await ConfirmDeviceWorksAfterReprovisioning(result, auth, transportProtocol, reprovisionPolicy, transportProtocolSupportsTwinOperations).ConfigureAwait(false); if (attestationType == AttestationMechanismType.X509 && enrollmentType == EnrollmentType.Group) { Logger.Trace($"The test enrollment type {attestationType}-{enrollmentType} with group Id {groupId} is currently hardcoded - do not delete."); } else { Logger.Trace($"Deleting test enrollment type {attestationType}-{enrollmentType} with registration Id {security.GetRegistrationID()}."); await DeleteCreatedEnrollmentAsync(enrollmentType, security, groupId, Logger).ConfigureAwait(false); } if (security is SecurityProviderX509 x509Security) { X509Certificate2 deviceCertificate = x509Security.GetAuthenticationCertificate(); deviceCertificate?.Dispose(); } if (auth != null && auth is IDisposable disposableAuth) { disposableAuth?.Dispose(); } }