Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Removing all dependency to Guard.NET. #437

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Arcus.Security.Core;
using GuardNet;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -22,8 +21,15 @@ public static class IFunctionHostBuilderExtensions
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="functionsHostBuilder"/> or <paramref name="configureSecretStores"/> is <c>null</c>.</exception>
public static IFunctionsHostBuilder ConfigureSecretStore(this IFunctionsHostBuilder functionsHostBuilder, Action<SecretStoreBuilder> configureSecretStores)
{
Guard.NotNull(functionsHostBuilder, nameof(functionsHostBuilder), "Requires a functions host builder to add the secret store");
Guard.NotNull(configureSecretStores, nameof(configureSecretStores), "Requires a function to configure the secret store with potential secret providers");
if (functionsHostBuilder is null)
{
throw new ArgumentNullException(nameof(functionsHostBuilder), "Requires a functions host builder to add the secret store");
}

if (configureSecretStores is null)
{
throw new ArgumentNullException(nameof(configureSecretStores), "Requires a function to configure the secret store with potential secret providers");
}

functionsHostBuilder.Services.AddSecretStore(configureSecretStores);
return functionsHostBuilder;
@@ -39,8 +45,15 @@ public static IFunctionsHostBuilder ConfigureSecretStore(
this IFunctionsHostBuilder functionsHostBuilder,
Action<FunctionsHostBuilderContext, IConfiguration, SecretStoreBuilder> configureSecretStores)
{
Guard.NotNull(functionsHostBuilder, nameof(functionsHostBuilder), "Requires a functions host builder to add the secret store");
Guard.NotNull(configureSecretStores, nameof(configureSecretStores), "Requires a function to configure the secret store with potential secret providers");
if (functionsHostBuilder is null)
{
throw new ArgumentNullException(nameof(functionsHostBuilder), "Requires a functions host builder to add the secret store");
}

if (configureSecretStores is null)
{
throw new ArgumentNullException(nameof(configureSecretStores), "Requires a function to configure the secret store with potential secret providers");
}

FunctionsHostBuilderContext context = functionsHostBuilder.GetContext();
functionsHostBuilder.Services.AddSecretStore(stores => configureSecretStores(context, context.Configuration, stores));
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using GuardNet;

namespace Arcus.Security.Core.Caching.Configuration
{
@@ -15,7 +14,11 @@ public class CacheConfiguration : ICacheConfiguration
/// <exception cref="ArgumentOutOfRangeException">Thrown when the cache duration is not a positive time duration.</exception>
public CacheConfiguration(TimeSpan duration)
{
Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration in which the caching should take place");
if (duration < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(duration), duration, "Requires a positive time duration in which the caching should take place");
}

Duration = duration;
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Arcus.Security.Core.Caching.Configuration;
using GuardNet;
using Microsoft.Extensions.Caching.Memory;

namespace Arcus.Security.Core.Caching
@@ -21,9 +20,20 @@ public static class SecretProviderCachingExtensions
/// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="cachingDuration"/> is not a positive time duration.</exception>
public static ICachedSecretProvider WithCaching(this ISecretProvider secretProvider, TimeSpan cachingDuration, IMemoryCache memoryCache)
{
Guard.NotNull(secretProvider, nameof(secretProvider), "Requires a secret provider instance to include caching while retrieving secrets");
Guard.NotLessThan(cachingDuration, TimeSpan.Zero, nameof(cachingDuration), "Requires a positive time duration in which the caching should take place");
Guard.NotNull(memoryCache, nameof(memoryCache), "Requires a memory caching implementation to include caching while retrieving secrets");
if (secretProvider is null)
{
throw new ArgumentNullException(nameof(secretProvider), "Requires a secret provider instance to include caching while retrieving secrets");
}

if (cachingDuration < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(cachingDuration), "Requires a positive time duration in which the caching should take place");
}

if (memoryCache is null)
{
throw new ArgumentNullException(nameof(memoryCache), "Requires a memory caching implementation to include caching while retrieving secrets");
}

return new CachedSecretProvider(secretProvider, new CacheConfiguration(cachingDuration), memoryCache);
}
@@ -39,8 +49,15 @@ public static ICachedSecretProvider WithCaching(this ISecretProvider secretProvi
/// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="cachingDuration"/> is not a positive time duration.</exception>
public static ICachedSecretProvider WithCaching(this ISecretProvider secretProvider, TimeSpan cachingDuration)
{
Guard.NotNull(secretProvider, nameof(secretProvider), "Requires a secret provider instance to include caching while retrieving secrets");
Guard.NotLessThan(cachingDuration, TimeSpan.Zero, nameof(cachingDuration), "Requires a positive time duration in which the caching should take place");
if (secretProvider is null)
{
throw new ArgumentNullException(nameof(secretProvider), "Requires a secret provider instance to include caching while retrieving secrets");
}

if (cachingDuration < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(cachingDuration), cachingDuration, "Requires a positive time duration in which the caching should take place");
}

return new CachedSecretProvider(secretProvider, new CacheConfiguration(cachingDuration));
}
@@ -54,7 +71,10 @@ public static ICachedSecretProvider WithCaching(this ISecretProvider secretProvi
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="secretProvider"/> is <c>null</c>.</exception>
public static ICachedSecretProvider WithCaching(this ISecretProvider secretProvider)
{
Guard.NotNull(secretProvider, nameof(secretProvider), "Requires a secret provider instance to include caching while retrieving secrets");
if (secretProvider is null)
{
throw new ArgumentNullException(nameof(secretProvider), "Requires a secret provider instance to include caching while retrieving secrets");
}

return new CachedSecretProvider(secretProvider);
}
23 changes: 18 additions & 5 deletions src/Arcus.Security.Core/Extensions/IHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Arcus.Security.Core;
using GuardNet;
using Microsoft.Extensions.Configuration;
using System;
using Microsoft.Extensions.DependencyInjection;
@@ -21,8 +20,15 @@ public static class IHostBuilderExtensions
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="hostBuilder"/> or <paramref name="configureSecretStores"/> is <c>null</c>.</exception>
public static IHostBuilder ConfigureSecretStore(this IHostBuilder hostBuilder, Action<IConfiguration, SecretStoreBuilder> configureSecretStores)
{
Guard.NotNull(hostBuilder, nameof(hostBuilder), "Requires a host builder to add the secret store");
Guard.NotNull(configureSecretStores, nameof(configureSecretStores), "Requires a function to register the secret providers in the secret store");
if (hostBuilder is null)
{
throw new ArgumentNullException(nameof(hostBuilder), "Requires a host builder to add the secret store");
}

if (configureSecretStores is null)
{
throw new ArgumentNullException(nameof(configureSecretStores), "Requires a function to register the secret providers in the secret store");
}

return ConfigureSecretStore(hostBuilder, (context, config, secretStores) => configureSecretStores(config, secretStores));
}
@@ -35,8 +41,15 @@ public static IHostBuilder ConfigureSecretStore(this IHostBuilder hostBuilder, A
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="hostBuilder"/> or <paramref name="configureSecretStores"/> is <c>null</c>.</exception>
public static IHostBuilder ConfigureSecretStore(this IHostBuilder hostBuilder, Action<HostBuilderContext, IConfiguration, SecretStoreBuilder> configureSecretStores)
{
Guard.NotNull(hostBuilder, nameof(hostBuilder), "Requires a host builder to add the secret store");
Guard.NotNull(configureSecretStores, nameof(configureSecretStores), "Requires a function to register the secret providers in the secret store");
if (hostBuilder is null)
{
throw new ArgumentNullException(nameof(hostBuilder), "Requires a host builder to add the secret store");
}

if (configureSecretStores is null)
{
throw new ArgumentNullException(nameof(configureSecretStores), "Requires a function to register the secret providers in the secret store");
}

return hostBuilder.ConfigureServices((context, services) =>
{
33 changes: 26 additions & 7 deletions src/Arcus.Security.Core/Extensions/ISecretProviderExtensions.cs
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GuardNet;

// ReSharper disable once CheckNamespace
namespace Arcus.Security.Core
@@ -23,8 +22,15 @@ public static class ISecretProviderExtensions
/// <exception cref="SecretNotFoundException">Thrown when the secret was not found, using the given name.</exception>
public static string GetRawSecret(this ISecretProvider secretProvider, string secretName)
{
Guard.NotNull(secretProvider, nameof(secretProvider), "Requires a secret provider to synchronously look up the secret");
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the secret");
if (secretProvider is null)
{
throw new ArgumentNullException(nameof(secretProvider), "Requires a secret provider to synchronously look up the secret");
}

if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the secret", nameof(secretName));
}

if (secretProvider is ISyncSecretProvider composite)
{
@@ -46,8 +52,15 @@ public static string GetRawSecret(this ISecretProvider secretProvider, string se
/// <exception cref="SecretNotFoundException">Thrown when the secret was not found, using the given name.</exception>
public static Secret GetSecret(this ISecretProvider secretProvider, string secretName)
{
Guard.NotNull(secretProvider, nameof(secretProvider), "Requires a secret provider to synchronously look up the secret");
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the secret");
if (secretProvider is null)
{
throw new ArgumentNullException(nameof(secretProvider
}

if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the secret", nameof(secretName));
}

if (secretProvider is ISyncSecretProvider composite)
{
@@ -73,7 +86,10 @@ public static Secret GetSecret(this ISecretProvider secretProvider, string secre
/// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception>
public static async Task<IEnumerable<string>> GetRawSecretsAsync(this ISecretProvider secretProvider, string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the secret");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the secret", nameof(secretName));
}

if (secretProvider is CompositeSecretProvider composite)
{
@@ -99,7 +115,10 @@ public static async Task<IEnumerable<string>> GetRawSecretsAsync(this ISecretPro
/// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception>
public static async Task<IEnumerable<Secret>> GetSecretsAsync(this ISecretProvider secretProvider, string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the secret");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the secret", nameof(secretName));
}

if (secretProvider is CompositeSecretProvider composite)
{
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Arcus.Security.Core;
using GuardNet;
using Microsoft.Extensions.Hosting;

// ReSharper disable once CheckNamespace
@@ -20,8 +19,15 @@ public static class IServiceCollectionExtensions
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="services"/> or <paramref name="configureSecretStores"/> is <c>null</c>.</exception>
public static IServiceCollection AddSecretStore(this IServiceCollection services, Action<SecretStoreBuilder> configureSecretStores)
{
Guard.NotNull(services, nameof(services), "Requires a set of services to add the secret store");
Guard.NotNull(configureSecretStores, nameof(configureSecretStores), "Requires a function to register the secret providers in the secret store");
if (services is null)
{
throw new ArgumentNullException(nameof(services), "Requires a set of services to add the secret store");
}

if (configureSecretStores is null)
{
throw new ArgumentNullException(nameof(configureSecretStores), "Requires a function to register the secret providers in the secret store");
}

var builder = new SecretStoreBuilder(services);
configureSecretStores(builder);
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Arcus.Security.Core.Providers;
using GuardNet;
using Microsoft.Extensions.Configuration;

// ReSharper disable once CheckNamespace
@@ -25,13 +24,7 @@ public static SecretStoreBuilder AddEnvironmentVariables(
EnvironmentVariableTarget target = EnvironmentVariableSecretProvider.DefaultTarget,
string prefix = null,
Func<string, string> mutateSecretName = null)
{
Guard.NotNull(builder, nameof(builder), "Requires a secret store builder to add the environment secrets");
Guard.For<ArgumentException>(() => !Enum.IsDefined(typeof(EnvironmentVariableTarget), target),
$"Requires an environment variable target of either '{EnvironmentVariableTarget.Process}', '{EnvironmentVariableTarget.Machine}', or '{EnvironmentVariableTarget.User}'");

return AddEnvironmentVariables(builder, target, prefix, name: null, mutateSecretName: mutateSecretName);
}
=> AddEnvironmentVariables(builder, target, prefix, name: null, mutateSecretName: mutateSecretName);

/// <summary>
/// Adds a secret source to the secret store of the application that gets its secrets from the environment.
@@ -50,9 +43,15 @@ public static SecretStoreBuilder AddEnvironmentVariables(
string name,
Func<string, string> mutateSecretName)
{
Guard.NotNull(builder, nameof(builder), "Requires a secret store builder to add the environment secrets");
Guard.For<ArgumentException>(() => !Enum.IsDefined(typeof(EnvironmentVariableTarget), target),
$"Requires an environment variable target of either '{EnvironmentVariableTarget.Process}', '{EnvironmentVariableTarget.Machine}', or '{EnvironmentVariableTarget.User}'");
if (builder is null)
{
throw new ArgumentNullException(nameof(builder), "Requires a secret store builder to add the environment secrets");
}

if (!Enum.IsDefined(typeof(EnvironmentVariableTarget), target))
{
throw new ArgumentException($"Requires an environment variable target of either '{EnvironmentVariableTarget.Process}', '{EnvironmentVariableTarget.Machine}', or '{EnvironmentVariableTarget.User}'");
}

return builder.AddProvider(new EnvironmentVariableSecretProvider(target, prefix), options =>
{
@@ -72,12 +71,7 @@ public static SecretStoreBuilder AddConfiguration(
this SecretStoreBuilder builder,
IConfiguration configuration,
Func<string, string> mutateSecretName = null)
{
Guard.NotNull(builder, nameof(builder), "Requires a secret store builder to add the configuration secrets");
Guard.NotNull(configuration, nameof(configuration), "Requires a configuration instance to retrieve the secrets from");

return AddConfiguration(builder, configuration, name: null, mutateSecretName: mutateSecretName);
}
=> AddConfiguration(builder, configuration, name: null, mutateSecretName: mutateSecretName);

/// <summary>
/// Adds a secret source to the secret store of the application that gets its secrets from the <see cref="IConfiguration"/>.
@@ -93,8 +87,15 @@ public static SecretStoreBuilder AddConfiguration(
string name,
Func<string, string> mutateSecretName)
{
Guard.NotNull(builder, nameof(builder), "Requires a secret store builder to add the configuration secrets");
Guard.NotNull(configuration, nameof(configuration), "Requires a configuration instance to retrieve the secrets from");
if (builder is null)
{
throw new ArgumentNullException(nameof(builder), "Requires a secret store builder to add the configuration secrets");
}

if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration), "Requires a configuration instance to retrieve the secrets from");
}

return builder.AddProvider(new ConfigurationSecretProvider(configuration), options =>
{
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Threading.Tasks;
using GuardNet;
using Microsoft.Extensions.Configuration;

namespace Arcus.Security.Core.Providers
@@ -19,7 +18,10 @@ public class ConfigurationSecretProvider : ISyncSecretProvider
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="configuration"/> is <c>null</c>.</exception>
public ConfigurationSecretProvider(IConfiguration configuration)
{
Guard.NotNull(configuration, nameof(configuration), "Requires a configuration instance to retrieve the secrets from");
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration), "Requires a configuration instance to retrieve the secrets from");
}

_configuration = configuration;
}
@@ -32,7 +34,10 @@ public ConfigurationSecretProvider(IConfiguration configuration)
/// <exception cref="T:Arcus.Security.Core.SecretNotFoundException">The secret was not found, using the given name</exception>
public Task<Secret> GetSecretAsync(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the secret configuration value");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the secret configuration value", nameof(secretName));
}

Secret secret = GetSecret(secretName);
return Task.FromResult(secret);
@@ -46,7 +51,10 @@ public Task<Secret> GetSecretAsync(string secretName)
/// <exception cref="T:Arcus.Security.Core.SecretNotFoundException">The secret was not found, using the given name</exception>
public Task<string> GetRawSecretAsync(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the secret configuration value");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the secret configuration value", nameof(secretName));
}

string secretValue = GetRawSecret(secretName);
return Task.FromResult(secretValue);
@@ -61,7 +69,10 @@ public Task<string> GetRawSecretAsync(string secretName)
/// <exception cref="SecretNotFoundException">Thrown when the secret was not found, using the given name.</exception>
public Secret GetSecret(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the secret configuration value");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the secret configuration value", nameof(secretName));
}

string secretValue = GetRawSecret(secretName);
if (secretValue is null)
@@ -81,7 +92,10 @@ public Secret GetSecret(string secretName)
/// <exception cref="SecretNotFoundException">Thrown when the secret was not found, using the given name.</exception>
public string GetRawSecret(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the secret configuration value");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the secret configuration value", nameof(secretName));
}

string secretValue = _configuration[secretName];
return secretValue;
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Threading.Tasks;
using GuardNet;

namespace Arcus.Security.Core.Providers
{
@@ -22,8 +21,10 @@ public class EnvironmentVariableSecretProvider : ISyncSecretProvider
/// <exception cref="ArgumentException">Thrown when the <paramref name="target"/> is outside the bounds of the enumeration.</exception>
public EnvironmentVariableSecretProvider(EnvironmentVariableTarget target = DefaultTarget, string prefix = null)
{
Guard.For<ArgumentException>(() => !Enum.IsDefined(typeof(EnvironmentVariableTarget), target),
$"Requires an environment variable target of either '{EnvironmentVariableTarget.Process}', '{EnvironmentVariableTarget.Machine}', or '{EnvironmentVariableTarget.User}'");
if (!Enum.IsDefined(typeof(EnvironmentVariableTarget), target))
{
throw new ArgumentException($"Requires an environment variable target of either '{EnvironmentVariableTarget.Process}', '{EnvironmentVariableTarget.Machine}', or '{EnvironmentVariableTarget.User}'", nameof(target));
}

_prefix = prefix ?? String.Empty;
_target = target;
@@ -39,7 +40,10 @@ public EnvironmentVariableSecretProvider(EnvironmentVariableTarget target = Defa
/// <exception cref="T:Arcus.Security.Core.SecretNotFoundException">The secret was not found, using the given name</exception>
public Task<Secret> GetSecretAsync(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the environment secret");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the environment secret", nameof(secretName));
}

Secret secret = GetSecret(secretName);
return Task.FromResult(secret);
@@ -55,7 +59,10 @@ public Task<Secret> GetSecretAsync(string secretName)
/// <exception cref="T:Arcus.Security.Core.SecretNotFoundException">The secret was not found, using the given name</exception>
public Task<string> GetRawSecretAsync(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the environment secret");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the environment secret", nameof(secretName));
}

string secretValue = GetRawSecret(secretName);
return Task.FromResult(secretValue);
@@ -70,7 +77,10 @@ public Task<string> GetRawSecretAsync(string secretName)
/// <exception cref="SecretNotFoundException">Thrown when the secret was not found, using the given name.</exception>
public Secret GetSecret(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the environment secret");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the environment secret", nameof(secretName));
}

string secretValue = GetRawSecret(secretName);
if (secretValue is null)
@@ -90,7 +100,10 @@ public Secret GetSecret(string secretName)
/// <exception cref="SecretNotFoundException">Thrown when the secret was not found, using the given name.</exception>
public string GetRawSecret(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to look up the environment secret");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name to look up the environment secret", nameof(secretName));
}

string environmentVariable = Environment.GetEnvironmentVariable(_prefix + secretName, _target);
return environmentVariable;
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@
using System.Threading.Tasks;
using Arcus.Security.Core.Caching;
using Arcus.Security.Core.Caching.Configuration;
using GuardNet;
using Microsoft.Extensions.Logging;

namespace Arcus.Security.Core.Providers
@@ -29,7 +28,11 @@ public MutatedSecretNameCachedSecretProvider(
ILogger logger)
: base(implementation, mutateSecretName, logger)
{
Guard.NotNull(implementation, nameof(implementation), "Requires an secret provider instance to pass the mutated secret name to");
if (implementation is null)
{
throw new ArgumentNullException(nameof(implementation), "Requires a secret provider instance to pass the mutated")
}

_implementation = implementation;
}

@@ -49,7 +52,10 @@ public MutatedSecretNameCachedSecretProvider(
/// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception>
public async Task<string> GetRawSecretAsync(string secretName, bool ignoreCache)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

string secretValue = await SafeguardMutateSecretAsync(secretName, mutatedSecretName =>
{
@@ -70,7 +76,10 @@ public async Task<string> GetRawSecretAsync(string secretName, bool ignoreCache)
/// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception>
public async Task<Secret> GetSecretAsync(string secretName, bool ignoreCache)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

Secret secret = await SafeguardMutateSecretAsync(secretName, mutatedSecretName =>
{
@@ -87,7 +96,10 @@ public async Task<Secret> GetSecretAsync(string secretName, bool ignoreCache)
/// <param name="secretName">The name of the secret that should be removed from the cache.</param>
public async Task InvalidateSecretAsync(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

await SafeguardMutateSecretAsync(secretName, async mutatedSecretName =>
{
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Threading.Tasks;
using GuardNet;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

@@ -25,9 +24,15 @@ public class MutatedSecretNameSecretProvider : ISyncSecretProvider
/// </exception>
public MutatedSecretNameSecretProvider(ISecretProvider implementation, Func<string, string> mutateSecretName, ILogger logger)
{
Guard.NotNull(implementation, nameof(implementation), "Requires an secret provider instance to pass the mutated secret name to");
Guard.NotNull(mutateSecretName, nameof(mutateSecretName),
"Requires an transformation function to mutate the incoming secret name to something that the actual secret provider can understand");
if (implementation is null)
{
throw new ArgumentNullException(nameof(implementation), "Requires a secret provider instance to pass the mutated");
}

if (mutateSecretName is null)
{
throw new ArgumentNullException(nameof(mutateSecretName), "Requires a transformation function to mutate the incoming secret name to something that the actual secret provider can understand");
}

_mutateSecretName = mutateSecretName;
_implementation = implementation;
@@ -50,7 +55,10 @@ public MutatedSecretNameSecretProvider(ISecretProvider implementation, Func<stri
/// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception>
public async Task<string> GetRawSecretAsync(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

string secretValue = await SafeguardMutateSecretAsync(secretName, mutatedSecretName =>
{
@@ -70,7 +78,10 @@ public async Task<string> GetRawSecretAsync(string secretName)
/// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception>
public async Task<Secret> GetSecretAsync(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

Secret secret = await SafeguardMutateSecretAsync(secretName, mutatedSecretName =>
{
@@ -89,7 +100,10 @@ public async Task<Secret> GetSecretAsync(string secretName)
/// <exception cref="SecretNotFoundException">Thrown when the secret was not found, using the given name.</exception>
public string GetRawSecret(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

string secretValue = SafeguardMutateSecret(secretName, mutatedSecretName =>
{
@@ -108,7 +122,10 @@ public string GetRawSecret(string secretName)
/// <exception cref="SecretNotFoundException">Thrown when the secret was not found, using the given name.</exception>
public Secret GetSecret(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

Secret secret = SafeguardMutateSecret(secretName, mutatedSecretName =>
{
@@ -127,8 +144,15 @@ public Secret GetSecret(string secretName)
/// <exception cref="ArgumentException">Thrown when the <paramref name="asyncFuncAfterMutation"/> is <c>null</c>.</exception>
protected async Task SafeguardMutateSecretAsync(string secretName, Func<string, Task> asyncFuncAfterMutation)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
Guard.NotNull(asyncFuncAfterMutation, nameof(asyncFuncAfterMutation), "Requires a function to run after the secret name mutation");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

if (asyncFuncAfterMutation is null)
{
throw new ArgumentNullException(nameof(asyncFuncAfterMutation), "Requires a function to run after the secret name mutation");
}

await SafeguardMutateSecretAsync(secretName, async mutatedSecretName =>
{
@@ -152,8 +176,15 @@ await SafeguardMutateSecretAsync(secretName, async mutatedSecretName =>
/// <exception cref="ArgumentException">Thrown when the <paramref name="asyncFuncAfterMutation"/> is <c>null</c>.</exception>
protected async Task<T> SafeguardMutateSecretAsync<T>(string secretName, Func<string, Task<T>> asyncFuncAfterMutation)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
Guard.NotNull(asyncFuncAfterMutation, nameof(asyncFuncAfterMutation), "Requires a function to run after the secret name mutation");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

if (asyncFuncAfterMutation is null)
{
throw new ArgumentNullException(nameof(asyncFuncAfterMutation), "Requires a function to run after the secret name mutation");
}

string mutatedSecretName = MutateSecretName(secretName);
Task<T> task = asyncFuncAfterMutation(mutatedSecretName);
@@ -190,8 +221,15 @@ protected async Task<T> SafeguardMutateSecretAsync<T>(string secretName, Func<st
/// <exception cref="ArgumentException">Thrown when the <paramref name="afterMutation"/> is <c>null</c>.</exception>
protected T SafeguardMutateSecret<T>(string secretName, Func<string, T> afterMutation)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
Guard.NotNull(afterMutation, nameof(afterMutation), "Requires a function to run after the secret name mutation");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

if (afterMutation is null)
{
throw new ArgumentNullException(nameof(afterMutation), "Requires a function to run after the secret name mutation");
}

string mutatedSecretName = MutateSecretName(secretName);

@@ -209,7 +247,10 @@ protected T SafeguardMutateSecret<T>(string secretName, Func<string, T> afterMut

private string MutateSecretName(string secretName)
{
Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names");
if (string.IsNullOrWhiteSpace(secretName))
{
throw new ArgumentException("Requires a non-blank secret name when mutating secret names", nameof(secretName));
}

try
{
1 change: 0 additions & 1 deletion src/Arcus.Security.Core/Secret.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using GuardNet;

namespace Arcus.Security.Core
{
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@
using System.Threading.Tasks;
using Arcus.Security.Core;
using Microsoft.Extensions.Configuration;
using GuardNet;

namespace Arcus.Security.Providers.AzureKeyVault.Configuration
{
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Guard.Net" Version="3.0.0" />
<PackageReference Include="Arcus.Testing.Logging.Xunit" Version="1.0.3" />
<PackageReference Include="Arcus.Testing.Security.Providers.InMemory" Version="1.0.3" />
<PackageReference Include="Serilog" Version="2.10.0" />
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using GuardNet;

namespace Arcus.Security.Tests.Core.Fixture
{
@@ -12,7 +11,11 @@ public class TemporaryEnvironmentVariable : IDisposable

private TemporaryEnvironmentVariable(string name)
{
Guard.NotNull(name, nameof(name));
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}

_name = name;
}

@@ -23,7 +26,11 @@ private TemporaryEnvironmentVariable(string name)
/// <param name="value">The value of the environment variable.</param>
public static TemporaryEnvironmentVariable Create(string name, string value)
{
Guard.NotNull(name, nameof(name));
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}

Environment.SetEnvironmentVariable(name, value);

return new TemporaryEnvironmentVariable(name);
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@
using Arcus.Security.Providers.HashiCorp;
using Arcus.Security.Tests.Integration.HashiCorp.Mounting;
using Arcus.Testing;
using GuardNet;
using Microsoft.Extensions.Logging;
using Polly;
using Vault;
@@ -43,10 +42,25 @@ public class HashiCorpVaultTestServer : IDisposable

private HashiCorpVaultTestServer(Process process, string rootToken, string listenAddress, ILogger logger)
{
Guard.NotNull(process, nameof(process));
Guard.NotNullOrWhitespace(rootToken, nameof(rootToken));
Guard.NotNullOrWhitespace(listenAddress, nameof(listenAddress));
Guard.NotNull(logger, nameof(logger));
if (process is null)
{
throw new ArgumentNullException(nameof(process));
}

if (string.IsNullOrWhiteSpace(rootToken))
{
throw new ArgumentException(nameof(rootToken));
}

if (string.IsNullOrWhiteSpace(listenAddress))
{
throw new ArgumentException(nameof(listenAddress));
}

if (logger is null)
{
throw new ArgumentNullException(nameof(logger));
}

_process = process;
_rootToken = rootToken;
@@ -84,10 +98,15 @@ private HashiCorpVaultTestServer(Process process, string rootToken, string liste
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="configuration"/> or <paramref name="logger"/> is <c>null</c>.</exception>
public static async Task<HashiCorpVaultTestServer> StartServerAsync(TestConfig configuration, ILogger logger)
{
Guard.NotNull(logger, nameof(logger),
"Requires a logger for logging diagnostic trace messages during the lifetime of the test server");
Guard.NotNull(configuration, nameof(configuration),
"Requires a configuration instance to retrieve the HashiCorp Vault installation folder");
if (logger is null)
{
throw new ArgumentNullException(nameof(logger), "Requires a logger for logging diagnostic trace messages during the lifetime of the test server");
}

if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration), "Requires a configuration instance to retrieve the HashiCorp Vault installation folder");
}

var rootToken = Guid.NewGuid().ToString();
int port = GetRandomUnusedPort();
@@ -186,8 +205,15 @@ private async Task StartHashiCorpVaultAsync()
/// </exception>
public async Task MountKeyValueAsync(string path, VaultKeyValueSecretEngineVersion version)
{
Guard.NotNullOrWhitespace(path, nameof(path), "Requires a path to mount the KeyValue secret engine to");
Guard.For<ArgumentException>(() => !Enum.IsDefined(typeof(VaultKeyValueSecretEngineVersion), version), "Requires a KeyValue secret engine version that is either V1 or V2");
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("Requires a path to mount the KeyValue secret engine to", nameof(path));
}

if (!Enum.IsDefined(typeof(VaultKeyValueSecretEngineVersion), version))
{
throw new ArgumentException("Requires a KeyValue secret engine version that is either V1 or V2", nameof(version));
}

var content = new MountInfo
{
@@ -211,11 +237,25 @@ public async Task MountKeyValueAsync(string path, VaultKeyValueSecretEngineVersi
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="capabilities"/> is <c>null</c>.</exception>
public async Task AddPolicyAsync(string name, string path, string[] capabilities)
{
Guard.NotNullOrWhitespace(name, nameof(name), "Requires a name to identify the policy");
Guard.NotNullOrWhitespace(path, nameof(path), "Requires a path where the policy will be applicable");
Guard.NotNull(capabilities, nameof(capabilities), "Requires a set of capabilities that should be available in this policy");
Guard.NotAny(capabilities, nameof(capabilities), "Requires a set of capabilities that should be available in this policy");
Guard.For<ArgumentException>(() => capabilities.Any(String.IsNullOrWhiteSpace), "Requires all the capabilities of the policy to be filled out (not blank)");
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Requires a name to identify the policy", nameof(name));
}

if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("Requires a path where the policy will be applicable", nameof(path));
}

if (capabilities is null || !capabilities.Any())
{
throw new ArgumentException("Requires a set of capabilities that should be available in this policy", nameof(capabilities));
}

if (capabilities.Any(string.IsNullOrWhiteSpace))
{
throw new ArgumentException("Requires all the capabilities of the policy to be filled out (not blank)", nameof(capabilities));
}

string joinedCapabilities = String.Join(", ", capabilities.Select(c => $"\"{c}\""));
string rules = $"path \"{path}/*\" {{ capabilities = [ {joinedCapabilities} ]}}";
@@ -231,7 +271,10 @@ public async Task AddPolicyAsync(string name, string path, string[] capabilities
/// <exception cref="ArgumentException">Thrown when the <paramref name="type"/> is blank.</exception>
public async Task EnableAuthenticationTypeAsync(string type, string description)
{
Guard.NotNullOrWhitespace(type, nameof(type), "Requires an authentication type to enable the authentication");
if (string.IsNullOrWhiteSpace(type))
{
throw new ArgumentException("Requires an authentication type to enable the authentication", nameof(type));
}

await _systemEndpoint.EnableAuth(path: type, authType: type, description: description);
}
@@ -246,9 +289,20 @@ public async Task EnableAuthenticationTypeAsync(string type, string description)
/// <exception cref="ArgumentException">Thrown when the <paramref name="username"/>, <paramref name="password"/>, or <paramref name="policyName"/> is blank.</exception>
public async Task AddUserPassUserAsync(string username, string password, string policyName)
{
Guard.NotNullOrWhitespace(username, nameof(username));
Guard.NotNullOrWhitespace(password, nameof(password));
Guard.NotNullOrWhitespace(policyName, nameof(policyName));
if (string.IsNullOrWhiteSpace(username))
{
throw new ArgumentException("Requires a non-blank user name", nameof(username));
}

if (string.IsNullOrWhiteSpace(password))
{
throw new ArgumentException("Requires a non-blank password", nameof(password));
}

if (string.IsNullOrWhiteSpace(policyName))
{
throw new ArgumentException("Requires a non-blank policy name", nameof(policyName));
}

await _authenticationEndpoint.Write($"/userpass/users/{username}", new UsersRequest
{
@@ -292,9 +346,20 @@ private void StopHashiCorpVault()

protected PolicyResult RetryAction(Action action, int timeoutInSeconds = 30, int retryIntervalInSeconds = 1)
{
Guard.NotNull(action, nameof(action), "Requires disposing function to be retried");
Guard.NotLessThan(timeoutInSeconds, 0, nameof(timeoutInSeconds), "Requires a timeout (in sec) greater than zero");
Guard.NotLessThan(retryIntervalInSeconds, 0, nameof(retryIntervalInSeconds), "Requires a retry interval (in sec) greater than zero");
if (action is null)
{
throw new ArgumentNullException(nameof(action), "Requires disposing function to be retried");
}

if (timeoutInSeconds < 0)
{
throw new ArgumentOutOfRangeException(nameof(timeoutInSeconds), "Requires a timeout (in sec) greater than zero");
}

if (retryIntervalInSeconds < 0)
{
throw new ArgumentOutOfRangeException(nameof(retryIntervalInSeconds), "Requires a retry interval (in sec) greater than zero");
}

return Policy.Timeout(TimeSpan.FromSeconds(timeoutInSeconds))
.Wrap(Policy.Handle<Exception>()
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@
using System.Net;
using System.Threading.Tasks;
using Arcus.Security.Core;
using GuardNet;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
@@ -24,7 +23,11 @@ public class OrderFunction
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="secretProvider"/> is <c>null</c>.</exception>
public OrderFunction(ISecretProvider secretProvider, ILogger<OrderFunction> logger)
{
Guard.NotNull(secretProvider, nameof(secretProvider), "Requires a secret provider instance");
if (secretProvider is null)
{
throw new ArgumentNullException(nameof(secretProvider), "Requires a secret provider instance");
}

_secretProvider = secretProvider;
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Threading.Tasks;
using Arcus.Security.Core;
using GuardNet;

namespace Arcus.Security.Tests.Unit.Core.Stubs
{
@@ -19,7 +18,11 @@ public class SaboteurSecretProvider: ISyncSecretProvider
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="exception"/> is <c>null</c>.</exception>
public SaboteurSecretProvider(Exception exception)
{
Guard.NotNull(exception, nameof(exception), "Requires an specific exception to sabotage the secret retrieval");
if (exception is null)
{
throw new ArgumentNullException(nameof(exception), "Requires a specific exception to sabotage the secret retrieval");
}

_exception = exception;
}