From 7a26eda629b6b1b564535d2476423ef7fad56dc6 Mon Sep 17 00:00:00 2001
From: dylanbulfinMS <95251881+dylanbulfinMS@users.noreply.github.com>
Date: Mon, 10 Jan 2022 17:21:11 -0500
Subject: [PATCH] Adding RBAC support for provisioning SDK (#2262)
* Added types for different credentials
* Altered code to work with new types
* Fixed Managers
* removed unneeded #if !NET451
* Filled in method summaries
* refactored GetHeaderProvider
* Added documentation
* Formatting fixes to address comments
---
.../src/Auth/ProvisioningSasCredential.cs | 27 +++
.../src/Auth/ProvisioningTokenCredential.cs | 43 +++++
provisioning/service/src/Auth/TokenHelper.cs | 21 +++
.../src/Manager/EnrollmentGroupManager.cs | 5 +-
.../Manager/IndividualEnrollmentManager.cs | 5 +-
.../src/Manager/RegistrationStatusManager.cs | 6 +-
....Azure.Devices.Provisioning.Service.csproj | 1 +
.../service/src/ProvisioningServiceClient.cs | 168 ++++++++++++++++--
provisioning/service/src/Query.cs | 65 +++++--
9 files changed, 298 insertions(+), 43 deletions(-)
create mode 100644 provisioning/service/src/Auth/ProvisioningSasCredential.cs
create mode 100644 provisioning/service/src/Auth/ProvisioningTokenCredential.cs
create mode 100644 provisioning/service/src/Auth/TokenHelper.cs
diff --git a/provisioning/service/src/Auth/ProvisioningSasCredential.cs b/provisioning/service/src/Auth/ProvisioningSasCredential.cs
new file mode 100644
index 0000000000..c003148f6c
--- /dev/null
+++ b/provisioning/service/src/Auth/ProvisioningSasCredential.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using Azure;
+using Microsoft.Azure.Devices.Common.Service.Auth;
+
+namespace Microsoft.Azure.Devices.Provisioning.Service.Auth
+{
+ ///
+ /// Allows authentication to the API using a Shared Access Key provided by custom implementation.
+ /// The PnP client is auto generated from swagger and needs to implement a specific class to pass to the protocol layer
+ /// unlike the rest of the clients which are hand-written. So, this implementation for authentication is specific to digital twin (PnP).
+ ///
+ internal class ProvisioningSasCredential: IAuthorizationHeaderProvider
+ {
+ private readonly AzureSasCredential _azureSasCredential;
+
+ public ProvisioningSasCredential(AzureSasCredential azureSasCredential)
+ {
+ _azureSasCredential = azureSasCredential;
+ }
+
+ public string GetAuthorizationHeader()
+ {
+ return _azureSasCredential.Signature;
+ }
+ }
+}
diff --git a/provisioning/service/src/Auth/ProvisioningTokenCredential.cs b/provisioning/service/src/Auth/ProvisioningTokenCredential.cs
new file mode 100644
index 0000000000..fe05a70ae6
--- /dev/null
+++ b/provisioning/service/src/Auth/ProvisioningTokenCredential.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Threading;
+using Azure.Core;
+using Microsoft.Azure.Devices.Common.Service.Auth;
+
+namespace Microsoft.Azure.Devices.Provisioning.Service.Auth
+{
+ ///
+ /// Allows authentication to the API using a JWT token generated for Azure active directory.
+ /// The PnP client is auto generated from swagger and needs to implement a specific class to pass to the protocol layer
+ /// unlike the rest of the clients which are hand-written. so, this implementation for authentication is specific to digital twin (PnP).
+ ///
+ internal class ProvisioningTokenCredential : IAuthorizationHeaderProvider
+ {
+ private readonly TokenCredential _credential;
+ private readonly object _tokenLock = new object();
+ private AccessToken? _cachedAccessToken;
+
+ public ProvisioningTokenCredential(TokenCredential credential)
+ {
+ _credential = credential;
+ }
+
+ // The HTTP protocol uses this method to get the bearer token for authentication.
+ public string GetAuthorizationHeader()
+ {
+ lock (_tokenLock)
+ {
+ // A new token is generated if it is the first time or the cached token is close to expiry.
+ if (!_cachedAccessToken.HasValue
+ || TokenHelper.IsCloseToExpiry(_cachedAccessToken.Value.ExpiresOn))
+ {
+ _cachedAccessToken = _credential.GetToken(
+ new TokenRequestContext(new string[] { "https://azure-devices-provisioning.net/.default" }),
+ new CancellationToken());
+ }
+ }
+
+ return $"Bearer {_cachedAccessToken.Value.Token}";
+ }
+ }
+}
diff --git a/provisioning/service/src/Auth/TokenHelper.cs b/provisioning/service/src/Auth/TokenHelper.cs
new file mode 100644
index 0000000000..f98c07eac3
--- /dev/null
+++ b/provisioning/service/src/Auth/TokenHelper.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
+
+namespace Microsoft.Azure.Devices.Provisioning.Service.Auth
+{
+ internal static class TokenHelper
+ {
+ ///
+ /// Determines if the given token expiry date time is close to expiry. The date and time is
+ /// considered close to expiry if it has less than 10 minutes relative to the current time.
+ ///
+ /// The token expiration date and time.
+ /// True if the token expiry has less than 10 minutes relative to the current time, otherwise false.
+ public static bool IsCloseToExpiry(DateTimeOffset expiry)
+ {
+ TimeSpan timeToExpiry = expiry - DateTimeOffset.UtcNow;
+ return timeToExpiry.TotalMinutes < 10;
+ }
+ }
+}
diff --git a/provisioning/service/src/Manager/EnrollmentGroupManager.cs b/provisioning/service/src/Manager/EnrollmentGroupManager.cs
index 9cddfe1fe7..08052420f1 100644
--- a/provisioning/service/src/Manager/EnrollmentGroupManager.cs
+++ b/provisioning/service/src/Manager/EnrollmentGroupManager.cs
@@ -109,7 +109,8 @@ await contractApiHttp.RequestAsync(
}
internal static Query CreateQuery(
- ServiceConnectionString provisioningConnectionString,
+ string hostName,
+ IAuthorizationHeaderProvider headerProvider,
QuerySpecification querySpecification,
HttpTransportSettings httpTransportSettings,
CancellationToken cancellationToken,
@@ -128,7 +129,7 @@ internal static Query CreateQuery(
/* SRS_ENROLLMENT_GROUP_MANAGER_28_015: [The CreateQuery shall return a new Query for EnrollmentGroup.] */
- return new Query(provisioningConnectionString, ServiceName, querySpecification, httpTransportSettings, pageSize, cancellationToken);
+ return new Query(hostName, headerProvider, ServiceName, querySpecification, httpTransportSettings, pageSize, cancellationToken);
}
private static Uri GetEnrollmentUri(string enrollmentGroupId)
diff --git a/provisioning/service/src/Manager/IndividualEnrollmentManager.cs b/provisioning/service/src/Manager/IndividualEnrollmentManager.cs
index c4c5d40c22..35f9bad5e3 100644
--- a/provisioning/service/src/Manager/IndividualEnrollmentManager.cs
+++ b/provisioning/service/src/Manager/IndividualEnrollmentManager.cs
@@ -150,7 +150,8 @@ await contractApiHttp.RequestAsync(
}
internal static Query CreateQuery(
- ServiceConnectionString provisioningConnectionString,
+ string hostName,
+ IAuthorizationHeaderProvider headerProvider,
QuerySpecification querySpecification,
HttpTransportSettings httpTransportSettings,
CancellationToken cancellationToken,
@@ -168,7 +169,7 @@ internal static Query CreateQuery(
}
/* SRS_INDIVIDUAL_ENROLLMENT_MANAGER_21_015: [The CreateQuery shall return a new Query for IndividualEnrollments.] */
- return new Query(provisioningConnectionString, ServiceName, querySpecification, httpTransportSettings, pageSize, cancellationToken);
+ return new Query(hostName, headerProvider, ServiceName, querySpecification, httpTransportSettings, pageSize, cancellationToken);
}
private static Uri GetEnrollmentUri(string registrationId)
diff --git a/provisioning/service/src/Manager/RegistrationStatusManager.cs b/provisioning/service/src/Manager/RegistrationStatusManager.cs
index cd786744ff..044b7128f2 100644
--- a/provisioning/service/src/Manager/RegistrationStatusManager.cs
+++ b/provisioning/service/src/Manager/RegistrationStatusManager.cs
@@ -82,7 +82,8 @@ await contractApiHttp.RequestAsync(
[SuppressMessage("Microsoft.Design", "CA1068",
Justification = "Public API cannot change parameter order.")]
internal static Query CreateEnrollmentGroupQuery(
- ServiceConnectionString provisioningConnectionString,
+ string hostName,
+ IAuthorizationHeaderProvider headerProvider,
QuerySpecification querySpecification,
HttpTransportSettings httpTransportSettings,
CancellationToken cancellationToken,
@@ -102,7 +103,8 @@ internal static Query CreateEnrollmentGroupQuery(
/* SRS_REGISTRATION_STATUS_MANAGER_28_010: [The CreateQuery shall return a new Query for DeviceRegistrationState.] */
return new Query(
- provisioningConnectionString,
+ hostName,
+ headerProvider,
GetGetDeviceRegistrationStatus(enrollmentGroupId),
querySpecification,
httpTransportSettings,
diff --git a/provisioning/service/src/Microsoft.Azure.Devices.Provisioning.Service.csproj b/provisioning/service/src/Microsoft.Azure.Devices.Provisioning.Service.csproj
index 9e25d16aba..d1fa2e37d0 100644
--- a/provisioning/service/src/Microsoft.Azure.Devices.Provisioning.Service.csproj
+++ b/provisioning/service/src/Microsoft.Azure.Devices.Provisioning.Service.csproj
@@ -56,6 +56,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/provisioning/service/src/ProvisioningServiceClient.cs b/provisioning/service/src/ProvisioningServiceClient.cs
index df4c53e24e..216627b733 100644
--- a/provisioning/service/src/ProvisioningServiceClient.cs
+++ b/provisioning/service/src/ProvisioningServiceClient.cs
@@ -5,7 +5,10 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Azure;
+using Azure.Core;
using Microsoft.Azure.Devices.Common.Service.Auth;
+using Microsoft.Azure.Devices.Provisioning.Service.Auth;
namespace Microsoft.Azure.Devices.Provisioning.Service
{
@@ -65,8 +68,12 @@ namespace Microsoft.Azure.Devices.Provisioning.Service
public class ProvisioningServiceClient : IDisposable
{
private readonly ServiceConnectionString _provisioningConnectionString;
+ private readonly ProvisioningTokenCredential _provisioningTokenCredential;
+ private readonly ProvisioningSasCredential _provisioningSasCredential;
private readonly IContractApiHttp _contractApiHttp;
+ private string _hostName;
+
///
/// Create a new instance of the ProvisioningServiceClient
that exposes
/// the API to the Device Provisioning Service.
@@ -104,6 +111,75 @@ public static ProvisioningServiceClient CreateFromConnectionString(string connec
return new ProvisioningServiceClient(connectionString, httpTransportSettings);
}
+ ///
+ /// Create a new instance of the ProvisioningServiceClient
from the provided hostName and TokenCredential
+ /// that exposes the API to the Device Provisioning Service.
+ ///
+ ///
+ /// The string
that carries the hostName that will be used for this object
+ /// The TokenCredential
that provides authentication for this object
+ /// The ProvisioningServiceClient
with the new instance of this object.
+ /// if the credential is null
+ public static ProvisioningServiceClient Create(string hostName, TokenCredential credential)
+ {
+ return ProvisioningServiceClient.Create(hostName, credential, new HttpTransportSettings());
+ }
+
+ ///
+ /// Create a new instance of the ProvisioningServiceClient
from the provided hostName and TokenCredential
+ /// that exposes the API to the Device Provisioning Service.
+ ///
+ ///
+ /// The string
that carries the hostName that will be used for this object
+ /// The TokenCredential
that provides authentication for this object
+ /// Specifies the HTTP transport settings for the request
+ /// The ProvisioningServiceClient
with the new instance of this object.
+ /// if the credential is null
+ public static ProvisioningServiceClient Create(string hostName, TokenCredential credential, HttpTransportSettings httpTransportSettings)
+ {
+ if (credential == null)
+ {
+ throw new ArgumentNullException(nameof(credential), "Parameter cannot be null");
+ }
+
+ return new ProvisioningServiceClient(hostName, credential, httpTransportSettings);
+ }
+
+ ///
+ /// Create a new instance of the ProvisioningServiceClient
from the provided hostName and TokenCredential
+ /// that exposes the API to the Device Provisioning Service.
+ ///
+ ///
+ /// The string
that carries the host name that will be used for this object
+ /// The AzureSasCredential
that provides authentication for this object
+ /// The ProvisioningServiceClient
with the new instance of this object.
+ /// if the azureSasCredential is null
+ public static ProvisioningServiceClient Create(string hostName, AzureSasCredential azureSasCredential)
+ {
+ return ProvisioningServiceClient.Create(hostName, azureSasCredential, new HttpTransportSettings());
+ }
+
+ ///
+ /// Create a new instance of the ProvisioningServiceClient
from the provided hostName and TokenCredential
+ /// that exposes the API to the Device Provisioning Service.
+ ///
+ ///
+ /// The string
that carries the host name that will be used for this object
+ /// The AzureSasCredential
that provides authentication for this object
+ /// Specifies the HTTP transport settings for the request
+ /// The ProvisioningServiceClient
with the new instance of this object.
+ /// if the azureSasCredential is null
+ public static ProvisioningServiceClient Create(string hostName, AzureSasCredential azureSasCredential, HttpTransportSettings httpTransportSettings)
+ {
+ if (azureSasCredential == null)
+ {
+ throw new ArgumentNullException(nameof(azureSasCredential), "Parameter cannot be null");
+ }
+
+ return new ProvisioningServiceClient(hostName, azureSasCredential, httpTransportSettings);
+ }
+
+
///
/// PRIVATE CONSTRUCTOR
///
@@ -121,12 +197,43 @@ private ProvisioningServiceClient(string connectionString, HttpTransportSettings
/* SRS_PROVISIONING_SERVICE_CLIENT_21_003: [The constructor shall throw ArgumentException if the ProvisioningConnectionString or one of the inner Managers failed to create a new instance.] */
/* SRS_PROVISIONING_SERVICE_CLIENT_21_004: [The constructor shall create a new instance of the ContractApiHttp class using the provided connectionString.] */
_provisioningConnectionString = ServiceConnectionString.Parse(connectionString);
+ _hostName = _provisioningConnectionString.HostName;
_contractApiHttp = new ContractApiHttp(
_provisioningConnectionString.HttpsEndpoint,
_provisioningConnectionString,
httpTransportSettings);
}
+ private ProvisioningServiceClient(string hostName, TokenCredential credential, HttpTransportSettings httpTransportSettings)
+ {
+ if (string.IsNullOrWhiteSpace(hostName ?? throw new ArgumentNullException(nameof(hostName))))
+ {
+ throw new ArgumentException($"{nameof(hostName)} cannot be empty string");
+ }
+
+ _provisioningTokenCredential = new ProvisioningTokenCredential(credential);
+ _hostName = hostName;
+ _contractApiHttp = new ContractApiHttp(
+ new UriBuilder("https", _hostName).Uri,
+ _provisioningTokenCredential,
+ httpTransportSettings);
+ }
+
+ private ProvisioningServiceClient(string hostName, AzureSasCredential azureSasCredential, HttpTransportSettings httpTransportSettings)
+ {
+ if (string.IsNullOrWhiteSpace(hostName ?? throw new ArgumentNullException(nameof(hostName))))
+ {
+ throw new ArgumentException($"{nameof(hostName)} cannot be empty string");
+ }
+
+ _provisioningSasCredential = new ProvisioningSasCredential(azureSasCredential);
+ _hostName = hostName;
+ _contractApiHttp = new ContractApiHttp(
+ new UriBuilder("https", _hostName).Uri,
+ _provisioningSasCredential,
+ httpTransportSettings);
+ }
+
///
/// Dispose the Provisioning Service Client and its dependencies.
///
@@ -472,7 +579,8 @@ public Query CreateIndividualEnrollmentQuery(QuerySpecification querySpecificati
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_014: [The CreateIndividualEnrollmentQuery shall create a new individual enrollment query by calling the CreateQuery in the IndividualEnrollmentManager.] */
return IndividualEnrollmentManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
CancellationToken.None);
@@ -496,7 +604,8 @@ public Query CreateIndividualEnrollmentQuery(QuerySpecification querySpecificati
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_014: [The CreateIndividualEnrollmentQuery shall create a new individual enrollment query by calling the CreateQuery in the IndividualEnrollmentManager.] */
return IndividualEnrollmentManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
httpTransportSettings,
CancellationToken.None);
@@ -520,7 +629,8 @@ public Query CreateIndividualEnrollmentQuery(QuerySpecification querySpecificati
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_014: [The CreateIndividualEnrollmentQuery shall create a new individual enrollment query by calling the CreateQuery in the IndividualEnrollmentManager.] */
return IndividualEnrollmentManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
cancellationToken);
@@ -548,7 +658,8 @@ public Query CreateIndividualEnrollmentQuery(QuerySpecification querySpecificati
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_015: [The CreateIndividualEnrollmentQuery shall create a new individual enrollment query by calling the CreateQuery in the IndividualEnrollmentManager.] */
return IndividualEnrollmentManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
CancellationToken.None,
@@ -578,7 +689,8 @@ public Query CreateIndividualEnrollmentQuery(QuerySpecification querySpecificati
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_015: [The CreateIndividualEnrollmentQuery shall create a new individual enrollment query by calling the CreateQuery in the IndividualEnrollmentManager.] */
return IndividualEnrollmentManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
cancellationToken,
@@ -608,7 +720,8 @@ public Query CreateIndividualEnrollmentQuery(QuerySpecification querySpecificati
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_015: [The CreateIndividualEnrollmentQuery shall create a new individual enrollment query by calling the CreateQuery in the IndividualEnrollmentManager.] */
return IndividualEnrollmentManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
httpTransportSettings,
CancellationToken.None,
@@ -855,7 +968,8 @@ public Query CreateEnrollmentGroupQuery(QuerySpecification querySpecification)
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_021: [The createEnrollmentGroupQuery shall create a new enrolmentGroup query by calling the createQuery in the EnrollmentGroupManager.] */
return EnrollmentGroupManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
CancellationToken.None);
@@ -879,7 +993,8 @@ public Query CreateEnrollmentGroupQuery(QuerySpecification querySpecification, H
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_021: [The createEnrollmentGroupQuery shall create a new enrolmentGroup query by calling the createQuery in the EnrollmentGroupManager.] */
return EnrollmentGroupManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
httpTransportSettings,
CancellationToken.None);
@@ -903,7 +1018,8 @@ public Query CreateEnrollmentGroupQuery(QuerySpecification querySpecification, C
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_021: [The createEnrollmentGroupQuery shall create a new enrolmentGroup query by calling the createQuery in the EnrollmentGroupManager.] */
return EnrollmentGroupManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
cancellationToken);
@@ -931,7 +1047,8 @@ public Query CreateEnrollmentGroupQuery(QuerySpecification querySpecification, i
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_022: [The createEnrollmentGroupQuery shall create a new enrolmentGroup query by calling the createQuery in the EnrollmentGroupManager.] */
return EnrollmentGroupManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
CancellationToken.None,
@@ -961,7 +1078,8 @@ public Query CreateEnrollmentGroupQuery(QuerySpecification querySpecification, i
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_022: [The createEnrollmentGroupQuery shall create a new enrolmentGroup query by calling the createQuery in the EnrollmentGroupManager.] */
return EnrollmentGroupManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
cancellationToken,
@@ -991,7 +1109,8 @@ public Query CreateEnrollmentGroupQuery(QuerySpecification querySpecification, i
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_022: [The createEnrollmentGroupQuery shall create a new enrolmentGroup query by calling the createQuery in the EnrollmentGroupManager.] */
return EnrollmentGroupManager.CreateQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
httpTransportSettings,
CancellationToken.None,
@@ -1178,7 +1297,8 @@ public Query CreateEnrollmentGroupRegistrationStateQuery(QuerySpecification quer
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_027: [The createEnrollmentGroupRegistrationStateQuery shall create a new DeviceRegistrationState query by calling the createQuery in the registrationStatusManager.] */
return RegistrationStatusManager.CreateEnrollmentGroupQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
CancellationToken.None,
@@ -1203,7 +1323,8 @@ public Query CreateEnrollmentGroupRegistrationStateQuery(QuerySpecification quer
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_027: [The createEnrollmentGroupRegistrationStateQuery shall create a new DeviceRegistrationState query by calling the createQuery in the registrationStatusManager.] */
return RegistrationStatusManager.CreateEnrollmentGroupQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
httpTransportSettings,
CancellationToken.None,
@@ -1231,7 +1352,8 @@ public Query CreateEnrollmentGroupRegistrationStateQuery(
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_027: [The createEnrollmentGroupRegistrationStateQuery shall create a new DeviceRegistrationState query by calling the createQuery in the registrationStatusManager.] */
return RegistrationStatusManager.CreateEnrollmentGroupQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
cancellationToken,
@@ -1261,7 +1383,8 @@ public Query CreateEnrollmentGroupRegistrationStateQuery(QuerySpecification quer
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_028: [The createEnrollmentGroupRegistrationStateQuery shall create a new DeviceRegistrationState query by calling the createQuery in the registrationStatusManager.] */
return RegistrationStatusManager.CreateEnrollmentGroupQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
CancellationToken.None,
@@ -1297,7 +1420,8 @@ public Query CreateEnrollmentGroupRegistrationStateQuery(
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_028: [The createEnrollmentGroupRegistrationStateQuery shall create a new DeviceRegistrationState query by calling the createQuery in the registrationStatusManager.] */
return RegistrationStatusManager.CreateEnrollmentGroupQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
httpTransportSettings,
CancellationToken.None,
@@ -1333,7 +1457,8 @@ public Query CreateEnrollmentGroupRegistrationStateQuery(
{
/* SRS_PROVISIONING_SERVICE_CLIENT_21_028: [The createEnrollmentGroupRegistrationStateQuery shall create a new DeviceRegistrationState query by calling the createQuery in the registrationStatusManager.] */
return RegistrationStatusManager.CreateEnrollmentGroupQuery(
- _provisioningConnectionString,
+ _hostName,
+ GetHeaderProvider(),
querySpecification,
new HttpTransportSettings(),
cancellationToken,
@@ -1368,5 +1493,12 @@ public Task GetEnrollmentGroupAttestationAsync(string enro
{
return EnrollmentGroupManager.GetEnrollmentAttestationAsync(_contractApiHttp, enrollmentGroupId, cancellationToken);
}
+
+ internal IAuthorizationHeaderProvider GetHeaderProvider()
+ {
+ return _provisioningConnectionString
+ ?? (IAuthorizationHeaderProvider) _provisioningTokenCredential
+ ?? _provisioningSasCredential;
+ }
}
}
diff --git a/provisioning/service/src/Query.cs b/provisioning/service/src/Query.cs
index 4b449710ab..183556ec06 100644
--- a/provisioning/service/src/Query.cs
+++ b/provisioning/service/src/Query.cs
@@ -75,50 +75,86 @@ internal Query(
int pageSize,
CancellationToken cancellationToken)
{
- /* SRS_QUERY_21_001: [The constructor shall throw ArgumentNullException if the provided serviceConnectionString is null.] */
if (serviceConnectionString == null)
{
throw new ArgumentNullException(nameof(serviceConnectionString));
}
- /* SRS_QUERY_21_002: [The constructor shall throw ArgumentException if the provided serviceName is null or empty.] */
if (string.IsNullOrWhiteSpace(serviceName ?? throw new ArgumentNullException(nameof(serviceName))))
{
throw new ArgumentException($"{nameof(serviceName)} cannot be an empty string");
}
- /* SRS_QUERY_21_003: [The constructor shall throw ArgumentException if the provided querySpecification is null.] */
if (querySpecification == null)
{
throw new ArgumentNullException(nameof(querySpecification));
}
- /* SRS_QUERY_21_004: [The constructor shall throw ArgumentException if the provided pageSize is negative.] */
if (pageSize < 0)
{
throw new ArgumentException($"{nameof(pageSize)} cannot be negative.");
}
// TODO: Refactor ContractApiHttp being created again
- /* SRS_QUERY_21_005: [The constructor shall create and store a `contractApiHttp` using the provided Service Connection String.] */
_contractApiHttp = new ContractApiHttp(
serviceConnectionString.HttpsEndpoint,
serviceConnectionString, httpTransportSettings);
- /* SRS_QUERY_21_006: [The constructor shall store the provided `pageSize`, and `cancelationToken`.] */
PageSize = pageSize;
_cancellationToken = cancellationToken;
- /* SRS_QUERY_21_007: [The constructor shall create and store a JSON from the provided querySpecification.] */
_querySpecificationJson = JsonConvert.SerializeObject(querySpecification);
- /* SRS_QUERY_21_008: [The constructor shall create and store a queryPath adding `/query` to the provided `targetPath`.] */
_queryPath = GetQueryUri(serviceName);
- /* SRS_QUERY_21_009: [The constructor shall set continuationToken and current as null.] */
ContinuationToken = null;
- /* SRS_QUERY_21_010: [The constructor shall set hasNext as true.] */
+ _hasNext = true;
+ }
+
+ internal Query(
+ string hostName,
+ IAuthorizationHeaderProvider headerProvider,
+ string serviceName,
+ QuerySpecification querySpecification,
+ HttpTransportSettings httpTransportSettings,
+ int pageSize,
+ CancellationToken cancellationToken)
+ {
+ if (hostName == null)
+ {
+ throw new ArgumentNullException(nameof(hostName));
+ }
+
+ if (string.IsNullOrWhiteSpace(serviceName ?? throw new ArgumentNullException(nameof(serviceName))))
+ {
+ throw new ArgumentException($"{nameof(serviceName)} cannot be an empty string");
+ }
+
+ if (querySpecification == null)
+ {
+ throw new ArgumentNullException(nameof(querySpecification));
+ }
+
+ if (pageSize < 0)
+ {
+ throw new ArgumentException($"{nameof(pageSize)} cannot be negative.");
+ }
+
+ // TODO: Refactor ContractApiHttp being created again
+ _contractApiHttp = new ContractApiHttp(
+ new UriBuilder("https", hostName).Uri,
+ headerProvider, httpTransportSettings);
+
+ PageSize = pageSize;
+ _cancellationToken = cancellationToken;
+
+ _querySpecificationJson = JsonConvert.SerializeObject(querySpecification);
+
+ _queryPath = GetQueryUri(serviceName);
+
+ ContinuationToken = null;
+
_hasNext = true;
}
@@ -155,17 +191,14 @@ public bool HasNext()
/// if the query does no have more pages to return.
public async Task NextAsync(string continuationToken)
{
- /* SRS_QUERY_21_011: [The next shall throw IndexOutOfRangeException if the provided continuationToken is null or empty.] */
if (string.IsNullOrWhiteSpace(continuationToken))
{
throw new IndexOutOfRangeException($"There is no {nameof(continuationToken)} to get pending elements.");
}
- /* SRS_QUERY_21_012: [The next shall store the provided continuationToken.] */
ContinuationToken = continuationToken;
_hasNext = true;
- /* SRS_QUERY_21_013: [The next shall return the next page of results by calling the next().] */
return await NextAsync().ConfigureAwait(false);
}
@@ -176,25 +209,21 @@ public async Task NextAsync(string continuationToken)
/// if the query does no have more pages to return.
public async Task NextAsync()
{
- /* SRS_QUERY_21_014: [The next shall throw IndexOutOfRangeException if the hasNext is false.] */
if (!_hasNext)
{
throw new IndexOutOfRangeException("There are no more pending elements");
}
- /* SRS_QUERY_21_015: [If the pageSize is not 0, the next shall send the HTTP request with `x-ms-max-item-count=[pageSize]` in the header.] */
IDictionary headerParameters = new Dictionary();
if (PageSize != 0)
{
headerParameters.Add(PageSizeHeaderKey, PageSize.ToString(CultureInfo.InvariantCulture));
}
- /* SRS_QUERY_21_016: [If the continuationToken is not null or empty, the next shall send the HTTP request with `x-ms-continuation=[continuationToken]` in the header.] */
if (!string.IsNullOrWhiteSpace(ContinuationToken))
{
headerParameters.Add(ContinuationTokenHeaderKey, ContinuationToken);
}
- /* SRS_QUERY_21_017: [The next shall send a HTTP request with a HTTP verb `POST`.] */
ContractApiResponse httpResponse = await _contractApiHttp.RequestAsync(
HttpMethod.Post,
_queryPath,
@@ -203,12 +232,10 @@ public async Task NextAsync()
null,
_cancellationToken).ConfigureAwait(false);
- /* SRS_QUERY_21_018: [The next shall create and return a new instance of the QueryResult using the `x-ms-item-type` as type, `x-ms-continuation` as the next continuationToken, and the message body.] */
httpResponse.Fields.TryGetValue(ItemTypeHeaderKey, out string type);
httpResponse.Fields.TryGetValue(ContinuationTokenHeaderKey, out string continuationToken);
ContinuationToken = continuationToken;
- /* SRS_QUERY_21_017: [The next shall set hasNext as true if the continuationToken is not null, or false if it is null.] */
_hasNext = (ContinuationToken != null);
var result = new QueryResult(type, httpResponse.Body, ContinuationToken);