aboutsummaryrefslogtreecommitdiff
path: root/lib/VNLib.Plugins.Extensions.Loading/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.Loading/src')
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs18
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationValidationException.cs (renamed from lib/VNLib.Plugins.Extensions.Loading/src/ConfigrationValidationException.cs)12
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs111
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs211
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs7
5 files changed, 235 insertions, 124 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs
index 0ae6ed6..190c153 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationExtensions.cs
@@ -221,12 +221,12 @@ namespace VNLib.Plugins.Extensions.Loading
/// <summary>
/// Deserialzes the configuration to the desired object and calls its
/// <see cref="IOnConfigValidation.Validate"/> method. Validation exceptions
- /// are wrapped in a <see cref="ConfigrationValidationException"/>
+ /// are wrapped in a <see cref="ConfigurationValidationException"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="scope"></param>
/// <returns></returns>
- /// <exception cref="ConfigrationValidationException"></exception>
+ /// <exception cref="ConfigurationValidationException"></exception>
public static T DeserialzeAndValidate<T>(this IConfigScope scope) where T : IOnConfigValidation
{
T conf = scope.Deserialze<T>();
@@ -236,7 +236,7 @@ namespace VNLib.Plugins.Extensions.Loading
}
catch(Exception ex)
{
- throw new ConfigrationValidationException($"Configuration validation failed for type {typeof(T).Name}", ex);
+ throw new ConfigurationValidationException($"Configuration validation failed for type {typeof(T).Name}", ex);
}
return conf;
}
@@ -261,7 +261,7 @@ namespace VNLib.Plugins.Extensions.Loading
/// and deserializes it into the desired type.
/// <para>
/// If the type inherits <see cref="IOnConfigValidation"/> the <see cref="IOnConfigValidation.Validate"/>
- /// method is invoked, and exceptions are warpped in <see cref="ConfigrationValidationException"/>
+ /// method is invoked, and exceptions are warpped in <see cref="ConfigurationValidationException"/>
/// </para>
/// <para>
/// If the type inherits <see cref="IAsyncConfigurable"/> the <see cref="IAsyncConfigurable.ConfigureServiceAsync(PluginBase)"/>
@@ -271,7 +271,7 @@ namespace VNLib.Plugins.Extensions.Loading
/// <typeparam name="TConfig">The configuration type</typeparam>
/// <param name="plugin"></param>
/// <returns>The deserialzed configuration element</returns>
- /// <exception cref="ConfigrationValidationException"></exception>
+ /// <exception cref="ConfigurationValidationException"></exception>
public static TConfig GetConfigElement<TConfig>(this PluginBase plugin)
{
//Deserialze the element
@@ -286,7 +286,7 @@ namespace VNLib.Plugins.Extensions.Loading
}
catch (Exception ex)
{
- throw new ConfigrationValidationException($"Configuration validation failed for type {typeof(TConfig).Name}", ex);
+ throw new ConfigurationValidationException($"Configuration validation failed for type {typeof(TConfig).Name}", ex);
}
}
@@ -304,7 +304,7 @@ namespace VNLib.Plugins.Extensions.Loading
/// and deserializes it into the desired type.
/// <para>
/// If the type inherits <see cref="IOnConfigValidation"/> the <see cref="IOnConfigValidation.Validate"/>
- /// method is invoked, and exceptions are warpped in <see cref="ConfigrationValidationException"/>
+ /// method is invoked, and exceptions are warpped in <see cref="ConfigurationValidationException"/>
/// </para>
/// <para>
/// If the type inherits <see cref="IAsyncConfigurable"/> the <see cref="IAsyncConfigurable.ConfigureServiceAsync(PluginBase)"/>
@@ -315,7 +315,7 @@ namespace VNLib.Plugins.Extensions.Loading
/// <param name="plugin"></param>
/// <param name="elementName">The configuration element name override</param>
/// <returns>The deserialzed configuration element</returns>
- /// <exception cref="ConfigrationValidationException"></exception>
+ /// <exception cref="ConfigurationValidationException"></exception>
public static TConfig GetConfigElement<TConfig>(this PluginBase plugin, string elementName)
{
//Deserialze the element
@@ -330,7 +330,7 @@ namespace VNLib.Plugins.Extensions.Loading
}
catch (Exception ex)
{
- throw new ConfigrationValidationException($"Configuration validation failed for type {typeof(TConfig).Name}", ex);
+ throw new ConfigurationValidationException($"Configuration validation failed for type {typeof(TConfig).Name}", ex);
}
}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/ConfigrationValidationException.cs b/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationValidationException.cs
index 83ce558..ebf4d9e 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/ConfigrationValidationException.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/ConfigurationValidationException.cs
@@ -3,9 +3,9 @@
*
* Library: VNLib
* Package: VNLib.Plugins.Extensions.Loading
-* File: ConfigrationValidationException.cs
+* File: ConfigurationValidationException.cs
*
-* ConfigrationValidationException.cs is part of VNLib.Plugins.Extensions.Loading which is part of the larger
+* ConfigurationValidationException.cs is part of VNLib.Plugins.Extensions.Loading which is part of the larger
* VNLib collection of libraries and utilities.
*
* VNLib.Plugins.Extensions.Loading is free software: you can redistribute it and/or modify
@@ -29,14 +29,14 @@ namespace VNLib.Plugins.Extensions.Loading
/// <summary>
/// An exception raised when a configuration validation exception has occured
/// </summary>
- public class ConfigrationValidationException : Exception
+ public class ConfigurationValidationException : Exception
{
- public ConfigrationValidationException(string message) : base(message)
+ public ConfigurationValidationException(string message) : base(message)
{}
- public ConfigrationValidationException(string message, Exception innerException) : base(message, innerException)
+ public ConfigurationValidationException(string message, Exception innerException) : base(message, innerException)
{}
- public ConfigrationValidationException()
+ public ConfigurationValidationException()
{}
}
}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs
index 3e151b4..5511398 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs
@@ -32,14 +32,11 @@ using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
-using VNLib.Utils;
-using VNLib.Utils.Memory;
using VNLib.Utils.Logging;
using VNLib.Utils.Extensions;
-using VNLib.Plugins.Essentials.Accounts;
namespace VNLib.Plugins.Extensions.Loading
-{
+{
/// <summary>
/// Provides common loading (and unloading when required) extensions for plugins
@@ -54,6 +51,7 @@ namespace VNLib.Plugins.Extensions.Loading
public const string DEBUG_CONFIG_KEY = "debug";
public const string SECRETS_CONFIG_KEY = "secrets";
public const string PASSWORD_HASHING_KEY = "passwords";
+ public const string CUSTOM_PASSWORD_ASM_KEY = "custom_asm";
/*
* Plugin local cache used for storing singletons for a plugin instance
@@ -95,23 +93,6 @@ namespace VNLib.Plugins.Extensions.Loading
public static T GetOrCreateSingleton<T>(PluginBase plugin, Func<PluginBase, T> serviceFactory)
=> (T)GetOrCreateSingleton(plugin, typeof(T), p => serviceFactory(p)!);
-
- /// <summary>
- /// Gets the plugins ambient <see cref="PasswordHashing"/> if loaded, or loads it if required. This class will
- /// be unloaded when the plugin us unloaded.
- /// </summary>
- /// <param name="plugin"></param>
- /// <returns>The ambient <see cref="PasswordHashing"/></returns>
- /// <exception cref="OverflowException"></exception>
- /// <exception cref="KeyNotFoundException"></exception>
- /// <exception cref="ObjectDisposedException"></exception>
- public static IPasswordHashingProvider GetPasswords(this PluginBase plugin)
- {
- plugin.ThrowIfUnloaded();
- //Check if a password configuration element is loaded, otherwise load with defaults
- return plugin.GetOrCreateSingleton<SecretProvider>().Passwords;
- }
-
/// <summary>
/// Loads an assembly into the current plugin's load context and will unload when disposed
/// or the plugin is unloaded from the host application.
@@ -551,93 +532,5 @@ namespace VNLib.Plugins.Extensions.Loading
return lazyFactory;
}
}
-
- [ConfigurationName(PASSWORD_HASHING_KEY, Required = false)]
- private sealed class SecretProvider : VnDisposeable, ISecretProvider, IAsyncConfigurable
- {
- private byte[]? _pepper;
- private Exception? _error;
-
- public SecretProvider(PluginBase plugin, IConfigScope config)
- {
- if(config.TryGetValue("args", out JsonElement el))
- {
- //Convert to dict
- IReadOnlyDictionary<string, JsonElement> hashingArgs = el.EnumerateObject().ToDictionary(static k => k.Name, static v => v.Value);
-
- //Get hashing arguments
- uint saltLen = hashingArgs["salt_len"].GetUInt32();
- uint hashLen = hashingArgs["hash_len"].GetUInt32();
- uint timeCost = hashingArgs["time_cost"].GetUInt32();
- uint memoryCost = hashingArgs["memory_cost"].GetUInt32();
- uint parallelism = hashingArgs["parallelism"].GetUInt32();
- //Load passwords
- Passwords = new(this, (int)saltLen, timeCost, memoryCost, parallelism, hashLen);
- }
- else
- {
- Passwords = new(this);
- }
- }
-
- public SecretProvider(PluginBase plugin)
- {
- Passwords = new(this);
- }
-
-
- public PasswordHashing Passwords { get; }
-
- ///<inheritdoc/>
- public int BufferSize
- {
- get
- {
- Check();
- return _pepper!.Length;
- }
- }
-
- public ERRNO GetSecret(Span<byte> buffer)
- {
- Check();
- //Coppy pepper to buffer
- _pepper.CopyTo(buffer);
- //Return pepper length
- return _pepper!.Length;
- }
-
- protected override void Check()
- {
- base.Check();
- if(_error != null)
- {
- throw _error;
- }
- }
-
- protected override void Free()
- {
- //Clear the pepper if set
- MemoryUtil.InitializeBlock(_pepper.AsSpan());
- }
-
- public async Task ConfigureServiceAsync(PluginBase plugin)
- {
- try
- {
- //Get the pepper from secret storage
- _pepper = await plugin.TryGetSecretAsync(PASSWORD_HASHING_KEY).ToBase64Bytes();
- }
- catch (Exception ex)
- {
- //Store exception for re-propagation
- _error = ex;
-
- //Propagate exception to system
- throw;
- }
- }
- }
}
}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs b/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs
new file mode 100644
index 0000000..522bfae
--- /dev/null
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs
@@ -0,0 +1,211 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Extensions.Loading
+* File: ManagedPasswordHashing.cs
+*
+* ManagedPasswordHashing.cs is part of VNLib.Plugins.Extensions.Loading which
+* is part of the larger VNLib collection of libraries and utilities.
+*
+* VNLib.Plugins.Extensions.Loading is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* VNLib.Plugins.Extensions.Loading is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see https://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+using VNLib.Utils;
+using VNLib.Utils.Memory;
+using VNLib.Utils.Extensions;
+using VNLib.Plugins.Essentials.Accounts;
+
+namespace VNLib.Plugins.Extensions.Loading
+{
+
+ /// <summary>
+ /// A plugin configurable <see cref="IPasswordHashingProvider"/> managed implementation. Users may load custom
+ /// assemblies backing instances of this class or configure the <see cref="PasswordHashing"/> implementation
+ /// </summary>
+ [ConfigurationName(LoadingExtensions.PASSWORD_HASHING_KEY, Required = false)]
+ public sealed class ManagedPasswordHashing : IPasswordHashingProvider
+ {
+ public ManagedPasswordHashing(PluginBase plugin, IConfigScope config)
+ {
+ //Check for custom hashing assembly
+ if (config.TryGetValue(LoadingExtensions.CUSTOM_PASSWORD_ASM_KEY, out JsonElement el))
+ {
+ string customAsm = el.GetString() ?? throw new KeyNotFoundException("You must specify a string file path for your custom password hashing assembly");
+
+ //Load the custom assembly
+ AssemblyLoader<IPasswordHashingProvider> prov = plugin.LoadAssembly<IPasswordHashingProvider>(customAsm);
+
+ //Configure async
+ if (prov.Resource is IAsyncConfigurable ac)
+ {
+ //Configure async
+ _ = plugin.ConfigureServiceAsync(ac);
+ }
+
+ //Store
+ Passwords = new CustomPasswordHashingAsm(prov);
+ }
+ else
+ {
+ Passwords = plugin.GetOrCreateSingleton<SecretProvider>().Passwords;
+ }
+ }
+
+ public ManagedPasswordHashing(PluginBase plugin)
+ {
+ //Only configure a default password impl
+ Passwords = plugin.GetOrCreateSingleton<SecretProvider>().Passwords;
+ }
+
+ /// <summary>
+ /// The underlying <see cref="IPasswordHashingProvider"/>
+ /// </summary>
+ public IPasswordHashingProvider Passwords { get; }
+
+ ///<inheritdoc/>
+ public bool Verify(ReadOnlySpan<char> passHash, ReadOnlySpan<char> password) => Passwords.Verify(passHash, password);
+
+ ///<inheritdoc/>
+ public bool Verify(ReadOnlySpan<byte> passHash, ReadOnlySpan<byte> password) => Passwords.Verify(passHash, password);
+
+ ///<inheritdoc/>
+ public PrivateString Hash(ReadOnlySpan<char> password) => Passwords.Hash(password);
+
+ ///<inheritdoc/>
+ public PrivateString Hash(ReadOnlySpan<byte> password) => Passwords.Hash(password);
+
+ ///<inheritdoc/>
+ public ERRNO Hash(ReadOnlySpan<byte> password, Span<byte> hashOutput) => Passwords.Hash(password, hashOutput);
+
+ sealed class CustomPasswordHashingAsm : IPasswordHashingProvider
+ {
+ private readonly AssemblyLoader<IPasswordHashingProvider> _loader;
+
+ public CustomPasswordHashingAsm(AssemblyLoader<IPasswordHashingProvider> loader)
+ {
+ _loader = loader;
+ }
+
+ /*
+ * Password hashing isnt a super high performance system
+ * so adding method overhead shouldnt be a large issue for the
+ * asm wrapper providing unload protection
+ */
+
+ public PrivateString Hash(ReadOnlySpan<char> password) => _loader.Resource.Hash(password);
+
+ public PrivateString Hash(ReadOnlySpan<byte> password) => _loader.Resource.Hash(password);
+
+ public ERRNO Hash(ReadOnlySpan<byte> password, Span<byte> hashOutput) => _loader.Resource.Hash(password, hashOutput);
+
+ public bool Verify(ReadOnlySpan<char> passHash, ReadOnlySpan<char> password) => _loader.Resource.Verify(passHash, password);
+
+ public bool Verify(ReadOnlySpan<byte> passHash, ReadOnlySpan<byte> password) => _loader.Resource.Verify(passHash, password);
+ }
+
+ private sealed class SecretProvider : VnDisposeable, ISecretProvider, IAsyncConfigurable
+ {
+ private byte[]? _pepper;
+ private Exception? _error;
+
+ public SecretProvider(PluginBase plugin, IConfigScope config)
+ {
+ if (config.TryGetValue("args", out JsonElement el))
+ {
+ //Convert to dict
+ IReadOnlyDictionary<string, JsonElement> hashingArgs = el.EnumerateObject().ToDictionary(static k => k.Name, static v => v.Value);
+
+ //Get hashing arguments
+ uint saltLen = hashingArgs["salt_len"].GetUInt32();
+ uint hashLen = hashingArgs["hash_len"].GetUInt32();
+ uint timeCost = hashingArgs["time_cost"].GetUInt32();
+ uint memoryCost = hashingArgs["memory_cost"].GetUInt32();
+ uint parallelism = hashingArgs["parallelism"].GetUInt32();
+ //Load passwords
+ Passwords = new(this, (int)saltLen, timeCost, memoryCost, parallelism, hashLen);
+ }
+ else
+ {
+ Passwords = new(this);
+ }
+ }
+
+ public SecretProvider(PluginBase plugin)
+ {
+ Passwords = new(this);
+ }
+
+
+ public PasswordHashing Passwords { get; }
+
+ ///<inheritdoc/>
+ public int BufferSize
+ {
+ get
+ {
+ Check();
+ return _pepper!.Length;
+ }
+ }
+
+ public ERRNO GetSecret(Span<byte> buffer)
+ {
+ Check();
+ //Coppy pepper to buffer
+ _pepper.CopyTo(buffer);
+ //Return pepper length
+ return _pepper!.Length;
+ }
+
+ protected override void Check()
+ {
+ base.Check();
+ if (_error != null)
+ {
+ throw _error;
+ }
+ }
+
+ protected override void Free()
+ {
+ //Clear the pepper if set
+ MemoryUtil.InitializeBlock(_pepper.AsSpan());
+ }
+
+ public async Task ConfigureServiceAsync(PluginBase plugin)
+ {
+ try
+ {
+ //Get the pepper from secret storage
+ _pepper = await plugin.TryGetSecretAsync(LoadingExtensions.PASSWORD_HASHING_KEY).ToBase64Bytes();
+ }
+ catch (Exception ex)
+ {
+ //Store exception for re-propagation
+ _error = ex;
+
+ //Propagate exception to system
+ throw;
+ }
+ }
+ }
+ }
+}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs b/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs
index 4cf1c9d..9e3c222 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs
@@ -56,6 +56,7 @@ namespace VNLib.Plugins.Extensions.Loading
public const string VAULT_TOKEN_KEY = "token";
public const string VAULT_ROLE_KEY = "role";
public const string VAULT_SECRET_KEY = "secret";
+ public const string VAULT_TOKNE_ENV_NAME = "VNLIB_PLUGINS_VAULT_TOKEN";
public const string VAULT_URL_KEY = "url";
@@ -306,6 +307,12 @@ namespace VNLib.Plugins.Extensions.Loading
{
authMethod = new AppRoleAuthMethodInfo(roleEl.GetString(), secretEl.GetString());
}
+ //Try to get the token as an environment variable
+ else if(Environment.GetEnvironmentVariable(VAULT_TOKNE_ENV_NAME) != null)
+ {
+ string tokenValue = Environment.GetEnvironmentVariable(VAULT_TOKNE_ENV_NAME)!;
+ authMethod = new TokenAuthMethodInfo(tokenValue);
+ }
else
{
throw new KeyNotFoundException($"Failed to load the vault authentication method from {VAULT_OBJECT_NAME}");