Skip to content

Commit

Permalink
Fix/447 add scope id configuration to portal (#469)
Browse files Browse the repository at this point in the history
* Add ID scope to portal deployment template

* Read DPS Scope ID from Settings

* Add unit tests coverage to config handlers
  • Loading branch information
kbeaugrand committed Mar 22, 2022
1 parent bd16055 commit cd29c6a
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Server.Tests.Unit
{
using System;
using System.Globalization;
using System.Reflection;
using AzureIoTHub.Portal.Server;
using Microsoft.Extensions.Configuration;
using Moq;
using NUnit.Framework;

[TestFixture]
public class DevelopmentConfigHandlerTests
{
private MockRepository mockRepository;

private Mock<IConfiguration> mockConfiguration;

[SetUp]
public void SetUp()
{
this.mockRepository = new MockRepository(MockBehavior.Strict);

this.mockConfiguration = this.mockRepository.Create<IConfiguration>();
}

private DevelopmentConfigHandler CreateDevelopmentConfigHandler()
{
return new DevelopmentConfigHandler(this.mockConfiguration.Object);
}

[TestCase(ConfigHandler.PortalNameKey, nameof(ConfigHandler.PortalName))]
[TestCase(ConfigHandler.DPSServiceEndpointKey, nameof(ConfigHandler.DPSEndpoint))]
[TestCase(ConfigHandler.DPSIDScopeKey, nameof(ConfigHandler.DPSScopeID))]
[TestCase(ConfigHandler.OIDCScopeKey, nameof(ConfigHandler.OIDCScope))]
[TestCase(ConfigHandler.OIDCAuthorityKey, nameof(ConfigHandler.OIDCAuthority))]
[TestCase(ConfigHandler.OIDCMetadataUrlKey, nameof(ConfigHandler.OIDCMetadataUrl))]
[TestCase(ConfigHandler.OIDCClientIdKey, nameof(ConfigHandler.OIDCClientId))]
[TestCase(ConfigHandler.OIDCApiClientIdKey, nameof(ConfigHandler.OIDCApiClientId))]
[TestCase(ConfigHandler.StorageAccountBlobContainerNameKey, nameof(ConfigHandler.StorageAccountBlobContainerName))]
[TestCase(ConfigHandler.StorageAccountBlobContainerPartitionKeyKey, nameof(ConfigHandler.StorageAccountBlobContainerPartitionKey))]
[TestCase(ConfigHandler.LoRaKeyManagementUrlKey, nameof(ConfigHandler.LoRaKeyManagementUrl))]
[TestCase(ConfigHandler.LoRaKeyManagementCodeKey, nameof(ConfigHandler.LoRaKeyManagementCode))]
[TestCase(ConfigHandler.LoRaRegionRouterConfigUrlKey, nameof(ConfigHandler.LoRaRegionRouterConfigUrl))]
[TestCase(ConfigHandler.IoTHubConnectionStringKey, nameof(ConfigHandler.IoTHubConnectionString))]
[TestCase(ConfigHandler.DPSConnectionStringKey, nameof(ConfigHandler.DPSConnectionString))]
[TestCase(ConfigHandler.StorageAccountConnectionStringKey, nameof(ConfigHandler.StorageAccountConnectionString))]
public void SettingsShouldGetValueFromAppSettings(string configKey, string configPropertyName)
{
// Arrange
var expected = Guid.NewGuid().ToString();
var configHandler = this.CreateDevelopmentConfigHandler();

_ = this.mockConfiguration.SetupGet(c => c[It.Is<string>(x => x == configKey)])
.Returns(expected);

// Act
var result = configHandler
.GetType()
.GetProperty(configPropertyName, BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(configHandler, null);

// Assert
Assert.AreEqual(expected, result);
this.mockRepository.VerifyAll();
}

[TestCase(ConfigHandler.IsLoRaFeatureEnabledKey, nameof(ConfigHandler.IsLoRaEnabled))]
public void SettingsShouldGetBoolFromAppSettings(string configKey, string configPropertyName)
{
// Arrange
var expected = false;
var productionConfigHandler = this.CreateDevelopmentConfigHandler();

_ = this.mockConfiguration.SetupGet(c => c[It.Is<string>(x => x == configKey)])
.Returns(Convert.ToString(expected, CultureInfo.InvariantCulture));

// Act
var result = productionConfigHandler
.GetType()
.GetProperty(configPropertyName, BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(productionConfigHandler, null);

// Assert
Assert.AreEqual(expected, result);

// Arrange
expected = true;

_ = this.mockConfiguration.SetupGet(c => c[It.Is<string>(x => x == configKey)])
.Returns(Convert.ToString(expected, CultureInfo.InvariantCulture));

// Act
result = productionConfigHandler
.GetType()
.GetProperty(configPropertyName, BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(productionConfigHandler, null);

// Assert
Assert.AreEqual(expected, result);
this.mockRepository.VerifyAll();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Server.Tests.Unit
{
using System;
using System.Globalization;
using System.Reflection;
using AzureIoTHub.Portal.Server;
using Microsoft.Extensions.Configuration;
using Moq;
using NUnit.Framework;

[TestFixture]
public class ProductionConfigHandlerTests
{
private MockRepository mockRepository;

private Mock<IConfiguration> mockConfiguration;

[SetUp]
public void SetUp()
{
this.mockRepository = new MockRepository(MockBehavior.Strict);

this.mockConfiguration = this.mockRepository.Create<IConfiguration>();
}

private ProductionConfigHandler CreateProductionConfigHandler()
{
return new ProductionConfigHandler(this.mockConfiguration.Object);
}

[TestCase(ConfigHandler.IoTHubConnectionStringKey, nameof(ConfigHandler.IoTHubConnectionString))]
[TestCase(ConfigHandler.DPSConnectionStringKey, nameof(ConfigHandler.DPSConnectionString))]
[TestCase(ConfigHandler.StorageAccountConnectionStringKey, nameof(ConfigHandler.StorageAccountConnectionString))]
[TestCase(ConfigHandler.LoRaKeyManagementCodeKey, nameof(ConfigHandler.LoRaKeyManagementCode))]
public void SecretsShouldGetValueFromConnectionStrings(string configKey, string configPropertyName)
{
// Arrange
var expected = Guid.NewGuid().ToString();
var productionConfigHandler = this.CreateProductionConfigHandler();
var mockConfigurationSection = this.mockRepository.Create<IConfigurationSection>();

_ = this.mockConfiguration.Setup(c => c.GetSection(It.Is<string>(x => x == "ConnectionStrings")))
.Returns(mockConfigurationSection.Object);

_ = mockConfigurationSection.SetupGet(c => c[It.Is<string>(x => x == configKey)])
.Returns(expected);

// Act
var result = productionConfigHandler
.GetType()
.GetProperty(configPropertyName, BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(productionConfigHandler, null);

// Assert
Assert.AreEqual(expected, result);
this.mockRepository.VerifyAll();
}

[TestCase(ConfigHandler.PortalNameKey, nameof(ConfigHandler.PortalName))]
[TestCase(ConfigHandler.DPSServiceEndpointKey, nameof(ConfigHandler.DPSEndpoint))]
[TestCase(ConfigHandler.DPSIDScopeKey, nameof(ConfigHandler.DPSScopeID))]
[TestCase(ConfigHandler.OIDCScopeKey, nameof(ConfigHandler.OIDCScope))]
[TestCase(ConfigHandler.OIDCAuthorityKey, nameof(ConfigHandler.OIDCAuthority))]
[TestCase(ConfigHandler.OIDCMetadataUrlKey, nameof(ConfigHandler.OIDCMetadataUrl))]
[TestCase(ConfigHandler.OIDCClientIdKey, nameof(ConfigHandler.OIDCClientId))]
[TestCase(ConfigHandler.OIDCApiClientIdKey, nameof(ConfigHandler.OIDCApiClientId))]
[TestCase(ConfigHandler.StorageAccountBlobContainerNameKey, nameof(ConfigHandler.StorageAccountBlobContainerName))]
[TestCase(ConfigHandler.StorageAccountBlobContainerPartitionKeyKey, nameof(ConfigHandler.StorageAccountBlobContainerPartitionKey))]
[TestCase(ConfigHandler.LoRaKeyManagementUrlKey, nameof(ConfigHandler.LoRaKeyManagementUrl))]
[TestCase(ConfigHandler.LoRaRegionRouterConfigUrlKey, nameof(ConfigHandler.LoRaRegionRouterConfigUrl))]
public void SettingsShouldGetValueFromAppSettings(string configKey, string configPropertyName)
{
// Arrange
var expected = Guid.NewGuid().ToString();
var productionConfigHandler = this.CreateProductionConfigHandler();

_ = this.mockConfiguration.SetupGet(c => c[It.Is<string>(x => x == configKey)])
.Returns(expected);

// Act
var result = productionConfigHandler
.GetType()
.GetProperty(configPropertyName, BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(productionConfigHandler, null);

// Assert
Assert.AreEqual(expected, result);
this.mockRepository.VerifyAll();
}

[TestCase(ConfigHandler.IsLoRaFeatureEnabledKey, nameof(ConfigHandler.IsLoRaEnabled))]
public void SettingsShouldGetBoolFromAppSettings(string configKey, string configPropertyName)
{
// Arrange
var expected = false;
var productionConfigHandler = this.CreateProductionConfigHandler();

_ = this.mockConfiguration.SetupGet(c => c[It.Is<string>(x => x == configKey)])
.Returns(Convert.ToString(expected, CultureInfo.InvariantCulture));

// Act
var result = productionConfigHandler
.GetType()
.GetProperty(configPropertyName, BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(productionConfigHandler, null);

// Assert
Assert.AreEqual(expected, result);

// Arrange
expected = true;

_ = this.mockConfiguration.SetupGet(c => c[It.Is<string>(x => x == configKey)])
.Returns(Convert.ToString(expected, CultureInfo.InvariantCulture));

// Act
result = productionConfigHandler
.GetType()
.GetProperty(configPropertyName, BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(productionConfigHandler, null);

// Assert
Assert.AreEqual(expected, result);
this.mockRepository.VerifyAll();
}
}
}
35 changes: 19 additions & 16 deletions src/AzureIoTHub.Portal/Server/ConfigHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,27 @@ namespace AzureIoTHub.Portal.Server

public abstract class ConfigHandler
{
protected const string PortalNameKey = "SiteName";
protected const string IoTHubConnectionStringKey = "IoTHub:ConnectionString";
protected const string DPSConnectionStringKey = "IoTDPS:ConnectionString";
protected const string DPSServiceEndpointKey = "IoTDPS:ServiceEndpoint";
internal const string PortalNameKey = "SiteName";
internal const string IoTHubConnectionStringKey = "IoTHub:ConnectionString";
internal const string DPSConnectionStringKey = "IoTDPS:ConnectionString";
internal const string DPSServiceEndpointKey = "IoTDPS:ServiceEndpoint";
internal const string DPSIDScopeKey = "IoTDPS:IDScope";

protected const string OIDCScopeKey = "OIDC:Scope";
protected const string OIDCAuthorityKey = "OIDC:Authority";
protected const string OIDCMetadataUrlKey = "OIDC:MetadataUrl";
protected const string OIDCClientIdKey = "OIDC:ClientId";
protected const string OIDCApiClientIdKey = "OIDC:ApiClientId";
internal const string OIDCScopeKey = "OIDC:Scope";
internal const string OIDCAuthorityKey = "OIDC:Authority";
internal const string OIDCMetadataUrlKey = "OIDC:MetadataUrl";
internal const string OIDCClientIdKey = "OIDC:ClientId";
internal const string OIDCApiClientIdKey = "OIDC:ApiClientId";

protected const string IsLoRaFeatureEnabledKey = "LoRaFeature:Enabled";
internal const string IsLoRaFeatureEnabledKey = "LoRaFeature:Enabled";

protected const string StorageAccountConnectionStringKey = "StorageAccount:ConnectionString";
protected const string StorageAccountBlobContainerNameKey = "StorageAccount:BlobContainerName";
protected const string StorageAccountBlobContainerPartitionKeyKey = "StorageAccount:BlobContainerPartitionKey";
internal const string StorageAccountConnectionStringKey = "StorageAccount:ConnectionString";
internal const string StorageAccountBlobContainerNameKey = "StorageAccount:BlobContainerName";
internal const string StorageAccountBlobContainerPartitionKeyKey = "StorageAccount:BlobContainerPartitionKey";

protected const string LoRaKeyManagementUrlKey = "LoRaKeyManagement:Url";
protected const string LoRaKeyManagementCodeKey = "LoRaKeyManagement:Code";
protected const string LoRaRegionRouterConfigUrlKey = "LoRaRegionRouterConfig:Url";
internal const string LoRaKeyManagementUrlKey = "LoRaKeyManagement:Url";
internal const string LoRaKeyManagementCodeKey = "LoRaKeyManagement:Code";
internal const string LoRaRegionRouterConfigUrlKey = "LoRaRegionRouterConfig:Url";

internal static ConfigHandler Create(IWebHostEnvironment env, IConfiguration config)
{
Expand All @@ -50,6 +51,8 @@ internal static ConfigHandler Create(IWebHostEnvironment env, IConfiguration con

internal abstract string DPSEndpoint { get; }

internal abstract string DPSScopeID { get; }

internal abstract string StorageAccountConnectionString { get; }

internal abstract string OIDCScope { get; }
Expand Down
2 changes: 2 additions & 0 deletions src/AzureIoTHub.Portal/Server/DevelopmentConfigHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal DevelopmentConfigHandler(IConfiguration config)

internal override string DPSEndpoint => this.config[DPSServiceEndpointKey];

internal override string DPSScopeID => this.config[DPSIDScopeKey];

internal override string StorageAccountConnectionString => this.config[StorageAccountConnectionStringKey];

internal override string OIDCScope => this.config[OIDCScopeKey];
Expand Down
2 changes: 2 additions & 0 deletions src/AzureIoTHub.Portal/Server/ProductionConfigHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal ProductionConfigHandler(IConfiguration config)

internal override string DPSEndpoint => this.config[DPSServiceEndpointKey];

internal override string DPSScopeID => this.config[DPSIDScopeKey];

internal override string StorageAccountConnectionString => this.config.GetConnectionString(StorageAccountConnectionStringKey);

internal override string OIDCScope => this.config[OIDCScopeKey];
Expand Down
4 changes: 4 additions & 0 deletions templates/portalDeploy.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@
"name": "IoTDPS__ServiceEndpoint",
"value": "[concat(variables('dpsName'), '.azure-devices-provisioning.net')]"
},
{
"name": "IoTDPS__IDScope",
"value": "[reference(variables('dpsName')).idScope]"
},
{
"name": "LoRaKeyManagement__Url",
"value": "[concat('https://', variables('functionAppName'), '.azurewebsites.net')]"
Expand Down

0 comments on commit cd29c6a

Please sign in to comment.