aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-09-08 23:21:30 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-09-08 23:21:30 -0400
commit3cf5bf80e4feea06728947f20657d49268b785d5 (patch)
tree4f8d6b2269c5d7f7bfc148b6e41d831760f7c6b1
parentbf01b244f3bf2aa3c236fb86c0d75c261f54b334 (diff)
Password hashing updates & async resource updates
-rw-r--r--lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs7
-rw-r--r--lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs45
-rw-r--r--lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs126
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs23
4 files changed, 65 insertions, 136 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs b/lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs
index 3fca1cb..0edf653 100644
--- a/lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs
+++ b/lib/VNLib.Plugins.Extensions.Data/src/Storage/Blob.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Extensions.Data
@@ -24,6 +24,7 @@
using System;
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Versioning;
@@ -206,7 +207,7 @@ namespace VNLib.Plugins.Extensions.Data.Storage
GC.SuppressFinalize(this);
}
///<inheritdoc/>
- public async ValueTask ReleaseAsync()
+ public async ValueTask ReleaseAsync(CancellationToken cancellation = default)
{
try
{
@@ -237,7 +238,7 @@ namespace VNLib.Plugins.Extensions.Data.Storage
//Dispose the stream
await BaseStream.DisposeAsync();
//Release the descriptor
- await Descriptor.ReleaseAsync();
+ await Descriptor.ReleaseAsync(cancellation);
}
}
}
diff --git a/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs b/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs
index d410187..78fdfa0 100644
--- a/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs
+++ b/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageDescriptor.cs
@@ -23,17 +23,15 @@
*/
using System;
-using System.IO;
using System.Text.Json;
+using System.Threading;
using System.Collections;
-using System.IO.Compression;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using VNLib.Utils;
using VNLib.Utils.Async;
-using VNLib.Utils.Memory;
namespace VNLib.Plugins.Extensions.Data.Storage
{
@@ -60,35 +58,32 @@ namespace VNLib.Plugins.Extensions.Data.Storage
private readonly Lazy<Dictionary<string, string>> StringStorage;
+ protected override IAsyncResourceStateHandler AsyncHandler { get; }
+
/// <summary>
/// The currnt descriptor's identifier string within its backing table. Usually the primary key.
/// </summary>
public string DescriptorID => Entry.Id;
+
/// <summary>
/// The identifier of the user for which this descriptor belongs to
/// </summary>
public string UserID => Entry.UserId!;
+
/// <summary>
/// The <see cref="DateTime"/> when the descriptor was created
/// </summary>
public DateTimeOffset Created => Entry.Created;
+
/// <summary>
/// The last time this descriptor was updated
/// </summary>
public DateTimeOffset LastModified => Entry.LastModified;
- ///<inheritdoc/>
- protected override AsyncUpdateCallback UpdateCb { get; }
- ///<inheritdoc/>
- protected override AsyncDeleteCallback DeleteCb { get; }
- ///<inheritdoc/>
- protected override JsonSerializerOptions JSO => SerializerOptions;
-
- internal LWStorageDescriptor(LWStorageManager manager, LWStorageEntry entry)
+ internal LWStorageDescriptor(IAsyncResourceStateHandler handler, LWStorageEntry entry)
{
Entry = entry;
- UpdateCb = manager.UpdateDescriptorAsync;
- DeleteCb = manager.RemoveDescriptorAsync;
+ AsyncHandler = handler;
StringStorage = new(OnStringStoreLoad);
}
@@ -100,15 +95,8 @@ namespace VNLib.Plugins.Extensions.Data.Storage
}
else
{
- //Calc and alloc decode buffer
- int bufferSize = (int)(Entry.Data.Length * 1.75);
-
- using UnsafeMemoryHandle<byte> decodeBuffer = MemoryUtil.UnsafeAlloc(bufferSize);
-
//Decode and deserialize the data
- return BrotliDecoder.TryDecompress(Entry.Data, decodeBuffer, out int written)
- ? JsonSerializer.Deserialize<Dictionary<string, string>>(decodeBuffer.Span[..written], SerializerOptions) ?? new(StringComparer.OrdinalIgnoreCase)
- : throw new InvalidDataException("Failed to decompress data");
+ return JsonSerializer.Deserialize<Dictionary<string, string>>(Entry.Data, SerializerOptions) ?? new(StringComparer.OrdinalIgnoreCase);
}
}
@@ -142,7 +130,6 @@ namespace VNLib.Plugins.Extensions.Data.Storage
SetStringValue(key, value);
}
}
-
/// <summary>
/// Gets a string value from string storage matching a given key
@@ -211,9 +198,10 @@ namespace VNLib.Plugins.Extensions.Data.Storage
}
///<inheritdoc/>
- public override async ValueTask ReleaseAsync()
+ public override async ValueTask ReleaseAsync(CancellationToken cancellation = default)
{
- await base.ReleaseAsync();
+ await base.ReleaseAsync(cancellation);
+
//Cleanup dict on exit
if (StringStorage.IsValueCreated)
{
@@ -223,8 +211,15 @@ namespace VNLib.Plugins.Extensions.Data.Storage
///<inheritdoc/>
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => StringStorage.Value.GetEnumerator();
+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
///<inheritdoc/>
- protected override object GetResource() => StringStorage.Value;
+ protected override object GetResource()
+ {
+ //Serlaize the state data and store it in the data entry
+ Entry.Data = JsonSerializer.SerializeToUtf8Bytes(StringStorage.Value, SerializerOptions);
+ return Entry;
+ }
}
} \ No newline at end of file
diff --git a/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs b/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs
index eabe618..bc506c7 100644
--- a/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs
+++ b/lib/VNLib.Plugins.Extensions.Data/src/Storage/LWStorageManager.cs
@@ -23,18 +23,17 @@
*/
using System;
-using System.IO;
using System.Data;
using System.Linq;
using System.Threading;
-using System.IO.Compression;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using VNLib.Utils;
-using VNLib.Utils.IO;
-using VNLib.Utils.Memory;
+using VNLib.Utils.Async;
+using VNLib.Plugins.Extensions.Data.Extensions;
+
namespace VNLib.Plugins.Extensions.Data.Storage
{
@@ -42,8 +41,9 @@ namespace VNLib.Plugins.Extensions.Data.Storage
/// <summary>
/// Provides single table database object storage services
/// </summary>
- public sealed class LWStorageManager
+ public sealed class LWStorageManager : IAsyncResourceStateHandler
{
+
/// <summary>
/// The generator function that is invoked when a new <see cref="LWStorageDescriptor"/> is to
/// be created without an explicit id
@@ -112,20 +112,11 @@ namespace VNLib.Plugins.Extensions.Data.Storage
//Add and save changes
ctx.Descriptors.Add(entry);
- ERRNO result = await ctx.SaveChangesAsync(cancellation);
+ ERRNO result = await ctx.SaveAndCloseAsync(true, cancellation);
- if (!result)
- {
- //Rollback and raise exception
- await ctx.RollbackTransctionAsync(cancellation);
- throw new LWDescriptorCreationException("Failed to create descriptor, because changes could not be saved");
- }
- else
- {
- //Commit transaction and return the new descriptor
- await ctx.CommitTransactionAsync(cancellation);
- return new LWStorageDescriptor(this, entry);
- }
+ return result
+ ? new LWStorageDescriptor(this, entry)
+ : throw new LWDescriptorCreationException("Failed to create descriptor, because changes could not be saved");
}
/// <summary>
@@ -154,17 +145,10 @@ namespace VNLib.Plugins.Extensions.Data.Storage
select s)
.SingleOrDefaultAsync(cancellation);
+ await db.SaveAndCloseAsync(true, cancellation);
+
//Close transactions and return
- if (entry == null)
- {
- await db.RollbackTransctionAsync(cancellation);
- return null;
- }
- else
- {
- await db.CommitTransactionAsync(cancellation);
- return new (this, entry);
- }
+ return entry == null ? null : new (this, entry);
}
/// <summary>
@@ -193,17 +177,10 @@ namespace VNLib.Plugins.Extensions.Data.Storage
select s)
.SingleOrDefaultAsync(cancellation);
+ await db.SaveAndCloseAsync(true, cancellation);
+
//Close transactions and return
- if (entry == null)
- {
- await db.RollbackTransctionAsync(cancellation);
- return null;
- }
- else
- {
- await db.CommitTransactionAsync(cancellation);
- return new (this, entry);
- }
+ return entry == null ? null : new(this, entry);
}
/// <summary>
@@ -236,61 +213,27 @@ namespace VNLib.Plugins.Extensions.Data.Storage
//Delete
db.Descriptors.RemoveRange(expired);
- //Save changes
- ERRNO count = await db.SaveChangesAsync(cancellation);
-
//Commit transaction
- await db.CommitTransactionAsync(cancellation);
-
- return count;
+ return await db.SaveAndCloseAsync(true, cancellation);
}
-
- /// <summary>
- /// Updates a descriptor's data field
- /// </summary>
- /// <param name="descriptorObj">Descriptor to update</param>
- /// <param name="data">Data string to store to descriptor record</param>
- /// <exception cref="LWStorageUpdateFailedException"></exception>
- internal async Task UpdateDescriptorAsync(object descriptorObj, Stream data)
+
+ async Task IAsyncResourceStateHandler.UpdateAsync(AsyncUpdatableResource resource, object state, CancellationToken cancellation)
{
- LWStorageEntry entry = (descriptorObj as LWStorageDescriptor)!.Entry;
+ LWStorageEntry entry = (state as LWStorageEntry)!;
ERRNO result = 0;
try
{
await using LWStorageContext ctx = GetContext();
- await ctx.OpenTransactionAsync(CancellationToken.None);
+ await ctx.OpenTransactionAsync(System.Transactions.IsolationLevel.RepeatableRead, cancellation);
//Begin tracking
ctx.Descriptors.Attach(entry);
-
- //Convert stream to vnstream
- VnMemoryStream vms = (VnMemoryStream)data;
- using (IMemoryHandle<byte> encBuffer = MemoryUtil.SafeAlloc<byte>((int)vms.Length))
- {
- //try to compress
- if(!BrotliEncoder.TryCompress(vms.AsSpan(), encBuffer.Span, out int compressed))
- {
- throw new InvalidDataException("Failed to compress the descriptor data");
- }
-
- //Set the data
- entry.Data = encBuffer.Span[..compressed].ToArray();
- }
+
//Update modified time
entry.LastModified = DateTime.UtcNow;
//Save changes
- result = await ctx.SaveChangesAsync(CancellationToken.None);
-
- //Commit or rollback
- if (result)
- {
- await ctx.CommitTransactionAsync(CancellationToken.None);
- }
- else
- {
- await ctx.RollbackTransctionAsync(CancellationToken.None);
- }
+ result = await ctx.SaveAndCloseAsync(true, cancellation);
}
catch (Exception ex)
{
@@ -302,37 +245,23 @@ namespace VNLib.Plugins.Extensions.Data.Storage
throw new LWStorageUpdateFailedException($"Descriptor {entry.Id} failed to update");
}
}
-
- /// <summary>
- /// Function to remove the specified descriptor
- /// </summary>
- /// <param name="descriptorObj">The active descriptor to remove from the database</param>
- /// <exception cref="LWStorageRemoveFailedException"></exception>
- internal async Task RemoveDescriptorAsync(object descriptorObj)
+
+ async Task IAsyncResourceStateHandler.DeleteAsync(AsyncUpdatableResource resource, CancellationToken cancellation)
{
- LWStorageEntry descriptor = (descriptorObj as LWStorageDescriptor)!.Entry;
+ LWStorageEntry descriptor = (resource as LWStorageDescriptor)!.Entry;
ERRNO result;
try
{
//Init db
await using LWStorageContext db = GetContext();
//Begin transaction
- await db.OpenTransactionAsync();
+ await db.OpenTransactionAsync(cancellation);
//Delete the user from the database
db.Descriptors.Remove(descriptor);
//Save changes and commit if successful
- result = await db.SaveChangesAsync();
-
- if (result)
- {
- await db.CommitTransactionAsync();
- }
- else
- {
- await db.RollbackTransctionAsync();
- }
+ result = await db.SaveAndCloseAsync(true, cancellation);
}
catch (Exception ex)
{
@@ -343,6 +272,5 @@ namespace VNLib.Plugins.Extensions.Data.Storage
throw new LWStorageRemoveFailedException("Failed to delete the user account because of a database failure, the user may already be deleted");
}
}
-
}
} \ No newline at end of file
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs b/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs
index 3489789..e382681 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/ManagedPasswordHashing.cs
@@ -131,18 +131,22 @@ namespace VNLib.Plugins.Extensions.Loading
//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();
+ Argon2ConfigParams p = new()
+ {
+ HashLen = hashingArgs["hash_len"].GetUInt32(),
+ MemoryCost = hashingArgs["memory_cost"].GetUInt32(),
+ Parallelism = hashingArgs["parallelism"].GetUInt32(),
+ SaltLen = (int)hashingArgs["salt_len"].GetUInt32(),
+ TimeCost = hashingArgs["time_cost"].GetUInt32()
+ };
+
//Load passwords
- Passwords = new(this, (int)saltLen, timeCost, memoryCost, parallelism, hashLen);
+ Passwords = PasswordHashing.Create(this, in p);
}
else
{
- Passwords = new(this);
+ //Load passwords with default config
+ Passwords = PasswordHashing.Create(this, new Argon2ConfigParams());
}
//Get the pepper from secret storage
@@ -152,7 +156,8 @@ namespace VNLib.Plugins.Extensions.Loading
public SecretProvider(PluginBase plugin)
{
- Passwords = new(this);
+ //Load passwords with default config
+ Passwords = PasswordHashing.Create(this, new Argon2ConfigParams());
//Get the pepper from secret storage
_pepper = plugin.GetSecretAsync(LoadingExtensions.PASSWORD_HASHING_KEY)