aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj13
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj14
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/IAsyncLazy.cs9
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs29
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Routing/MiddlewareHelpers.cs70
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Secrets/HCVaultClient.cs151
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Secrets/IKvVaultClient.cs (renamed from lib/VNLib.Plugins.Extensions.Loading/src/Secrets/IHCVaultClient.cs)8
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Secrets/OnDemandSecret.cs15
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretConstants.cs2
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretStore.cs23
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj12
-rw-r--r--lib/VNLib.Plugins.Extensions.Validation/src/VNLib.Plugins.Extensions.Validation.csproj12
-rw-r--r--lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MYSql.csproj8
-rw-r--r--lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MySQL.csproj8
-rw-r--r--lib/sql-providers/sqlite/VNLib.Plugins.Extensions.Loading.Sql.SQLite/src/VNLib.Plugins.Extensions.Loading.Sql.SQLite.csproj10
-rw-r--r--lib/sql-providers/sqlserver/VNLib.Plugins.Extensions.Loading.Sql.SQLServer/src/VNLib.Plugins.Extensions.Loading.Sql.SQLServer.csproj10
16 files changed, 241 insertions, 153 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj b/lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj
index 465864d..c957b26 100644
--- a/lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj
+++ b/lib/VNLib.Plugins.Extensions.Data/src/VNLib.Plugins.Extensions.Data.csproj
@@ -2,14 +2,16 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<RootNamespace>VNLib.Plugins.Extensions.Data</RootNamespace>
<AssemblyName>VNLib.Plugins.Extensions.Data</AssemblyName>
- <Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
- <AnalysisLevel>latest-all</AnalysisLevel>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
+ <PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
<PropertyGroup>
<Authors>Vaughn Nugent</Authors>
@@ -20,12 +22,11 @@
<Copyright>Copyright © 2024 Vaughn Nugent</Copyright>
<PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Plugins.Extensions</PackageProjectUrl>
<RepositoryUrl>https://github.com/VnUgE/VNLib.Plugins.Extensions/tree/master/lib/VNLib.Plugins.Extensions.Data</RepositoryUrl>
- </PropertyGroup>
-
- <PropertyGroup>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
+
<ItemGroup>
<None Include="..\..\..\LICENSE">
<Pack>True</Pack>
@@ -47,7 +48,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.5" />
</ItemGroup>
<ItemGroup>
diff --git a/lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj b/lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj
index 9df509b..5292a5d 100644
--- a/lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj
+++ b/lib/VNLib.Plugins.Extensions.Loading.Sql/src/VNLib.Plugins.Extensions.Loading.Sql.csproj
@@ -2,13 +2,16 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<RootNamespace>VNLib.Plugins.Extensions.Loading.Sql</RootNamespace>
<AssemblyName>VNLib.Plugins.Extensions.Loading.Sql</AssemblyName>
- <Nullable>enable</Nullable>
- <AnalysisLevel>latest-all</AnalysisLevel>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
+
+ <PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
<PropertyGroup>
<Authors>Vaughn Nugent</Authors>
@@ -19,12 +22,11 @@
<Copyright>Copyright © 2024 Vaughn Nugent</Copyright>
<PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Plugins.Extensions</PackageProjectUrl>
<RepositoryUrl>https://github.com/VnUgE/VNLib.Plugins.Extensions/tree/master/lib/VNLib.Plugins.Extensions.Loading.Sql</RepositoryUrl>
- </PropertyGroup>
-
- <PropertyGroup>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
+
<ItemGroup>
<None Include="..\..\..\LICENSE">
<Pack>True</Pack>
@@ -38,7 +40,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.5" />
</ItemGroup>
<ItemGroup>
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/IAsyncLazy.cs b/lib/VNLib.Plugins.Extensions.Loading/src/IAsyncLazy.cs
index 482785c..cb907b9 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/IAsyncLazy.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/IAsyncLazy.cs
@@ -52,6 +52,12 @@ namespace VNLib.Plugins.Extensions.Loading
/// If the operation has not completed, throws an exception.
/// </summary>
T Value { get; }
+
+ /// <summary>
+ /// Gets or allocates a task that represents the async result
+ /// </summary>
+ /// <returns>A task that represents the asynchronous lazy result that completes with the resulting value</returns>
+ Task<T> AsTask();
}
/// <summary>
@@ -141,6 +147,9 @@ namespace VNLib.Plugins.Extensions.Loading
///<inheritdoc/>
public TaskAwaiter<T> GetAwaiter() => _task.GetAwaiter();
+
+ ///<inheritdoc/>
+ public Task<T> AsTask() => _task;
}
#nullable enable
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs b/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs
index 28b3a08..3718ea5 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Extensions.Loading
@@ -25,11 +25,13 @@
using System;
using System.Linq;
using System.Text.Json;
+using System.Threading.Tasks;
using System.Collections.Generic;
using VNLib.Hashing;
using VNLib.Utils;
using VNLib.Utils.Memory;
+using VNLib.Utils.Logging;
using VNLib.Utils.Extensions;
using VNLib.Plugins.Essentials.Accounts;
@@ -123,7 +125,11 @@ namespace VNLib.Plugins.Extensions.Loading
if(config.TryGetValue("lib_path", out JsonElement manualLibPath))
{
- SafeArgon2Library lib = VnArgon2.LoadCustomLibrary(manualLibPath.GetString()!, System.Runtime.InteropServices.DllImportSearchPath.SafeDirectories);
+ SafeArgon2Library lib = VnArgon2.LoadCustomLibrary(
+ manualLibPath.GetString()!,
+ System.Runtime.InteropServices.DllImportSearchPath.SafeDirectories
+ );
+
_ = plugin.RegisterForUnload(lib.Dispose);
safeLib = lib;
}
@@ -154,6 +160,15 @@ namespace VNLib.Plugins.Extensions.Loading
//Get the pepper from secret storage
_pepper = plugin.GetSecretAsync(LoadingExtensions.PASSWORD_HASHING_KEY)
.ToLazy(static sr => sr.GetFromBase64());
+
+ _ = _pepper.AsTask()
+ .ContinueWith(secT => {
+ plugin.Log.Error("Failed to load password pepper: {reason}", secT.Exception?.Message);
+ },
+ default,
+ TaskContinuationOptions.OnlyOnFaulted, //Only run if an exception occured to notify the user during startup
+ TaskScheduler.Default
+ );
}
public SecretProvider(PluginBase plugin)
@@ -163,7 +178,8 @@ namespace VNLib.Plugins.Extensions.Loading
//Get the pepper from secret storage
_pepper = plugin.GetSecretAsync(LoadingExtensions.PASSWORD_HASHING_KEY)
- .ToLazy(static sr => sr.GetFromBase64());
+ .ToBase64Bytes()
+ .AsLazy();
}
///<inheritdoc/>
@@ -193,9 +209,10 @@ namespace VNLib.Plugins.Extensions.Loading
protected override void Free()
{
- if (_pepper.Completed)
- {
- //Clear the pepper if set
+ Task pepperTask = _pepper.AsTask();
+ //Only zero pepper value if the pepper was retrieved successfully
+ if (pepperTask.IsCompletedSuccessfully)
+ {
MemoryUtil.InitializeBlock(_pepper.Value.AsSpan());
}
}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Routing/MiddlewareHelpers.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/MiddlewareHelpers.cs
new file mode 100644
index 0000000..6a0d848
--- /dev/null
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/MiddlewareHelpers.cs
@@ -0,0 +1,70 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Extensions.Loading
+* File: MiddlewareHelpers.cs
+*
+* MiddlewareHelpers.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.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+using VNLib.Utils.Extensions;
+using VNLib.Plugins.Essentials.Middleware;
+
+namespace VNLib.Plugins.Extensions.Loading.Routing
+{
+ /// <summary>
+ /// Provides helper extensions for http middleware
+ /// </summary>
+ public static class MiddlewareHelpers
+ {
+ private static readonly ConditionalWeakTable<PluginBase, List<IHttpMiddleware>> _pluginMiddlewareList = new();
+
+ /// <summary>
+ /// Exports a single middlware instance to the collection for the plugin.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="plugin"></param>
+ /// <param name="instances">A params array of middleware instances to export to the plugin</param>
+ /// <remarks>
+ /// WARNING: Adding middleware arrays explicitly to the plugin service pool will override
+ /// this function. All instances must be exposed though this function
+ /// </remarks>
+ public static void ExportMiddleware<T>(this PluginBase plugin, params T[] instances) where T : IHttpMiddleware
+ {
+ /*
+ * The runtime accepts an enumeration of IHttpMiddleware instances, so
+ * a list can just be exported as an enumerable instance
+ */
+ static List<IHttpMiddleware> OnCreate(PluginBase plugin)
+ {
+ List<IHttpMiddleware> collection = new(1);
+ plugin.ExportService<IEnumerable<IHttpMiddleware>>(collection);
+ return collection;
+ }
+
+ //Get the endpoint collection for the current plugin
+ List<IHttpMiddleware> middlewares = _pluginMiddlewareList.GetValue(plugin, OnCreate);
+
+ //Add the endpoint to the collection
+ instances.ForEach(mw => middlewares.Add(mw));
+ }
+ }
+}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/HCVaultClient.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/HCVaultClient.cs
index a06f490..885f22f 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/HCVaultClient.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/HCVaultClient.cs
@@ -30,7 +30,6 @@ using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Net.Security;
-using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Text.Json.Serialization;
@@ -50,7 +49,12 @@ using VNLib.Utils.Extensions;
namespace VNLib.Plugins.Extensions.Loading
{
- internal sealed class HCVaultClient : VnDisposeable, IHCVaultClient
+
+ /// <summary>
+ /// A concret implementation of a Hashicorp Vault client instance used to
+ /// retrieve key-value secrets from a server
+ /// </summary>
+ public sealed class HCVaultClient : VnDisposeable, IKvVaultClient
{
const string VaultTokenHeaderName = "X-Vault-Token";
const long MaxErrResponseContentLength = 8192;
@@ -62,7 +66,7 @@ namespace VNLib.Plugins.Extensions.Loading
private readonly int _kvVersion;
private readonly IUnmangedHeap _bufferHeap;
- HCVaultClient(string serverAddress, string hcToken, int kvVersion, bool trustCert, IUnmangedHeap heap)
+ private HCVaultClient(string serverAddress, string hcToken, int kvVersion, bool trustCert, IUnmangedHeap heap)
{
#pragma warning disable CA2000 // Dispose objects before losing scope
HttpClientHandler handler = new()
@@ -99,17 +103,17 @@ namespace VNLib.Plugins.Extensions.Loading
/// Creates a new Hashicorp vault client with the given server address, token, and KV storage version
/// </summary>
/// <param name="serverAddress">The vault server address</param>
- /// <param name="hcToken">The vault token used to connect to the vault server</param>
+ /// <param name="token">The vault token used to connect to the vault server</param>
/// <param name="kvVersion">The hc vault Key value store version (must be 1 or 2)</param>
/// <param name="trustCert">A value that tells the HTTP client to trust the Vault server's certificate even if it's not valid</param>
/// <param name="heap">Heap instance to allocate internal buffers from</param>
/// <returns>The new client instance</returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
- public static HCVaultClient Create(string serverAddress, string hcToken, int kvVersion, bool trustCert, IUnmangedHeap heap)
+ public static HCVaultClient Create(string serverAddress, string token, int kvVersion, bool trustCert, IUnmangedHeap heap)
{
ArgumentException.ThrowIfNullOrEmpty(serverAddress);
- ArgumentException.ThrowIfNullOrEmpty(hcToken);
+ ArgumentException.ThrowIfNullOrEmpty(token);
ArgumentNullException.ThrowIfNull(heap);
if(kvVersion != 1 && kvVersion != 2)
@@ -117,7 +121,29 @@ namespace VNLib.Plugins.Extensions.Loading
throw new ArgumentException($"Unsupported vault KV storage version {kvVersion}, must be either 1 or 2");
}
- return new HCVaultClient(serverAddress, hcToken, kvVersion, trustCert, heap);
+ return new HCVaultClient(serverAddress, token, kvVersion, trustCert, heap);
+ }
+
+ /// <summary>
+ /// Creates a new Hashicorp vault client from the default Vault environment
+ /// variables VAULT_ADDR and VAULT_TOKEN. From client documentation
+ /// </summary>
+ /// <param name="kvVersion">The hc vault Key value store version (must be 1 or 2)</param>
+ /// <param name="trustCert">A value that tells the HTTP client to trust the Vault server's certificate even if it's not valid</param>
+ /// <param name="heap">Heap instance to allocate internal buffers from</param>
+ /// <returns>The new client instance</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="KeyNotFoundException"></exception>
+ public static HCVaultClient CreateFromEnv(int kvVersion, bool trustCert, IUnmangedHeap heap)
+ {
+ string address = Environment.GetEnvironmentVariable("VAULT_ADDR")
+ ?? throw new KeyNotFoundException("VAULT_ADDR environment variable not found");
+
+ string token = Environment.GetEnvironmentVariable("VAULT_TOKEN")
+ ?? throw new KeyNotFoundException("VAULT_TOKEN environment variable not found");
+
+ return Create(address, token, kvVersion, trustCert, heap);
}
///<inheritdoc/>
@@ -137,10 +163,10 @@ namespace VNLib.Plugins.Extensions.Loading
using HttpResponseMessage response = await _client.SendAsync(ms, HttpCompletionOption.ResponseHeadersRead);
//Check if an error occured in the response
- await ProcessVaultErrorResponseAsync(response, true);
+ await ProcessVaultErrorResponseAsync(response);
//Read the response async
- using SecretResponse res = await ReadSecretResponse(response.Content, true);
+ using SecretResponse res = await ReadSecretResponse(response.Content);
return FromResponse(res, secretName);
}
@@ -160,90 +186,42 @@ namespace VNLib.Plugins.Extensions.Loading
}
///<inheritdoc/>
+ ///<exception cref="TimeoutException"></exception>
public ISecretResult? ReadSecret(string path, string mountPoint, string secretName)
{
- string secretPath = GetSecretPathForKvVersion(_kvVersion, path, mountPoint);
- using HttpRequestMessage ms = GetRequestMessageForPath(secretPath);
-
- try
- {
- //Exec the response synchronously
- using HttpResponseMessage response = _client.Send(ms, HttpCompletionOption.ResponseHeadersRead);
-
- /*
- * It is safe to await the error result here because its
- * already completed when the async flag is false
- */
- ValueTask errTask = ProcessVaultErrorResponseAsync(response, false);
- Debug.Assert(errTask.IsCompleted);
- errTask.GetAwaiter().GetResult();
-
- //Did not throw, handle a secret response
-
- ValueTask<SecretResponse> resTask = ReadSecretResponse(response.Content, false);
- Debug.Assert(resTask.IsCompleted);
-
- //Always wrap response in using to clean memory
- using SecretResponse res = resTask.GetAwaiter().GetResult();
+ /*
+ * Since this method will syncrhonously block the calling thread, a new
+ * task must be created to ignore the current async context and run the
+ * funciton in an new context to block safely without causing a deadlock.
+ */
- return FromResponse(res, secretName);
- }
- catch (HttpRequestException he) when (he.InnerException is SocketException se)
- {
- throw se.SocketErrorCode switch
- {
- SocketError.HostNotFound => new HCVaultException("Failed to connect to Hashicorp Vault server, because it's DNS hostname could not be resolved"),
- SocketError.ConnectionRefused => new HCVaultException("Failed to establish a TCP connection to the vault server, the server refused the connection"),
- _ => new HCVaultException("Failed to establish a TCP connection to the vault server, see inner exception", se),
- };
- }
- catch (Exception ex)
+ Task<ISecretResult?> asAsync = Task.Run(() => ReadSecretAsync(path, mountPoint, secretName));
+
+ if(!asAsync.Wait(ClientDefaultTimeout))
{
- throw new HCVaultException("Failed to retreive secret from Hashicorp Vault server, see inner exception", ex);
+ throw new TimeoutException("Failed to retreive the secret from the vault in the configured timeout period");
}
+
+ return asAsync.Result;
}
- private ValueTask<SecretResponse> ReadSecretResponse(HttpContent content, bool async)
+ private async Task<SecretResponse> ReadSecretResponse(HttpContent content)
{
- SecretResponse res = new(DefaultBufferSize, _bufferHeap);
+ SecretResponse response = new(DefaultBufferSize, _bufferHeap);
+
try
{
- if (async)
- {
- return ReadStreamAsync(content, res);
- }
- else
- {
- //Read into a memory stream
- content.CopyTo(res.StreamData, null, default);
- res.ResetStream();
+ await content.CopyToAsync(response.StreamData);
- return ValueTask.FromResult(res);
- }
+ response.ResetStream();
+
+ return response;
}
catch
{
- res.Dispose();
+ response.Dispose();
throw;
}
-
- async static ValueTask<SecretResponse> ReadStreamAsync(HttpContent content, SecretResponse response)
- {
- try
- {
- await content.CopyToAsync(response.StreamData);
-
- response.ResetStream();
-
- return response;
- }
- catch
- {
- response.Dispose();
- throw;
- }
- }
-
}
private static string GetSecretPathForKvVersion(int version, string path, string mount)
@@ -288,7 +266,7 @@ namespace VNLib.Plugins.Extensions.Loading
return null;
}
- private static ValueTask ProcessVaultErrorResponseAsync(HttpResponseMessage response, bool async)
+ private static ValueTask ProcessVaultErrorResponseAsync(HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
{
@@ -322,12 +300,12 @@ namespace VNLib.Plugins.Extensions.Loading
);
}
- return async ? ExceptionsFromContentAsync(response) : ExceptionsFromContent(response);
+ return ExceptionsFromContentAsync(response);
static ValueTask ExceptionFromVaultErrors(HttpStatusCode code, VaultErrorMessage? errs)
{
//If the error message is null, raise an exception
- if (errs == null || errs.Errors == null || errs.Errors.Length == 0)
+ if (errs?.Errors is null || errs.Errors.Length == 0)
{
return ValueTask.FromException(
new HttpRequestException($"Failed to fetch secret from vault with error code {code}")
@@ -352,19 +330,6 @@ namespace VNLib.Plugins.Extensions.Loading
await ExceptionFromVaultErrors(response.StatusCode, errs);
}
-
- static ValueTask ExceptionsFromContent(HttpResponseMessage response)
- {
-#pragma warning disable CA1849 // Call async methods when in an async method
-
- //Read the error content stream and deserialize
- using Stream stream = response.Content.ReadAsStream();
- VaultErrorMessage? errs = JsonSerializer.Deserialize<VaultErrorMessage>(stream);
-
-#pragma warning restore CA1849 // Call async methods when in an async method
-
- return ExceptionFromVaultErrors(response.StatusCode, errs);
- }
}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/IHCVaultClient.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/IKvVaultClient.cs
index aab2541..77579ef 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/IHCVaultClient.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/IKvVaultClient.cs
@@ -3,9 +3,9 @@
*
* Library: VNLib
* Package: VNLib.Plugins.Extensions.Loading
-* File: IHCVaultClient.cs
+* File: IKvVaultClient.cs
*
-* IHCVaultClient.cs is part of VNLib.Plugins.Extensions.Loading which is
+* IKvVaultClient.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,9 +29,9 @@ using System.Threading.Tasks;
namespace VNLib.Plugins.Extensions.Loading
{
/// <summary>
- /// A Hashicorp Vault client for reading secrets from a vault server
+ /// A secret client interace for reading secrets from a vault server
/// </summary>
- public interface IHCVaultClient
+ public interface IKvVaultClient
{
/// <summary>
/// Reads a single KeyValue secret from the vault server asyncrhonously and returns the result
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/OnDemandSecret.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/OnDemandSecret.cs
index 6e6d560..edbef8c 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/OnDemandSecret.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/OnDemandSecret.cs
@@ -35,13 +35,20 @@ using System.Collections.Generic;
using VNLib.Utils.Memory;
using VNLib.Utils.Logging;
using VNLib.Utils.Extensions;
+using VNLib.Utils.Resources;
using static VNLib.Plugins.Extensions.Loading.PluginSecretConstants;
namespace VNLib.Plugins.Extensions.Loading
{
- internal sealed class OnDemandSecret(PluginBase plugin, string secretName, IHCVaultClient? vault) : IOnDemandSecret
+ internal sealed class OnDemandSecret(PluginBase plugin, string secretName, Func<IKvVaultClient?> vaultCb) : IOnDemandSecret
{
+ /*
+ * Defer loading vault until needed by a vault secret. This avoids loading the vault client
+ * if no secrets are needed from the vault.
+ */
+ private readonly LazyInitializer<IKvVaultClient?> vault = new(vaultCb);
+
public string SecretName { get; } = secretName ?? throw new ArgumentNullException(nameof(secretName));
///<inheritdoc/>
@@ -175,16 +182,16 @@ namespace VNLib.Plugins.Extensions.Loading
string secret = path[(lastSep + 1)..].ToString();
//Try load client
- _ = vault ?? throw new KeyNotFoundException("Vault client not found");
+ _ = vault.Instance ?? throw new KeyNotFoundException("Vault client not found");
if (async)
{
- Task<ISecretResult?> asTask = Task.Run(() => vault.ReadSecretAsync(secret, mount, secretTableKey));
+ Task<ISecretResult?> asTask = Task.Run(() => vault.Instance.ReadSecretAsync(secret, mount, secretTableKey));
return new ValueTask<ISecretResult?>(asTask);
}
else
{
- ISecretResult? result = vault.ReadSecret(secret, mount, secretTableKey);
+ ISecretResult? result = vault.Instance.ReadSecret(secret, mount, secretTableKey);
return new ValueTask<ISecretResult?>(result);
}
}
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretConstants.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretConstants.cs
index 5c5a644..54bfa17 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretConstants.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretConstants.cs
@@ -33,7 +33,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_TOKEN_ENV_NAME = "VAULT_TOKEN";
public const string VAULT_KV_VERSION_KEY = "kv_version";
public const string VAULT_URL_KEY = "url";
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretStore.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretStore.cs
index 6b20e30..ec3871f 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretStore.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretStore.cs
@@ -40,16 +40,18 @@ namespace VNLib.Plugins.Extensions.Loading
/// <param name="plugin">The plugin instance to get secrets from</param>
public readonly struct PluginSecretStore(PluginBase plugin) : IEquatable<PluginSecretStore>
{
+ const int HCVaultDefaultKvVersion = 2;
+
private readonly PluginBase _plugin = plugin;
/// <summary>
/// Gets the ambient vault client for the current plugin
/// if the configuration is loaded, null otherwise
/// </summary>
- /// <returns>The ambient <see cref="IVaultClient"/> if loaded, null otherwise</returns>
+ /// <returns>The ambient <see cref="IKvVaultClient"/> if loaded, null otherwise</returns>
/// <exception cref="KeyNotFoundException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
- public IHCVaultClient? GetVaultClient() => LoadingExtensions.GetOrCreateSingleton(_plugin, TryGetVaultLoader);
+ public IKvVaultClient? GetVaultClient() => LoadingExtensions.GetOrCreateSingleton(_plugin, TryGetVaultLoader);
private static HCVaultClient? TryGetVaultLoader(PluginBase pbase)
{
@@ -63,21 +65,19 @@ namespace VNLib.Plugins.Extensions.Loading
//try get server address creds from config
string serverAddress = conf.GetRequiredProperty(VAULT_URL_KEY, p => p.GetString()!);
- bool trustCert = conf.TryGetValue(VAULT_TRUST_CERT_KEY, out JsonElement trustCertEl) && trustCertEl.GetBoolean();
+ bool trustCert = conf.GetValueOrDefault(VAULT_TRUST_CERT_KEY, el => el.GetBoolean(), false);
- int version = 2; //Default to version 2 now
string? authToken;
-
- //Get authentication method from config
+
if (conf.TryGetValue(VAULT_TOKEN_KEY, out JsonElement tokenEl))
{
//Init token
authToken = tokenEl.GetString();
}
//Try to get the token as an environment variable
- else if (Environment.GetEnvironmentVariable(VAULT_TOKNE_ENV_NAME) != null)
+ else if (Environment.GetEnvironmentVariable(VAULT_TOKEN_ENV_NAME) != null)
{
- authToken = Environment.GetEnvironmentVariable(VAULT_TOKNE_ENV_NAME)!;
+ authToken = Environment.GetEnvironmentVariable(VAULT_TOKEN_ENV_NAME)!;
}
else
{
@@ -87,10 +87,7 @@ namespace VNLib.Plugins.Extensions.Loading
_ = authToken ?? throw new KeyNotFoundException($"Failed to load the vault authentication method from {VAULT_OBJECT_NAME}");
//Check for vault kv version, otherwise use the default
- if (conf.TryGetValue(VAULT_KV_VERSION_KEY, out JsonElement kvVersionEl))
- {
- version = kvVersionEl.GetInt32();
- }
+ int version = conf.GetValueOrDefault(VAULT_KV_VERSION_KEY, el => el.GetInt32(), HCVaultDefaultKvVersion);
//create vault client, invalid or nulls will raise exceptions here
return HCVaultClient.Create(serverAddress, authToken, version, trustCert, MemoryUtil.Shared);
@@ -114,7 +111,7 @@ namespace VNLib.Plugins.Extensions.Loading
public IOnDemandSecret GetOnDemandSecret(string secretName)
{
ArgumentException.ThrowIfNullOrWhiteSpace(secretName);
- return new OnDemandSecret(_plugin, secretName, GetVaultClient());
+ return new OnDemandSecret(_plugin, secretName, GetVaultClient);
}
///<inheritdoc/>
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj b/lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj
index be21770..8fa72fb 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/VNLib.Plugins.Extensions.Loading.csproj
@@ -2,13 +2,16 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<RootNamespace>VNLib.Plugins.Extensions.Loading</RootNamespace>
<AssemblyName>VNLib.Plugins.Extensions.Loading</AssemblyName>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
- <Nullable>enable</Nullable>
- <AnalysisLevel>latest-all</AnalysisLevel>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
+
+ <PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
<PropertyGroup>
<Authors>Vaughn Nugent</Authors>
@@ -19,12 +22,11 @@
<Copyright>Copyright © 2024 Vaughn Nugent</Copyright>
<PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Plugins.Extensions</PackageProjectUrl>
<RepositoryUrl>https://github.com/VnUgE/VNLib.Plugins.Extensions/tree/master/lib/VNLib.Plugins.Extensions.Loading</RepositoryUrl>
- </PropertyGroup>
-
- <PropertyGroup>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
+
<ItemGroup>
<None Include="..\..\..\LICENSE">
<Pack>True</Pack>
diff --git a/lib/VNLib.Plugins.Extensions.Validation/src/VNLib.Plugins.Extensions.Validation.csproj b/lib/VNLib.Plugins.Extensions.Validation/src/VNLib.Plugins.Extensions.Validation.csproj
index ebd8910..754bec6 100644
--- a/lib/VNLib.Plugins.Extensions.Validation/src/VNLib.Plugins.Extensions.Validation.csproj
+++ b/lib/VNLib.Plugins.Extensions.Validation/src/VNLib.Plugins.Extensions.Validation.csproj
@@ -2,15 +2,18 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<RootNamespace>VNLib.Plugins.Extensions.Validation</RootNamespace>
<AssemblyName>VNLib.Plugins.Extensions.Validation</AssemblyName>
- <Nullable>enable</Nullable>
- <AnalysisLevel>latest-all</AnalysisLevel>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
<PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
+
+ <PropertyGroup>
<Authors>Vaughn Nugent</Authors>
<Company>Vaughn Nugent</Company>
<Product>VNLib.Plugins.Extensions.Validation</Product>
@@ -19,12 +22,11 @@
<Copyright>Copyright © 2024 Vaughn Nugent</Copyright>
<PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Plugins.Sessions</PackageProjectUrl>
<RepositoryUrl>https://github.com/VnUgE/VNLib.Plugins.Extensions/tree/master/lib/VNLib.Plugins.Extensions.Validation</RepositoryUrl>
- </PropertyGroup>
-
- <PropertyGroup>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
+
<ItemGroup>
<None Include="..\..\..\LICENSE">
<Pack>True</Pack>
diff --git a/lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MYSql.csproj b/lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MYSql.csproj
index 9f5c2bf..3d52e85 100644
--- a/lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MYSql.csproj
+++ b/lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MYSql.csproj
@@ -2,14 +2,17 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<AssemblyName>VNLib.Plugins.Extensions.Sql.MySQL</AssemblyName>
<RootNamespace>VNLib.Plugins.Extensions.Sql</RootNamespace>
- <Nullable>enable</Nullable>
- <ImplicitUsings>disable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<!--Enable dynamic loading-->
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
+
+ <PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
<PropertyGroup>
<PackageId>VNLib.Plugins.Extensions.Sql.MySQL</PackageId>
@@ -22,6 +25,7 @@
<Description>A runtime asset library that provides MySQL interfaces for ADO and EFCore SQL server clients</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
<ItemGroup>
diff --git a/lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MySQL.csproj b/lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MySQL.csproj
index 9f5c2bf..3d52e85 100644
--- a/lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MySQL.csproj
+++ b/lib/sql-providers/mysql/VNLib.Plugins.Extensions.Loading.Sql.MySql/src/VNLib.Plugins.Extensions.Loading.Sql.MySQL.csproj
@@ -2,14 +2,17 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<AssemblyName>VNLib.Plugins.Extensions.Sql.MySQL</AssemblyName>
<RootNamespace>VNLib.Plugins.Extensions.Sql</RootNamespace>
- <Nullable>enable</Nullable>
- <ImplicitUsings>disable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<!--Enable dynamic loading-->
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
+
+ <PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
<PropertyGroup>
<PackageId>VNLib.Plugins.Extensions.Sql.MySQL</PackageId>
@@ -22,6 +25,7 @@
<Description>A runtime asset library that provides MySQL interfaces for ADO and EFCore SQL server clients</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
<ItemGroup>
diff --git a/lib/sql-providers/sqlite/VNLib.Plugins.Extensions.Loading.Sql.SQLite/src/VNLib.Plugins.Extensions.Loading.Sql.SQLite.csproj b/lib/sql-providers/sqlite/VNLib.Plugins.Extensions.Loading.Sql.SQLite/src/VNLib.Plugins.Extensions.Loading.Sql.SQLite.csproj
index d251923..753deaf 100644
--- a/lib/sql-providers/sqlite/VNLib.Plugins.Extensions.Loading.Sql.SQLite/src/VNLib.Plugins.Extensions.Loading.Sql.SQLite.csproj
+++ b/lib/sql-providers/sqlite/VNLib.Plugins.Extensions.Loading.Sql.SQLite/src/VNLib.Plugins.Extensions.Loading.Sql.SQLite.csproj
@@ -2,14 +2,17 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<AssemblyName>VNLib.Plugins.Extensions.Sql.SQLite</AssemblyName>
<RootNamespace>VNLib.Plugins.Extensions.Sql</RootNamespace>
- <Nullable>enable</Nullable>
- <ImplicitUsings>disable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<!--Enable dynamic loading-->
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
+
+ <PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
<PropertyGroup>
<PackageId>VNLib.Plugins.Extensions.Sql.SQLite</PackageId>
@@ -22,6 +25,7 @@
<Description>A runtime asset library that provides SQLite interfaces for ADO and EFCore SQL server clients</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
<ItemGroup>
@@ -45,7 +49,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
</ItemGroup>
<ItemGroup>
diff --git a/lib/sql-providers/sqlserver/VNLib.Plugins.Extensions.Loading.Sql.SQLServer/src/VNLib.Plugins.Extensions.Loading.Sql.SQLServer.csproj b/lib/sql-providers/sqlserver/VNLib.Plugins.Extensions.Loading.Sql.SQLServer/src/VNLib.Plugins.Extensions.Loading.Sql.SQLServer.csproj
index 897f9df..97c6096 100644
--- a/lib/sql-providers/sqlserver/VNLib.Plugins.Extensions.Loading.Sql.SQLServer/src/VNLib.Plugins.Extensions.Loading.Sql.SQLServer.csproj
+++ b/lib/sql-providers/sqlserver/VNLib.Plugins.Extensions.Loading.Sql.SQLServer/src/VNLib.Plugins.Extensions.Loading.Sql.SQLServer.csproj
@@ -2,14 +2,17 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<AssemblyName>VNLib.Plugins.Extensions.Sql.SqlServer</AssemblyName>
<RootNamespace>VNLib.Plugins.Extensions.Sql</RootNamespace>
- <Nullable>enable</Nullable>
- <ImplicitUsings>disable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<!--Enable dynamic loading-->
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
+
+ <PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
<PropertyGroup>
<PackageId>VNLib.Plugins.Extensions.Sql.SqlServer</PackageId>
@@ -22,6 +25,7 @@
<Description>A runtime asset library that provides SqlServer interfaces for ADO and EFCore SQL server clients</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
</PropertyGroup>
<ItemGroup>
@@ -45,7 +49,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.4" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.5" />
</ItemGroup>