diff --git a/e2e/test/provisioning/ProvisioningE2ETests.cs b/e2e/test/provisioning/ProvisioningE2ETests.cs index b78ccca42e..777541f173 100644 --- a/e2e/test/provisioning/ProvisioningE2ETests.cs +++ b/e2e/test/provisioning/ProvisioningE2ETests.cs @@ -38,7 +38,7 @@ public class ProvisioningE2ETests : E2EMsTestBase private static readonly string s_proxyServerAddress = TestConfiguration.IotHub.ProxyServerAddress; private static readonly string s_certificatePassword = TestConfiguration.Provisioning.CertificatePassword; - private static readonly HashSet s_retryableExceptions = new HashSet { typeof(ProvisioningServiceClientHttpException) }; + private static readonly HashSet s_retryableExceptions = new() { typeof(ProvisioningServiceClientHttpException) }; private static readonly IRetryPolicy s_provisioningServiceRetryPolicy = new ProvisioningServiceRetryPolicy(); private readonly string _idPrefix = $"e2e-{nameof(ProvisioningE2ETests).ToLower()}-"; @@ -606,7 +606,7 @@ public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok( bool setCustomProxy, string proxyServerAddress = null) { - //Default reprovisioning settings: Hashed allocation, no reprovision policy, hub names, or custom allocation policy + // Default reprovisioning settings: Hashed allocation, no reprovision policy, hub names, or custom allocation policy await ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( transportType, attestationType, @@ -663,14 +663,9 @@ private async Task ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( string groupId = null; if (enrollmentType == EnrollmentType.Group) { - if (attestationType == AttestationMechanismType.X509) - { - groupId = TestConfiguration.Provisioning.X509GroupEnrollmentName; - } - else - { - groupId = _idPrefix + AttestationTypeToString(attestationType) + "-" + Guid.NewGuid(); - } + groupId = attestationType == AttestationMechanismType.X509 + ? TestConfiguration.Provisioning.X509GroupEnrollmentName + : $"{_idPrefix}{AttestationTypeToString(attestationType)}-{Guid.NewGuid()}"; } using ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportType); @@ -704,7 +699,7 @@ private async Task ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( DeviceRegistrationResult result = null; Client.IAuthenticationMethod auth = null; - VerboseTestLogger.WriteLine($"ProvisioningDeviceClient RegisterAsync for group {groupId} . . . "); + VerboseTestLogger.WriteLine($"ProvisioningDeviceClient RegisterAsync for group {groupId}... "); try { @@ -714,9 +709,9 @@ private async Task ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( { try { - result = timeout != TimeSpan.MaxValue - ? await provClient.RegisterAsync(timeout).ConfigureAwait(false) - : await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); + result = timeout == TimeSpan.MaxValue + ? await provClient.RegisterAsync(cts.Token).ConfigureAwait(false) + : await provClient.RegisterAsync(timeout).ConfigureAwait(false); break; } // Catching all ProvisioningTransportException as the status code is not the same for Mqtt, Amqp and Http. @@ -728,20 +723,20 @@ private async Task ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( } } - ProvisioningE2ETests.ValidateDeviceRegistrationResult(false, result); + ValidateDeviceRegistrationResult(false, 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. auth = CreateAuthenticationMethodFromSecurityProvider(security, result.DeviceId); #pragma warning restore CA2000 // Dispose objects before losing scope - await ProvisioningE2ETests.ConfirmRegisteredDeviceWorksAsync(result, auth, transportType, false).ConfigureAwait(false); + await ConfirmRegisteredDeviceWorksAsync(result, auth, transportType, false).ConfigureAwait(false); await ConfirmExpectedDeviceCapabilitiesAsync(result, auth, deviceCapabilities).ConfigureAwait(false); } finally { - await ProvisioningE2ETests.CleanupEnrollmentsAsync(attestationType, enrollmentType, security.GetRegistrationID(), groupId); - ProvisioningE2ETests.DisposeTestObjects(security, auth); + await CleanupEnrollmentsAsync(attestationType, enrollmentType, security.GetRegistrationID(), groupId).ConfigureAwait(false); + DisposeTestObjects(security, auth); } } @@ -799,15 +794,21 @@ private async Task ProvisioningDeviceClientInvalidIdScopeRegisterFailAsync( using var cts = new CancellationTokenSource(FailingTimeoutMiliseconds); - ProvisioningTransportException exception = await Assert.ThrowsExceptionAsync( - () => provClient.RegisterAsync(cts.Token)).ConfigureAwait(false); + ProvisioningTransportException exception = await Assert + .ThrowsExceptionAsync( + () => provClient.RegisterAsync(cts.Token)) + .ConfigureAwait(false); VerboseTestLogger.WriteLine($"Exception: {exception}"); } finally { - await ProvisioningE2ETests.CleanupEnrollmentsAsync(attestationType, enrollmentType, security.GetRegistrationID(), groupId); - ProvisioningE2ETests.DisposeTestObjects(security, null); + if (security != null) + { + await CleanupEnrollmentsAsync(attestationType, enrollmentType, security.GetRegistrationID(), groupId).ConfigureAwait(false); + } + + DisposeTestObjects(security, null); } } @@ -842,16 +843,16 @@ private async Task ProvisioningDeviceClientInvalidGlobalAddressRegisterFailAsync VerboseTestLogger.WriteLine("ProvisioningDeviceClient RegisterAsync . . . "); - ProvisioningTransportException exception = await Assert. - ThrowsExceptionAsync(() => provClient.RegisterAsync(cts.Token)) + ProvisioningTransportException exception = await Assert + .ThrowsExceptionAsync(() => provClient.RegisterAsync(cts.Token)) .ConfigureAwait(false); VerboseTestLogger.WriteLine($"Exception: {exception}"); } finally { - await ProvisioningE2ETests.CleanupEnrollmentsAsync(attestationType, enrollmentType, security.GetRegistrationID(), groupId); - ProvisioningE2ETests.DisposeTestObjects(security, null); + await CleanupEnrollmentsAsync(attestationType, enrollmentType, security.GetRegistrationID(), groupId); + DisposeTestObjects(security, null); } } @@ -913,9 +914,9 @@ private static async Task ConfirmExpectedDeviceCapabilitiesAsync(DeviceRegistrat { if (capabilities != null && capabilities.IotEdge) { - //If device is edge device, it should be able to connect to iot hub as its edgehub module identity + // If device is edge device, it should be able to connect to IoT hub as its edgehub module identity var connectionStringBuilder = Client.IotHubConnectionStringBuilder.Create(result.AssignedHub, auth); - string edgehubConnectionString = connectionStringBuilder.ToString() + ";ModuleId=$edgeHub"; + string edgehubConnectionString = $"{connectionStringBuilder};ModuleId=$edgeHub"; using var moduleClient = ModuleClient.CreateFromConnectionString(edgehubConnectionString); await moduleClient.OpenAsync().ConfigureAwait(false); } @@ -933,28 +934,34 @@ private async Task CreateSecurityProviderFromNameAsync( { VerboseTestLogger.WriteLine($"{nameof(CreateSecurityProviderFromNameAsync)}({attestationType})"); - string registrationId = AttestationTypeToString(attestationType) + "-" + Guid.NewGuid(); + // Using a colon tests the encoding/decoding code path as it is used in the URL for registration and is an invalid character in a URI path which requires encoding + char separator = attestationType == AttestationMechanismType.SymmetricKey + ? ':' + : '-'; + string registrationId = $"{AttestationTypeToString(attestationType)}{separator}{Guid.NewGuid()}"; using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); switch (attestationType) { case AttestationMechanismType.Tpm: IndividualEnrollment tpmEnrollment = await CreateIndividualEnrollmentAsync( - provisioningServiceClient, - registrationId, - AttestationMechanismType.Tpm, - null, - reprovisionPolicy, - allocationPolicy, - customAllocationDefinition, - iothubs, - capabilities).ConfigureAwait(false); + provisioningServiceClient, + registrationId, + AttestationMechanismType.Tpm, + null, + reprovisionPolicy, + allocationPolicy, + customAllocationDefinition, + iothubs, + capabilities) + .ConfigureAwait(false); return new SecurityProviderTpmSimulator(tpmEnrollment.RegistrationId); case AttestationMechanismType.X509: X509Certificate2 certificate = null; X509Certificate2Collection collection = null; + switch (enrollmentType) { case EnrollmentType.Individual: @@ -969,15 +976,16 @@ private async Task CreateSecurityProviderFromNameAsync( using (X509Certificate2 publicCertificate = X509Certificate2Helper.CreateX509Certificate2FromCerFile(registrationId, s_x509CertificatesFolder)) { IndividualEnrollment x509IndividualEnrollment = await CreateIndividualEnrollmentAsync( - provisioningServiceClient, - registrationId, - AttestationMechanismType.X509, - publicCertificate, - reprovisionPolicy, - allocationPolicy, - customAllocationDefinition, - iothubs, - capabilities).ConfigureAwait(false); + provisioningServiceClient, + registrationId, + AttestationMechanismType.X509, + publicCertificate, + reprovisionPolicy, + allocationPolicy, + customAllocationDefinition, + iothubs, + capabilities) + .ConfigureAwait(false); x509IndividualEnrollment.Attestation.Should().BeAssignableTo(); } @@ -1004,7 +1012,7 @@ private async Task CreateSecurityProviderFromNameAsync( TestConfiguration.CommonCertificates.GetRootCaCertificate(), TestConfiguration.CommonCertificates.GetIntermediate1Certificate(), TestConfiguration.CommonCertificates.GetIntermediate2Certificate(), - X509Certificate2Helper.CreateX509Certificate2FromCerFile(registrationId, s_x509CertificatesFolder) + X509Certificate2Helper.CreateX509Certificate2FromCerFile(registrationId, s_x509CertificatesFolder), }; break; @@ -1030,31 +1038,31 @@ private async Task CreateSecurityProviderFromNameAsync( .ConfigureAwait(false); Assert.IsTrue(symmetricKeyEnrollmentGroup.Attestation is SymmetricKeyAttestation); var symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollmentGroup.Attestation; - string registrationIdSymmetricKey = _idPrefix + Guid.NewGuid(); string primaryKeyEnrollmentGroup = symmetricKeyAttestation.PrimaryKey; string secondaryKeyEnrollmentGroup = symmetricKeyAttestation.SecondaryKey; - string primaryKeyIndividual = ComputeDerivedSymmetricKey(Convert.FromBase64String(primaryKeyEnrollmentGroup), registrationIdSymmetricKey); - string secondaryKeyIndividual = ComputeDerivedSymmetricKey(Convert.FromBase64String(secondaryKeyEnrollmentGroup), registrationIdSymmetricKey); + string primaryKeyIndividual = ComputeDerivedSymmetricKey(Convert.FromBase64String(primaryKeyEnrollmentGroup), registrationId); + string secondaryKeyIndividual = ComputeDerivedSymmetricKey(Convert.FromBase64String(secondaryKeyEnrollmentGroup), registrationId); - return new SecurityProviderSymmetricKey(registrationIdSymmetricKey, primaryKeyIndividual, secondaryKeyIndividual); + return new SecurityProviderSymmetricKey(registrationId, primaryKeyIndividual, secondaryKeyIndividual); case EnrollmentType.Individual: IndividualEnrollment symmetricKeyEnrollment = await CreateIndividualEnrollmentAsync( - provisioningServiceClient, - registrationId, - AttestationMechanismType.SymmetricKey, - null, - reprovisionPolicy, - allocationPolicy, - customAllocationDefinition, - iothubs, - capabilities).ConfigureAwait(false); + provisioningServiceClient, + registrationId, + AttestationMechanismType.SymmetricKey, + null, + reprovisionPolicy, + allocationPolicy, + customAllocationDefinition, + iothubs, + capabilities) + .ConfigureAwait(false); Assert.IsTrue(symmetricKeyEnrollment.Attestation is SymmetricKeyAttestation); symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollment.Attestation; - registrationIdSymmetricKey = symmetricKeyEnrollment.RegistrationId; + string registrationIdSymmetricKey = symmetricKeyEnrollment.RegistrationId; string primaryKey = symmetricKeyAttestation.PrimaryKey; string secondaryKey = symmetricKeyAttestation.SecondaryKey; return new SecurityProviderSymmetricKey(registrationIdSymmetricKey, primaryKey, secondaryKey); @@ -1142,31 +1150,25 @@ public static async Task DeleteCreatedEnrollmentAsync( if (enrollmentType == EnrollmentType.Individual) { await RetryOperationHelper - .RetryOperationsAsync( - async () => - { - await dpsClient.DeleteIndividualEnrollmentAsync(registrationId).ConfigureAwait(false); - }, - s_provisioningServiceRetryPolicy, - s_retryableExceptions) - .ConfigureAwait(false); + .RetryOperationsAsync( + async () => await dpsClient.DeleteIndividualEnrollmentAsync(registrationId).ConfigureAwait(false), + s_provisioningServiceRetryPolicy, + s_retryableExceptions) + .ConfigureAwait(false); } else if (enrollmentType == EnrollmentType.Group) { await RetryOperationHelper - .RetryOperationsAsync( - async () => - { - await dpsClient.DeleteEnrollmentGroupAsync(groupId).ConfigureAwait(false); - }, - s_provisioningServiceRetryPolicy, - s_retryableExceptions) - .ConfigureAwait(false); + .RetryOperationsAsync( + async () => await dpsClient.DeleteEnrollmentGroupAsync(groupId).ConfigureAwait(false), + s_provisioningServiceRetryPolicy, + s_retryableExceptions) + .ConfigureAwait(false); } } catch (Exception ex) { - VerboseTestLogger.WriteLine($"$Encountered an exception while cleaning enrollments. Test will ignore this exception and continue: {ex}."); + VerboseTestLogger.WriteLine($"$Encountered an exception while cleaning enrollments. Test will ignore this exception and continue: {ex}"); } } @@ -1192,7 +1194,11 @@ public static bool ImplementsWebProxy(Client.TransportType transportProtocol) throw new NotSupportedException($"Unknown transport: '{transportProtocol}'."); } - private static async Task CleanupEnrollmentsAsync(AttestationMechanismType attestationMechanismType, EnrollmentType enrollmentType, string registrationId, string groupId) + private static async Task CleanupEnrollmentsAsync( + AttestationMechanismType attestationMechanismType, + EnrollmentType enrollmentType, + string registrationId, + string groupId) { try { @@ -1212,9 +1218,11 @@ private static async Task CleanupEnrollmentsAsync(AttestationMechanismType attes } } - private static void DisposeTestObjects(SecurityProvider securityProvider, Client.IAuthenticationMethod authenticationMethod) + private static void DisposeTestObjects( + SecurityProvider securityProvider, + Client.IAuthenticationMethod authenticationMethod) { - string registrationId = securityProvider.GetRegistrationID(); + string registrationId = securityProvider?.GetRegistrationID(); if (securityProvider is SecurityProviderX509 x509SecurityProvider) { diff --git a/provisioning/device/samples/getting started/ComputeDerivedSymmetricKeySample/ComputeDerivedKeySample.cs b/provisioning/device/samples/getting started/ComputeDerivedSymmetricKeySample/ComputeDerivedKeySample.cs index df5f481cce..545eaea0d9 100644 --- a/provisioning/device/samples/getting started/ComputeDerivedSymmetricKeySample/ComputeDerivedKeySample.cs +++ b/provisioning/device/samples/getting started/ComputeDerivedSymmetricKeySample/ComputeDerivedKeySample.cs @@ -38,7 +38,7 @@ public void RunSample() /// The registration Id of the key to create. /// The key for the specified device Id registration in the enrollment group. /// - /// https://docs.microsoft.com/en-us/azure/iot-edge/how-to-auto-provision-symmetric-keys?view=iotedge-2018-06#derive-a-device-key + /// https://docs.microsoft.com/azure/iot-edge/how-to-auto-provision-symmetric-keys?view=iotedge-2018-06#derive-a-device-key /// private static string ComputeDerivedSymmetricKey(string enrollmentKey, string registrationId) { diff --git a/provisioning/device/samples/how to guides/SymmetricKeySample/ProvisioningDeviceClientSample.cs b/provisioning/device/samples/how to guides/SymmetricKeySample/ProvisioningDeviceClientSample.cs index 8214f0a4a3..a264c57e3c 100644 --- a/provisioning/device/samples/how to guides/SymmetricKeySample/ProvisioningDeviceClientSample.cs +++ b/provisioning/device/samples/how to guides/SymmetricKeySample/ProvisioningDeviceClientSample.cs @@ -75,6 +75,7 @@ public async Task RunSampleAsync() private ProvisioningTransportHandler GetTransportHandler() { + Console.WriteLine($"Using transport type {_parameters.TransportType}."); return _parameters.TransportType switch { TransportType.Mqtt => new ProvisioningTransportHandlerMqtt(), diff --git a/provisioning/device/samples/how to guides/TpmSample/TpmSample.csproj b/provisioning/device/samples/how to guides/TpmSample/TpmSample.csproj index 98fb7581ed..355e5615c5 100644 --- a/provisioning/device/samples/how to guides/TpmSample/TpmSample.csproj +++ b/provisioning/device/samples/how to guides/TpmSample/TpmSample.csproj @@ -3,7 +3,6 @@ Exe net6.0 - $(MSBuildProjectDirectory)\..\..\..\.. 8.0 diff --git a/provisioning/transport/http/src/Generated/RuntimeRegistration.cs b/provisioning/transport/http/src/Generated/RuntimeRegistration.cs index 54e605bf30..652b533d9e 100644 --- a/provisioning/transport/http/src/Generated/RuntimeRegistration.cs +++ b/provisioning/transport/http/src/Generated/RuntimeRegistration.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Azure.Devices.Provisioning.Client.Transport.Models; -using Microsoft.Azure.Devices.Shared; -using Microsoft.Rest; -using Microsoft.Rest.Serialization; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Globalization; @@ -15,14 +10,18 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Azure.Devices.Provisioning.Client.Transport.Models; +using Microsoft.Azure.Devices.Shared; +using Microsoft.Rest; +using Microsoft.Rest.Serialization; +using Newtonsoft.Json; namespace Microsoft.Azure.Devices.Provisioning.Client.Transport { /// /// RuntimeRegistration operations. /// - internal partial class RuntimeRegistration - : IServiceOperations, IRuntimeRegistration + internal partial class RuntimeRegistration : IServiceOperations, IRuntimeRegistration { /// /// Initializes a new instance of the RuntimeRegistration class. @@ -80,7 +79,7 @@ public async Task> OperationS string operationId, string idScope, Dictionary> customHeaders = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { if (registrationId == null) { @@ -94,12 +93,14 @@ public async Task> OperationS { throw new ValidationException(ValidationRules.CannotBeNull, "idScope"); } + // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) + bool shouldTrace = ServiceClientTracing.IsEnabled; + string invocationId = null; + + if (shouldTrace) { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); + invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); var tracingParameters = new Dictionary { { "registrationId", registrationId }, @@ -107,136 +108,118 @@ public async Task> OperationS { "idScope", idScope }, { "cancellationToken", cancellationToken } }; - ServiceClientTracing.Enter(_invocationId, this, "OperationStatusLookup", tracingParameters); + ServiceClientTracing.Enter(invocationId, this, "OperationStatusLookup", tracingParameters); } + // Construct URL - string _baseUrl = Client.BaseUri.AbsoluteUri; - string _url = new Uri( - new Uri(_baseUrl + (_baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), - "{idScope}/registrations/{registrationId}/operations/{operationId}").ToString(); - -#if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER - _url = _url.Replace("{registrationId}", Uri.EscapeDataString(registrationId), StringComparison.Ordinal); - _url = _url.Replace("{operationId}", Uri.EscapeDataString(operationId), StringComparison.Ordinal); - _url = _url.Replace("{idScope}", Uri.EscapeDataString(idScope), StringComparison.Ordinal); -#else - _url = _url.Replace("{registrationId}", Uri.EscapeDataString(registrationId)); - _url = _url.Replace("{operationId}", Uri.EscapeDataString(operationId)); - _url = _url.Replace("{idScope}", Uri.EscapeDataString(idScope)); -#endif + string baseUrl = Client.BaseUri.AbsoluteUri; + string url = new Uri( + new Uri(baseUrl + (baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), + $"{Uri.EscapeDataString(idScope)}/registrations/{Uri.EscapeDataString(registrationId)}/operations/{Uri.EscapeDataString(operationId)}").ToString(); // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("GET"); - _httpRequest.RequestUri = new Uri(_url); - // Set Headers + var httpRequest = new HttpRequestMessage + { + Method = new HttpMethod("GET"), + RequestUri = new Uri(url) + }; + // Set Headers if (customHeaders != null) { - foreach (KeyValuePair> _header in customHeaders) + foreach (KeyValuePair> header in customHeaders) { - if (_httpRequest.Headers.Contains(_header.Key)) + if (httpRequest.Headers.Contains(header.Key)) { - _httpRequest.Headers.Remove(_header.Key); + httpRequest.Headers.Remove(header.Key); } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + httpRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); } } // Serialize Request - string _requestContent = null; + string requestContent = null; + // Set Credentials if (Client.Credentials != null) { cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + await Client.Credentials.ProcessHttpRequestAsync(httpRequest, cancellationToken).ConfigureAwait(false); } + // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } + if (shouldTrace) + ServiceClientTracing.SendRequest(invocationId, httpRequest); + cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; + HttpResponseMessage httpResponse = await Client.HttpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false); + + if (shouldTrace) + ServiceClientTracing.ReceiveResponse(invocationId, httpResponse); + + HttpStatusCode statusCode = httpResponse.StatusCode; cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200 && (int)_statusCode != 202) - { - var ex = new HttpOperationException(string.Format(CultureInfo.InvariantCulture, "Operation returned an invalid status code '{0}'", _statusCode)); - if (_httpResponse.Content != null) - { - _responseContent = await _httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); - } - else - { - _responseContent = string.Empty; - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } + string responseContent; + if ((int)statusCode != 200 && (int)statusCode != 202) + { + var ex = new HttpOperationException(string.Format(CultureInfo.InvariantCulture, "Operation returned an invalid status code '{0}'", statusCode)); + responseContent = httpResponse.Content == null + ? string.Empty + : await httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); + ex.Request = new HttpRequestMessageWrapper(httpRequest, requestContent); + ex.Response = new HttpResponseMessageWrapper(httpResponse, responseContent); + + if (shouldTrace) + ServiceClientTracing.Error(invocationId, ex); + + httpRequest.Dispose(); + httpResponse?.Dispose(); throw ex; } + // Create Result - var _result = new HttpOperationResponse + var result = new HttpOperationResponse { - Request = _httpRequest, - Response = _httpResponse + Request = httpRequest, + Response = httpResponse }; - // Deserialize Response - if ((int)_statusCode == 200) + + // Deserialize 200 Response + if ((int)statusCode == 200) { - _responseContent = await _httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); + responseContent = await httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); try { - _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + result.Body = SafeJsonConvert.DeserializeObject(responseContent, Client.DeserializationSettings); } catch (JsonException ex) { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + httpRequest.Dispose(); + httpResponse?.Dispose(); + throw new SerializationException("Unable to deserialize the response.", responseContent, ex); } } - // Deserialize Response - if ((int)_statusCode == 202) + + // Deserialize 202 Response + if ((int)statusCode == 202) { - _responseContent = await _httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); + responseContent = await httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); try { - _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + result.Body = SafeJsonConvert.DeserializeObject(responseContent, Client.DeserializationSettings); } catch (JsonException ex) { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + httpRequest.Dispose(); + httpResponse?.Dispose(); + throw new SerializationException("Unable to deserialize the response.", responseContent, ex); } } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; + + if (shouldTrace) + ServiceClientTracing.Exit(invocationId, result); + + return result; } /// @@ -274,9 +257,9 @@ public async Task> OperationS public async Task> DeviceRegistrationStatusLookupWithHttpMessagesAsync( string registrationId, string idScope, - DeviceRegistration deviceRegistration = default(DeviceRegistration), + DeviceRegistration deviceRegistration = default, Dictionary> customHeaders = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { if (registrationId == null) { @@ -286,12 +269,14 @@ public async Task> OperationS { throw new ValidationException(ValidationRules.CannotBeNull, "idScope"); } + // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) + bool shouldTrace = ServiceClientTracing.IsEnabled; + string invocationId = null; + + if (shouldTrace) { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); + invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); var tracingParameters = new Dictionary { { "registrationId", registrationId }, @@ -299,123 +284,109 @@ public async Task> OperationS { "idScope", idScope }, { "cancellationToken", cancellationToken } }; - ServiceClientTracing.Enter(_invocationId, this, "DeviceRegistrationStatusLookup", tracingParameters); + ServiceClientTracing.Enter(invocationId, this, "DeviceRegistrationStatusLookup", tracingParameters); } + // Construct URL string baseUrl = Client.BaseUri.AbsoluteUri; string url = new Uri( new Uri(baseUrl + (baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), - "{idScope}/registrations/{registrationId}") + $"{Uri.EscapeUriString(idScope)}/registrations/{Uri.EscapeUriString(registrationId)}") .ToString(); -#if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER - url = url.Replace("{registrationId}", Uri.EscapeDataString(registrationId), StringComparison.Ordinal); - url = url.Replace("{idScope}", Uri.EscapeDataString(idScope), StringComparison.Ordinal); -#else - url = url.Replace("{registrationId}", Uri.EscapeDataString(registrationId)); - url = url.Replace("{idScope}", Uri.EscapeDataString(idScope)); -#endif - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("POST"); - _httpRequest.RequestUri = new Uri(url); - // Set Headers + var httpRequest = new HttpRequestMessage + { + Method = new HttpMethod("POST"), + RequestUri = new Uri(url) + }; + // Set Headers if (customHeaders != null) { - foreach (KeyValuePair> _header in customHeaders) + foreach (KeyValuePair> header in customHeaders) { - if (_httpRequest.Headers.Contains(_header.Key)) + if (httpRequest.Headers.Contains(header.Key)) { - _httpRequest.Headers.Remove(_header.Key); + httpRequest.Headers.Remove(header.Key); } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + httpRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); } } // Serialize Request - string _requestContent = null; + string requestContent = null; if (deviceRegistration != null) { - _requestContent = SafeJsonConvert.SerializeObject(deviceRegistration, Client.SerializationSettings); - _httpRequest.Content = new StringContent(_requestContent, Encoding.UTF8); - _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + requestContent = SafeJsonConvert.SerializeObject(deviceRegistration, Client.SerializationSettings); + httpRequest.Content = new StringContent(requestContent, Encoding.UTF8); + httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); } + // Set Credentials if (Client.Credentials != null) { cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + await Client.Credentials.ProcessHttpRequestAsync(httpRequest, cancellationToken).ConfigureAwait(false); } + // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } + if (shouldTrace) + ServiceClientTracing.SendRequest(invocationId, httpRequest); + cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; + HttpResponseMessage httpResponse = await Client.HttpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false); + + if (shouldTrace) + ServiceClientTracing.ReceiveResponse(invocationId, httpResponse); + + HttpStatusCode statusCode = httpResponse.StatusCode; cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new HttpOperationException(string.Format(CultureInfo.InvariantCulture, "Operation returned an invalid status code '{0}'", _statusCode)); - if (_httpResponse.Content != null) - { - _responseContent = await _httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); - } - else - { - _responseContent = string.Empty; - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } + string responseContent; + if ((int)statusCode != 200) + { + var ex = new HttpOperationException(string.Format(CultureInfo.InvariantCulture, "Operation returned an invalid status code '{0}'", statusCode)); + responseContent = httpResponse.Content == null + ? string.Empty + : await httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); + ex.Request = new HttpRequestMessageWrapper(httpRequest, requestContent); + ex.Response = new HttpResponseMessageWrapper(httpResponse, responseContent); + + if (shouldTrace) + ServiceClientTracing.Error(invocationId, ex); + + httpRequest.Dispose(); + httpResponse?.Dispose(); throw ex; } + // Create Result - var _result = new HttpOperationResponse + var result = new HttpOperationResponse { - Request = _httpRequest, - Response = _httpResponse + Request = httpRequest, + Response = httpResponse }; - // Deserialize Response - if ((int)_statusCode == 200) + + // Deserialize 200 Response + if ((int)statusCode == 200) { - _responseContent = await _httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); + responseContent = await httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); try { - _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + result.Body = SafeJsonConvert.DeserializeObject(responseContent, Client.DeserializationSettings); } catch (JsonException ex) { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + httpRequest.Dispose(); + httpResponse?.Dispose(); + throw new SerializationException("Unable to deserialize the response.", responseContent, ex); } } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; + + if (shouldTrace) + ServiceClientTracing.Exit(invocationId, result); + + return result; } /// @@ -457,10 +428,10 @@ public async Task> OperationS public async Task> RegisterDeviceWithHttpMessagesAsync( string registrationId, string idScope, - DeviceRegistration deviceRegistration = default(DeviceRegistration), - bool? forceRegistration = default(bool?), + DeviceRegistration deviceRegistration = default, + bool? forceRegistration = default, Dictionary> customHeaders = null, - CancellationToken cancellationToken = default(CancellationToken)) + CancellationToken cancellationToken = default) { if (registrationId == null) { @@ -470,12 +441,13 @@ public async Task> RegisterDe { throw new ValidationException(ValidationRules.CannotBeNull, "idScope"); } + // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) + bool shouldTrace = ServiceClientTracing.IsEnabled; + string invocationId = null; + if (shouldTrace) { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); + invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); var tracingParameters = new Dictionary { { "registrationId", registrationId }, @@ -484,135 +456,121 @@ public async Task> RegisterDe { "idScope", idScope }, { "cancellationToken", cancellationToken } }; - ServiceClientTracing.Enter(_invocationId, this, "RegisterDevice", tracingParameters); + ServiceClientTracing.Enter(invocationId, this, "RegisterDevice", tracingParameters); } + // Construct URL string baseUrl = Client.BaseUri.AbsoluteUri; string url = new Uri( new Uri(baseUrl + (baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), - "{idScope}/registrations/{registrationId}/register") + $"{WebUtility.UrlEncode(idScope)}/registrations/{WebUtility.UrlEncode(registrationId)}/register") .ToString(); -#if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER - url = url.Replace("{registrationId}", Uri.EscapeDataString(registrationId), StringComparison.Ordinal); - url = url.Replace("{idScope}", Uri.EscapeDataString(idScope), StringComparison.Ordinal); -#else - url = url.Replace("{registrationId}", Uri.EscapeDataString(registrationId)); - url = url.Replace("{idScope}", Uri.EscapeDataString(idScope)); -#endif - - var _queryParameters = new List(); + var queryParameters = new List(); if (forceRegistration != null) { - _queryParameters.Add(string.Format( + queryParameters.Add(string.Format( CultureInfo.InvariantCulture, "forceRegistration={0}", Uri.EscapeDataString(SafeJsonConvert.SerializeObject(forceRegistration, Client.SerializationSettings).Trim('"')))); } - if (_queryParameters.Count > 0) + if (queryParameters.Count > 0) { - url += "?" + string.Join("&", _queryParameters); + url += "?" + string.Join("&", queryParameters); } + // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("PUT"); - _httpRequest.RequestUri = new Uri(url); - // Set Headers + var httpRequest = new HttpRequestMessage + { + Method = new HttpMethod("PUT"), + RequestUri = new Uri(url) + }; + // Set Headers if (customHeaders != null) { - foreach (KeyValuePair> _header in customHeaders) + foreach (KeyValuePair> header in customHeaders) { - if (_httpRequest.Headers.Contains(_header.Key)) + if (httpRequest.Headers.Contains(header.Key)) { - _httpRequest.Headers.Remove(_header.Key); + httpRequest.Headers.Remove(header.Key); } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + httpRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); } } // Serialize Request - string _requestContent = null; + string requestContent = null; if (deviceRegistration != null) { - _requestContent = SafeJsonConvert.SerializeObject(deviceRegistration, Client.SerializationSettings); - _httpRequest.Content = new StringContent(_requestContent, Encoding.UTF8); - _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + requestContent = SafeJsonConvert.SerializeObject(deviceRegistration, Client.SerializationSettings); + httpRequest.Content = new StringContent(requestContent, Encoding.UTF8); + httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); } // Set Credentials if (Client.Credentials != null) { cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + await Client.Credentials.ProcessHttpRequestAsync(httpRequest, cancellationToken).ConfigureAwait(false); } + // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } + if (shouldTrace) + ServiceClientTracing.SendRequest(invocationId, httpRequest); + cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; + HttpResponseMessage httpResponse = await Client.HttpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false); + + if (shouldTrace) + ServiceClientTracing.ReceiveResponse(invocationId, httpResponse); + + HttpStatusCode statusCode = httpResponse.StatusCode; cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 202) - { - var ex = new HttpOperationException(string.Format(CultureInfo.InvariantCulture, "Operation returned an invalid status code '{0}'", _statusCode)); - if (_httpResponse.Content != null) - { - _responseContent = await _httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); - } - else - { - _responseContent = string.Empty; - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } + string responseContent; + if ((int)statusCode != 202) + { + var ex = new HttpOperationException(string.Format(CultureInfo.InvariantCulture, "Operation returned an invalid status code '{0}'", statusCode)); + responseContent = httpResponse.Content != null + ? await httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false) + : string.Empty; + ex.Request = new HttpRequestMessageWrapper(httpRequest, requestContent); + ex.Response = new HttpResponseMessageWrapper(httpResponse, responseContent); + + if (shouldTrace) + ServiceClientTracing.Error(invocationId, ex); + + httpRequest.Dispose(); + httpResponse?.Dispose(); throw ex; } + // Create Result - var _result = new HttpOperationResponse + var result = new HttpOperationResponse { - Request = _httpRequest, - Response = _httpResponse + Request = httpRequest, + Response = httpResponse }; + // Deserialize Response - if ((int)_statusCode == 202) + if ((int)statusCode == 202) { - _responseContent = await _httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); + responseContent = await httpResponse.Content.ReadHttpContentAsStringAsync(cancellationToken).ConfigureAwait(false); try { - _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + result.Body = SafeJsonConvert.DeserializeObject(responseContent, Client.DeserializationSettings); } catch (JsonException ex) { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + httpRequest.Dispose(); + httpResponse?.Dispose(); + throw new SerializationException("Unable to deserialize the response.", responseContent, ex); } } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; + + if (shouldTrace) + ServiceClientTracing.Exit(invocationId, result); + + return result; } } } diff --git a/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs b/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs index d9683806ed..fa8fdd3b79 100644 --- a/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs +++ b/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs @@ -20,8 +20,8 @@ namespace Microsoft.Azure.Devices.Provisioning.Client.Transport /// public class ProvisioningTransportHandlerHttp : ProvisioningTransportHandler { - private static readonly TimeSpan s_defaultOperationPoolingIntervalMilliseconds = TimeSpan.FromSeconds(2); private const int DefaultHttpsPort = 443; + private static readonly TimeSpan s_defaultOperationPoolingIntervalMilliseconds = TimeSpan.FromSeconds(2); /// /// Creates an instance of the ProvisioningTransportHandlerHttp class. diff --git a/provisioning/transport/http/src/SymmetricKeyCredentials.cs b/provisioning/transport/http/src/SymmetricKeyCredentials.cs index 426f050bd4..ddd24938f0 100644 --- a/provisioning/transport/http/src/SymmetricKeyCredentials.cs +++ b/provisioning/transport/http/src/SymmetricKeyCredentials.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; @@ -12,14 +13,15 @@ namespace Microsoft.Azure.Devices.Provisioning.Client.Transport { internal class SymmetricKeyCredentials : ServiceClientCredentials { - private const string SASHeaderName = "SharedAccessSignature"; + private const string SasHeaderName = "SharedAccessSignature"; private const string Registration = "registration"; - private readonly string SymmetricKey; - private volatile string _sasToken; - public SymmetricKeyCredentials(string symmetricKey) : base() + private readonly string _symmetricKey; + + public SymmetricKeyCredentials(string symmetricKey) + : base() { - SymmetricKey = symmetricKey; + _symmetricKey = symmetricKey; } public override Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) @@ -27,15 +29,22 @@ public override Task ProcessHttpRequestAsync(HttpRequestMessage request, Cancell string audience = request.RequestUri.AbsolutePath.Trim('/'); string[] segments = audience.Split('/'); - _sasToken = ProvisioningSasBuilder.BuildSasSignature(Registration, this.SymmetricKey, string.Concat(segments[0], '/', segments[1], '/', segments[2]), TimeSpan.FromDays(1)); - SetAuthorizationHeader(request, _sasToken); + string sasToken = ProvisioningSasBuilder.BuildSasSignature( + Registration, + _symmetricKey, + // These values may come in encoded, so decode them for the SAS token + $"{WebUtility.UrlDecode(segments[0])}/{WebUtility.UrlDecode(segments[1])}/{WebUtility.UrlDecode(segments[2])}", + TimeSpan.FromDays(1)); + SetAuthorizationHeader(request, sasToken); return base.ProcessHttpRequestAsync(request, cancellationToken); } private static void SetAuthorizationHeader(HttpRequestMessage request, string sasToken) { - request.Headers.Authorization = new AuthenticationHeaderValue(SASHeaderName, sasToken.Substring(SASHeaderName.Length + 1)); + request.Headers.Authorization = new AuthenticationHeaderValue( + SasHeaderName, + sasToken.Substring(SasHeaderName.Length + 1)); } } } \ No newline at end of file