From e0b4f97e4e4afe0f00ba08b047287ee459d8becb Mon Sep 17 00:00:00 2001 From: Stijn Moreels <9039753+stijnmoreels@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:52:58 +0200 Subject: [PATCH] chore: deploy test resources w/ new test packages (#433) * chore: deploy test resources * pr-fix: missing assignment keyword * pr-fix: correct rg assignment * pr-fix: key vault name assignment * pr-fix: add location param + correct existing service principal condition * pr-fix: add depends on * pr-fix: use correct way to retrieve secret version * pr-fix: add logging for sp-related commands * pr-fix: use solely security-related resources * pr-fix: add logging for secret version * pr-fix: add secret version other way * pr-fix: use property outputs * pr-fix: add logging for deploy outputs * pr-fix: correct new output variable assignment * pr-fix: mark as string * pr-add: integrate tests and key vault * pr-fix: hashicorp template path * pr-fix: add devops resources * pr-fix: add test variables * pr-fix: az identity vulnerability * pr-fix: correct secret value * pr-fix: run secret retrieval as a pre-job * pr-fix: get own key vault secrets * pr-fix: correct parameters * pr-fix: install module az.keyvault * pr-fix: use az keyvault * pr-fix: use client id as var * pr-fix: remove app insights reference * pr-fix: add infra smoke tests * pr-fix: use az cli task * pr-fix: argument syntax * pr-fix: add enabled assertion * pr-fix: correct running * pr-fix: add az module * pr-fix: use new arguments syntax * pr-fix: use higher version of pester * pr-fix: use at least 5.3.0 * pr-fix: use env variables * pr-fix: correct test result * pr-fix: import module * pr-fix: remove param * pr-fix: enable test result * pr-fix: correct env vars * pr-fix: use other env vars * pr-fix: use correct secret version extraction * pr-fix: use pester container for external data * pr-fix: broaden test assertion + trim secret version setup * pr-fix: simplify config value retrieval * pr-fix: remove any spaces from version and secret * pr-fix: use direct setting of variable * pr-fix: clean tests * pr-fix: remove tried smoke tests * Update Arcus.Security.Providers.AzureKeyVault.csproj * Update Arcus.Security.Providers.AzureKeyVault.csproj * pr-fix: use most recent test fixtures * pr-fix: use correct unauthorized secret names * pr-fix: remove remote resource group * Update deploy-test-resources.yml * Update deploy-test-resources.yml * Update deploy-test-resources.yml --- build/ci-build.yml | 13 +- build/deploy-test-resources.yml | 54 ++++++ build/nuget-release.yml | 7 - build/templates/deploy-test-resources.bicep | 56 ++++++ build/templates/run-integration-tests.yml | 33 ++++ build/variables/test.yml | 11 +- ...us.Security.Providers.AzureKeyVault.csproj | 4 +- .../Arcus.Security.Tests.Core.csproj | 8 +- .../Stubs/SpyLogger.cs | 45 ----- .../Stubs/TestLoggerProvider.cs | 36 ---- .../Arcus.Security.Tests.Integration.csproj | 1 + .../AzureFunctions/SecretStoreBuilderTests.cs | 16 +- .../Fixture/TestConfig.cs | 165 ------------------ .../Fixture/TestConfigExtensions.cs | 121 +++++++++++++ .../Fixture/XunitTestLogSink.cs | 37 ---- .../HashiCorp/HashiCorpSecretProviderTests.cs | 75 +++----- .../Hosting/HashiCorpVaultTestServer.cs | 4 +- .../SecretStoreBuilderExtensionsTests.cs | 41 +---- .../IntegrationTest.cs | 29 ++- .../TemporaryManagedIdentityConnection.cs | 36 ++++ ...aultSecretProvider.ManagedIdentityTests.cs | 78 +++------ ...ultSecretProvider.ServicePrincipalTests.cs | 12 +- .../KeyVault/KeyVaultSecretProviderTests.cs | 21 ++- .../appsettings.json | 26 +-- .../Extensions/IHostBuilderExtensionsTests.cs | 9 +- .../IServiceCollectionExtensionsTests.cs | 8 +- 26 files changed, 433 insertions(+), 513 deletions(-) create mode 100644 build/deploy-test-resources.yml create mode 100644 build/templates/deploy-test-resources.bicep delete mode 100644 src/Arcus.Security.Tests.Core/Stubs/SpyLogger.cs delete mode 100644 src/Arcus.Security.Tests.Core/Stubs/TestLoggerProvider.cs delete mode 100644 src/Arcus.Security.Tests.Integration/Fixture/TestConfig.cs create mode 100644 src/Arcus.Security.Tests.Integration/Fixture/TestConfigExtensions.cs delete mode 100644 src/Arcus.Security.Tests.Integration/Fixture/XunitTestLogSink.cs create mode 100644 src/Arcus.Security.Tests.Integration/KeyVault/Fixture/TemporaryManagedIdentityConnection.cs diff --git a/build/ci-build.yml b/build/ci-build.yml index 266e1937..a551999b 100644 --- a/build/ci-build.yml +++ b/build/ci-build.yml @@ -18,6 +18,10 @@ parameters: - name: 'Package.Version.ManualTrigger' type: string default: 'preview' + - name: azureServiceConnection + displayName: 'Azure service connection' + type: string + default: 'Azure Codit-Arcus Service Principal' resources: repositories: @@ -27,9 +31,6 @@ resources: endpoint: arcus-azure variables: - # 'Arcus_ServicePrincipal_AccessKey' is added as secret on build in Azure DevOps - - group: 'Arcus Security - Integration Testing' - - group: 'Arcus - GitHub Package Registry' - group: 'Build Configuration' - template: ./variables/build.yml - template: ./variables/test.yml @@ -106,14 +107,10 @@ stages: inputs: artifact: 'Build' path: '$(Build.SourcesDirectory)' - - template: 'templates/download-hashicorp-vault.yml' - parameters: - targetFolder: '$(Build.SourcesDirectory)' - version: $(HashiCorp.Vault.Version) - vaultBinVariableName: 'Arcus.HashiCorp.VaultBin' - template: templates/run-integration-tests.yml parameters: dockerProjectName: '$(Project).Tests.Runtimes.AzureFunctions' + azureServiceConnection: '${{ parameters.azureServiceConnection }}' - stage: ReleaseToMyget displayName: 'Release to MyGet' diff --git a/build/deploy-test-resources.yml b/build/deploy-test-resources.yml new file mode 100644 index 00000000..89584bb6 --- /dev/null +++ b/build/deploy-test-resources.yml @@ -0,0 +1,54 @@ +name: Arcus Security - Deploy test resources + +trigger: none +pr: none + +parameters: + - name: azureServiceConnection + displayName: 'Azure service connection' + type: string + default: 'Azure Codit-Arcus Service Principal' + - name: resourceGroupName + displayName: 'Resource group name' + default: arcus-security-dev-we-rg + +variables: + - template: ./variables/build.yml + - template: ./variables/test.yml + +resources: + repositories: + - repository: templates + type: github + name: arcus-azure/azure-devops-templates + endpoint: arcus-azure + +stages: + - stage: Deploy + jobs: + - job: DeployBicep + displayName: 'Deploy test resources' + pool: + vmImage: '$(Vm.Image)' + steps: + - task: AzureCLI@2 + inputs: + azureSubscription: '${{ parameters.azureServiceConnection }}' + addSpnToEnvironment: true + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + $secretName = $env:ARCUS_SECURITY_KEYVAULT_TESTSECRETNAME + $secretValue = [System.Guid]::NewGuid().ToString() + $objectId = (az ad sp show --id $env:servicePrincipalId | ConvertFrom-Json).id + + az deployment sub create ` + --location westeurope ` + --template-file ./build/templates/deploy-test-resources.bicep ` + --parameters location=westeurope ` + --parameters resourceGroupName=${{ parameters.resourceGroupName }} ` + --parameters keyVaultName=$env:ARCUS_SECURITY_KEYVAULT_NAME ` + --parameters secretName=$secretName ` + --parameters secretValue=$secretValue ` + --parameters servicePrincipal_objectId=$objectId ` + | ConvertFrom-Json diff --git a/build/nuget-release.yml b/build/nuget-release.yml index c3d656a9..164c041f 100644 --- a/build/nuget-release.yml +++ b/build/nuget-release.yml @@ -15,8 +15,6 @@ resources: endpoint: arcus-azure variables: - - group: 'Arcus Security - Integration Testing' - - group: 'Arcus - GitHub Package Registry' - group: 'Build Configuration' - template: ./variables/build.yml - template: ./variables/test.yml @@ -92,11 +90,6 @@ stages: inputs: artifact: 'Build' path: '$(Build.SourcesDirectory)' - - template: 'templates/download-hashicorp-vault.yml' - parameters: - targetFolder: '$(Build.SourcesDirectory)' - version: $(HashiCorp.Vault.Version) - vaultBinVariableName: 'Arcus.HashiCorp.VaultBin' - template: templates/run-integration-tests.yml parameters: dockerProjectName: '$(Project).Tests.Runtimes.AzureFunctions' diff --git a/build/templates/deploy-test-resources.bicep b/build/templates/deploy-test-resources.bicep new file mode 100644 index 00000000..dbc56a58 --- /dev/null +++ b/build/templates/deploy-test-resources.bicep @@ -0,0 +1,56 @@ +// Define the location for the deployment of the components. +param location string + +// Define the name of the resource group where the components will be deployed. +param resourceGroupName string + +// Define the name of the Key vault. +param keyVaultName string + +// Define the name of the secret that will be added to the Key vault. +param secretName string + +// Define the secret value that will be by default added to the Key vault. +@secure() +param secretValue string + +// Define the Service Principal ID that needs access full access to the deployed resource group. +param servicePrincipal_objectId string + +targetScope='subscription' + +module resourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' = { + name: 'resourceGroupDeployment' + params: { + name: resourceGroupName + location: location + } +} + +resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' existing = { + name: resourceGroupName +} + +module vault 'br/public:avm/res/key-vault/vault:0.6.1' = { + name: 'vaultDeployment' + dependsOn: [ + resourceGroup + ] + scope: rg + params: { + name: keyVaultName + location: location + roleAssignments: [ + { + principalId: servicePrincipal_objectId + roleDefinitionIdOrName: 'Key Vault Secrets officer' + } + ] + secrets: [ + { + name: secretName + value: secretValue + } + ] + } +} diff --git a/build/templates/run-integration-tests.yml b/build/templates/run-integration-tests.yml index b12ba655..01225d55 100644 --- a/build/templates/run-integration-tests.yml +++ b/build/templates/run-integration-tests.yml @@ -1,5 +1,6 @@ parameters: dockerProjectName: '' + azureServiceConnection: '' steps: - bash: | @@ -9,6 +10,38 @@ steps: fi env: PROJECT_NAME: ${{ parameters.dockerProjectName }} + - task: AzureCLI@2 + displayName: 'Import secrets from Azure Key Vault' + inputs: + azureSubscription: '${{ parameters.azureServiceConnection }}' + addSpnToEnvironment: true + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + Install-Module -Name Arcus.Scripting.DevOps -AllowClobber + + Set-AzDevOpsVariable 'Arcus.Security.TenantId' -Value $env:tenantId -AsSecret + Set-AzDevOpsVariable 'Arcus.Security.ServicePrincipal.ClientId' -Value $env:servicePrincipalId -AsSecret + Set-AzDevOpsVariable 'Arcus.Security.ServicePrincipal.ClientSecret' -Value $env:servicePrincipalKey -AsSecret + + $unauthorizedClientId = az keyvault secret show --name $env:ARCUS_GENERAL_UNAUTHORIZED_SERVICEPRINCIPAL_CLIENTID_SECRETNAME --vault-name $env:ARCUS_GENERAL_KEYVAULT_NAME | ConvertFrom-Json + $unauthorizedClientSecret = az keyvault secret show --name $env:ARCUS_GENERAL_UNAUTHORIZED_SERVICEPRINCIPAL_CLIENTSECRET_SECRETNAME --vault-name $env:ARCUS_GENERAL_KEYVAULT_NAME | ConvertFrom-Json + Set-AzDevOpsVariable 'Arcus.Security.Unauthorized.ServicePrincipal.ClientId' -Value $unauthorizedClientId.value -AsSecret + Set-AzDevOpsVariable 'Arcus.Security.Unauthorized.ServicePrincipal.ClientSecret' -Value $unauthorizedClientSecret.value -AsSecret + + $testSecret = az keyvault secret show --name $env:ARCUS_SECURITY_KEYVAULT_TESTSECRETNAME --vault-name $env:ARCUS_SECURITY_KEYVAULT_NAME | ConvertFrom-Json + $testSecretVersion = $testSecret.id.Split('/') | Select-Object -Last 1 + Write-Host "Test secret '$($testSecret.name)' version is '$testSecretVersion'" + $testSecretValue = $testSecret.value + + Set-AzDevOpsVariable -AsSecret -Name 'Arcus.Security.KeyVault.TestSecretValue' -Value $testSecretValue + Set-AzDevOpsVariable -AsSecret -Name 'Arcus.Security.KeyVault.TestSecretVersion' -Value $testSecretVersion + - template: 'download-hashicorp-vault.yml' + parameters: + targetFolder: '$(Build.SourcesDirectory)' + version: $(HashiCorp.Vault.Version) + vaultBinVariableName: 'Arcus.HashiCorp.VaultBin' - task: UseDotNet@2 displayName: 'Import .NET Core SDK ($(DotNet.Sdk.VersionBC))' inputs: diff --git a/build/variables/test.yml b/build/variables/test.yml index 77b73775..d6920bcb 100644 --- a/build/variables/test.yml +++ b/build/variables/test.yml @@ -1,5 +1,8 @@ variables: - Arcus.KeyVault.TestKeyName: "ArcusTestSecret" - Arcus.KeyVault.TestKeyVersion: "8bde7a16366849e28b7abe26732e12e3" - HashiCorp.Vault.Version: 1.5.0 - Arcus.AzureFunctions.HttpPort: "5000" \ No newline at end of file + Arcus.Security.KeyVault.Name: 'arcus-security-kv' + Arcus.Security.KeyVault.TestSecretName: 'ArcusTestSecret' + Arcus.General.KeyVault.Name: 'arcus-kv' + Arcus.General.Unauthorized.ServicePrincipal.ClientId.SecretName: 'Arcus-Unauthorized-ServicePrincipal-ClientId' + Arcus.General.Unauthorized.ServicePrincipal.ClientSecret.SecretName: 'Arcus-Unauthorized-ServicePrincipal-ClientSecret' + Arcus.AzureFunctions.HttpPort: '5000' + HashiCorp.Vault.Version: 1.5.0 \ No newline at end of file diff --git a/src/Arcus.Security.Providers.AzureKeyVault/Arcus.Security.Providers.AzureKeyVault.csproj b/src/Arcus.Security.Providers.AzureKeyVault/Arcus.Security.Providers.AzureKeyVault.csproj index 3cc88b84..8f216d5d 100644 --- a/src/Arcus.Security.Providers.AzureKeyVault/Arcus.Security.Providers.AzureKeyVault.csproj +++ b/src/Arcus.Security.Providers.AzureKeyVault/Arcus.Security.Providers.AzureKeyVault.csproj @@ -24,7 +24,7 @@ - + @@ -34,4 +34,4 @@ - \ No newline at end of file + diff --git a/src/Arcus.Security.Tests.Core/Arcus.Security.Tests.Core.csproj b/src/Arcus.Security.Tests.Core/Arcus.Security.Tests.Core.csproj index 05a4be2f..e47afac1 100644 --- a/src/Arcus.Security.Tests.Core/Arcus.Security.Tests.Core.csproj +++ b/src/Arcus.Security.Tests.Core/Arcus.Security.Tests.Core.csproj @@ -1,13 +1,13 @@ - netstandard2.1 + net6.0;net8.0 - - - + + + diff --git a/src/Arcus.Security.Tests.Core/Stubs/SpyLogger.cs b/src/Arcus.Security.Tests.Core/Stubs/SpyLogger.cs deleted file mode 100644 index c46761ae..00000000 --- a/src/Arcus.Security.Tests.Core/Stubs/SpyLogger.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; - -namespace Arcus.Security.Tests.Core.Stubs -{ - /// - /// Represents an instance that spies on the send log messages. - /// - public class SpyLogger : ILogger - { - /// - /// Gets the flag indicating the logger instance was called. - /// - public bool IsCalled { get; private set; } - - /// Writes a log entry. - /// Entry will be written on this level. - /// Id of the event. - /// The entry to be written. Can be also an object. - /// The exception related to this entry. - /// Function to create a string message of the and . - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - IsCalled = true; - } - - /// - /// Checks if the given is enabled. - /// - /// level to be checked. - /// true if enabled. - public bool IsEnabled(LogLevel logLevel) - { - return true; - } - - /// Begins a logical operation scope. - /// The identifier for the scope. - /// An IDisposable that ends the logical operation scope on dispose. - public IDisposable BeginScope(TState state) - { - return null; - } - } -} diff --git a/src/Arcus.Security.Tests.Core/Stubs/TestLoggerProvider.cs b/src/Arcus.Security.Tests.Core/Stubs/TestLoggerProvider.cs deleted file mode 100644 index bdf0728e..00000000 --- a/src/Arcus.Security.Tests.Core/Stubs/TestLoggerProvider.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Arcus.Security.Tests.Core.Stubs -{ - /// - /// Represents an that delegates the logger creation to an fixed instance. - /// - public class TestLoggerProvider : ILoggerProvider - { - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - public TestLoggerProvider(ILogger logger) - { - _logger = logger; - } - - /// - /// Creates a new instance. - /// - /// The category name for messages produced by the logger. - public ILogger CreateLogger(string categoryName) - { - return _logger; - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - } - } -} diff --git a/src/Arcus.Security.Tests.Integration/Arcus.Security.Tests.Integration.csproj b/src/Arcus.Security.Tests.Integration/Arcus.Security.Tests.Integration.csproj index f2b97b96..8c8e9eca 100644 --- a/src/Arcus.Security.Tests.Integration/Arcus.Security.Tests.Integration.csproj +++ b/src/Arcus.Security.Tests.Integration/Arcus.Security.Tests.Integration.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Arcus.Security.Tests.Integration/AzureFunctions/SecretStoreBuilderTests.cs b/src/Arcus.Security.Tests.Integration/AzureFunctions/SecretStoreBuilderTests.cs index 1d6a79aa..30d8a48d 100644 --- a/src/Arcus.Security.Tests.Integration/AzureFunctions/SecretStoreBuilderTests.cs +++ b/src/Arcus.Security.Tests.Integration/AzureFunctions/SecretStoreBuilderTests.cs @@ -1,7 +1,5 @@ using System.Net.Http; using System.Threading.Tasks; -using Arcus.Security.Tests.Integration.Fixture; -using Arcus.Testing.Logging; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Xunit; @@ -10,34 +8,30 @@ namespace Arcus.Security.Tests.Integration.AzureFunctions { [Collection("Azure Functions")] - public class SecretStoreBuilderTests + public class SecretStoreBuilderTests : IntegrationTest { private readonly string _defaultRoute; - private readonly ILogger _logger; private static readonly HttpClient HttpClient = new HttpClient(); /// /// Initializes a new instance of the class. /// - public SecretStoreBuilderTests(ITestOutputHelper outputWriter) + public SecretStoreBuilderTests(ITestOutputHelper outputWriter) : base(outputWriter) { - var config = TestConfig.Create(); - var httpPort = config.GetValue("Arcus:AzureFunctions:HttpPort"); + var httpPort = Configuration.GetValue("Arcus:AzureFunctions:HttpPort"); _defaultRoute = $"http://localhost:{httpPort}/api/order"; - - _logger = new XunitTestLogger(outputWriter); } [Fact] public async Task ConfigureSecretStore_WithConfiguration_ReturnsConfigurationSecret() { // Act - _logger.LogInformation("GET -> '{Uri}'", _defaultRoute); + Logger.LogInformation("GET -> '{Uri}'", _defaultRoute); using (HttpResponseMessage response = await HttpClient.GetAsync(_defaultRoute)) { // Assert - _logger.LogInformation("{StatusCode} <- {Uri}", response.StatusCode, _defaultRoute); + Logger.LogInformation("{StatusCode} <- {Uri}", response.StatusCode, _defaultRoute); string contents = await response.Content.ReadAsStringAsync(); Assert.Equal("TestSecret", contents); } diff --git a/src/Arcus.Security.Tests.Integration/Fixture/TestConfig.cs b/src/Arcus.Security.Tests.Integration/Fixture/TestConfig.cs deleted file mode 100644 index 10647750..00000000 --- a/src/Arcus.Security.Tests.Integration/Fixture/TestConfig.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using GuardNet; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Primitives; - -namespace Arcus.Security.Tests.Integration.Fixture -{ - /// - /// Represents the configuration used in the integration test suite. - /// - public class TestConfig : IConfiguration - { - private readonly IConfiguration _configuration; - - /// - /// Prevents a new instance of the class from being created. - /// - private TestConfig(IConfiguration configuration) - { - Guard.NotNull(configuration, nameof(configuration), $"Requires an {nameof(IConfiguration)} instance to initialize the test config"); - - _configuration = configuration; - } - - /// - /// Creates a new instance of the class. - /// - public static TestConfig Create() - { - IConfiguration configuration = new ConfigurationBuilder() - .AddJsonFile("appsettings.json", optional: false) - .AddJsonFile("appsettings.local.json", optional: true) - .Build(); - - return new TestConfig(configuration); - } - - /// - /// Gets the configured tenant ID from the application configuration. - /// - /// Thrown when there's no tenant ID found in the application configuration. - public string GetTenantId() - { - string tenantId = GetRequiredValue("Arcus:Tenant"); - return tenantId; - } - - /// - /// Gets the configured client ID of the service principal from the application configuration. - /// - /// Thrown when there's no application ID found in the application configuration. - public string GetServicePrincipalClientId() - { - string clientId = GetRequiredValue("Arcus:ServicePrincipal:ApplicationId"); - return clientId; - } - - /// - /// Gets the configured client secret of the service principal from the application configuration. - /// - /// Thrown when there's no application secret found in the application configuration. - public string GetServicePrincipalClientSecret() - { - string clientSecret = GetRequiredValue("Arcus:ServicePrincipal:AccessKey"); - return clientSecret; - } - - /// - /// Gets the configured HashiCorp Vault execution file. - /// - /// Thrown when no installation file path was found in the configuration app settings. - /// Thrown when the installation file path doesn't point to a valid HashiCorp Vault execution file. - public FileInfo GetHashiCorpVaultBin() - { - const string key = "Arcus:HashiCorp:VaultBin"; - string vaultBin = _configuration[key]; - - if (string.IsNullOrWhiteSpace(vaultBin)) - { - throw new KeyNotFoundException( - "Could not find the installation file path of the HashiCorp Vault in the local app settings" - + "please install the HashiCorp Vault on this machine (https://releases.hashicorp.com/vault/) " - + $"and add the installation folder as configuration key '{key}' to your local app settings"); - } - - FileInfo vaultFile; - try - { - vaultFile = new FileInfo(vaultBin); - } - catch (Exception exception) - { - throw new FileNotFoundException( - $"Could not find file path returned for key '{key}' because it doesn't point to valid HashiCorp vault execution file, " - + "please install the HashiCorp Vault on this machine (https://releases.hashicorp.com/vault/) " - + $"and add the installation folder as configuration key '{key}' to your local app settings", exception); - } - - if (!vaultFile.Exists || !vaultFile.Name.StartsWith("vault")) - { - throw new FileNotFoundException( - $"Could not find file path returned for key '{key}' because it doesn't point to valid HashiCorp vault execution file ('vault'), " - + "please install the HashiCorp Vault on this machine (https://releases.hashicorp.com/vault/) " - + $"and add the installation folder as configuration key '{key}' to your local app settings"); - } - - return vaultFile; - } - - public string GetRequiredValue(string key) - { - string value = _configuration[key]; - if (string.IsNullOrWhiteSpace(value)) - { - throw new KeyNotFoundException( - $"Could not find configuration value for key: '{key}', was blank"); - } - - return value; - } - - /// - /// Gets a configuration sub-section with the specified key. - /// - /// The key of the configuration section. - /// The . - /// - /// This method will never return null. If no matching sub-section is found with the specified key, - /// an empty will be returned. - /// - public IConfigurationSection GetSection(string key) - { - return _configuration.GetSection(key); - } - - /// - /// Gets the immediate descendant configuration sub-sections. - /// - /// The configuration sub-sections. - public IEnumerable GetChildren() - { - return _configuration.GetChildren(); - } - - /// - /// Returns a that can be used to observe when this configuration is reloaded. - /// - /// A . - public IChangeToken GetReloadToken() - { - return _configuration.GetReloadToken(); - } - - /// Gets or sets a configuration value. - /// The configuration key. - /// The configuration value. - public string this[string key] - { - get => _configuration[key]; - set => _configuration[key] = value; - } - } -} diff --git a/src/Arcus.Security.Tests.Integration/Fixture/TestConfigExtensions.cs b/src/Arcus.Security.Tests.Integration/Fixture/TestConfigExtensions.cs new file mode 100644 index 00000000..dbe05282 --- /dev/null +++ b/src/Arcus.Security.Tests.Integration/Fixture/TestConfigExtensions.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.IO; + +// ReSharper disable once CheckNamespace +namespace Arcus.Testing +{ + /// + /// Represents the configuration used in the integration test suite. + /// + public static class TestConfigExtensions + { + /// + /// Gets the configured tenant ID from the application configuration. + /// + /// Thrown when there's no tenant ID found in the application configuration. + public static string GetTenantId(this TestConfig config) + { + return config["Arcus:Tenant"]; + } + + /// + /// Gets the configured client ID of the service principal from the application configuration. + /// + /// Thrown when there's no application ID found in the application configuration. + public static string GetServicePrincipalClientId(this TestConfig config) + { + return config["Arcus:ServicePrincipal:ApplicationId"]; + } + + /// + /// Gets the configured client secret of the service principal from the application configuration. + /// + /// Thrown when there's no application secret found in the application configuration. + public static string GetServicePrincipalClientSecret(this TestConfig config) + { + string clientSecret = config["Arcus:ServicePrincipal:AccessKey"]; + return clientSecret; + } + + /// + /// Gets the configured client ID of the service principal that is not authenticated. + /// + public static string GetUnauthorizedServicePrincipalClientId(this TestConfig config) + { + return config["Arcus:UnauthorizedServicePrincipal:ApplicationId"]; + } + + /// + /// Gets the configured client secret of the service principal that is not authenticated. + /// + /// + public static string GetUnauthorizedServicePrincipalClientSecret(this TestConfig config) + { + return config["Arcus:UnauthorizedServicePrincipal:AccessKey"]; + } + + /// + /// Gets the name of the expected secret present in the Azure Key vault. + /// + public static string GetSecretName(this TestConfig config) + { + return config["Arcus:KeyVault:TestSecretName"]; + } + + /// + /// Gets the value of the expected secret present in the Azure Key vault. + /// + public static string GetSecretValue(this TestConfig config) + { + return config["Arcus:KeyVault:TestSecretValue"]; + } + + /// + /// Gets the version of the expected secret present in the Azure Key vault. + /// + public static string GetSecretVersion(this TestConfig config) + { + return config["Arcus:KeyVault:TestSecretVersion"]; + } + + /// + /// Gets the configured HashiCorp Vault execution file. + /// + /// Thrown when no installation file path was found in the configuration app settings. + /// Thrown when the installation file path doesn't point to a valid HashiCorp Vault execution file. + public static FileInfo GetHashiCorpVaultBin(this TestConfig config) + { + const string key = "Arcus:HashiCorp:VaultBin"; + string vaultBin = config[key]; + + FileInfo vaultFile; + try + { + vaultFile = new FileInfo(vaultBin); + } + catch (Exception exception) + { + throw new FileNotFoundException( + $"Could not find file path returned for key '{key}' because it doesn't point to valid HashiCorp vault execution file, " + + "please install the HashiCorp Vault on this machine (https://releases.hashicorp.com/vault/) " + + $"and add the installation folder as configuration key '{key}' to your local app settings", exception); + } + + if (!vaultFile.Exists || !vaultFile.Name.StartsWith("vault")) + { + throw new FileNotFoundException( + $"Could not find file path returned for key '{key}' because it doesn't point to valid HashiCorp vault execution file ('vault'), " + + "please install the HashiCorp Vault on this machine (https://releases.hashicorp.com/vault/) " + + $"and add the installation folder as configuration key '{key}' to your local app settings"); + } + + return vaultFile; + } + + public static string GetRequiredValue(this TestConfig config, string key) + { + return config[key]; + } + } +} diff --git a/src/Arcus.Security.Tests.Integration/Fixture/XunitTestLogSink.cs b/src/Arcus.Security.Tests.Integration/Fixture/XunitTestLogSink.cs deleted file mode 100644 index 41ca62ac..00000000 --- a/src/Arcus.Security.Tests.Integration/Fixture/XunitTestLogSink.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using GuardNet; -using Serilog.Core; -using Serilog.Events; -using Xunit.Abstractions; - -namespace Arcus.Security.Tests.Integration.Fixture -{ - /// - /// xUnit test implementation of an Serilog to delegate Serilog events to the xUnit . - /// - public class XunitTestLogSink : ILogEventSink - { - private readonly ITestOutputHelper _outputWriter; - - /// - /// Initializes a new instance of the class. - /// - /// The xUnit test output helper to delegate the Serilog log events to. - /// Thrown when the is null. - public XunitTestLogSink(ITestOutputHelper outputWriter) - { - Guard.NotNull(outputWriter, nameof(outputWriter), "Requires a xUnit test output helper to write Serilog log events to the xUnit test output"); - _outputWriter = outputWriter; - } - - /// - /// Emit the provided log event to the sink. - /// - /// The log event to write. - public void Emit(LogEvent logEvent) - { - string message = logEvent.RenderMessage(); - _outputWriter.WriteLine(message); - } - } -} diff --git a/src/Arcus.Security.Tests.Integration/HashiCorp/HashiCorpSecretProviderTests.cs b/src/Arcus.Security.Tests.Integration/HashiCorp/HashiCorpSecretProviderTests.cs index a59a71ce..0e5b3075 100644 --- a/src/Arcus.Security.Tests.Integration/HashiCorp/HashiCorpSecretProviderTests.cs +++ b/src/Arcus.Security.Tests.Integration/HashiCorp/HashiCorpSecretProviderTests.cs @@ -4,12 +4,9 @@ using Arcus.Security.Providers.HashiCorp; using Arcus.Security.Providers.HashiCorp.Configuration; using Arcus.Security.Providers.HashiCorp.Extensions; -using Arcus.Security.Tests.Integration.Fixture; using Arcus.Security.Tests.Integration.HashiCorp.Hosting; -using Arcus.Testing.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using VaultSharp; using VaultSharp.V1.AuthMethods; @@ -20,22 +17,20 @@ namespace Arcus.Security.Tests.Integration.HashiCorp { [Trait(name: "Category", value: "Integration")] - public class HashiCorpSecretProviderTests + public partial class HashiCorpSecretProviderTests : IntegrationTest { private const string DefaultDevMountPoint = "secret"; - private readonly TestConfig _config; - private readonly ILogger _logger; - /// /// Initializes a new instance of the class. /// - public HashiCorpSecretProviderTests(ITestOutputHelper outputWriter) + public HashiCorpSecretProviderTests(ITestOutputHelper outputWriter) : base(outputWriter) { - _config = TestConfig.Create(); - _logger = new XunitTestLogger(outputWriter); } + private string UserPassUserName => Configuration["Arcus:HashiCorp:UserPass:UserName"]; + private string UserPassPassword => Configuration["Arcus:HashiCorp:UserPass:Password"]; + [Fact] public async Task AuthenticateWithUserPassKeyValueV2_GetSecret_Succeeds() { @@ -44,17 +39,14 @@ public async Task AuthenticateWithUserPassKeyValueV2_GetSecret_Succeeds() string secretName = "my-value"; string expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; - - using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) + using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint: DefaultDevMountPoint, path: secretPath, data: new Dictionary { [secretName] = expected }); - var authentication = new UserPassAuthMethodInfo(userName, password); + var authentication = new UserPassAuthMethodInfo(UserPassUserName, UserPassPassword); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); var provider = new HashiCorpSecretProvider(settings, secretPath, new HashiCorpVaultOptions { @@ -78,17 +70,14 @@ public async Task AuthenticateWithUserPassKeyValueV2_GetNotFoundSecret_Fails() string secretName = "my-value"; string expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; - - using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) + using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint: DefaultDevMountPoint, path: secretPath, data: new Dictionary { ["unknown-prefix-" + secretName] = expected }); - var authentication = new UserPassAuthMethodInfo(userName, password); + var authentication = new UserPassAuthMethodInfo(UserPassUserName, UserPassPassword); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); var provider = new HashiCorpSecretProvider(settings, secretPath, new HashiCorpVaultOptions { @@ -110,13 +99,11 @@ public async Task AddHashiCorpVaultWithUserPass_WithMutationToRemovePrefix_Succe // Arrange string secretPath = "secretpath"; string secretKey = "my-value", expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; const string secretNamePrefix = "Test-"; var builder = new HostBuilder(); - using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) + using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint: DefaultDevMountPoint, @@ -127,7 +114,7 @@ await server.KeyValueV2.WriteSecretAsync( builder.ConfigureSecretStore((config, stores) => { stores.AddHashiCorpVaultWithUserPass( - server.ListenAddress.ToString(), userName, password, secretPath, + server.ListenAddress.ToString(), UserPassUserName, UserPassPassword, secretPath, configureOptions: options => options.KeyValueMountPoint = DefaultDevMountPoint, mutateSecretName: secretName => secretName.Remove(0, secretNamePrefix.Length), name: null); @@ -148,12 +135,10 @@ public async Task AddHashiCorpVaultWithUserPass_WithWrongMutation_Fails() // Arrange string secretPath = "secretpath"; string secretKey = "my-value", expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; var builder = new HostBuilder(); - using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) + using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint: DefaultDevMountPoint, @@ -164,7 +149,7 @@ await server.KeyValueV2.WriteSecretAsync( builder.ConfigureSecretStore((config, stores) => { stores.AddHashiCorpVaultWithUserPass( - server.ListenAddress.ToString(), userName, password, secretPath, + server.ListenAddress.ToString(), UserPassUserName, UserPassPassword, secretPath, configureOptions: options => options.KeyValueMountPoint = DefaultDevMountPoint, mutateSecretName: secretName => "Test-" + secretName, name: null); @@ -183,20 +168,18 @@ public async Task AddHashiCorpVault_WithMutationToRemovePrefix_Succeeds() // Arrange string secretPath = "secretpath"; string secretKey = "my-value", expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; const string secretNamePrefix = "Test-"; var builder = new HostBuilder(); - using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) + using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint: DefaultDevMountPoint, path: secretPath, data: new Dictionary { [secretKey] = expected }); - var authentication = new UserPassAuthMethodInfo(userName, password); + var authentication = new UserPassAuthMethodInfo(UserPassUserName, UserPassPassword); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); // Act @@ -223,19 +206,17 @@ public async Task AddHashiCorpVault_WithWrongMutation_Fails() // Arrange string secretPath = "secretpath"; string secretKey = "my-value", expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; var builder = new HostBuilder(); - using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, DefaultDevMountPoint)) + using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(DefaultDevMountPoint)) { await server.KeyValueV2.WriteSecretAsync( mountPoint: DefaultDevMountPoint, path: secretPath, data: new Dictionary { [secretKey] = expected }); - var authentication = new UserPassAuthMethodInfo(userName, password); + var authentication = new UserPassAuthMethodInfo(UserPassUserName, UserPassPassword); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); // Act @@ -262,13 +243,10 @@ public async Task AuthenticateWithUserPassKeyValueV1_GetSecret_Succeeds() string secretName = "my-value"; string expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; - const string mountPoint = "secret-v1"; const VaultKeyValueSecretEngineVersion keyValueVersion = VaultKeyValueSecretEngineVersion.V1; - using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, mountPoint)) + using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(mountPoint)) { await server.MountKeyValueAsync(mountPoint, keyValueVersion); await server.KeyValueV1.WriteSecretAsync( @@ -276,7 +254,7 @@ await server.KeyValueV1.WriteSecretAsync( path: secretPath, values: new Dictionary { [secretName] = expected }); - var authentication = new UserPassAuthMethodInfo(userName, password); + var authentication = new UserPassAuthMethodInfo(UserPassUserName, UserPassPassword); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); var provider = new HashiCorpSecretProvider(settings, secretPath, new HashiCorpVaultOptions { @@ -300,13 +278,10 @@ public async Task AuthenticateWithUserPassKeyValueV1_GetNotFoundSecret_Fails() string secretName = "my-value"; string expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; - const string mountPoint = "secret-v1"; const VaultKeyValueSecretEngineVersion keyValueVersion = VaultKeyValueSecretEngineVersion.V1; - using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(userName, password, mountPoint)) + using (HashiCorpVaultTestServer server = await StartServerWithUserPassAsync(mountPoint)) { await server.MountKeyValueAsync(mountPoint, keyValueVersion); await server.KeyValueV1.WriteSecretAsync( @@ -314,7 +289,7 @@ await server.KeyValueV1.WriteSecretAsync( path: secretPath, values: new Dictionary { ["unknown-prefix-" + secretName] = expected }); - var authentication = new UserPassAuthMethodInfo(userName, password); + var authentication = new UserPassAuthMethodInfo(UserPassUserName, UserPassPassword); var settings = new VaultClientSettings(server.ListenAddress.ToString(), authentication); var provider = new HashiCorpSecretProvider(settings, secretPath, new HashiCorpVaultOptions{ KeyValueMountPoint = mountPoint, @@ -329,16 +304,18 @@ await server.KeyValueV1.WriteSecretAsync( } } - private async Task StartServerWithUserPassAsync(string userName, string password, string availableSecretMountPoint) + private async Task StartServerWithUserPassAsync(string availableSecretMountPoint) { const string policyName = "my-policy"; - var server = await HashiCorpVaultTestServer.StartServerAsync(_config, _logger); + var server = await HashiCorpVaultTestServer.StartServerAsync(Configuration, Logger); await server.AddPolicyAsync(policyName, availableSecretMountPoint, new[] { "read" }); await server.EnableAuthenticationTypeAsync(AuthMethodDefaultPaths.UserPass, "Authenticating with username and password"); - await server.AddUserPassUserAsync(userName, password, policyName); + await server.AddUserPassUserAsync(UserPassUserName, UserPassPassword, policyName); return server; } + + } } diff --git a/src/Arcus.Security.Tests.Integration/HashiCorp/Hosting/HashiCorpVaultTestServer.cs b/src/Arcus.Security.Tests.Integration/HashiCorp/Hosting/HashiCorpVaultTestServer.cs index 0f24dcfb..435a0ddd 100644 --- a/src/Arcus.Security.Tests.Integration/HashiCorp/Hosting/HashiCorpVaultTestServer.cs +++ b/src/Arcus.Security.Tests.Integration/HashiCorp/Hosting/HashiCorpVaultTestServer.cs @@ -8,8 +8,8 @@ using System.Threading; using System.Threading.Tasks; using Arcus.Security.Providers.HashiCorp; -using Arcus.Security.Tests.Integration.Fixture; using Arcus.Security.Tests.Integration.HashiCorp.Mounting; +using Arcus.Testing; using GuardNet; using Microsoft.Extensions.Logging; using Polly; @@ -21,8 +21,6 @@ using VaultSharp.V1.AuthMethods.Token; using VaultSharp.V1.SecretsEngines.KeyValue.V1; using VaultSharp.V1.SecretsEngines.KeyValue.V2; -using VaultSharp.V1.SystemBackend; -using IVaultClient = VaultSharp.IVaultClient; using MountInfo = Arcus.Security.Tests.Integration.HashiCorp.Mounting.MountInfo; using Policy = Polly.Policy; using VaultClient = Vault.VaultClient; diff --git a/src/Arcus.Security.Tests.Integration/HashiCorp/SecretStoreBuilderExtensionsTests.cs b/src/Arcus.Security.Tests.Integration/HashiCorp/SecretStoreBuilderExtensionsTests.cs index 1a2a0bf4..40b12171 100644 --- a/src/Arcus.Security.Tests.Integration/HashiCorp/SecretStoreBuilderExtensionsTests.cs +++ b/src/Arcus.Security.Tests.Integration/HashiCorp/SecretStoreBuilderExtensionsTests.cs @@ -5,36 +5,18 @@ using System.Threading.Tasks; using Arcus.Security.Core; using Arcus.Security.Providers.HashiCorp.Extensions; -using Arcus.Security.Tests.Integration.Fixture; using Arcus.Security.Tests.Integration.HashiCorp.Hosting; -using Arcus.Testing.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; using VaultSharp.Core; using VaultSharp.V1.AuthMethods; using Xunit; -using Xunit.Abstractions; namespace Arcus.Security.Tests.Integration.HashiCorp { - [Trait(name: "Category", value: "Integration")] - public class SecretStoreBuilderExtensionsTests : IntegrationTest + public partial class HashiCorpSecretProviderTests { - private const string DefaultDevMountPoint = "secret"; - - private readonly TestConfig _config; - private readonly XunitTestLogger _logger; - - /// - /// Initializes a new instance of the class. - /// - public SecretStoreBuilderExtensionsTests(ITestOutputHelper outputWriter) : base(outputWriter) - { - _config = TestConfig.Create(); - _logger = new XunitTestLogger(outputWriter); - } - [Theory] [InlineData(false, 0)] [InlineData(true, 2)] @@ -45,20 +27,18 @@ public async Task AuthenticateWithInvalidUserPassPasswordKeyValue_GetSecret_Fail string secretName = "my-value"; string expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; string invalidPassword = Guid.NewGuid().ToString(); const string policyName = "my-policy"; var builder = new HostBuilder(); - builder.UseSerilog(Logger); + builder.UseSerilog(SerilogLogger); - using (var server = await HashiCorpVaultTestServer.StartServerAsync(_config, _logger)) + using (var server = await HashiCorpVaultTestServer.StartServerAsync(Configuration, Logger)) { await server.AddPolicyAsync(policyName, DefaultDevMountPoint, new[] { "read" }); await server.EnableAuthenticationTypeAsync(AuthMethodDefaultPaths.UserPass, "Authenticating with username and password"); - await server.AddUserPassUserAsync(userName, password, policyName); + await server.AddUserPassUserAsync(UserPassUserName, UserPassPassword, policyName); await server.KeyValueV2.WriteSecretAsync( mountPoint: DefaultDevMountPoint, path: secretPath, @@ -67,7 +47,7 @@ await server.KeyValueV2.WriteSecretAsync( // Act builder.ConfigureSecretStore((config, stores) => { - stores.AddHashiCorpVaultWithUserPass(server.ListenAddress.ToString(), userName, invalidPassword, secretPath, options => + stores.AddHashiCorpVaultWithUserPass(server.ListenAddress.ToString(), UserPassUserName, invalidPassword, secretPath, options => { options.KeyValueMountPoint = secretPath; options.TrackDependency = trackDependency; @@ -98,18 +78,15 @@ public async Task AuthenticateWithUnauthorizedUserPassUserKeyValue_GetSecret_Fai string secretName = "my-value"; string expected = "s3cr3t"; - string userName = _config["Arcus:HashiCorp:UserPass:UserName"]; - string password = _config["Arcus:HashiCorp:UserPass:Password"]; - const string policyName = "my-policy"; var builder = new HostBuilder(); - builder.UseSerilog(Logger); + builder.UseSerilog(SerilogLogger); - using (var server = await HashiCorpVaultTestServer.StartServerAsync(_config, _logger)) + using (var server = await HashiCorpVaultTestServer.StartServerAsync(Configuration, Logger)) { await server.EnableAuthenticationTypeAsync(AuthMethodDefaultPaths.UserPass, "Authenticating with username and password"); - await server.AddUserPassUserAsync(userName, password, policyName); + await server.AddUserPassUserAsync(UserPassUserName, UserPassPassword, policyName); await server.KeyValueV2.WriteSecretAsync( mountPoint: DefaultDevMountPoint, path: secretPath, @@ -118,7 +95,7 @@ await server.KeyValueV2.WriteSecretAsync( // Act builder.ConfigureSecretStore((config, stores) => { - stores.AddHashiCorpVaultWithUserPass(server.ListenAddress.ToString(), userName, password, secretPath, options => + stores.AddHashiCorpVaultWithUserPass(server.ListenAddress.ToString(), UserPassUserName, UserPassPassword, secretPath, options => { options.KeyValueMountPoint = secretPath; options.TrackDependency = trackDependency; diff --git a/src/Arcus.Security.Tests.Integration/IntegrationTest.cs b/src/Arcus.Security.Tests.Integration/IntegrationTest.cs index 1adf35a8..b6a7a088 100644 --- a/src/Arcus.Security.Tests.Integration/IntegrationTest.cs +++ b/src/Arcus.Security.Tests.Integration/IntegrationTest.cs @@ -1,37 +1,36 @@ using System; -using Arcus.Security.Tests.Integration.Fixture; -using Arcus.Testing.Logging.Extensions; -using Arcus.Testing.Logging; -using Microsoft.Extensions.Configuration; +using Arcus.Testing; using Serilog; using Serilog.Configuration; using Serilog.Core; using Xunit.Abstractions; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Arcus.Security.Tests.Integration { public class IntegrationTest : IDisposable { private bool _disposed; - - protected TestConfig Configuration { get; } - protected Logger Logger { get; } - protected InMemoryLogSink InMemoryLogSink { get; } - public IntegrationTest(ITestOutputHelper testOutput) + protected IntegrationTest(ITestOutputHelper testOutput) { - // The appsettings.local.json allows users to override (gitignored) settings locally for testing purposes Configuration = TestConfig.Create(); + Logger = new XunitTestLogger(testOutput); + InMemoryLogSink = new InMemoryLogSink(); - + var configuration = new LoggerConfiguration() .WriteTo.XunitTestLogging(testOutput) - .WriteTo.Sink(InMemoryLogSink) - .WriteTo.AzureApplicationInsightsWithInstrumentationKey(Configuration.GetValue("Arcus:ApplicationInsights:InstrumentationKey")); + .WriteTo.Sink(InMemoryLogSink); - Logger = configuration.CreateLogger(); + SerilogLogger = configuration.CreateLogger(); } + protected TestConfig Configuration { get; } + protected ILogger Logger { get; } + protected Logger SerilogLogger { get; } + protected InMemoryLogSink InMemoryLogSink { get; } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -53,7 +52,7 @@ public void Dispose() /// protected virtual void Dispose(bool disposing) { - Logger.Dispose(); + SerilogLogger.Dispose(); } } } \ No newline at end of file diff --git a/src/Arcus.Security.Tests.Integration/KeyVault/Fixture/TemporaryManagedIdentityConnection.cs b/src/Arcus.Security.Tests.Integration/KeyVault/Fixture/TemporaryManagedIdentityConnection.cs new file mode 100644 index 00000000..23567ef6 --- /dev/null +++ b/src/Arcus.Security.Tests.Integration/KeyVault/Fixture/TemporaryManagedIdentityConnection.cs @@ -0,0 +1,36 @@ +using System; +using Arcus.Security.Tests.Core.Fixture; +using Xunit; + +namespace Arcus.Security.Tests.Integration.KeyVault.Fixture +{ + public class TemporaryManagedIdentityConnection : IDisposable + { + private readonly TemporaryEnvironmentVariable[] _variables; + + private TemporaryManagedIdentityConnection(string clientId, params TemporaryEnvironmentVariable[] variables) + { + _variables = variables; + ClientId = clientId; + } + + public string ClientId { get; } + + public static TemporaryManagedIdentityConnection Create(string tenantId, string clientId, string clientSecret) + { + return new TemporaryManagedIdentityConnection( + clientId, + TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, tenantId), + TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, clientId), + TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, clientSecret)); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Assert.All(_variables, var => var.Dispose()); + } + } +} diff --git a/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProvider.ManagedIdentityTests.cs b/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProvider.ManagedIdentityTests.cs index 0e31aa26..c7ef7491 100644 --- a/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProvider.ManagedIdentityTests.cs +++ b/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProvider.ManagedIdentityTests.cs @@ -5,9 +5,9 @@ using Arcus.Security.Providers.AzureKeyVault.Configuration; using Arcus.Security.Tests.Core.Fixture; using Arcus.Security.Tests.Integration.Fixture; +using Arcus.Security.Tests.Integration.KeyVault.Fixture; using Azure.Identity; using Azure.Security.KeyVault.Secrets; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; @@ -22,9 +22,7 @@ public partial class KeyVaultSecretProviderTests public async Task KeyVaultSecretProvider_WithUserAssignedManagedIdentity_GetSecret_Succeeds() { // Arrange - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { var keyVaultSecretProvider = new KeyVaultSecretProvider( tokenCredential: new ChainedTokenCredential(new ManagedIdentityCredential(ClientId), new EnvironmentCredential()), @@ -42,9 +40,7 @@ public async Task KeyVaultSecretProvider_WithUserAssignedManagedIdentity_GetSecr public async Task KeyVaultSecretProvider_WithUserAssignedManagedIdentity_GetSecret_NonExistingSecret_ThrowsSecretNotFoundException() { // Arrange - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { var notExistingSecretName = $"secret-{Guid.NewGuid():N}"; var keyVaultSecretProvider = new KeyVaultSecretProvider( @@ -67,11 +63,9 @@ public async Task KeyVaultSecretProvider_StoreSecret_Succeeds() var secretName = $"Test-Secret-{Guid.NewGuid()}"; var secretValue = Guid.NewGuid().ToString(); - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using TemporaryManagedIdentityConnection connection = UseTemporaryManagedIdentityConnection(); { - var tokenCredential = new ChainedTokenCredential(new ManagedIdentityCredential(ClientId), new EnvironmentCredential()); + var tokenCredential = new ChainedTokenCredential(new ManagedIdentityCredential(connection.ClientId), new EnvironmentCredential()); try { var keyVaultSecretProvider = new KeyVaultSecretProvider( @@ -106,9 +100,7 @@ public async Task AddAzureKeyVault_WithManagedIdentity_GetSecretSucceeds() builder.ConfigureSecretStore((config, stores) => stores.AddAzureKeyVaultWithManagedIdentity(VaultUri, cacheConfiguration: null, ClientId)); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using IHost host = builder.Build(); var provider = host.Services.GetRequiredService(); @@ -141,9 +133,7 @@ public async Task AddAzureKeyVaultSimple_WithManagedIdentity_GetSecretSucceeds() }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using IHost host = builder.Build(); var provider = host.Services.GetRequiredService(); @@ -163,7 +153,7 @@ public async Task AddAzureKeyVaultWithDependencyTracking_WithManagedIdentity_Get { // Arrange var builder = new HostBuilder(); - builder.UseSerilog(Logger, dispose: true); + builder.UseSerilog(SerilogLogger, dispose: true); // Act builder.ConfigureSecretStore((config, stores) => @@ -175,9 +165,7 @@ public async Task AddAzureKeyVaultWithDependencyTracking_WithManagedIdentity_Get }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); using (IHost host = builder.Build()) { var provider = host.Services.GetRequiredService(); @@ -206,9 +194,7 @@ public async Task AddAzureKeyVault_WithManagedIdentityRemovesPrefix_GetsSecretSu }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using IHost host = builder.Build(); var provider = host.Services.GetRequiredService(); @@ -238,9 +224,7 @@ public async Task AddAzureKeyVault_WithManagedIdentityWrongMutation_GetsSecretFa }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using IHost host = builder.Build(); var provider = host.Services.GetRequiredService(); @@ -259,7 +243,7 @@ public async Task AddAzureKeyVaultWithDependencyTracking_WithManagedIdentityWron { // Arrange var builder = new HostBuilder(); - builder.UseSerilog(Logger, dispose: true); + builder.UseSerilog(SerilogLogger, dispose: true); // Act builder.ConfigureSecretStore((config, stores) => @@ -272,9 +256,7 @@ public async Task AddAzureKeyVaultWithDependencyTracking_WithManagedIdentityWron }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using (IHost host = builder.Build()) { @@ -301,9 +283,7 @@ public async Task AddAzureKeyVault_WithCachedManagedIdentity_GetSecretSucceeds() }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using IHost host = builder.Build(); var provider = host.Services.GetRequiredService(); @@ -332,9 +312,7 @@ public async Task AddAzureKeyVault_WithCachedManagedIdentity_GetSecretFails() }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using IHost host = builder.Build(); var provider = host.Services.GetRequiredService(); @@ -364,9 +342,7 @@ public async Task AddAzureKeyVault_WithCachedManagedIdentityRemovesPrefix_GetsSe }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using IHost host = builder.Build(); var provider = host.Services.GetRequiredService(); @@ -398,9 +374,7 @@ public async Task AddAzureKeyVault_WithCachedManagedIdentityWrongMutation_GetsSe }); // Assert - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, TenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, ClientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, ClientSecret)) + using var _ = UseTemporaryManagedIdentityConnection(); { using IHost host = builder.Build(); var provider = host.Services.GetRequiredService(); @@ -416,22 +390,16 @@ public async Task AddAzureKeyVault_WithCachedManagedIdentityWrongMutation_GetsSe public async Task CachedKeyVaultSecretProvider_StoreSecret_Succeeds() { // Arrange - var keyVaultUri = Configuration.GetValue("Arcus:KeyVault:Uri"); - string tenantId = Configuration.GetTenantId(); - string clientId = Configuration.GetServicePrincipalClientId(); - string clientKey = Configuration.GetServicePrincipalClientSecret(); - + var secretName = $"Test-Secret-{Guid.NewGuid()}"; var secretValue = Guid.NewGuid().ToString(); - - using (TemporaryEnvironmentVariable.Create(Constants.AzureTenantIdEnvironmentVariable, tenantId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientIdVariable, clientId)) - using (TemporaryEnvironmentVariable.Create(Constants.AzureServicePrincipalClientSecretVariable, clientKey)) + + using TemporaryManagedIdentityConnection connection = UseTemporaryManagedIdentityConnection(); { - var tokenCredential = new ChainedTokenCredential(new ManagedIdentityCredential(clientId), new EnvironmentCredential()); + var tokenCredential = new ChainedTokenCredential(new ManagedIdentityCredential(connection.ClientId), new EnvironmentCredential()); var keyVaultSecretProvider = new KeyVaultSecretProvider( tokenCredential: tokenCredential, - vaultConfiguration: new KeyVaultConfiguration(keyVaultUri)); + vaultConfiguration: new KeyVaultConfiguration(VaultUri)); var cachedSecretProvider = new KeyVaultCachedSecretProvider(keyVaultSecretProvider); try @@ -450,7 +418,7 @@ public async Task CachedKeyVaultSecretProvider_StoreSecret_Succeeds() } finally { - var client = new SecretClient(new Uri(keyVaultUri), tokenCredential); + var client = new SecretClient(new Uri(VaultUri), tokenCredential); await client.StartDeleteSecretAsync(secretName); } } diff --git a/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProvider.ServicePrincipalTests.cs b/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProvider.ServicePrincipalTests.cs index 173ac3cd..40531e49 100644 --- a/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProvider.ServicePrincipalTests.cs +++ b/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProvider.ServicePrincipalTests.cs @@ -7,9 +7,9 @@ using Arcus.Security.Core.Caching.Configuration; using Arcus.Security.Providers.AzureKeyVault; using Arcus.Security.Providers.AzureKeyVault.Configuration; +using Arcus.Testing; using Azure; using Azure.Identity; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; @@ -84,7 +84,7 @@ public async Task AddAzureKeyVaultWithDependencyTracking_WithServicePrincipal_Ge var keyName = "UnknownSecretName"; var builder = new HostBuilder(); - builder.UseSerilog(Logger, dispose: true); + builder.UseSerilog(SerilogLogger, dispose: true); // Act builder.ConfigureSecretStore((config, stores) => @@ -268,8 +268,8 @@ public async Task AddAzureKeyVault_WithWrongServicePrincipalCredentials_Throws() public async Task AddAzureKeyVault_WithWrongUnauthorizedServicePrincipal_Throws() { // Arrange - string applicationId = Configuration.GetValue("Arcus:UnauthorizedServicePrincipal:ApplicationId"); - var clientKey = Configuration.GetValue("Arcus:UnauthorizedServicePrincipal:AccessKey"); + string applicationId = Configuration.GetUnauthorizedServicePrincipalClientId(); + string clientKey = Configuration.GetUnauthorizedServicePrincipalClientSecret(); string keyName = TestSecretName; var builder = new HostBuilder(); @@ -302,7 +302,7 @@ public async Task NewKeyVaultSecretProvider_ReturnsManySecrets_Succeeds() IEnumerable secrets = await keyVaultSecretProvider.GetSecretsAsync(TestSecretName, amountOfVersions: 2); // Assert - Assert.Equal(2, secrets.Count()); + Assert.True(10 >= secrets.Count(), "should only retrieve 10 or less versioned secrets"); Assert.Equal(TestSecretVersion, secrets.ElementAt(0).Version); } @@ -318,7 +318,7 @@ public async Task NewKeyVaultSecretProvider_ReturnsOnlyAvailableSecrets_Succeeds IEnumerable secrets = await keyVaultSecretProvider.GetSecretsAsync(TestSecretName, amountOfVersions: 10); // Assert - Assert.Equal(2, secrets.Count()); + Assert.True(10 >= secrets.Count(), "should only retrieve 10 or less versioned secrets"); Assert.Equal(TestSecretVersion, secrets.ElementAt(0).Version); } } diff --git a/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProviderTests.cs b/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProviderTests.cs index 230af9f8..2a5af1ce 100644 --- a/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProviderTests.cs +++ b/src/Arcus.Security.Tests.Integration/KeyVault/KeyVaultSecretProviderTests.cs @@ -1,6 +1,7 @@ using System.Linq; using Arcus.Security.Core; -using Microsoft.Extensions.Configuration; +using Arcus.Security.Tests.Integration.KeyVault.Fixture; +using Arcus.Testing; using Xunit; using Xunit.Abstractions; @@ -14,13 +15,17 @@ public KeyVaultSecretProviderTests(ITestOutputHelper testOutput) : base(testOutp } private string TenantId => Configuration.GetTenantId(); - private string ClientId => Configuration.GetValue("Arcus:ServicePrincipal:ApplicationId"); - private string ClientSecret => Configuration.GetValue("Arcus:ServicePrincipal:AccessKey"); - private string VaultUri => Configuration.GetValue("Arcus:KeyVault:Uri"); - private string TestSecretName => Configuration.GetValue("Arcus:KeyVault:TestKeyName"); - private string TestSecretValue => Configuration.GetValue("Arcus:KeyVault:TestKeyValue"); - private string TestSecretVersion => Configuration.GetRequiredValue("Arcus:KeyVault:TestKeyVersion"); - + private string ClientId => Configuration.GetServicePrincipalClientId(); + private string ClientSecret => Configuration.GetServicePrincipalClientSecret(); + private string VaultUri => $"https://{Configuration.GetRequiredValue("Arcus:KeyVault:Name")}.vault.azure.net/"; + private string TestSecretName => Configuration.GetSecretName(); + private string TestSecretValue => Configuration.GetSecretValue(); + private string TestSecretVersion => Configuration.GetSecretVersion(); + + private TemporaryManagedIdentityConnection UseTemporaryManagedIdentityConnection() + { + return TemporaryManagedIdentityConnection.Create(TenantId, ClientId, ClientSecret); + } private void AssertTrackedAzureKeyVaultDependency(int expectedTrackedDependencyCount) { diff --git a/src/Arcus.Security.Tests.Integration/appsettings.json b/src/Arcus.Security.Tests.Integration/appsettings.json index 29447f82..07a5c688 100644 --- a/src/Arcus.Security.Tests.Integration/appsettings.json +++ b/src/Arcus.Security.Tests.Integration/appsettings.json @@ -1,24 +1,19 @@ { "Arcus": { - "Tenant": "#{Arcus.Tenant}#", + "Tenant": "#{Arcus.Security.TenantId}#", "KeyVault": { - "Uri": "#{Arcus_KeyVault_Uri}#", - "TestKeyName": "#{Arcus.KeyVault.TestKeyName}#", - "TestKeyValue": "#{Arcus.KeyVault.TestKeyValue}#", - "TestKeyVersion": "#{Arcus.KeyVault.TestKeyVersion}#" + "Name": "#{Arcus.Security.KeyVault.Name}#", + "TestSecretName": "#{Arcus.Security.KeyVault.TestSecretName}#", + "TestSecretValue": "#{Arcus.Security.KeyVault.TestSecretValue}#", + "TestSecretVersion": "#{Arcus.Security.KeyVault.TestSecretVersion}#" }, "ServicePrincipal": { - "ApplicationId": "#{Arcus_ServicePrincipal_ApplicationId}#", - "AccessKey": "#{Arcus_ServicePrincipal_AccessKey}#" + "ApplicationId": "#{Arcus.Security.ServicePrincipal.ClientId}#", + "AccessKey": "#{Arcus.Security.ServicePrincipal.ClientSecret}#" }, "UnauthorizedServicePrincipal": { - "ApplicationId": "#{Arcus.UnauthorizedServicePrincipal.ApplicationId}#", - "AccessKey": "#{Arcus.UnauthorizedServicePrincipal.AccessKey}#" - }, - "MSI": { - "AzureServicesAuth": { - "ConnectionString": "#{Arcus_MSI_AzureServicesAuth_ConnectionString}#" - } + "ApplicationId": "#{Arcus.Security.Unauthorized.ServicePrincipal.ClientId}#", + "AccessKey": "#{Arcus.Security.Unauthorized.ServicePrincipal.ClientSecret}#" }, "HashiCorp": { "VaultBin": "#{Arcus.HashiCorp.VaultBin}#", @@ -27,9 +22,6 @@ "Password": "123" } }, - "ApplicationInsights": { - "InstrumentationKey": "#{Arcus.ApplicationInsights.InstrumentationKey}#" - }, "AzureFunctions": { "HttpPort": "#{Arcus.AzureFunctions.HttpPort}#" } diff --git a/src/Arcus.Security.Tests.Unit/Core/Extensions/IHostBuilderExtensionsTests.cs b/src/Arcus.Security.Tests.Unit/Core/Extensions/IHostBuilderExtensionsTests.cs index 3b56dabd..24e9fd8b 100644 --- a/src/Arcus.Security.Tests.Unit/Core/Extensions/IHostBuilderExtensionsTests.cs +++ b/src/Arcus.Security.Tests.Unit/Core/Extensions/IHostBuilderExtensionsTests.cs @@ -8,9 +8,8 @@ using Arcus.Security.Core; using Arcus.Security.Core.Caching; using Arcus.Security.Providers.HashiCorp.Extensions; -using Arcus.Security.Tests.Core.Stubs; using Arcus.Security.Tests.Unit.Core.Stubs; -using Arcus.Testing.Logging; +using Arcus.Testing; using Arcus.Testing.Security.Providers.InMemory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -418,7 +417,7 @@ public async Task ConfigureSecretStore_WithDefaultAuditing_DoesntLogsSecurityEve var stubProvider = new InMemorySecretProvider(new Dictionary { [secretName] = $"secret-{Guid.NewGuid()}" }); var spyLogger = new InMemoryLogger(); var builder = new HostBuilder(); - builder.ConfigureLogging(logging => logging.AddProvider(new TestLoggerProvider(spyLogger))); + builder.ConfigureLogging(logging => logging.AddProvider(new CustomLoggerProvider(spyLogger))); // Act builder.ConfigureSecretStore((config, stores) => @@ -443,7 +442,7 @@ public async Task ConfigureSecretStore_WithAuditing_LogsSecurityEvent(bool emitS var stubProvider = new InMemorySecretProvider(new Dictionary { [secretName] = $"secret-{Guid.NewGuid()}" }); var spyLogger = new InMemoryLogger(); var builder = new HostBuilder(); - builder.ConfigureLogging(logging => logging.AddProvider(new TestLoggerProvider(spyLogger))); + builder.ConfigureLogging(logging => logging.AddProvider(new CustomLoggerProvider(spyLogger))); // Act builder.ConfigureSecretStore((config, stores) => @@ -467,7 +466,7 @@ public async Task ConfigureSecretStore_WithAuditingIncrement_LogsSecurityEvent() var stubProvider = new InMemorySecretProvider(new Dictionary { [secretName] = $"secret-{Guid.NewGuid()}" }); var spyLogger = new InMemoryLogger(); var builder = new HostBuilder(); - builder.ConfigureLogging(logging => logging.AddProvider(new TestLoggerProvider(spyLogger))); + builder.ConfigureLogging(logging => logging.AddProvider(new CustomLoggerProvider(spyLogger))); // Act builder.ConfigureSecretStore((config, stores) => diff --git a/src/Arcus.Security.Tests.Unit/Core/Extensions/IServiceCollectionExtensionsTests.cs b/src/Arcus.Security.Tests.Unit/Core/Extensions/IServiceCollectionExtensionsTests.cs index b3d705ce..723f6290 100644 --- a/src/Arcus.Security.Tests.Unit/Core/Extensions/IServiceCollectionExtensionsTests.cs +++ b/src/Arcus.Security.Tests.Unit/Core/Extensions/IServiceCollectionExtensionsTests.cs @@ -5,8 +5,8 @@ using System.Threading.Tasks; using Arcus.Security.Core; using Arcus.Security.Core.Caching; -using Arcus.Security.Tests.Core.Stubs; using Arcus.Security.Tests.Unit.Core.Stubs; +using Arcus.Testing; using Arcus.Testing.Security.Providers.InMemory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -184,9 +184,9 @@ public async Task AddSecretStore_WithLogger_UsesLogger() { // Arrange var services = new ServiceCollection(); - var spyLogger = new SpyLogger(); + var spyLogger = new InMemoryLogger(); services.AddLogging(logging => logging.SetMinimumLevel(LogLevel.Trace) - .AddProvider(new TestLoggerProvider(spyLogger))); + .AddProvider(new CustomLoggerProvider(spyLogger))); const string secretName = "MySecret"; var stubProvider = new InMemorySecretProvider(new Dictionary { [secretName] = $"secret-{Guid.NewGuid()}" }); @@ -198,7 +198,7 @@ public async Task AddSecretStore_WithLogger_UsesLogger() IServiceProvider serviceProvider = services.BuildServiceProvider(); var secretProvider = serviceProvider.GetRequiredService(); await secretProvider.GetRawSecretAsync(secretName); - Assert.True(spyLogger.IsCalled); + Assert.NotEmpty(spyLogger.Messages); } [Fact]