diff options
author | vnugent <public@vaughnnugent.com> | 2024-02-29 21:23:26 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-02-29 21:23:26 -0500 |
commit | b679ddd4e647ac915febd0d5a5e488a1e8e48842 (patch) | |
tree | cf414be9a53342e8d59194198cde5bf3c2187fc1 /lib/Plugins.Essentials | |
parent | 2b1314c1475e7e1831c691cf349cb89c66fa320c (diff) |
Squashed commit of the following:
commit 231e26e5c6731e6e156d7c0591518e84a3b82f5a
Author: vnugent <public@vaughnnugent.com>
Date: Thu Feb 29 20:59:42 2024 -0500
fix: #5 find and patch Windows compression bug and some deployment
commit d0bfe14e0a0e27172b8dd41f468265e651784837
Author: vnugent <public@vaughnnugent.com>
Date: Wed Feb 21 21:39:19 2024 -0500
fix: #4 fix readme licensing message for accuracy
commit 6f37f152fcd105e40af6689192a36b87eda95f51
Author: vnugent <public@vaughnnugent.com>
Date: Wed Feb 21 21:37:55 2024 -0500
fix: jwt hashalg sizing and public api for hashalg sizes
Diffstat (limited to 'lib/Plugins.Essentials')
7 files changed, 120 insertions, 46 deletions
diff --git a/lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs b/lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs index e346af1..2f2ba91 100644 --- a/lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs +++ b/lib/Plugins.Essentials/src/Accounts/UserCreationRequest.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -22,6 +22,8 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ +using System; + using VNLib.Utils.Memory; using VNLib.Plugins.Essentials.Users; @@ -40,7 +42,17 @@ namespace VNLib.Plugins.Essentials.Accounts public ulong Privileges { get; init; } = AccountUtil.MINIMUM_LEVEL; ///<inheritdoc/> - public string EmailAddress { get; init; } = string.Empty; + public string Username { get; init; } = string.Empty; + + /// <summary> + /// Obsolete: Use the Username property instead + /// </summary> + [Obsolete("Use the Username property instead")] + public string EmailAddress + { + get => Username; + init => Username = value; + } ///<inheritdoc/> public bool UseRawPassword { get; init; } diff --git a/lib/Plugins.Essentials/src/EventProcessor.cs b/lib/Plugins.Essentials/src/EventProcessor.cs index 61a9b64..f052c56 100644 --- a/lib/Plugins.Essentials/src/EventProcessor.cs +++ b/lib/Plugins.Essentials/src/EventProcessor.cs @@ -188,8 +188,7 @@ namespace VNLib.Plugins.Essentials //If the processor had an error recovering the session, return the result to the processor if (entity.EventSessionHandle.EntityStatus != FileProcessArgs.Continue) { - ProcessFile(httpEvent, entity.EventSessionHandle.EntityStatus); - return; + goto ProcessRoutine; } //Attach the new session to the entity @@ -209,7 +208,7 @@ namespace VNLib.Plugins.Essentials //Loop through nodes while(mwNode != null) { - //Process + //Invoke mw handler on our event entity.EventArgs = await mwNode.ValueRef.ProcessAsync(entity); switch (entity.EventArgs.Routine) @@ -237,7 +236,7 @@ namespace VNLib.Plugins.Essentials //Process a virtual file GetArgsFromVirtualReturn(entity, rt, out entity.EventArgs); - //If the entity was processed, exit + //If the entity was processed by the handler, exit if (entity.EventArgs != FileProcessArgs.Continue) { goto RespondAndExit; @@ -275,8 +274,10 @@ namespace VNLib.Plugins.Essentials } } + ProcessRoutine: + //Finally process the file - ProcessFile(httpEvent, in entity.EventArgs); + ProcessRoutine(httpEvent, in entity.EventArgs); } catch (ContentTypeUnacceptableException) { @@ -329,7 +330,7 @@ namespace VNLib.Plugins.Essentials /// </summary> /// <param name="entity">The entity to process the file for</param> /// <param name="args">The selected <see cref="FileProcessArgs"/> to determine what file to process</param> - protected virtual void ProcessFile(IHttpEvent entity, ref readonly FileProcessArgs args) + protected virtual void ProcessRoutine(IHttpEvent entity, ref readonly FileProcessArgs args) { try { @@ -413,8 +414,7 @@ namespace VNLib.Plugins.Essentials //Get the content type of he file ContentType fileType = HttpHelpers.GetContentTypeFromFile(filename); - - //Make sure the client accepts the content type + if (!entity.Server.Accepts(fileType)) { //Unacceptable @@ -729,7 +729,7 @@ namespace VNLib.Plugins.Essentials WeakReference<object?> wr = Volatile.Read(ref _objects[tableIndex]); //Try to get the object instance - wr.TryGetTarget(out object? value); + _ = wr.TryGetTarget(out object? value); instance = (T?)value; } diff --git a/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs b/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs index 92fae08..a99b1ab 100644 --- a/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs +++ b/lib/Plugins.Essentials/src/Extensions/ConnectionInfoExtensions.cs @@ -354,6 +354,7 @@ namespace VNLib.Plugins.Essentials.Extensions /// <param name="domain"></param> /// <param name="path"></param> /// <param name="sameSite"></param> + /// <param name="expires"></param> /// <param name="httpOnly"></param> /// <param name="secure"></param> [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs b/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs index f49af32..5f59346 100644 --- a/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs +++ b/lib/Plugins.Essentials/src/Extensions/EssentialHttpEventExtensions.cs @@ -163,6 +163,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponseJson(this IHttpEvent ev, HttpStatusCode code, JsonDocument data) { + ArgumentNullException.ThrowIfNull(ev); + if(data == null) { ev.CloseResponse(code); @@ -195,7 +197,9 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponse<T>(this IHttpEvent ev, T webm) where T:WebMessage { - if (webm == null) + ArgumentNullException.ThrowIfNull(ev); + + if (webm is null) { ev.CloseResponse(HttpStatusCode.OK); } @@ -220,6 +224,9 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponseAttachment(this IHttpEvent ev, HttpStatusCode code, FileInfo file) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(file); + //Close with file ev.CloseResponse(code, file); //Set content dispostion as attachment (only if successfull) @@ -236,6 +243,9 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponseAttachment(this IHttpEvent ev, HttpStatusCode code, FileStream file) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(file); + //Close with file ev.CloseResponse(code, file); //Set content dispostion as attachment (only if successfull) @@ -255,6 +265,9 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponseAttachment(this IHttpEvent ev, HttpStatusCode code, ContentType ct, Stream data, string fileName, long length) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(data); + //Close with file ev.CloseResponse(code, ct, data, length); //Set content dispostion as attachment (only if successfull) @@ -275,6 +288,9 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, FileInfo file) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(file); + //Open filestream for file FileStream fs = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read); try @@ -302,13 +318,16 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, FileStream file) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(file); + //Get content type from filename ContentType ct = HttpHelpers.GetContentTypeFromFile(file.Name); //Set the input as a stream ev.CloseResponse(code, ct, file, file.Length); } - + /// <summary> /// Close a response to a connection with a character buffer using the server wide /// <see cref="ConnectionInfo.Encoding"/> encoding @@ -321,12 +340,10 @@ namespace VNLib.Plugins.Essentials.Extensions /// <exception cref="InvalidOperationException"></exception> /// <exception cref="ContentTypeUnacceptableException"></exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, ContentType type, ReadOnlySpan<char> data) - { + public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, ContentType type, ReadOnlySpan<char> data) => //Get a memory stream using server built-in encoding CloseResponse(ev, code, type, data, ev.Server.Encoding); - } - + /// <summary> /// Close a response to a connection with a character buffer using the specified encoding type /// </summary> @@ -339,6 +356,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, ContentType type, ReadOnlySpan<char> data, Encoding encoding) { + ArgumentNullException.ThrowIfNull(ev); + if (data.IsEmpty) { ev.CloseResponse(code); @@ -367,6 +386,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CloseResponse(this IHttpEvent ev, HttpStatusCode code, ContentType type, ReadOnlySpan<byte> data) { + ArgumentNullException.ThrowIfNull(ev); + if (data.IsEmpty) { ev.CloseResponse(code); @@ -391,6 +412,9 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool CloseWithRelativeFile(this HttpEntity entity, HttpStatusCode code, string filePath) { + ArgumentNullException.ThrowIfNull(entity); + ArgumentNullException.ThrowIfNull(filePath); + //See if file exists and is within the root's directory if (entity.RequestedRoot.FindResourceInRoot(filePath, out string realPath)) { @@ -412,11 +436,9 @@ namespace VNLib.Plugins.Essentials.Extensions /// <remarks>Sets required headers for redirection, disables cache control, and returns the status code to the client</remarks> /// <exception cref="UriFormatException"></exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Redirect(this IHttpEvent ev, RedirectType type, string location) - { - Redirect(ev, type, new Uri(location, UriKind.RelativeOrAbsolute)); - } - + public static void Redirect(this IHttpEvent ev, RedirectType type, string location) + => Redirect(ev, type, new Uri(location, UriKind.RelativeOrAbsolute)); + /// <summary> /// Redirects a client using the specified <see cref="RedirectType"/> /// </summary> @@ -427,6 +449,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Redirect(this IHttpEvent ev, RedirectType type, Uri location) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(location); if(type == RedirectType.None) { @@ -472,6 +496,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryGetJsonFromArg<T>(this IHttpEvent ev, string key, JsonSerializerOptions options, out T? obj) { + ArgumentNullException.ThrowIfNull(ev); + //Check for key in argument if (ev.RequestArgs.TryGetNonEmptyValue(key, out string? value)) { @@ -502,6 +528,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static JsonDocument? GetJsonFromArg(this IHttpEvent ev, string key, in JsonDocumentOptions options = default) { + ArgumentNullException.ThrowIfNull(ev); + try { //Check for key in argument @@ -528,6 +556,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T? GetJsonFromFile<T>(this IHttpEvent ev, JsonSerializerOptions? options = null, int uploadIndex = 0) { + ArgumentNullException.ThrowIfNull(ev); + if (ev.Files.Count <= uploadIndex) { return default; @@ -563,6 +593,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static JsonDocument? GetJsonFromFile(this IHttpEvent ev, int uploadIndex = 0) { + ArgumentNullException.ThrowIfNull(ev); + if (ev.Files.Count <= uploadIndex) { return default; @@ -599,6 +631,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ValueTask<T?> GetJsonFromFileAsync<T>(this HttpEntity ev, JsonSerializerOptions? options = null, int uploadIndex = 0) { + ArgumentNullException.ThrowIfNull(ev); + if (ev.Files.Count <= uploadIndex) { return ValueTask.FromResult<T?>(default); @@ -640,6 +674,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Task<JsonDocument?> GetJsonFromFileAsync(this HttpEntity ev, int uploadIndex = 0) { + ArgumentNullException.ThrowIfNull(ev); + if (ev.Files.Count <= uploadIndex) { return DocTaskDefault; @@ -677,6 +713,9 @@ namespace VNLib.Plugins.Essentials.Extensions /// <exception cref="IndexOutOfRangeException"></exception> public static Task<T?> ParseFileAsAsync<T>(this IHttpEvent ev, Func<Stream, Task<T?>> parser, int uploadIndex = 0) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(parser); + if (ev.Files.Count <= uploadIndex) { return Task.FromResult<T?>(default); @@ -698,6 +737,9 @@ namespace VNLib.Plugins.Essentials.Extensions /// <exception cref="IndexOutOfRangeException"></exception> public static Task<T?> ParseFileAsAsync<T>(this IHttpEvent ev, Func<Stream, string, Task<T?>> parser, int uploadIndex = 0) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(parser); + if (ev.Files.Count <= uploadIndex) { return Task.FromResult<T?>(default); @@ -720,6 +762,9 @@ namespace VNLib.Plugins.Essentials.Extensions /// <exception cref="IndexOutOfRangeException"></exception> public static ValueTask<T?> ParseFileAsAsync<T>(this IHttpEvent ev, Func<Stream, ValueTask<T?>> parser, int uploadIndex = 0) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(parser); + if (ev.Files.Count <= uploadIndex) { return ValueTask.FromResult<T?>(default); @@ -741,6 +786,9 @@ namespace VNLib.Plugins.Essentials.Extensions /// <exception cref="IndexOutOfRangeException"></exception> public static ValueTask<T?> ParseFileAsAsync<T>(this IHttpEvent ev, Func<Stream, string, ValueTask<T?>> parser, int uploadIndex = 0) { + ArgumentNullException.ThrowIfNull(ev); + ArgumentNullException.ThrowIfNull(parser); + if (ev.Files.Count <= uploadIndex) { return ValueTask.FromResult<T?>(default); @@ -760,6 +808,8 @@ namespace VNLib.Plugins.Essentials.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasAuthorization(this IConnectionInfo ci, [NotNullWhen(true)] out string? token) { + ArgumentNullException.ThrowIfNull(ci); + //Get auth header value string? authorization = ci.Headers[HttpRequestHeader.Authorization]; //Check if its set @@ -767,7 +817,7 @@ namespace VNLib.Plugins.Essentials.Extensions { int bearerIndex = authorization.IndexOf(BEARER_STRING, StringComparison.OrdinalIgnoreCase); //Calc token offset, get token, and trim any whitespace - token = authorization[(bearerIndex + BEARER_LEN)..].Trim(); + token = authorization.AsSpan(bearerIndex + BEARER_LEN).Trim().ToString(); return true; } token = null; @@ -819,11 +869,9 @@ namespace VNLib.Plugins.Essentials.Extensions ) { //Must define an accept callback - _ = socketOpenedCallback ?? throw new ArgumentNullException(nameof(socketOpenedCallback)); - - bool success = PrepWebSocket(entity, subProtocol); + ArgumentNullException.ThrowIfNull(socketOpenedCallback); - if (success) + if (PrepWebSocket(entity, subProtocol)) { //Set a default keep alive if none was specified if (keepAlive == default) @@ -841,8 +889,11 @@ namespace VNLib.Plugins.Essentials.Extensions //Setup a new websocket session with a new session id entity.DangerousChangeProtocol(ws); + + return true; } - return success; + + return false; } /// <summary> @@ -858,11 +909,10 @@ namespace VNLib.Plugins.Essentials.Extensions public static bool AcceptWebSocket(this IHttpEvent entity, WebSocketAcceptedCallback socketOpenedCallback, string? subProtocol = null, TimeSpan keepAlive = default) { //Must define an accept callback - _ = socketOpenedCallback ?? throw new ArgumentNullException(nameof(socketOpenedCallback)); + ArgumentNullException.ThrowIfNull(entity); + ArgumentNullException.ThrowIfNull(socketOpenedCallback); - bool success = PrepWebSocket(entity, subProtocol); - - if(success) + if(PrepWebSocket(entity, subProtocol)) { //Set a default keep alive if none was specified if (keepAlive == default) @@ -879,15 +929,19 @@ namespace VNLib.Plugins.Essentials.Extensions //Setup a new websocket session with a new session id entity.DangerousChangeProtocol(ws); + + return true; } - return success; + return false; } private static string GetNewSocketId() => Guid.NewGuid().ToString("N"); private static bool PrepWebSocket(this IHttpEvent entity, string? subProtocol = null) { + ArgumentNullException.ThrowIfNull(entity); + //Make sure this is a websocket request if (!entity.Server.IsWebSocketRequest) { diff --git a/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs b/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs index a235b13..e1706f4 100644 --- a/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs +++ b/lib/Plugins.Essentials/src/SemiConsistentVeTable.cs @@ -28,6 +28,7 @@ using System.Threading; using System.Threading.Tasks; using System.Collections.Frozen; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using VNLib.Net.Http; using VNLib.Plugins.Essentials.Endpoints; @@ -65,6 +66,7 @@ namespace VNLib.Plugins.Essentials new Dictionary<string, IVirtualEndpoint<HttpEntity>>(StringComparer.OrdinalIgnoreCase) .ToFrozenDictionary(); + private bool _isEmpty = true; /* * A lock that is held by callers that intend to @@ -73,14 +75,14 @@ namespace VNLib.Plugins.Essentials private readonly object VeUpdateLock = new(); ///<inheritdoc/> - public bool IsEmpty => VirtualEndpoints.Count == 0; + public bool IsEmpty => _isEmpty; ///<inheritdoc/> public void AddEndpoint(params IEndpoint[] endpoints) { //Check - _ = endpoints ?? throw new ArgumentNullException(nameof(endpoints)); + ArgumentNullException.ThrowIfNull(endpoints); //Make sure all endpoints specify a path if (endpoints.Any(static e => string.IsNullOrWhiteSpace(e?.Path))) { @@ -105,6 +107,7 @@ namespace VNLib.Plugins.Essentials //Uinion endpoints by their paths to combine them IEnumerable<IVirtualEndpoint<HttpEntity>> allEndpoints = eps.UnionBy(evs, static s => s.Path); + //Only allow 1 thread at a time to mutate the table lock (VeUpdateLock) { //Clone the current dictonary @@ -115,10 +118,11 @@ namespace VNLib.Plugins.Essentials newTable.Add(ep.Path, ep); } - FrozenDictionary<string, IVirtualEndpoint<HttpEntity>> newTableFrozen = newTable.ToFrozenDictionary(); + //Update is-empty flag + _isEmpty = newTable.Count == 0; - //Store the new table - _ = Interlocked.Exchange(ref VirtualEndpoints, newTableFrozen); + //Create the new table and store the entire table + _ = Interlocked.Exchange(ref VirtualEndpoints, newTable.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase)); } } @@ -157,24 +161,27 @@ namespace VNLib.Plugins.Essentials _ = newTable.Remove(eps); } - FrozenDictionary<string, IVirtualEndpoint<HttpEntity>> newTableFrozen = newTable.ToFrozenDictionary(); + //Update is-empty flag + _isEmpty = newTable.Count == 0; //Store the new table - _ = Interlocked.Exchange(ref VirtualEndpoints, newTableFrozen); + _ = Interlocked.Exchange(ref VirtualEndpoints, newTable.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase)); } } ///<inheritdoc/> - public bool TryGetEndpoint(string path, out IVirtualEndpoint<HttpEntity>? endpoint) => VirtualEndpoints.TryGetValue(path, out endpoint); + public bool TryGetEndpoint(string path, [NotNullWhen(true)] out IVirtualEndpoint<HttpEntity>? endpoint) + => VirtualEndpoints.TryGetValue(path, out endpoint); /* * Wrapper class for converting IHttpEvent endpoints to * httpEntityEndpoints */ - private sealed record class EvEndpointWrapper(IVirtualEndpoint<IHttpEvent> Wrapped) : IVirtualEndpoint<HttpEntity> + private sealed class EvEndpointWrapper(IVirtualEndpoint<IHttpEvent> Wrapped) : IVirtualEndpoint<HttpEntity> { string IEndpoint.Path => Wrapped.Path; + ValueTask<VfReturnType> IVirtualEndpoint<HttpEntity>.Process(HttpEntity entity) => Wrapped.Process(entity); } } diff --git a/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs b/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs index d3fc475..05d6712 100644 --- a/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs +++ b/lib/Plugins.Essentials/src/Sessions/ISessionExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials @@ -49,7 +49,7 @@ namespace VNLib.Plugins.Essentials.Sessions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string GetOrigin(this ISession session) => session[ORIGIN_ENTRY]; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Uri GetOriginUri(this ISession session) => Uri.TryCreate(session[ORIGIN_ENTRY], UriKind.Absolute, out Uri origin) ? origin : null; + public static Uri? GetOriginUri(this ISession session) => Uri.TryCreate(session[ORIGIN_ENTRY], UriKind.Absolute, out Uri? origin) ? origin : null; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetOrigin(this ISession session, string origin) => session[ORIGIN_ENTRY] = origin; diff --git a/lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs b/lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs index a5b9a30..bd89be1 100644 --- a/lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs +++ b/lib/Plugins.Essentials/src/Users/IUserCreationRequest.cs @@ -44,9 +44,9 @@ namespace VNLib.Plugins.Essentials.Users ulong Privileges { get; } /// <summary> - /// The user's email address + /// The user's unique username (may also be an email address /// </summary> - string EmailAddress { get; } + string Username { get; } /// <summary> /// Should the password be stored as-is in the database? |